From 52c7084c8fe57c259e50ff0a7d4f99ccecfc4c7a Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 11 Jun 2026 19:00:44 +0000 Subject: [PATCH 001/501] arm64: dts: qcom: glymur: fix PCIe SMMU interrupts The PCIe SMMUv3 wired interrupts are routed to GIC extended SPI INTIDs 4100, 4098 and 4096. Describe them as ESPIs with the ESPI-relative interrupt numbers instead of regular SPIs 964, 962 and 960. Assisted-by: Codex:GPT-5.5 Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260611-glymur-pcie-smmu-espi-v1-1-5effbaa3e1af@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index 20b49af7298e..c7edfa8156f3 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -6874,9 +6874,9 @@ apps_smmu: iommu@15000000 { pcie_smmu: iommu@15480000 { compatible = "arm,smmu-v3"; reg = <0x0 0x15480000 0x0 0x20000>; - interrupts = , - , - ; + interrupts = , + , + ; interrupt-names = "eventq", "cmdq-sync", "gerror"; dma-coherent; #iommu-cells = <1>; From a74a98f956ef8e075f28d58507bc5dad7f937fb4 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 11 Jun 2026 17:22:37 +0000 Subject: [PATCH 002/501] arm64: dts: qcom: glymur: fix QUP serial engine IRQs The Geni serial-engine interrupts from QUP wrapper 0 all fall in ESPI INTIDs space. While some of the i2c instances has gotten their interrupt specifiers corrected, even the other functions on the same serial-engines are wrong. Ensure that all the serial engine interrupts for QUP wrapper 0 matches the datasheet. Assisted-by: Codex:GPT-5.5 Fixes: 41b6e8db400c ("arm64: dts: qcom: Introduce Glymur base dtsi") Signed-off-by: Bjorn Andersson Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20260611-glymur-geni-irqs-v1-1-269428faeb6d@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/glymur.dtsi | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi index c7edfa8156f3..129be417ac42 100644 --- a/arch/arm64/boot/dts/qcom/glymur.dtsi +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi @@ -1876,7 +1876,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi0: spi@b80000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b80000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S0_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1903,7 +1903,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, i2c1: i2c@b84000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b84000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1930,7 +1930,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi1: spi@b84000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b84000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S1_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1957,7 +1957,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, i2c2: i2c@b88000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -1984,7 +1984,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi2: spi@b88000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2011,7 +2011,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, uart2: serial@b88000 { compatible = "qcom,geni-uart"; reg = <0x0 0x00b88000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S2_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2056,7 +2056,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi3: spi@b8c000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b8c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S3_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2110,7 +2110,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi4: spi@b90000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b90000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S4_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2164,7 +2164,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi5: spi@b94000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b94000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S5_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2191,7 +2191,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, i2c6: i2c@b98000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b98000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2218,7 +2218,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi6: spi@b98000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b98000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S6_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2245,7 +2245,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, i2c7: i2c@b9c000 { compatible = "qcom,geni-i2c"; reg = <0x0 0x00b9c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS @@ -2272,7 +2272,7 @@ &config_noc SLAVE_QUP_0 QCOM_ICC_TAG_ALWAYS>, spi7: spi@b9c000 { compatible = "qcom,geni-spi"; reg = <0x0 0x00b9c000 0x0 0x4000>; - interrupts = ; + interrupts = ; clocks = <&gcc GCC_QUPV3_WRAP0_S7_CLK>; clock-names = "se"; interconnects = <&clk_virt MASTER_QUP_CORE_0 QCOM_ICC_TAG_ALWAYS From 9d12eb98582fec2578d17e025b13740dcfb57d8e Mon Sep 17 00:00:00 2001 From: David Carlier Date: Tue, 17 Mar 2026 08:32:52 +0000 Subject: [PATCH 003/501] dmaengine: switchtec-dma: fix FIELD_GET misuse when programming SE threshold FIELD_GET(SE_THRESH_MASK, thresh) extracts bits [31:23] from thresh and right-shifts them, which is the inverse of the intended operation. Since thresh is derived from se_buf_len / 2 (at most 255), bits [31:23] are always zero, so the SE threshold is never actually programmed into the register. Use FIELD_PREP() instead to correctly left-shift thresh into bits [31:23] of the valid_en_se register, consistent with the FIELD_PREP usage for the perf tuner config just above. Fixes: 30eba9df76ad ("dmaengine: switchtec-dma: Implement hardware initialization and cleanup") Signed-off-by: David Carlier Review-by: Logan Gunthorpe Reviewed-by: Frank Li Link: https://patch.msgid.link/20260317083252.13224-1-devnexen@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/switchtec_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/dma/switchtec_dma.c b/drivers/dma/switchtec_dma.c index 3ef928640615..71d9868ce613 100644 --- a/drivers/dma/switchtec_dma.c +++ b/drivers/dma/switchtec_dma.c @@ -1099,7 +1099,7 @@ static int switchtec_dma_chan_init(struct switchtec_dma_dev *swdma_dev, dev_dbg(&pdev->dev, "Channel %d: SE buffer count %d\n", i, se_buf_len); thresh = se_buf_len / 2; - valid_en_se |= FIELD_GET(SE_THRESH_MASK, thresh); + valid_en_se |= FIELD_PREP(SE_THRESH_MASK, thresh); writel(valid_en_se, &swdma_chan->mmio_chan_fw->valid_en_se); /* request irqs */ From ab1150115e68a46b687eb38c1ab92782018c9f2c Mon Sep 17 00:00:00 2001 From: Hongling Zeng Date: Wed, 1 Jul 2026 12:57:33 +0800 Subject: [PATCH 004/501] dmaengine: sun6i-dma: Fix reclaim descriptors while terminating DMA When terminating DMA transfers, active descriptors are not properly reclaimed. Only cyclic descriptors were handled, leaving non-cyclic descriptors and their LLI chains to be permanently leaked. Fix by using vchan_terminate_vdesc() which handles both cyclic and non-cyclic descriptors by adding them to desc_terminated queue for proper cleanup. Add pchan->desc != pchan->done check to prevent double-adding completed descriptors, which would corrupt the list. Fixes: 555859308723 ("dmaengine: sun6i: Add driver for the Allwinner A31 DMA controller") Signed-off-by: Hongling Zeng Acked-by: Jernej Skrabec Suggested-by: Frank Li Reviewed-by: Frank Li Link: https://patch.msgid.link/20260701045733.33654-1-zenghongling@kylinos.cn Signed-off-by: Vinod Koul --- drivers/dma/sun6i-dma.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index a9a254dbf8cb..f47a326dd7ff 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -945,16 +945,13 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan) spin_lock_irqsave(&vchan->vc.lock, flags); - if (vchan->cyclic) { - vchan->cyclic = false; - if (pchan && pchan->desc) { - struct virt_dma_desc *vd = &pchan->desc->vd; - struct virt_dma_chan *vc = &vchan->vc; + if (pchan && pchan->desc && pchan->desc != pchan->done) { + struct virt_dma_desc *vd = &pchan->desc->vd; - list_add_tail(&vd->node, &vc->desc_completed); - } + vchan_terminate_vdesc(vd); } + vchan->cyclic = false; vchan_get_all_descriptors(&vchan->vc, &head); if (pchan) { From 34db32102439d948e0c9aea060e48f979aae827d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 18 Jun 2026 10:08:11 +0200 Subject: [PATCH 005/501] MAINTAINERS: Drop Karthikeyan Mitran from Mobiveil PCIe entry Karthikeyan's Mobiveil email address is now bouncing and there is no sign of him [1] willing to carry out maintainership duties for this driver. Drop him from the MAINTAINERS entry for Mobiveil PCIe. Link: https://lore.kernel.org/linux-pci/ahWB7yRchlW_KjEm@wunner.de Signed-off-by: Manivannan Sadhasivam Signed-off-by: Manivannan Sadhasivam Signed-off-by: Bjorn Helgaas Cc: Lukas Wunner Cc: Mitran Karthikeyan Link: https://patch.msgid.link/20260618080811.26337-1-manivannan.sadhasivam@oss.qualcomm.com --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..c20756e28d60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20686,7 +20686,6 @@ F: include/linux/switchtec.h F: include/uapi/linux/switchtec_ioctl.h PCI DRIVER FOR MOBIVEIL PCIE IP -M: Karthikeyan Mitran M: Hou Zhiqiang L: linux-pci@vger.kernel.org S: Supported From 4cd774c1feb3f720265c512174c5c3312eca1be2 Mon Sep 17 00:00:00 2001 From: Akhil P Oommen Date: Fri, 10 Apr 2026 02:38:51 +0530 Subject: [PATCH 006/501] arm64: dts: qcom: purwa: Fix GPU IOMMU property Purwa's GPU does not support SID 1, which is typically used for LPAC-related traffic. Remove SID 1 from the GPU node's iommus property to accurately describe the hardware. This fixes the splat below, seen with some versions of Gunyah hypervisor: Internal error: synchronous external abort: 0000000096000010 [#1] SMP CPU: 0 UID: 0 PID: 80 Comm: kworker/u33:2 Tainted: G M Tainted: [M]=MACHINE_CHECK Hardware name: Qualcomm Technologies, Inc. Purwa IoT EVK (DT) Workqueue: events_unbound deferred_probe_work_func pstate: 21400005 (nzCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) pc : arm_smmu_write_s2cr+0x9c/0xbc lr : arm_smmu_master_install_s2crs+0x78/0xa4 sp : ffff80008039b570 x29: ffff80008039b570 x28: 0000000000000000 x27: ffffaddd62f1ab78 x26: ffff00080a4ff280 x25: 0000000000000018 x24: ffff00080b896480 x23: ffff00080ba9b7a0 x22: ffff00080bb05160 x21: 0000000000000000 x20: 0000000000000000 x19: 0000000000000001 x18: 00000000ffffffff x17: 0000000000000000 x16: 0000000000000000 x15: ffff80008039b1d0 x14: ffff80010039b37d x13: 00746c7561662d74 x12: 0000000000000000 x11: ffff00080b7fbd98 x10: ffffffffffffffc0 x9 : ffffffffffffffff x8 : 0000000000000228 x7 : 0000000000000e87 x6 : 0000000000000000 x5 : 0000000000000000 x4 : ffff00080a4ff280 x3 : 0000000000000000 x2 : ffff800082a40c04 x1 : 0000000000000000 x0 : ffff800082a40000 Call trace: arm_smmu_write_s2cr+0x9c/0xbc (P) arm_smmu_master_install_s2crs+0x78/0xa4 arm_smmu_attach_dev+0xb0/0x1d8 __iommu_device_set_domain+0x84/0x11c __iommu_group_set_domain_internal+0x60/0x120 __iommu_attach_group+0x88/0x9c iommu_attach_device+0x6c/0xa0 msm_iommu_new.part.0+0x84/0xe4 [msm] msm_iommu_gpu_new+0x3c/0x104 [msm] adreno_iommu_create_vm+0x24/0xc8 [msm] a6xx_create_vm+0x48/0x78 [msm] msm_gpu_init+0x2d8/0x508 [msm] adreno_gpu_init+0x208/0x324 [msm] a6xx_gpu_init+0x604/0x8cc [msm] adreno_bind+0xb4/0x124 [msm] component_bind_all+0x114/0x23c msm_drm_init+0x1b0/0x1ec [msm] msm_drm_bind+0x30/0x3c [msm] try_to_bring_up_aggregate_device+0x164/0x1d0 __component_add+0xa4/0x16c component_add+0x14/0x20 msm_dp_display_probe_tail+0x4c/0xac [msm] msm_dp_auxbus_done_probe+0x14/0x20 [msm] dp_aux_ep_probe+0x4c/0xf4 [drm_dp_aux_bus] really_probe+0xbc/0x29c __driver_probe_device+0x78/0x12c driver_probe_device+0x3c/0x15c __device_attach_driver+0xb8/0x134 bus_for_each_drv+0x88/0xe8 __device_attach+0xa0/0x190 device_initial_probe+0x50/0x54 bus_probe_device+0x38/0xa4 deferred_probe_work_func+0x88/0xc0 process_one_work+0x148/0x28c worker_thread+0x2cc/0x3d4 kthread+0x12c/0x204 ret_from_fork+0x10/0x20 ---[ end trace 0000000000000000 ]--- Fixes: 1aa0b4e36436 ("arm64: dts: qcom: x1p42100: Add GPU support") Signed-off-by: Akhil P Oommen Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260410-purwa-gpu-dt-fix-v1-1-4637892156cf@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/purwa.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/purwa.dtsi b/arch/arm64/boot/dts/qcom/purwa.dtsi index 9ab4f26b35f2..5b17840fb62f 100644 --- a/arch/arm64/boot/dts/qcom/purwa.dtsi +++ b/arch/arm64/boot/dts/qcom/purwa.dtsi @@ -47,6 +47,8 @@ &gmu { &gpu { compatible = "qcom,adreno-43030c00", "qcom,adreno"; + iommus = <&adreno_smmu 0 0x0>; + nvmem-cells = <&gpu_speed_bin>; nvmem-cell-names = "speed_bin"; From d291245e2fb1eba55d751f88613a41e292958a96 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 7 Apr 2026 22:15:19 +0200 Subject: [PATCH 007/501] arm64: dts: qcom: monaco: Add default GIC address cells Add missing address-cells 0 to GIC interrupt node to silence W=1 warning: monaco.dtsi:2326.4-2329.30: Warning (interrupt_map): /soc@0/pci@1c00000:interrupt-map: Missing property '#address-cells' in node /soc@0/interrupt-controller@17a00000, using 0 as fallback Value '0' is correct because: 1. GIC interrupt controller does not have children, 2. interrupt-map property (in PCI node) consists of five components and the fourth component 'parent unit address', which size is defined by '#address-cells' of the node pointed to by the interrupt-parent component, is not used (=0). Fixes: 46a7c01e7e9d ("arm64: dts: qcom: qcs8300: enable pcie0") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Manivannan Sadhasivam Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260407201518.24949-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/monaco.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi index e4c8466f941b..a350a8ba48fa 100644 --- a/arch/arm64/boot/dts/qcom/monaco.dtsi +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi @@ -7384,6 +7384,7 @@ intc: interrupt-controller@17a00000 { interrupt-controller; #redistributor-regions = <1>; redistributor-stride = <0x0 0x20000>; + #address-cells = <0>; }; watchdog@17c10000 { From 41d237b1546af5d5cf877175930cd23163422c83 Mon Sep 17 00:00:00 2001 From: Alexander Koskovich Date: Wed, 15 Apr 2026 23:45:18 +0000 Subject: [PATCH 008/501] arm64: dts: qcom: sm8650: Fix IPA IMEM slice Downstream the IPA IMEM slice for SM8650 is described as: qcom,additional-mapping = <0x14683000 0x14683000 0x2000>; Update upstream ipa_modem_tables to reflect downstream. Signed-off-by: Alexander Koskovich Fixes: 581fc5d5ade6 ("arm64: dts: qcom: sm8650: Explicitly describe the IPA IMEM slice") Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260415-fix-8650-ipa-modem-tables-v1-1-95f8f425e416@pm.me Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sm8650.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sm8650.dtsi b/arch/arm64/boot/dts/qcom/sm8650.dtsi index 160ead25ecf7..65c4a2b46d21 100644 --- a/arch/arm64/boot/dts/qcom/sm8650.dtsi +++ b/arch/arm64/boot/dts/qcom/sm8650.dtsi @@ -7091,8 +7091,8 @@ sram@14680000 { ranges = <0 0 0x14680000 0x2c000>; - ipa_modem_tables: modem-tables@8000 { - reg = <0x8000 0x2000>; + ipa_modem_tables: modem-tables@3000 { + reg = <0x3000 0x2000>; }; }; From 96f65d01f3132a163a8c0e84aca00f2acdc75e9b Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Mon, 20 Apr 2026 01:32:49 +0800 Subject: [PATCH 009/501] arm64: dts: qcom: sc8280xp: add several missing pdc map entries pdc 215, 256, 257 are missing, but we can find tlmm pin 103, 84, 90 are mapped to them respectively, so add the map entries from pdc to gic. These entries are reversed from .data section of qcgpio.sys Signed-off-by: Pengyu Luo Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260419173251.1180026-1-mitltlatltl@gmail.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sc8280xp.dtsi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi index a2bd6b10e475..b0de9e262f29 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp.dtsi +++ b/arch/arm64/boot/dts/qcom/sc8280xp.dtsi @@ -5356,7 +5356,7 @@ pdc: interrupt-controller@b220000 { <211 699 1>, <212 705 1>, <213 450 1>, - <214 643 1>, + <214 643 2>, <216 646 5>, <221 390 5>, <226 700 3>, @@ -5379,7 +5379,7 @@ pdc: interrupt-controller@b220000 { <252 798 1>, <253 765 1>, <254 763 1>, - <255 454 1>, + <255 454 3>, <258 139 1>, <259 786 2>, <261 370 2>, From 6267f93eac9aa6963797c5bc1a18de9571ccac26 Mon Sep 17 00:00:00 2001 From: Pengyu Luo Date: Mon, 20 Apr 2026 01:32:50 +0800 Subject: [PATCH 010/501] arm64: dts: qcom: sc8280xp: gaokun3: correct EC interrupt pin Unlike other sc8280xp platforms, on this platform, in dsdt, gpio 0x2c0 is not mapped to gpio107, it is gpio103, so fix it. I found this until I did a trigger track, irq is regularly triggerd every several millisecs. In the past, since here gpio107 was low forever, ec irq would keep to be triggered and polling the event every several millisecs. About how to get the map, please check openbsd driver for it https://github.com/openbsd/src/blob/master/sys/dev/acpi/qcgpio.c Signed-off-by: Pengyu Luo Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260419173251.1180026-2-mitltlatltl@gmail.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts index f3c00be67081..cd575e20d90a 100644 --- a/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts +++ b/arch/arm64/boot/dts/qcom/sc8280xp-huawei-gaokun3.dts @@ -637,7 +637,7 @@ embedded-controller@38 { compatible = "huawei,gaokun3-ec"; reg = <0x38>; - interrupts-extended = <&tlmm 107 IRQ_TYPE_LEVEL_LOW>; + interrupts-extended = <&tlmm 103 IRQ_TYPE_LEVEL_LOW>; #address-cells = <1>; #size-cells = <0>; From ec2d428b2e32dd157de8f86a86dd85c5b2c8f45c Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Wed, 15 Apr 2026 16:54:52 -0400 Subject: [PATCH 011/501] dmaengine: idxd: fix double free of wq, engine, and group structs The release callbacks for wq, engine, and group devices (idxd_conf_wq_release, idxd_conf_engine_release, idxd_conf_group_release) each call kfree() on the enclosing struct. The setup error paths and cleanup functions also call kfree() explicitly after put_device(), producing a double free whenever put_device() drops the reference count to zero and fires the release. In the setup functions, device_initialize() is called before device_add(), so the reference count is exactly 1 at the error sites. put_device() unconditionally fires the release, which frees the struct; the subsequent explicit kfree() then operates on freed memory. For idxd_setup_wqs(), the wq release callback also owns opcap_bmap and wqcfg. The error unwind additionally freed those fields explicitly before calling put_device(), causing further double frees on both. Remove the redundant explicit kfree() calls from all setup error paths and cleanup functions for wq, engine, and group structs, delegating sole ownership of those allocations to the release callbacks. Fixes: 7c5dd23e57c1 ("dmaengine: idxd: fix wq conf_dev 'struct device' lifetime") Fixes: 75b911309060 ("dmaengine: idxd: fix engine conf_dev lifetime") Fixes: defe49f96012 ("dmaengine: idxd: fix group conf_dev lifetime") Signed-off-by: Yuho Choi Acked-by: Vinicius Costa Gomes Reviewed-by: Frank Li Link: https://patch.msgid.link/20260415205452.67155-1-dbgh9129@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/init.c | 36 +++++------------------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index f1cfc7790d95..4b827a329756 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -159,18 +159,12 @@ static void idxd_cleanup_interrupts(struct idxd_device *idxd) static void idxd_clean_wqs(struct idxd_device *idxd) { - struct idxd_wq *wq; struct device *conf_dev; int i; for (i = 0; i < idxd->max_wqs; i++) { - wq = idxd->wqs[i]; - if (idxd->hw.wq_cap.op_config) - bitmap_free(wq->opcap_bmap); - kfree(wq->wqcfg); - conf_dev = wq_confdev(wq); + conf_dev = wq_confdev(idxd->wqs[i]); put_device(conf_dev); - kfree(wq); } bitmap_free(idxd->wq_enable_map); kfree(idxd->wqs); @@ -212,7 +206,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "wq%d.%d", idxd->id, wq->id); if (rc < 0) { put_device(conf_dev); - kfree(wq); goto err_unwind; } @@ -227,7 +220,6 @@ static int idxd_setup_wqs(struct idxd_device *idxd) wq->wqcfg = kzalloc_node(idxd->wqcfg_size, GFP_KERNEL, dev_to_node(dev)); if (!wq->wqcfg) { put_device(conf_dev); - kfree(wq); rc = -ENOMEM; goto err_unwind; } @@ -235,9 +227,7 @@ static int idxd_setup_wqs(struct idxd_device *idxd) if (idxd->hw.wq_cap.op_config) { wq->opcap_bmap = bitmap_zalloc(IDXD_MAX_OPCAP_BITS, GFP_KERNEL); if (!wq->opcap_bmap) { - kfree(wq->wqcfg); put_device(conf_dev); - kfree(wq); rc = -ENOMEM; goto err_unwind; } @@ -252,13 +242,8 @@ static int idxd_setup_wqs(struct idxd_device *idxd) err_unwind: while (--i >= 0) { - wq = idxd->wqs[i]; - if (idxd->hw.wq_cap.op_config) - bitmap_free(wq->opcap_bmap); - kfree(wq->wqcfg); - conf_dev = wq_confdev(wq); + conf_dev = wq_confdev(idxd->wqs[i]); put_device(conf_dev); - kfree(wq); } bitmap_free(idxd->wq_enable_map); @@ -270,15 +255,12 @@ static int idxd_setup_wqs(struct idxd_device *idxd) static void idxd_clean_engines(struct idxd_device *idxd) { - struct idxd_engine *engine; struct device *conf_dev; int i; for (i = 0; i < idxd->max_engines; i++) { - engine = idxd->engines[i]; - conf_dev = engine_confdev(engine); + conf_dev = engine_confdev(idxd->engines[i]); put_device(conf_dev); - kfree(engine); } kfree(idxd->engines); } @@ -313,7 +295,6 @@ static int idxd_setup_engines(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "engine%d.%d", idxd->id, engine->id); if (rc < 0) { put_device(conf_dev); - kfree(engine); goto err; } @@ -324,10 +305,8 @@ static int idxd_setup_engines(struct idxd_device *idxd) err: while (--i >= 0) { - engine = idxd->engines[i]; - conf_dev = engine_confdev(engine); + conf_dev = engine_confdev(idxd->engines[i]); put_device(conf_dev); - kfree(engine); } kfree(idxd->engines); @@ -336,13 +315,10 @@ static int idxd_setup_engines(struct idxd_device *idxd) static void idxd_clean_groups(struct idxd_device *idxd) { - struct idxd_group *group; int i; for (i = 0; i < idxd->max_groups; i++) { - group = idxd->groups[i]; - put_device(group_confdev(group)); - kfree(group); + put_device(group_confdev(idxd->groups[i])); } kfree(idxd->groups); } @@ -377,7 +353,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) rc = dev_set_name(conf_dev, "group%d.%d", idxd->id, group->id); if (rc < 0) { put_device(conf_dev); - kfree(group); goto err; } @@ -402,7 +377,6 @@ static int idxd_setup_groups(struct idxd_device *idxd) while (--i >= 0) { group = idxd->groups[i]; put_device(group_confdev(group)); - kfree(group); } kfree(idxd->groups); From ee1d7274102285d78a53161fc705a8d8cd40b066 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 25 May 2026 10:15:50 -0400 Subject: [PATCH 012/501] dmaengine: idxd: fix fdev setup failure cleanup in idxd_cdev_open() The failed_dev_add and failed_dev_name paths drop the file-device reference while wq->wq_lock is still held. If put_device(fdev) drops the last reference, idxd_file_dev_release() runs synchronously and tries to take wq->wq_lock again, deadlocking. Those paths also fall through into the later ctx cleanup labels even though idxd_file_dev_release() owns that cleanup and frees ctx. This can make idxd_xa_pasid_remove(ctx) and kfree(ctx) operate on a freed context. Move idxd_wq_get() before file-device setup can fail, since the release callback always calls idxd_wq_put(). Then unlock wq->wq_lock before put_device(fdev) and return directly from the file-device setup failure path, leaving ctx cleanup to the release callback. Fixes: e6fd6d7e5f0fe ("dmaengine: idxd: add a device to represent the file opened") Signed-off-by: Yuho Choi Reviewed-by: Dave Jiang Acked-by: Vinicius Costa Gomes Link: https://patch.msgid.link/20260525141550.1385581-1-dbgh9129@gmail.com Signed-off-by: Vinod Koul --- drivers/dma/idxd/cdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/dma/idxd/cdev.c b/drivers/dma/idxd/cdev.c index 0366c7cf3502..82b07cf942ef 100644 --- a/drivers/dma/idxd/cdev.c +++ b/drivers/dma/idxd/cdev.c @@ -288,6 +288,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) fdev->parent = cdev_dev(idxd_cdev); fdev->bus = &dsa_bus_type; fdev->type = &idxd_cdev_file_type; + idxd_wq_get(wq); rc = dev_set_name(fdev, "file%d", ctx->id); if (rc < 0) { @@ -301,13 +302,14 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp) goto failed_dev_add; } - idxd_wq_get(wq); mutex_unlock(&wq->wq_lock); return 0; failed_dev_add: failed_dev_name: + mutex_unlock(&wq->wq_lock); put_device(fdev); + return rc; failed_ida: failed_set_pasid: if (device_user_pasid_enabled(idxd)) From 867621ba203027338b525af6729719c544135336 Mon Sep 17 00:00:00 2001 From: Md Sadre Alam Date: Mon, 15 Jun 2026 11:39:08 +0530 Subject: [PATCH 013/501] dmaengine: qcom: bam_dma: Fix command element mask field for BAM v1.6.0+ BAM version 1.6.0 and later changed the behavior of the mask field in command elements for read operations. In older BAM versions, or prior implementation assumptions, the mask field was effectively ignored for read commands. However, starting from BAM v1.6.0, the mask field for read commands is repurposed to carry the upper 4 bits of the destination address, enabling support for 36-bit addressing. For write commands, the mask field continues to function as a traditional write mask. The current driver sets mask = 0xffffffff for all command elements. While this works for write operations, it breaks read operations on BAM v1.6.0+ hardware. In such cases, the hardware interprets the upper address bits as 0xf, resulting in an invalid destination address (0xf_xxxxxxxx instead of 0x0_xxxxxxxx). This leads to failures such as NAND enumeration issues observed on platforms like IPQ5424. Fix this by assigning the mask field based on command type: - For read commands: set mask = 0 (upper address bits = 0) - For write commands: retain mask = 0xffffffff Also update the bam_cmd_element structure documentation to reflect the dual purpose of the mask field across BAM versions. This ensures correct behavior on BAM v1.6.0+ while maintaining backward compatibility with older hardware. Fixes: dfebb055f73a2 ("dmaengine: qcom: bam_dma: wrapper functions for command descriptor") Tested-by: Lakshmi Sowjanya D Signed-off-by: Md Sadre Alam Reviewed-by: Frank Li Reviewed-by: Dmitry Baryshkov Cc: stable@vger.kernel.org Signed-off-by: Varadarajan Narayanan Link: https://patch.msgid.link/20260615060908.1263171-1-varadarajan.narayanan@oss.qualcomm.com Signed-off-by: Vinod Koul --- include/linux/dma/qcom_bam_dma.h | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/include/linux/dma/qcom_bam_dma.h b/include/linux/dma/qcom_bam_dma.h index 68fc0e643b1b..d9d07a9ab313 100644 --- a/include/linux/dma/qcom_bam_dma.h +++ b/include/linux/dma/qcom_bam_dma.h @@ -13,9 +13,12 @@ * supported by BAM DMA Engine. * * @cmd_and_addr - upper 8 bits command and lower 24 bits register address. - * @data - for write command: content to be written into peripheral register. - * for read command: dest addr to write peripheral register value. - * @mask - register mask. + * @data - For write command: content to be written into peripheral register. + * For read command: lower 32 bits of destination address. + * @mask - For write command: register write mask. + * For read command on BAM v1.6.0+: upper 4 bits of destination address. + * For read command on BAM < v1.6.0: ignored by hardware. + * Setting to 0 ensures 32-bit addressing compatibility. * @reserved - for future usage. * */ @@ -42,6 +45,10 @@ enum bam_command_type { * @addr: target address * @cmd: BAM command * @data: actual data for write and dest addr for read in le32 + * + * For BAM v1.6.0+, the mask field behavior depends on command type: + * - Write commands: mask = write mask (typically 0xffffffff) + * - Read commands: mask = upper 4 bits of destination address (0 for 32-bit) */ static inline void bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, @@ -50,7 +57,11 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, bam_ce->cmd_and_addr = cpu_to_le32((addr & 0xffffff) | ((cmd & 0xff) << 24)); bam_ce->data = data; - bam_ce->mask = cpu_to_le32(0xffffffff); + if (cmd == BAM_READ_COMMAND) + bam_ce->mask = cpu_to_le32(0x0); /* 32-bit addressing */ + else + bam_ce->mask = cpu_to_le32(0xffffffff); /* Write mask */ + bam_ce->reserved = 0; } /* @@ -60,7 +71,7 @@ bam_prep_ce_le32(struct bam_cmd_element *bam_ce, u32 addr, * @bam_ce: BAM command element * @addr: target address * @cmd: BAM command - * @data: actual data for write and dest addr for read + * @data: actual data for write and destination address for read */ static inline void bam_prep_ce(struct bam_cmd_element *bam_ce, u32 addr, From 07db10de262f4150e24fd631a7a6c428f7bf80c9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 29 Apr 2026 01:27:12 +0300 Subject: [PATCH 014/501] arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower PSCI cluster idle With the default PSCI suspend value for cluster idle state Lenovo Yoga C630 isn't stable enough. For example it might reset if display device isn't probed early enough. Drop the bit 0x4000 from the PSCI suspend value to make C630 work in stable way. The bit was found by expertimenting with the cluster idle PSCI value. Most likely it results in the less deep sleep and more energy beign spent in the suspend state, but it's better than the non-stable system behaviour. Fixes: a1ade6cac5a2 ("arm64: dts: qcom: sdm845: Switch PSCI cpu idle states from PC to OSI") Signed-off-by: Dmitry Baryshkov Reviewed-by: Abel Vesa Link: https://lore.kernel.org/r/20260429-c630-fix-idle-v2-1-ac867dad6f21@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts index e41200839dbe..8cdaef5360d1 100644 --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts @@ -347,6 +347,10 @@ &cdsp_pas { status = "okay"; }; +&cluster_sleep_0 { + arm,psci-suspend-param = <0x41008244>; +}; + &gcc { protected-clocks = , , From ba5c0f28a26e7d9be1e0997f8920dd638e2782fd Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Fri, 10 Jul 2026 20:29:52 +0800 Subject: [PATCH 015/501] iommufd: Fix wrong hwpt passed to iommufd_auto_response_faults on replace iommufd_hwpt_replace_device() calls: iommufd_auto_response_faults(hwpt, old_handle); passing the *new* hwpt together with the handle of the device's *old* domain. This should be a parameter mismatch: 1. Semantically, iommufd_auto_response_faults(x, handle) scans x->fault's deliver list and response xarray for groups matching "handle". A group is queued under the hwpt that was attached at fault-delivery time. old_handle is fetched *before* the domain switch, so its group lives on old->fault, not on the new hwpt->fault. 2. Historically, the first argument was "old". The routine was introduced by commit b7d8833677ba ("iommufd: Fault-capable hwpt attach/detach/replace") as __fault_domain_replace_dev() in fault.c, correctly calling iommufd_auto_response_faults(old, curr). Commit fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") moved this into iommufd_hwpt_replace_device() in device.c and swapped it to "hwpt". This should be a refactor regression, not an intentional change. Fix this by passing "old" instead. Link: https://patch.msgid.link/r/9D652384339C69D5+20260710122952.885325-1-peiyang_he@smail.nju.edu.cn Fixes: fb21b1568ada ("iommufd: Make attach_handle generic than fault specific") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c index 170a7005f0bc..2895e5370910 100644 --- a/drivers/iommu/iommufd/device.c +++ b/drivers/iommu/iommufd/device.c @@ -589,7 +589,7 @@ static int iommufd_hwpt_replace_device(struct iommufd_device *idev, if (rc) goto out_free_handle; - iommufd_auto_response_faults(hwpt, old_handle); + iommufd_auto_response_faults(old, old_handle); kfree(old_handle); return 0; From 5f3fc0ad9a41883a62098359b9fdbe4257f20e53 Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Thu, 9 Jul 2026 13:08:00 +0800 Subject: [PATCH 016/501] iommufd: Reject DMABUF pages from the access pin path DMABUF pages are not supported for iommufd access pinning. iommufd_access_pin_pages() returns struct page pointers for in-kernel CPU access, but DMABUF-backed iopt_pages do not carry a userspace address that can be passed to the GUP path. iopt_pages_rw_access() already rejects IOPT_ADDRESS_DMABUF before doing CPU access. Apply the same rejection to iopt_area_add_access() before it takes pages->mutex and calls iopt_pages_fill_xarray(). Otherwise a DMABUF-backed iopt_pages can reach the hole-fill path, where pfn_reader_user_pin() interprets the union as uptr and calls pin_user_pages_fast()/pin_user_pages_remote(). This fix also avoids the lockdep warning reported from that path, where pages_dmabuf_mutex_key is held while gup_fast_fallback() may acquire mmap_lock. Link: https://patch.msgid.link/r/CD68F549BF3761B7+20260709050800.520607-1-peiyang_he@smail.nju.edu.cn Reported-by: Peiyang He Closes: https://lore.kernel.org/all/E8540D7D05768C91+8b2ef227-3368-494e-909d-7b28e1489dfb@smail.nju.edu.cn/ Fixes: 71db84a092c3 ("iommufd: Add DMABUF to iopt_pages") Cc: stable@vger.kernel.org Tested-by: Peiyang He Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/pages.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/iommu/iommufd/pages.c b/drivers/iommu/iommufd/pages.c index 03c8379bbc34..404f31d8f729 100644 --- a/drivers/iommu/iommufd/pages.c +++ b/drivers/iommu/iommufd/pages.c @@ -2451,6 +2451,9 @@ int iopt_area_add_access(struct iopt_area *area, unsigned long start_index, if ((flags & IOMMUFD_ACCESS_RW_WRITE) && !pages->writable) return -EPERM; + if (iopt_is_dmabuf(pages)) + return -EINVAL; + mutex_lock(&pages->mutex); access = iopt_pages_get_exact_access(pages, start_index, last_index); if (access) { From 339bd11591593ab7ce88136ab7fd01ef3813b724 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:09 -0700 Subject: [PATCH 017/501] iommufd/viommu: Release the igroup lock on the vdevice_size error path iommufd_vdevice_alloc_ioctl() takes idev->igroup->lock, then validates the driver's vdevice_size against the core structure size with a WARN_ON_ONCE. On failure that guard jumps to out_put_idev, below out_unlock_igroup, so it skips the mutex_unlock(), leaving the igroup lock held and deadlocking the next vDEVICE operation on that group. Jump to out_unlock_igroup instead. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/e903f775d491296a525097e2a90b3eb6a47cf2ef.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/viommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 4081deda9b33..0c12c7e352a1 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -189,7 +189,7 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) if (WARN_ON_ONCE(viommu->ops->vdevice_size < vdev_size || !viommu->ops->vdevice_init)) { rc = -EOPNOTSUPP; - goto out_put_idev; + goto out_unlock_igroup; } vdev_size = viommu->ops->vdevice_size; } From 9be311cfbe6154da146a7408e0d5e518a9321ed3 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:10 -0700 Subject: [PATCH 018/501] iommufd/viommu: Publish a vDEVICE only after vdevice_init() succeeds iommufd_vdevice_alloc_ioctl() adds the vDEVICE to the viommu->vdevs xarray with xa_cmpxchg() before the driver's vdevice_init() op runs. That op is where a driver validates the device and may reject it, but the xarray entry is already live by then: a concurrent IOMMU_HWPT_INVALIDATE can look it up with iommufd_viommu_find_dev() and run the driver invalidation path against a device that vdevice_init() would have refused. Reserve the index with xa_insert(): it stores a zero entry that reads back as NULL, and returns -EBUSY on a duplicate virt_id. Run vdevice_init() and store the vDEVICE pointer only once it succeeds. A failed vdevice_init() releases the reservation, so lookups observe the vDEVICE only after it is fully initialized and accepted. Fixes: ed42eee797ff3 ("iommufd/viommu: Add driver-defined vDEVICE support") Link: https://patch.msgid.link/r/1e05999347f4bf583edbc6a1312c857d5548708c.1783311134.git.nicolinc@nvidia.com Cc: stable@vger.kernel.org Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- drivers/iommu/iommufd/viommu.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/iommufd/viommu.c b/drivers/iommu/iommufd/viommu.c index 0c12c7e352a1..fc13cf4737ea 100644 --- a/drivers/iommu/iommufd/viommu.c +++ b/drivers/iommu/iommufd/viommu.c @@ -143,7 +143,7 @@ void iommufd_vdevice_destroy(struct iommufd_object *obj) int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) { struct iommu_vdevice_alloc *cmd = ucmd->cmd; - struct iommufd_vdevice *vdev, *curr; + struct iommufd_vdevice *vdev; size_t vdev_size = sizeof(*vdev); struct iommufd_viommu *viommu; struct iommufd_device *idev; @@ -218,18 +218,28 @@ int iommufd_vdevice_alloc_ioctl(struct iommufd_ucmd *ucmd) */ idev->vdev = vdev; - curr = xa_cmpxchg(&viommu->vdevs, virt_id, NULL, vdev, GFP_KERNEL); - if (curr) { - rc = xa_err(curr) ?: -EEXIST; + /* + * Reserve the slot with a zero entry (reads back as NULL) until the + * vdevice_init() op accepts the vDEVICE. Only the xa_* helpers hide a + * reserved entry, so never use a raw xas_* iterator on this xarray. + */ + rc = xa_insert(&viommu->vdevs, virt_id, NULL, GFP_KERNEL); + if (rc) { + if (rc == -EBUSY) + rc = -EEXIST; goto out_abort; } if (viommu->ops && viommu->ops->vdevice_init) { rc = viommu->ops->vdevice_init(vdev); - if (rc) + if (rc) { + xa_release(&viommu->vdevs, virt_id); goto out_abort; + } } + xa_store(&viommu->vdevs, virt_id, vdev, GFP_KERNEL); + cmd->out_vdevice_id = vdev->obj.id; rc = iommufd_ucmd_respond(ucmd, sizeof(*cmd)); if (rc) From c3b8ee84a965058b41275069d4696f37a8b14bf6 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Sun, 5 Jul 2026 22:36:11 -0700 Subject: [PATCH 019/501] iommu/arm-smmu-v3-iommufd: Require exactly one Stream ID for a vDEVICE arm_vsmmu_vsid_to_sid() maps a guest's vSID to a single physical Stream ID taken from master->streams[0], assuming a device has exactly one stream. A device with several streams gets only its first one mapped, so a guest vSID invalidation cannot reach the others' ATC and IOTLB entries; a device with none makes master->streams a ZERO_SIZE_PTR, read out of bounds. Add an arm_vsmmu_vdevice_init() op to reject the vDEVICE with -EOPNOTSUPP when master->num_streams is not one, rather than mapping it silently. Fixes: d68beb276ba26 ("iommu/arm-smmu-v3: Support IOMMU_HWPT_INVALIDATE using a VIOMMU object") Link: https://patch.msgid.link/r/b15f2b73520f389f3f57881da2f040e7bdc18876.1783311134.git.nicolinc@nvidia.com Reviewed-by: Kevin Tian Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Pranjal Shrivastava Signed-off-by: Nicolin Chen Signed-off-by: Jason Gunthorpe --- .../iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c index 1e9f7d2de344..85ebfdb3d2a7 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3-iommufd.c @@ -297,6 +297,20 @@ static int arm_vsmmu_vsid_to_sid(struct arm_vsmmu *vsmmu, u32 vsid, u32 *sid) return ret; } +static int arm_vsmmu_vdevice_init(struct iommufd_vdevice *vdev) +{ + struct device *dev = iommufd_vdevice_to_device(vdev); + struct arm_smmu_master *master = dev_iommu_priv_get(dev); + + /* + * arm_vsmmu_vsid_to_sid() maps a vSID to master->streams[0] alone, so + * more streams would leave the rest stale and none reads out of bounds. + */ + if (master->num_streams != 1) + return -EOPNOTSUPP; + return 0; +} + /* This is basically iommu_viommu_arm_smmuv3_invalidate in u64 for conversion */ struct arm_vsmmu_invalidation_cmd { union { @@ -403,6 +417,7 @@ int arm_vsmmu_cache_invalidate(struct iommufd_viommu *viommu, static const struct iommufd_viommu_ops arm_vsmmu_ops = { .alloc_domain_nested = arm_vsmmu_alloc_domain_nested, .cache_invalidate = arm_vsmmu_cache_invalidate, + .vdevice_init = arm_vsmmu_vdevice_init, }; size_t arm_smmu_get_viommu_size(struct device *dev, From fadeedd7cfc5d73d33fa3d7ac54b9b27aabd09d2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 12 Jul 2026 07:23:55 -1000 Subject: [PATCH 020/501] sched/psi: Create the psimon kthread outside of cgroup_mutex a5b98009f16d ("sched/psi: fix race between file release and pressure write") made pressure_write() hold cgroup_mutex across psi_trigger_create(), which forks the psimon kthread for the first rtpoll trigger. As kthread creation depends on the whole fork path, the commit inadvertently created a lot of unwanted locking dependencies from cgroup_mutex. sched_ext got hit by one: its enable path blocks forks and then grabs cgroup_mutex, so a pressure write racing a scheduler enable deadlocks, with every other fork piling up behind. Fix it by splitting trigger creation so that the worker is forked with cgroup_mutex dropped and the kernfs active reference left broken. The latter matters because rmdir and cgroup.pressure writes drain active references under cgroup_mutex. Publishing the trigger last keeps error reporting synchronous and preserves the of->priv lifetime rules. The trigger registered in the first stage pins the group's rtpoll machinery across the unlocked window, leaving only creation races to resolve. The catch-up poll on installation covers scheduling attempts dropped while there was no worker. v2: Retagged sched/psi (was cgroup). Fixes: a5b98009f16d ("sched/psi: fix race between file release and pressure write") Cc: stable@vger.kernel.org Cc: Edward Adam Davis Cc: Chen Ridong Reported-by: Matt Fleming Closes: https://lore.kernel.org/all/20260710100441.2653477-1-matt@readmodwrite.com/ Signed-off-by: Tejun Heo Acked-by: Johannes Weiner Tested-by: Matt Fleming Acked-by: Suren Baghdasaryan --- include/linux/psi.h | 4 ++- kernel/cgroup/cgroup.c | 23 +++++++++++++- kernel/sched/psi.c | 69 ++++++++++++++++++++++++++++++++---------- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/include/linux/psi.h b/include/linux/psi.h index e0745873e3f2..7966e3ac03b9 100644 --- a/include/linux/psi.h +++ b/include/linux/psi.h @@ -25,7 +25,9 @@ void psi_memstall_leave(unsigned long *flags); int psi_show(struct seq_file *s, struct psi_group *group, enum psi_res res); struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, enum psi_res res, struct file *file, - struct kernfs_open_file *of); + struct kernfs_open_file *of, + bool *need_rtpoll_worker); +int psi_trigger_create_rtpoll_worker(struct psi_group *group); void psi_trigger_destroy(struct psi_trigger *t); __poll_t psi_trigger_poll(void **trigger_ptr, struct file *file, diff --git a/kernel/cgroup/cgroup.c b/kernel/cgroup/cgroup.c index 38f8d9df8fbc..b5b461d4418b 100644 --- a/kernel/cgroup/cgroup.c +++ b/kernel/cgroup/cgroup.c @@ -3996,6 +3996,7 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf, struct psi_trigger *new; struct cgroup *cgrp; struct psi_group *psi; + bool need_rtpoll_worker; ssize_t ret = 0; cgrp = cgroup_kn_lock_live(of->kn, false); @@ -4015,12 +4016,32 @@ static ssize_t pressure_write(struct kernfs_open_file *of, char *buf, } psi = cgroup_psi(cgrp); - new = psi_trigger_create(psi, buf, res, of->file, of); + new = psi_trigger_create(psi, buf, res, of->file, of, + &need_rtpoll_worker); if (IS_ERR(new)) { ret = PTR_ERR(new); goto out_unlock; } + /* + * The worker fork must run with neither cgroup_mutex nor the file's + * kernfs active reference held. The latter is broken since + * cgroup_kn_lock_live(). @of->priv may be released while unlocked, so + * recheck before publishing @new. + */ + if (need_rtpoll_worker) { + cgroup_unlock(); + ret = psi_trigger_create_rtpoll_worker(psi); + cgroup_lock(); + + if (!ret && !of->priv) + ret = -ENODEV; + if (ret) { + psi_trigger_destroy(new); + goto out_unlock; + } + } + smp_store_release(&ctx->psi.trigger, new); out_unlock: diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index d9c9d9480a45..565ec7b80743 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -1292,9 +1292,44 @@ int psi_show(struct seq_file *m, struct psi_group *group, enum psi_res res) return 0; } +/* + * Create @group's rtpoll worker after psi_trigger_create() reported the need + * for one. kthread creation depends on the whole fork path and we don't want + * all of that nested inside cgroup_mutex, so the caller must drop it and any + * other lock that forks can wait behind. If two callers race, the loser stops + * its never-woken kthread. + */ +int psi_trigger_create_rtpoll_worker(struct psi_group *group) +{ + struct task_struct *task; + + task = kthread_create(psi_rtpoll_worker, group, "psimon"); + if (IS_ERR(task)) + return PTR_ERR(task); + + scoped_guard(mutex, &group->rtpoll_trigger_lock) { + if (!rcu_access_pointer(group->rtpoll_task)) { + atomic_set(&group->rtpoll_wakeup, 0); + wake_up_process(task); + rcu_assign_pointer(group->rtpoll_task, task); + + /* + * Poll once to catch up on scheduling attempts dropped + * while there was no rtpoll worker. + */ + psi_schedule_rtpoll_work(group, 1, true); + return 0; + } + } + + kthread_stop(task); + return 0; +} + struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, enum psi_res res, struct file *file, - struct kernfs_open_file *of) + struct kernfs_open_file *of, + bool *need_rtpoll_worker) { struct psi_trigger *t; enum psi_states state; @@ -1302,6 +1337,8 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, bool privileged; u32 window_us; + *need_rtpoll_worker = false; + if (static_branch_likely(&psi_disabled)) return ERR_PTR(-EOPNOTSUPP); @@ -1362,26 +1399,14 @@ struct psi_trigger *psi_trigger_create(struct psi_group *group, char *buf, if (privileged) { mutex_lock(&group->rtpoll_trigger_lock); - if (!rcu_access_pointer(group->rtpoll_task)) { - struct task_struct *task; - - task = kthread_create(psi_rtpoll_worker, group, "psimon"); - if (IS_ERR(task)) { - kfree(t); - mutex_unlock(&group->rtpoll_trigger_lock); - return ERR_CAST(task); - } - atomic_set(&group->rtpoll_wakeup, 0); - wake_up_process(task); - rcu_assign_pointer(group->rtpoll_task, task); - } - list_add(&t->node, &group->rtpoll_triggers); group->rtpoll_min_period = min(group->rtpoll_min_period, div_u64(t->win.size, UPDATES_PER_WINDOW)); group->rtpoll_nr_triggers[t->state]++; group->rtpoll_states |= (1 << t->state); + *need_rtpoll_worker = !rcu_access_pointer(group->rtpoll_task); + mutex_unlock(&group->rtpoll_trigger_lock); } else { mutex_lock(&group->avgs_lock); @@ -1541,6 +1566,8 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, size_t buf_size; struct seq_file *seq; struct psi_trigger *new; + bool need_rtpoll_worker; + int ret; if (static_branch_likely(&psi_disabled)) return -EOPNOTSUPP; @@ -1565,12 +1592,22 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf, return -EBUSY; } - new = psi_trigger_create(&psi_system, buf, res, file, NULL); + new = psi_trigger_create(&psi_system, buf, res, file, NULL, + &need_rtpoll_worker); if (IS_ERR(new)) { mutex_unlock(&seq->lock); return PTR_ERR(new); } + if (need_rtpoll_worker) { + ret = psi_trigger_create_rtpoll_worker(&psi_system); + if (ret) { + psi_trigger_destroy(new); + mutex_unlock(&seq->lock); + return ret; + } + } + smp_store_release(&seq->private, new); mutex_unlock(&seq->lock); From 5457025fa8ca3c0d2732109513de839e3e797190 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 12 Jul 2026 07:23:55 -1000 Subject: [PATCH 021/501] sched/psi: Shut down rtpoll_timer in psi_cgroup_free() psi_schedule_rtpoll_work() is called locklessly from the scheduler hotpath and can race psi_trigger_destroy() taking down the last rtpoll trigger under rtpoll_trigger_lock: psi_schedule_rtpoll_work() psi_trigger_destroy() rcu_read_lock(); task = rcu_dereference(rtpoll_task); rcu_assign_pointer(rtpoll_task, NULL); timer_delete(&rtpoll_timer); mod_timer(&rtpoll_timer, ...); rcu_read_unlock(); synchronize_rcu(); kthread_stop(task_to_destroy); The group can then be freed with the re-armed timer still pending, and poll_timer_fn() runs on freed memory. 461daba06bdc ("psi: eliminate kthread_worker from psi trigger scheduling mechanism") deleted the timer synchronously after the synchronize_rcu(), which prevented this but raced trigger creation instead: the deletion could cancel the timer that a new trigger set armed during the grace period and, as creation also reinitialized the timer at the time, corrupt it. 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") moved the initialization into group_init() and the deletion into the locked section, trading the creation races for the window above. Neither placement in the destruction path works. A pending timer firing while the group is alive is harmless though. poll_timer_fn() just wakes the rtpoll waitqueue and doesn't re-arm itself. Bind the timer to the group's lifetime instead and shut it down in psi_cgroup_free(). Nothing can arm it by then. timer_shutdown_sync() because the timer is never armed again. Fixes: 8f91efd870ea ("psi: Fix race between psi_trigger_create/destroy") Cc: stable@vger.kernel.org # v5.10+ Reported-by: Sashiko AI Closes: https://lore.kernel.org/all/20260711000434.36C4A1F000E9@smtp.kernel.org/ Signed-off-by: Tejun Heo Acked-by: Johannes Weiner Tested-by: Matt Fleming Acked-by: Suren Baghdasaryan --- kernel/sched/psi.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c index 565ec7b80743..e2e825dcd088 100644 --- a/kernel/sched/psi.c +++ b/kernel/sched/psi.c @@ -1134,6 +1134,12 @@ void psi_cgroup_free(struct cgroup *cgroup) return; cancel_delayed_work_sync(&cgroup->psi->avgs_work); + /* + * A psi_schedule_rtpoll_work() call racing the last trigger's + * destruction may have re-armed the timer after psi_trigger_destroy() + * deleted it. Spurious firing while the group is alive is harmless. + */ + timer_shutdown_sync(&cgroup->psi->rtpoll_timer); free_percpu(cgroup->psi->pcpu); /* All triggers must be removed by now */ WARN_ONCE(cgroup->psi->rtpoll_states, "psi: trigger leak\n"); From b4bee12ebeccfcd5959ace2c3a4af08f5a917d4c Mon Sep 17 00:00:00 2001 From: Soeren Moch Date: Fri, 17 Jul 2026 11:32:03 +0800 Subject: [PATCH 022/501] PCI: imx6: Keep i.MX6 Root Port MSI/MSI-X Capabilities with iMSI-RX to work around hardware bug On some NXP chipsets, disabling Root Port MSI/MSI-X Capabilities blocks MSIs originating from Endpoints from reaching the iMSI-RX controller. To address this hardware bug, commit 3a4e8302e72f ("PCI: imx6: Keep Root Port MSI capability with iMSI-RX to work around hardware bug") preserves Root Port MSI and MSI-X Capabilities on i.MX7D, i.MX8MM, and i.MX8MQ when iMSI-RX is in use. The same applies to i.MX6Q, i.MX6QP, and i.MX6SX, so preserve Root Port MSI/MSI-X Capabilities there as well. Note that preserving these Capabilities means Root Port-originated MSIs such as AER and PME won't be received due to separate hardware limitations. Users may need to use workarounds such as passing the 'pcie_pme=nomsi' command-line parameter. Fixes: f5cd8a929c825 ("PCI: dwc: Remove MSI/MSIX capability for Root Port if iMSI-RX is used as MSI controller") Signed-off-by: Soeren Moch Signed-off-by: Richard Zhu Signed-off-by: Bjorn Helgaas Reviewed-by: Frank Li Reviewed-by: Manivannan Sadhasivam Acked-by: Richard Zhu Cc: stable@vger.kernel.org # 7.0+ Link: https://patch.msgid.link/20260717033203.2965045-1-hongxing.zhu@oss.nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 98e1db751132..dba27eac6bff 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1995,7 +1995,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_BROKEN_SUSPEND | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, @@ -2011,7 +2012,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_SKIP_L23_READY | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, .ltssm_mask = IMX6Q_GPR12_PCIE_CTL_2, @@ -2026,7 +2028,8 @@ static const struct imx_pcie_drvdata drvdata[] = { .flags = IMX_PCIE_FLAG_IMX_PHY | IMX_PCIE_FLAG_SPEED_CHANGE_WORKAROUND | IMX_PCIE_FLAG_SKIP_L23_READY | - IMX_PCIE_FLAG_SUPPORTS_SUSPEND, + IMX_PCIE_FLAG_SUPPORTS_SUSPEND | + IMX_PCIE_FLAG_KEEP_MSI_CAP, .dbi_length = 0x200, .gpr = "fsl,imx6q-iomuxc-gpr", .ltssm_off = IOMUXC_GPR12, From 523307b8516fc740895238af8473aa0630b3e088 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Sat, 11 Jul 2026 16:36:55 +0900 Subject: [PATCH 023/501] ntfs: preserve RECALL_ON_OPEN on WSL special-file reparse points When creating a WSL special file (socket, fifo, character or block device), __ntfs_create() sets FILE_ATTRIBUTE_RECALL_ON_OPEN in ni->flags as valid_reparse_data() requires for these tags. This flag is intentionally absent from $FILE_NAME, so the subsequent reload ni->flags = fn->file_attributes; drops it from ni->flags, the authoritative copy written back to $STANDARD_INFORMATION. The on-disk file_attributes becomes 0x00000404 instead of 0x00040404, and after a remount valid_reparse_data() rejects the reparse point while fsck reports "$REPARSE_POINT data is corrupted". Preserve the RECALL_ON_OPEN bit across the reload. Symlinks do not set that bit, so they are unaffected. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: Namjae Jeon --- fs/ntfs/namei.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/ntfs/namei.c b/fs/ntfs/namei.c index 5ff25e9aaa32..cd403b1d99ee 100644 --- a/fs/ntfs/namei.c +++ b/fs/ntfs/namei.c @@ -685,7 +685,8 @@ static struct ntfs_inode *__ntfs_create(struct mnt_idmap *idmap, struct inode *d mutex_unlock(&dir_ni->mrec_lock); mutex_unlock(&ni->mrec_lock); - ni->flags = fn->file_attributes; + ni->flags = fn->file_attributes | + (ni->flags & FILE_ATTRIBUTE_RECALL_ON_OPEN); /* Set the sequence number. */ vi->i_generation = ni->seq_no; set_nlink(vi, 1); From ef19a9cf037957fe3a35df8355c76ff0a63a0436 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 12 Jul 2026 15:31:37 -0700 Subject: [PATCH 024/501] ata: sata_mv: accept 1 or 2 resources in platform probe Board files in arch/arm/plat-orion, arch/arm/mach-dove, arch/arm/mach-mv78xx0 and arch/arm/mach-orion5x still register the "sata_mv" device with two resources (IORESOURCE_MEM plus IORESOURCE_IRQ). Those devices are rejected with -EINVAL, so SATA no longer probes on legacy Marvell Orion/Kirkwood-style boards. Accept both 1 resource (DT, IRQ fetched via platform_get_irq()) and 2 resources (legacy, IRQ supplied as a second resource) so both probing paths work. Fixes: b3b2bec9646e ("ata: sata_mv: Fixes expected number of resources now IRQs are gone") Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Signed-off-by: Damien Le Moal --- drivers/ata/sata_mv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 41647a56a9f4..365454390d7e 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -4027,7 +4027,7 @@ static int mv_platform_probe(struct platform_device *pdev) /* * Simple resource validation .. */ - if (unlikely(pdev->num_resources != 1)) { + if (unlikely(pdev->num_resources != 1 && pdev->num_resources != 2)) { dev_err(&pdev->dev, "invalid number of resources\n"); return -EINVAL; } From 4d99a91574c420decab56cc880fad0dc15b8a7a3 Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Fri, 17 Jul 2026 23:55:26 +0530 Subject: [PATCH 025/501] ata: ahci_ceva: fix error paths in ceva_ahci_platform_enable_resources() On phy_init() failure the error path fallsthrough to disable_rsts, which deasserts the controller reset and then enters disable_phys calling phy_power_off() on PHYs that were never powered on. That corrupts the PHY power_count and triggers an extra runtime PM put. Use a separate exit_phys path that unwinds with phy_exit() only and falls through to disable_clks while the controller remains in reset. Reserve phy_power_off() for the phy_power_on() failure path only, and skip masked-out ports in both unwind loops. On phy_power_on() failure re-assert the controller reset before disabling clocks and regulators, matching the teardown order used by ahci_platform_enable_resources() and ahci_platform_disable_resources(). Fixes: 26c8404e162b ("ata: ahci_ceva: fix error handling for Xilinx GT PHY support") Signed-off-by: Radhey Shyam Pandey Signed-off-by: Damien Le Moal --- drivers/ata/ahci_ceva.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/ata/ahci_ceva.c b/drivers/ata/ahci_ceva.c index 2d6a08c23d6a..2961e53288f4 100644 --- a/drivers/ata/ahci_ceva.c +++ b/drivers/ata/ahci_ceva.c @@ -211,7 +211,7 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) rc = phy_init(hpriv->phys[i]); if (rc) - goto disable_rsts; + goto exit_phys; } /* De-assert the controller reset */ @@ -230,14 +230,24 @@ static int ceva_ahci_platform_enable_resources(struct ahci_host_priv *hpriv) return 0; -disable_rsts: - ahci_platform_deassert_rsts(hpriv); - disable_phys: while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + phy_power_off(hpriv->phys[i]); phy_exit(hpriv->phys[i]); } + ahci_platform_assert_rsts(hpriv); + goto disable_clks; + +exit_phys: + while (--i >= 0) { + if (ahci_ignore_port(hpriv, i)) + continue; + + phy_exit(hpriv->phys[i]); + } disable_clks: ahci_platform_disable_clks(hpriv); From 477869bfafea65492d23de62c1b5208147c09dd2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:43:25 -1000 Subject: [PATCH 026/501] sched_ext: Reject setting disallow from init_task outside the enable path The p->scx.disallow revert assumes the root enable path, where the switching loop reads the reverted policy right afterwards and leaves the task off SCX. The sub-scheduler disable path also reaches it when re-initializing the returned tasks on a root parent. Nothing reads the policy there: the task is enabled on root anyway and keeps running on the ext class with a silently rewritten policy. Kill the sched instead, matching the fork and non-root branches, and update the disallow documentation, which equated !fork with the load path and pointed at a stale debugfs path for nr_rejected. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- include/linux/sched/ext.h | 10 +++++----- kernel/sched/ext/ext.c | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/sched/ext.h b/include/linux/sched/ext.h index 20b2343aa344..87e353f7e011 100644 --- a/include/linux/sched/ext.h +++ b/include/linux/sched/ext.h @@ -244,11 +244,11 @@ struct sched_ext_entity { * to %SCHED_EXT with -%EACCES. * * Can be set from ops.init_task() while the BPF scheduler is being - * loaded (!scx_init_task_args->fork). If set and the task's policy is - * already %SCHED_EXT, the task's policy is rejected and forcefully - * reverted to %SCHED_NORMAL. The number of such events are reported - * through /sys/kernel/debug/sched_ext::nr_rejected. Setting this flag - * during fork is not allowed. + * loaded. If set and the task's policy is already %SCHED_EXT, the + * task's policy is rejected and forcefully reverted to %SCHED_NORMAL. + * The number of such events are reported through + * /sys/kernel/sched_ext/nr_rejected. Setting this flag from any other + * ops.init_task() invocation, such as during fork, fails the scheduler. */ bool disallow; /* reject switching into SCX */ diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index e3fa7b2fac9d..46f135bddd46 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -3606,6 +3606,9 @@ static int __scx_init_task(struct scx_sched *sch, struct task_struct *p, bool fo } else if (unlikely(fork)) { scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] during fork", p->comm, p->pid); + } else if (unlikely(scx_enable_state() != SCX_ENABLING)) { + scx_error(sch, "ops.init_task() set task->scx.disallow for %s[%d] outside the enable path", + p->comm, p->pid); } else { struct rq *rq; struct rq_flags rf; From 5f8b69642d18e1f3e11996707842ac530444e959 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:44:02 -1000 Subject: [PATCH 027/501] sched_ext: Take cgroup_lock() first in scx_cgroup_lock() scx_cgroup_lock() write-locks scx_cgroup_ops_rwsem and then takes cgroup_lock(), which can deadlock through kernfs: scx enable/disable cgroup rmdir cpu.weight write ------------------ ------------ ---------------- cgroup_lock() percpu_down_write(rwsem) cgroup_lock() kernfs_get_active() percpu_down_read(rwsem) kernfs_drain() The enable path waits for the rmdir to release cgroup_mutex. The rmdir, deactivating the cpu controller's files, waits in kernfs_drain() for the write's active reference. The write, in scx_group_set_weight(), waits for the rwsem behind the pending writer. Take cgroup_lock() first. The set_* paths take no cgroup locks inside the read side, so a pending write-lock then only waits for read sections that always run to completion, and no dependency from the rwsem back to cgroup_mutex remains. Fixes: a5bd6ba30b33 ("sched_ext: Use cgroup_lock/unlock() to synchronize against cgroup operations") Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 46f135bddd46..58898cd0727b 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -4580,20 +4580,25 @@ static struct cgroup *root_cgroup(void) return &cgrp_dfl_root.cgrp; } +/* + * cgroup_lock() must nest outside the rwsem write side: a writer waiting + * for cgroup_mutex deadlocks with cgroup teardown, which holds it while + * draining a set_* file write blocked on the rwsem behind the writer. + */ static void scx_cgroup_lock(void) { + cgroup_lock(); #ifdef CONFIG_EXT_GROUP_SCHED percpu_down_write(&scx_cgroup_ops_rwsem); #endif - cgroup_lock(); } static void scx_cgroup_unlock(void) { - cgroup_unlock(); #ifdef CONFIG_EXT_GROUP_SCHED percpu_up_write(&scx_cgroup_ops_rwsem); #endif + cgroup_unlock(); } #else /* CONFIG_EXT_GROUP_SCHED || CONFIG_EXT_SUB_SCHED */ static inline struct cgroup *root_cgroup(void) { return NULL; } From 8c13364db9c9a43ed286f3a8d0fb9477b1adc43c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:45:23 -1000 Subject: [PATCH 028/501] sched_ext: Skip sub-disable teardown for never-linked sub-schedulers A sub-scheduler enable can fail before scx_link_sched() links the sched into the hierarchy, e.g. when the parent is already being disabled, and cleanup still runs the full scx_sub_disable(). That is racy against root disable: drain_descendants() is the only ordering between a sub's disable-time task walk and root disable's all-task teardown, and an unlinked sub is invisible to it. Root's teardown can thus run between the never-linked sub's drain and its walk, exiting every task to no scheduler. The walk then trips the membership WARN and re-homes the exited tasks onto the dying hierarchy, a use-after-free. Skip the cgroup ownership reset and the task walk if @sch was never linked, indicated by the empty ->sibling as unlinking only happens later in the same function. The membership WARN remains valid: a linked sub is always waited on by an ancestor's drain. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 58898cd0727b..98dd7df88db4 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -5937,6 +5937,15 @@ static void scx_sub_disable(struct scx_sched *sch) percpu_down_write(&scx_fork_rwsem); scx_cgroup_lock(); + /* + * An enable that failed before scx_link_sched() never owned a cgroup or + * task and won't be waited on by an ancestor's drain_descendants(). + * Nothing to reparent and walking the tasks can misbehave as the task + * ownership invariant (either owned by self or parent) does not hold. + */ + if (list_empty(&sch->sibling)) + goto dump; + set_cgroup_sched(sch_cgroup(sch), parent); scx_task_iter_start(&sti, sch->cgrp); @@ -5949,8 +5958,8 @@ static void scx_sub_disable(struct scx_sched *sch) continue; /* - * By the time control reaches here, all descendant schedulers - * should already have been disabled. + * By the time control reaches here, all linked descendant + * schedulers should have been disabled. */ WARN_ON_ONCE(!scx_task_on_sched(sch, p)); @@ -6017,6 +6026,7 @@ static void scx_sub_disable(struct scx_sched *sch) } scx_task_iter_stop(&sti); +dump: scx_disable_dump(sch); scx_cgroup_unlock(); From 5cdc928598095b2c7d5f265e5f21eadd1634bfbe Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 16 Jul 2026 10:46:04 -1000 Subject: [PATCH 029/501] sched_ext: Don't enable non-ext tasks in the sub-sched task loops Root enable and scx_post_fork() enable a task only if it's on the ext class. Tasks on other classes, possible under an SCX_OPS_SWITCH_PARTIAL root, are left READY and enabled by switching_to_scx() when they switch over. The sub enable-commit pass and the sub-disable re-home loop enable unconditionally, so a fair-class READY task in the subtree becomes ENABLED while not on sched_ext. A later switch to SCHED_EXT then trips the task state validation WARN (ENABLED with the previous state not READY) and calls ops.enable() a second time. Gate scx_enable_task() on the task's class in both loops. Fixes: 337ec00b1d9c ("sched_ext: Implement cgroup sub-sched enabling and disabling") Signed-off-by: Tejun Heo Reviewed-by: Andrea Righi --- kernel/sched/ext/ext.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/kernel/sched/ext/ext.c b/kernel/sched/ext/ext.c index 98dd7df88db4..18183062f751 100644 --- a/kernel/sched/ext/ext.c +++ b/kernel/sched/ext/ext.c @@ -6010,15 +6010,22 @@ static void scx_sub_disable(struct scx_sched *sch) /* * $p is initialized for $parent and still attached to * @sch. Disable and exit for @sch, switch over to - * $parent, override the state to READY to account for - * $p having already been initialized, and then enable. + * $parent and override the state to READY to account + * for $p having already been initialized. */ scx_disable_and_exit_task(sch, p); scx_set_task_state(p, SCX_TASK_INIT_BEGIN); scx_set_task_state(p, SCX_TASK_INIT); scx_set_task_sched(p, parent); scx_set_task_state(p, SCX_TASK_READY); - scx_enable_task(parent, p); + + /* + * A task on a non-ext class, possible under an + * %SCX_OPS_SWITCH_PARTIAL root, stays READY and is + * enabled by switching_to_scx() if it switches over. + */ + if (p->sched_class == &ext_sched_class) + scx_enable_task(parent, p); } task_rq_unlock(rq, p, &rf); @@ -7726,10 +7733,14 @@ static void scx_sub_enable_workfn(struct kthread_work *work) /* * $p is now only initialized for @sch and READY, which - * is what we want. Assign it to @sch and enable. + * is what we want. Assign it to @sch and, if it's on + * the ext class, enable. A non-ext task, possible under + * an %SCX_OPS_SWITCH_PARTIAL root, stays READY and is + * enabled by switching_to_scx() if it switches over. */ scx_set_task_sched(p, sch); - scx_enable_task(sch, p); + if (p->sched_class == &ext_sched_class) + scx_enable_task(sch, p); p->scx.flags &= ~SCX_TASK_SUB_INIT; } From 8e957e4907c58e9ca944f98799524f2bbb9cf68a Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:40 +0800 Subject: [PATCH 030/501] ASoC: tas2562: fix DVC coefficient write order The TAS2562 applies the 32-bit digital volume coefficient to the playback path when the last byte, DVC_CFG4 (book 0 page 2 reg 0x0F), is written. tas2562_volume_control_put() wrote DVC_CFG4 first and DVC_CFG1 (the MSB) last, so every volume change latched a value made of the previous coefficient's upper three bytes combined with the new LSB; the remaining bytes only took effect on the next volume change. In practice the control was unusable: the first setting after power-on always played at roughly 0 dB no matter what value was requested (the chip's default upper bytes were still latched), and most subsequent changes muted the output entirely or produced a distorted, over-unity gain. Verified on a TAS2562 (ADLINK OSM-520 / MT8189 board) by tracing the I2C writes with ftrace and by writing the same coefficients manually in both byte orders: written MSB-first the register block behaves exactly as the driver expects, LSB-first reproduces the broken behaviour. Write the bytes MSB first with DVC_CFG4 last so the complete new coefficient is latched atomically. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-1-072b13901b20@adlinktech.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index e1d62f30418a..ec32ef0afd7e 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -475,20 +475,27 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, u32 reg_val; reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, - (reg_val & 0xff)); - if (ret) - return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, - ((reg_val >> 8) & 0xff)); + /* + * The device applies the 32-bit coefficient to the playback path on + * the write to DVC_CFG4 (the LSB, book 0 page 2 reg 0x0F), so the + * bytes must be written MSB first and DVC_CFG4 last. Writing CFG4 + * first latches a mix of the previous coefficient's upper bytes and + * the new LSB instead of the requested value. + */ + ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, + ((reg_val >> 24) & 0xff)); if (ret) return ret; ret = snd_soc_component_write(component, TAS2562_DVC_CFG2, ((reg_val >> 16) & 0xff)); if (ret) return ret; - ret = snd_soc_component_write(component, TAS2562_DVC_CFG1, - ((reg_val >> 24) & 0xff)); + ret = snd_soc_component_write(component, TAS2562_DVC_CFG3, + ((reg_val >> 8) & 0xff)); + if (ret) + return ret; + ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, + (reg_val & 0xff)); if (ret) return ret; From bdb0fd6de403fcea7b85dc9d38f0a571583ebe80 Mon Sep 17 00:00:00 2001 From: Haidar Lee Date: Wed, 15 Jul 2026 14:04:41 +0800 Subject: [PATCH 031/501] ASoC: tas2562: fix broken entries in the volume lookup table The float_vol_db_lookup table is supposed to hold round(10^(dB/20) * 2^30) for every 2 dB step from -110 dB to 0 dB, which is 56 entries, but it only has 55: the -90 dB entry duplicates the -92 dB value (0x0000695b) and the -20 dB entry (0x06666666) is missing altogether. As a result every step between -90 dB and -22 dB is off by 2 dB, and the control's maximum raw value of 110 indexes one element past the end of the array. Replace the duplicated -90 dB entry with the correct value 0x000084a3 and add the missing -20 dB entry, bringing the table to the full 56 entries so index 55 (raw value 110, 0 dB) is in range again. Fixes: bf726b1c86f2 ("ASoC: tas2562: Add support for digital volume control") Cc: stable@vger.kernel.org Signed-off-by: Haidar Lee Link: https://patch.msgid.link/20260715-tas2562-dvc-fix-v1-2-072b13901b20@adlinktech.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index ec32ef0afd7e..cdd695c3807e 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -32,15 +32,16 @@ static const unsigned int float_vol_db_lookup[] = { 0x00000d43, 0x000010b2, 0x00001505, 0x00001a67, 0x00002151, 0x000029f1, 0x000034cd, 0x00004279, 0x000053af, 0x0000695b, -0x0000695b, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, +0x000084a3, 0x0000a6fa, 0x0000d236, 0x000108a4, 0x00014d2a, 0x0001a36e, 0x00021008, 0x000298c0, 0x000344df, 0x00041d8f, 0x00052e5a, 0x000685c8, 0x00083621, 0x000a566d, 0x000d03a7, 0x0010624d, 0x0014a050, 0x0019f786, 0x0020b0bc, 0x0029279d, 0x0033cf8d, 0x004139d3, 0x00521d50, 0x00676044, 0x0082248a, 0x00a3d70a, 0x00ce4328, 0x0103ab3d, 0x0146e75d, 0x019b8c27, 0x02061b89, 0x028c423f, 0x03352529, 0x0409c2b0, 0x05156d68, -0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, 0x143d1362, -0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, 0x40000000 +0x06666666, 0x080e9f96, 0x0a24b062, 0x0cc509ab, 0x10137987, +0x143d1362, 0x197a967f, 0x2013739e, 0x28619ae9, 0x32d64617, +0x40000000 }; struct tas2562_data { From 8fb41964f7e4e4207c8999af2056894caa7a252a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:09 +0100 Subject: [PATCH 032/501] ASoC: tas2562: Validate values for volume writes tas2562_volume_control_put() does not do any validation of the control value written by userspace, it uses it to look up a value in a fixed size array which can easily be overflowed and then writes whatever value it gets back to the device. Add validation that we are loading a value we have in the array. Cc: stable@vger.kernel.org Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-1-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index e1d62f30418a..40b7803c68be 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -471,10 +471,14 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, { struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); struct tas2562_data *tas2562 = snd_soc_component_get_drvdata(component); - int ret; + int ret, index; u32 reg_val; - reg_val = float_vol_db_lookup[ucontrol->value.integer.value[0]/2]; + index = ucontrol->value.integer.value[0] / 2; + if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup)) + return -EINVAL; + + reg_val = float_vol_db_lookup[index]; ret = snd_soc_component_write(component, TAS2562_DVC_CFG4, (reg_val & 0xff)); if (ret) From 9a9269dbbf1aba4bf329f770d6c473dc9cfb29e8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:10 +0100 Subject: [PATCH 033/501] ASoC: tas2562: Fix event generation for volume control ALSA put() operations should return 0 for noop updates and 1 if the value of the control changed, this is used by the ALSA core to generate events to userspace. tas2562_volume_control_put() does not implement this, it just writes whatever value userspace wrote to the device and returns 0 regardless of what the previous value was. Fix this by suppressing writes if the value is unchanged and returning 1 if the writes succeed. Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-2-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 40b7803c68be..d3d0aaa0f607 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -474,6 +474,9 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, int ret, index; u32 reg_val; + if (tas2562->volume_lvl == ucontrol->value.integer.value[0]) + return 0; + index = ucontrol->value.integer.value[0] / 2; if (index < 0 || index >= ARRAY_SIZE(float_vol_db_lookup)) return -EINVAL; @@ -498,7 +501,7 @@ static int tas2562_volume_control_put(struct snd_kcontrol *kcontrol, tas2562->volume_lvl = ucontrol->value.integer.value[0]; - return 0; + return 1; } /* Digital Volume Control. From 0 dB to -110 dB in 1 dB steps */ From 58b638e7c4559b35367a25e319adfe91e8f5ebe7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 15 Jul 2026 21:18:11 +0100 Subject: [PATCH 034/501] ASoC: tas2562: Fix default digital volume The tas2562 digital volume is spread over four registers and is implemented as lookups into a table so the driver stores the value for the userspace control in the driver data. This defaults to 0 due to kzalloc() but the register default is 0x40400000 which maps onto something a bit over the largest value defined in the lookup table. While it's not an exact match update the default to the largest value, avoiding user surprise due to a sudden change on first write. Reviewed-by: Cezary Rojewski Link: https://patch.msgid.link/20260715-asoc-tas2562-put-retval-v1-3-97bf467c924e@kernel.org Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index d3d0aaa0f607..e5fba2195a24 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -739,6 +739,8 @@ static int tas2562_probe(struct i2c_client *client) data->client = client; data->dev = &client->dev; data->model_id = (uintptr_t)i2c_get_match_data(client); + /* Register default is 0x40400000, this is closest */ + data->volume_lvl = (ARRAY_SIZE(float_vol_db_lookup) - 1) * 2; tas2562_parse_dt(data); From 8e29ed571788f41d1121e1e515ba8745695a38dc Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Sat, 18 Jul 2026 16:09:49 +0800 Subject: [PATCH 035/501] ASoC: amd: yc: Add DMI quirk for ASUS EXPERTBOOK PM1403CDA Add a DMI quirk for the Asus Vivobook Pro 15 M6500RE fixing the issue where the internal microphone was not detected. https://bugzilla.kernel.org/show_bug.cgi?id=220806 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260718080949.157230-1-zhangheng@kylinos.cn Signed-off-by: Mark Brown --- sound/soc/amd/yc/acp6x-mach.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c index af1dce9c9b8a..2f54d4e30539 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -829,6 +829,14 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_BOARD_NAME, "PM1403CDA"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vivobook_ASUSLaptop M6500RE_M6500RE"), + } + }, + {} }; From 890b4253134f3a39883af7d5bea67af9c494c56d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:57 +0800 Subject: [PATCH 036/501] ASoC: fsl_asrc: fix m2m_init error path to use goto instead of bare return When fsl_asrc_m2m_init() fails in fsl_asrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_get_sync. Use goto err_pm_get_sync to ensure proper cleanup on failure. Fixes: 286d658477a4 ("ASoC: fsl_asrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-2-shengjiu.wang@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_asrc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c index 5fda9b647c70..f23c21032287 100644 --- a/sound/soc/fsl/fsl_asrc.c +++ b/sound/soc/fsl/fsl_asrc.c @@ -1419,7 +1419,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(asrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_get_sync; } return 0; From a54bc0eef90ea760039c14bb7f3b5db42529f84d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 15 Jul 2026 10:47:58 +0800 Subject: [PATCH 037/501] ASoC: fsl_easrc: fix m2m_init error path to use goto instead of bare return When fsl_asrc_m2m_init() fails in fsl_easrc_probe(), the code did a bare return ret, bypassing pm_runtime_disable() in err_pm_disable. Use goto err_pm_disable to ensure proper cleanup on failure. Fixes: b62eaff0650d ("ASoC: fsl_easrc: register m2m platform device") Cc: stable@vger.kernel.org Signed-off-by: Shengjiu Wang Link: https://patch.msgid.link/20260715024758.1252801-3-shengjiu.wang@oss.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 114a6c0b6b73..8535ef844ce0 100644 --- a/sound/soc/fsl/fsl_easrc.c +++ b/sound/soc/fsl/fsl_easrc.c @@ -2269,7 +2269,7 @@ static int fsl_easrc_probe(struct platform_device *pdev) ret = fsl_asrc_m2m_init(easrc); if (ret) { dev_err(&pdev->dev, "failed to init m2m device %d\n", ret); - return ret; + goto err_pm_disable; } return 0; From b7e53968cb8882c2d276429ea8550848a4940874 Mon Sep 17 00:00:00 2001 From: Guanghui Yang <3497809730@qq.com> Date: Sun, 12 Jul 2026 03:42:56 +0000 Subject: [PATCH 038/501] xfs: propagate errors from xfs_rtginode_load xfs_rtginode_ensure() treats every xfs_rtginode_load() error other than -ENOENT as success. This can leave the realtime group inode unset after an I/O, allocation, or corruption error. Growfs then continues as though the inode had been loaded. Only -ENOENT means that the inode needs to be created. Return all other errors to the growfs caller. Fixes: ae897e0bed0f ("xfs: support creating per-RTG files in growfs") Cc: stable@vger.kernel.org Signed-off-by: Guanghui Yang <3497809730@qq.com> Reviewed-by: Christoph Hellwig Reviewed-by: "Darrick J. Wong" Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_rtalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_rtalloc.c b/fs/xfs/xfs_rtalloc.c index 7a3f97686989..84efe5a8fb11 100644 --- a/fs/xfs/xfs_rtalloc.c +++ b/fs/xfs/xfs_rtalloc.c @@ -737,7 +737,7 @@ xfs_rtginode_ensure( xfs_trans_cancel(tp); if (error != -ENOENT) - return 0; + return error; return xfs_rtginode_create(rtg, type, true); } From 361f533a2dce2c2841fe4dc0c9d85a67117edf95 Mon Sep 17 00:00:00 2001 From: Krishna Kurapati Date: Sat, 18 Jul 2026 15:01:31 +0530 Subject: [PATCH 039/501] phy: qcom: m31-eusb2: Fix return value of init call The init call currently returns success irrespective of any failures during repeater init or clock enablement. Return appropriate error value in the init call failure path. Fixes: 9c8504861cc4 ("phy: qcom: Add M31 based eUSB2 PHY driver") Signed-off-by: Krishna Kurapati Link: https://patch.msgid.link/20260718-m31-eusb2-fix-v1-1-8588a1b94d76@oss.qualcomm.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-m31-eusb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index 0bec8657149c..275df38256de 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -221,7 +221,7 @@ static int m31eusb2_phy_init(struct phy *uphy) disable_vreg: regulator_bulk_disable(M31_EUSB_NUM_VREGS, phy->vregs); - return 0; + return ret; } static int m31eusb2_phy_exit(struct phy *uphy) From be2b5b17b7053fee142939076746d26b2d6c9702 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Tue, 14 Jul 2026 19:41:20 +0400 Subject: [PATCH 040/501] phy: rockchip: naneng-combphy: Always configure SSC spread direction Commit 0b31f297557f ("phy: rockchip: naneng-combphy: Consolidate SSC configuration") moved the SSC spread spectrum direction setup into the new rk_combphy_common_cfg_ssc() helper. That helper returns early when the 'rockchip,enable-ssc' property is absent, whereas the equivalent RK3568_PHYREG32 direction writes previously ran unconditionally in the per-type switch statements, independent of whether SSC modulation was actually enabled. As no in-tree board sets 'rockchip,enable-ssc', this changed the behavior at least for USB3 on RK3576, which now fails to bring up the link. USB 2.0 still enumerates, but USB 3.0 does not, and the SuperSpeed root port floods the log every second with: usb usb2-port1: Cannot enable. Maybe the USB cable is bad? This was observed on two different RK3576 devices with a CoreChips SL6341 USB 2.0/3.0 hub connected to the USB DRD controller running in host mode. Perform the SSC direction writes for PCIe/USB3 (and SATA) before the enable_ssc check so that they always run, as they did before the consolidation. Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/CAKTNdwH_ZMQa-97h+tqdsWqXKtorkFF9wHAMn60-8ZGKuze_Mg@mail.gmail.com/ Fixes: 0b31f297557f ("phy: rockchip: naneng-combphy: Consolidate SSC configuration") Signed-off-by: Alexey Charkov Tested-by: Liu Changjie Link: https://patch.msgid.link/20260714-naneng-ssc-fix-v1-1-1c40a58061ae@flipper.net Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-naneng-combphy.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index 2b0f152f5470..7843356a4dd4 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -452,9 +452,6 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign struct device_node *np = priv->dev->of_node; u32 val; - if (!priv->enable_ssc) - return; - /* Set SSC downward spread spectrum for PCIe and USB3 */ if (priv->type == PHY_TYPE_PCIE || priv->type == PHY_TYPE_USB3) { val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); @@ -471,6 +468,9 @@ static void rk_combphy_common_cfg_ssc(struct rockchip_combphy_priv *priv, unsign RK3568_PHYREG32); } + if (!priv->enable_ssc) + return; + /* Enable SSC */ val = readl(priv->mmio + RK3568_PHYREG8); val |= RK3568_PHYREG8_SSC_EN; From 317e21532e6ffa1de026bdbce5ba98e1b70ca5c6 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:09:50 +0530 Subject: [PATCH 041/501] ASoC: max98095: fix missing IS_ERR() before PTR_ERR() on mclk lookup In max98095_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98095->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: e3048c3d2be5 ("ASoC: max98095: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720103950.14474-1-udaykhare77@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98095.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index ced9bd4d94da..130b6694a634 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -1987,8 +1987,9 @@ static int max98095_probe(struct snd_soc_component *component) int ret = 0; max98095->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98095->mclk)) + if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* reset the codec, the DSP core, and disable all interrupts */ max98095_reset(component); From cf616096a0f3a2b60f7d68b6b39674a6867ded9c Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 30 Jun 2026 14:31:00 -0400 Subject: [PATCH 042/501] NFS: Pin the 'struct nfs_server' during a FREE_STATEID call Dan Aloni reports that he was able to hit a use-after-free bug if a FREE_STATEID operation gets delayed for whatever reason. Fix this by bumping the refcount of the 'struct nfs_server' object for the duration of the FREE_STATEID so it doesn't get cleaned up from underneath us while operations are still in flight. Reported-by: Dan Aloni Fixes: 7c1d5fae4a87 ("NFSv4: Convert nfs41_free_stateid to use an asynchronous RPC call") Tested-by: Dan Aloni Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 1360409d8de9..71b6ab863b39 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10364,6 +10364,7 @@ static void nfs41_free_stateid_release(void *calldata) struct nfs_free_stateid_data *data = calldata; struct nfs_client *clp = data->server->nfs_client; + nfs_sb_deactive(data->server->super); nfs_put_client(clp); kfree(calldata); } @@ -10405,6 +10406,10 @@ static int nfs41_free_stateid(struct nfs_server *server, if (!refcount_inc_not_zero(&clp->cl_count)) return -EIO; + if (!nfs_sb_active(server->super)) { + nfs_put_client(clp); + return -EIO; + } nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &task_setup.rpc_client, &msg); From 4aeb63d5ac2dba2a474e7b64d60776d9dd1c6cd2 Mon Sep 17 00:00:00 2001 From: Anna Schumaker Date: Tue, 30 Jun 2026 15:39:45 -0400 Subject: [PATCH 043/501] NFS: Decrement refcounts if allocating nfs_free_stateid_data fails I noticed that we were immediately exiting this function if the allocation fails, leaving the client and server object refcounts bumped. Fix this by creating a common exit point to clean up dangling references. Fixes: 576acc259146 ("nfs4: take a reference on the nfs_client when running FREE_STATEID") Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 71b6ab863b39..7d98e9a98580 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10403,21 +10403,22 @@ static int nfs41_free_stateid(struct nfs_server *server, struct nfs_free_stateid_data *data; struct rpc_task *task; struct nfs_client *clp = server->nfs_client; + int ret = -EIO; if (!refcount_inc_not_zero(&clp->cl_count)) - return -EIO; - if (!nfs_sb_active(server->super)) { - nfs_put_client(clp); - return -EIO; - } + return ret; + if (!nfs_sb_active(server->super)) + goto out_put_clp; nfs4_state_protect(clp, NFS_SP4_MACH_CRED_STATEID, &task_setup.rpc_client, &msg); dprintk("NFS call free_stateid %p\n", stateid); data = kmalloc_obj(*data); - if (!data) - return -ENOMEM; + if (!data) { + ret = -ENOMEM; + goto out_put_server; + } data->server = server; nfs4_stateid_copy(&data->args.stateid, stateid); @@ -10433,6 +10434,11 @@ static int nfs41_free_stateid(struct nfs_server *server, rpc_put_task(task); stateid->type = NFS4_FREED_STATEID_TYPE; return 0; +out_put_server: + nfs_sb_deactive(server->super); +out_put_clp: + nfs_put_client(clp); + return ret; } static void From a792ce0fad61a70793ec565743f11d6ca534de59 Mon Sep 17 00:00:00 2001 From: Uday Khare Date: Mon, 20 Jul 2026 16:12:54 +0530 Subject: [PATCH 044/501] ASoC: max98090: fix missing IS_ERR() before PTR_ERR() on mclk lookup In max98090_probe(), the -EPROBE_DEFER check after devm_clk_get() is broken due to a missing IS_ERR() guard. The code intends to return -EPROBE_DEFER only when the clock lookup fails with that specific error. However, without IS_ERR() the check: if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) is called unconditionally, including when devm_clk_get() succeeds and returns a valid pointer. Calling PTR_ERR() on a valid pointer reinterprets its address as a signed long; the result is arbitrary and is almost never equal to -EPROBE_DEFER, so the check silently does nothing in the success case. When devm_clk_get() fails with any error other than -EPROBE_DEFER the check is also skipped, leaving max98090->mclk holding an error pointer with no indication to the caller. This means a deferred probe will never actually be triggered for this device, and any non-EPROBE_DEFER clock error is silently swallowed with the error pointer left in the mclk field. Fix this by adding the missing IS_ERR() guard around the PTR_ERR() call, matching the pattern already used in the sibling max98088 and wm8960 drivers. Fixes: b10ab7b838bd ("ASoC: max98090: Add master clock handling") Signed-off-by: Uday Khare Link: https://patch.msgid.link/20260720104254.14948-1-udaykhare77@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98090.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index da416329b038..57bd5a1c34c8 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2423,8 +2423,9 @@ static int max98090_probe(struct snd_soc_component *component) dev_dbg(component->dev, "max98090_probe\n"); max98090->mclk = devm_clk_get(component->dev, "mclk"); - if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) - return -EPROBE_DEFER; + if (IS_ERR(max98090->mclk)) + if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER) + return -EPROBE_DEFER; max98090->component = component; From db3dbdfea1b8f38774419c5c2c14e4b81c48708d Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Sun, 14 Jun 2026 22:38:06 +0900 Subject: [PATCH 045/501] of: reserved_mem: prevent OOB when too many dynamic regions are defined On boot, fdt_scan_reserved_mem() saves each dynamically-placed /reserved-memory subnode into a local array of size MAX_RESERVED_REGIONS. If the device tree defines more than MAX_RESERVED_REGIONS dynamically-placed regions, fdt_scan_reserved_mem() writes past the end of the local array. Add a bounds check that logs an error and skips the excess regions, restoring the original behavior. Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed") Signed-off-by: Sang-Heon Jeon Link: https://patch.msgid.link/20260614133807.2165124-2-ekffu200098@gmail.com Signed-off-by: Rob Herring (Arm) --- drivers/of/of_reserved_mem.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 82222bd45ac6..42e3e2d8a2b8 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -359,6 +359,7 @@ int __init fdt_scan_reserved_mem(void) err = __reserved_mem_reserve_reg(child, uname); if (!err) count++; + /* * Save the nodes for the dynamically-placed regions * into an array which will be used for allocation right @@ -366,10 +367,17 @@ int __init fdt_scan_reserved_mem(void) * or marked as no-map. This is done to avoid dynamically * allocating from one of the statically-placed regions. */ - if (err == -ENOENT && of_get_flat_dt_prop(child, "size", NULL)) { - dynamic_nodes[dynamic_nodes_cnt] = child; - dynamic_nodes_cnt++; + if (err != -ENOENT || !of_get_flat_dt_prop(child, "size", NULL)) + continue; + + if (dynamic_nodes_cnt == MAX_RESERVED_REGIONS) { + pr_err("too many defined dynamic regions, skip '%s'\n", + uname); + continue; } + + dynamic_nodes[dynamic_nodes_cnt] = child; + dynamic_nodes_cnt++; } for (int i = 0; i < dynamic_nodes_cnt; i++) { const char *uname; From b056f21a38276ead20353d71d50a52206609d242 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 19 Jun 2026 18:18:21 -0700 Subject: [PATCH 046/501] power: supply: bd71828: add a terminating table border Fix a documentation build error by adding a bottom table border: Documentation/ABI/testing/sysfs-class-power-bd71828:1: ERROR: Malformed table. No bottom table border found. ============ =========================================== 1 automatic adjustment of input current limit 0 no adjustment of input current limit. This helps for more unusual power sources like solar modules. [docutils] Fixes: e92786dd86a2 ("power: supply: bd71828: sysfs for auto input current limitation") Signed-off-by: Randy Dunlap Acked-by: Matti Vaittinen Link: https://patch.msgid.link/20260620011821.3568674-1-rdunlap@infradead.org Signed-off-by: Sebastian Reichel --- Documentation/ABI/testing/sysfs-class-power-bd71828 | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-class-power-bd71828 b/Documentation/ABI/testing/sysfs-class-power-bd71828 index 2d451e1c8336..f9a325e005f3 100644 --- a/Documentation/ABI/testing/sysfs-class-power-bd71828 +++ b/Documentation/ABI/testing/sysfs-class-power-bd71828 @@ -10,3 +10,4 @@ Description: 0 no adjustment of input current limit. This helps for more unusual power sources like solar modules. + ============ =========================================== From 725668c6b6aa3971fe850659102c250d0d676e18 Mon Sep 17 00:00:00 2001 From: Jianing Li Date: Wed, 1 Jul 2026 14:10:42 +0800 Subject: [PATCH 047/501] power: supply: max17040: handle missing status supplier MAX17040 does not report charger state itself, so the driver forwards POWER_SUPPLY_PROP_STATUS to a supplier power supply. If no supplier is registered, power_supply_get_property_from_supplier() returns -ENODEV and leaves the output value untouched. max17040_get_property() currently ignores that error and returns success, so userspace can read an uninitialized status value from the battery power supply. This happens on systems that use the fuel gauge without a charger supplier relationship in firmware. Return POWER_SUPPLY_STATUS_UNKNOWN when no supplier provides STATUS, and propagate other supplier lookup errors. Fixes: f4b782af61ae ("power: max17040: pass status property from supplier") Cc: stable@vger.kernel.org # 6.7+ Signed-off-by: Jianing Li Link: https://patch.msgid.link/20260701061042.1008-1-m13940358460@163.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/max17040_battery.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/power/supply/max17040_battery.c b/drivers/power/supply/max17040_battery.c index e94d53b36aa4..03ac569e185c 100644 --- a/drivers/power/supply/max17040_battery.c +++ b/drivers/power/supply/max17040_battery.c @@ -405,7 +405,11 @@ static int max17040_get_property(struct power_supply *psy, val->intval = chip->low_soc_alert; break; case POWER_SUPPLY_PROP_STATUS: - power_supply_get_property_from_supplier(psy, psp, val); + ret = power_supply_get_property_from_supplier(psy, psp, val); + if (ret == -ENODEV) + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + else if (ret) + return ret; break; case POWER_SUPPLY_PROP_TEMP: if (!chip->channel_temp) From f7b253a6e217f71d754d70c525e9b4c1dcbd4414 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Sun, 12 Jul 2026 00:48:56 +0200 Subject: [PATCH 048/501] power: supply: macsmc: Support macOS 27 SMC firmware The SMC firmware included in macOS 27 changed the size of BCF0 key from 4 to 1 bytes. This key is used for indicating that battery state is critically low. In addition, B0RM key has changed endianness. Reviewed-by: Sven Peter Reviewed-by: Joshua Peisach Reviewed-by: Janne Grunau Cc: stable@vger.kernel.org Fixes: 0ebf821cf6c7 ("power: supply: Add macsmc-power driver for Apple Silicon") Signed-off-by: Sasha Finkelstein Link: https://patch.msgid.link/20260712-gate-power-v4-1-aa59c6583247@chaosmail.tech Signed-off-by: Sebastian Reichel --- drivers/power/supply/macsmc-power.c | 52 +++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/drivers/power/supply/macsmc-power.c b/drivers/power/supply/macsmc-power.c index ced07f71e0a8..ef735f86394e 100644 --- a/drivers/power/supply/macsmc-power.c +++ b/drivers/power/supply/macsmc-power.c @@ -86,6 +86,11 @@ struct macsmc_power { bool has_ch0i; /* Force discharge (Older firmware) */ bool has_ch0c; /* Inhibit charge (Older firmware) */ bool has_chte; /* Inhibit charge (Modern firmware) */ + /* + * Battery critical key is 1 byte and charge key is little endian + * (Modern firmware) + */ + bool fw_ge_27; u8 num_cells; int nominal_voltage_mv; @@ -273,6 +278,20 @@ static int macsmc_battery_get_date(const char *s, int *out) return 0; } +static int macsmc_battery_read_bcf0(struct macsmc_power *power, u32 *val) +{ + u8 tval = 0; + int ret; + + if (power->fw_ge_27) { + ret = apple_smc_read_u8(power->smc, SMC_KEY(BCF0), &tval); + *val = tval; + return ret; + } + + return apple_smc_read_u32(power->smc, SMC_KEY(BCF0), val); +} + static int macsmc_battery_get_capacity_level(struct macsmc_power *power) { bool flag; @@ -280,7 +299,7 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power) int ret; /* Check for emergency shutdown condition */ - if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val) >= 0 && val) + if (macsmc_battery_read_bcf0(power, &val) >= 0 && val) return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; /* Check AC status for whether we could boot in this state */ @@ -303,6 +322,12 @@ static int macsmc_battery_get_capacity_level(struct macsmc_power *power) return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; } +static s16 macsmc_swap_b0rm(struct macsmc_power *power, s16 b0rm) +{ + /* B0RM was Big Endian, likely pass through from TI gas gauge */ + return power->fw_ge_27 ? b0rm : (s16)swab16(b0rm); +} + static int macsmc_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) @@ -397,8 +422,7 @@ static int macsmc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_CHARGE_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); - /* B0RM is Big Endian, likely pass through from TI gas gauge */ - val->intval = (s16)swab16(vu16) * 1000; + val->intval = macsmc_swap_b0rm(power, vu16) * 1000; break; case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0DC), &vu16); @@ -410,8 +434,7 @@ static int macsmc_battery_get_property(struct power_supply *psy, break; case POWER_SUPPLY_PROP_ENERGY_NOW: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0RM), &vu16); - /* B0RM is Big Endian, likely pass through from TI gas gauge */ - val->intval = (s16)swab16(vu16) * power->nominal_voltage_mv; + val->intval = macsmc_swap_b0rm(power, vu16) * power->nominal_voltage_mv; break; case POWER_SUPPLY_PROP_TEMP: ret = apple_smc_read_u16(power->smc, SMC_KEY(B0AT), &vu16); @@ -577,7 +600,7 @@ static void macsmc_power_critical_work(struct work_struct *wrk) * Check if SMC flagged the battery as empty. * We trigger a graceful shutdown to let the OS save data. */ - if (apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &bcf0) == 0 && bcf0 != 0) { + if (macsmc_battery_read_bcf0(power, &bcf0) == 0 && bcf0 != 0) { power->orderly_shutdown_triggered = true; dev_crit(power->dev, "Battery critical (empty flag set). Triggering orderly shutdown.\n"); orderly_poweroff(true); @@ -616,6 +639,7 @@ static int macsmc_power_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); struct power_supply_config psy_cfg = {}; + struct apple_smc_key_info info; struct macsmc_power *power; bool has_battery = false; bool has_ac_adapter = false; @@ -714,6 +738,20 @@ static int macsmc_power_probe(struct platform_device *pdev) if (apple_smc_key_exists(smc, SMC_KEY(CH0I))) power->has_ch0i = true; + ret = apple_smc_get_key_info(power->smc, SMC_KEY(BCF0), &info); + if (ret) { + dev_err(&pdev->dev, "Failed to determine BCF0 key size\n"); + return ret; + } + if (info.size == 1) + power->fw_ge_27 = true; + else if (info.size == 4) + power->fw_ge_27 = false; + else { + dev_err(&pdev->dev, "Unexpected BCF0 key size %d\n", info.size); + return -EIO; + } + /* Reset "Optimised Battery Charging" flags to default state */ if (power->has_chte) apple_smc_write_u32(smc, SMC_KEY(CHTE), 0); @@ -766,7 +804,7 @@ static int macsmc_power_probe(struct platform_device *pdev) power->nominal_voltage_mv = MACSMC_NOMINAL_CELL_VOLTAGE_MV * power->num_cells; /* Enable critical shutdown notifications by reading status once */ - apple_smc_read_u32(power->smc, SMC_KEY(BCF0), &val32); + macsmc_battery_read_bcf0(power, &val32); psy_cfg.drv_data = power; power->batt = devm_power_supply_register(dev, &power->batt_desc, &psy_cfg); From 8bed376124ab4505b70083a2b91f2c7ef6d51e24 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Fri, 10 Jul 2026 14:22:57 +0900 Subject: [PATCH 049/501] ntfs: harden runlist realloc size calculations Add a shared helper to safely convert runlist element counts to byte sizes using overflow checks, and use it in both ntfs_rl_realloc() and ntfs_rl_realloc_nofail(). Fixes: 11ccc9107dc4 ("ntfs: update runlist handling and cluster allocator") Co-developed-by: Alper Mudar Signed-off-by: Alper Mudar Tested-by: Alper Mudar Signed-off-by: Namjae Jeon --- fs/ntfs/runlist.c | 50 +++++++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/fs/ntfs/runlist.c b/fs/ntfs/runlist.c index cbb6576cf725..8e0fd400e7f7 100644 --- a/fs/ntfs/runlist.c +++ b/fs/ntfs/runlist.c @@ -71,29 +71,46 @@ static inline void ntfs_rl_mc(struct runlist_element *dstbase, int dst, * On success, return a pointer to the newly allocated, or recycled, memory. * On error, return -errno. */ -struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, - int old_size, int new_size) +static inline struct runlist_element *ntfs_rl_realloc_gfp(struct runlist_element *rl, + int old_size, int new_size, gfp_t gfp) { struct runlist_element *new_rl; + size_t new_bytes; + + if (old_size < 0 || new_size < 0) + return ERR_PTR(-EINVAL); - old_size = old_size * sizeof(*rl); - new_size = new_size * sizeof(*rl); if (old_size == new_size) return rl; - new_rl = kvzalloc(new_size, GFP_NOFS); + if (check_mul_overflow(new_size, sizeof(*rl), &new_bytes)) + return ERR_PTR(-EINVAL); + + new_rl = kvzalloc(new_bytes, gfp); if (unlikely(!new_rl)) return ERR_PTR(-ENOMEM); if (likely(rl != NULL)) { - if (unlikely(old_size > new_size)) - old_size = new_size; - memcpy(new_rl, rl, old_size); + size_t old_bytes; + + if (check_mul_overflow(old_size, sizeof(*rl), &old_bytes)) { + kvfree(new_rl); + return ERR_PTR(-EINVAL); + } + if (unlikely(old_bytes > new_bytes)) + old_bytes = new_bytes; + memcpy(new_rl, rl, old_bytes); kvfree(rl); } return new_rl; } +struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, + int old_size, int new_size) +{ + return ntfs_rl_realloc_gfp(rl, old_size, new_size, GFP_NOFS); +} + /* * ntfs_rl_realloc_nofail - Reallocate memory for runlists * @rl: original runlist @@ -118,21 +135,8 @@ struct runlist_element *ntfs_rl_realloc(struct runlist_element *rl, static inline struct runlist_element *ntfs_rl_realloc_nofail(struct runlist_element *rl, int old_size, int new_size) { - struct runlist_element *new_rl; - - old_size = old_size * sizeof(*rl); - new_size = new_size * sizeof(*rl); - if (old_size == new_size) - return rl; - - new_rl = kvmalloc(new_size, GFP_NOFS | __GFP_NOFAIL); - if (likely(rl != NULL)) { - if (unlikely(old_size > new_size)) - old_size = new_size; - memcpy(new_rl, rl, old_size); - kvfree(rl); - } - return new_rl; + return ntfs_rl_realloc_gfp(rl, old_size, new_size, + GFP_NOFS | __GFP_NOFAIL); } /* From 4e646ecd44759e552b0b9ccd995f3f608daab414 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 13 Jul 2026 16:49:57 +0900 Subject: [PATCH 050/501] ntfs: drop stale page-cache when shrinking a non-resident attr ntfs_non_resident_attr_shrink() shrinks attribute sizes but fails to trim the page cache. This leaves orphaned dirty folios beyond the new end of the attribute, leading to writeback failures (-ENOENT), data loss, and $EA chain corruption. Fix this by truncating the page cache to the new size immediately after updating the sizes, preventing writeback from flushing out-of-range folios. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Signed-off-by: Namjae Jeon --- fs/ntfs/attrib.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 239b7bcbaedf..58f32aac5f61 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -4293,6 +4293,16 @@ static int ntfs_non_resident_attr_shrink(struct ntfs_inode *ni, const s64 newsiz ni->initialized_size = newsize; ctx->attr->data.non_resident.initialized_size = cpu_to_le64(newsize); } + + /* + * Drop any page-cache folios that now lie beyond the shrunk + * attribute. The clusters backing them have just been freed and the + * runlist truncated, so leaving stale dirty folios around makes a + * later writeback map a vcn past the new allocation, which fails with + * -ENOENT and loses the write. + */ + truncate_inode_pages(VFS_I(ni)->i_mapping, newsize); + /* Update data size in the index. */ if (ni->type == AT_DATA && ni->name == AT_UNNAMED) NInoSetFileNameDirty(ni); From fafb66e5903c2bcfc7b7e259042a8282f18a6faa Mon Sep 17 00:00:00 2001 From: Tomas Glozar Date: Mon, 13 Jul 2026 16:10:47 +0200 Subject: [PATCH 051/501] rtla/timerlat_top: Fix on-threshold actions firing on signal A bug was reported when rtla-timerlat-top tool performs on-threshold actions, even though no threshold was hit. This is reproduced even if no threshold is set at all: $ rtla timerlat top -q -c 0 --on-threshold shell,command='echo BAD' BAD Timer Latency ... The bug is due to incorrect logic in timerlat_top_bpf_main_loop(). The loop uses timerlat_bpf_wait(), the return values of which are: - > 0 (number of ringbuffer entries): at least 1 CPU hit threshold - = 0: time out - < 0: wait was interrupted by a signal Commit 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval") changed the condition for "threshold hit" from "wait_reval == 1" (exactly 1 CPU hit threshold) to "wait_retval != 0", to fix a race where multiple CPUs hit the threshold at the same time. That also made it incorrectly include a signal (< 0), coming from either duration expired (SIGALRM) or user interrupt (SIGINT). Check for wait_retval greater than zero in the if condition to cover all return values correctly. Fixes: 3138df6f0cd0 ("rtla/timerlat: Exit top main loop on any non-zero wait_retval") Reported-by: Attila Fazekas Reviewed-by: Wander Lairson Costa Link: https://lore.kernel.org/r/20260713141047.687877-1-tglozar@redhat.com Signed-off-by: Tomas Glozar --- tools/tracing/rtla/src/timerlat_top.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tracing/rtla/src/timerlat_top.c b/tools/tracing/rtla/src/timerlat_top.c index 18e1071a2e24..6206a0a565ad 100644 --- a/tools/tracing/rtla/src/timerlat_top.c +++ b/tools/tracing/rtla/src/timerlat_top.c @@ -536,7 +536,7 @@ timerlat_top_bpf_main_loop(struct osnoise_tool *tool) if (!params->quiet) timerlat_print_stats(tool); - if (wait_retval != 0) { + if (wait_retval > 0) { /* Stopping requested by tracer */ retval = common_threshold_handler(tool); if (retval) From f7074624bafc44b236ddc7159d962075c3747f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=83=91=E9=B8=BF=E6=BA=90?= Date: Tue, 21 Jul 2026 03:31:58 +0800 Subject: [PATCH 052/501] ASoC: amd: yc: Add MSI Crosshair A16 HX D7WFKG to quirk table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MSI Crosshair A16 HX D7WFKG has an internal digital microphone connected through AMD ACP6x, but the system does not expose the AcpDmicConnected ACPI property. As a result, acp_yc_mach does not bind and no DMIC capture device is created. Add a DMI quirk for this model. This was tested on a system with board MS-15PL, BIOS E15PLAMS.10B, and AMD ACP6x rev 0x62. After applying the quirk, the internal microphone appears as an acp6x DMIC capture device and records correctly. Signed-off-by: 郑鸿源 Reviewed-by: Vijendar Mukunda Link: https://patch.msgid.link/20260721-master-v1-1-6f56d5a54d96@outlook.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 2f54d4e30539..0d93e45b6178 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -521,6 +521,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Vector A16 HX A8WHG"), } }, + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."), + DMI_MATCH(DMI_PRODUCT_NAME, "Crosshair A16 HX D7WFKG"), + } + }, { .driver_data = &acp6x_card, .matches = { From 75952cfc7752c52a2b692b59d34ce160d3edabb2 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Thu, 16 Jul 2026 21:45:27 +0100 Subject: [PATCH 053/501] arm64: dts: broadcom: bcm2712: Remove non-functional EL2 virtual timer Commit d87773de9efe1 ("clocksource/drivers/arm_arch_timer: Default to EL2 virtual timer when running VHE") causes boot to hang on Raspberry Pi 5. The newly-selected EL2 virtual timer does not generate any interrupts, even though the GIC_DIST_ENABLE_SET flag has been confirmed set via readback. It is highly unusual that this timer interrupt is non-operational because this is a standard GIC interrupt corresponding to a standard Cortex-A76 CPU timer. However, Broadcom have confirmed for this SoC: > the interrupt line was never connected in the first place as this was > not identified as being a requirement Remove the corresponding DeviceTree entry. Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/all/ea15cce1-b393-43f6-8d58-3d6f90f0c0cd@samsung.com/ Fixes: faa3381267d0 ("arm64: dts: broadcom: Add minimal support for Raspberry Pi 5") Signed-off-by: Daniel Drake Link: https://lore.kernel.org/r/20260716-bcm2712-el2-v2-1-e708f7fb42fa@reactivated.net Signed-off-by: Florian Fainelli --- arch/arm64/boot/dts/broadcom/bcm2712.dtsi | 2 -- 1 file changed, 2 deletions(-) diff --git a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi index 761c59d90ffc..09ff5e9959d3 100644 --- a/arch/arm64/boot/dts/broadcom/bcm2712.dtsi +++ b/arch/arm64/boot/dts/broadcom/bcm2712.dtsi @@ -678,8 +678,6 @@ IRQ_TYPE_LEVEL_LOW)>, , , - ; }; From e4779e2a16d600892aaf743438f6ce8cc4eb3c4c Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:30 +0530 Subject: [PATCH 054/501] phy: zynqmp: fix clock error handling in xpsgtr_phy_init() Propagate clk_prepare_enable() failures to the caller instead of returning success, and disable the reference clock on initialization error paths to avoid leaking clock references when phy_exit() is not called. Fixes: 25d700833513 ("phy: xilinx: phy-zynqmp: dynamic clock support for power-save") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index fe6b4925d166..c8230f2bda62 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -658,12 +658,13 @@ static int xpsgtr_phy_init(struct phy *phy) { struct xpsgtr_phy *gtr_phy = phy_get_drvdata(phy); struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - int ret = 0; + int ret; mutex_lock(>r_dev->gtr_mutex); /* Configure and enable the clock when peripheral phy_init call */ - if (clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk])) + ret = clk_prepare_enable(gtr_dev->clk[gtr_phy->refclk]); + if (ret) goto out; /* Skip initialization if not required. */ @@ -673,7 +674,7 @@ static int xpsgtr_phy_init(struct phy *phy) if (gtr_dev->tx_term_fix) { ret = xpsgtr_phy_tx_term_fix(gtr_phy); if (ret < 0) - goto out; + goto out_disable_clk; gtr_dev->tx_term_fix = false; } @@ -687,7 +688,7 @@ static int xpsgtr_phy_init(struct phy *phy) */ ret = xpsgtr_configure_pll(gtr_phy); if (ret) - goto out; + goto out_disable_clk; xpsgtr_lane_set_protocol(gtr_phy); @@ -705,6 +706,10 @@ static int xpsgtr_phy_init(struct phy *phy) break; } + goto out; + +out_disable_clk: + clk_disable_unprepare(gtr_dev->clk[gtr_phy->refclk]); out: mutex_unlock(>r_dev->gtr_mutex); return ret; From f3506e15cf72e94f62d5f2d173e5b7008f644cde Mon Sep 17 00:00:00 2001 From: Radhey Shyam Pandey Date: Mon, 20 Jul 2026 21:08:31 +0530 Subject: [PATCH 055/501] phy: zynqmp: fix runtime PM leak on probe allocation failure Allocate saved_regs before pm_runtime_resume_and_get() so a devm_kmalloc() failure does not leave an unreleased runtime PM usage counter. Fixes: 5af9b304bc60 ("phy: xilinx: phy-zynqmp: Fix SGMII linkup failure on resume") Signed-off-by: Radhey Shyam Pandey Reviewed-by: Michal Simek Link: https://patch.msgid.link/20260720153832.1130006-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index c8230f2bda62..2138f5399821 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -1044,6 +1044,12 @@ static int xpsgtr_probe(struct platform_device *pdev) return PTR_ERR(provider); } + gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, + sizeof(save_reg_address), + GFP_KERNEL); + if (!gtr_dev->saved_regs) + return -ENOMEM; + pm_runtime_set_active(gtr_dev->dev); pm_runtime_enable(gtr_dev->dev); @@ -1053,12 +1059,6 @@ static int xpsgtr_probe(struct platform_device *pdev) return ret; } - gtr_dev->saved_regs = devm_kmalloc(gtr_dev->dev, - sizeof(save_reg_address), - GFP_KERNEL); - if (!gtr_dev->saved_regs) - return -ENOMEM; - return 0; } From b5e24d6377f9062aff79c3c493bcbe923deb380c Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Wed, 22 Jul 2026 04:07:07 +0000 Subject: [PATCH 056/501] ALSA: usb-audio: Add iface reset and delay quirk for Generic USB Headphone Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. usb 1-1: New USB device found, idVendor=0124, idProduct=0c21 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: USB Headphone usb 1-1: Manufacturer: Generic usb 1-1: SerialNumber: 20210726905926 Signed-off-by: Lianqin Hu Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217CBB68C8F868C076A4353D2C12@TYUPR06MB6217.apcprd06.prod.outlook.com --- sound/usb/quirks.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 0de7c9d94259..49b56287cdb9 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2208,6 +2208,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x0020, 0x0b21, /* GHW-123P */ QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), + DEVICE_FLG(0x0124, 0x0c21, /* Generic USB Headphone */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), DEVICE_FLG(0x03f0, 0x654a, /* HP 320 FHD Webcam */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIC_RES_16), DEVICE_FLG(0x040b, 0x0897, /* Weltrend Semiconductor, sold as Redragon H510-PRO Wireless headset */ From 704de5697008ea5caa04bb6a7266624699801ddc Mon Sep 17 00:00:00 2001 From: Zhang Heng Date: Tue, 21 Jul 2026 19:35:38 +0800 Subject: [PATCH 057/501] ALSA: hda/realtek: Add quirk for HP Pavilion All-in-One 27-ca1xxx This HP All-in-One desktop (model 27-ca1xxx) uses an ALC274 codec and has no sound from either the internal speakers or the headphone jack. The audio output requires a combination of a specific verb setup and GPIO configuration to become functional. Additionally, the power_save feature must be disabled; otherwise, audible clicks or pops occur during power state transitions. Link: https://bugzilla.kernel.org/show_bug.cgi?id=220694 Signed-off-by: Zhang Heng Link: https://patch.msgid.link/20260721113539.317561-1-zhangheng@kylinos.cn Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 35 +++++++++++++++++++++++++++++++ sound/hda/controllers/intel.c | 2 ++ 2 files changed, 37 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 379e1458f4ac..fcc60e64137d 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3863,6 +3863,16 @@ static void alc288_fixup_surface_swap_dacs(struct hda_codec *codec, spec->gen.preferred_dacs = preferred_pairs; } +static void alc274_fixup_hp_89e9_amp(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + if (action == HDA_FIXUP_ACT_INIT) { + /* need to toggle GPIO to enable the amp */ + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x03, 0); + msleep(100); + snd_hda_codec_set_gpio(codec, 0x03, 0x03, 0x00, 0); + } +} enum { ALC269_FIXUP_GPIO2, ALC269_FIXUP_SONY_VAIO, @@ -4213,6 +4223,8 @@ enum { ALC245_FIXUP_HP_ENVY_X360_15_FH0XXX, ALC287_FIXUP_ACER_MICMUTE_LED, ALC236_FIXUP_DELL_HP_POP_NOISE, + ALC274_FIXUP_HP_89E9_GPIO, + ALC274_FIXUP_HP_VERBS, }; /* A special fixup for Lenovo C940 and Yoga Duet 7; @@ -6855,6 +6867,28 @@ static const struct hda_fixup alc269_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = alc285_fixup_invalidate_dacs, }, + [ALC274_FIXUP_HP_89E9_GPIO] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc274_fixup_hp_89e9_amp, + }, + [ALC274_FIXUP_HP_VERBS] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x0b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x7778 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x10 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xc580 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x26 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5757 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x62 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0xa007 }, + { 0x20, AC_VERB_SET_COEF_INDEX, 0x6b }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x0060 }, + { } + }, + .chained = true, + .chain_id = ALC274_FIXUP_HP_89E9_GPIO, + }, }; static const struct hda_quirk alc269_fixup_tbl[] = { @@ -7231,6 +7265,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8a3d, "HP Victus 15-fb0xxx (MB 8A3D)", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a4f, "HP Victus 15-fa0xxx (MB 8A4F)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8a50, "HP Victus 15-fa0xxx (MB 8A50)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x8a6b, "HP Pavilion All-in-One Desktop 27-ca1xxx", ALC274_FIXUP_HP_VERBS), SND_PCI_QUIRK(0x103c, 0x8a6e, "HP EDNA 360", ALC287_FIXUP_CS35L41_I2C_4), SND_PCI_QUIRK(0x103c, 0x8a74, "HP ProBook 440 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a75, "HP ProBook 450 G8 Notebook PC", ALC236_FIXUP_HP_GPIO_LED), diff --git a/sound/hda/controllers/intel.c b/sound/hda/controllers/intel.c index 4b03c64e72ab..28c55c5a2d2e 100644 --- a/sound/hda/controllers/intel.c +++ b/sound/hda/controllers/intel.c @@ -2306,6 +2306,8 @@ static const struct snd_pci_quirk power_save_denylist[] = { SND_PCI_QUIRK(0x1028, 0x0962, "Dell ALC3271", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=220210 */ SND_PCI_QUIRK(0x17aa, 0x5079, "Lenovo Thinkpad E15", 0), + /* https://bugzilla.kernel.org/show_bug.cgi?id=220694 */ + SND_PCI_QUIRK(0x103c, 0x8a6b, "HP 89E9", 0), {} }; From 1acef6d85bfd98bd9dfe1f08bffa397a4dda8a6f Mon Sep 17 00:00:00 2001 From: Karthikeyan KS Date: Fri, 12 Jun 2026 19:07:43 +0000 Subject: [PATCH 058/501] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read put_fifo_with_discard() acts as both producer and consumer on the kfifo: it calls kfifo_skip() (advances out) and kfifo_put() (advances in) from the IRQ handler without synchronizing with snoop_file_read(), which also consumes via kfifo_to_user(). On SMP systems this concurrent access can leave (in - out) larger than the ring buffer, so __kfifo_to_user()'s clamp to (in - out) is ineffective and kfifo_copy_to_user() can attempt a copy_to_user() past the kmalloc-2k backing store: usercopy: Kernel memory exposure attempt detected from SLUB object 'kmalloc-2k' (offset 0, size 2049)! kernel BUG at mm/usercopy.c! Call trace: usercopy_abort __check_heap_object __check_object_size kfifo_copy_to_user __kfifo_to_user snoop_file_read vfs_read Serialize kfifo access with a per-channel spinlock shared between the IRQ handler (producer) and the file reader (consumer). Annotate @fifo with __guarded_by(&lock) and opt the driver into context analysis so the compiler enforces that all fifo access holds the lock. Fixes: 3772e5da4454 ("drivers/misc: Aspeed LPC snoop output using misc chardev") Signed-off-by: Karthikeyan KS Signed-off-by: Andrew Jeffery --- drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-lpc-snoop.c | 38 ++++++++++++++++++--------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index b35d74592964..b5188dcde37a 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_UART_ROUTING) += aspeed-uart-routing.o obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +CONTEXT_ANALYSIS_aspeed-lpc-snoop.o := y diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c index 28e491fffc7d..b09dca34099f 100644 --- a/drivers/soc/aspeed/aspeed-lpc-snoop.c +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c @@ -11,6 +11,7 @@ */ #include +#include #include #include #include @@ -74,7 +75,8 @@ struct aspeed_lpc_snoop_channel_cfg { struct aspeed_lpc_snoop_channel { const struct aspeed_lpc_snoop_channel_cfg *cfg; bool enabled; - struct kfifo fifo; + spinlock_t lock; + struct kfifo fifo __guarded_by(&lock); wait_queue_head_t wq; struct miscdevice miscdev; }; @@ -114,6 +116,7 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); + u8 *buf __free(kfree) = NULL; unsigned int copied; int ret = 0; @@ -125,9 +128,16 @@ static ssize_t snoop_file_read(struct file *file, char __user *buffer, if (ret == -ERESTARTSYS) return -EINTR; } - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); - if (ret) - return ret; + + count = min_t(size_t, count, SNOOP_FIFO_SIZE); + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + copied = kfifo_out_spinlocked(&chan->fifo, buf, count, &chan->lock); + if (copied && copy_to_user(buffer, buf, copied)) + return -EFAULT; return copied; } @@ -151,11 +161,13 @@ static const struct file_operations snoop_fops = { /* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val) { - if (!kfifo_initialized(&chan->fifo)) - return; - if (kfifo_is_full(&chan->fifo)) - kfifo_skip(&chan->fifo); - kfifo_put(&chan->fifo, val); + scoped_guard(spinlock, &chan->lock) { + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + } wake_up_interruptible(&chan->wq); } @@ -239,9 +251,11 @@ static int aspeed_lpc_enable_snoop(struct device *dev, if (!channel->miscdev.name) return -ENOMEM; - rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); - if (rc) - return rc; + scoped_guard(spinlock_init, &channel->lock) { + rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + } rc = misc_register(&channel->miscdev); if (rc) From 87b2a82e135ba81e49d82b59e3d72468cb66ea98 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Thu, 16 Jul 2026 17:22:45 +0800 Subject: [PATCH 059/501] xfs: clear zapped attr fork state when bmap repair finds no attr fork xfstests xfs/377 can make xfs_scrub repeatedly check and repair the attr block map after inode repair zaps an attr fork. When inode repair zaps an attr fork, it records XFS_SICK_INO_BMBTA_ZAPPED so that scrub/repair can revisit the attr fork block map. If the fork has been reset to an empty state and removed, BMBTA repair has no attr fork mappings to rebuild and can return success. The post-repair scrub then runs with XREP_ALREADY_FIXED set, which means xchk_file_looks_zapped() deliberately ignores the stale zapped health bit and asks xchk_bmap() to check the current attr fork. For an absent attr fork, xchk_bmap() returns -ENOENT. Returning that error prevents xchk_bmap_attr() from marking XFS_SICK_INO_BMBTA_ZAPPED healthy, leaving the zapped health state behind even though there are no attr fork mappings left to check. Treat -ENOENT during post-repair BMBTA revalidation as a clean result for the zapped attr fork: clear XFS_SICK_INO_BMBTA_ZAPPED and return success. Keep the existing -ENOENT behavior for ordinary scrubs of absent attr forks. Fixes: d9041681dd2f ("xfs: set inode sick state flags when we zap either ondisk fork") Cc: stable@vger.kernel.org # v6.8 Reviewed-by: "Darrick J. Wong" Signed-off-by: Qiang Ma Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/bmap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 70028da1aacc..401c278725d2 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -1170,6 +1170,11 @@ xchk_bmap_attr( } error = xchk_bmap(sc, XFS_ATTR_FORK); + /* A repaired, empty attr fork no longer has mappings to check. */ + if (error == -ENOENT && (sc->flags & XREP_ALREADY_FIXED)) { + xchk_mark_healthy_if_clean(sc, XFS_SICK_INO_BMBTA_ZAPPED); + return 0; + } if (error) return error; From cc3144da377de5fb422d44a2311f978623f7c900 Mon Sep 17 00:00:00 2001 From: Xiang Mei Date: Tue, 14 Jul 2026 14:56:12 -0700 Subject: [PATCH 060/501] xfs: fix off-by-one in rtrefcount btree root level validation xfs_rtrefcountbt_compute_maxlevels() sets mp->m_rtrefc_maxlevels = min(d_maxlevels, r_maxlevels) + 1; where the trailing "+ 1" already accounts for the inode-root level, so the deepest valid on-disk root level is m_rtrefc_maxlevels - 1 and a cursor must satisfy bc_nlevels <= bc_maxlevels (= m_rtrefc_maxlevels). The two on-disk validation paths, xfs_rtrefcountbt_verify() and xfs_iformat_rtrefcount(), check the root level with ">" instead of ">=", so a crafted rtreflink (metadir + realtime + reflink) image whose /rtgroups/N.refcount inode has bb_level == m_rtrefc_maxlevels is accepted on mount. xfs_rtrefcountbt_init_cursor() then sets bc_nlevels = bb_level + 1, exceeding bc_maxlevels by one. Since the xfs_rtrefcountbt_cur slab object is sized for exactly bc_maxlevels entries, the first btree op on such a cursor indexes bc_levels[m_rtrefc_maxlevels] past the end of the object. This is reached by the first rtrefcount cursor built after mount, via log/CoW recovery (xfs_reflink_recover_cow() during xfs_mountfs()) or an FS_IOC_GETFSMAP over the realtime device. Reject a root level equal to m_rtrefc_maxlevels, matching the ">=" form already used by the sibling data-device refcount/rmap verifiers and the in-memory rtrmap verifier. BUG: KASAN: slab-out-of-bounds in xfs_btree_lookup (fs/xfs/libxfs/xfs_btree.c:2101) Write of size 2 at addr ffff888018391658 by task exploit/144 xfs_btree_lookup (fs/xfs/libxfs/xfs_btree.c:2101) xfs_btree_query_range (fs/xfs/libxfs/xfs_btree.c:5308) xfs_refcount_recover_cow_leftovers (fs/xfs/libxfs/xfs_refcount.c:2113) xfs_reflink_recover_cow (fs/xfs/xfs_reflink.c:1085) xlog_recover_finish (fs/xfs/xfs_log_recover.c:3551) xfs_mountfs (fs/xfs/xfs_mount.c:1158) xfs_fs_fill_super (fs/xfs/xfs_super.c:1940) get_tree_bdev_flags (fs/super.c:1634) vfs_get_tree (fs/super.c:1694) path_mount (fs/namespace.c:4161) __x64_sys_mount (fs/namespace.c:4367) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) The buggy address belongs to the cache xfs_rtrefcountbt_cur of size 216 The buggy address is located 8 bytes to the right of allocated 216-byte region [ffff888018391578, ffff888018391650) Kernel panic - not syncing: Fatal exception Cc: stable@vger.kernel.org # v6.14 Fixes: 9abe03a0e4f978 ("xfs: introduce realtime refcount btree ondisk definitions") Reported-by: Weiming Shi Signed-off-by: Xiang Mei Reviewed-by: "Darrick J. Wong" Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_rtrefcount_btree.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_rtrefcount_btree.c b/fs/xfs/libxfs/xfs_rtrefcount_btree.c index f27b80a199ba..22acc1411aac 100644 --- a/fs/xfs/libxfs/xfs_rtrefcount_btree.c +++ b/fs/xfs/libxfs/xfs_rtrefcount_btree.c @@ -201,7 +201,7 @@ xfs_rtrefcountbt_verify( if (fa) return fa; level = be16_to_cpu(block->bb_level); - if (level > mp->m_rtrefc_maxlevels) + if (level >= mp->m_rtrefc_maxlevels) return __this_address; return xfs_btree_fsblock_verify(bp, mp->m_rtrefc_mxr[level != 0]); @@ -651,7 +651,7 @@ xfs_iformat_rtrefcount( numrecs = be16_to_cpu(dfp->bb_numrecs); level = be16_to_cpu(dfp->bb_level); - if (level > mp->m_rtrefc_maxlevels || + if (level >= mp->m_rtrefc_maxlevels || xfs_rtrefcount_droot_space_calc(level, numrecs) > dsize) { xfs_inode_mark_sick(ip, XFS_SICK_INO_CORE); return -EFSCORRUPTED; From 813f8136a2ce1fee266d02a7df73db6e8a541604 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Wed, 15 Jul 2026 09:17:23 +0200 Subject: [PATCH 061/501] xfs: bounds-check buffer log item's dirty bitmap xlog_recover_do_reg_buffer() replays each dirty region described by a buffer log item's bitmap into the buffer read for that item: memcpy(xfs_buf_offset(bp, (uint)bit << XFS_BLF_SHIFT), item->ri_buf[i].iov_base, nbits << XFS_BLF_SHIFT); The destination offset (bit/nbits, from the logged dirty bitmap) and the buffer size (from the logged blf_len) are both attacker-controlled and otherwise unrelated, yet the only thing bounding the copy is an ASSERT(), which compiles away on production kernels. A crafted image logging a small blf_len together with a bitmap bit past the end of that buffer drives the memcpy() past the buffer's allocation, corrupting adjacent kernel heap during mount-time log recovery. This is reachable by anyone who can get a crafted image mounted -- the malicious-filesystem threat model XFS already guards against elsewhere. Turn the ASSERT() into a real XFS_IS_CORRUPT() check that aborts recovery of the buffer with -EFSCORRUPTED, consistent with the validate-and-fail idiom already used in xlog_recover_do_inode_buffer() and xfs_dquot_item_recover.c. xlog_recover_do_reg_buffer() therefore becomes STATIC int and its three callers propagate the error. Found and confirmed with KASAN on a CONFIG_XFS_DEBUG=n build: the crafted image trips a slab-out-of-bounds write before this change and fails recovery cleanly with -EFSCORRUPTED after it. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Reviewed-by: "Darrick J. Wong" Reviewed-by: Brian Foster Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf_item_recover.c | 57 +++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/fs/xfs/xfs_buf_item_recover.c b/fs/xfs/xfs_buf_item_recover.c index 02b95b89d1b5..240deb3f7827 100644 --- a/fs/xfs/xfs_buf_item_recover.c +++ b/fs/xfs/xfs_buf_item_recover.c @@ -461,7 +461,7 @@ xlog_recover_validate_buf_type( * given buffer. The bitmap in the buf log format structure indicates * where to place the logged data. */ -STATIC void +STATIC int xlog_recover_do_reg_buffer( struct xfs_mount *mp, struct xlog_recover_item *item, @@ -489,8 +489,24 @@ xlog_recover_do_reg_buffer( ASSERT(nbits > 0); ASSERT(item->ri_buf[i].iov_base != NULL); ASSERT(item->ri_buf[i].iov_len % XFS_BLF_CHUNK == 0); - ASSERT(BBTOB(bp->b_length) >= - ((uint)bit << XFS_BLF_SHIFT) + (nbits << XFS_BLF_SHIFT)); + /* + * The bitmap is only trustworthy to the extent that it + * describes a region that actually fits inside the buffer we + * read in based on the (attacker-controlled) blf_len. Do not + * rely on an ASSERT() for this -- it compiles away entirely on + * non-DEBUG kernels, which is exactly where this matters, so + * validate it for real and abort recovery of this buffer rather + * than copying past the end of it. + */ + if (XFS_IS_CORRUPT(mp, BBTOB(bp->b_length) < + ((uint)bit << XFS_BLF_SHIFT) + + (nbits << XFS_BLF_SHIFT))) { + xfs_alert(mp, + "Bad buffer log item dirty bitmap (bit %d, nbits %d) for %d-byte buffer at daddr 0x%llx.", + bit, nbits, BBTOB(bp->b_length), + xfs_buf_daddr(bp)); + return -EFSCORRUPTED; + } /* * The dirty regions logged in the buffer, even though @@ -544,6 +560,7 @@ xlog_recover_do_reg_buffer( ASSERT(i == item->ri_total); xlog_recover_validate_buf_type(mp, bp, buf_f, current_lsn); + return 0; } /* @@ -552,10 +569,10 @@ xlog_recover_do_reg_buffer( * (ie. USR or GRP), then just toss this buffer away; don't recover it. * Else, treat it as a regular buffer and do recovery. * - * Return false if the buffer was tossed and true if we recovered the buffer to - * indicate to the caller if the buffer needs writing. + * Return 0 if the buffer was not recovered (tossed), 1 if it was recovered and + * needs writing, or a negative errno if recovery of the buffer failed. */ -STATIC bool +STATIC int xlog_recover_do_dquot_buffer( struct xfs_mount *mp, struct xlog *log, @@ -564,6 +581,7 @@ xlog_recover_do_dquot_buffer( struct xfs_buf_log_format *buf_f) { uint type; + int error; trace_xfs_log_recover_buf_dquot_buf(log, buf_f); @@ -571,7 +589,7 @@ xlog_recover_do_dquot_buffer( * Filesystems are required to send in quota flags at mount time. */ if (!mp->m_qflags) - return false; + return 0; type = 0; if (buf_f->blf_flags & XFS_BLF_UDQUOT_BUF) @@ -584,10 +602,12 @@ xlog_recover_do_dquot_buffer( * This type of quotas was turned off, so ignore this buffer */ if (log->l_quotaoffs_flag & type) - return false; + return 0; - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN); - return true; + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, NULLCOMMITLSN); + if (error) + return error; + return 1; } /* @@ -724,7 +744,9 @@ xlog_recover_do_primary_sb_buffer( xfs_rgnumber_t orig_rgcount = mp->m_sb.sb_rgcount; int error; - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + if (error) + return error; if (orig_agcount == 0) { xfs_alert(mp, "Trying to grow file system without AGs"); @@ -1081,11 +1103,11 @@ xlog_recover_buf_commit_pass2( goto out_release; } else if (buf_f->blf_flags & (XFS_BLF_UDQUOT_BUF|XFS_BLF_PDQUOT_BUF|XFS_BLF_GDQUOT_BUF)) { - bool dirty; - - dirty = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f); - if (!dirty) + error = xlog_recover_do_dquot_buffer(mp, log, item, bp, buf_f); + if (error <= 0) goto out_release; + /* write dirty buffer */ + error = 0; } else if ((xfs_blft_from_flags(buf_f) & XFS_BLFT_SB_BUF) && xfs_buf_daddr(bp) == 0) { error = xlog_recover_do_primary_sb_buffer(mp, item, bp, buf_f, @@ -1105,7 +1127,10 @@ xlog_recover_buf_commit_pass2( xfs_buf_relse(rtsb_bp); } } else { - xlog_recover_do_reg_buffer(mp, item, bp, buf_f, current_lsn); + error = xlog_recover_do_reg_buffer(mp, item, bp, buf_f, + current_lsn); + if (error) + goto out_release; } /* From d852729c5f4f830fbe7413df032e29459b3daf83 Mon Sep 17 00:00:00 2001 From: Yun Zhou Date: Sun, 19 Jul 2026 23:11:24 +0800 Subject: [PATCH 062/501] xfs: handle NULL b_addr in xfs_buf_free When xfs_buf_alloc_backing_mem() fails, xfs_buf_free() is called with bp->b_addr still NULL. The code falls through to the folio_put path which calls virt_to_folio(NULL), dereferencing an invalid address and causing a kernel crash. Call Trace: xfs_buf_free+0x25f/0x510 xfs_buf_alloc+0xc98/0x19b0 xfs_buf_find_insert+0x55/0x14d0 xfs_buf_get_map+0x122b/0x17c0 xfbtree_init_leaf_block+0x11c/0x4a0 xfbtree_init+0x1bb/0x460 xrep_rmap_setup_scan+0x100/0x1f0 xrep_rmapbt+0x41/0xc0 Fix this by skipping folio_put() when bp->b_addr is NULL. Fixes: 5076a6040ca1 ("xfs: support in-memory buffer cache targets") Reported-by: syzbot+94c22d92f72f5a235b7d@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=94c22d92f72f5a235b7d Signed-off-by: Yun Zhou Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_buf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index e1465e950acc..48d7dfd3e15f 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -114,7 +114,7 @@ xfs_buf_free( vfree(bp->b_addr); else if (bp->b_flags & _XBF_KMEM) kfree(bp->b_addr); - else + else if (bp->b_addr) folio_put(virt_to_folio(bp->b_addr)); call_rcu(&bp->b_rcu, xfs_buf_free_callback); From 7aa67044e7d8b6eebc44b25745f16c83ccc40a94 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 20 Jul 2026 16:08:47 +0200 Subject: [PATCH 063/501] xfs: update BDI {io,ra}_pages values based on the RT device limits When using XFS with a main device on an SSD that stores metadata and a RT device to store data on a HDD, we fail to take the I/O sizes for the RT device into accounting, leading to up to 5% slower read performance when using an SSD for metadata vs storing data and metadata on the HDD. Fix this up by taking the RT settings into account at mount an restoring the old settings at unmount time, unless the BDI settings have changed from those set by XFS. Reported-by: Filip Blagojevic Signed-off-by: Christoph Hellwig Reviewed-by: Carlos Maiolino Reviewed-by: Damien Le Moal Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_mount.h | 7 ++++++ fs/xfs/xfs_super.c | 53 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_mount.h b/fs/xfs/xfs_mount.h index 66a02d1b9ad7..216a38a354e7 100644 --- a/fs/xfs/xfs_mount.h +++ b/fs/xfs/xfs_mount.h @@ -349,6 +349,13 @@ typedef struct xfs_mount { /* Index of uuid record in the uuid xarray. */ unsigned int m_uuid_table_index; + + /* + * Old io_pages/ra_pages valued in the main bdev BDI, and our initial + * calculated values. + */ + unsigned long m_old_io_pages, m_initial_io_pages; + unsigned long m_old_ra_pages, m_initial_ra_pages; } xfs_mount_t; #define M_IGEO(mp) (&(mp)->m_ino_geo) diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index eac7f9503805..323aa1228b6c 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -545,6 +545,52 @@ xfs_open_devices( return error; } +/* + * When using a RT device some or all data I/O is using the RT device, but + * the BDI is inherited from the main data device. When the underlying block + * device for the RT device has larger I/O sizes, the BDI settings might be + * incorrect, which is especially bad if the main device is a SSD and the + * RT device is a HDD, as the io_opt fixup in blk_apply_bdi_limits is missing + * for this case. + * + * Update the BDI values to the max of the data and RT device to cover our + * bases. + */ +static void +xfs_update_bdi_rahead( + struct xfs_mount *mp) +{ + struct backing_dev_info *rt_bdi = + mp->m_rtdev_targp->bt_bdev->bd_disk->bdi; + struct backing_dev_info *sb_bdi = mp->m_super->s_bdi; + + mp->m_old_io_pages = sb_bdi->io_pages; + mp->m_old_ra_pages = sb_bdi->ra_pages; + + sb_bdi->io_pages = mp->m_initial_io_pages = + max(sb_bdi->io_pages, rt_bdi->io_pages); + sb_bdi->ra_pages = mp->m_initial_ra_pages = + max(sb_bdi->ra_pages, rt_bdi->ra_pages); +} + +static void +xfs_restore_bdi_rahead( + struct xfs_mount *mp) +{ + struct backing_dev_info *sb_bdi = mp->m_super->s_bdi; + + if (sb_bdi->io_pages == mp->m_initial_io_pages) + sb_bdi->io_pages = mp->m_old_io_pages; + else + xfs_info(mp, "io_pages changed from %lu to %lu, not restoring.", + mp->m_initial_io_pages, sb_bdi->io_pages); + if (sb_bdi->ra_pages == mp->m_initial_ra_pages) + sb_bdi->ra_pages = mp->m_old_ra_pages; + else + xfs_info(mp, "ra_pages changed from %lu to %lu, not restoring.", + mp->m_initial_ra_pages, sb_bdi->ra_pages); +} + /* * Setup xfs_mount buffer target pointers based on superblock */ @@ -582,6 +628,7 @@ xfs_setup_devices( mp->m_sb.sb_sectsize, mp->m_sb.sb_rblocks); if (error) return error; + xfs_update_bdi_rahead(mp); } return 0; @@ -2280,8 +2327,12 @@ static void xfs_kill_sb( struct super_block *sb) { + struct xfs_mount *mp = XFS_M(sb); + + if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp) + xfs_restore_bdi_rahead(mp); kill_block_super(sb); - xfs_mount_free(XFS_M(sb)); + xfs_mount_free(mp); } static struct file_system_type xfs_fs_type = { From 270ffcd9b0a46254fbf3079d0e3341db7d7ec0e6 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:23:46 -0700 Subject: [PATCH 064/501] xfs: check cowextsize in xrep_inode_cowextsize LOLLM points out that the function that corrects cowextsize should check i_cowextsize, not i_extsize. Cc: stable@vger.kernel.org # v6.14 Fixes: a9600db96f74af ("xfs: detect and repair misaligned rtinherit directory cowextsize hints") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/inode_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index 3ec41c198351..b88427a4460c 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1960,7 +1960,7 @@ xrep_inode_cowextsize( /* Fix misaligned CoW extent size hints on a directory. */ if ((sc->ip->i_diflags & XFS_DIFLAG_RTINHERIT) && (sc->ip->i_diflags2 & XFS_DIFLAG2_COWEXTSIZE) && - sc->ip->i_extsize % sc->mp->m_sb.sb_rextsize > 0) { + xfs_extlen_to_rtxmod(sc->mp, sc->ip->i_cowextsize) > 0) { sc->ip->i_cowextsize = 0; sc->ip->i_diflags2 &= ~XFS_DIFLAG2_COWEXTSIZE; } From 61606f8846a9da8ab7d2d36ff4617134c9d6df1b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:02 -0700 Subject: [PATCH 065/501] xfs: fix transaction block reservation in xrep_rtbitmap LOLLM pointed out an inconsistency in the block reservation code in xrep_rtbitmap. The first is that the reservation computation is not consistent between the code that sets up the repair and the code that tries to avoid exceeding the transaction reservation once we know how big the rtbitmap really must be. As a result, the logic doesn't work. In fixing that, a second problem emerges: if we do readjust, we ask for the entire reservation all over again. We really only need the delta, so ask only for that. Fix all these problems by hoisting the computation to a trivial helper so that it gets used in both places. Cc: stable@vger.kernel.org # v6.14 Fixes: 8defee8dff2b20 ("xfs: online repair of realtime bitmaps for a realtime group") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtbitmap_repair.c | 46 +++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/fs/xfs/scrub/rtbitmap_repair.c b/fs/xfs/scrub/rtbitmap_repair.c index dc64902d6c25..442a17bf9720 100644 --- a/fs/xfs/scrub/rtbitmap_repair.c +++ b/fs/xfs/scrub/rtbitmap_repair.c @@ -36,6 +36,24 @@ /* rt bitmap content repairs */ +/* + * Reserve enough blocks to write out a completely new bitmap file, plus twice + * as many blocks as we would need if we can only allocate one block per data + * fork mapping. This should cover the preallocation of the temporary file and + * exchanging the extent mappings. + * + * We cannot use xfs_exchmaps_estimate because we have not yet constructed the + * replacement bitmap and therefore do not know how many extents it will use. + * By the time we do, we will have a dirty transaction (which we cannot drop + * because we cannot drop the rtbitmap ILOCK) and cannot ask for more + * reservation. + */ +static inline unsigned long long +xrep_rtbitmap_calc_blocks(struct xfs_mount *mp, unsigned long long blocks) +{ + return blocks + (xfs_bmbt_calc_size(mp, blocks) * 2); +} + /* Set up to repair the realtime bitmap for this group. */ int xrep_setup_rtbitmap( @@ -56,20 +74,7 @@ xrep_setup_rtbitmap( if (error) return error; - /* - * Reserve enough blocks to write out a completely new bitmap file, - * plus twice as many blocks as we would need if we can only allocate - * one block per data fork mapping. This should cover the - * preallocation of the temporary file and exchanging the extent - * mappings. - * - * We cannot use xfs_exchmaps_estimate because we have not yet - * constructed the replacement bitmap and therefore do not know how - * many extents it will use. By the time we do, we will have a dirty - * transaction (which we cannot drop because we cannot drop the - * rtbitmap ILOCK) and cannot ask for more reservation. - */ - blocks += xfs_bmbt_calc_size(mp, blocks) * 2; + blocks = xrep_rtbitmap_calc_blocks(mp, mp->m_sb.sb_rbmblocks); if (blocks > UINT_MAX) return -EOPNOTSUPP; @@ -512,7 +517,7 @@ xrep_rtbitmap( struct xchk_rtbitmap *rtb = sc->buf; struct xfs_mount *mp = sc->mp; struct xfs_group *xg = rtg_group(sc->sr.rtg); - unsigned long long blocks = 0; + unsigned long long blocks; unsigned int busy_gen; int error; @@ -532,15 +537,20 @@ xrep_rtbitmap( * figure out if we need to adjust the block reservation in the * transaction. */ - blocks = xfs_bmbt_calc_size(mp, rtb->rbmblocks); + blocks = xrep_rtbitmap_calc_blocks(mp, rtb->rbmblocks); if (blocks > UINT_MAX) return -EOPNOTSUPP; if (blocks > rtb->resblks) { - error = xfs_trans_reserve_more(sc->tp, blocks, 0); + uint64_t delta = blocks - rtb->resblks; + + if (delta > UINT_MAX) + return -EOPNOTSUPP; + + error = xfs_trans_reserve_more(sc->tp, delta, 0); if (error) return error; - rtb->resblks += blocks; + rtb->resblks += delta; } /* Fix inode core and forks. */ From 8ed78104722b57df69478b0b1608a408c1036085 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:17 -0700 Subject: [PATCH 066/501] xfs: rtsummary scrub should treat rtbitmap corruption errors as an xref error LOLLM notes the discrepancy between the comment saying that this is an xref error and the code, which marks a (non-xref) corruption error. This appears to be a regression. Cc: stable@vger.kernel.org # v6.13 Fixes: e3088ae2dcae3c ("xfs: move RT bitmap and summary information to the rtgroup") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/rtsummary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/rtsummary.c b/fs/xfs/scrub/rtsummary.c index 78f72a046887..546b335ade13 100644 --- a/fs/xfs/scrub/rtsummary.c +++ b/fs/xfs/scrub/rtsummary.c @@ -358,7 +358,7 @@ xchk_rtsummary( * EFSCORRUPTED means the rtbitmap is corrupt, which is an xref * error since we're checking the summary file. */ - xchk_ip_set_corrupt(sc, rbmip); + xchk_ip_xref_set_corrupt(sc, rbmip); return 0; } if (error) From 49933254ba8d421ed706cbe6ed0fbc264e572cab Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:33 -0700 Subject: [PATCH 067/501] xfs: zero i_nlink before repair puts inode on unlinked list LOLLM observes that we don't reset i_nlink if we encounter a file with no parent that isn't on the unlinked list. This causes unnecessary assertion trips on debugging kernels and an inconsistent file, so let's fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: 669dfe883c8e20 ("xfs: update the unlinked list when repairing link counts") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/nlinks_repair.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/nlinks_repair.c b/fs/xfs/scrub/nlinks_repair.c index fbc2ff809fc0..09e097e16689 100644 --- a/fs/xfs/scrub/nlinks_repair.c +++ b/fs/xfs/scrub/nlinks_repair.c @@ -232,9 +232,14 @@ xrep_nlinks_repair_inode( * unlinked list, put it on the unlinked list. */ if (total_links == 0 && !xfs_inode_on_unlinked_list(ip)) { + if (actual_nlink) + clear_nlink(VFS_I(ip)); error = xfs_iunlink(sc->tp, ip); - if (error) + if (error) { + if (actual_nlink) + set_nlink(VFS_I(ip), actual_nlink); goto out_trans; + } dirty = true; } From b28d23c51635b646784a2a62c71ba99458c07d5e Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Mon, 20 Jul 2026 20:24:48 -0700 Subject: [PATCH 068/501] xfs: mark nonzero sb_gquotino as corrupt on metadir filesystems On a metadir filesystem, the superblock gquotino field is always zero because we find the quota inodes through the metadata directory tree. A nonzero value is therefore a corruption (as pointed out by LOLLM) so mark the failure as such. Cc: stable@vger.kernel.org # v6.13 Fixes: 06b20ef09ba163 ("xfs: check pre-metadir fields correctly") Signed-off-by: "Darrick J. Wong" Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 9ed053b5f061..62ed5eaf08fb 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -266,7 +266,7 @@ xchk_superblock( xchk_block_set_corrupt(sc, bp); if (sb->sb_gquotino != cpu_to_be64(0)) - xchk_block_set_preen(sc, bp); + xchk_block_set_corrupt(sc, bp); } else { if (sb->sb_uquotino != cpu_to_be64(mp->m_sb.sb_uquotino)) xchk_block_set_preen(sc, bp); From 11055a46f398779b69aa36afb7c9f4124529a075 Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Wed, 22 Jul 2026 10:38:24 +0100 Subject: [PATCH 069/501] ACPI: CPPC: Check all controls for fast switching ACPI 6.2, Section 6.2.11.2 permits _CPC registers to use flexible address spaces. Linux advertises that capability through _OSC and parses the address space of each _CPC register independently. A directly accessible DESIRED_PERF combined with PCC-backed limits is therefore a valid configuration. cppc_allow_fast_switch() only checks DESIRED_PERF, although the fast-switch callback passes DESIRED_PERF, MIN_PERF and MAX_PERF to cppc_set_perf(). If a limit uses PCC, that function can sleep while called from scheduler context. Allow fast switching only when every supported control used by the callback has an address space already accepted for fast access. Check the complete policy domain, including initialized CPUs that are currently offline and may later become the policy's managing CPU. Fixes: 658fa7b1c47a ("ACPI: CPPC: Add cppc_get_perf() API to read performance controls") Cc: stable@vger.kernel.org Signed-off-by: Christian Loehle Link: https://patch.msgid.link/20260722093825.1030594-2-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 22 +++++++++++++++++----- drivers/cpufreq/cppc_cpufreq.c | 2 +- include/acpi/cppc_acpi.h | 5 +++-- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 9f572f481241..1d3a94100491 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -475,17 +475,29 @@ bool acpi_cpc_valid(void) } EXPORT_SYMBOL_GPL(acpi_cpc_valid); -bool cppc_allow_fast_switch(void) +bool cppc_allow_fast_switch(const struct cpumask *cpus) { - struct cpc_register_resource *desired_reg; + struct cpc_register_resource *desired_reg, *min_reg, *max_reg; struct cpc_desc *cpc_ptr; int cpu; - for_each_online_cpu(cpu) { + for_each_cpu(cpu, cpus) { cpc_ptr = per_cpu(cpc_desc_ptr, cpu); + if (!cpc_ptr) + return false; desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF]; - if (!CPC_IN_SYSTEM_MEMORY(desired_reg) && - !CPC_IN_SYSTEM_IO(desired_reg)) + min_reg = &cpc_ptr->cpc_regs[MIN_PERF]; + max_reg = &cpc_ptr->cpc_regs[MAX_PERF]; + + if (!CPC_SUPPORTED(desired_reg) || + (!CPC_IN_SYSTEM_MEMORY(desired_reg) && + !CPC_IN_SYSTEM_IO(desired_reg)) || + (CPC_SUPPORTED(min_reg) && + !CPC_IN_SYSTEM_MEMORY(min_reg) && + !CPC_IN_SYSTEM_IO(min_reg)) || + (CPC_SUPPORTED(max_reg) && + !CPC_IN_SYSTEM_MEMORY(max_reg) && + !CPC_IN_SYSTEM_IO(max_reg))) return false; } diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index f6cea0c54dd9..b943bf78d3f5 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -693,7 +693,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->fast_switch_possible = cppc_allow_fast_switch(); + policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus); policy->dvfs_possible_from_any_cpu = true; /* diff --git a/include/acpi/cppc_acpi.h b/include/acpi/cppc_acpi.h index 8693890a7275..8c191b9ac18f 100644 --- a/include/acpi/cppc_acpi.h +++ b/include/acpi/cppc_acpi.h @@ -170,7 +170,7 @@ extern u64 cppc_get_dmi_max_khz(void); extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf); extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq); extern bool acpi_cpc_valid(void); -extern bool cppc_allow_fast_switch(void); +bool cppc_allow_fast_switch(const struct cpumask *cpus); extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data); extern int cppc_get_transition_latency(int cpu); extern bool cpc_ffh_supported(void); @@ -234,7 +234,8 @@ static inline bool acpi_cpc_valid(void) { return false; } -static inline bool cppc_allow_fast_switch(void) + +static inline bool cppc_allow_fast_switch(const struct cpumask *cpus) { return false; } From 9753c0ab89b7516aba4884dc3cc725ca33c2e3da Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Wed, 22 Jul 2026 10:38:25 +0100 Subject: [PATCH 070/501] cpufreq: cppc: Sanitize lockless policy limit snapshots cppc_cpufreq_update_perf_limits() reads policy->min and policy->max without holding the policy lock. The cpufreq core updates those fields with separate stores, so a reader can observe the old minimum together with the new maximum and construct MIN_PERF greater than MAX_PERF. Read both fields once and, if the lockless snapshot is inconsistent, reduce the minimum to the observed maximum. This matches the conservative correction used by cpufreq_driver_resolve_freq() and ensures that CPPC never receives an inverted limit pair. Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") Cc: stable@vger.kernel.org Signed-off-by: Christian Loehle Link: https://patch.msgid.link/20260722093825.1030594-3-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cppc_cpufreq.c | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index b943bf78d3f5..6fe0e972952a 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void) } #endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */ +static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data, + struct cpufreq_policy *policy, + u32 *min_perf, u32 *max_perf) +{ + struct cppc_perf_caps *caps = &cpu_data->perf_caps; + unsigned int min_freq, max_freq; + u32 min, max; + + min_freq = READ_ONCE(policy->min); + max_freq = READ_ONCE(policy->max); + if (unlikely(min_freq > max_freq)) + min_freq = max_freq; + + min = cppc_khz_to_perf(caps, min_freq); + max = cppc_khz_to_perf(caps, max_freq); + + *min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf); + *max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf); +} + static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data, struct cpufreq_policy *policy) { - struct cppc_perf_caps *caps = &cpu_data->perf_caps; - u32 min_perf, max_perf; - - min_perf = cppc_khz_to_perf(caps, policy->min); - max_perf = cppc_khz_to_perf(caps, policy->max); - - cpu_data->perf_ctrls.min_perf = - clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf); - cpu_data->perf_ctrls.max_perf = - clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf); + cppc_cpufreq_get_perf_limits(cpu_data, policy, + &cpu_data->perf_ctrls.min_perf, + &cpu_data->perf_ctrls.max_perf); } static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, From f0a3f042293a8c5a2152346b3637ea60866c503a Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Thu, 16 Jul 2026 19:51:58 +0800 Subject: [PATCH 071/501] cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized Commit 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") merged the per-CPU initialization and the utilization-hook registration into a single loop in sugov_start(). For a shared cpufreq policy this re-introduces the race originally fixed by commit ab2f7cf141aa ("cpufreq: schedutil: Fix sugov_start() versus sugov_update_shared() race"). The scheduler's util path reaches the hook under RCU-sched and never takes policy->rwsem, so the rwsem held across sugov_start() cannot serialize the two. Once the first CPU's hook is published, sugov_update_shared() may run and, via sugov_next_freq_shared(), read/write each sibling sugov_cpu (iowait_boost, util, bw_min, ...) concurrently with the memset() still initializing them, with no lock common to both sides: the update side holds sg_policy->update_lock while the init side holds only policy->rwsem, which the scheduler's util path never takes. The walk only accesses scalar members, never a pointer like ->sg_policy, so it does not crash today; it merely uses stale (or zero on first start) values that skew the frequency selection and tracepoints. It is still a genuine data race, and a latent crash once any pointer member is dereferenced there. Restore the two-phase approach: initialize all per-CPU structures first, and only then publish the per-CPU utilization update hooks. Fixes: 16a03c71bba0 ("cpufreq: schedutil: Merge initialization code of sg_cpu in single loop") Cc: stable@vger.kernel.org Signed-off-by: Zhongqiu Han Reviewed-by: Christian Loehle Link: https://patch.msgid.link/20260716115159.848403-1-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki --- kernel/sched/cpufreq_schedutil.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index a4e689eefdfb..dff4ee04694c 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_policy *policy) memset(sg_cpu, 0, sizeof(*sg_cpu)); sg_cpu->cpu = cpu; sg_cpu->sg_policy = sg_policy; + } + + /* + * Publish the hooks only after all per-CPU data is initialized, so a + * shared policy's sugov_update_shared() never reads an uninitialized + * sibling sugov_cpu. + */ + for_each_cpu(cpu, policy->cpus) { + struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu); + cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu); } + return 0; } From 8eb052f48331474c2789d07b7f11165c323bd2f9 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Sun, 24 May 2026 23:38:46 -0400 Subject: [PATCH 072/501] ARM: npcm: Fix OF node refcount leaks in SMP setup npcm7xx_smp_boot_secondary() and npcm7xx_smp_prepare_cpus() look up the GCR and SCU nodes with of_find_compatible_node(). The returned nodes are used for of_iomap(), but the node references are never released. of_iomap() does not consume the device node reference, and iounmap() only releases the MMIO mapping. Drop each node reference after the corresponding mapping attempt. Fixes: 7bffa14c9aed ("arm: npcm: add basic support for Nuvoton BMCs") Signed-off-by: Yuho Choi Reviewed-by: Avi Fishman Signed-off-by: Andrew Jeffery --- arch/arm/mach-npcm/platsmp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/mach-npcm/platsmp.c b/arch/arm/mach-npcm/platsmp.c index 41891d3aa124..4c1fc9983746 100644 --- a/arch/arm/mach-npcm/platsmp.c +++ b/arch/arm/mach-npcm/platsmp.c @@ -32,6 +32,7 @@ static int npcm7xx_smp_boot_secondary(unsigned int cpu, goto out; } gcr_base = of_iomap(gcr_np, 0); + of_node_put(gcr_np); if (!gcr_base) { pr_err("could not iomap gcr"); ret = -ENOMEM; @@ -60,6 +61,7 @@ static void __init npcm7xx_smp_prepare_cpus(unsigned int max_cpus) return; } scu_base = of_iomap(scu_np, 0); + of_node_put(scu_np); if (!scu_base) { pr_err("could not iomap scu"); return; From 6cb22477929489a412df8d153e550e77a012e701 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:27 +0530 Subject: [PATCH 073/501] phy: zynqmp: fix L0_TM_DISABLE_SCRAMBLE_ENCODER mask The L0_TX_DIG_61 register bit 2 is a reserved read-only field. The previous mask value 0x0f incorrectly included bit 2, causing unintended writes to a reserved bit on every scrambler bypass operation. Correct the mask to (BIT(3) | GENMASK(1, 0)) to cover only the valid scramble bypass control bits. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-2-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 2138f5399821..58fa2f837689 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -53,7 +53,7 @@ #define L0_TM_DIG_6 0x106c #define L0_TM_DIS_DESCRAMBLE_DECODER 0x0f #define L0_TX_DIG_61 0x00f4 -#define L0_TM_DISABLE_SCRAMBLE_ENCODER 0x0f +#define L0_TM_DISABLE_SCRAMBLE_ENCODER (BIT(3) | GENMASK(1, 0)) /* PLL Test Mode register parameters */ #define L0_TM_PLL_DIG_37 0x2094 From 21e0749f931702765b9d52d05740092bc87fcd8d Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:28 +0530 Subject: [PATCH 074/501] phy: zynqmp: use read-modify-write for SERDES scrambler bypass xpsgtr_bypass_scrambler_8b10b() used xpsgtr_write_phy() which performs a full register write, silently clearing any bits beyond the intended bypass control fields. Switch to xpsgtr_clr_set_phy() with clr=mask, set=mask to set only the bypass bits while preserving the remaining bits in each register. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-3-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 58fa2f837689..0e99b2cc2a2d 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -505,8 +505,12 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) /* Bypass (de)scrambler and 8b/10b decoder and encoder. */ static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) { - xpsgtr_write_phy(gtr_phy, L0_TM_DIG_6, L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_write_phy(gtr_phy, L0_TX_DIG_61, L0_TM_DISABLE_SCRAMBLE_ENCODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); } /* DP-specific initialization. */ From 7eb61caf45607e1e1270f51f8f93f0ded53146da Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Sat, 27 Jun 2026 21:22:29 +0530 Subject: [PATCH 075/501] phy: zynqmp: keep SERDES scrambler and 8b/10b enabled for USB USB Gen1 requires scrambling and 8b/10b encoding to be performed in the physical layer. Do not bypass PHY-side scrambler or encoder/decoder for USB operation, as mandated by the USB 3.x specification. Scrambler and 8b/10b bypass remain restricted to SATA and SGMII modes, where encoding is handled in the controller. Fixes: 4a33bea00314 ("phy: zynqmp: Add PHY driver for the Xilinx ZynqMP Gigabit Transceiver") Cc: stable@vger.kernel.org Signed-off-by: Nava kishore Manne Signed-off-by: Radhey Shyam Pandey Acked-by: Michal Simek Link: https://patch.msgid.link/20260627155229.2791113-4-radhey.shyam.pandey@amd.com Signed-off-by: Vinod Koul --- drivers/phy/xilinx/phy-zynqmp.c | 39 ++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/drivers/phy/xilinx/phy-zynqmp.c b/drivers/phy/xilinx/phy-zynqmp.c index 0e99b2cc2a2d..240626b55475 100644 --- a/drivers/phy/xilinx/phy-zynqmp.c +++ b/drivers/phy/xilinx/phy-zynqmp.c @@ -502,15 +502,30 @@ static void xpsgtr_lane_set_protocol(struct xpsgtr_phy *gtr_phy) } } -/* Bypass (de)scrambler and 8b/10b decoder and encoder. */ -static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy) +/** + * xpsgtr_bypass_scrambler_8b10b - Configure scrambler/encoder behavior + * @gtr_phy: pointer to lane context + * @bypass: true to enable scrambler/encoder bypass (SATA/SGMII), + * false to disable scrambler/encoder bypass (USB3) + * + * Uses RMW to preserve reserved and unrelated register fields. + */ +static void xpsgtr_bypass_scrambler_8b10b(struct xpsgtr_phy *gtr_phy, + bool bypass) { - xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, - L0_TM_DIS_DESCRAMBLE_DECODER, - L0_TM_DIS_DESCRAMBLE_DECODER); - xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, - L0_TM_DISABLE_SCRAMBLE_ENCODER, - L0_TM_DISABLE_SCRAMBLE_ENCODER); + if (bypass) { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, + L0_TM_DIS_DESCRAMBLE_DECODER); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, + L0_TM_DISABLE_SCRAMBLE_ENCODER); + } else { + xpsgtr_clr_set_phy(gtr_phy, L0_TM_DIG_6, + L0_TM_DIS_DESCRAMBLE_DECODER, 0); + xpsgtr_clr_set_phy(gtr_phy, L0_TX_DIG_61, + L0_TM_DISABLE_SCRAMBLE_ENCODER, 0); + } } /* DP-specific initialization. */ @@ -531,7 +546,7 @@ static void xpsgtr_phy_init_sata(struct xpsgtr_phy *gtr_phy) { struct xpsgtr_dev *gtr_dev = gtr_phy->dev; - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); writel(gtr_phy->lane, gtr_dev->siou + SATA_CONTROL_OFFSET); } @@ -547,7 +562,7 @@ static void xpsgtr_phy_init_sgmii(struct xpsgtr_phy *gtr_phy) xpsgtr_clr_set(gtr_dev, TX_PROT_BUS_WIDTH, mask, val); xpsgtr_clr_set(gtr_dev, RX_PROT_BUS_WIDTH, mask, val); - xpsgtr_bypass_scrambler_8b10b(gtr_phy); + xpsgtr_bypass_scrambler_8b10b(gtr_phy, true); } /* Configure TX de-emphasis and margining for DP. */ @@ -708,6 +723,10 @@ static int xpsgtr_phy_init(struct phy *phy) case ICM_PROTOCOL_SGMII: xpsgtr_phy_init_sgmii(gtr_phy); break; + + case ICM_PROTOCOL_USB: + xpsgtr_bypass_scrambler_8b10b(gtr_phy, false); + break; } goto out; From 9591fcc95dddfd24298724f8eb5239e907980779 Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Wed, 22 Jul 2026 14:33:07 +0000 Subject: [PATCH 076/501] sched_ext: Mark waker CPU busy when selected in WAKE_SYNC case SCX's built-in idle CPU tracking is imperfect and can be out-of-sync with the actual idle state of CPUs, especially immediately after enabling SCX due to scx_idle_enable() marking all online CPUs idle. scx_select_cpu_dfl() skips marking the selected CPU as busy if the selected CPU is the waker CPU in the SCX_WAKE_SYNC case. If the waker CPU was marked idle by SCX, it will still be marked idle after CPU selection and potentially even after switching to the wakee. In the allowed_cpus selftest, this can manifest as the test failing with the following message in dmesg: allowed_cpus.bpf.c:21: CPU 0 should be marked as busy This patch explicitly marks the waker CPU as busy. With this patch, the test failure no longer reproduces. There are still some pretty unlikely races that could make the test fail (e.g. pick_task_idle() marking the selected CPU idle between selection and validation), but these can't be fixed easily. Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo --- kernel/sched/ext/idle.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/sched/ext/idle.c b/kernel/sched/ext/idle.c index 8e8c6201b7df..6f93cc32b650 100644 --- a/kernel/sched/ext/idle.c +++ b/kernel/sched/ext/idle.c @@ -554,8 +554,10 @@ s32 scx_select_cpu_dfl(struct task_struct *p, s32 prev_cpu, u64 wake_flags, cpu_rq(cpu)->scx.local_dsq.nr == 0 && (!(flags & SCX_PICK_IDLE_IN_NODE) || (waker_node == node)) && !cpumask_empty(idle_cpumask(waker_node)->cpu)) { - if (cpumask_test_cpu(cpu, allowed)) + if (cpumask_test_cpu(cpu, allowed)) { + scx_idle_test_and_clear_cpu(cpu); goto out_unlock; + } } } From 6842427bf2990271c771081f11fd8fab17f86c82 Mon Sep 17 00:00:00 2001 From: Mario Limonciello Date: Wed, 15 Jul 2026 12:43:18 -0500 Subject: [PATCH 077/501] cpufreq/amd-pstate: Loosen requirement on lowest nonlinear frequency != min freq This requirement was introduced by commit 8f8b42c1fcc93 ("cpufreq: amd-pstate: optimize the initial frequency values verification") specifically to aid in debugging BIOS issues with invalid _CPC tables on some older systems. This requirement is too tight for new systems though as some systems actually have lowest nonlinear frequency identical to minimum frequency. Allow that combo to work. Signed-off-by: Mario Limonciello Reviewed-by: K Prateek Nayak Tested-by: K Prateek Nayak Link: https://lore.kernel.org/r/20260715174318.18235-1-mario.limonciello@amd.com Signed-off-by: Mario Limonciello --- drivers/cpufreq/amd-pstate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index a74a4cf99d22..3d72337a3336 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1031,7 +1031,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) return -EINVAL; } - if (lowest_nonlinear_freq <= min_freq || lowest_nonlinear_freq > nominal_freq) { + if (lowest_nonlinear_freq < min_freq || lowest_nonlinear_freq > nominal_freq) { pr_err("lowest_nonlinear_freq(%d) value is out of range [min_freq(%d), nominal_freq(%d)]\n", lowest_nonlinear_freq, min_freq, nominal_freq); return -EINVAL; From 08fc1e7b31f8832e356ede8afae1ad1ff0d5a1fc Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 22 Jul 2026 02:13:43 +0800 Subject: [PATCH 078/501] cpufreq/amd-pstate: Prevent the driver from loading on unsupported hardware X86_FEATURE_HW_PSTATE indicates if the processor supports frequency scaling or not. Without it, the driver is unusable and thus will not load. This check also prevents the driver from loading in guests and thus not confuse users with misleading prints. Reviewed-by: Michael Kelley Tested-by: Michael Kelley Acked-by: Mario Limonciello (AMD) Reviewed-by: K Prateek Nayak Tested-by: K Prateek Nayak Acked-by: Borislav Petkov (AMD) Signed-off-by: Rong Zhang Link: https://lore.kernel.org/r/20260722-amd-pstate-vm-v4-1-d6607d9e9d9a@rong.moe Signed-off-by: Mario Limonciello --- drivers/cpufreq/amd-pstate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 3d72337a3336..3a6b4b224a66 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -2167,6 +2167,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = { }; /* + * Processors without frequency scaling support can't do CPPC. * CPPC function is not supported for family ID 17H with model_ID ranging from 0x10 to 0x2F. * show the debug message that helps to check if the CPU has CPPC support for loading issue. */ @@ -2175,6 +2176,11 @@ static bool amd_cppc_supported(void) struct cpuinfo_x86 *c = &cpu_data(0); bool warn = false; + if (!cpu_feature_enabled(X86_FEATURE_HW_PSTATE)) { + pr_debug_once("frequency scaling is not supported by the processor\n"); + return false; + } + if ((boot_cpu_data.x86 == 0x17) && (boot_cpu_data.x86_model < 0x30)) { pr_debug_once("CPPC feature is not supported by the processor\n"); return false; From d3c87e71539cb183ea6173a99dc0e6f84fa0bc4f Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Wed, 22 Jul 2026 21:54:44 +0530 Subject: [PATCH 079/501] spi: spacemit: prepare both DMA descriptors before submitting k1_spi_dma_one() currently submits the TX DMA descriptor to the DMA engine before preparing the RX DMA descriptor. If preparing the RX descriptor subsequently fails, the function jumps to the fallback error path without canceling or aborting the already submitted TX DMA descriptor. Fix this by preparing both the TX and RX descriptors before submitting either of them to the DMA engine. Fixes: efcd8b9d1111 ("spi: spacemit: introduce SpacemiT K1 SPI controller driver") Reviewed-by: Alex Elder Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260722162444.11415-1-kr494167@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-spacemit-k1.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/spi/spi-spacemit-k1.c b/drivers/spi/spi-spacemit-k1.c index 0faf7ffed676..fd5893a92e59 100644 --- a/drivers/spi/spi-spacemit-k1.c +++ b/drivers/spi/spi-spacemit-k1.c @@ -278,25 +278,25 @@ static int k1_spi_dma_one(struct spi_controller *host, struct spi_device *spi, struct spi_transfer *transfer) { struct k1_spi_driver_data *drv_data = spi_controller_get_devdata(host); - struct dma_async_tx_descriptor *desc; + struct dma_async_tx_descriptor *txdesc, *rxdesc; u32 val; - /* Prepare the TX descriptor and submit it */ - desc = k1_spi_dma_prep(drv_data, transfer, true); - if (!desc) + /* Prepare the TX descriptor */ + txdesc = k1_spi_dma_prep(drv_data, transfer, true); + if (!txdesc) goto fallback; - dmaengine_submit(desc); - /* Prepare the RX descriptor and submit it */ - desc = k1_spi_dma_prep(drv_data, transfer, false); - if (!desc) + /* Prepare the RX descriptor */ + rxdesc = k1_spi_dma_prep(drv_data, transfer, false); + if (!rxdesc) goto fallback; /* When RX is complete we also know TX has completed */ - desc->callback = k1_spi_dma_callback; - desc->callback_param = drv_data; + rxdesc->callback = k1_spi_dma_callback; + rxdesc->callback_param = drv_data; - dmaengine_submit(desc); + dmaengine_submit(txdesc); + dmaengine_submit(rxdesc); val = readl(drv_data->base + SSP_TOP_CTRL); val |= TOP_TRAIL; /* Trailing bytes handled by DMA */ From d6f0248f04a96249660591e47fcf37ba98ac7ea3 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Tue, 30 Jun 2026 14:57:54 -0700 Subject: [PATCH 080/501] mshv: fix hv_input_get_system_property struct Keep it in sync with the correct definition. The old code worked by chance. Fixes: e68bda71a2384 ("hyperv: Add new Hyper-V headers in include/hyperv") Cc: stable@kernel.org Signed-off-by: Wei Liu --- include/hyperv/hvhdk_mini.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hyperv/hvhdk_mini.h b/include/hyperv/hvhdk_mini.h index b4cb2fa26e9b..035ba20870f7 100644 --- a/include/hyperv/hvhdk_mini.h +++ b/include/hyperv/hvhdk_mini.h @@ -184,8 +184,9 @@ enum hv_dynamic_processor_feature_property { struct hv_input_get_system_property { u32 property_id; /* enum hv_system_property */ + u32 reserved; union { - u32 as_uint32; + u64 as_uint64; #if IS_ENABLED(CONFIG_X86) /* enum hv_dynamic_processor_feature_property */ u32 hv_processor_feature; From 233a4d3a39fc1585f5e271b2adab43c6af025ae0 Mon Sep 17 00:00:00 2001 From: Ruoyu Wang Date: Tue, 7 Jul 2026 23:05:28 +0800 Subject: [PATCH 081/501] drm/mediatek: Check CRTC state before freeing mtk_crtc_reset() destroys the current CRTC state only when crtc->state is non-NULL, but it always converts crtc->state to struct mtk_crtc_state and passes the result to kfree(). When reset is called without an existing state, container_of(NULL, ...) does not produce NULL. Keep the mtk state free in the same crtc->state guard as the helper state destruction. This issue was found by a static analysis checker and confirmed by manual source review. Fixes: 2d267b81898e ("drm/mtk: Use __drm_atomic_helper_crtc_reset") Signed-off-by: Ruoyu Wang Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260707150528.2270739-1-ruoyuw560@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_crtc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_crtc.c b/drivers/gpu/drm/mediatek/mtk_crtc.c index 8e552cdc3b53..97e3ff412e6e 100644 --- a/drivers/gpu/drm/mediatek/mtk_crtc.c +++ b/drivers/gpu/drm/mediatek/mtk_crtc.c @@ -154,10 +154,10 @@ static void mtk_crtc_reset(struct drm_crtc *crtc) { struct mtk_crtc_state *state; - if (crtc->state) + if (crtc->state) { __drm_atomic_helper_crtc_destroy_state(crtc->state); - - kfree(to_mtk_crtc_state(crtc->state)); + kfree(to_mtk_crtc_state(crtc->state)); + } crtc->state = NULL; state = kzalloc_obj(*state); From d19d8542808c3be5f4af849da6509e877b74d811 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Mon, 13 Jul 2026 19:29:57 +0800 Subject: [PATCH 082/501] drm/mediatek: mtk_hdmi: Fix DDC adapter double put in v2 mtk_hdmi_common_probe() gets the DDC adapter with of_find_i2c_adapter_by_node() and registers a devm action to release the adapter device reference with put_device(). The HDMI v2 remove callback also calls i2c_put_adapter() on the same DDC adapter. This is not paired with of_find_i2c_adapter_by_node(): it drops the adapter device reference before the devm action drops it again, and it also puts a module reference that was never taken. Remove the extra i2c_put_adapter() call and drop the now-empty HDMI v2 remove callback. The common devm action releases the adapter device reference. Fixes: 8d0f79886273 ("drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188") Cc: stable@vger.kernel.org Reviewed-by: Johan Hovold Signed-off-by: Guangshuo Li Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260713112957.884640-1-lgs201920130244@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_hdmi_v2.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c index 7bbf463056c9..ffe456238a2b 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c @@ -1499,13 +1499,6 @@ static int mtk_hdmi_v2_probe(struct platform_device *pdev) return 0; } -static void mtk_hdmi_v2_remove(struct platform_device *pdev) -{ - struct mtk_hdmi *hdmi = platform_get_drvdata(pdev); - - i2c_put_adapter(hdmi->ddc_adpt); -} - static const struct of_device_id mtk_drm_hdmi_v2_of_ids[] = { { .compatible = "mediatek,mt8188-hdmi-tx", .data = &mtk_hdmi_conf_mt8188 }, { .compatible = "mediatek,mt8195-hdmi-tx", .data = &mtk_hdmi_conf_mt8195 }, @@ -1515,7 +1508,6 @@ MODULE_DEVICE_TABLE(of, mtk_drm_hdmi_v2_of_ids); static struct platform_driver mtk_hdmi_v2_driver = { .probe = mtk_hdmi_v2_probe, - .remove = mtk_hdmi_v2_remove, .driver = { .name = "mediatek-drm-hdmi-v2", .of_match_table = mtk_drm_hdmi_v2_of_ids, From ab96bc97cdcf77c6f38634c73d00074c4f92c324 Mon Sep 17 00:00:00 2001 From: Michael Kelley Date: Sun, 7 Jun 2026 19:06:16 -0700 Subject: [PATCH 083/501] Drivers: hv: vmbus: Set DMA coherent mask for VMBus devices In current code, the coherent_dma_mask for VMBus devices is not set, so it has the default value of 0, which essentially means "invalid". Because drivers for VMBus devices do not use dma_alloc_*() functions, the usual use of the coherent mask does not occur, and no errors result. However, a valid coherent_dma_mask may be needed even though the drivers don't use dma_alloc_*() functions. In a CoCo VM, the VMBus storvsc and netvsc drivers must bounce buffer DMA operations through the swiotlb because the Hyper-V host can't DMA into encrypted guest memory. If the kernel is built with CONFIG_SWIOTLB_DYNAMIC and the initial swiotlb size is small, swiotlb code may need to grow the swiotlb in response to a DMA mapping request. That growth first allocates a transient pool while the swiotlb is expanded in the background. The transient pool memory is allocated from the DMA atomic pools, and the allocation code checks for a valid coherent_dma_mask. With current code, this check fails, then the DMA mapping request from the storvsc or netvsc driver fails, and finally an I/O error occurs. Fix this problem by setting coherent_dma_mask for VMBus devices at the same time that dma_mask is set. Being a synthetic bus, VMBus does not have any restrictions on coherent DMA, so the coherent mask is set to the full 64 bits for all VMBus devices, just like with dma_mask. Signed-off-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 23206640c613..a6b9a33db657 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -2169,6 +2169,7 @@ int vmbus_device_register(struct hv_device *child_device_obj) child_device_obj->device.dma_parms = &child_device_obj->dma_parms; child_device_obj->device.dma_mask = &child_device_obj->dma_mask; dma_set_mask(&child_device_obj->device, DMA_BIT_MASK(64)); + dma_set_coherent_mask(&child_device_obj->device, DMA_BIT_MASK(64)); /* * Register with the LDM. This will kick off the driver/device From b496f042e0da26dd673806870c204adfe697f169 Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Thu, 25 Jun 2026 20:13:14 +0200 Subject: [PATCH 084/501] mshv_vtl: clear hypercall output before copyout mshv_vtl_hvcall_call() copies output_size bytes to userspace. The output page is freshly allocated. Userspace chooses the copyout length. If the hypercall writes less, the tail can contain stale page data. Clear the copied range before issuing the hypercall. Also check both bounce page allocations before either page is used. Signed-off-by: Yousef Alhouseen Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 0d3d4161974f..dbf03b6676cd 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -1148,12 +1148,22 @@ static int mshv_vtl_hvcall_call(struct mshv_vtl_hvcall_fd *fd, */ in = (void *)__get_free_page(GFP_KERNEL); out = (void *)__get_free_page(GFP_KERNEL); + if (!in || !out) { + ret = -ENOMEM; + goto free_pages; + } if (copy_from_user(in, (void __user *)hvcall.input_ptr, hvcall.input_size)) { ret = -EFAULT; goto free_pages; } + /* + * The caller supplies output_size, so clear the range copied back to + * userspace in case the hypercall writes fewer bytes than requested. + */ + memset(out, 0, hvcall.output_size); + hvcall.status = hv_do_hypercall(hvcall.control, in, out); if (copy_to_user((void __user *)hvcall.output_ptr, out, hvcall.output_size)) { From ec08dd5b9ffa52980908805bdc8a55a8f1bc6667 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Wed, 8 Jul 2026 09:28:52 +0800 Subject: [PATCH 085/501] mshv_vtl: fix fd leak in mshv_ioctl_create_vtl() put_unused_fd() if anon_inode_getfile() fails. Fixes: 7bfe3b8ea6e30 ("Drivers: hv: Introduce mshv_vtl driver") Signed-off-by: Yi Xie Reviewed-by: Hamza Mahfooz Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index dbf03b6676cd..5ba1efb3b4e7 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -129,6 +129,7 @@ mshv_ioctl_create_vtl(void __user *user_arg, struct device *module_dev) file = anon_inode_getfile("mshv_vtl", &mshv_vtl_fops, vtl, O_RDWR); if (IS_ERR(file)) { + put_unused_fd(fd); kfree(vtl); return PTR_ERR(file); } From 8c7ab779c8850f4dab8473463cca9a7d52fdaecc Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Jul 2026 17:32:15 +0200 Subject: [PATCH 086/501] Drivers: hv: vmbus: Replace lockdep_hardirq_threaded() with lockdep annotation lockdep_hardirq_threaded() is supposed to be used within IRQ core code and not within drivers. It is not obvious from within the driver, that this is the only interrupt service routing and that it is not shared handler. Replace lockdep_hardirq_threaded() with a lockdep annotation limiting threaded context on PREEMPT_RT to __vmbus_isr(). Fixes: f8e6343b7a89c ("Drivers: hv: vmbus: Use kthread for vmbus interrupts on PREEMPT_RT") Signed-off-by: Sebastian Andrzej Siewior Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index a6b9a33db657..7d095c0add25 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1384,8 +1384,19 @@ void vmbus_isr(void) if (IS_ENABLED(CONFIG_PREEMPT_RT)) { vmbus_irqd_wake(); } else { - lockdep_hardirq_threaded(); + static DEFINE_WAIT_OVERRIDE_MAP(vmbus_map, LD_WAIT_CONFIG); + + /* + * vmbus_isr is never force-threaded and always invoked at hard + * IRQ level. __vmbus_isr() below can acquire a spinlock_t + * which becomes a sleeping lock and must not be acquired in + * this context. Therefore on PREEMPT_RT this will be threaded + * via vmbus_irqd_wake(). On non-PREEMPT the annotation lets + * lockdep know that acquiring a spinlock_t is not an issue. + */ + lock_map_acquire_try(&vmbus_map); __vmbus_isr(); + lock_map_release(&vmbus_map); } } EXPORT_SYMBOL_FOR_MODULES(vmbus_isr, "mshv_vtl"); From 66688e655dcb1e9d06fa2c0651bd9fd588d2a2c3 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Tue, 21 Jul 2026 17:32:16 +0200 Subject: [PATCH 087/501] Drivers: hv: vmbus: Remove vmbus_irq_initialized vmbus_irq_initialized is only true if the registration of the per-CPU threads succeeded. If it failed, the whole registration aborts and the vmbus_exit() path is never called. Remove vmbus_irq_initialized. Reviewed-by: Michael Kelley Signed-off-by: Sebastian Andrzej Siewior Signed-off-by: Wei Liu --- drivers/hv/vmbus_drv.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 7d095c0add25..6824bd7cb3c4 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1369,8 +1369,6 @@ static void run_vmbus_irqd(unsigned int cpu) __vmbus_isr(); } -static bool vmbus_irq_initialized; - static struct smp_hotplug_thread vmbus_irq_threads = { .store = &vmbus_irqd, .setup = vmbus_irqd_setup, @@ -1497,11 +1495,10 @@ static int vmbus_bus_init(void) * the VMbus interrupt handler. */ - if (IS_ENABLED(CONFIG_PREEMPT_RT) && !vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) { ret = smpboot_register_percpu_thread(&vmbus_irq_threads); if (ret) goto err_kthread; - vmbus_irq_initialized = true; } if (vmbus_irq == -1) { @@ -1545,10 +1542,8 @@ static int vmbus_bus_init(void) else free_percpu_irq(vmbus_irq, &vmbus_evt); err_setup: - if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) smpboot_unregister_percpu_thread(&vmbus_irq_threads); - vmbus_irq_initialized = false; - } err_kthread: bus_unregister(&hv_bus); return ret; @@ -3046,10 +3041,9 @@ static void __exit vmbus_exit(void) hv_remove_vmbus_handler(); else free_percpu_irq(vmbus_irq, &vmbus_evt); - if (IS_ENABLED(CONFIG_PREEMPT_RT) && vmbus_irq_initialized) { + if (IS_ENABLED(CONFIG_PREEMPT_RT)) smpboot_unregister_percpu_thread(&vmbus_irq_threads); - vmbus_irq_initialized = false; - } + for_each_online_cpu(cpu) { struct hv_per_cpu_context *hv_cpu = per_cpu_ptr(hv_context.cpu_context, cpu); From 649dd135491945afa544351e3e6d4a727de87020 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:49 +0000 Subject: [PATCH 088/501] mshv: Fix duplicate GSI detection for GSI 0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The duplicate routing entry check in mshv_update_routing_table() uses guest_irq_num != 0 to detect whether a GSI slot is already occupied. This fails for GSI 0 because its guest_irq_num is 0 both when the slot is unused (zero-initialized) and when legitimately assigned. As a result, duplicate entries for GSI 0 are silently accepted, with the second entry overwriting the first — corrupting the routing table without any error reported to userspace. While GSI 0 (legacy timer) is unlikely to appear in MSI-based routing in practice, the check is semantically wrong — it conflates "uninitialized" with "GSI number 0." Use girq_entry_valid instead, which is explicitly set to true when an entry is populated and remains zero for unused slots regardless of the GSI number. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_irq.c b/drivers/hv/mshv_irq.c index b3142c84dcbc..65a4ffc82d56 100644 --- a/drivers/hv/mshv_irq.c +++ b/drivers/hv/mshv_irq.c @@ -51,7 +51,7 @@ int mshv_update_routing_table(struct mshv_partition *partition, /* * Allow only one to one mapping between GSI and MSI routing. */ - if (girq->guest_irq_num != 0) { + if (girq->girq_entry_valid) { r = -EINVAL; goto out; } From a9708e550d11a53b22d932cdbfaa10c26346a2d0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:59 +0000 Subject: [PATCH 089/501] mshv: Fix sleeping under spinlock in mshv_portid_alloc idr_alloc() is called with GFP_KERNEL inside idr_lock(), which holds a spinlock. GFP_KERNEL allows the allocator to sleep, triggering a sleeping-while-atomic bug. Fix by using idr_preload(GFP_KERNEL) before taking the lock to pre-allocate memory in a sleepable context, then idr_alloc() with GFP_NOWAIT inside the spinlock-protected section. Fixes: 621191d709b1 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_portid_table.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c index c349af1f0aaa..6f59b3e37624 100644 --- a/drivers/hv/mshv_portid_table.c +++ b/drivers/hv/mshv_portid_table.c @@ -40,12 +40,14 @@ mshv_port_table_fini(void) int mshv_portid_alloc(struct port_table_info *info) { - int ret = 0; + int ret; + idr_preload(GFP_KERNEL); idr_lock(&port_table_idr); ret = idr_alloc(&port_table_idr, info, PORTID_MIN, - PORTID_MAX, GFP_KERNEL); + PORTID_MAX, GFP_NOWAIT); idr_unlock(&port_table_idr); + idr_preload_end(); return ret; } From a9df7939a2c5e3794769fde809ea892e5a414b93 Mon Sep 17 00:00:00 2001 From: Jorn Baayen Date: Tue, 21 Jul 2026 17:19:23 +0200 Subject: [PATCH 090/501] ASoC: amd: yc: Add DMI quirk for Acer Aspire AG14-22P The Acer Aspire AG14-22P has its internal microphone connected to the ACP as a digital microphone, but the BIOS does not advertise it via the AcpDmicConnected ACPI property, so the internal microphone does not work out of the box. Add a DMI quirk to enable it. Tested on an Aspire AG14-22P (board Dove2_MDU, BIOS V1.03): the acp6x DMIC card is created and the internal microphone captures audio. Assisted-by: Claude-Code:claude-fable-5 Signed-off-by: Jorn Baayen Link: https://patch.msgid.link/20260721151924.30316-1-jhbaayen@fastmail.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 0d93e45b6178..de5c895e9fbd 100644 --- a/sound/soc/amd/yc/acp6x-mach.c +++ b/sound/soc/amd/yc/acp6x-mach.c @@ -45,6 +45,13 @@ static struct snd_soc_card acp6x_card = { }; static const struct dmi_system_id yc_acp_quirk_table[] = { + { + .driver_data = &acp6x_card, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire AG14-22P"), + } + }, { .driver_data = &acp6x_card, .matches = { From 160a783aa65b74782bc17cb874af1a6d3f5fba3c Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 14:54:44 +0800 Subject: [PATCH 091/501] power: supply: bq25890: fix the -10 C NTC lookup entry The TSPCT lookup table is monotonically decreasing except for ADC code 121, where the sequence reads -9.0 C, -1.0 C, -12.0 C. This makes the reported battery temperature jump upward by eight degrees for one code and then downward by eleven degrees for the next code. The entry is a missing zero: use -10.0 C so the sequence remains monotonic between -9.0 C and -12.0 C. Fixes: 9652c02428f3 ("power: bq25890: add POWER_SUPPLY_PROP_TEMP") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/0619C8BF15F43B7C+20260723065444.1796002-1-raoxu@uniontech.com Signed-off-by: Sebastian Reichel --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index c1c12a447178..180bc137a863 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -320,7 +320,7 @@ static const u32 bq25890_tspct_tbl[] = { 145, 140, 130, 120, 115, 110, 100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0, -10, -20, -30, -40, -60, -70, -80, - -90, -10, -120, -140, -150, -170, -190, -210, + -90, -100, -120, -140, -150, -170, -190, -210, }; #define BQ25890_TSPCT_TBL_SIZE ARRAY_SIZE(bq25890_tspct_tbl) From d4a00d61a5c2c24973175ace5368d1f6acf9bb0a Mon Sep 17 00:00:00 2001 From: Kuba Piecuch Date: Thu, 23 Jul 2026 09:59:55 +0000 Subject: [PATCH 092/501] selftests/sched_ext: Handle sleeping task affinity changes in numa test When a sleeping task's affinity is changed, task_cpu(p) can be outside of p->cpus_ptr until after select_task_rq() selects a new runqueue for the task during wakeup. Thus, the task's NUMA node determined by numa_select_cpu() can be completely outside of the task's cpumask, leading to scx_pick_{idle,any}_cpu_node() failing to find an eligible CPU and returning -EBUSY. This leads to the numa.bpf.c scheduler abnormally exiting with the following message in dmesg: sched_ext: numa: invalid CPU -16 scx_bpf_cpu_node+0x120/0x190 bpf_prog_0a34b8e0f515771f_numa_select_cpu+0x108/0x14e bpf__sched_ext_ops_select_cpu+0x4f/0xb4 select_task_rq_scx+0xb0/0x210 select_task_rq+0xa0/0xd0 __try_to_wake_up+0x196/0x650 complete_all+0x76/0x100 migration_cpu_stop+0x22b/0x300 cpu_stopper_thread+0xc1/0x180 smpboot_thread_fn+0x16b/0x230 kthread+0x2d7/0x350 ret_from_fork+0x1c2/0x350 ret_from_fork_asm+0x1a/0x30 Make numa_select_cpu() robust against this case by returning @prev_cpu if no CPU could be found in the selected NUMA node _and_ we have reason to believe that the task's affinity was changed while it was sleeping. Fixes: 5ae5161820e5 ("selftests/sched_ext: Add NUMA-aware scheduler test") Signed-off-by: Kuba Piecuch Signed-off-by: Tejun Heo --- tools/testing/selftests/sched_ext/numa.bpf.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/sched_ext/numa.bpf.c b/tools/testing/selftests/sched_ext/numa.bpf.c index 78cc49a7f9a6..6b4515c28aa0 100644 --- a/tools/testing/selftests/sched_ext/numa.bpf.c +++ b/tools/testing/selftests/sched_ext/numa.bpf.c @@ -34,7 +34,8 @@ static bool is_cpu_idle(s32 cpu, int node) s32 BPF_STRUCT_OPS(numa_select_cpu, struct task_struct *p, s32 prev_cpu, u64 wake_flags) { - int node = __COMPAT_scx_bpf_cpu_node(scx_bpf_task_cpu(p)); + s32 task_cpu = scx_bpf_task_cpu(p); + int node = __COMPAT_scx_bpf_cpu_node(task_cpu); s32 cpu; /* @@ -48,6 +49,16 @@ s32 BPF_STRUCT_OPS(numa_select_cpu, cpu = __COMPAT_scx_bpf_pick_any_cpu_node(p->cpus_ptr, node, __COMPAT_SCX_PICK_IDLE_IN_NODE); + /* + * @task_cpu may be outside of p->cpus_ptr if @p's affinity + * changed while it was sleeping. This means it's possible for + * p->cpus_ptr to not include any CPUs from @node. + * If we failed to find a cpu in @node, check if @task_cpu + * is outside of p->cpus_ptr and just return @prev_cpu if it is. + */ + if (cpu < 0 && !bpf_cpumask_test_cpu(task_cpu, p->cpus_ptr)) + return prev_cpu; + if (is_cpu_idle(cpu, node)) scx_bpf_error("CPU %d should be marked as busy", cpu); From 90ef2f2961c2dc55957dafe2f53b3efdb4675efc Mon Sep 17 00:00:00 2001 From: Vijaya Krishna Nivarthi Date: Wed, 22 Jul 2026 14:53:58 +0530 Subject: [PATCH 093/501] spi: qcom-qspi: Correct max DMA length to avoid 64K boundary failure The maximum size for a DMA data descriptor is 64KB-1 because the size field in HW is 16 bits wide. For this reason, transfers fail at 64KB and beyond. Lower max_dma_len to 60KB so larger transfers are split into multiple DMA blocks and do not hit the failing 64KB boundary. 60KB is chosen as a safe round number below the 64KB-1 hardware limit while satisfying alignment requirements. Tested on x1e80100 (Hamoa) with SPI-NOR flash (/dev/mtd0): Without patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=32768 count=2 # works dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # fails With patch: dd if=/dev/mtd0 of=/tmp/spi_dump.bin bs=65536 count=1 # works Fixes: b5762d95607e ("spi: spi-qcom-qspi: Add DMA mode support") Cc: stable@vger.kernel.org Signed-off-by: Vijaya Krishna Nivarthi Link: https://patch.msgid.link/20260722092358.459943-1-vnivarth@qti.qualcomm.com Signed-off-by: Mark Brown --- drivers/spi/spi-qcom-qspi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/spi/spi-qcom-qspi.c b/drivers/spi/spi-qcom-qspi.c index caf55a6f70b3..e9fdefa9d65d 100644 --- a/drivers/spi/spi-qcom-qspi.c +++ b/drivers/spi/spi-qcom-qspi.c @@ -775,7 +775,8 @@ static int qcom_qspi_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "could not set DMA mask\n"); host->max_speed_hz = 300000000; - host->max_dma_len = 65536; /* as per HPG */ + /* as per HPG, it is 64KB, limit to 60KB to avoid boundary condition failures */ + host->max_dma_len = 0xf000; host->dma_alignment = QSPI_ALIGN_REQ; host->num_chipselect = QSPI_NUM_CS; host->bus_num = -1; From af421e9aed3920c7ac88c24daa48606c7112feca Mon Sep 17 00:00:00 2001 From: Michael Bommarito Date: Mon, 22 Jun 2026 08:47:22 -0400 Subject: [PATCH 094/501] um: vector: fix use-after-free in vector_mmsg_rx() When vector_mmsg_rx() discards a packet whose overlay header fails verify_header(), it frees the skb and continues the loop: if (header_check < 0) { dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; continue; } The normal and short-packet paths fall through to the bottom of the loop body, which clears the consumed slot and advances the cursors: (*skbuff_vector) = NULL; mmsg_vector++; skbuff_vector++; The verify_header() < 0 path skips that via continue, so the freed skb is left in skbuff_vector[] and the cursors do not advance. The next iteration reads the same slot, gets the freed skb, and frees it again, producing a refcount underflow / use-after-free in the RX path. Discard the slot the same way the other paths do before continuing. Only transports whose verify_header() can return negative are affected: GRE and L2TPv3 do so on a cookie/session-id mismatch (raw/tap do not), so any peer on such a transport can trigger it without authentication. Fixes: 49da7e64f33e ("High Performance UML Vector Network Driver") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito Signed-off-by: Richard Weinberger --- arch/um/drivers/vector_kern.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c index 2cc90055499a..8a70b3a62553 100644 --- a/arch/um/drivers/vector_kern.c +++ b/arch/um/drivers/vector_kern.c @@ -995,6 +995,9 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget) */ dev_kfree_skb_irq(skb); vp->estats.rx_encaps_errors++; + (*skbuff_vector) = NULL; + mmsg_vector++; + skbuff_vector++; continue; } if (header_check > 0) { From 6a50332e194f58b562d396ab772c34e8c9890c0f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:57 +0100 Subject: [PATCH 095/501] ASoC: SDCA: Correct pointer passed to devm_acpi_table_put devm_acpi_table_put() takes a struct acpi_table_header * but the value passed in is struct acpi_table_header ** so the value passed to acpi_put_table() is actually the pointer not the table itself. Remove the extra reference to correct the passed value. Fixes: c4d096c3ca42 ("ASoC: SDCA: Add SDCA FDL data parsing") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c index 405e80b979de..4bcd8d1fdff8 100644 --- a/sound/soc/sdca/sdca_device.c +++ b/sound/soc/sdca/sdca_device.c @@ -43,7 +43,7 @@ void sdca_lookup_swft(struct sdw_slave *slave) dev_info(&slave->dev, "SWFT not available\n"); else devm_add_action_or_reset(&slave->dev, devm_acpi_table_put, - &slave->sdca_data.swft); + slave->sdca_data.swft); } EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA"); From 7f64ccc374b2fe1f6a169182e95ab8e104cae406 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:58 +0100 Subject: [PATCH 096/501] ASoC: SDCA: Always free firmware in FDL path In the case a disk firmware exists but is invalid and no SWFT firmware exists fdl_load_file() will return without calling release_firmware(). Update the code to call this to ensure the firmware is released on the error path. Fixes: 71f7990a34cd ("ASoC: SDCA: Add FDL library for XU entities") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_fdl.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 994821a6df61..60fdd406220d 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -258,7 +258,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, if (!swf) { dev_err(dev, "failed to locate SWF\n"); - return -ENOENT; + ret = -ENOENT; + goto error; } dev_info(dev, "loading SWF: %x-%x-%x\n", @@ -270,6 +271,8 @@ static int fdl_load_file(struct sdca_interrupt *interrupt, SDCA_CTL_XU_FDL_MESSAGEOFFSET, fdl_file->fdl_offset, SDCA_CTL_XU_FDL_MESSAGELENGTH, swf->data, swf->file_length - offsetof(struct acpi_sw_file, data)); + +error: release_firmware(firmware); return ret; } From 556d872e7c2a0b570c5b0974813847ef0d0cd637 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:34:59 +0100 Subject: [PATCH 097/501] ASoC: SDCA: Make UMP message size check more robust If message offset was larger than the buffer length the size check will pass incorrectly. Refactor the check such that it is more robust to invalid sizes. Fixes: daab108504be ("ASoC: SDCA: Add UMP buffer helper functions") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_ump.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/sdca/sdca_ump.c b/sound/soc/sdca/sdca_ump.c index a86bb28c6d0a..82a8bf75bbca 100644 --- a/sound/soc/sdca/sdca_ump.c +++ b/sound/soc/sdca/sdca_ump.c @@ -141,7 +141,7 @@ int sdca_ump_read_message(struct device *dev, return ret; } - if (msg_len > buf_len - msg_offset) { + if (msg_offset + msg_len > buf_len) { dev_err(dev, "%s: message too big for UMP buffer: %d\n", entity->label, msg_len); return -EINVAL; @@ -207,7 +207,7 @@ int sdca_ump_write_message(struct device *dev, buf_len = sdca_range(range, SDCA_MESSAGEOFFSET_BUFFER_LENGTH, 0); ump_mode = sdca_range(range, SDCA_MESSAGEOFFSET_UMP_MODE, 0); - if (msg_len > buf_len - msg_offset) { + if (msg_offset + msg_len > buf_len) { dev_err(dev, "%s: message too big for UMP buffer: %d\n", entity->label, msg_len); return -EINVAL; From 951e921b039b793bef7050eaf5c5fb1a4a5341d1 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 22 Jul 2026 11:35:00 +0100 Subject: [PATCH 098/501] ASoC: SDCA: Ensure that Control Range is large enough for header When reading the Ranges structure from an SDCA Control, ensure that the read data is large enough to encompass the required header before accessing it. Fixes: 64fb5af1d1bb ("ASoC: SDCA: Add parsing for Control range structures") Signed-off-by: Charles Keepax Reviewed-by: Pierre-Louis Bossart Link: https://patch.msgid.link/20260722103500.872714-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_functions.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c index 77940bd6b33c..7a7a9f1a4938 100644 --- a/sound/soc/sdca/sdca_functions.c +++ b/sound/soc/sdca/sdca_functions.c @@ -855,6 +855,8 @@ static int find_sdca_control_range(struct device *dev, return 0; else if (num_range < 0) return num_range; + else if (num_range < 2 * sizeof(*limits)) + return -EINVAL; range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL); if (!range_list) From 52a3a0cc28bf6fd8b38ef6c81b21f948bf13996a Mon Sep 17 00:00:00 2001 From: Balamurugan C Date: Thu, 23 Jul 2026 14:21:21 +0800 Subject: [PATCH 099/501] ASoC: Intel: soc-acpi: Add entry for sof_rt5682 in NVL match table. Adding rt5682 I2S codec support for NVL platforms and entry in match table. Signed-off-by: Balamurugan C Signed-off-by: Bard Liao Link: https://patch.msgid.link/20260723062121.869857-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-nvl-match.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c index 217272260803..a7f4097d426c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-nvl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-nvl-match.c @@ -8,6 +8,7 @@ #include #include +#include #include "soc-acpi-intel-sdw-mockup-match.h" static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { @@ -15,6 +16,11 @@ static const struct snd_soc_acpi_codecs nvl_essx_83x6 = { .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"}, }; +static const struct snd_soc_acpi_codecs nvl_rt5682_rt5682s_hp = { + .num_codecs = 2, + .codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID}, +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { { .comp_ids = &nvl_essx_83x6, @@ -24,6 +30,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_nvl_machines[] = { SND_SOC_ACPI_TPLG_INTEL_SSP_MSB | SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER, }, + { + .comp_ids = &nvl_rt5682_rt5682s_hp, + .drv_name = "sof_rt5682", + .sof_tplg_filename = "sof-nvl-rt5682", /* the tplg suffix is added at run time */ + .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER | + SND_SOC_ACPI_TPLG_INTEL_SSP_MSB, + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_nvl_machines); From d8a0962d3506f7c9fdc3cc49c81ff0b8ded09f2f Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Thu, 16 Jul 2026 15:21:03 +0200 Subject: [PATCH 100/501] drm/mediatek: mtk_hdmi_common: take i2c adapter module reference The i2c subsystem currently blocks during adapter deregistration whenever there are consumers holding a reference. Switch to using of_get_i2c_adapter_by_node() which also takes a reference to the adapter module so that an attempt to unload the module while in use fails gracefully instead of blocking uninterruptibly. Signed-off-by: Johan Hovold Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260716132103.1564995-1-johan@kernel.org/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_hdmi_common.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c index a049489daa2d..042b98e5a519 100644 --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c @@ -290,6 +290,13 @@ static int mtk_hdmi_get_cec_dev(struct mtk_hdmi *hdmi, struct device *dev, struc return 0; } +static void mtk_hdmi_put_adapter(void *_adap) +{ + struct i2c_adapter *adap = _adap; + + i2c_put_adapter(adap); +} + static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device *pdev, const char * const *clk_names, size_t num_clocks) { @@ -328,12 +335,12 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device if (!i2c_np) return dev_err_probe(dev, -EINVAL, "No ddc-i2c-bus in connector\n"); - hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np); + hdmi->ddc_adpt = of_get_i2c_adapter_by_node(i2c_np); of_node_put(i2c_np); if (!hdmi->ddc_adpt) return dev_err_probe(dev, -EPROBE_DEFER, "Failed to get ddc i2c adapter by node\n"); - ret = devm_add_action_or_reset(dev, mtk_hdmi_put_device, &hdmi->ddc_adpt->dev); + ret = devm_add_action_or_reset(dev, mtk_hdmi_put_adapter, hdmi->ddc_adpt); if (ret) return ret; From 112badb7245059c176e3924f9cd9ce2933cb0082 Mon Sep 17 00:00:00 2001 From: Alessio Belle Date: Mon, 20 Jul 2026 16:07:28 +0100 Subject: [PATCH 101/501] drm/imagination: Fix value of HWRT address in pvr_job_create tracepoint The pvr_job_create tracepoint was using pvr_fw_object::fw_addr_offset to display the firmware address of the render target structure attached to a job, but that's an offset into the firmware heap, not the expected full address, which is more useful in general e.g. for cross referencing against firmware logs. Use pvr_fw_object_get_fw_addr() to get the full firmware address. Fixes: c1079aebb4de ("drm/imagination: Add support for trace points") Reviewed-by: Brajesh Gupta Link: https://patch.msgid.link/20260720-fix-pvr-job-trace-hwrt-v1-1-b24551802efb@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_trace.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_trace.h b/drivers/gpu/drm/imagination/pvr_trace.h index ffbbde3b2bcc..943d6b2418cb 100644 --- a/drivers/gpu/drm/imagination/pvr_trace.h +++ b/drivers/gpu/drm/imagination/pvr_trace.h @@ -41,6 +41,17 @@ TRACE_EVENT(pvr_job_submit_ioctl, __entry->count) ); +#define PVR_JOB_GET_HWRT_FW_ADDR(job) \ + ({ \ + struct pvr_job *_job = (job); \ + u32 _hwrt_fw_addr = 0; \ + \ + if (_job && _job->hwrt) \ + pvr_fw_object_get_fw_addr(_job->hwrt->fw_obj, &_hwrt_fw_addr); \ + \ + _hwrt_fw_addr; \ + }) + #define PVR_JOB_TYPE_TO_STR(val) \ __print_symbolic(val, \ { DRM_PVR_JOB_TYPE_GEOMETRY, "geometry" }, \ @@ -64,9 +75,7 @@ TRACE_EVENT(pvr_job_create, __entry->ctx = job->ctx; __entry->fw_obj = job->ctx->fw_obj; pvr_fw_object_get_fw_addr(job->ctx->fw_obj, &__entry->fw_addr); - __entry->hwrt_addr = job->hwrt ? - job->hwrt->fw_obj->fw_addr_offset : - 0; + __entry->hwrt_addr = PVR_JOB_GET_HWRT_FW_ADDR(job); __entry->job = job; __entry->job_type = job->type; __entry->sync_op_count = sync_op_count;), @@ -82,6 +91,7 @@ TRACE_EVENT(pvr_job_create, ); #undef PVR_JOB_TYPE_TO_STR +#undef PVR_JOB_GET_HWRT_FW_ADDR TRACE_EVENT(pvr_job_submit_fw, TP_PROTO(struct pvr_job *job), From acd8aa3c4b91a38c8521000790890bc9d1083f1d Mon Sep 17 00:00:00 2001 From: Markus Lindner Date: Wed, 22 Jul 2026 03:09:18 +0200 Subject: [PATCH 102/501] ALSA: usb-audio: Add dB map quirk for Razer Barracuda X 2.4 The Razer Barracuda X 2.4 GHz USB headset dongle (0x1532:0x0552) reports a minimum volume register value of cval->min = -16800. In UAC 1/256 dB units, -16800 corresponds to -65.625 dB. However, stock ALSA misinterprets this raw integer as 1/100 dB units (-168.00 dB), causing user-space audio servers (PipeWire / PulseAudio) to map their volume curves against an incorrectly wide range. Add an explicit usbmix_dB_map entry overriding Unit 2 to -6562 (-65.62 dB) to accurately report the physical hardware attenuation bounds. Signed-off-by: Markus Lindner Link: https://patch.msgid.link/AS8P195MB2142F4EFF83980BD02BA6566E1C12@AS8P195MB2142.EURP195.PROD.OUTLOOK.COM Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index faac7df1fbcf..4d8dca04f87d 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -344,6 +344,16 @@ static const struct usbmix_name_map bose_soundlink_map[] = { { 0 } /* terminator */ }; +/* + * Razer Barracuda X 2.4: Firmware reports cval->min = -16800 in 1/256 dB units + * (-65.62 dB), which stock ALSA misinterprets as a -168 dB floor + */ +static const struct usbmix_dB_map razer_barracuda_x_2_4_dB = {-6562, 0}; +static const struct usbmix_name_map razer_barracuda_x_2_4_map[] = { + { 2, NULL, .dB = &razer_barracuda_x_2_4_dB }, + { 0 } /* terminator */ +}; + /* Sennheiser Communications Headset [PC 8], the dB value is reported as -6 negative maximum */ static const struct usbmix_dB_map sennheiser_pc8_dB = {-9500, 0}; static const struct usbmix_name_map sennheiser_pc8_map[] = { @@ -689,6 +699,11 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x17aa, 0x1046), .map = lenovo_p620_rear_map, }, + { + /* Razer Barracuda X 2.4 */ + .id = USB_ID(0x1532, 0x0552), + .map = razer_barracuda_x_2_4_map, + }, { /* Sennheiser Communications Headset [PC 8] */ .id = USB_ID(0x1395, 0x0025), From a3caaa06809248b996254be5b47e10804a3494e2 Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Thu, 16 Jul 2026 16:39:38 +0200 Subject: [PATCH 103/501] drm/panthor: reject firmware sections with oversized data In panthor_fw_load_section_entry(), the data size to copy is calculated without validating it against the allocated section_size: section->data.size = hdr.data.end - hdr.data.start; If a crafted firmware sets data.size larger than the allocated memory, this could cause a heap buffer overflow in panthor_fw_init_section_mem() memcpy(section->mem->kmap, section->data.buf, section->data.size); Additionally, if the section->data.size exceeds the BO size, could this memset underflow the size calculation, leading to a massive out-of-bounds zeroing of kernel memory? memset(section->mem->kmap + section->data.size, 0, panthor_kernel_bo_size(section->mem) - section->data.size); Reject section entries whose initial data is larger than the section size. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Reviewed-by: Boris Brezillon Link: https://patch.msgid.link/20260716143939.21903-1-osama.abdelkader@gmail.com Signed-off-by: Steven Price --- drivers/gpu/drm/panthor/panthor_fw.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 90f59d782a80..0623d37ac617 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -545,6 +545,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, struct panthor_fw_binary_section_entry_hdr hdr; struct panthor_fw_section *section; u32 section_size; + u32 data_size; u32 name_len; int ret; @@ -595,6 +596,13 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, return -EINVAL; } + section_size = hdr.va.end - hdr.va.start; + data_size = hdr.data.end - hdr.data.start; + if (data_size > section_size) { + drm_err(&ptdev->base, "Firmware corrupted, section data exceeds section size\n"); + return -EINVAL; + } + name_len = iter->size - iter->offset; section = drmm_kzalloc(&ptdev->base, sizeof(*section), GFP_KERNEL); @@ -603,7 +611,7 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, list_add_tail(§ion->node, &ptdev->fw->sections); section->flags = hdr.flags; - section->data.size = hdr.data.end - hdr.data.start; + section->data.size = data_size; if (section->data.size > 0) { void *data = drmm_kmalloc(&ptdev->base, section->data.size, GFP_KERNEL); @@ -626,7 +634,6 @@ static int panthor_fw_load_section_entry(struct panthor_device *ptdev, section->name = name; } - section_size = hdr.va.end - hdr.va.start; if (section_size) { u32 cache_mode = hdr.flags & CSF_FW_BINARY_IFACE_ENTRY_CACHE_MODE_MASK; struct panthor_gem_object *bo; From 645effc0984ba8cd83cdf31e5a29945cd40973e1 Mon Sep 17 00:00:00 2001 From: Frank Li Date: Tue, 30 Jun 2026 16:16:18 -0400 Subject: [PATCH 104/501] MAINTAINERS: ARM/FREESCALE: merge Layerscape entry into i.MX entry Both platforms are maintained by the same people, and the two entries contain largely identical information. Maintaining separate entries adds unnecessary duplication. Additionally, the standalone Layerscape entry does not include the imx@lists.linux.dev mailing list, causing Layerscape patches to miss NXP's Patchwork instance. Consolidating the entries ensures patches for both platforms are routed consistently and reduces future maintenance overhead. Signed-off-by: Frank Li --- MAINTAINERS | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..31188163a859 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2881,7 +2881,7 @@ W: http://www.armlinux.org.uk/ F: arch/arm/include/asm/hardware/dec21285.h F: arch/arm/mach-footbridge/ -ARM/FREESCALE IMX / MXC ARM ARCHITECTURE +ARM/FREESCALE IMX / MXC / LAYERSCAPE ARM ARCHITECTURE M: Frank Li M: Sascha Hauer R: Pengutronix Kernel Team @@ -2895,22 +2895,11 @@ F: Documentation/devicetree/bindings/firmware/nxp* F: arch/arm/boot/dts/nxp/ F: arch/arm64/boot/dts/freescale/ X: Documentation/devicetree/bindings/media/i2c/ -X: arch/arm64/boot/dts/freescale/fsl-* -X: arch/arm64/boot/dts/freescale/qoriq-* X: drivers/media/i2c/ N: imx N: mxs N: \bmxc[^\d] -ARM/FREESCALE LAYERSCAPE ARM ARCHITECTURE -M: Frank Li -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained -T: git git://git.kernel.org/pub/scm/linux/kernel/git/frank.li/linux.git -F: arch/arm/boot/dts/nxp/ls/ -F: arch/arm64/boot/dts/freescale/fsl-* -F: arch/arm64/boot/dts/freescale/qoriq-* - ARM/FREESCALE VYBRID ARM ARCHITECTURE M: Frank Li M: Sascha Hauer From 39d6e68f50f4a444a6ba23b9ea2eaee806601c6d Mon Sep 17 00:00:00 2001 From: Nicolas Schier Date: Thu, 23 Jul 2026 12:58:45 +0200 Subject: [PATCH 105/501] kbuild: Stop modifying $(objtree)/Makefile when building oot-kmods oos Update output Makefile in the corresponding tree only: for out-of-source in-tree builds update $(objtree)/Makefile, for out-of-source out-of-tree module builds update $(KBUILD_EXTMOD_OUTPUT)/Makefile instead. In-source builds are not affected. Since commit c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree"), building out-of-tree kernel modules out-of-source (make M=... MO=...) causes a rewrite of $(objtree)/Makefile with KBUILD_EXTMOD and KBUILD_EXTMOD_OUTPUT being set. That is problematic: * $(objtree)/ must not be changed in any way when building out-of-tree modules as it breaks other uses of $(objtree). * Setting KBUILD_EXTMOD and KBUILD_EXTMOD_OUTPUT in $(objtree)/Makefile kills the tree for incremental builds that start right there ('make -C $(objtree)'); builds starting in $(srctree) reset $(objtree)/Makefile to its original content. Further, $(KBUILD_EXTMOD_OUTPUT)/Makefile was not generated any more at all. This commit restores the previous kbuild behaviour prior to commit c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree") but leaves in-place the use of filechk for output spam reduction. Fixes: c9bb03ac2c66 ("kbuild: reduce output spam when building out of tree") Reported-by: Anish Rashinkar Closes: https://lore.kernel.org/r/CAOESE2Q2-0KUDaM0mUo+c_F-tMaUsBZ-gpnhdoe0rmYdgnnuJQ@mail.gmail.com Cc: stable@vger.kernel.org Tested-by: Philipp Hahn Reviewed-by: Philipp Hahn Signed-off-by: Nicolas Schier Signed-off-by: Nicolas Schier Link: https://patch.msgid.link/20260723105845.1704689-2-nsc@kernel.org Signed-off-by: Nathan Chancellor --- Makefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 11539c3fd405..17d34968b7f0 100644 --- a/Makefile +++ b/Makefile @@ -695,13 +695,11 @@ filechk_makefile = { \ echo "include $(abs_srctree)/Makefile"; \ } -$(objtree)/Makefile: FORCE +PHONY += $(CURDIR)/Makefile +$(CURDIR)/Makefile: FORCE $(call filechk,makefile) -# Prevent $(srcroot)/Makefile from inhibiting the rule to run. -PHONY += $(objtree)/Makefile - -outputmakefile: $(objtree)/Makefile +outputmakefile: $(CURDIR)/Makefile ifeq ($(KBUILD_EXTMOD),) @if [ -f $(srctree)/.config -o \ -d $(srctree)/include/config -o \ From 21e19688433452dfbbbe6b2bb670dea6eb92f0f6 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sat, 25 Jul 2026 08:33:45 +0200 Subject: [PATCH 106/501] ALSA: seq: Fix division by zero in initialize_timer() A userspace-driven ALSA timer (SND_UTIMER) lets an unprivileged user set the backing snd_timer's hardware resolution to an arbitrary 64-bit value via SNDRV_TIMER_IOCTL_CREATE. snd_utimer_create() only rejects zero. When such a timer is bound to a sequencer queue, initialize_timer() computes the tick period as tmr->ticks = 1000000000 / (r * freq); where r is that user-controlled resolution and freq is the sequencer update rate in Hz, clamped to MIN_FREQUENCY..MAX_FREQUENCY (10..6250). A resolution of 2^63 makes the 64-bit product r * freq wrap to zero for any even freq, including DEFAULT_FREQUENCY (1000), so the division faults with a divide-by-zero. The division runs under tmr->lock with interrupts disabled, so the oops leaves the spinlock held and hangs the CPU. It is reachable by an unprivileged user with access to /dev/snd/timer and /dev/snd/seq. Oops: divide error: 0000 [#1] SMP KASAN PTI CPU: 7 UID: 1000 PID: 456 Comm: alsa_seq_utimer Not tainted 7.2.0-rc4+ RIP: 0010:initialize_timer.constprop.0+0x20a/0x2d0 snd_seq_timer_start+0x15e/0x2b0 snd_seq_control_queue+0x56f/0xba0 snd_seq_write+0x3e0/0x730 Reject an overflowing product with check_mul_overflow() and fall back to a single tick, which also avoids feeding a wrapped-but-nonzero divisor (e.g. 2^63 * 1000 mod 2^64 == 0, or other resolutions wrapping to a small value) into the period computation. Fixes: 37745918e0e7 ("ALSA: timer: Introduce virtual userspace-driven timers") Cc: Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/DF8A3844-AD5E-4B8A-9CFC-BD83C212BA38@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_timer.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 419288eec4bb..7b671e270ef4 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c @@ -362,11 +362,10 @@ static int initialize_timer(struct snd_seq_timer *tmr) tmr->ticks = 1; if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { unsigned long r = snd_timer_resolution(tmr->timeri); - if (r) { - tmr->ticks = (unsigned int)(1000000000uL / (r * freq)); - if (! tmr->ticks) - tmr->ticks = 1; - } + unsigned long den; + + if (r && !check_mul_overflow(r, freq, &den)) + tmr->ticks = max(1U, (unsigned int)(1000000000uL / den)); } tmr->initialized = 1; return 0; From 797fe91e50d6927f90f0c3b4444277c2c7c8b42b Mon Sep 17 00:00:00 2001 From: Michal Clapinski Date: Fri, 17 Jul 2026 15:40:28 +0200 Subject: [PATCH 107/501] kho: align kho_scratch to MAX_ORDER_NR_PAGES pages While booting with KHO, the following crash was observed: BUG: unable to handle page fault for address: ff19164fffff8328 RIP: 0010:__free_one_page+0x1a1/0x6b0 Call Trace: [] free_one_page+0xaf/0x240 [] deferred_free_pages+0xa8/0xd0 [] deferred_init_memmap_chunk+0x10f/0x1b0 [] padata_mt_helper+0x65/0xa0 [] process_scheduled_works+0x202/0x410 [] worker_thread+0x1f9/0x2d0 [] kthread+0x27d/0x2f0 [] ? __pfx_worker_thread+0x10/0x10 [] ? __pfx_kthread+0x10/0x10 [] ret_from_fork+0x145/0x280 [] ? __pfx_kthread+0x10/0x10 [] ret_from_fork_asm+0x1a/0x30 deferred_init_memmap_chunk() interleaves initialization of struct pages with freeing them. This works fine without KHO because free regions will never be buddy neighbors. However, with KHO, free memory will be split into (free && scratch) and (free && !scratch), that can be buddy neighbors. KHO scratch is aligned to CMA_MIN_ALIGNMENT_PAGES pages but buddy looks at the neighborhood of MAX_ORDER_NR_PAGES pages. These values are configurable but CMA_MIN_ALIGNMENT_PAGES is always less or equal to MAX_ORDER_NR_PAGES. In the crashing configuration they were set as follows: CMA_MIN_ALIGNMENT_PAGES = 1 << 9 MAX_ORDER_NR_PAGES = 1 << 10 So while freeing one chunk, buddy accessed uninitialized struct pages from another chunk, tried to merge the blocks and crashed. To fix this, let's just align KHO scratch to MAX_ORDER_NR_PAGES pages. Fixes: c6073743d0c7 ("kho: make preserved pages compatible with deferred struct page init") Signed-off-by: Michal Clapinski Link: https://patch.msgid.link/20260717134028.2880508-1-mclapinski@google.com [rppt: massaged the changelog] Signed-off-by: Mike Rapoport (Microsoft) --- kernel/liveupdate/kexec_handover.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/kernel/liveupdate/kexec_handover.c b/kernel/liveupdate/kexec_handover.c index 4834a809985a..175c08a6e41e 100644 --- a/kernel/liveupdate/kexec_handover.c +++ b/kernel/liveupdate/kexec_handover.c @@ -38,6 +38,16 @@ #include "../kexec_internal.h" #include "kexec_handover_internal.h" +/* + * This is the minimal alignment required by deferred struct page init. + * deferred_init_memmap_chunk frees memory to the buddy allocator, which looks + * at the neighboring pages (up to MAX_PAGE_ORDER) to merge them. + * If KHO scratch is not aligned to that value, buddy can access uninitialized + * struct pages, which can cause a crash. + */ +#define SCRATCH_ALIGNMENT_BYTES (PAGE_SIZE * MAX_ORDER_NR_PAGES) +static_assert(SCRATCH_ALIGNMENT_BYTES >= CMA_MIN_ALIGNMENT_BYTES); + /* The magic token for preserved pages */ #define KHO_PAGE_MAGIC 0x4b484f50U /* ASCII for 'KHOP' */ @@ -640,8 +650,8 @@ static void __init scratch_size_update(void) * Scratch areas are released as MIGRATE_CMA. Round them up to the right * size. */ - scratch_size_lowmem = round_up(scratch_size_lowmem, CMA_MIN_ALIGNMENT_BYTES); - scratch_size_global = round_up(scratch_size_global, CMA_MIN_ALIGNMENT_BYTES); + scratch_size_lowmem = round_up(scratch_size_lowmem, SCRATCH_ALIGNMENT_BYTES); + scratch_size_global = round_up(scratch_size_global, SCRATCH_ALIGNMENT_BYTES); } static phys_addr_t __init scratch_size_node(int nid) @@ -656,7 +666,7 @@ static phys_addr_t __init scratch_size_node(int nid) size = scratch_size_pernode; } - return round_up(size, CMA_MIN_ALIGNMENT_BYTES); + return round_up(size, SCRATCH_ALIGNMENT_BYTES); } /** @@ -692,7 +702,7 @@ static void __init kho_reserve_scratch(void) * next kernel */ size = scratch_size_lowmem; - addr = memblock_phys_alloc_range(size, CMA_MIN_ALIGNMENT_BYTES, 0, + addr = memblock_phys_alloc_range(size, SCRATCH_ALIGNMENT_BYTES, 0, ARCH_LOW_ADDRESS_LIMIT); if (!addr) { pr_err("Failed to reserve lowmem scratch buffer\n"); @@ -705,7 +715,7 @@ static void __init kho_reserve_scratch(void) /* reserve large contiguous area for allocations without nid */ size = scratch_size_global; - addr = memblock_phys_alloc(size, CMA_MIN_ALIGNMENT_BYTES); + addr = memblock_phys_alloc(size, SCRATCH_ALIGNMENT_BYTES); if (!addr) { pr_err("Failed to reserve global scratch buffer\n"); goto err_free_scratch_areas; @@ -721,7 +731,7 @@ static void __init kho_reserve_scratch(void) */ for_each_node_state(nid, N_MEMORY) { size = scratch_size_node(nid); - addr = memblock_alloc_range_nid(size, CMA_MIN_ALIGNMENT_BYTES, + addr = memblock_alloc_range_nid(size, SCRATCH_ALIGNMENT_BYTES, 0, MEMBLOCK_ALLOC_ACCESSIBLE, nid, true); if (!addr) { From 6437033bffe8bd2af174d139af552d90d40c7ac6 Mon Sep 17 00:00:00 2001 From: Xu Rao Date: Thu, 23 Jul 2026 16:57:10 +0800 Subject: [PATCH 108/501] ALSA: lx6464es: fix period byte count for 16-bit streams The lx6464es driver advertises both 16-bit and packed 24-bit PCM formats, but lx_trigger_start() and lx_interrupt_request_new_buffer() calculate the DMA period size as runtime->period_size * runtime->channels * 3. That is only correct for the packed 24-bit formats. For 16-bit streams the driver submits buffers that are 50% larger than the actual ALSA period and advances the DMA address by the same wrong amount. For example, with 2 channels, 256 frames and 4 periods, the third buffer already extends beyond the ALSA buffer and the fourth buffer starts outside it. Use snd_pcm_lib_period_bytes() so the byte count matches the runtime format, channel count and period size. Fixes: 02bec4904508 ("ALSA: lx6464es - driver for the digigram lx6464es interface") Cc: stable@vger.kernel.org Signed-off-by: Xu Rao Link: https://patch.msgid.link/8BB12E8D92A7CDBA+20260723085710.2567463-1-raoxu@uniontech.com Signed-off-by: Takashi Iwai --- sound/pci/lx6464es/lx6464es.c | 5 +---- sound/pci/lx6464es/lx_core.c | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c index 96df00db51d5..3b40ec071379 100644 --- a/sound/pci/lx6464es/lx6464es.c +++ b/sound/pci/lx6464es/lx6464es.c @@ -402,11 +402,8 @@ static void lx_trigger_start(struct lx6464es *chip, struct lx_stream *lx_stream) int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; const u32 periods = substream->runtime->periods; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); dma_addr_t buf = substream->dma_buffer.addr; int i; diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c index 6f0843cfb3be..9909f1067871 100644 --- a/sound/pci/lx6464es/lx_core.c +++ b/sound/pci/lx6464es/lx_core.c @@ -969,10 +969,7 @@ static int lx_interrupt_request_new_buffer(struct lx6464es *chip, const unsigned int is_capture = lx_stream->is_capture; int err; - const u32 channels = substream->runtime->channels; - const u32 bytes_per_frame = channels * 3; - const u32 period_size = substream->runtime->period_size; - const u32 period_bytes = period_size * bytes_per_frame; + const u32 period_bytes = snd_pcm_lib_period_bytes(substream); const u32 pos = lx_stream->frame_pos; const u32 next_pos = ((pos+1) == substream->runtime->periods) ? 0 : pos + 1; From bf4fc9f33ec21595143132a3e7fb8b5d2c2261cd Mon Sep 17 00:00:00 2001 From: Madhavender Singh Date: Thu, 23 Jul 2026 16:17:36 +0530 Subject: [PATCH 109/501] ALSA: hda/realtek: Add mute LED quirk for HP Laptop 14s-dr1xxx This laptop with an ALC236 codec requires the ALC236_FIXUP_HP_MUTE_LED_COEFBIT2 fixup for its mute LED to function correctly. Add the subsystem ID 0x103c:0x86c8 to the quirk table to apply this fixup. Signed-off-by: Madhavender Singh Link: https://patch.msgid.link/20260723104736.23386-1-madhav@disroot.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 fcc60e64137d..c246086c5082 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7142,6 +7142,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x86c1, "HP Laptop 15-da3001TU", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86c7, "HP Envy AiO 32", ALC274_FIXUP_HP_ENVY_GPIO), + SND_PCI_QUIRK(0x103c, 0x86c8, "HP Laptop 14s-dr1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), SND_PCI_QUIRK(0x103c, 0x86e7, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), SND_PCI_QUIRK(0x103c, 0x863e, "HP Spectre x360 15-df1xxx", ALC285_FIXUP_HP_SPECTRE_X360_DF1), SND_PCI_QUIRK(0x103c, 0x86e8, "HP Spectre x360 15-eb0xxx", ALC285_FIXUP_HP_SPECTRE_X360_EB1), From 8c63f59dcf474f5843d71fb58281a8329be5b034 Mon Sep 17 00:00:00 2001 From: Lianqin Hu Date: Fri, 24 Jul 2026 12:46:33 +0000 Subject: [PATCH 110/501] ALSA: usb-audio: Add iface reset and delay quirk for JKY Technology Q2A Setting up the interface when suspended/resuming fails on this card. Adding a reset and delay quirk will eliminate this problem. Note: This device's VID conflicts with Apple's (0x05ac). usb 1-1: New USB device found, idVendor=05ac, idProduct=110b usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 usb 1-1: Product: Q2A usb 1-1: Manufacturer: JKY Technology usb 1-1: SerialNumber: 330270D2251225 Suggested-by: Rong Zhang Signed-off-by: Lianqin Hu Reviewed-by: Rong Zhang Signed-off-by: Takashi Iwai Link: https://patch.msgid.link/TYUPR06MB6217566CFD33F57D3AE46816D2CF2@TYUPR06MB6217.apcprd06.prod.outlook.com --- sound/usb/quirks.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 49b56287cdb9..40aa40fccb46 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2200,6 +2200,9 @@ struct usb_audio_quirk_flags_table { static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { /* Device and string descriptor matches */ + DEVICE_STRING_FLG(0x05ac, 0x110b, /* VID conflicts with Apple */ + "JKY Technology", NULL, /* Q2A */ + QUIRK_FLAG_FORCE_IFACE_RESET | QUIRK_FLAG_IFACE_DELAY), /* Device matches */ DEVICE_FLG(0x001f, 0x0b21, /* AB13X USB Audio */ From 26a94400ffa4fcbeff32e23abebb83a1a20eb401 Mon Sep 17 00:00:00 2001 From: Eckhart Mohr Date: Fri, 24 Jul 2026 21:00:13 +0200 Subject: [PATCH 111/501] ALSA: hda/realtek: Add quirk for TongFang X6SP45xU TongFang X6KK45xU and X6SP45xU have actually different PCI IDs. This patch Adds the missing PCI ID to fix headphone detection and clarifies the naming. Fixes: d595255241e5 ("ALSA: hda/realtek: Add quirk for TongFang X6xx45xU") Signed-off-by: Eckhart Mohr Cc: stable@vger.kernel.org Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260724190109.169889-1-wse@tuxedocomputers.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index c246086c5082..a0abb8993440 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -8036,7 +8036,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1d05, 0x300f, "TongFang X6AR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3019, "TongFang X6FR5xxY", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d05, 0x3031, "TongFang X6AR55xU", ALC2XX_FIXUP_HEADSET_MIC), - SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6xx45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3033, "TongFang X6SP45xU", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1d05, 0x3034, "TongFang X6KK45xU", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1d17, 0x3288, "Haier Boyue G42", ALC269VC_FIXUP_ACER_VCOPPERBOX_PINS), SND_PCI_QUIRK(0x1d72, 0x1602, "RedmiBook", ALC255_FIXUP_XIAOMI_HEADSET_MIC), SND_PCI_QUIRK(0x1d72, 0x1701, "XiaomiNotebook Pro", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE), From cd74e5cba460e9ba604e9a418317e38cf50401c8 Mon Sep 17 00:00:00 2001 From: Marcos Paulo Medeiros Date: Sat, 25 Jul 2026 18:41:31 -0300 Subject: [PATCH 112/501] ALSA: hda/realtek: Fix headset mic on Acer Nitro 5 AN515-46 The Acer Nitro 5 AN515-46 (SSID 1025:159e, Realtek ALC287) has a combo headset jack whose microphone does not work out of the box: the BIOS leaves pin 0x19 unconfigured, so no headset mic is created. Apply ALC2XX_FIXUP_HEADSET_MIC, the same fixup already used by the sibling models AN515-57 (1025:1539) and AN517-55 (1025:1597), which makes the headset microphone work correctly. Tested on an Acer Nitro 5 AN515-46 by overriding the model via a patch firmware with model=alc2xx-fixup-headset-mic. Signed-off-by: Marcos Paulo Medeiros Link: https://patch.msgid.link/20260725214131.25872-1-maarcospm1996@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 a0abb8993440..9533be5c398b 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -6940,6 +6940,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", ALC287_FIXUP_ACER_MICMUTE_LED), SND_PCI_QUIRK(0x1025, 0x1597, "Acer Nitro 5 AN517-55", ALC2XX_FIXUP_HEADSET_MIC), + SND_PCI_QUIRK(0x1025, 0x159e, "Acer Nitro 5 AN515-46", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x160e, "Acer PT316-51S", ALC2XX_FIXUP_HEADSET_MIC), SND_PCI_QUIRK(0x1025, 0x161f, "Acer S40-54", ALC256_FIXUP_ACER_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1025, 0x1640, "Acer Aspire A315-44P", ALC256_FIXUP_ACER_SFG16_MICMUTE_LED), From 4a05b2d1b4642df74f30b6f54843e825c4a2bfd3 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:13:37 +0900 Subject: [PATCH 113/501] ALSA: usb-audio: fix use-after-free in ump_to_endpoint() create_midi2_ump() registers a card-owned snd_ump_endpoint and stores a back-pointer to its per-interface snd_usb_midi2_ump object in ump->private_data, but it never installs an ump->private_free hook and never clears that pointer. If a later step of snd_usb_midi_v2_create() fails, its error path calls free_all_midi2_umps(), which kfree()s the snd_usb_midi2_ump object while the already-registered endpoint keeps pointing at it. The created /dev/snd/umpC*D* node stays exposed, so the first operation of any UMP open, ump_to_endpoint(), dereferences the dangling ump->private_data and reads rmidi->eps[dir] out of freed memory. A malicious USB MIDI 2.0 device that makes creation fail after the endpoint is registered can thus trigger a slab use-after-free read on a subsequent open of the UMP node. Clear the endpoint's back-pointer before freeing the object, and let ump_to_endpoint() tolerate a NULL private_data so the open/close/trigger callbacks fail cleanly (their callers already handle a NULL endpoint) instead of dereferencing a stale pointer. Discovered by XBOW, triaged by Baul Lee Fixes: ff49d1df79ae ("ALSA: usb-audio: USB MIDI 2.0 UMP support") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051337.41124-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/midi2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sound/usb/midi2.c b/sound/usb/midi2.c index 3ec633291772..83980fb83ac8 100644 --- a/sound/usb/midi2.c +++ b/sound/usb/midi2.c @@ -329,7 +329,7 @@ ump_to_endpoint(struct snd_ump_endpoint *ump, int dir) { struct snd_usb_midi2_ump *rmidi = ump->private_data; - return rmidi->eps[dir]; + return rmidi ? rmidi->eps[dir] : NULL; } /* ump open callback */ @@ -685,6 +685,8 @@ static void free_all_midi2_umps(struct snd_usb_midi2_interface *umidi) rmidi = list_first_entry(&umidi->rawmidi_list, struct snd_usb_midi2_ump, list); list_del(&rmidi->list); + if (rmidi->ump) + rmidi->ump->private_data = NULL; kfree(rmidi); } } From 70c977815af0d997feb2d0c5d284d55689bf7051 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 14:16:33 +0900 Subject: [PATCH 114/501] ALSA: ump: fix double free of out_cvts on rawmidi error snd_ump_attach_legacy_rawmidi() allocates the legacy conversion array ump->out_cvts and, on the snd_rawmidi_new() error path, frees it with kfree() but leaves ump->out_cvts pointing at the freed memory. When the endpoint is later torn down, snd_ump_endpoint_free() frees ump->out_cvts a second time, resulting in a double free. The host snd-usb-audio driver attaches the legacy rawmidi for any USB MIDI 2.0 (UMP) device, so a device that makes snd_rawmidi_new() fail reaches this path on enumeration. Clear ump->out_cvts after freeing it on the error path so it is not freed again during teardown. Discovered by XBOW, triaged by Baul Lee Fixes: 33cd7630782d ("ALSA: ump: Export MIDI1 / UMP conversion helpers") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726051633.41206-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/core/ump.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sound/core/ump.c b/sound/core/ump.c index 70520c7ca293..632c13baf21e 100644 --- a/sound/core/ump.c +++ b/sound/core/ump.c @@ -1365,6 +1365,7 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump, &rmidi); if (err < 0) { kfree(ump->out_cvts); + ump->out_cvts = NULL; return err; } From 441aaad150c57edaf57ee482a79a3bf4c5b7e353 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 15:50:20 +0900 Subject: [PATCH 115/501] ALSA: usb-audio: fix stack info leak in RME Digiface status snd_rme_digiface_read_status() reads a four-word status block from the device into an uninitialised on-stack __le32 buf[4] and, whenever the vendor control-IN transfer does not return a negative error, copies all four words into the caller's status[]. snd_usb_ctl_msg() copies the full requested size back into the caller's buffer regardless of how many bytes the data stage actually delivered: buf = kmemdup(data, size, GFP_KERNEL); err = usb_control_msg(dev, pipe, request, requesttype, value, index, buf, size, timeout); memcpy(data, buf, size); usb_control_msg() returns the transferred length on a short control-IN, which is a non-negative value, and writes only that many bytes. The remainder of the copy back is the kmemdup()ed image of the caller's buffer, so a device answering with a short data stage leaves the trailing words of buf[] holding leftover kernel stack. The only guard in the caller is err < 0, so those words are stored into status[]. They then reach user space: snd_rme_digiface_get_status_val() selects a 16-bit halfword of status[] per the control's reg/mask, and the eight Digiface status controls together expose the whole 16-byte frame to an unprivileged reader of /dev/snd/controlC*. Zero-initialise the buffer so a short read yields zeros instead of stack residue. This mirrors snd_rme_get_status1(), which already clears its output word before the same kind of vendor read. Discovered by XBOW, triaged by Baul Lee Fixes: 611a96f6acf2 ("ALSA: usb-audio: Add mixer quirk for RME Digiface USB") Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726065020.46070-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 10792d26fa94..74d26fd86efb 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -3496,7 +3496,7 @@ static int snd_rme_digiface_read_status(struct snd_kcontrol *kcontrol, u32 statu struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol); struct snd_usb_audio *chip = list->mixer->chip; struct usb_device *dev = chip->dev; - __le32 buf[4]; + __le32 buf[4] = {}; int err; err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), From 0970274613fb463d376211450cab066d34ebfe6a Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Sun, 26 Jul 2026 16:45:00 +0900 Subject: [PATCH 116/501] ALSA: usb-audio: fix OOB write in snd_usbmidi_akai_output() snd_usbmidi_akai_output() computes its fill-loop bound buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; as a signed int, so a small device-advertised bulk-OUT max_transfer makes buf_end negative. The loop guard then compares the u32 urb->transfer_buffer_length against that negative int: the usual arithmetic conversion turns buf_end into a large unsigned value, so the guard stays true and each iteration keeps appending SysEx framing and payload bytes past the end of the URB transfer buffer, which is only max_transfer bytes long. A USB device that advertises a tiny bulk-OUT endpoint can therefore trigger an attacker-length- and content-controlled heap out-of-bounds write when a process writes to the created /dev/snd/midiC*D* node. Return early when there is no room for even one SysEx, so the loop is never entered with a bound that would wrap. The loop is the last statement of the function, so bailing out is equivalent to it not running. Discovered by XBOW, triaged by Baul Lee Fixes: 4434ade8c933 ("ALSA: usb-audio: add support for Akai MPD16") Suggested-by: Takashi Iwai Reported-by: Federico Kirschbaum Reported-by: Baul Lee Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726074500.50145-1-baul.lee@xbow.com Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/usb/midi.c b/sound/usb/midi.c index d87e3f357cf7..f8996416c3be 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -797,6 +797,8 @@ static void snd_usbmidi_akai_output(struct snd_usb_midi_out_endpoint *ep, msg = urb->transfer_buffer + urb->transfer_buffer_length; buf_end = ep->max_transfer - MAX_AKAI_SYSEX_LEN - 1; + if (buf_end <= 0) + return; /* only try adding more data when there's space for at least 1 SysEx */ while (urb->transfer_buffer_length < buf_end) { From a54bf16965f896415c3337bc4fbb40fb11941d99 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 26 Jul 2026 09:48:19 +0200 Subject: [PATCH 117/501] ALSA: 6fire: Fix UAF at error handling during probe Although 6fire driver had a few fixes for dealing with the early error handling during the probe phase, it forgot a pending URB before freeing the resources, which may lead to a UAF. This patch addresses it by doing the almost same cleanup procedure like the normal disconnect phase at the error path. Reported-and-tested-by: Shuangpeng Bai Closes: https://lore.kernel.org/20260724030900.1984491-1-shuangpeng.kernel@gmail.com Cc: Link: https://patch.msgid.link/20260726074821.2288158-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/6fire/chip.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index 2c5648966412..6cf410a92fa2 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -150,6 +150,10 @@ static int usb6fire_chip_probe(struct usb_interface *intf, return 0; destroy_chip: + chip->shutdown = true; + if (card) + snd_card_disconnect(card); + usb6fire_chip_abort(chip); snd_card_free(card); return ret; } From fd71820e876ef4b816b6305416ba5973bb6b9665 Mon Sep 17 00:00:00 2001 From: Gunal Seenivasagan Date: Sun, 26 Jul 2026 14:27:11 +0530 Subject: [PATCH 118/501] ALSA: hda/realtek: Add quirk for Infinix INBOOK X3 Slim The Infinix INBOOK X3 Slim (ALC269VB, subsystem 0x2782:0250) has its internal speakers wired to pin 0x1b, but the BIOS pin configuration table declares 0x1b as "no physical connection" (0x411111f0). It instead declares pin 0x14 as the internal speaker, although nothing is connected to 0x14. As a result the internal speakers are silent under Linux while the headphone jack works correctly. The codec output path to 0x14 is fully open (DAC assigned and streaming, mixer and pin unmuted, EAPD asserted), so the failure is silent with no error reported. The speakers work under Windows, where the vendor driver supplies its own pin table. Add a fixup that disables the unconnected pin 0x14 and declares pin 0x1b as the internal speaker. Reusing the existing ALC269VC_FIXUP_INFINIX_Y4_MAX was tried first, since it also remaps 0x1b to an internal speaker. It is not sufficient here: it leaves 0x14 declared, so autoconfig finds two line_outs line_outs=2 (0x14/0x1b/0x0/0x0/0x0) type:speaker and binds the primary "Speaker" control to the unconnected pin 0x14 while demoting the working speaker at 0x1b to "Bass Speaker". Audio is audible that way, but the volume and mute controls a desktop actually uses end up attached to a pin that is not wired to anything. Disabling 0x14 is what produces a single correct Speaker output. Verified on the affected machine: with the corrected pin configuration the driver's autoconfig reports line_outs=1 (0x1b/0x0/0x0/0x0/0x0) type:speaker both channels play, headphone auto-mute switches correctly in both directions, and audio survives codec runtime suspend (D3) and resume. Signed-off-by: Gunal Seenivasagan Link: https://patch.msgid.link/20260726085715.229802-1-gunal2002@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 9533be5c398b..09225801a177 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -3949,6 +3949,7 @@ enum { ALC269_FIXUP_DMIC_THINKPAD_ACPI, ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13, ALC269VC_FIXUP_INFINIX_Y4_MAX, + ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM, ALC269VC_FIXUP_LUNNEN_GROUND_14, ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO, ALC255_FIXUP_ACER_MIC_NO_PRESENCE, @@ -4421,6 +4422,14 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC269_FIXUP_LIMIT_INT_MIC_BOOST }, + [ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x14, 0x411111f0 }, /* disable, not connected */ + { 0x1b, 0x90170110 }, /* use as internal speaker */ + { } + }, + }, [ALC269VC_FIXUP_LUNNEN_GROUND_14] = { .type = HDA_FIXUP_PINS, .v.pins = (const struct hda_pintbl[]) { @@ -8058,6 +8067,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x2782, 0x0214, "VAIO VJFE-CL", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x2782, 0x0228, "Infinix ZERO BOOK 13", ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13), SND_PCI_QUIRK(0x2782, 0x0232, "CHUWI CoreBook XPro", ALC269VB_FIXUP_CHUWI_COREBOOK_XPRO), + SND_PCI_QUIRK(0x2782, 0x0250, "Infinix INBOOK X3 Slim", ALC269VB_FIXUP_INFINIX_INBOOK_X3_SLIM), SND_PCI_QUIRK(0x2782, 0x1407, "Positivo P15X", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x1409, "Positivo K116J", ALC269_FIXUP_POSITIVO_P15X_HEADSET_MIC), SND_PCI_QUIRK(0x2782, 0x1701, "Infinix Y4 Max", ALC269VC_FIXUP_INFINIX_Y4_MAX), From c2744d5f3aea474513fd2298daecb94a952ce441 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Sun, 26 Jul 2026 10:01:45 +0200 Subject: [PATCH 119/501] ALSA: timer: Clear SNDRV_TIMER_IFLG_DEAD once the close completes snd_timer_close_locked() marks an instance with SNDRV_TIMER_IFLG_DEAD and returns early when the flag is already set, but the flag is never cleared again. A completed close ends in remove_slave_links(), which leaves timeri->timer NULL, so a second close is already harmless through the timer == NULL path; the early return can only be reached by an instance that was opened again in between. For such an instance the close unlinks nothing, so snd_timer_instance_free() frees an object that is still on timer->open_list_head, still on snd_timer_master_list if it was opened with a slave key, still owns any adopted slaves, and still holds its timer and module references. snd_seq_timer_open() reopens an instance exactly like that: it retries its fallback open on the same object after a failure that has already run snd_timer_close_locked() internally. An unprivileged user with access to /dev/snd/timer and /dev/snd/seq can force that failure, since snd_timer_check_master() returns -EBUSY when a pending slave matches the new master's (slave_class, slave_id) key and the target timer has reached max_instances, and SNDRV_TIMER_IOCTL_SELECT with dev_class = SNDRV_TIMER_CLASS_SLAVE keeps the caller-supplied dev_sclass, so a sequencer queue's key can be forged. The freed instance is afterwards dereferenced by any further snd_timer_open() on that timer, by snd_timer_check_slave(), and by /proc/asound/timers, which faults on the stale ti->owner pointer. The flag only has to be visible while the close is in progress, which is all its other users need. Clear it in remove_slave_links(), under the same timer->lock that sets it, once the instance is off every list. Fixes: da3039e91d1f ("ALSA: timer: Forcibly close timer instances at closing") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/CA41AA48-75BF-45E9-A36D-3A5D2F124F60@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/core/timer.c b/sound/core/timer.c index 07ef127b29e1..9407954034cf 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -458,6 +458,8 @@ static void remove_slave_links(struct snd_timer_instance *timeri, list_del_init(&slave->ack_list); list_del_init(&slave->active_list); } + /* the close is done; a reopen must not see the mark */ + timeri->flags &= ~SNDRV_TIMER_IFLG_DEAD; } /* From 7e7f81cf6f5ca3311e526308f55d7c54d3ba71f9 Mon Sep 17 00:00:00 2001 From: "Borislav Petkov (AMD)" Date: Tue, 2 Jun 2026 21:26:44 -0700 Subject: [PATCH 120/501] x86/bugs: Make Safe-RET robust against interrupt injection An attacker injecting interrupts while the Safe-RET mitigation executes on machines affected by SRSO can neutralize the safe return sequence, potentially leading to data leakage through speculative execution. Fixup register state as if the Safe-RET sequence executed successfully by "emulating" it, in a manner of speaking, and avoid executing a RET instruction after returning from the interrupt. Co-developed-by: David Kaplan Signed-off-by: David Kaplan Signed-off-by: Borislav Petkov (AMD) --- arch/x86/entry/entry_64.S | 8 +++- arch/x86/include/asm/nospec-branch.h | 57 ++++++++++++++++++++++++++++ arch/x86/kernel/cpu/bugs.c | 39 +++++++++++++++++++ arch/x86/lib/retpoline.S | 20 ++++++++++ 4 files changed, 123 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index c6d996593f32..253f0a585927 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -937,6 +937,8 @@ SYM_CODE_START(paranoid_entry) IBRS_ENTER save_reg=%r15 UNTRAIN_RET_FROM_CALL + HANDLE_INTR_SAFERET 8(%rsp) + RET SYM_CODE_END(paranoid_entry) @@ -1039,6 +1041,11 @@ SYM_CODE_START(error_entry) movl %ecx, %eax /* zero extend */ cmpq %rax, RIP+8(%rsp) je .Lbstep_iret + + VALIDATE_UNRET_END + + HANDLE_INTR_SAFERET 8(%rsp) + cmpq $.Lgs_change, RIP+8(%rsp) jne .Lerror_entry_done_lfence @@ -1057,7 +1064,6 @@ SYM_CODE_START(error_entry) FENCE_SWAPGS_KERNEL_ENTRY CALL_DEPTH_ACCOUNT leaq 8(%rsp), %rax /* return pt_regs pointer */ - VALIDATE_UNRET_END RET .Lbstep_iret: diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index b68892e6d58c..2ea6591bc7b9 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -12,6 +12,7 @@ #include #include #include +#include /* * Call depth tracking for Intel SKL CPUs to address the RSB underflow @@ -176,6 +177,50 @@ add $(BITS_PER_LONG/8), %_ASM_SP; \ lfence; +/* + * Helper for detecting if an interrupt occurred at an unsafe location within + * Safe-RET. If Safe-RET is interrupted after the CALL or LEA the RSB may get + * poisoned by the interrupt handler. + * + * The Safe-RET sequence is: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * The two CMPs below check whether RIP points to after the CALL or after the + * LEA. + * + * The LFENCE below is to address this particular speculation case: + * + * 1. Userspace runs and poisons the BTB around the safe-RET routine + * + * 2. Userspace triggers some kind of exception + * + * 3. Kernel executes error_entry() and mis-speculates the branch into thinking + * it actually came from kernel space + * + * 4. The kernel then further mis-speculates that the exception occurred due + * to an interrupted safe-RET + * + * 5. The handle_interrupted_saferet() routine speculatively executes and + * speculatively does a safe-RET. But this is unsafe since it was never + * untrained. + * + * The LFENCE fixes this by ensuring step 5 is never reached speculatively. + * Note that this LFENCE only occurs if safe-RET was actually interrupted (so + * it's outside of the normal path). + */ +#define __HANDLE_INTR_SAFERET(name, pt_regs) \ + cmpq $(name), RIP+pt_regs; \ + jb 1f; \ + cmpq $(name)+5, RIP+pt_regs; \ + ja 1f; \ + lfence; \ + leaq pt_regs, %rdi; \ + call handle_interrupted_saferet; \ + 1: + #ifdef __ASSEMBLER__ /* @@ -293,6 +338,14 @@ #define UNTRAIN_RET_FROM_CALL \ __UNTRAIN_RET X86_FEATURE_ENTRY_IBPB, __stringify(RESET_CALL_DEPTH_FROM_CALL) +.macro HANDLE_INTR_SAFERET pt_regs +#ifdef CONFIG_MITIGATION_SRSO + ALTERNATIVE_2 "", \ + __stringify(__HANDLE_INTR_SAFERET(srso_safe_ret, \pt_regs)), X86_FEATURE_SRSO, \ + __stringify(__HANDLE_INTR_SAFERET(srso_alias_safe_ret, \pt_regs)), X86_FEATURE_SRSO_ALIAS + +#endif +.endm .macro CALL_DEPTH_ACCOUNT #ifdef CONFIG_MITIGATION_CALL_DEPTH_TRACKING @@ -625,6 +678,10 @@ static __always_inline void x86_idle_clear_cpu_buffers(void) x86_clear_cpu_buffers(); } +void srso_safe_ret(void); +void srso_alias_safe_ret(void); +void handle_interrupted_saferet(struct pt_regs *regs); + #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_NOSPEC_BRANCH_H_ */ diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c index d9af230c0512..82436b3534fa 100644 --- a/arch/x86/kernel/cpu/bugs.c +++ b/arch/x86/kernel/cpu/bugs.c @@ -3775,3 +3775,42 @@ void __warn_thunk(void) { WARN_ONCE(1, "Unpatched return thunk in use. This should not happen!\n"); } + +#ifdef CONFIG_MITIGATION_SRSO +/* + * Called during exception/interrupt entry if interrupted during the + * safe-RET sequence. The safe-RET sequence consists of 3 instructions: + * + * CALL + * LEA 8(%RSP), %RSP + * RET + * + * An interrupt after the CALL or after the LEA could potentially lead + * to branch predictor poisoning and results in the sequence not being + * able to be safely resumed. + * + * Therefore, modify the regs state as if the remaining part of the + * safe-RET sequence executed so the interrupt returns back to the + * desired return target, instead of the to the safe-RET sequence. + */ +void noinstr handle_interrupted_saferet(struct pt_regs *regs) +{ + unsigned long rip = regs->ip; + + if (rip == (unsigned long) srso_safe_ret || + rip == (unsigned long) srso_alias_safe_ret) { + /* Modify stack pointer as if LEA executed: */ + regs->sp += 8; + } + + /* + * Adjust registers as if RET executed: + * + * 1. Read the return address off the stack and into rIP: + */ + regs->ip = *(unsigned long *)(regs->sp); + + /* 2. Pop rIP off the stack: */ + regs->sp += 8; +} +#endif /* CONFIG_MITIGATION_SRSO */ diff --git a/arch/x86/lib/retpoline.S b/arch/x86/lib/retpoline.S index 8f1fed0c3b83..f9ca1d8eabca 100644 --- a/arch/x86/lib/retpoline.S +++ b/arch/x86/lib/retpoline.S @@ -207,10 +207,24 @@ __EXPORT_THUNK(srso_alias_untrain_ret) .pushsection .text..__x86.rethunk_safe SYM_CODE_START_NOALIGN(srso_alias_safe_ret) + + /* + * Tell objtool that those are not function pointers referenced by + * __HANDLE_INTR_SAFERET(). Below too. + */ + ANNOTATE_NOENDBR + + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP UNWIND_HINT_FUNC + + ANNOTATE_NOENDBR ANNOTATE_UNRET_SAFE ret + /* End of Safe-RET sequence */ int3 SYM_FUNC_END(srso_alias_safe_ret) @@ -245,8 +259,14 @@ SYM_CODE_START_LOCAL_NOALIGN(srso_untrain_ret) * the stack. */ SYM_INNER_LABEL(srso_safe_ret, SYM_L_GLOBAL) + /* + * Safe-RET sequence. If you need to change it, adjust + * handle_interrupted_saferet() too. + */ lea 8(%_ASM_SP), %_ASM_SP ret + /* End of Safe-RET sequence */ + int3 int3 /* end of movabs */ From 738e6f32e61d80b554e37015ecb7bc620b88001c Mon Sep 17 00:00:00 2001 From: Peiyang He Date: Mon, 20 Jul 2026 16:50:16 +0800 Subject: [PATCH 121/501] iommu/iommufd: Fix IOPF group ownership UAF iopf_group_alloc() links each last-page IOPF group into the generic IOPF pending list before invoking the domain fault handler. iommufd_fault_iopf_handler() also queued an accepted group in the IOMMUFD deliver list without removing it from the generic pending list. When detach or HWPT replacement drops the device's IOPF reference count to zero, an IOMMU driver may call iopf_queue_remove_device(). That function responds to and frees groups through the generic pending list without removing the same groups from IOMMUFD's deliver list or response xarray. A later read, response, or cleanup can then access the freed group and cause a UAF. Fix this by dequeuing an accepted group from the generic pending list before IOMMUFD queues it for userspace response. Make iopf_group_response() send a response regardless of pending-list membership, so the dequeued group can still be completed by IOMMUFD. Link: https://patch.msgid.link/r/3CFD314D0FE4D7EC+20260720085017.3998878-2-peiyang_he@smail.nju.edu.cn Closes: https://lore.kernel.org/all/B4F28798E2E784CA+d29f723c-b2b5-4b67-8d1c-4f7b9b0b27cb@smail.nju.edu.cn/ Fixes: 34765cbc679c ("iommufd: Associate fault object with iommufd_hw_pgtable") Cc: stable@vger.kernel.org Tested-by: Peiyang He Assisted-by: Codex:gpt-5.6-sol Signed-off-by: Peiyang He Reviewed-by: Kevin Tian Signed-off-by: Jason Gunthorpe --- drivers/iommu/io-pgfault.c | 24 +++++++++++++++++++----- drivers/iommu/iommufd/eventq.c | 2 ++ include/linux/iommu.h | 5 +++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/io-pgfault.c b/drivers/iommu/io-pgfault.c index cca52a34d0ed..c16ff1fc4b95 100644 --- a/drivers/iommu/io-pgfault.c +++ b/drivers/iommu/io-pgfault.c @@ -332,16 +332,30 @@ void iopf_group_response(struct iopf_group *group, .code = status, }; - /* Only send response if there is a fault report pending */ mutex_lock(&fault_param->lock); - if (!list_empty(&group->pending_node)) { - ops->page_response(dev, &group->last_fault, &resp); - list_del_init(&group->pending_node); - } + ops->page_response(dev, &group->last_fault, &resp); + list_del_init(&group->pending_node); mutex_unlock(&fault_param->lock); } EXPORT_SYMBOL_GPL(iopf_group_response); +/** + * iopf_group_dequeue - Dequeue a page fault group from the pending list + * @group: the group to dequeue + * + * The fault handler is responsible for responding to the group after + * this function returns. + */ +void iopf_group_dequeue(struct iopf_group *group) +{ + struct iommu_fault_param *fault_param = group->fault_param; + + mutex_lock(&fault_param->lock); + list_del_init(&group->pending_node); + mutex_unlock(&fault_param->lock); +} +EXPORT_SYMBOL_GPL(iopf_group_dequeue); + /** * iopf_queue_discard_partial - Remove all pending partial fault * @queue: the queue whose partial faults need to be discarded diff --git a/drivers/iommu/iommufd/eventq.c b/drivers/iommu/iommufd/eventq.c index 5129e3bf5461..747dd5155121 100644 --- a/drivers/iommu/iommufd/eventq.c +++ b/drivers/iommu/iommufd/eventq.c @@ -484,6 +484,8 @@ int iommufd_fault_iopf_handler(struct iopf_group *group) hwpt = group->attach_handle->domain->iommufd_hwpt; fault = hwpt->fault; + iopf_group_dequeue(group); + spin_lock(&fault->common.lock); list_add_tail(&group->node, &fault->common.deliver); spin_unlock(&fault->common.lock); diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d20aa6f6863a..ac43b8b93f14 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -1704,6 +1704,7 @@ void iopf_free_group(struct iopf_group *group); int iommu_report_device_fault(struct device *dev, struct iopf_fault *evt); void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status); +void iopf_group_dequeue(struct iopf_group *group); #else static inline int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev) @@ -1749,5 +1750,9 @@ static inline void iopf_group_response(struct iopf_group *group, enum iommu_page_response_code status) { } + +static inline void iopf_group_dequeue(struct iopf_group *group) +{ +} #endif /* CONFIG_IOMMU_IOPF */ #endif /* __LINUX_IOMMU_H */ From 98b87885de4b7f605533a2860685f5689fce8e82 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Tue, 14 Jul 2026 19:49:34 +0900 Subject: [PATCH 122/501] scsi: libiscsi: Fix stale-data leak into the SCSI sense buffer iscsi_scsi_cmd_rsp() copies the sense data of a SCSI Response from the target-supplied data segment. The segment carries a 2-byte sense length followed by the sense bytes, so it must hold 2 + senselen bytes, but the bounds check only requires datalen >= senselen: senselen = get_unaligned_be16(data); if (datalen < senselen) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); A target that returns a SCSI Response whose datalen equals senselen (with senselen <= SCSI_SENSE_BUFFERSIZE) makes the memcpy() from data + 2 read up to two bytes past the received data. Those bytes are stale conn->data contents and end up in the command's sense buffer, which is returned to userspace. Account for the 2-byte sense length prefix in the check. Fixes: 7996a778ff8c ("[SCSI] iscsi: add libiscsi") Suggested-by: Sashiko AI Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260714104934.1404423-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 160f02f2f51d..5cbc51899de0 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -918,7 +918,7 @@ static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, } senselen = get_unaligned_be16(data); - if (datalen < senselen) + if (datalen < senselen + 2) goto invalid_datalen; memcpy(sc->sense_buffer, data + 2, From c1dea15f819cded9b3faf58f8bec72323568b6e6 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Thu, 16 Jul 2026 15:58:48 +0900 Subject: [PATCH 123/501] scsi: libiscsi_tcp: Bound SCSI Response data segment to the connection buffer iscsi_tcp_hdr_dissect() receives the data segment of several PDU types into the fixed-size conn->data buffer, which is allocated for ISCSI_DEF_MAX_RECV_SEG_LEN (8192) bytes. For the LOGIN_RSP, TEXT_RSP, REJECT and ASYNC_EVENT opcodes the dissect path already rejects a PDU whose DataSegmentLength exceeds that buffer. The SCSI Command Response (ISCSI_OP_SCSI_CMD_RSP) path also copies its data segment (sense/response data) into conn->data via iscsi_tcp_data_recv_prep(), but it does so without the same check. The only upstream bound on in.datalen is conn->max_recv_dlength, the initiator's advertised MaxRecvDataSegmentLength, which is commonly negotiated well above 8192 (open-iscsi defaults to 262144). A target that returns a SCSI Response with a DataSegmentLength between 8193 and max_recv_dlength therefore overflows the 8192-byte conn->data buffer. Once the same bound applies, ISCSI_OP_SCSI_CMD_RSP is handled exactly like those responses: bound the data segment, receive it into conn->data when present, and otherwise complete the PDU with no data. Fold the opcode into that case group rather than duplicating the check. Fixes: a081c13e39b5 ("[SCSI] iscsi_tcp: split module into lib and lld") Suggested-by: Chris Leech Assisted-by: Claude:claude-opus-4-8 Signed-off-by: HyeongJun An Acked-by: Chris Leech Link: https://patch.msgid.link/20260716065848.1653431-1-sammiee5311@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/libiscsi_tcp.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index e90805ba868f..7223bb18b048 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -752,13 +752,6 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = __iscsi_complete_pdu(conn, hdr, NULL, 0); spin_unlock(&conn->session->back_lock); break; - case ISCSI_OP_SCSI_CMD_RSP: - if (tcp_conn->in.datalen) { - iscsi_tcp_data_recv_prep(tcp_conn); - return 0; - } - rc = iscsi_complete_pdu(conn, hdr, NULL, 0); - break; case ISCSI_OP_R2T: if (ahslen) { rc = ISCSI_ERR_AHSLEN; @@ -766,6 +759,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) } rc = iscsi_tcp_r2t_rsp(conn, hdr); break; + case ISCSI_OP_SCSI_CMD_RSP: case ISCSI_OP_LOGIN_RSP: case ISCSI_OP_TEXT_RSP: case ISCSI_OP_REJECT: From 3dbbbf656b850c9c8de05df6ad4a1dfc6ff02845 Mon Sep 17 00:00:00 2001 From: Xingui Yang Date: Thu, 16 Jul 2026 16:11:45 +0800 Subject: [PATCH 124/501] scsi: libsas: Fix HA resume deadlock and hisi_sas disk-wake race Commit fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") introduced sas_resume_ha_no_sync() to avoid a deadlock: the PHYE_RESUME_TIMEOUT handler, running on the HA event workqueue, calls sas_deform_port() -> sas_destruct_devices(), which removes SCSI devices and waits for the host to become runtime-active. But the host cannot resume until sas_resume_ha() -> sas_drain_work() returns, and the drain is blocked on that very handler. However skipping the drain reintroduces a race: hisi_sas returns from resume before all PHY UP work and libsas discovery work finish. The controller may then autosuspend while disks are still waking up. The disks issue IO to a suspended controller, the IO fails, and the disks get disabled. Fix the deadlock at its source by moving the PHYE_RESUME_TIMEOUT notification to after sas_drain_work(). By then the host resume is about to complete, so device removal through device_link no longer blocks on the resume and the cycle is broken. With the deadlock gone, restore sas_resume_ha() (the draining variant) in hisi_sas and remove sas_resume_ha_no_sync(). The reorder is safe for the other libsas consumers (isci, pm8001, aic94xx, mvsas). During suspend, sas_suspend_devices() calls sas_notify_lldd_dev_gone() for each device, which sets dev->lldd_dev to NULL. When scsi_unblock_requests re-enables I/O in resume, any I/O to a timed-out phy's disk is immediately rejected by the LLDD before reaching hardware: isci returns SAS_DEVICE_UNKNOWN (mapped to DID_BAD_TARGET), and pm8001 returns SAS_PHY_DOWN (mapped to DID_NO_CONNECT). Both complete directly via scsi_done() without entering SCSI EH. This is identical in both the old and new ordering since lldd_dev_gone runs during suspend, before resume. The reorder only affects when the PHYE_RESUME_TIMEOUT handler runs (synchronized by sas_drain_work() vs. asynchronous after resume returns), not whether I/O can reach the device. aic94xx and mvsas do not register any PM ops and never reach this code path. Fixes: fbefe22811c3 ("scsi: libsas: Don't always drain event workqueue for HA resume") Signed-off-by: Xingui Yang Reviewed-by: John Garry Link: https://patch.msgid.link/20260716081145.3950172-1-yangxingui@huawei.com Signed-off-by: Martin K. Petersen --- drivers/scsi/hisi_sas/hisi_sas_v3_hw.c | 10 +------ drivers/scsi/libsas/sas_init.c | 37 +++++++++++++------------- include/scsi/libsas.h | 1 - 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index 213d5b5dea94..8a2500993e19 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -5261,15 +5261,7 @@ static int _resume_v3_hw(struct device *device) return rc; } phys_init_v3_hw(hisi_hba); - - /* - * If a directly-attached disk is removed during suspend, a deadlock - * may occur, as the PHYE_RESUME_TIMEOUT processing will require the - * hisi_hba->device to be active, which can only happen when resume - * completes. So don't wait for the HA event workqueue to drain upon - * resume. - */ - sas_resume_ha_no_sync(sha); + sas_resume_ha(sha); clear_bit(HISI_SAS_RESETTING_BIT, &hisi_hba->flags); dev_warn(dev, "end of resuming controller\n"); diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index 0bec236f0fb5..c3f3d05b46de 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -410,7 +410,7 @@ static void sas_resume_insert_broadcast_ha(struct sas_ha_struct *ha) } } -static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) +void sas_resume_ha(struct sas_ha_struct *ha) { const unsigned long tmo = msecs_to_jiffies(25000); int i; @@ -426,6 +426,23 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) dev_info(ha->dev, "waiting up to 25 seconds for %d phy%s to resume\n", i, i > 1 ? "s" : ""); wait_event_timeout(ha->eh_wait_q, phys_suspended(ha) == 0, tmo); + + /* + * All phys are back up or timed out. Turn on I/O and drain + * pending work. + */ + scsi_unblock_requests(ha->shost); + sas_drain_work(ha); + + /* + * Send PHYE_RESUME_TIMEOUT after sas_drain_work(). The handler + * calls sas_deform_port() -> sas_destruct_devices(), which removes + * SCSI devices and, for LLDDs using device_link() PM sync, waits + * for the host to be runtime-active. Sending it before the drain + * would deadlock: the drain waits for the handler, the handler + * waits for host resume, and host resume waits for the drain to + * finish. + */ for (i = 0; i < ha->num_phys; i++) { struct asd_sas_phy *phy = ha->sas_phy[i]; @@ -436,12 +453,6 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) } } - /* all phys are back up or timed out, turn on i/o so we can - * flush out disks that did not return - */ - scsi_unblock_requests(ha->shost); - if (drain) - sas_drain_work(ha); clear_bit(SAS_HA_RESUMING, &ha->state); sas_queue_deferred_work(ha); @@ -450,20 +461,8 @@ static void _sas_resume_ha(struct sas_ha_struct *ha, bool drain) */ sas_resume_insert_broadcast_ha(ha); } - -void sas_resume_ha(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, true); -} EXPORT_SYMBOL(sas_resume_ha); -/* A no-sync variant, which does not call sas_drain_ha(). */ -void sas_resume_ha_no_sync(struct sas_ha_struct *ha) -{ - _sas_resume_ha(ha, false); -} -EXPORT_SYMBOL(sas_resume_ha_no_sync); - void sas_suspend_ha(struct sas_ha_struct *ha) { int i; diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..36d4cb567837 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -680,7 +680,6 @@ extern int sas_register_ha(struct sas_ha_struct *); extern int sas_unregister_ha(struct sas_ha_struct *); extern void sas_prep_resume_ha(struct sas_ha_struct *sas_ha); extern void sas_resume_ha(struct sas_ha_struct *sas_ha); -extern void sas_resume_ha_no_sync(struct sas_ha_struct *sas_ha); extern void sas_suspend_ha(struct sas_ha_struct *sas_ha); int sas_phy_reset(struct sas_phy *phy, int hard_reset); From 4d5282c06ca198319c2de41b10511ddcb8068f42 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 20 Jul 2026 10:35:52 +0800 Subject: [PATCH 125/501] scsi: ufs: dt-bindings: Add missing mcq reg for qcom,sa8255p-ufshc Qualcomm UFS controller found on SoCs SA8255P/SA8797P has a MCQ I/O address space. It should be defined in the bindings even though Linux driver currently doesn't utilize it. Fix the binding before it gets adopted by DTS. Fixes: e2725ed2a7fb ("scsi: ufs: dt-bindings: Document bindings for SA8255P UFS Host Controller") Cc: stable@vger.kernel.org Signed-off-by: Shawn Guo Acked-by: Krzysztof Kozlowski Link: https://patch.msgid.link/20260720023552.2667237-1-shengchao.guo@oss.qualcomm.com Signed-off-by: Martin K. Petersen --- .../devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml index db165a235cb6..66d73e9025d6 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,sa8255p-ufshc.yaml @@ -18,7 +18,14 @@ properties: - const: qcom,sa8255p-ufshc reg: - maxItems: 1 + minItems: 1 + maxItems: 2 + + reg-names: + minItems: 1 + items: + - const: std + - const: mcq interrupts: maxItems: 1 From 9349dd0646673964eb9e993050526029fc599677 Mon Sep 17 00:00:00 2001 From: Fredric Cover Date: Wed, 22 Jul 2026 14:18:44 -0700 Subject: [PATCH 126/501] smb: client: use GFP_KERNEL for registry allocation Currently, cifs_get_swn_reg() allocates new registry entries using GFP_ATOMIC. Since we lock a mutex here, this is clearly not an atomic context. Use GFP_KERNEL instead. Also, fix a minor grammatical error in the comment above the function. Signed-off-by: Fredric Cover Signed-off-by: Steve French --- fs/smb/client/cifs_swn.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifs_swn.c b/fs/smb/client/cifs_swn.c index 9951817d0d7f..fe10719e627e 100644 --- a/fs/smb/client/cifs_swn.c +++ b/fs/smb/client/cifs_swn.c @@ -425,7 +425,7 @@ static struct cifs_swn_reg *cifs_find_swn_reg(struct cifs_tcon *tcon) /* * Get a registration for the tcon's server and share name, allocating a new one if it does not - * exists + * exist. */ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) { @@ -443,7 +443,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) goto unlock; } - reg = kmalloc_obj(struct cifs_swn_reg, GFP_ATOMIC); + reg = kmalloc_obj(struct cifs_swn_reg, GFP_KERNEL); if (reg == NULL) { ret = -ENOMEM; goto fail_unlock; @@ -451,7 +451,7 @@ static struct cifs_swn_reg *cifs_get_swn_reg(struct cifs_tcon *tcon) kref_init(®->ref_count); - reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_ATOMIC); + reg->id = idr_alloc(&cifs_swnreg_idr, reg, 1, 0, GFP_KERNEL); if (reg->id < 0) { cifs_dbg(FYI, "%s: failed to allocate registration id\n", __func__); ret = reg->id; From 6a3e16d60e81a4aa3056ab15617036cfbea2e07d Mon Sep 17 00:00:00 2001 From: Dawei Feng Date: Sun, 28 Jun 2026 14:59:09 +0800 Subject: [PATCH 127/501] smb: client: fix buffer leaks in SMB1 read and write CIFSSMBRead(), CIFSSMBWrite() and CIFSSMBWrite2() allocate a request buffer before checking whether tcon->ses->server is NULL. If that defensive check ever fails, the helper returns -ECONNABORTED without releasing the request buffer. Fix these leaks by releasing the allocated request buffer before returning from these error paths. Use cifs_small_buf_release() for the buffers allocated by small_smb_init() and cifs_buf_release() for the buffer allocated by smb_init(). The bug was first flagged by an experimental analysis tool we are developing for kernel memory-management bugs while analyzing v6.13-rc1. The tool is still under development and is not yet publicly available. Manual inspection confirms that the bug is still present in v7.1.1. An x86_64 allyesconfig build showed no new warnings. Runtime validation used a temporary fault-injection hook to force tcon->ses->server to NULL after request-buffer initialization. On the unfixed kernel, the harness observed two leaked small request buffers and one leaked large request buffer, with directed kmemleak dumps confirming the CIFS buffer allocation stacks. After the fix, no CIFS request-buffer deltas remained. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Dawei Feng Signed-off-by: Steve French --- fs/smb/client/cifssmb.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c index 40162d5554ea..1f77512252e7 100644 --- a/fs/smb/client/cifssmb.c +++ b/fs/smb/client/cifssmb.c @@ -1681,8 +1681,10 @@ CIFSSMBRead(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -1796,8 +1798,10 @@ CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; @@ -2077,8 +2081,10 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms, pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid >> 16)); /* tcon and ses pointer are checked in smb_init */ - if (tcon->ses->server == NULL) + if (!tcon->ses->server) { + cifs_small_buf_release(pSMB); return -ECONNABORTED; + } pSMB->AndXCommand = 0xFF; /* none */ pSMB->Fid = netfid; From aed0714255c80d143e9f6d4ae00ee14423204ad5 Mon Sep 17 00:00:00 2001 From: Sang-Heon Jeon Date: Fri, 24 Jul 2026 03:45:36 +0900 Subject: [PATCH 128/501] smb: client: remove conditional return with no effect Both branches of the check return the same value, so the check has no effect. Remove it and return the value directly. This is the result of running the Coccinelle script from scripts/coccinelle/misc/cond_return_no_effect.cocci. Signed-off-by: Sang-Heon Jeon Signed-off-by: Steve French --- fs/smb/client/smb1maperror.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/smb/client/smb1maperror.c b/fs/smb/client/smb1maperror.c index ab3d09613c91..395299f9121b 100644 --- a/fs/smb/client/smb1maperror.c +++ b/fs/smb/client/smb1maperror.c @@ -234,11 +234,7 @@ int __init smb1_init_maperror(void) if (rc) return rc; - rc = mapping_table_ERRSRV_is_sorted(); - if (rc) - return rc; - - return rc; + return mapping_table_ERRSRV_is_sorted(); } #if IS_ENABLED(CONFIG_SMB1_KUNIT_TESTS) From 455488cd5054bcc59db40fa1cc2c004031a5b2a5 Mon Sep 17 00:00:00 2001 From: Li Qiang Date: Sun, 19 Jul 2026 00:22:27 +0800 Subject: [PATCH 129/501] cifs: validate idmap key payload length The cifs.idmap key type stores its payload length in key->datalen, which is limited to U16_MAX. Accepting a larger key payload truncates the recorded length and can make later users interpret the payload using inconsistent bounds. Reject oversized preparsed payloads before allocating or copying them. This keeps key->datalen consistent with the stored data for both inline and separately allocated idmap payloads. Signed-off-by: Li Qiang Signed-off-by: Steve French --- fs/smb/client/cifsacl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c index 9424281a7674..12005f46307d 100644 --- a/fs/smb/client/cifsacl.c +++ b/fs/smb/client/cifsacl.c @@ -68,6 +68,9 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) { char *payload; + if (prep->datalen > U16_MAX) + return -EINVAL; + /* * If the payload is less than or equal to the size of a pointer, then * an allocation here is wasteful. Just copy the data directly to the From 6a39ca1286dd97ad02bb8e03bbb65ee05368d778 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Fri, 17 Jul 2026 21:48:36 +0300 Subject: [PATCH 130/501] drm/bridge: display-connector: Fix I2C adapter resource leak If the probe function returns an error after getting the I2C adapter for DDC, the reference to the adapter is never released. Fix it by releasing it in the bridge .destroy() handler. There is no need to test the ddc pointer with !IS_ERR(), as of_get_i2c_adapter_by_node() returns NULL on error. Fixes: 2e2bf3a5584d ("drm/bridge: display-connector: add DP support") Cc: stable@vger.kernel.org Signed-off-by: Laurent Pinchart Reviewed-by: Johan Hovold Reviewed-by: Luca Ceresoli Link: https://patch.msgid.link/20260717184836.2017386-1-laurent.pinchart+renesas@ideasonboard.com Signed-off-by: Luca Ceresoli --- drivers/gpu/drm/bridge/display-connector.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/bridge/display-connector.c b/drivers/gpu/drm/bridge/display-connector.c index 16c0631adeb1..6b128fabe3a9 100644 --- a/drivers/gpu/drm/bridge/display-connector.c +++ b/drivers/gpu/drm/bridge/display-connector.c @@ -40,6 +40,13 @@ static int display_connector_attach(struct drm_bridge *bridge, return flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR ? 0 : -EINVAL; } +static void display_connector_destroy(struct drm_bridge *bridge) +{ + struct display_connector *conn = to_display_connector(bridge); + + i2c_put_adapter(conn->bridge.ddc); +} + static enum drm_connector_status display_connector_detect(struct drm_bridge *bridge) { struct display_connector *conn = to_display_connector(bridge); @@ -177,6 +184,7 @@ static u32 *display_connector_get_input_bus_fmts(struct drm_bridge *bridge, static const struct drm_bridge_funcs display_connector_bridge_funcs = { .attach = display_connector_attach, + .destroy = display_connector_destroy, .detect = display_connector_bridge_detect, .edid_read = display_connector_edid_read, .atomic_get_output_bus_fmts = display_connector_get_output_bus_fmts, @@ -403,9 +411,6 @@ static void display_connector_remove(struct platform_device *pdev) regulator_disable(conn->supply); drm_bridge_remove(&conn->bridge); - - if (!IS_ERR(conn->bridge.ddc)) - i2c_put_adapter(conn->bridge.ddc); } static const struct of_device_id display_connector_match[] = { From 47d4e945dff8139050473be4ab263a32e1da910c Mon Sep 17 00:00:00 2001 From: Christian Loehle Date: Fri, 24 Jul 2026 11:40:42 +0100 Subject: [PATCH 131/501] ACPI: CPPC: Skip writes to unsupported performance controls MIN_PERF and MAX_PERF are optional CPPC controls. DESIRED_PERF is also optional with CPPC2 when autonomous selection is supported. The cppc-cpufreq target callbacks populate both limits for every request without checking whether the controls are implemented. cppc_set_perf() consequently passes NULL register descriptors to cpc_write(). The writes fail width validation and their return values are ignored, so the failed access paths are repeated on every target request. An autonomous-only platform can take the same path for DESIRED_PERF. Check that each performance control is supported before calling cpc_write(). Fixes: ea3db45ae476 ("cpufreq: cppc: Update MIN_PERF/MAX_PERF in target callbacks") Reviewed-by: Sumit Gupta Signed-off-by: Christian Loehle Reviewed-by: Lifeng Zheng Link: https://patch.msgid.link/20260724104042.1481804-1-christian.loehle@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/cppc_acpi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/cppc_acpi.c b/drivers/acpi/cppc_acpi.c index 1d3a94100491..53d09ca98f06 100644 --- a/drivers/acpi/cppc_acpi.c +++ b/drivers/acpi/cppc_acpi.c @@ -1963,16 +1963,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls) cpc_desc->write_cmd_status = 0; } - cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); + if (CPC_SUPPORTED(desired_reg)) + cpc_write(cpu, desired_reg, perf_ctrls->desired_perf); /* * Only write if min_perf and max_perf not zero. Some drivers pass zero * value to min and max perf, but they don't mean to set the zero value, * they just don't want to write to those registers. */ - if (perf_ctrls->min_perf) + if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg)) cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf); - if (perf_ctrls->max_perf) + if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg)) cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf); if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg)) From 27460bd80bbab542f7f2ddf687923a73984757eb Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Mon, 27 Jul 2026 16:00:48 +0800 Subject: [PATCH 132/501] ALSA:hda/realtek:ALC269 fixup for Legion 7 15ASH11 Mic Mute LED Lenovo Legion 7 15ASH11 with AMD RYZEN AI MAX+ 392 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec. Its audio subsystem adopts a hardware design similar to that of the Yoga Pro 7 15ASH11. It shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5. Therefore, use HDA_CODEC_QUIRK to apply ALC287_FIXUP_LENOVO_YOGA_PRO7 for identification. After added the HDA_CODEC_QUIRK quirk special for Lenovo Legion 7 15ASH11, the mic mute LED works well. Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260727080048.13254-1-xy-jackie@139.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index 09225801a177..5a516dee56ff 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7973,6 +7973,10 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x38e0, "Yoga Y990 Intel VECO Dual", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38f8, "Yoga Book 9i", ALC287_FIXUP_TAS2781_I2C), SND_PCI_QUIRK(0x17aa, 0x38df, "Y990 YG DUAL", ALC287_FIXUP_TAS2781_I2C), + /* Legion 7 15ASH11 shares PCI SSID 17aa:38f9 with Thinkbook 16P Gen5; + * use codec SSID to distinguish them + */ + HDA_CODEC_QUIRK(0x17aa, 0x395b, "Lenovo Legion 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), SND_PCI_QUIRK(0x17aa, 0x38f9, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fa, "Thinkbook 16P Gen5", ALC287_FIXUP_MG_RTKC_CSAMP_CS35L41_I2C_THINKPAD), SND_PCI_QUIRK(0x17aa, 0x38fc, "Lenovo Yoga Pro 7 15ASH11", ALC287_FIXUP_LENOVO_YOGA_PRO7), From bed0c8084044364f5ac3f3e89e1bbad423f6b0d4 Mon Sep 17 00:00:00 2001 From: Michael Diesen Date: Mon, 27 Jul 2026 09:19:21 +0000 Subject: [PATCH 133/501] ALSA: hda/realtek: Add quirk for HP Dragonfly Folio G3 2-in-1 (103c:8a05) The HP Dragonfly Folio G3 2-in-1 also ships with PCI SSID 103c:8a05. On this unit the ALC245 codec reports subsystem id 103c:8a06 - the SSID that is already covered by commit 0a10faad5ca5 ("ALSA: hda/realtek: add quirk for HP Dragonfly Folio G3 2-in-1") - while the PCI SSID that SND_PCI_QUIRK matches against is 103c:8a05: snd_hda_codec_alc269 ehdaudio0D0: ALC245: picked fixup for PCI SSID 103c:8a05 cs35l41-hda spi1-CSC3551:00-cs35l41-hda.0: CS35L41 Bound - SSID: 103C8A06 The existing entry therefore never applies here, the four CS35L41 amplifiers on SPI are not registered and the internal speakers stay silent. Add the same fixup that the 8a06 entry uses: the four amplifiers bind and the speaker mute LED (codec GPIO 0x04) works. Signed-off-by: Michael Diesen Link: https://patch.msgid.link/20260727091920.4634-1-michael.diesen@posteo.de 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 5a516dee56ff..f620cd429f20 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7255,6 +7255,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF), SND_PCI_QUIRK(0x103c, 0x89da, "HP Spectre x360 14t-ea100", ALC245_FIXUP_HP_SPECTRE_X360_EU0XXX), SND_PCI_QUIRK(0x103c, 0x89e7, "HP Elite x2 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED), + SND_PCI_QUIRK(0x103c, 0x8a05, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a06, "HP Dragonfly Folio G3 2-in-1", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8a1b, "HP 255 15.6 inch G9 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), From d5f8e5f6040d052d44fcbf4f31dd35145c0c8d7d Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 27 Jul 2026 15:05:51 +0530 Subject: [PATCH 134/501] cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init() The memory allocated for data->powernow_table inside powernow_k8_cpu_init_acpi() or find_psb_table() is not freed in one of the error paths in powernowk8_cpu_init(). Fix that by adding a kfree(). Fixes: 1ff6e97f1d99 ("[CPUFREQ] cpumask: avoid playing with cpus_allowed in powernow-k8.c") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Acked-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/20260727093553.98246-1-nihaal@cse.iitm.ac.in Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernow-k8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 2b791f1ec51b..909a3f0598a9 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1084,6 +1084,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol) err_out_exit_acpi: powernow_k8_cpu_exit_acpi(data); + kfree(data->powernow_table); err_out: kfree(data); From 83195b778f2d109a3a4f3ffaba4dce7e4cdb58aa Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:22 -0400 Subject: [PATCH 135/501] drm/vmwgfx: fix guest_memory_dirty bitfield clobbered as size Two sites in vmwgfx_resource.c assign boolean literals to res->guest_memory_size, which is an unsigned long allocation-size field; the intended target is the adjacent res->guest_memory_dirty bitfield. After the assignments the field holds 0 or 1 instead of the resource's MOB allocation size: - vmw_resource_release() writes 0 (false), and - vmw_resource_unbind_list() writes 1 (true). Subsequent revalidation paths read guest_memory_size when computing the dirty page range (vmw_bo_dirty_transfer_to_res()) and the buffer allocation size (vmw_resource_buf_alloc()), producing zero-length walks or wrap-around ranges that read or write past the MOB bitmap. The dirty-tracking intent of the original code (mark the resource as dirtied since the last sync) is also lost, since guest_memory_dirty is never updated. Rename both assignments to guest_memory_dirty. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-2-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 388011696941..e3a187a2c7a1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -136,7 +136,7 @@ static void vmw_resource_release(struct kref *kref) val_buf.num_shared = 0; res->func->unbind(res, false, &val_buf); } - res->guest_memory_size = false; + res->guest_memory_dirty = false; vmw_resource_mob_detach(res); if (res->dirty) res->func->dirty_free(res); @@ -773,7 +773,7 @@ void vmw_resource_unbind_list(struct vmw_bo *vbo) if (!WARN_ON_ONCE(!res->func->unbind)) (void) res->func->unbind(res, res->res_dirty, &val_buf); - res->guest_memory_size = true; + res->guest_memory_dirty = true; res->res_dirty = false; vmw_resource_mob_detach(res); } From 55ec09c9ce10b1272802c7ab6c1be2ea0dbc68db Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:23 -0400 Subject: [PATCH 136/501] drm/vmwgfx: reject DX_BIND_QUERY without a DX context vmw_cmd_dx_bind_query() unconditionally dereferences sw_context->dx_ctx_node->ctx. Userspace can trigger a NULL pointer dereference from any render-node fd by submitting an execbuf with dx_context_handle == SVGA3D_INVALID_ID and a SVGA_3D_CMD_DX_BIND_QUERY opcode in the command stream: dx_ctx_node is left NULL and the kernel oopses on the assignment. The same NULL is then re-read in vmw_resources_reserve() via vmw_context_get_dx_query_mob(). All sibling DX handlers fail-close on a missing dx_ctx_node using VMW_GET_CTX_NODE(). Use the same pattern here, returning -EINVAL up front before any relocation state is published. Fixes: 9c079b8ce8bf ("drm/vmwgfx: Adapt execbuf to the new validation api") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-3-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index e1f18020170a..b07f052474d0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1272,9 +1272,13 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, SVGA3dCmdHeader *header) { VMW_DECLARE_CMD_VAR(*cmd, SVGA3dCmdDXBindQuery); + struct vmw_ctx_validation_info *ctx_node = VMW_GET_CTX_NODE(sw_context); struct vmw_bo *vmw_bo; int ret; + if (!ctx_node) + return -EINVAL; + cmd = container_of(header, typeof(*cmd), header); /* @@ -1288,7 +1292,7 @@ static int vmw_cmd_dx_bind_query(struct vmw_private *dev_priv, return ret; sw_context->dx_query_mob = vmw_bo; - sw_context->dx_query_ctx = sw_context->dx_ctx_node->ctx; + sw_context->dx_query_ctx = ctx_node->ctx; return 0; } From f47d542d5912f236273399d7139b522f6950e2e4 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:24 -0400 Subject: [PATCH 137/501] drm/vmwgfx: clamp dirty-page range with min, not max vmw_bo_dirty_transfer_to_res() and vmw_bo_dirty_clear() compute the intersection of a resource's page range with the BO's tracked dirty range, but clamp res_end against dirty->end with max() instead of min(). When dirty->end exceeds the resource end, the loop walks past the resource's pages, calls vmw_resource_dirty_update() for ranges owned by other resources sharing the same backing MOB and clears their pending dirty bits via bitmap_clear(). The result is silent loss of writeback for unrelated resources whenever two resources share a MOB. Use min() in both functions so the loop is bounded to the intersection of the resource and dirty ranges. Fixes: b7468b15d271 ("drm/vmwgfx: Implement an infrastructure for write-coherent resources") Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-4-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c index 45561bc1c9ef..8ab88f388652 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_page_dirty.c @@ -311,7 +311,7 @@ void vmw_bo_dirty_transfer_to_res(struct vmw_resource *res) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; @@ -347,7 +347,7 @@ void vmw_bo_dirty_clear(struct vmw_bo *vbo) return; cur = max(res_start, dirty->start); - res_end = max(res_end, dirty->end); + res_end = min(res_end, dirty->end); while (cur < res_end) { unsigned long num; From 250af2e8c3e90dc978e062a936b633870a22e660 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:25 -0400 Subject: [PATCH 138/501] drm/vmwgfx: take fman->lock around fence list mutation in fifo_down vmw_fence_fifo_down() drops fman->lock to wait on a fence and, on timeout, mutates fman->fence_list via list_del_init() and signals the fence without re-acquiring the lock. __vmw_fences_update() walks and removes entries from the same list under fman->lock from any other waiter, the fence-IRQ thread, or vmw_fences_update(), so the unlocked list_del_init() can corrupt the list head. Re-take fman->lock before manipulating fence->head and use dma_fence_signal_locked(). Wrap the locked signalling in dma_fence_begin_signalling() / dma_fence_end_signalling() so the lockdep annotation that dma_fence_signal() previously provided is preserved (the same pattern as __vmw_fences_update()). dma_fence_put() is moved outside the lock to avoid a recursive acquire from vmw_fence_obj_destroy(), which also takes fman->lock. Fixes: ae2a104058e2 ("vmwgfx: Implement fence objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-5-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 4ef84ff9b638..384c6736cf6b 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -367,13 +367,24 @@ void vmw_fence_fifo_down(struct vmw_fence_manager *fman) ret = vmw_fence_obj_wait(fence, false, false, VMW_FENCE_WAIT_TIMEOUT); + spin_lock(&fman->lock); if (unlikely(ret != 0)) { + bool cookie = dma_fence_begin_signalling(); + list_del_init(&fence->head); - dma_fence_signal(&fence->base); + if (fence->waiter_added) { + vmw_seqno_waiter_remove(fman->dev_priv); + fence->waiter_added = false; + } + dma_fence_signal_locked(&fence->base); + dma_fence_end_signalling(cookie); } BUG_ON(!list_empty(&fence->head)); + spin_unlock(&fman->lock); + dma_fence_put(&fence->base); + spin_lock(&fman->lock); } spin_unlock(&fman->lock); From f739416dc555fa205a785e5135d73fa39b26f35d Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:26 -0400 Subject: [PATCH 139/501] drm/vmwgfx: drop dma_buf reference on foreign-fd prime import ttm_prime_fd_to_handle() returns -ENOSYS when the imported fd's dma_buf->ops do not match the ttm_object_device's ops, but does so without releasing the reference acquired by dma_buf_get(). Any unprivileged renderD client passing a non-vmwgfx prime fd through the DRM_VMW_GB_SURFACE_REF{,_EXT} path leaks one dma_buf reference per call and indefinitely pins the foreign exporter's GEM resources. Funnel the error path through the existing dma_buf_put() so the reference is always dropped. Fixes: 65981f7681ab ("drm/ttm: Add a minimal prime implementation for ttm base objects") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-6-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/ttm_object.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/ttm_object.c b/drivers/gpu/drm/vmwgfx/ttm_object.c index 2421b0dd057c..f9042bafdc93 100644 --- a/drivers/gpu/drm/vmwgfx/ttm_object.c +++ b/drivers/gpu/drm/vmwgfx/ttm_object.c @@ -547,14 +547,17 @@ int ttm_prime_fd_to_handle(struct ttm_object_file *tfile, if (IS_ERR(dma_buf)) return PTR_ERR(dma_buf); - if (dma_buf->ops != &tdev->ops) - return -ENOSYS; + if (dma_buf->ops != &tdev->ops) { + ret = -ENOSYS; + goto out; + } prime = (struct ttm_prime_object *) dma_buf->priv; base = &prime->base; *handle = base->handle; ret = ttm_ref_object_add(tfile, base, NULL, false); +out: dma_buf_put(dma_buf); return ret; From 85891d174707d8bddcec7a888fb4e1d17def34f3 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:27 -0400 Subject: [PATCH 140/501] drm/vmwgfx: validate DRAW_PRIMITIVES header size before division vmw_cmd_draw() computes maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); where header->size is u32 and is taken straight from the user-supplied command stream. When header->size is less than sizeof(cmd->body) the unsigned subtraction wraps to nearly 4 GiB, producing a huge maxnum. Any user-controlled cmd->body.numVertexDecls then passes the bound and the loop dereferences decl[i] far past the end of the kernel command bounce buffer, producing an out-of-bounds read of kernel memory. Reject undersized headers up front. Fixes: 7a73ba7469cb ("drm/vmwgfx: Use TTM handles instead of SIDs as user-space surface handles.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-7-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index b07f052474d0..2410d53a75aa 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1571,11 +1571,17 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv, uint32_t maxnum; int ret; + cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body))) { + VMW_DEBUG_USER("Illegal DRAW_PRIMITIVES header size.\n"); + return -EINVAL; + } + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); if (unlikely(ret != 0)) return ret; - cmd = container_of(header, typeof(*cmd), header); maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl); if (unlikely(cmd->body.numVertexDecls > maxnum)) { From f4f1db96bfd68b81053693ba53405b6f510ac16c Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:28 -0400 Subject: [PATCH 141/501] drm/vmwgfx: bound DMA command body size against suffix pointer vmw_cmd_dma() locates the DMA suffix at (unsigned long) &cmd->body + header->size - sizeof(*suffix) without checking that header->size is large enough to contain both cmd->body and the suffix. An undersized header makes the suffix pointer underflow back into the previous command in the bounce buffer. The verifier later writes suffix->maximumOffset, clobbering verified fields of an already-relocated earlier command -- a TOCTOU on the device-visible command stream that lets one command rewrite another's GMR id, surface id, or other authenticated fields. Reject the command if the body is too small for the suffix to fit. Fixes: 4e4ddd477743 ("drm/vmwgfx: Fix queries if no dma buffer thrashing is occuring.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-8-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 2410d53a75aa..a9136a6523cb 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -1510,6 +1510,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, bool dirty; cmd = container_of(header, typeof(*cmd), header); + + if (unlikely(header->size < sizeof(cmd->body) + sizeof(*suffix))) { + VMW_DEBUG_USER("Illegal SVGA_3D_CMD_SURFACE_DMA size.\n"); + return -EINVAL; + } + suffix = (SVGA3dCmdSurfaceDMASuffix *)((unsigned long) &cmd->body + header->size - sizeof(*suffix)); From 05eaa887e7b4f40fba425f8a1d7a5a8a043092a6 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:29 -0400 Subject: [PATCH 142/501] drm/vmwgfx: avoid destroy_workqueue(NULL) on vkms init failure Two paths through vmw_vkms_init() can leave vmw->crc_workq NULL while still leaving the rest of the driver in a state that calls vmw_vkms_cleanup() at module unload: 1. vmw_host_get_guestinfo(GUESTINFO_VBLANK, ...) failing or returning an oversized buffer -- the common case on hosts without a VBLANK guestinfo entry -- early-returned before the workqueue allocation. 2. alloc_ordered_workqueue() returning NULL on memory pressure. vmw_vkms_cleanup() then calls destroy_workqueue(NULL), which dereferences wq->name and panics. Fix the first case by removing the early return: vmw->vkms_enabled is already false on the rpci-failure path so no work will ever be queued, and allocating the workqueue unconditionally keeps the control flow simple. Fix the second case by guarding the cleanup with a NULL check, since alloc_ordered_workqueue() can still fail under low memory. Fixes: 7b0062036c3b ("drm/vmwgfx: Implement virtual crc generation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-9-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c index 7b8163b5e501..3d0d5dfa869f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_vkms.c @@ -206,14 +206,14 @@ vmw_vkms_init(struct vmw_private *vmw) vmw->vkms_enabled = false; ret = vmw_host_get_guestinfo(GUESTINFO_VBLANK, buffer, &buf_len); - if (ret || buf_len > max_buf_len) - return; - buffer[buf_len] = '\0'; + if (!ret && buf_len <= max_buf_len) { + buffer[buf_len] = '\0'; - ret = kstrtobool(buffer, &vmw->vkms_enabled); - if (!ret && vmw->vkms_enabled) { - ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); - vmw->vkms_enabled = (ret == 0); + ret = kstrtobool(buffer, &vmw->vkms_enabled); + if (!ret && vmw->vkms_enabled) { + ret = drm_vblank_init(&vmw->drm, VMWGFX_NUM_DISPLAY_UNITS); + vmw->vkms_enabled = (ret == 0); + } } vmw->crc_workq = alloc_ordered_workqueue("vmwgfx_crc_generator", 0); @@ -228,7 +228,8 @@ vmw_vkms_init(struct vmw_private *vmw) void vmw_vkms_cleanup(struct vmw_private *vmw) { - destroy_workqueue(vmw->crc_workq); + if (vmw->crc_workq) + destroy_workqueue(vmw->crc_workq); } bool From d5ed8749168ad13c0dbaa8300f68d854b6076966 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:30 -0400 Subject: [PATCH 143/501] drm/vmwgfx: enforce cursor size limits for MOB cursors vmw_cursor_plane_atomic_check() bounds cursor width and height only on the legacy update path; the SVGA_CAP2_CURSOR_MOB path -- the default on modern hosts -- accepts any size. When the requested size exceeds SVGA_REG_CURSOR_MAX_DIMENSION or SVGA_REG_MOB_MAX_SIZE, vmw_cursor_mob_get() returns -EINVAL and leaves vps->cursor.mob NULL. Its return value is then discarded in vmw_cursor_plane_prepare_fb(), so the subsequent vmw_cursor_update_mob() calls vmw_bo_map_and_cache(NULL) and oopses inside vmw_bo_map_and_cache_size() on the tbo.base.size load. Reachable from any DRM master via DRM_IOCTL_MODE_CURSOR2 with a sufficiently large width or height (e.g. cursor_max_dim + 1). Reject oversized cursors in atomic_check for both MOB-backed cursor update types. The MOB byte-size limit only applies to the SVGA_CAP2_CURSOR_MOB path (vmw_cursor_mob_size() returns 0 for GB_ONLY); compute the required MOB size in 64-bit to avoid overflow when very large dimensions are requested. In prepare_fb only call vmw_cursor_mob_get()/_map() for VMW_CURSOR_UPDATE_MOB -- the GB_ONLY path uses bo->map.virtual directly and would otherwise be silently downgraded to NONE on hosts without SVGA_CAP2_CURSOR_MOB (where vmw_cursor_mob_get() always returns -EINVAL). Degrade the update to NONE if vmw_cursor_mob_get() or vmw_cursor_mob_map() fails so the update path does not run with a NULL backing MOB. Fixes: 965544150d1c ("drm/vmwgfx: Refactor cursor handling") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-10-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c | 49 ++++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c index b010fc7ca68e..d1e7df500190 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_cursor_plane.c @@ -432,6 +432,7 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) u32 size = vmw_cursor_mob_size(vps->cursor.update_type, vps->base.crtc_w, vps->base.crtc_h); struct vmw_bo *vbo = vps->cursor.mob; + void *map; if (!vbo) return -EINVAL; @@ -446,11 +447,15 @@ vmw_cursor_mob_map(struct vmw_plane_state *vps) if (unlikely(ret != 0)) return -ENOMEM; - vmw_bo_map_and_cache(vbo); + map = vmw_bo_map_and_cache(vbo); + if (!map) { + vmw_bo_unmap(vbo); + ret = -ENOMEM; + } ttm_bo_unreserve(&vbo->tbo); - return 0; + return ret; } /** @@ -663,9 +668,15 @@ int vmw_cursor_plane_prepare_fb(struct drm_plane *plane, !vmw_cursor_buffer_changed(vps, old_vps)) { vps->cursor.update_type = VMW_CURSOR_UPDATE_NONE; - } else { - vmw_cursor_mob_get(vcp, vps); - vmw_cursor_mob_map(vps); + } else if (vps->cursor.update_type == + VMW_CURSOR_UPDATE_MOB && + (vmw_cursor_mob_get(vcp, vps) || + vmw_cursor_mob_map(vps))) { + /* + * Reset the cursor to avoid crashes later. + */ + vps->cursor.update_type = + VMW_CURSOR_UPDATE_NONE; } } } @@ -732,6 +743,34 @@ int vmw_cursor_plane_atomic_check(struct drm_plane *plane, "surface not suitable for cursor\n"); return -EINVAL; } + } else if (update_type == VMW_CURSOR_UPDATE_GB_ONLY || + update_type == VMW_CURSOR_UPDATE_MOB) { + u32 cursor_max_dim = + vmw_read(vmw, SVGA_REG_CURSOR_MAX_DIMENSION); + + if (new_state->crtc_w > cursor_max_dim || + new_state->crtc_h > cursor_max_dim) { + drm_warn(&vmw->drm, + "Cursor dimensions (%d, %d) exceed device max %u\n", + new_state->crtc_w, new_state->crtc_h, + cursor_max_dim); + return -EINVAL; + } + + if (update_type == VMW_CURSOR_UPDATE_MOB) { + u32 mob_max_size = + vmw_read(vmw, SVGA_REG_MOB_MAX_SIZE); + u64 mob_size = (u64)new_state->crtc_w * + new_state->crtc_h * sizeof(u32) + + sizeof(SVGAGBCursorHeader); + + if (mob_size > mob_max_size) { + drm_warn(&vmw->drm, + "Cursor MOB size %llu exceeds device max %u\n", + mob_size, mob_max_size); + return -EINVAL; + } + } } return 0; From e5c3e484e0d84744a9bd9349469cd41dc8666a2f Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:31 -0400 Subject: [PATCH 144/501] drm/vmwgfx: skip hash_del_rcu when validation context has no hash table vmw_validation_add_resource() calls hash_add_rcu() only when ctx->sw_context is non-NULL, but the doomed-resource error path calls hash_del_rcu() unconditionally. The validation contexts declared with DECLARE_VAL_CONTEXT(_, NULL, 0) in vmwgfx_kms.c, vmwgfx_scrn.c, vmwgfx_stdu.c and vmwgfx_execbuf.c consequently reach a delete for a node that was never added to any hash chain. That is harmless today, but only incidentally so. hash_del_rcu() is hlist_del_init_rcu(), which is guarded by hlist_unhashed(), and vmw_validation_mem_alloc() hands out memory from __GFP_ZERO pages that are never recycled within a context's lifetime, so node->hash.head.pprev is always NULL and the delete does nothing. Neither property is apparent at the call site, and the asymmetry with the add side invites a real bug the first time either one changes. Mirror the condition from the add side so the node is only unlinked when it was actually linked. No functional change. Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-11-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_validation.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c index 35dc94c3db39..45fde7ec514f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_validation.c @@ -309,7 +309,8 @@ int vmw_validation_add_resource(struct vmw_validation_context *ctx, } node->res = vmw_resource_reference_unless_doomed(res); if (!node->res) { - hash_del_rcu(&node->hash.head); + if (ctx->sw_context) + hash_del_rcu(&node->hash.head); return -ESRCH; } From 54d56d5b42d2e4c72ba6e365e9774da90698aa22 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:32 -0400 Subject: [PATCH 145/501] drm/vmwgfx: use check_add_overflow for shader size+offset bound vmw_shader_define() validates the user-supplied shader window against its backing buffer with (u64)buffer->tbo.base.size < (u64)size + (u64)offset drm_vmw_shader_create_arg::offset is __u64 in the uapi; when it is near U64_MAX the unsigned addition wraps and the resulting tiny value passes the check. The unbounded offset is then stored in res->guest_memory_offset and forwarded to host SVGA shader-create commands. Use check_add_overflow() to detect the wrap and compare the resulting endpoint against the buffer size. Fixes: 668b206601c5 ("drm/vmwgfx: Stop using raw ttm_buffer_object's") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-12-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_shader.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c index eca4e3e97eb4..39811cf19db1 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_shader.c @@ -25,6 +25,8 @@ * **************************************************************************/ +#include + #include #include "vmwgfx_binding.h" @@ -685,7 +687,7 @@ int vmw_shader_destroy_ioctl(struct drm_device *dev, void *data, static int vmw_user_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type, uint8_t num_input_sig, uint8_t num_output_sig, @@ -739,7 +741,7 @@ static int vmw_user_shader_alloc(struct vmw_private *dev_priv, static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, struct vmw_bo *buffer, size_t shader_size, - size_t offset, + u64 offset, SVGA3dShaderType shader_type) { struct vmw_shader *shader; @@ -768,7 +770,7 @@ static struct vmw_resource *vmw_shader_alloc(struct vmw_private *dev_priv, static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, enum drm_vmw_shader_type shader_type_drm, - u32 buffer_handle, size_t size, size_t offset, + u32 buffer_handle, size_t size, u64 offset, uint8_t num_input_sig, uint8_t num_output_sig, uint32_t *shader_handle) { @@ -779,13 +781,16 @@ static int vmw_shader_define(struct drm_device *dev, struct drm_file *file_priv, int ret; if (buffer_handle != SVGA3D_INVALID_ID) { + u64 end; + ret = vmw_user_bo_lookup(file_priv, buffer_handle, &buffer); if (unlikely(ret != 0)) { VMW_DEBUG_USER("Couldn't find buffer for shader creation.\n"); return ret; } - if ((u64)buffer->tbo.base.size < (u64)size + (u64)offset) { + if (check_add_overflow((u64)size, (u64)offset, &end) || + end > buffer->tbo.base.size) { VMW_DEBUG_USER("Illegal buffer- or shader size.\n"); ret = -EINVAL; goto out_bad_arg; From 706c93c5813caabbb0d0a576c017d15aeec2c113 Mon Sep 17 00:00:00 2001 From: Zack Rusin Date: Tue, 5 May 2026 18:22:33 -0400 Subject: [PATCH 146/501] drm/vmwgfx: validate external BO copy bounds for both stride paths vmw_external_bo_copy() trusts caller-supplied offsets, strides, and heights and operates on imported dma-buf vmaps: - The equal-stride memcpy() bound was clamped after subtracting the offsets from dst_size and src_size; an offset larger than the BO size wraps the unsigned subtraction to a huge value and the resulting memcpy() runs off the end of the vmap. dst_stride * height is also a u32 multiplication that can overflow. - The non-equal-stride row-by-row path had no bound at all. The loop touches bytes through offset + (height - 1) * stride + width_in_bytes, with only a WARN_ON(dst_stride < width_in_bytes), and could likewise step past the end of either mapping. The offsets and strides are derived from STDU/SOU plane state, so a configured CRTC submitting a crafted atomic commit on an imported framebuffer can reach this path. Validate the exact row-copy endpoint against each BO's size up front using check_mul_overflow() and check_add_overflow(). Use the bulk memcpy() path only when width_in_bytes covers the whole stride; otherwise copy one row at a time so partial-row updates near the bottom of a framebuffer remain valid. Also reject zero strides and stride < width_in_bytes, both of which the row-by-row path cannot represent safely. Fixes: 50f119925091 ("drm/vmwgfx: Fix prime with external buffers") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.7 Signed-off-by: Zack Rusin Reviewed-by: Ian Forbes Link: https://patch.msgid.link/20260505222728.519626-13-zack.rusin@broadcom.com --- drivers/gpu/drm/vmwgfx/vmwgfx_blit.c | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c index 135b75a3e013..56f965ec99dc 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_blit.c @@ -30,6 +30,7 @@ #include "vmwgfx_bo.h" #include +#include /* * Template that implements find_first_diff() for a generic @@ -463,19 +464,42 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, container_of(dst->tbo.bdev, struct vmw_private, bdev); size_t dst_size = dst->tbo.resource->size; size_t src_size = src->tbo.resource->size; + size_t dst_end, src_end; struct iosys_map dst_map = {0}; struct iosys_map src_map = {0}; + bool dst_mapped = false; + bool src_mapped = false; int ret, i; int x_in_bytes; u8 *vsrc; u8 *vdst; + if (!height || !width_in_bytes) + return 0; + + if (!dst_stride || !src_stride) + return -EINVAL; + if (dst_stride < width_in_bytes || src_stride < width_in_bytes) + return -EINVAL; + if (check_mul_overflow((size_t)dst_stride, (size_t)height - 1, &dst_end) || + check_add_overflow(dst_end, (size_t)width_in_bytes, &dst_end) || + check_add_overflow((size_t)dst_offset, dst_end, &dst_end) || + dst_end > dst_size || + check_mul_overflow((size_t)src_stride, (size_t)height - 1, &src_end) || + check_add_overflow(src_end, (size_t)width_in_bytes, &src_end) || + check_add_overflow((size_t)src_offset, src_end, &src_end) || + src_end > src_size) { + drm_dbg_driver(&vmw->drm, "Out-of-bounds external BO copy\n"); + return -EINVAL; + } + vsrc = map_external(src, &src_map); if (!vsrc) { drm_dbg_driver(&vmw->drm, "Wasn't able to map src\n"); ret = -ENOMEM; goto out; } + src_mapped = true; vdst = map_external(dst, &dst_map); if (!vdst) { @@ -483,16 +507,13 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = -ENOMEM; goto out; } + dst_mapped = true; vsrc += src_offset; vdst += dst_offset; - if (src_stride == dst_stride) { - dst_size -= dst_offset; - src_size -= src_offset; - memcpy(vdst, vsrc, - min(dst_stride * height, min(dst_size, src_size))); + if (src_stride == dst_stride && width_in_bytes == dst_stride) { + memcpy(vdst, vsrc, dst_stride * (size_t)height); } else { - WARN_ON(dst_stride < width_in_bytes); for (i = 0; i < height; ++i) { memcpy(vdst, vsrc, width_in_bytes); vsrc += src_stride; @@ -508,8 +529,10 @@ static int vmw_external_bo_copy(struct vmw_bo *dst, u32 dst_offset, ret = 0; out: - unmap_external(src, &src_map); - unmap_external(dst, &dst_map); + if (src_mapped) + unmap_external(src, &src_map); + if (dst_mapped) + unmap_external(dst, &dst_map); return ret; } From e65848e4ce352bac9e3465099354c8b8f845391f Mon Sep 17 00:00:00 2001 From: Ming Lei Date: Sun, 26 Jul 2026 09:50:25 -0500 Subject: [PATCH 147/501] ublk: reset kernel-owned dev_info fields in ublk_ctrl_add_dev() ublk_ctrl_add_dev() memcpy()s the userspace ublksrv_ctrl_dev_info into ub->dev_info and then fixes up the fields the driver owns, but misses ->state and ->ublksrv_pid. A device added with ->state = UBLK_S_DEV_LIVE passes the "->state != UBLK_S_DEV_DEAD" test that ublk_stop_dev_unlocked() uses as its proxy for "a disk is attached", while ->ub_disk is still NULL, so DEL_DEV right after ADD_DEV oopses in del_gendisk(). UBLK_S_DEV_QUIESCED plus UBLK_F_USER_RECOVERY dies one step earlier, in ublk_force_abort_dev(). A poisoned ->state also gets START_USER_RECOVERY and the char device read/write path onto a device that was never started, and wedges START_DEV at -EEXIST. A poisoned ->ublksrv_pid just makes GET_DEV_INFO report an unrelated task as the ublk server. Reset both after the memcpy(), as ublk_detach_disk() does. Userspace only ever reads these back, so correcting them silently breaks nothing. ADD_DEV has copied ->state in unsanitized since ublk was merged, but back then it was harmless: the gendisk was allocated during ADD_DEV, and both teardown and the START_DEV -EEXIST check keyed off disk_live() rather than ->state. The oops became reachable once the disk allocation moved to START_DEV and those checks switched to ->state. Fixes: 6d9e6dfdf3b2 ("ublk: defer disk allocation") Cc: stable@vger.kernel.org Signed-off-by: Ming Lei Reviewed-by: Caleb Sander Mateos Link: https://patch.msgid.link/20260726145025.1507383-1-tom.leiming@gmail.com Signed-off-by: Jens Axboe --- drivers/block/ublk_drv.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 4ca6ec738c93..2a22f9dc1f2f 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -4764,6 +4764,15 @@ static int ublk_ctrl_add_dev(const struct ublksrv_ctrl_cmd *header) /* update device id */ ub->dev_info.dev_id = ub->ub_number; + /* + * ->state and ->ublksrv_pid are owned by the driver and only read back + * by userspace, but they come from the copied-in dev_info, so reset + * them. Otherwise a device added with ->state != DEAD looks live while + * ->ub_disk is still NULL. + */ + ub->dev_info.state = UBLK_S_DEV_DEAD; + ub->dev_info.ublksrv_pid = -1; + /* * 64bit flags will be copied back to userspace as feature * negotiation result, so have to clear flags which driver From 4ecef577d1ceddaa906720c71c06672433aba318 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:30 +0100 Subject: [PATCH 148/501] ASoC: SDCA: Rename sdca_irq_allocate() to include devm Make it more clear sdca_irq_allocate() uses devm allocations by adding it into the name. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-2-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 5 +++-- sound/soc/sdca/sdca_class.c | 4 ++-- sound/soc/sdca/sdca_interrupts.c | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index a515cc3df097..28fd44eb9334 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -86,8 +86,9 @@ int sdca_irq_populate(struct sdca_function_data *function, 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); + +struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *dev, + struct regmap *regmap, int irq); void sdca_irq_enable_early(struct sdca_function_data *function, struct sdca_interrupt_info *info); diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c index 8d7b007a068f..d7444f442c71 100644 --- a/sound/soc/sdca/sdca_class.c +++ b/sound/soc/sdca/sdca_class.c @@ -111,8 +111,8 @@ static void class_boot_work(struct work_struct *work) regcache_cache_only(drv->dev_regmap, false); - drv->irq_info = sdca_irq_allocate(drv->dev, drv->dev_regmap, - drv->sdw->irq); + drv->irq_info = devm_sdca_irq_allocate(drv->dev, drv->dev_regmap, + drv->sdw->irq); if (IS_ERR(drv->irq_info)) goto err; diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 4539a52a8e32..1e4efc0609d9 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -592,7 +592,7 @@ void sdca_irq_cleanup(struct device *dev, EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); /** - * sdca_irq_allocate - allocate an SDCA interrupt structure for a device + * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device * @sdev: Device pointer against which things should be allocated. * @regmap: regmap to be used for accessing the SDCA IRQ registers. * @irq: The interrupt number. @@ -604,8 +604,8 @@ EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); * Return: A pointer to the allocated sdca_interrupt_info struct, or an * error code. */ -struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, - struct regmap *regmap, int irq) +struct sdca_interrupt_info *devm_sdca_irq_allocate(struct device *sdev, + struct regmap *regmap, int irq) { struct sdca_interrupt_info *info; int ret, i; @@ -634,7 +634,7 @@ struct sdca_interrupt_info *sdca_irq_allocate(struct device *sdev, return info; } -EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA"); +EXPORT_SYMBOL_NS_GPL(devm_sdca_irq_allocate, "SND_SOC_SDCA"); static void irq_enable_flags(struct sdca_function_data *function, struct sdca_interrupt_info *info, bool early) From f18e97fa7f125e0e7da130b4d0ff1edd48d7510a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:31 +0100 Subject: [PATCH 149/501] ASoC: SDCA: Add sdca_irq_cleanup_late() The SDCA IRQs are split into two groups, those registered at bus probe time (basically just FDL) and those registered at component time. There currently exists only a single cleanup function, if the FDL IRQ is freed at component time, then nothing would re-register it if the component is probed again. But the IRQs depending on a component need to be freed if the card is destroyed so they can't use stale components. Split the clean up into two functions one for the component level and one for the bus level. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-3-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 5 +++ sound/soc/sdca/sdca_class_function.c | 2 +- sound/soc/sdca/sdca_interrupts.c | 53 ++++++++++++++++++++++------ 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 28fd44eb9334..38c6c58c2cc7 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -33,6 +33,7 @@ struct sdca_function_data; * @priv: Pointer to private data for use by the handler. * @irq: IRQ number allocated to this interrupt, also used internally to track * the IRQ being assigned. + * @early_request: Flag to indicate this IRQ was requested at bus probe time. */ struct sdca_interrupt { const char *name; @@ -48,6 +49,7 @@ struct sdca_interrupt { void *priv; int irq; + bool early_request; }; /** @@ -86,6 +88,9 @@ int sdca_irq_populate(struct sdca_function_data *function, void sdca_irq_cleanup(struct device *dev, struct sdca_function_data *function, struct sdca_interrupt_info *info); +void sdca_irq_cleanup_late(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info); struct sdca_interrupt_info *devm_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 1496a15f7d2a..5ae6c727c796 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -408,7 +408,7 @@ 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); + sdca_irq_cleanup_late(drv->dev, drv->function, drv->core->irq_info); } static int class_function_runtime_suspend(struct device *dev) diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 1e4efc0609d9..d86884a89c40 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -456,6 +456,8 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, if (ret) return ret; + interrupt->early_request = true; + ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; @@ -562,17 +564,10 @@ 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 - * @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. - * - * Typically this would be called from the driver for a single SDCA Function. - */ -void sdca_irq_cleanup(struct device *dev, - struct sdca_function_data *function, - struct sdca_interrupt_info *info) +static void sdca_irq_cleanup_flags(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info, + bool late_cleanup) { int i; @@ -584,13 +579,49 @@ void sdca_irq_cleanup(struct device *dev, if (interrupt->function != function || !interrupt->irq) continue; + if (interrupt->early_request && !late_cleanup) + continue; + sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); kfree(interrupt->name); } } + +/** + * sdca_irq_cleanup - Free the regular IRQs for an SDCA Function + * @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. + * + * Typically this would be called from the driver for a single SDCA Function + * from component remove. + */ +void sdca_irq_cleanup(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + sdca_irq_cleanup_flags(dev, function, info, false); +} EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup, "SND_SOC_SDCA"); +/** + * sdca_irq_cleanup_late - Free the early IRQs for an SDCA Function + * @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. + * + * Typically this would be called from the driver for a single SDCA Function + * from bus remove. + */ +void sdca_irq_cleanup_late(struct device *dev, + struct sdca_function_data *function, + struct sdca_interrupt_info *info) +{ + sdca_irq_cleanup_flags(dev, function, info, true); +} +EXPORT_SYMBOL_NS_GPL(sdca_irq_cleanup_late, "SND_SOC_SDCA"); + /** * devm_sdca_irq_allocate - allocate an SDCA interrupt structure for a device * @sdev: Device pointer against which things should be allocated. From 0880082c27b6251cc3fea307dffa6899ad163e8b Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:32 +0100 Subject: [PATCH 150/501] ASoC: SDCA: Remove devm from primary IRQ cleanup To provide greater flexibility on when the IRQs are requested for client drivers don't use devm for the primary IRQ request/cleanup helper functions. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-4-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_fdl.h | 2 ++ include/sound/sdca_interrupts.h | 2 ++ include/sound/sdca_jack.h | 2 ++ sound/soc/sdca/sdca_fdl.c | 13 +++++++++++-- sound/soc/sdca/sdca_interrupts.c | 8 ++++++++ sound/soc/sdca/sdca_jack.c | 13 +++++++++++-- 6 files changed, 36 insertions(+), 4 deletions(-) diff --git a/include/sound/sdca_fdl.h b/include/sound/sdca_fdl.h index fbaf4b384c8a..dc33927b82bd 100644 --- a/include/sound/sdca_fdl.h +++ b/include/sound/sdca_fdl.h @@ -67,6 +67,8 @@ struct fdl_state { #if IS_ENABLED(CONFIG_SND_SOC_SDCA_FDL) int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt); +void sdca_fdl_free_state(struct sdca_interrupt *interrupt); + int sdca_fdl_process(struct sdca_interrupt *interrupt); int sdca_fdl_sync(struct device *dev, struct sdca_function_data *function, struct sdca_interrupt_info *info); diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 38c6c58c2cc7..8a44c19e917c 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -31,6 +31,7 @@ struct sdca_function_data; * @entity: Pointer to the Entity that the interrupt is associated with. * @control: Pointer to the Control that the interrupt is associated with. * @priv: Pointer to private data for use by the handler. + * @free_priv: Pointer to a function that can be used to free the priv data. * @irq: IRQ number allocated to this interrupt, also used internally to track * the IRQ being assigned. * @early_request: Flag to indicate this IRQ was requested at bus probe time. @@ -47,6 +48,7 @@ struct sdca_interrupt { struct sdca_control *control; void *priv; + void (*free_priv)(struct sdca_interrupt *interrupt); int irq; bool early_request; diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 181541f0f4d8..59de40b7d7d0 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -28,6 +28,8 @@ struct jack_state { }; int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); +void sdca_jack_free_state(struct sdca_interrupt *interrupt); + int sdca_jack_process(struct sdca_interrupt *interrupt); int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *jack); int sdca_jack_report(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c index 994821a6df61..82e09d960c12 100644 --- a/sound/soc/sdca/sdca_fdl.c +++ b/sound/soc/sdca/sdca_fdl.c @@ -481,10 +481,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_fdl_process, "SND_SOC_SDCA"); */ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) { - struct device *dev = interrupt->dev; struct fdl_state *fdl_state; - fdl_state = devm_kzalloc(dev, sizeof(*fdl_state), GFP_KERNEL); + fdl_state = kzalloc_obj(*fdl_state); if (!fdl_state) return -ENOMEM; @@ -499,3 +498,13 @@ int sdca_fdl_alloc_state(struct sdca_interrupt *interrupt) return 0; } EXPORT_SYMBOL_NS_GPL(sdca_fdl_alloc_state, "SND_SOC_SDCA"); + +/** + * sdca_fdl_free_state - free state for an FDL interrupt + * @interrupt: SDCA interrupt structure. + */ +void sdca_fdl_free_state(struct sdca_interrupt *interrupt) +{ + kfree(interrupt->priv); +} +EXPORT_SYMBOL_NS_GPL(sdca_fdl_free_state, "SND_SOC_SDCA"); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index d86884a89c40..cd2c5d49bb95 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -457,6 +457,7 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, return ret; interrupt->early_request = true; + interrupt->free_priv = sdca_fdl_free_state; ret = sdca_fdl_alloc_state(interrupt); if (ret) @@ -530,6 +531,8 @@ int sdca_irq_populate(struct sdca_function_data *function, handler = function_status_handler; break; case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + interrupt->free_priv = sdca_jack_free_state; + ret = sdca_jack_alloc_state(interrupt); if (ret) return ret; @@ -537,6 +540,8 @@ int sdca_irq_populate(struct sdca_function_data *function, handler = detected_mode_handler; break; case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + interrupt->free_priv = sdca_fdl_free_state; + ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; @@ -584,6 +589,9 @@ static void sdca_irq_cleanup_flags(struct device *dev, sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); + if (interrupt->free_priv) + interrupt->free_priv(interrupt); + kfree(interrupt->name); } } diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index ae9636622a84..ffa870964924 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -132,10 +132,9 @@ EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); */ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) { - struct device *dev = interrupt->dev; struct jack_state *jack_state; - jack_state = devm_kzalloc(dev, sizeof(*jack_state), GFP_KERNEL); + jack_state = kzalloc_obj(*jack_state); if (!jack_state) return -ENOMEM; @@ -145,6 +144,16 @@ int sdca_jack_alloc_state(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS_GPL(sdca_jack_alloc_state, "SND_SOC_SDCA"); +/** + * sdca_jack_free_state - free state for a jack interrupt + * @interrupt: SDCA interrupt structure. + */ +void sdca_jack_free_state(struct sdca_interrupt *interrupt) +{ + kfree(interrupt->priv); +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA"); + static int type_get_mask(enum sdca_terminal_type type) { switch (type) { From 050406cbd676615ba71081bce69df710754d27e4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:33 +0100 Subject: [PATCH 151/501] ASoC: SDCA: Populate IRQ data earlier Currently, the IRQ data (attached Entity/Control/etc) is populated as the IRQ is requested. However, this can cause issues as occasionally the setup process wants to access specifics of an IRQ before the IRQ is actually enabled. To facilitate this cache all the IRQ data during sdca_irq_populate_early() and make sdca_irq_populate() simply request the outstanding IRQs. This also has the advantage that sdca_irq_populate() can now just iterate through the IRQ array which is much smaller/faster than going through every Entity in the Function for Controls. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-5-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_interrupts.h | 2 + sound/soc/sdca/sdca_interrupts.c | 115 ++++++++++++------------------- 2 files changed, 47 insertions(+), 70 deletions(-) diff --git a/include/sound/sdca_interrupts.h b/include/sound/sdca_interrupts.h index 8a44c19e917c..3b30146e21db 100644 --- a/include/sound/sdca_interrupts.h +++ b/include/sound/sdca_interrupts.h @@ -30,6 +30,7 @@ struct sdca_function_data; * @function: Pointer to the Function that the interrupt is associated with. * @entity: Pointer to the Entity that the interrupt is associated with. * @control: Pointer to the Control that the interrupt is associated with. + * @handler: Handler function to be called for the IRQ. * @priv: Pointer to private data for use by the handler. * @free_priv: Pointer to a function that can be used to free the priv data. * @irq: IRQ number allocated to this interrupt, also used internally to track @@ -46,6 +47,7 @@ struct sdca_interrupt { struct sdca_function_data *function; struct sdca_entity *entity; struct sdca_control *control; + irq_handler_t handler; void *priv; void (*free_priv)(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index cd2c5d49bb95..6f0d8c0fe622 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -421,7 +421,8 @@ static struct sdca_interrupt *get_interrupt_data(struct device *dev, int irq, * * This is intended to be used as part of the Function boot process. It * can be called before the soundcard is registered (ie. doesn't depend - * on component) and will register the FDL interrupts. + * on component) and will populate all the required IRQ data, as well as + * registering the FDL interrupts to start booting the device. * * Return: Zero on success, and a negative error code on failure. */ @@ -448,24 +449,36 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, else if (!interrupt) continue; + ret = sdca_irq_data_populate(dev, regmap, NULL, function, + entity, control, interrupt); + if (ret) + return ret; + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { - case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): - ret = sdca_irq_data_populate(dev, regmap, NULL, - function, entity, - control, interrupt); + case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): + interrupt->handler = function_status_handler; + break; + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + interrupt->handler = detected_mode_handler; + interrupt->free_priv = sdca_jack_free_state; + + ret = sdca_jack_alloc_state(interrupt); if (ret) return ret; - - interrupt->early_request = true; + break; + case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): + interrupt->handler = fdl_owner_handler; interrupt->free_priv = sdca_fdl_free_state; ret = sdca_fdl_alloc_state(interrupt); if (ret) return ret; + interrupt->early_request = true; + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, - fdl_owner_handler, + interrupt->handler, interrupt); if (ret) { dev_err(dev, "failed to request irq %s: %d\n", @@ -473,7 +486,11 @@ int sdca_irq_populate_early(struct device *dev, struct regmap *regmap, return ret; } break; + case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): + interrupt->handler = hid_handler; + break; default: + interrupt->handler = base_handler; break; } } @@ -498,70 +515,26 @@ int sdca_irq_populate(struct sdca_function_data *function, struct sdca_interrupt_info *info) { struct device *dev = component->dev; - int i, j; + int i, ret; guard(mutex)(&info->irq_lock); - for (i = 0; i < function->num_entities; i++) { - struct sdca_entity *entity = &function->entities[i]; + for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { + struct sdca_interrupt *interrupt = &info->irqs[i]; + int irq; - for (j = 0; j < entity->num_controls; j++) { - struct sdca_control *control = &entity->controls[j]; - int irq = control->interrupt_position; - struct sdca_interrupt *interrupt; - irq_handler_t handler; - int ret; + if (interrupt->function != function || interrupt->irq) + continue; - interrupt = get_interrupt_data(dev, irq, info); - if (IS_ERR(interrupt)) - return PTR_ERR(interrupt); - else if (!interrupt) - continue; + interrupt->component = component; - ret = sdca_irq_data_populate(dev, NULL, component, - function, entity, control, - interrupt); - if (ret) - return ret; - - handler = base_handler; - - switch (SDCA_CTL_TYPE(entity->type, control->sel)) { - case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS): - handler = function_status_handler; - break; - case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): - interrupt->free_priv = sdca_jack_free_state; - - ret = sdca_jack_alloc_state(interrupt); - if (ret) - return ret; - - handler = detected_mode_handler; - break; - case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER): - interrupt->free_priv = sdca_fdl_free_state; - - ret = sdca_fdl_alloc_state(interrupt); - if (ret) - return ret; - - handler = fdl_owner_handler; - break; - case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER): - handler = hid_handler; - break; - default: - break; - } - - ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, - handler, interrupt); - if (ret) { - dev_err(dev, "failed to request irq %s: %d\n", - interrupt->name, ret); - return ret; - } + irq = interrupt->control->interrupt_position; + ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, + interrupt->handler, interrupt); + if (ret) { + dev_err(dev, "failed to request irq %s: %d\n", + interrupt->name, ret); + return ret; } } @@ -581,13 +554,15 @@ static void sdca_irq_cleanup_flags(struct device *dev, for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { struct sdca_interrupt *interrupt = &info->irqs[i]; - if (interrupt->function != function || !interrupt->irq) + if (interrupt->function != function || + (interrupt->early_request && !late_cleanup)) continue; - if (interrupt->early_request && !late_cleanup) - continue; + if (interrupt->irq) + sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); - sdca_irq_free_locked(dev, info, i, interrupt->name, interrupt); + if (!late_cleanup) + continue; if (interrupt->free_priv) interrupt->free_priv(interrupt); From 3e81e2fb216327a73510a7aa5318023c379d479a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:34 +0100 Subject: [PATCH 152/501] ASoC: Add a component fixup_controls callback A card level fixup_controls callback was added in: commit df4d27b19b89 ("ASoC: Introduce 'fixup_controls' card method") This allowed the machine driver to take actions after all the card controls have been added. However, there are times when a codec driver would also want to do things like obtain references to controls for later use, which require all the controls to be present. Add a component level fixup_controls callback, echoing the card level option. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-6-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/soc-component.h | 2 ++ sound/soc/soc-component.c | 10 ++++++++++ sound/soc/soc-core.c | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h index aa423865dbe7..4b7d7954953d 100644 --- a/include/sound/soc-component.h +++ b/include/sound/soc-component.h @@ -78,6 +78,7 @@ struct snd_soc_component_driver { unsigned int num_dapm_routes; int (*probe)(struct snd_soc_component *component); + int (*fixup_controls)(struct snd_soc_component *component); void (*remove)(struct snd_soc_component *component); int (*suspend)(struct snd_soc_component *component); int (*resume)(struct snd_soc_component *component); @@ -380,6 +381,7 @@ void snd_soc_component_suspend(struct snd_soc_component *component); void snd_soc_component_resume(struct snd_soc_component *component); int snd_soc_component_is_suspended(struct snd_soc_component *component); int snd_soc_component_probe(struct snd_soc_component *component); +int snd_soc_component_fixup_controls(struct snd_soc_component *component); void snd_soc_component_remove(struct snd_soc_component *component); int snd_soc_component_of_xlate_dai_id(struct snd_soc_component *component, struct device_node *ep); diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 21492d15833f..2ce24513fac5 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -310,6 +310,16 @@ int snd_soc_component_probe(struct snd_soc_component *component) return soc_component_ret(component, ret); } +int snd_soc_component_fixup_controls(struct snd_soc_component *component) +{ + int ret = 0; + + if (component->driver->fixup_controls) + ret = component->driver->fixup_controls(component); + + return soc_component_ret(component, ret); +} + void snd_soc_component_remove(struct snd_soc_component *component) { if (component->driver->remove) diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 7817beea5b3b..44f9bb4473f5 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2162,6 +2162,11 @@ static int snd_soc_bind_card(struct snd_soc_card *card) goto probe_end; snd_soc_dapm_new_widgets(card); + for_each_card_components(card, component) { + ret = snd_soc_component_fixup_controls(component); + if (ret < 0) + goto probe_end; + } snd_soc_card_fixup_controls(card); ret = snd_card_register(card->snd_card); From b8f71f16134fadc287fbb14be2a42b707efb7a30 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:35 +0100 Subject: [PATCH 153/501] ASoC: SDCA: Switch to fixup_controls callback for IRQ registration Currently there are some race conditions around the boot of SDCA jack detection. The core creates DAPM widgets/routes quite a long time before it creates the associated ALSA control, and the jack detection IRQ is currently registered in component probe. At the time of component probe, the DAPM widgets exist, shortly after this the DAPM routes are added. At the time the DAPM routes are added the register value for the control is checked and the appropriate path is connected. The existing handling in the SDCA jack IRQ handles the case the control doesn't exist and updates the registers directly, which works until the DAPM routes are added. After the routes are added the DAPM graph has already set connected on a particular DAPM path, which will not be updated until an IRQ is received when the control is present. Thus those updates are usually not reflected in the resulting DAPM graph which can lead to the audio path being erroneously powered on/off. Switch to the new fixup_controls callback to register the IRQs, this is guaranteed to run after all the controls have been created. Which means we can avoid the aforementioned race condition and as a bonus no longer need to concern ourselves with a case where the IRQ handler runs and the ALSA control is unavailable. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-7-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/sdca/sdca_class_function.c | 4 +-- sound/soc/sdca/sdca_jack.c | 44 ++++++++++++---------------- 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c index 5ae6c727c796..2fb2b043c979 100644 --- a/sound/soc/sdca/sdca_class_function.c +++ b/sound/soc/sdca/sdca_class_function.c @@ -191,7 +191,7 @@ static const struct snd_soc_dai_ops class_function_sdw_ops = { .hw_free = class_function_sdw_remove_peripheral, }; -static int class_function_component_probe(struct snd_soc_component *component) +static int class_function_component_fixup_controls(struct snd_soc_component *component) { struct class_function_drv *drv = snd_soc_component_get_drvdata(component); struct sdca_class_drv *core = drv->core; @@ -217,7 +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, + .fixup_controls = class_function_component_fixup_controls, .remove = class_function_component_remove, .endianness = 1, }; diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index ffa870964924..3c84d17244a2 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -41,6 +41,7 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) struct jack_state *state = interrupt->priv; struct snd_kcontrol *kctl = state->kctl; struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL; + struct soc_enum *soc_enum; unsigned int reg, val; int ret; @@ -55,10 +56,12 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) return -ENOMEM; kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) - dev_dbg(dev, "control not found: %s\n", name); - else - state->kctl = kctl; + if (!kctl) { + dev_err(dev, "control not found: %s\n", name); + return -ENODEV; + } + + state->kctl = kctl; } reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, @@ -96,30 +99,21 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) dev_dbg(dev, "%s: %#x\n", interrupt->name, val); - if (kctl) { - struct soc_enum *soc_enum = (struct soc_enum *)kctl->private_value; + ucontrol = kzalloc_obj(*ucontrol); + if (!ucontrol) + return -ENOMEM; - ucontrol = kzalloc_obj(*ucontrol); - if (!ucontrol) - return -ENOMEM; + soc_enum = (struct soc_enum *)kctl->private_value; + ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); - ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val); - - ret = snd_soc_dapm_put_enum_double(kctl, ucontrol); - if (ret < 0) { - dev_err(dev, "failed to update selected mode: %d\n", ret); - return ret; - } - - snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); - } else { - ret = regmap_write(interrupt->function_regmap, reg, val); - if (ret) { - dev_err(dev, "failed to write selected mode: %d\n", ret); - return ret; - } + ret = snd_soc_dapm_put_enum_double(kctl, ucontrol); + if (ret < 0) { + dev_err(dev, "failed to update selected mode: %d\n", ret); + return ret; } + snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id); + return sdca_jack_report(interrupt); } EXPORT_SYMBOL_NS_GPL(sdca_jack_process, "SND_SOC_SDCA"); @@ -201,7 +195,7 @@ int sdca_jack_set_jack(struct sdca_interrupt_info *info, struct snd_soc_jack *ja struct sdca_control_range *range; struct jack_state *jack_state; - if (!interrupt->irq) + if (!interrupt->dev) continue; switch (SDCA_CTL_TYPE(entity->type, control->sel)) { From bf1b7821f85383a8804441f8fd5165be148f3ec8 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 21 Jul 2026 15:36:36 +0100 Subject: [PATCH 154/501] ASoC: SDCA: Move kcontrol search out of IRQ Now that the IRQs are always registered after all the ALSA controls are created it is possible to search for the control at the point the IRQ is requested. Move the control search out of the IRQ handler and do it at IRQ request time. This also fixes a potential issue when the card was torn down and reprobed without destroying the codec device, the kctl pointer stored by the IRQ handler would not be updated to the new control on the second probe. Signed-off-by: Charles Keepax Link: https://patch.msgid.link/20260721143636.361814-8-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- include/sound/sdca_jack.h | 1 + sound/soc/sdca/sdca_interrupts.c | 12 +++++++++ sound/soc/sdca/sdca_jack.c | 43 +++++++++++++++++++------------- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/include/sound/sdca_jack.h b/include/sound/sdca_jack.h index 59de40b7d7d0..871ba2d8146a 100644 --- a/include/sound/sdca_jack.h +++ b/include/sound/sdca_jack.h @@ -28,6 +28,7 @@ struct jack_state { }; int sdca_jack_alloc_state(struct sdca_interrupt *interrupt); +int sdca_jack_init_state(struct sdca_interrupt *interrupt); void sdca_jack_free_state(struct sdca_interrupt *interrupt); int sdca_jack_process(struct sdca_interrupt *interrupt); diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c index 6f0d8c0fe622..42fbd3af8a75 100644 --- a/sound/soc/sdca/sdca_interrupts.c +++ b/sound/soc/sdca/sdca_interrupts.c @@ -521,6 +521,8 @@ int sdca_irq_populate(struct sdca_function_data *function, for (i = 0; i < SDCA_MAX_INTERRUPTS; i++) { struct sdca_interrupt *interrupt = &info->irqs[i]; + struct sdca_control *control = interrupt->control; + struct sdca_entity *entity = interrupt->entity; int irq; if (interrupt->function != function || interrupt->irq) @@ -528,6 +530,16 @@ int sdca_irq_populate(struct sdca_function_data *function, interrupt->component = component; + switch (SDCA_CTL_TYPE(entity->type, control->sel)) { + case SDCA_CTL_TYPE_S(GE, DETECTED_MODE): + ret = sdca_jack_init_state(interrupt); + if (ret) + return ret; + break; + default: + break; + } + irq = interrupt->control->interrupt_position; ret = sdca_irq_request_locked(dev, info, irq, interrupt->name, interrupt->handler, interrupt); diff --git a/sound/soc/sdca/sdca_jack.c b/sound/soc/sdca/sdca_jack.c index 3c84d17244a2..73f8067906a9 100644 --- a/sound/soc/sdca/sdca_jack.c +++ b/sound/soc/sdca/sdca_jack.c @@ -47,23 +47,6 @@ int sdca_jack_process(struct sdca_interrupt *interrupt) guard(rwsem_write)(rwsem); - if (!kctl) { - const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", - interrupt->entity->label, - SDCA_CTL_SELECTED_MODE_NAME); - - if (!name) - return -ENOMEM; - - kctl = snd_soc_component_get_kcontrol(component, name); - if (!kctl) { - dev_err(dev, "control not found: %s\n", name); - return -ENODEV; - } - - state->kctl = kctl; - } - reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id, interrupt->control->sel, 0); @@ -148,6 +131,32 @@ void sdca_jack_free_state(struct sdca_interrupt *interrupt) } EXPORT_SYMBOL_NS_GPL(sdca_jack_free_state, "SND_SOC_SDCA"); +/** + * sdca_jack_init_state - Initialise transient state for a jack interrupt + * @interrupt: SDCA interrupt structure. + * + * Return: Zero on success or a negative error code. + */ +int sdca_jack_init_state(struct sdca_interrupt *interrupt) +{ + struct jack_state *jack_state = interrupt->priv; + const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s", + interrupt->entity->label, + SDCA_CTL_SELECTED_MODE_NAME); + + if (!name) + return -ENOMEM; + + jack_state->kctl = snd_soc_component_get_kcontrol(interrupt->component, name); + if (!jack_state->kctl) { + dev_err(interrupt->dev, "control not found: %s\n", name); + return -ENODEV; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(sdca_jack_init_state, "SND_SOC_SDCA"); + static int type_get_mask(enum sdca_terminal_type type) { switch (type) { From dd88cf6273de61f2f7206c2066af97798dbb38b0 Mon Sep 17 00:00:00 2001 From: wangdicheng Date: Mon, 20 Jul 2026 16:16:16 +0800 Subject: [PATCH 155/501] ASoC: tas2781: Use correct calibration data for SINEGAIN2 register The SINEGAIN2_REG case in cali_reg_update() references t->sin_gn[] rather than t->sin_gn2[], causing the second pilot tone gain calibration to be programmed with the wrong register address. These are distinct fields in struct fct_param_address and are populated from separate firmware parameters by the parser in tas2781-fmwlib.c. Fixes: 84d6a465f211 ("ASoC: tas2781: Support dsp firmware Alpha and Beta seaies") Signed-off-by: wangdicheng Link: https://patch.msgid.link/20260720081616.631413-1-wangdich9700@163.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2781-i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index 9e6f0ad5f05d..209067e98e1f 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -1310,8 +1310,8 @@ static void cali_reg_update(struct bulk_reg_val *p, t->sin_gn[2]); break; case TAS2781_PRM_SINEGAIN2_REG: - reg = TASDEVICE_REG(t->sin_gn[0], t->sin_gn[1], - t->sin_gn[2]); + reg = TASDEVICE_REG(t->sin_gn2[0], t->sin_gn2[1], + t->sin_gn2[2]); break; default: reg = 0; From d9eadfce2fac49445db40808fe4d8259f20a9d2b Mon Sep 17 00:00:00 2001 From: Srikanth Boyapally Date: Mon, 20 Jul 2026 18:25:10 +0530 Subject: [PATCH 156/501] spi: spi-cadence: Move TX FIFO full busy-wait into FIFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPI host transfers could intermittently stall with spi_transfer timeouts. The TXFULL condition was checked only once in cdns_transfer_one() before cdns_spi_process_fifo(), so if the FIFO became full again during refill, writes could be dropped and the transfer would never complete. Move the TXFULL busy-wait into the TX path of cdns_spi_process_fifo() so the 10µs back-off is applied per FIFO entry during filling, ensuring forward progress and eliminating spurious timeouts. Restrict the delay to host mode using spi_controller_is_target(), the controller is passed into cdns_spi_process_fifo() so the check is made at the point of use. In target mode this delay must not run as it causes the target to miss its transfer window and corrupt data. Fixes: 49530e641178 ("spi: cadence: Add usleep_range() for cdns_spi_fill_tx_fifo()") Signed-off-by: Srikanth Boyapally Reviewed-by: Radhey Shyam Pandey Link: https://patch.msgid.link/20260720125510.60166-1-srikanth.boyapally@amd.com Signed-off-by: Mark Brown --- drivers/spi/spi-cadence.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c index 9b4e5b7013ae..af1a05e78492 100644 --- a/drivers/spi/spi-cadence.c +++ b/drivers/spi/spi-cadence.c @@ -388,11 +388,13 @@ static inline void cdns_spi_writer(struct cdns_spi *xspi) /** * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO + * @ctlr: Pointer to the spi_controller structure * @xspi: Pointer to the cdns_spi structure * @ntx: Number of bytes to pack into the TX FIFO * @nrx: Number of bytes to drain from the RX FIFO */ -static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) +static void cdns_spi_process_fifo(struct spi_controller *ctlr, + struct cdns_spi *xspi, int ntx, int nrx) { ntx = clamp(ntx, 0, xspi->tx_bytes); nrx = clamp(nrx, 0, xspi->rx_bytes); @@ -407,6 +409,16 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx) } if (ntx) { + /* When xspi in busy condition, bytes may send failed, + * then spi control didn't work thoroughly, add one byte + * delay. Only in host mode; in target mode this delay + * causes data corruption as the target fails to prepare + * data in time. + */ + if (!spi_controller_is_target(ctlr) && + (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)) + udelay(10); + cdns_spi_writer(xspi); ntx--; } @@ -460,14 +472,14 @@ static irqreturn_t cdns_spi_irq(int irq, void *dev_id) cdns_spi_write(xspi, CDNS_SPI_THLD, 1); if (xspi->tx_bytes) { - cdns_spi_process_fifo(xspi, trans_cnt, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, trans_cnt, trans_cnt); } else { /* Fixed delay due to controller limitation with * RX_NEMPTY incorrect status * Xilinx AR:65885 contains more details */ udelay(10); - cdns_spi_process_fifo(xspi, 0, trans_cnt); + cdns_spi_process_fifo(ctlr, xspi, 0, trans_cnt); cdns_spi_write(xspi, CDNS_SPI_IDR, CDNS_SPI_IXR_DEFAULT); spi_finalize_current_transfer(ctlr); @@ -520,17 +532,11 @@ static int cdns_transfer_one(struct spi_controller *ctlr, cdns_spi_write(xspi, CDNS_SPI_THLD, xspi->tx_fifo_depth >> 1); } - /* When xspi in busy condition, bytes may send failed, - * then spi control didn't work thoroughly, add one byte delay - */ - if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL) - udelay(10); - xspi->n_bytes = cdns_spi_n_bytes(transfer); xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes); xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes); - cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0); + cdns_spi_process_fifo(ctlr, xspi, xspi->tx_fifo_depth, 0); cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); return transfer->len; From b0e8adb2ccb43009796897ced09f91636685c9d3 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 22 Jul 2026 07:14:36 -0700 Subject: [PATCH 157/501] hwmon: (nct6775-core) Fix number of temperature registers for NCT6116 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike NCT6106, NCT6116 only has three temperature registers, and with it only three temperature source and temperature source configuration registers. The register addresses match those of NCT6106 and can be re-used. The code used a separate array to list the temperature source registers for NCT6116, but used the size of the NCT6106 register array to set the number of registers. The NCT6106 register array provides six addresses, while the temperature source register array for NCT6116 only provides three addresses. This causes a KASAN report. BUG: KASAN: global-out-of-bounds in nct6775_probe+0x936/0x46f0 [nct6775] Read of size 2 at addr ffffffffc19561a6 by task modprobe/954 ... Call Trace: dump_stack+0x7d/0xa7 print_address_description.constprop.0+0x1c/0x220 ? __kasan_kmalloc.constprop.0+0xc9/0xd0 ? __kmalloc_node_track_caller+0x194/0x5b0 ? nct6775_probe+0x936/0x46f0 [nct6775] ? nct6775_probe+0x936/0x46f0 [nct6775] ... Fix the problem by hard-coding the number of temperature and temperature configuration registers to three for NCT6116. Drop the unnecessary NCT6116_REG_TEMP_SOURCE array and re-use NCT6106_REG_TEMP_SOURCE. Reported-by: Florian Bezdeka Closes: https://lore.kernel.org/linux-hwmon/57cfc3fa-d4e9-4c10-8aa7-4ad0af7ebebe@roeck-us.net/T/#t Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index d668dc390def..51253acff4b0 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -846,8 +846,6 @@ static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; -static const u16 NCT6116_REG_TEMP_SOURCE[] = { - 0xb0, 0xb1, 0xb2 }; static const u16 NCT6116_REG_CRITICAL_TEMP[] = { 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; @@ -3652,7 +3650,7 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, = NCT6106_CRITICAL_PWM_ENABLE_MASK; data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; - data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; + data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; @@ -3666,13 +3664,13 @@ int nct6775_probe(struct device *dev, struct nct6775_data *data, reg_temp = NCT6106_REG_TEMP; reg_temp_mon = NCT6106_REG_TEMP_MON; - num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp = 3; num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); num_reg_tsi_temp = ARRAY_SIZE(NCT6116_REG_TSI_TEMP); reg_temp_over = NCT6106_REG_TEMP_OVER; reg_temp_hyst = NCT6106_REG_TEMP_HYST; reg_temp_config = NCT6106_REG_TEMP_CONFIG; - num_reg_temp_config = ARRAY_SIZE(NCT6106_REG_TEMP_CONFIG); + num_reg_temp_config = 3; reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; reg_temp_crit = NCT6106_REG_TEMP_CRIT; reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; From a64a7e8a0b012ba81b0eadbd7afc84ab0dbfd70c Mon Sep 17 00:00:00 2001 From: Vincent Jardin Date: Thu, 23 Jul 2026 17:44:56 +0200 Subject: [PATCH 158/501] hwmon: (pmbus/core) notify on the hwmon device, not the i2c client pmbus_notify() calls sysfs_notify() and kobject_uevent() on the i2c client's kobject, but the alarm attributes live on the hwmon class device registered by pmbus_do_probe(). Notifying the parent i2c device is a no-op for both poll(POLLPRI) waiters and udev listeners: the named attribute does not exist on that kobject. Notify the hwmon device instead, so poll() wakes up and "change" uevents fire on the inX_alarm/tempX_alarm attributes when SMBALERT# reports a fault. Fixes: f469bde9afd1 ("hwmon: (pmbus/core) Notify hwmon events") Cc: stable@vger.kernel.org # v6.4+ Signed-off-by: Vincent Jardin Link: https://lore.kernel.org/r/20260723-fix_hwmon_notify_v1-v1-1-5a24c528686d@free.fr Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 3143b9e0316c..0081f16c3a95 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -2985,8 +2985,9 @@ static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags) if (reg == sreg && page == spage && (smask & flags)) { dev_dbg(data->dev, "sysfs notify: %s", da->attr.name); - sysfs_notify(&data->dev->kobj, NULL, da->attr.name); - kobject_uevent(&data->dev->kobj, KOBJ_CHANGE); + sysfs_notify(&data->hwmon_dev->kobj, NULL, + da->attr.name); + kobject_uevent(&data->hwmon_dev->kobj, KOBJ_CHANGE); flags &= ~smask; } From e6c80061ca239f45c0eaf7e47a91d6d6df9bd636 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 10 Jun 2026 07:46:16 -0700 Subject: [PATCH 159/501] hwmon: (ina2xx) Fix various overflow issues Sashiko reports several integer overflow problems in the ina2xx driver caused by unbounded multiplications and inadequate types for intermediate calculations. Specifically: - In ina2xx_get_value(), the return type is changed from int to long. Intermediate calculations for current are now performed using 64-bit types to prevent 32-bit integer overflow before the division by 1000. - When calculating power in ina2xx_get_value() and sy24655_average_power_read(), interim values are cast to u64 and clamped to LONG_MAX. This prevents overflow when regval or accumulator_24 is multiplied by power_lsb_uW. - In ina226_alert_to_reg(), the clamping logic is rewritten using min_t(). This safely avoids integer overflows when scaling user-provided values for shunt voltage, bus voltage, power, and current limits. Cc: Loic Poulain Fixes: ab7fbee452be ("hwmon: (ina2xx) Fix various overflow issues") Signed-off-by: Guenter Roeck --- drivers/hwmon/ina2xx.c | 61 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index c4742e84b999..449a72c6b40b 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -266,30 +267,34 @@ static u16 ina226_interval_to_reg(long interval) return FIELD_PREP(INA226_AVG_RD_MASK, avg_bits); } -static int ina2xx_get_value(struct ina2xx_data *data, u8 reg, - unsigned int regval) +static long ina2xx_get_value(struct ina2xx_data *data, u8 reg, + unsigned int regval) { - int val; + s64 val64; + long val; switch (reg) { case INA2XX_SHUNT_VOLTAGE: /* signed register */ - val = (s16)regval >> data->config->shunt_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->shunt_div); + val = DIV_ROUND_CLOSEST((s16)regval >> data->config->shunt_voltage_shift, + data->config->shunt_div); break; case INA2XX_BUS_VOLTAGE: - val = (regval >> data->config->bus_voltage_shift) * - data->config->bus_voltage_lsb; - val = DIV_ROUND_CLOSEST(val, 1000); + val = DIV_ROUND_CLOSEST((regval >> data->config->bus_voltage_shift) * + data->config->bus_voltage_lsb, 1000); break; case INA2XX_POWER: - val = regval * data->power_lsb_uW; + val = min_t(u64, (u64)regval * data->power_lsb_uW, LONG_MAX); break; case INA2XX_CURRENT: /* signed register, result in mA */ - val = ((s16)regval >> data->config->current_shift) * + val64 = (s64)((s16)regval >> data->config->current_shift) * data->current_lsb_uA; - val = DIV_ROUND_CLOSEST(val, 1000); + if (val64 < 0) + val64 = -DIV_ROUND_CLOSEST_ULL(-val64, 1000); + else + val64 = DIV_ROUND_CLOSEST_ULL(val64, 1000); + val = clamp_val(val64, LONG_MIN, LONG_MAX); break; case INA2XX_CALIBRATION: val = regval; @@ -378,27 +383,29 @@ static int ina2xx_read_init(struct device *dev, int reg, long *val) */ static u16 ina226_alert_to_reg(struct ina2xx_data *data, int reg, long val) { + long limit; + switch (reg) { case INA2XX_SHUNT_VOLTAGE: - val = clamp_val(val, 0, SHRT_MAX * data->config->shunt_div); - val *= data->config->shunt_div; - val <<= data->config->shunt_voltage_shift; - return clamp_val(val, 0, SHRT_MAX); + val = min_t(long, val, DIV_ROUND_CLOSEST(SHRT_MAX, data->config->shunt_div)); + return min_t(long, (val * data->config->shunt_div) << data->config->shunt_voltage_shift, + SHRT_MAX); case INA2XX_BUS_VOLTAGE: - val = clamp_val(val, 0, 200000); - val = (val * 1000) << data->config->bus_voltage_shift; - val = DIV_ROUND_CLOSEST(val, data->config->bus_voltage_lsb); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, 130000); + return min_t(long, + DIV_ROUND_CLOSEST((val * 1000) << data->config->bus_voltage_shift, + data->config->bus_voltage_lsb), + USHRT_MAX); case INA2XX_POWER: - val = clamp_val(val, 0, UINT_MAX - data->power_lsb_uW); - val = DIV_ROUND_CLOSEST(val, data->power_lsb_uW); - return clamp_val(val, 0, USHRT_MAX); + val = min_t(long, val, LONG_MAX - data->power_lsb_uW); + return min_t(long, DIV_ROUND_CLOSEST(val, data->power_lsb_uW), USHRT_MAX); case INA2XX_CURRENT: - val = clamp_val(val, INT_MIN / 1000, INT_MAX / 1000); + limit = (LONG_MAX - (data->current_lsb_uA / 2)) / 1000; + val = min_t(long, val, limit); /* signed register, result in mA */ val = DIV_ROUND_CLOSEST(val * 1000, data->current_lsb_uA); - val <<= data->config->current_shift; - return clamp_val(val, SHRT_MIN, SHRT_MAX); + limit = SHRT_MAX >> data->config->current_shift; + return (u16)(min_t(long, val, limit) << data->config->current_shift); default: /* programmer goofed */ WARN_ON_ONCE(1); @@ -537,6 +544,7 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va u8 template[6]; int ret; long accumulator_24, sample_count; + u64 val64; /* 48-bit register read */ ret = i2c_smbus_read_i2c_block_data(data->client, reg, 6, template); @@ -555,7 +563,8 @@ static int sy24655_average_power_read(struct ina2xx_data *data, u8 reg, long *va return 0; } - *val = DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + val64 = (u64)DIV_ROUND_CLOSEST(accumulator_24, sample_count) * data->power_lsb_uW; + *val = min_t(u64, val64, LONG_MAX); return 0; } From 00feb1cce93dab948a299b69753d99c681d45a0b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Wed, 5 Feb 2025 12:27:15 -0800 Subject: [PATCH 160/501] hwmon: (ltc4282) Fix reading the minimum alarm voltage Coverity reports an out-of-bounds access when reading the minimum alarm voltage for the VGPIO channel. Add the missing return statement to fix the problem. Fixes: cbc29538dbf7 ("hwmon: Add driver for LTC4282") Cc: Nuno Sa Signed-off-by: Guenter Roeck --- drivers/hwmon/ltc4282.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/ltc4282.c b/drivers/hwmon/ltc4282.c index 39b9d3abca99..cc698803f8bf 100644 --- a/drivers/hwmon/ltc4282.c +++ b/drivers/hwmon/ltc4282.c @@ -374,8 +374,8 @@ static int ltc4282_read_in(struct ltc4282_state *st, u32 attr, long *val, channel, val); case hwmon_in_min_alarm: if (channel == LTC4282_CHAN_VGPIO) - ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, - LTC4282_GPIO_ALARM_L_MASK, val); + return ltc4282_read_alarm(st, LTC4282_ADC_ALERT_LOG, + LTC4282_GPIO_ALARM_L_MASK, val); return ltc4282_vdd_source_read_alm(st, LTC4282_VSOURCE_ALARM_L_MASK, From f46d5ab43a572b84773015a76966f5da56fc1748 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 09:34:46 -0700 Subject: [PATCH 161/501] hwmon: (sht3x) Fix unaligned accesses Sashiko reports: In sht3x_update_client(), the 16-bit temperature and humidity values are extracted from a stack-allocated byte array using be16_to_cpup(). The pointers passed to this function are calculated as buf and buf + 3. Since the difference between the two pointers is an odd number of bytes, at least one of them is guaranteed to be at an unaligned offset. This will trigger an alignment fault on strict-alignment architectures such as ARMv5 or SPARC, resulting in a kernel panic. Fix the problem by using get_unaligned_be16() instead of be16_to_cpup(), and put_unaligned_be16() instead of cpu_to_be16(). Fixes: 7c84f7f80d6f ("hwmon: add support for Sensirion SHT3x sensors") Reported-by: Sashiko Signed-off-by: Guenter Roeck --- drivers/hwmon/sht3x.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/sht3x.c b/drivers/hwmon/sht3x.c index c2f6b73aa7f3..4d90f89a9929 100644 --- a/drivers/hwmon/sht3x.c +++ b/drivers/hwmon/sht3x.c @@ -21,6 +21,7 @@ #include #include #include +#include /* commands (high repeatability mode) */ static const unsigned char sht3x_cmd_measure_single_hpm[] = { 0x24, 0x00 }; @@ -276,9 +277,9 @@ static struct sht3x_data *sht3x_update_client(struct device *dev) if (ret) goto out; - val = be16_to_cpup((__be16 *)buf); + val = get_unaligned_be16(buf); data->temperature = sht3x_extract_temperature(val); - val = be16_to_cpup((__be16 *)(buf + 3)); + val = get_unaligned_be16(buf + 3); data->humidity = sht3x_extract_humidity(val); data->last_update = jiffies; } @@ -336,7 +337,7 @@ static int limits_update(struct sht3x_data *data) if (ret) return ret; - raw = be16_to_cpup((__be16 *)buffer); + raw = get_unaligned_be16(buffer); temperature = sht3x_extract_temperature((raw & 0x01ff) << 7); humidity = sht3x_extract_humidity(raw & 0xfe00); data->temperature_limits[index] = temperature; @@ -389,7 +390,7 @@ static size_t limit_write(struct device *dev, raw = ((u32)(temperature + 45000) * 24543) >> (16 + 7); raw |= ((humidity * 42950) >> 16) & 0xfe00; - *((__be16 *)position) = cpu_to_be16(raw); + put_unaligned_be16(raw, position); position += SHT3X_WORD_LEN; *position = crc8(sht3x_crc8_table, position - SHT3X_WORD_LEN, From aa9429edf9fc0e90d6f4da19ea4b5495a54ab117 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Sat, 25 Jul 2026 15:27:28 -0700 Subject: [PATCH 162/501] hwmon: (lm90) Only report alarms if driver is ready Userspace can read sysfs attributes before driver registration is complete, immediately after devm_hwmon_device_register_with_info() has been called. At that time, data->hwmon_dev is not yet initialized. This can trigger a NULL pointer access since lm90_update_device() and with it lm90_update_alarms_locked() will be called. This call schedules report_work and lm90_report_alarms(), which passes the still-NULL data->hwmon_dev to hwmon_notify_event() and triggers a NULL pointer dereference. Fix the problem by only scheduling the report and alert workers data->hwmon_dev is set. Reported-by: Sashiko Fixes: f6d0775119fb9 ("hwmon: (lm90) Rework alarm/status handling") Signed-off-by: Guenter Roeck --- drivers/hwmon/lm90.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 4b9c0ccdf260..c6186505661f 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1194,7 +1194,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) check_enable = (client->irq || !(data->config_orig & 0x80)) && (data->config & 0x80); - if (force || check_enable) + if (data->hwmon_dev && (force || check_enable)) schedule_work(&data->report_work); /* @@ -1202,7 +1202,7 @@ static int lm90_update_alarms_locked(struct lm90_data *data, bool force) * alarms are all clear, and alerts are currently disabled. * Otherwise (re)schedule worker if needed. */ - if (check_enable) { + if (check_enable && data->hwmon_dev) { if (!(data->current_alarms & data->alert_alarms)) { dev_dbg(&client->dev, "Re-enabling ALERT#\n"); lm90_update_confreg(data, data->config & ~0x80); From 080bbf42faf77e6489ab30d5114c5f8f6ccbb1b8 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 09:54:23 -0700 Subject: [PATCH 163/501] hwmon: (nzxt-smart2) DMA-align output buffer Sashiko reports: When send_output_report() calls hid_hw_output_report(), the underlying USB HID core calls usb_interrupt_msg() which maps this buffer directly for DMA. When the DMA mapping flushes or invalidates the cacheline, it will corrupt the adjacent variables (mutex, update_interval) that were modified concurrently by the CPU. This causes memory corruption due to cacheline sharing on non-coherent CPU architectures (such as ARM or MIPS). The DMA API debugging tool (CONFIG_DMA_API_DEBUG) will trigger runtime warnings for this violation. Any operation that triggers send_output_report() (like setting a fan speed or updating the interval) causes the USB DMA mapping. On systems with non-coherent caches, this structural bug causes immediate and deterministic memory corruption. Align the output buffer to ARCH_DMA_MINALIGN to fix the problem. Reported-by: Sashiko Fixes: 53e68c20aeb1 ("hwmon: add driver for NZXT RGB&Fan Controller/Smart Device v2.") Cc: Aleksandr Mezin Signed-off-by: Guenter Roeck --- drivers/hwmon/nzxt-smart2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/nzxt-smart2.c b/drivers/hwmon/nzxt-smart2.c index e2316c46629d..ff0c0bee0e83 100644 --- a/drivers/hwmon/nzxt-smart2.c +++ b/drivers/hwmon/nzxt-smart2.c @@ -203,7 +203,7 @@ struct drvdata { */ struct mutex mutex; long update_interval; - u8 output_buffer[OUTPUT_REPORT_SIZE]; + u8 output_buffer[OUTPUT_REPORT_SIZE] __aligned(ARCH_DMA_MINALIGN); }; static long scale_pwm_value(long val, long orig_max, long new_max) From cdac670237258c8ca063aa8a16998f680d81b80d Mon Sep 17 00:00:00 2001 From: Linmao Li Date: Thu, 23 Jul 2026 10:11:40 +0800 Subject: [PATCH 164/501] i2c: spacemit: request IRQ after controller initialization spacemit_i2c_probe() requests the IRQ before it enables the clocks, resets the controller and runs init_completion(). If an interrupt is already pending, the handler runs too early: it reads registers while the clocks are still off and calls complete() on an uninitialized completion. Request the IRQ after the controller and completion are initialized, but still before the adapter is registered. Fixes: 5ea558473fa3 ("i2c: spacemit: add support for SpacemiT K1 SoC") Signed-off-by: Linmao Li Cc: # v6.15+ Reviewed-by: Troy Mitchell Reviewed-by: Alex Elder Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260723021140.2293844-1-lilinmao@kylinos.cn --- drivers/i2c/busses/i2c-k1.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/busses/i2c-k1.c b/drivers/i2c/busses/i2c-k1.c index 51a0c3d80fc9..3fe716cc153d 100644 --- a/drivers/i2c/busses/i2c-k1.c +++ b/drivers/i2c/busses/i2c-k1.c @@ -723,11 +723,6 @@ static int spacemit_i2c_probe(struct platform_device *pdev) if (i2c->irq < 0) return dev_err_probe(dev, i2c->irq, "failed to get irq resource"); - ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, - IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); - if (ret) - return dev_err_probe(dev, ret, "failed to request irq"); - clk = devm_clk_get_enabled(dev, "func"); if (IS_ERR(clk)) return dev_err_probe(dev, PTR_ERR(clk), "failed to enable func clock"); @@ -755,6 +750,11 @@ static int spacemit_i2c_probe(struct platform_device *pdev) init_completion(&i2c->complete); + ret = devm_request_irq(i2c->dev, i2c->irq, spacemit_i2c_irq_handler, + IRQF_NO_SUSPEND, dev_name(i2c->dev), i2c); + if (ret) + return dev_err_probe(dev, ret, "failed to request irq"); + platform_set_drvdata(pdev, i2c); ret = i2c_add_numbered_adapter(&i2c->adapt); From 82048795242f04275a3f49ffc66ad851b6120954 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Tue, 21 Jul 2026 23:41:47 +0900 Subject: [PATCH 165/501] i2c: amd-mp2: Unregister callback on adapter add failure amd_mp2_register_cb() stores the platform I2C context in the MP2 PCI driver's callback table before the adapter is registered. If i2c_add_adapter() fails, probe returns and devres frees the context, but the PCI driver can still dereference the stale pointer from its IRQ and system-sleep callbacks. Unregister the callback before returning the adapter registration error. Fixes: 529766e0a011 ("i2c: Add drivers for the AMD PCIe MP2 I2C controller") Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Cc: # v5.2+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260721144147.31150-1-mhun512@gmail.com --- drivers/i2c/busses/i2c-amd-mp2-plat.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-amd-mp2-plat.c b/drivers/i2c/busses/i2c-amd-mp2-plat.c index 188e24cc4d35..9fdd6a5fb8b6 100644 --- a/drivers/i2c/busses/i2c-amd-mp2-plat.c +++ b/drivers/i2c/busses/i2c-amd-mp2-plat.c @@ -316,8 +316,10 @@ static int i2c_amd_probe(struct platform_device *pdev) amd_mp2_pm_runtime_put(mp2_dev); - if (ret < 0) + if (ret < 0) { dev_err(&pdev->dev, "i2c add adapter failed = %d\n", ret); + amd_mp2_unregister_cb(&i2c_dev->common); + } return ret; } From d99607c888f26e8a4e9fe9772860cef4aff86bb4 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Sun, 19 Jul 2026 22:19:43 +0200 Subject: [PATCH 166/501] i2c: jz4780: Cache host clock rate at probe to prevent CCF prepare_lock deadlock Fix a severe AB/BA deadlock between the Common Clock Framework (CCF) and the I2C adapter lock, which triggers when an I2C-controlled clock generator client (like the Si5351) is registered or modified under the CCF. During an i2c client clock (generator) frequency change, the CCF acquires its global 'prepare_lock' mutex and the driver calls i2c_transfer() to update the client's chip registers, stalling for the adapter's I2C bus lock. Concurrently, an independent, parallel transfer on the same bus (e.g., a GPIO expander handling LEDs) can hold the I2C adapter lock. Inside this parallel transfer path, jz4780_i2c_set_speed() calls clk_get_rate() on the host controller's input clock to calculate bus timings. This call attempts to acquire the blocked CCF 'prepare_lock', creating a circular dependency that freezes the system. The jz4780 host controller clock itself is static and never changes at runtime. However, calling clk_get_rate() inside the active transfer path introduces an unnecessary dependency on the CCF internal locks. Eliminate this synchronous clk_get_rate() call from the active transfer path by caching the static host peripheral clock rate once - inside the private jz4780_i2c structure during jz4780_i2c_probe(). Update jz4780_i2c_set_speed() to use this cached value, safely decoupling active I2C transactions from the CCF internal locks without any risk of stale timings. Assisted-by web based Google AI (pinpointing the bug and writing the message). Fixes: ba92222ed63a12 ("i2c: jz4780: Add i2c bus controller driver for Ingenic JZ4780") Signed-off-by: H. Nikolaus Schaller Cc: # v4.1+ Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/2db6fd233aceb7238474e4833f4d25ca681c3ffb.1784492382.git.hns@goldelico.com --- drivers/i2c/busses/i2c-jz4780.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index 664a5471d933..695be3b21460 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -141,6 +141,7 @@ struct jz4780_i2c { void __iomem *iomem; int irq; struct clk *clk; + unsigned long clk_rate_khz; struct i2c_adapter adap; const struct ingenic_i2c_config *cdata; @@ -246,7 +247,7 @@ static int jz4780_i2c_set_target(struct jz4780_i2c *i2c, unsigned char address) static int jz4780_i2c_set_speed(struct jz4780_i2c *i2c) { - int dev_clk_khz = clk_get_rate(i2c->clk) / 1000; + int dev_clk_khz = i2c->clk_rate_khz; int cnt_high = 0; /* HIGH period count of the SCL clock */ int cnt_low = 0; /* LOW period count of the SCL clock */ int cnt_period = 0; /* period count of the SCL clock */ @@ -796,6 +797,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev) if (IS_ERR(i2c->clk)) return PTR_ERR(i2c->clk); + i2c->clk_rate_khz = clk_get_rate(i2c->clk) / 1000; + ret = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &clk_freq); if (ret) { From a60f58eb70e4e4c2ba4ace8b292dea64e5b7b290 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 13:13:54 -0700 Subject: [PATCH 167/501] hwmon: (lm63) Mask PWM frequency multiplier to supported bits Sashiko is concerned that reading a PWM frequency multiplier outside the supported range of [1, 31] might result in bad PWM values written to the chip. Technically, the chip should never return a value with the upper 3 bits set, so this should never happen. However, it is unknown if there are LM63 variants where the upper bits of the register can be written. Mask the register value read from the chip to only accept the lower 5 bit when reading it from the chip to avoid the problem. Reported-by: Sashiko Signed-off-by: Guenter Roeck --- drivers/hwmon/lm63.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index e2a429e579ac..da09770c05e7 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -247,8 +247,7 @@ static struct lm63_data *lm63_update_device(struct device *dev) LM63_REG_TACH_LIMIT_MSB) << 8); } - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); + data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f; if (data->pwm1_freq == 0) data->pwm1_freq = 1; data->pwm1[0] = i2c_smbus_read_byte_data(client, @@ -1187,7 +1186,7 @@ static void lm63_init_client(struct lm63_data *data) data->config |= 0x04; /* We may need pwm1_freq before ever updating the client data */ - data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); + data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ) & 0x1f; if (data->pwm1_freq == 0) data->pwm1_freq = 1; From d0b704e569ac3b8416d8e02270cdc9bf830ed395 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 27 Jul 2026 13:35:37 -0700 Subject: [PATCH 168/501] hwmon: (nct6775-core) Prevent access to unsupported weight registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sashiko reports: During initialization of the nct6116 chip, the driver sets data->pwm_num to 5. However, it assigns several NCT6106 register arrays (such as NCT6106_REG_WEIGHT_DUTY_STEP, NCT6106_REG_WEIGHT_TEMP_SEL, and NCT6106_REG_WEIGHT_TEMP_*) to data->REG_PWM and data->REG_WEIGHT_TEMP. These arrays only contain 3 elements. In nct6775_update_pwm(), the driver iterates up to data->pwm_num. If data->has_pwm has bits 3 or 4 set (which is structurally possible for nct6116), the loop attempts to read elements at index 3 and 4 from these 3-element arrays. This results in a global out-of-bounds read, which can be caught by KASAN. Furthermore, the driver uses these garbage out-of-bounds values as hardware register addresses for subsequent read and write operations. This leads to invalid hardware register access, potentially causing hardware misconfiguration or system crashes. The underlying problem is that the chip does support up to five fan control channels, but only the first three support weight control. Fix the problem by extending the affected weight register arrays with zeroed fields. The driver uses zeroed register addresses to determine if a register is supported or not, and skips accesses for unsupported registers. Reported-by: Sashiko Fixes: 29c7cb485b32 ("hwmon: (nct6775) Integrate new model nct6116") Cc: Björn Gerhart Cc: Florian Bezdeka Signed-off-by: Guenter Roeck --- drivers/hwmon/nct6775-core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/hwmon/nct6775-core.c b/drivers/hwmon/nct6775-core.c index 51253acff4b0..94482c8092cd 100644 --- a/drivers/hwmon/nct6775-core.c +++ b/drivers/hwmon/nct6775-core.c @@ -791,12 +791,12 @@ static const u16 NCT6106_REG_TOLERANCE_H[] = { 0x112, 0x122, 0x132 }; static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; -static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; -static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; -static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; +static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c, 0, 0 }; +static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d, 0, 0 }; static const u16 NCT6106_REG_AUTO_TEMP[] = { 0x160, 0x170, 0x180 }; static const u16 NCT6106_REG_AUTO_PWM[] = { 0x164, 0x174, 0x184 }; From 9388e7c820e33d3a7dfde9a9c16ad4ac60f29d37 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Sat, 4 Jul 2026 13:27:14 +0800 Subject: [PATCH 169/501] smb: client: free partially allocated transform folio queue netfs_alloc_folioq_buffer() may leave a partially allocated folio queue attached to the caller's buffer pointer when it returns an error. smb3_init_transform_rq() stores the buffer in the request only after allocation succeeds, so the common error path cannot free a partial allocation. Store the buffer pointer before checking the return value so err_free releases it. Signed-off-by: Yichong Chen Reviewed-by: ChenXiaoSong Signed-off-by: Steve French --- fs/smb/client/smb2ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index cbd51a08e97e..192649fec25d 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -4745,10 +4745,10 @@ smb3_init_transform_rq(struct TCP_Server_Info *server, int num_rqst, size_t cur_size = 0; rc = netfs_alloc_folioq_buffer(NULL, &buffer, &cur_size, size, GFP_NOFS); + new->rq_buffer = buffer; if (rc < 0) goto err_free; - new->rq_buffer = buffer; iov_iter_folio_queue(&new->rq_iter, ITER_SOURCE, buffer, 0, 0, size); From f47064c970d3e920a27435454c02df310695f6e1 Mon Sep 17 00:00:00 2001 From: Surendra Singh Chouhan Date: Mon, 27 Jul 2026 11:08:04 +0530 Subject: [PATCH 170/501] ASoC: sophgo: return 1 on volume change in cv1800b_adc_volume_set() cv1800b_adc_volume_set() serves as the .put callback for the "Internal I2S Capture Volume" control. ALSA mixer control callbacks must return 1 when the register value is modified, 0 if unchanged, or a negative error code on failure. Returning 0 unconditionally causes ALSA core to assume the value was unchanged, suppressing SNDRV_CTL_EVENT_MASK_VALUE change notifications to userspace sound servers (e.g. PipeWire/PulseAudio). Fix this by comparing the new register value with the existing register value. If unchanged, return 0; otherwise, write the updated value and return 1. Fixes: 4cf8752a03e6 ("ASoC: sophgo: add CV1800B internal ADC codec driver") Signed-off-by: Surendra Singh Chouhan Link: https://patch.msgid.link/20260727053804.25599-1-kr494167@gmail.com Signed-off-by: Mark Brown --- sound/soc/sophgo/cv1800b-sound-adc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/sound/soc/sophgo/cv1800b-sound-adc.c b/sound/soc/sophgo/cv1800b-sound-adc.c index b66761156b99..bd93e261bdd1 100644 --- a/sound/soc/sophgo/cv1800b-sound-adc.c +++ b/sound/soc/sophgo/cv1800b-sound-adc.c @@ -251,16 +251,22 @@ static int cv1800b_adc_volume_set(struct snd_kcontrol *kcontrol, u32 v_left = clamp_t(u32, ucontrol->value.integer.value[0], 0, 24); u32 v_right = clamp_t(u32, ucontrol->value.integer.value[1], 0, 24); - u32 val; + u32 val, old_val; val = readl(priv->regs + CV1800B_RXADC_ANA0); + old_val = val; + val = u32_replace_bits(val, cv1800b_gains[v_left], REG_COMB_LEFT_VOLUME); val = u32_replace_bits(val, cv1800b_gains[v_right], REG_COMB_RIGHT_VOLUME); + + if (val == old_val) + return 0; + writel(val, priv->regs + CV1800B_RXADC_ANA0); - return 0; + return 1; } static DECLARE_TLV_DB_SCALE(cv1800b_volume_tlv, 0, 200, 0); From b476724a6d9b2e7db43cb7a59defd396f94cf77c Mon Sep 17 00:00:00 2001 From: Dmitry Antipov Date: Mon, 27 Jul 2026 20:20:35 +0300 Subject: [PATCH 171/501] smb: client: simplify cifs_fscache_get_super_cookie() Avoid redundant 'strlen()' and use the convenient 'strreplace()' to simplify 'cifs_fscache_get_super_cookie()'. Signed-off-by: Dmitry Antipov Signed-off-by: Steve French --- fs/smb/client/fscache.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c index 01424a5cdb99..9f66bbcd3801 100644 --- a/fs/smb/client/fscache.c +++ b/fs/smb/client/fscache.c @@ -38,7 +38,6 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) struct TCP_Server_Info *server = tcon->ses->server; struct fscache_volume *vcookie; const struct sockaddr *sa = (struct sockaddr *)&server->dstaddr; - size_t slen, i; char *sharename; char *key; int ret = -ENOMEM; @@ -73,10 +72,7 @@ int cifs_fscache_get_super_cookie(struct cifs_tcon *tcon) return PTR_ERR(sharename); } - slen = strlen(sharename); - for (i = 0; i < slen; i++) - if (sharename[i] == '/') - sharename[i] = ';'; + strreplace(sharename, '/', ';'); key = kasprintf(GFP_KERNEL, "cifs,%pISpc,%s", sa, sharename); if (!key) From 9ad4c2331d2330ab280180a337c7d30656496c6d Mon Sep 17 00:00:00 2001 From: Jackie Dong Date: Mon, 27 Jul 2026 12:14:51 +0800 Subject: [PATCH 172/501] ASoC: amd: acp: Add DMI quirk for Lenovo Legion 7 15ASH11 Lenovo Legion 7 15ASH11 with AMD RYZEN AI MAX+ 392 (Strix Halo, ACP 7.0) uses Realtek ALC287 series codec and no any DMIC connected by ACP. All DMICs directly connet with ALC codec. Without this quirk, Input Device of Gnome Sound settings shows Internal Stereo Microphone and Digital Microphone by default. In fact, Digital Microphone of ACP doesn't work due to no connecting with ALC287 codec, the Internal Stereo Microphone as analog device based on snd_hda_intel driver can work well. Add a DMI quirk to override the flag to 0, consistent with the existing entry for the Lenovo Yoga Pro 7 15ASH11. Signed-off-by: Jackie Dong Link: https://patch.msgid.link/20260727041452.16701-1-xy-jackie@139.com Signed-off-by: Mark Brown --- sound/soc/amd/acp-config.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/soc/amd/acp-config.c b/sound/soc/amd/acp-config.c index 051946562725..88e4230d66c3 100644 --- a/sound/soc/amd/acp-config.c +++ b/sound/soc/amd/acp-config.c @@ -44,6 +44,13 @@ static const struct dmi_system_id acp70_acpi_flag_override_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "83W5"), }, }, + { + /* Lenovo Legion 7 15ASH11 (Strix Halo, ACP 7.0) */ + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83V9"), + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC"), From 0e3ea5445c228048f937ad5a944c27859a78f971 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Fri, 24 Jul 2026 11:30:35 -0500 Subject: [PATCH 173/501] cifs: consolidate time_last_write stamp into _cifsFileInfo_put() The time_last_write stamp was scattered across cifs_close(), smb2_deferred_work_close(), and the three drain functions in misc.c. This missed the case where background I/O holds the final reference after userspace close() returns, and required explicit maintenance at each close-path site. Move the smp_store_release() into _cifsFileInfo_put(), immediately before releasing open_file_lock. This single location covers all close paths unconditionally: normal close, background I/O dropping the final reference, deferred close via timer or external drain. The spinlock's store-release/load-acquire pairing with is_inode_writable() already provides the ordering guarantee documented in is_size_safe_to_change(). Remove the now-redundant stamps from cifs_close(), smb2_deferred_work_close(), and all six stamp sites in the misc.c deferred-close drain functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/file.c | 37 ++++++++++++++------------------ fs/smb/client/misc.c | 51 ++++++-------------------------------------- 2 files changed, 22 insertions(+), 66 deletions(-) diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index b279a44be729..ac89c1ba56b1 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -915,6 +915,14 @@ void _cifsFileInfo_put(struct cifsFileInfo *cifs_file, cifs_set_oplock_level(cifsi, 0); } + if (OPEN_FMODE(cifs_file->f_flags) & FMODE_WRITE) { + /* Stamp while open_file_lock is held; covers all close paths + * including background I/O. Pairs with smp_load_acquire() in + * is_size_safe_to_change(). + */ + smp_store_release(&cifsi->time_last_write, jiffies); + } + spin_unlock(&cifsi->open_file_lock); spin_unlock(&tcon->open_file_lock); @@ -1429,15 +1437,6 @@ void smb2_deferred_work_close(struct work_struct *work) cifs_del_deferred_close(cfile); cfile->deferred_close_scheduled = false; spin_unlock(&cinode->deferred_lock); - /* - * Refresh time_last_write immediately before the actual server close - * so the protection window is anchored to the real close time, not - * the earlier userspace close time stored by cifs_close(). - */ - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cinode->time_last_write, jiffies); - } _cifsFileInfo_put(cfile, true, false); } @@ -1467,10 +1466,6 @@ int cifs_close(struct inode *inode, struct file *file) if (file->private_data != NULL) { cfile = file->private_data; file->private_data = NULL; - if (file->f_mode & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cinode->time_last_write, jiffies); - } dclose = kmalloc_obj(struct cifs_deferred_close); if ((cfile->status_file_deleted == false) && (smb2_can_defer_close(inode, dclose))) { @@ -3276,13 +3271,13 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, * No writable handles open. Check whether we are within the attribute * cache validity window of a recent local modification. * - * For the close() path: cifs_close() calls smp_store_release() on - * time_last_write before _cifsFileInfo_put() removes the handle under - * open_file_lock. That spin_unlock() is a store-release that pairs - * with the spin_lock() (load-acquire) in is_inode_writable() above, - * so if is_inode_writable() returned false the smp_load_acquire() - * below is guaranteed to observe any time_last_write update from a - * concurrent close(). + * For the close() path: _cifsFileInfo_put() stamps time_last_write + * (via smp_store_release()) before releasing open_file_lock. That + * spin_unlock() is a store-release that pairs with the spin_lock() + * (load-acquire) in is_inode_writable() above, so if + * is_inode_writable() returned false the smp_load_acquire() below is + * guaranteed to observe any time_last_write update from a concurrent + * close(), covering all close paths including background I/O. * * For the setattr/truncate paths: those callers use smp_store_release() * directly; the smp_load_acquire() below pairs with that store. There @@ -3297,7 +3292,7 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file, * jiffies is still close to INITIAL_JIFFIES on 32-bit systems. */ if (from_readdir) { - /* Pairs with smp_store_release() at close and truncate sites. */ + /* Pairs with smp_store_release() in _cifsFileInfo_put() and setattr. */ tlw = smp_load_acquire(&cifsInode->time_last_write); if (tlw && time_before(jiffies, tlw + cifs_sb->ctx->acregmax)) return false; diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c index 6edebc0807ea..46e1382e8e04 100644 --- a/fs/smb/client/misc.c +++ b/fs/smb/client/misc.c @@ -525,24 +525,11 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode) } spin_unlock(&cifs_inode->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, false, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, false, false); + _cifsFileInfo_put(tmp_list->cfile, false, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -576,24 +563,11 @@ cifs_close_all_deferred_files(struct cifs_tcon *tcon) } spin_unlock(&tcon->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, true, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, true, false); + _cifsFileInfo_put(tmp_list->cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } @@ -663,24 +637,11 @@ void cifs_close_deferred_file_under_dentry(struct cifs_tcon *tcon, } spin_unlock(&tcon->open_file_lock); - if (failed_cfile) { - if (OPEN_FMODE(failed_cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(failed_cfile->dentry))->time_last_write, - jiffies); - } + if (failed_cfile) _cifsFileInfo_put(failed_cfile, true, false); - } list_for_each_entry_safe(tmp_list, tmp_next_list, &file_head, list) { - struct cifsFileInfo *cfile = tmp_list->cfile; - - if (OPEN_FMODE(cfile->f_flags) & FMODE_WRITE) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&CIFS_I(d_inode(cfile->dentry))->time_last_write, - jiffies); - } - _cifsFileInfo_put(cfile, true, false); + _cifsFileInfo_put(tmp_list->cfile, true, false); list_del(&tmp_list->list); kfree(tmp_list); } From ecababf08905958ba8c125979c4e39fc2f1a8a05 Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Fri, 24 Jul 2026 11:30:36 -0500 Subject: [PATCH 174/501] cifs: fix time_last_write stamp placement in setattr/truncate paths cifs_file_set_size() calls cifs_setsize() on success, which calls i_size_write(), updating i_size to the new value. The subsequent check attrs->ia_size != i_size_read() in both cifs_setattr_unix() and cifs_setattr_nounix() therefore always evaluates false after a successful cifs_file_set_size(), making the smp_store_release() of time_last_write dead code. The truncate path was unprotected against stale readdir size updates. Move the stamp to before the cifs_file_set_size() RPC call, guarded by attrs->ia_size != i_size_read() to exclude no-op same-size ftruncate(2) calls from stamping time_last_write unnecessarily. On the error path the stamp remains rather than being restored: restoring a stale snapshot (prev_tlw) could silently erase a concurrent _cifsFileInfo_put() close stamp if that close arrived between the READ_ONCE and the smp_store_release. readdir is suppressed until the stamp expires, which extends beyond one acregmax if the caller retries failed truncations. stat() is unaffected: the cifs_revalidate_dentry_attr() path calls cifs_fattr_to_inode() with from_readdir=false, which bypasses the time_last_write check in is_size_safe_to_change() entirely and always writes the authoritative QUERY_INFO result to i_size. Remove the now-unreachable stamp from the dead block in both functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara (Red Hat) Signed-off-by: Steve French --- fs/smb/client/inode.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index b2806371bfde..808085eb0cdc 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3190,6 +3190,17 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) rc = 0; if (attrs->ia_valid & ATTR_SIZE) { + if (attrs->ia_size != i_size_read(inode)) { + /* Stamp before RPC. On failure the stamp remains: restoring a + * stale snapshot could silently erase a concurrent + * _cifsFileInfo_put() close stamp. readdir is suppressed + * until the stamp expires; stat() bypasses this via the + * from_readdir=false path in is_size_safe_to_change() and + * always returns an authoritative QUERY_INFO result. + * Pairs with smp_load_acquire() in is_size_safe_to_change(). + */ + smp_store_release(&cifsInode->time_last_write, jiffies); + } rc = cifs_file_set_size(xid, direntry, full_path, open_file, attrs->ia_size); if (rc != 0) @@ -3279,8 +3290,6 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); @@ -3370,6 +3379,17 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) } if (attrs->ia_valid & ATTR_SIZE) { + if (attrs->ia_size != i_size_read(inode)) { + /* Stamp before RPC. On failure the stamp remains: restoring a + * stale snapshot could silently erase a concurrent + * _cifsFileInfo_put() close stamp. readdir is suppressed + * until the stamp expires; stat() bypasses this via the + * from_readdir=false path in is_size_safe_to_change() and + * always returns an authoritative QUERY_INFO result. + * Pairs with smp_load_acquire() in is_size_safe_to_change(). + */ + smp_store_release(&cifsInode->time_last_write, jiffies); + } rc = cifs_file_set_size(xid, direntry, full_path, cfile, attrs->ia_size); if (rc != 0) @@ -3482,8 +3502,6 @@ cifs_setattr_nounix(struct dentry *direntry, struct iattr *attrs) if ((attrs->ia_valid & ATTR_SIZE) && attrs->ia_size != i_size_read(inode)) { - /* Pairs with smp_load_acquire() in is_size_safe_to_change(). */ - smp_store_release(&cifsInode->time_last_write, jiffies); truncate_setsize(inode, attrs->ia_size); netfs_resize_file(&cifsInode->netfs, attrs->ia_size, true); fscache_resize_cookie(cifs_inode_cookie(inode), attrs->ia_size); From 625a2c02a1c04571232a746fe188b4d9a8d63edd Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:17 -0300 Subject: [PATCH 175/501] hwmon: (adt7470) Fix fans stuck in manual mode on I2C errors During adt7470_read_temperatures(), the driver temporarily switches the PWM channels to manual mode, performs the temperature collection, and then restores the original configuration registers. However, if an I2C transaction fails at any point after entering manual mode, the function aborts and returns immediately. This leaves the configuration registers un-restored, permanently trapping the fans in manual mode. Introduce a recovery path to ensure that the original PWM configuration registers are always restored, even when intermediate I2C operations fail. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-1-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 42 +++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 664349756dc2..481d51617f4b 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -205,11 +205,12 @@ static inline int adt7470_write_word_data(struct adt7470_data *data, unsigned in /* Probe for temperature sensors. Assumes lock is held */ static int adt7470_read_temperatures(struct adt7470_data *data) { - unsigned long res; - unsigned int pwm_cfg[2]; - int err; - int i; + struct device *dev = regmap_get_device(data->regmap); u8 pwm[ADT7470_FAN_COUNT]; + unsigned int pwm_cfg[2]; + unsigned long res; + int err, err2; + int i; /* save pwm[1-4] config register */ err = regmap_read(data->regmap, ADT7470_REG_PWM_CFG(0), &pwm_cfg[0]); @@ -233,19 +234,19 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(2), ADT7470_PWM_AUTO_MASK, 0); if (err < 0) - return err; + goto out_restore; /* write pwm control to whatever it was */ err = regmap_bulk_write(data->regmap, ADT7470_REG_PWM(0), &pwm[0], ADT7470_PWM_COUNT); if (err < 0) - return err; + goto out_restore; /* start reading temperature sensors */ err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, ADT7470_T05_STB_MASK); if (err < 0) - return err; + goto out_restore; /* Delay is 200ms * number of temp sensors. */ res = msleep_interruptible((data->num_temp_sensors >= 0 ? @@ -256,13 +257,30 @@ static int adt7470_read_temperatures(struct adt7470_data *data) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG, ADT7470_T05_STB_MASK, 0); if (err < 0) - return err; + goto out_restore; +out_restore: /* restore pwm[1-4] config registers */ - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); - if (err < 0) - return err; - err = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(0), pwm_cfg[0]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{1,2} config (%d)\n", + err2); + + if (!err) + err = err2; + } + + err2 = regmap_write(data->regmap, ADT7470_REG_PWM_CFG(2), pwm_cfg[1]); + if (err2 < 0) { + dev_warn_ratelimited(dev, + "failed to restore PWM{3,4} config (%d)\n", + err2); + + if (!err) + err = err2; + } + if (err < 0) return err; From 05270bd38d9bf88a2f4c212246a8fa29f4032078 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:18 -0300 Subject: [PATCH 176/501] hwmon: (adt7470) Fix cache updated before hardware write on I2C error adt7470_temp_write() and adt7470_pwm_write() update the driver's cached values (temp_min, temp_max, pwm_input, pwm_enable) before issuing the corresponding regmap_write(), and never check whether the write succeeded before committing that update. If the I2C transaction fails, the function correctly propagates the error to the caller, but the cache silently keeps the new value, which was never actually applied to the hardware. Subsequent reads then report a value that does not match the device state. Reorder both write paths to update the cache only after a successful regmap_write(), so the cache always reflects what was actually written to the hardware. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-2-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 481d51617f4b..62ec68ea0a40 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -589,14 +589,16 @@ static int adt7470_temp_write(struct device *dev, u32 attr, int channel, long va switch (attr) { case hwmon_temp_min: mutex_lock(&data->lock); - data->temp_min[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MIN_REG(channel), val); + if (!err) + data->temp_min[channel] = val; mutex_unlock(&data->lock); break; case hwmon_temp_max: mutex_lock(&data->lock); - data->temp_max[channel] = val; err = regmap_write(data->regmap, ADT7470_TEMP_MAX_REG(channel), val); + if (!err) + data->temp_max[channel] = val; mutex_unlock(&data->lock); break; default: @@ -831,9 +833,10 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val case hwmon_pwm_input: val = clamp_val(val, 0, 255); mutex_lock(&data->lock); - data->pwm[channel] = val; err = regmap_write(data->regmap, ADT7470_REG_PWM(channel), - data->pwm[channel]); + val); + if (!err) + data->pwm[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_enable: @@ -847,10 +850,11 @@ static int adt7470_pwm_write(struct device *dev, u32 attr, int channel, long val val--; mutex_lock(&data->lock); - data->pwm_automatic[channel] = val; err = regmap_update_bits(data->regmap, ADT7470_REG_PWM_CFG(channel), pwm_auto_reg_mask, val ? pwm_auto_reg_mask : 0); + if (!err) + data->pwm_automatic[channel] = val; mutex_unlock(&data->lock); break; case hwmon_pwm_freq: From cb0b7f9c43b0abbd422a7e4c2c85e91db429207c Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:19 -0300 Subject: [PATCH 177/501] hwmon: (adt7470) Fix busy-loop and I2C flooding in update thread When userspace configures 'auto_update_interval' to 0 via sysfs, the background kthread executes schedule_timeout_interruptible(0), which returns immediately. If 'num_temp_sensors' is concurrently or previously set to 0, the msleep_interruptible() delay inside adt7470_read_temperatures() also becomes 0. This combination forces the background thread into a tight, unbounded busy-loop, hogging the CPU and flooding the I2C bus with a continuous stream of transactions. Fix this vulnerability by raising the lower limit of the clamp_val in auto_update_interval_store() from 0 to 500 milliseconds. This guarantees a reasonable minimum sleep window between sensor updates, protecting the system from intentional or accidental I2C bus denial of service. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260716213252.EACA71F000E9@smtp.kernel.org Fixes: 89fac11cb3e7 ("adt7470: make automatic fan control really work") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-3-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 62ec68ea0a40..0b19b0925d1c 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -509,7 +509,7 @@ static ssize_t auto_update_interval_store(struct device *dev, if (kstrtol(buf, 10, &temp)) return -EINVAL; - temp = clamp_val(temp, 0, 60000); + temp = clamp_val(temp, 500, 60000); mutex_lock(&data->lock); data->auto_update_interval = temp; From 1a18c79c4bc44cc5349c60e16b0b744dc6ec5f77 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:21 -0300 Subject: [PATCH 178/501] hwmon: (adt7470) Fix temperature alarm logic in hwmon_temp_read() During the conversion the alarm callback started interpreting the channel index as an alarm bitmask, resulting in incorrect alarm reporting. Compute the proper alarm bit instead. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260717211224.B9E291F000E9@smtp.kernel.org Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-5-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 0b19b0925d1c..428bd1d91e70 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -110,6 +110,21 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ALARM2(x) ((x) << 8) +/* TEMP1..TEMP7 (ch 0..6) are, respectively BIT(0)..BIT(6) of reg 0x41 and + * 0x72, or BIT(0)..BIT(6) of data->alarm. + * TEMP8..TEMP9 (ch 7..9) are, respectively BIT(0)..BIT(2) of reg 0x42 and + * 0x73, or BIT(8)..BIT(10) of data->alarm. + */ +#define TEMP_ALARM_BIT(ch) ({ \ + typeof(ch) _ch = (ch); \ + (1 << (_ch < 7 ? _ch : _ch + 1)); \ +}) + +/* FAN1..FAN4 (ch 0..3) are respectively BIT(4)..BIT(7) in + * reg 0x42 and 0x73 or BIT(12)..BIT(15) in data->alarm. + */ +#define FAN_ALARM_BIT(ch) (1 << (12 + (ch))) + #define ADT7470_VENDOR 0x41 #define ADT7470_DEVICE 0x70 /* datasheet only mentions a revision 2 */ @@ -569,7 +584,7 @@ static int adt7470_temp_read(struct device *dev, u32 attr, int channel, long *va *val = 1000 * data->temp_max[channel]; break; case hwmon_temp_alarm: - *val = !!(data->alarm & channel); + *val = !!(data->alarm & TEMP_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; @@ -668,7 +683,7 @@ static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val *val = 0; break; case hwmon_fan_alarm: - *val = !!(data->alarm & (1 << (12 + channel))); + *val = !!(data->alarm & FAN_ALARM_BIT(channel)); break; default: return -EOPNOTSUPP; From a3850231521b06bbbb18c8ebea100320c14a08be Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:20 -0300 Subject: [PATCH 179/501] hwmon: (adt7470) Fix swapped PWM3 and PWM4 auto mode masks The ADT7470_PWM3_AUTO_MASK and ADT7470_PWM4_AUTO_MASK macros are currently defined with swapped bit values. According to Table 22 of the ADT7470 datasheet, the Fan Control Mode Configuration for register 0x69 follows the exact same bit position layout as register 0x68: - 0x68 Bit[7] corresponds to BHVR1 (PWM1) -> 0x80 - 0x68 Bit[6] corresponds to BHVR2 (PWM2) -> 0x40 - 0x69 Bit[7] corresponds to BHVR3 (PWM3) -> 0x80 - 0x69 Bit[6] corresponds to BHVR4 (PWM4) -> 0x40 Consequently, PWM3 should use mask 0x80 and PWM4 should use 0x40. This typo did not cause any functional bugs because these specific macros are never referenced in the driver code. Instead, the driver correctly applies the configuration by relying on the modulo parity of the channel index (e.g., `channel % 2`) to selectively apply either ADT7470_PWM1_AUTO_MASK (0x80) or ADT7470_PWM2_AUTO_MASK (0x40). Since the bit layout is identical between the two configuration registers, the hardware is currently configured correctly. Fix the macro definitions to reflect the datasheet accurately and prevent future bugs or confusion during code review and refactoring. As this is a purely cosmetic fix with no functional impact, a backport to stable kernels is not necessary. Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-4-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 428bd1d91e70..c6fc7d38d698 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -70,8 +70,8 @@ static const unsigned short normal_i2c[] = { 0x2C, 0x2E, 0x2F, I2C_CLIENT_END }; #define ADT7470_PWM1_AUTO_MASK 0x80 #define ADT7470_PWM_AUTO_MASK 0xC0 #define ADT7470_REG_PWM34_CFG 0x69 -#define ADT7470_PWM3_AUTO_MASK 0x40 -#define ADT7470_PWM4_AUTO_MASK 0x80 +#define ADT7470_PWM4_AUTO_MASK 0x40 +#define ADT7470_PWM3_AUTO_MASK 0x80 #define ADT7470_REG_PWM_MIN_BASE_ADDR 0x6A #define ADT7470_REG_PWM_MIN_MAX_ADDR 0x6D #define ADT7470_REG_PWM_TEMP_MIN_BASE_ADDR 0x6E From 60677cd4c28f44d5b307d3029dccece38fcce90f Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:22 -0300 Subject: [PATCH 180/501] hwmon: (adt7470) Use cached PWM frequency value adt7470_pwm_read() currently ignores failures returned by pwm1_freq_get(). If the register read fails, the negative error code is returned through *val while the function itself reports success, potentially exposing a negative PWM frequency through sysfs. Fix this by using the cached PWM frequency maintained by the driver, eliminating the register access from the read path. Apart from the corrected error propagation and using the cached value, no functional change is intended. Fixes: ef67959c4253 ("hwmon: (adt7470) Convert to use regmap") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-6-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index c6fc7d38d698..1fbca4869b7b 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -182,6 +182,7 @@ struct adt7470_data { u8 pwm_min[ADT7470_PWM_COUNT]; s8 pwm_tmin[ADT7470_PWM_COUNT]; u8 pwm_auto_temp[ADT7470_PWM_COUNT]; + u32 pwm_freq; struct task_struct *auto_update; unsigned int auto_update_interval; @@ -756,7 +757,7 @@ static ssize_t force_pwm_max_store(struct device *dev, } /* These are the valid PWM frequencies to the nearest Hz */ -static const int adt7470_freq_map[] = { +static const u32 adt7470_freq_map[] = { 11, 15, 22, 29, 35, 44, 59, 88, 1400, 22500 }; @@ -796,7 +797,7 @@ static int adt7470_pwm_read(struct device *dev, u32 attr, int channel, long *val *val = 1 + data->pwm_automatic[channel]; break; case hwmon_pwm_freq: - *val = pwm1_freq_get(dev); + *val = data->pwm_freq; break; default: return -EOPNOTSUPP; @@ -809,12 +810,14 @@ static int pwm1_freq_set(struct device *dev, long freq) { struct adt7470_data *data = dev_get_drvdata(dev); unsigned int low_freq = ADT7470_CFG_LF; + u32 closest_freq; int index; int err; /* Round the user value given to the closest available frequency */ index = find_closest(freq, adt7470_freq_map, ARRAY_SIZE(adt7470_freq_map)); + closest_freq = adt7470_freq_map[index]; if (index >= 8) { index -= 8; @@ -832,6 +835,10 @@ static int pwm1_freq_set(struct device *dev, long freq) err = regmap_update_bits(data->regmap, ADT7470_REG_CFG_2, ADT7470_FREQ_MASK, index << ADT7470_FREQ_SHIFT); + if (err < 0) + goto out; + + data->pwm_freq = closest_freq; out: mutex_unlock(&data->lock); @@ -1285,6 +1292,7 @@ static int adt7470_probe(struct i2c_client *client) struct device *dev = &client->dev; struct adt7470_data *data; struct device *hwmon_dev; + int freq_val; int err; data = devm_kzalloc(dev, sizeof(struct adt7470_data), GFP_KERNEL); @@ -1309,6 +1317,14 @@ static int adt7470_probe(struct i2c_client *client) if (err < 0) return err; + freq_val = pwm1_freq_get(dev); + if (freq_val <= 0) { + err = freq_val < 0 ? freq_val : -EINVAL; + return err; + } + + data->pwm_freq = (u32)freq_val; + /* Register sysfs hooks */ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &adt7470_chip_info, From 1b46fe9dc8f8de59310f37e6c5e5c0e05ded46c3 Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:23 -0300 Subject: [PATCH 181/501] hwmon: (adt7470) Fix divide-by-zero TOCTOU crash in fan speed read If the fan data becomes 0 between the FAN_DATA_VALID() check and the FAN_PERIOD_TO_RPM() conversion, it will result in a divide-by-zero crash due to a race with a concurrent update of the cached fan value. Fix a TOCTOU issue by reading fan data once. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/r/20260727034929.E29B71F000E9@smtp.kernel.org/ Fixes: fc958a61ff6d ("hwmon: (adt7470) Convert to devm_hwmon_device_register_with_info API") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-7-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 1fbca4869b7b..772d2a409bb5 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -660,36 +660,33 @@ static ssize_t alarm_mask_store(struct device *dev, static int adt7470_fan_read(struct device *dev, u32 attr, int channel, long *val) { struct adt7470_data *data = adt7470_update_device(dev); + u16 fan_data; if (IS_ERR(data)) return PTR_ERR(data); switch (attr) { case hwmon_fan_input: - if (FAN_DATA_VALID(data->fan[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan[channel]); break; case hwmon_fan_min: - if (FAN_DATA_VALID(data->fan_min[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_min[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_min[channel]); break; case hwmon_fan_max: - if (FAN_DATA_VALID(data->fan_max[channel])) - *val = FAN_PERIOD_TO_RPM(data->fan_max[channel]); - else - *val = 0; + fan_data = READ_ONCE(data->fan_max[channel]); break; case hwmon_fan_alarm: *val = !!(data->alarm & FAN_ALARM_BIT(channel)); - break; + return 0; default: return -EOPNOTSUPP; } + if (FAN_DATA_VALID(fan_data)) + *val = FAN_PERIOD_TO_RPM(fan_data); + else + *val = 0; + return 0; } From 92413f439d1ec5e55b73ede8d66a7b971cbd1ced Mon Sep 17 00:00:00 2001 From: Luiz Angelo Daros de Luca Date: Mon, 27 Jul 2026 21:22:24 -0300 Subject: [PATCH 182/501] hwmon: (adt7470) Fix PWM auto temp state array and bounds check In pwm_auto_temp_store(), the parsed user input was missing bounds checks, allowing values > 0xF to overflow into the adjacent channel's bits. Furthermore, the value was being incorrectly written to the pwm_automatic state array instead of pwm_auto_temp. Fix this by rejecting values > 0xF with -EINVAL, and assigning the value to the correct array only after a successful I2C write. Reported-by: sashiko-bot@kernel.org Closes: https://lore.kernel.org/all/20260727034932.0B7C41F000E9@smtp.kernel.org/#t Fixes: 6f9703d0be16 ("hwmon: add support for adt7470") Signed-off-by: Luiz Angelo Daros de Luca Link: https://lore.kernel.org/r/20260727-adt7470_fixes-v2-8-598e38a46ba6@gmail.com Signed-off-by: Guenter Roeck --- drivers/hwmon/adt7470.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 772d2a409bb5..c45b984c02e6 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1049,8 +1049,10 @@ static ssize_t pwm_auto_temp_store(struct device *dev, if (temp < 0) return temp; + if (temp > 0xF) + return -EINVAL; + mutex_lock(&data->lock); - data->pwm_automatic[attr->index] = temp; if (!(attr->index % 2)) { mask = 0xF0; @@ -1061,6 +1063,9 @@ static ssize_t pwm_auto_temp_store(struct device *dev, } err = regmap_update_bits(data->regmap, pwm_auto_reg, mask, val); + if (!err) + data->pwm_auto_temp[attr->index] = temp; + mutex_unlock(&data->lock); return err < 0 ? err : count; From fa724e235cfdb0fb0bb427d0f9dfe864ae27403e Mon Sep 17 00:00:00 2001 From: Frank Sorenson Date: Sat, 25 Jul 2026 21:04:44 +0000 Subject: [PATCH 183/501] cifs: add fscache_resize_cookie() to cifs_setsize() Several code paths update the VFS inode size by calling netfs_resize_file() and cifs_setsize(), but omit the corresponding fscache_resize_cookie() call, leaving the fscache cookie out of sync with the actual file size: - cifs_file_set_size() in inode.c: server-side truncation via setattr - cifs_do_truncate() in file.c: truncates to zero on O_TRUNC open - smb2_duplicate_extents() in smb2ops.c: file clone extending EOF - smb3_simple_falloc() in smb2ops.c: two branches that extend EOF via write-range and SMB2_set_eof respectively Since every caller of cifs_setsize() must resize the fscache cookie, add the call to cifs_setsize() itself, consistent with how truncate_pagecache() is already consolidated there. Fixes: 70431bfd825d ("cifs: Support fscache indexing rewrite") Fixes: 93a43155127f ("cifs: Fix missing set of remote_i_size") Fixes: 110fee6b9bb5 ("smb: client: fix missing timestamp updates with O_TRUNC") Fixes: 7a06d3b816d7 ("smb/client: emulate small EOF-extending mode 0 fallocate ranges") Cc: stable@vger.kernel.org Cc: David Howells Cc: Paulo Alcantara Cc: Huiwen He Signed-off-by: Frank Sorenson Reviewed-by: Paulo Alcantara Signed-off-by: Steve French --- fs/smb/client/inode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index 808085eb0cdc..0afff761aab9 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -3059,6 +3059,7 @@ void cifs_setsize(struct inode *inode, loff_t offset) inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode)); truncate_pagecache(inode, offset); netfs_wait_for_outstanding_io(inode); + fscache_resize_cookie(cifs_inode_cookie(inode), offset); } int cifs_file_set_size(const unsigned int xid, struct dentry *dentry, From aec49a34d928a0ecdea85bd9f6b4114b668b68b8 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 24 Jul 2026 18:52:09 -0700 Subject: [PATCH 184/501] ata: libata: avoid kernel-doc warnings Modify comments to prevent kernel-doc warnings: - use "/*" for a non-kernel-doc comment - add a Returns: section for ata_id_major_version() Warning: include/linux/ata.h:770 Cannot find identifier on line: * Warning: include/linux/ata.h:782 function parameter 'id' not described in 'ata_id_sct_data_tables' Warning: include/linux/ata.h:782 expecting prototype for Word(). Prototype was for ata_id_sct_data_tables() instead Warning: include/linux/ata.h:820 No description found for return value of 'ata_id_major_version' Signed-off-by: Randy Dunlap Signed-off-by: Damien Le Moal --- include/linux/ata.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/ata.h b/include/linux/ata.h index 8fd48bcb2a46..7daad4cad985 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -762,8 +762,7 @@ static inline bool ata_id_sense_reporting_enabled(const u16 *id) return id[ATA_ID_COMMAND_SET_4] & BIT(6); } -/** - * +/* * Word: 206 - SCT Command Transport * 15:12 - Vendor Specific * 11:6 - Reserved @@ -810,8 +809,9 @@ static inline bool ata_id_sct_supported(const u16 *id) * * The practical impact of this is that ata_id_major_version cannot * reliably report on drives below ATA3. + * + * Returns: major version of ATA drive level or %0 if unknown */ - static inline unsigned int ata_id_major_version(const u16 *id) { unsigned int mver; From 1e024d2b41ee32bc06818f7f09a3562c58842cf9 Mon Sep 17 00:00:00 2001 From: Matt Vollrath Date: Fri, 24 Jul 2026 03:39:42 -0400 Subject: [PATCH 185/501] ata: libata-eh: Increase STANDBY IMMEDIATE timeout Correct a previous change (see Fixes) which reduced the standby timeout from 30 to 5 seconds. Increase it to 15 seconds. I was troubleshooting an error spotted during system suspend: [ 1217.152867] ata1.00: Entering standby power mode [ 1222.322948] ata1.00: qc timeout after 5000 msecs (cmd 0xe0) [ 1222.324010] ata1.00: STANDBY IMMEDIATE failed (err_mask=0x4) This drive is a Samsung 870 EVO SSD in good SMART standing, and I wasn't aware of any reason it should be taking so long to standby. The issue is intermittent, but I observed it sometimes taking 7 seconds to manually standby. I assume this was interruption of background maintenance after a power outage. As a desktop user, I would prefer to wait the extra 2 seconds at suspend to let the drive finish its business rather than drop the rails from under it. The change from 30 to 5 seconds was implicit when switching suspend from START STOP UNIT to an internal command with no timeout table entry. No reason was stated for the change. Fixes: aa3998dbeb3a ("ata: libata-scsi: Disable scsi device manage_system_start_stop") Cc: stable@vger.kernel.org Signed-off-by: Matt Vollrath Assisted-by: Claude:claude-5-fable Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 8 ++++++++ include/linux/libata.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 05df7ea6954a..46947ed0c657 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -106,6 +106,12 @@ static const unsigned int ata_eh_flush_timeouts[] = { UINT_MAX, }; +static const unsigned int ata_eh_standby_timeouts[] = { + 15000, /* Some drives may be slow to standby */ + /* but don't hold up a suspend too long waiting for them */ + UINT_MAX, +}; + static const unsigned int ata_eh_other_timeouts[] = { 5000, /* same rationale as identify timeout */ 10000, /* ditto */ @@ -147,6 +153,8 @@ ata_eh_cmd_timeout_table[ATA_EH_CMD_TIMEOUT_TABLE_SIZE] = { .timeouts = ata_eh_other_timeouts, }, { .commands = CMDS(ATA_CMD_FLUSH, ATA_CMD_FLUSH_EXT), .timeouts = ata_eh_flush_timeouts }, + { .commands = CMDS(ATA_CMD_STANDBYNOW1), + .timeouts = ata_eh_standby_timeouts }, { .commands = CMDS(ATA_CMD_VERIFY), .timeouts = ata_eh_reset_timeouts }, }; diff --git a/include/linux/libata.h b/include/linux/libata.h index 96e626d6a7ca..6c0d385b2cde 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -425,7 +425,7 @@ enum { /* This should match the actual table size of * ata_eh_cmd_timeout_table in libata-eh.c. */ - ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 8, + ATA_EH_CMD_TIMEOUT_TABLE_SIZE = 9, /* User visible DMA mask for DMA control. DO NOT renumber. */ ATA_DMA_MASK_ATA = (1 << 0), /* DMA on ATA Disk */ From d568a43f6dbba3ba006304d95fd09862bd482a2f Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:46 +0100 Subject: [PATCH 186/501] afs: Fix afs_fs_fetch_data() to set call->async Fix afs_fs_fetch_data() to set call->async on an async operation as does afs_fs_fetch_data64(). Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-2-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/fsclient.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index a2ffd60889f8..626e1d37b915 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -477,6 +477,9 @@ void afs_fs_fetch_data(struct afs_operation *op) if (!call) return afs_op_nomem(op); + if (op->flags & AFS_OPERATION_ASYNC) + call->async = true; + /* marshall the parameters */ bp = call->request; bp[0] = htonl(FSFETCHDATA); From 222052c6be186f2074b3a4d741d5de200f654c43 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:47 +0100 Subject: [PATCH 187/501] afs: Fix afs_fs_fetch_data() to subtract transferred from len Fix afs_fs_fetch_data() to subtract subreq->transferred from subreq->len rather than adding it. Fixes: f28fc2010d62 ("afs: Eliminate afs_read") Link: https://sashiko.dev/#/patchset/20260713081022.2186481-1-dhowells%40redhat.com Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-3-dhowells@redhat.com cc: Marc Dionne cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/fsclient.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 626e1d37b915..1a3f186a6a11 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -487,7 +487,7 @@ void afs_fs_fetch_data(struct afs_operation *op) bp[2] = htonl(vp->fid.vnode); bp[3] = htonl(vp->fid.unique); bp[4] = htonl(lower_32_bits(subreq->start + subreq->transferred)); - bp[5] = htonl(lower_32_bits(subreq->len + subreq->transferred)); + bp[5] = htonl(lower_32_bits(subreq->len - subreq->transferred)); call->fid = vp->fid; trace_afs_make_fs_call(call, &vp->fid); From 4af1ec68d54b3871155914d584fb10669c41a861 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 23 Jul 2026 12:34:48 +0100 Subject: [PATCH 188/501] afs: Fix UAF when sending a message In afs_make_call(), there's a race with async call reception and destruction. If a call is dispatched that doesn't have call->write_iter set (used to specify the data content for FS.StoreData), then the first rxrpc_kernel_send_data() will not set MSG_MORE in the msghdr. Once rxrpc_send_data() queues the last request packet, the response could come in at any time and cause the call to be completed and put. However, afs_make_call() will look at the call again to see it ->write_iter should be handled - something it's only allowed to do if it has its own ref on the call. Whilst this is the case for synchronous calls, it isn't true for async calls such as FS.FetchData. There's also a potential UAF in afs_make_call() in the event that an asynchronous call is being sent, but the call fails in some way (e.g. it gets aborted from the server). The problem there is that afs_make_call() tries to abort a call if the rxrpc send fails, but the asynchronous notification from rxrpc may have caused the afs_call to be torn down. generic/650 plays games with randomly taking CPUs offline, and can interject a significant delay such that the call is deallocated before afs_make_call() gets to check call->write_iter - and a UAF ensues (caught by KASAN). BUG: KASAN: slab-use-after-free in afs_make_call+0x1c90/0x2210 [kafs] Read of size 8 at addr ffff888035e050e8 by task fsstress/1409 Fix this by making afs_make_op_call() give the op->call its own ref rather than transferring the caller's ref to it and then dropping the ref when afs_make_call() returns. This also means that the afs_make_call() func never loses its ref on the call now. Fixes: eddf51f2bb2c ("afs: Make {Y,}FS.FetchData an asynchronous operation") Fixes: e49c7b2f6de7 ("afs: Build an abstraction around an "operation" concept") Link: https://sashiko.dev/#/patchset/20260702144919.172295-1-dhowells%40redhat.com Reported-by: Marc Dionne Signed-off-by: David Howells Link: https://patch.msgid.link/20260723113452.566619-4-dhowells@redhat.com cc: Jeffrey Altman cc: linux-afs@lists.infradead.org cc: stable@kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/afs/internal.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 601f01e5c15f..290873bac89b 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -1421,7 +1421,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c { struct afs_addr_list *alist = op->estate->addresses; - op->call = call; + op->call = afs_get_call(call, afs_call_trace_get); op->type = call->type; call->op = op; call->key = op->key; @@ -1429,6 +1429,7 @@ static inline void afs_make_op_call(struct afs_operation *op, struct afs_call *c call->peer = rxrpc_kernel_get_peer(alist->addrs[op->addr_index].peer); call->service_id = op->server->service_id; afs_make_call(call, gfp); + afs_put_call(call); } static inline void afs_extract_begin(struct afs_call *call, void *buf, size_t size) From 0a4bb2abc3e56d7be6e69b050c88ba52c87e22bf Mon Sep 17 00:00:00 2001 From: Hardik Prakash Date: Sat, 18 Jul 2026 11:13:31 +0530 Subject: [PATCH 189/501] i2c: designware: defer probe if child GpioInt controllers are not bound I2C controllers may have child devices with GpioInt resources that depend on GPIO controllers being fully initialized. If the I2C controller probes and enumerates children before the referenced GPIO controller has completed probe, GPIO interrupts may not be properly configured, leading to device failures. On Lenovo Yoga 7 14AGP11, the WACF2200 touchscreen (child of AMDI0010:02) has a GpioInt resource pointing to GPIO 157 on the pinctrl-amd controller (AMDI0030:00). When i2c-designware probes AMDI0010:02 before pinctrl-amd finishes initializing, I2C transactions fail with lost arbitration errors: 0.285952 amd_gpio_probe: registering gpiochip <- GPIO chip visible 0.287121 amd_gpio_probe: requesting parent IRQ <- probe still running 0.301454 AMDI0010:02 dw_i2c_plat_probe: start <- races here 2.348157 lost arbitration Add a dependency check that walks ACPI child devices and defers probe until any referenced GPIO controller is bound. Fixes: 3812a9e84265 ("pinctrl-amd: enable IRQ for WACF2200 touchscreen on Lenovo Yoga 7 14AGP11") Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221494 Suggested-by: Mario Limonciello Suggested-by: Andy Shevchenko Signed-off-by: Hardik Prakash Assisted-by: Claude:claude-sonnet-5 Assisted-by: DeepSeek:deepseek-v4-pro Cc: # v7.1+ Acked-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260718054330.8975-2-hardikprakash.official@gmail.com --- drivers/i2c/busses/i2c-designware-platdrv.c | 80 +++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index 6d6e81242f74..c8a203fff4d1 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -8,12 +8,14 @@ * Copyright (C) 2007 MontaVista Software Inc. * Copyright (C) 2009 Provigent Ltd. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -130,6 +132,80 @@ static int i2c_dw_probe_lock_support(struct dw_i2c_dev *dev) return 0; } +#if defined(CONFIG_ACPI) && defined(CONFIG_GPIOLIB) +/* + * Check whether an ACPI GpioInt resource's referenced GPIO controller + * has finished probing. Resources with no named controller (resource + * source string) are skipped, since they can't be resolved to a + * struct device. + */ +static int check_gpioint_resource(struct acpi_resource *ares, void *data) +{ + struct acpi_resource_gpio *agpio; + struct acpi_device *gpio_adev; + struct device *gpio_dev; + acpi_handle handle; + acpi_status status; + + if (!acpi_gpio_get_irq_resource(ares, &agpio)) + return 1; /* not a GpioInt resource, skip */ + + if (!agpio->resource_source.string_length) + return 1; /* no named controller, skip */ + + status = acpi_get_handle(NULL, agpio->resource_source.string_ptr, &handle); + if (ACPI_FAILURE(status)) + return 1; + + gpio_adev = acpi_fetch_acpi_dev(handle); + if (!gpio_adev) + return 1; + + struct gpio_device *gdev __free(gpio_device_put) = + gpio_device_find_by_fwnode(acpi_fwnode_handle(gpio_adev)); + if (!gdev) + return -EPROBE_DEFER; /* controller not registered yet: abort walk */ + + gpio_dev = gpio_device_to_device(gdev)->parent; + + guard(device)(gpio_dev); + if (!device_is_bound(gpio_dev)) + return -EPROBE_DEFER; /* controller not bound yet: abort walk */ + + return 1; /* bound, skip adding to resource list, continue walk */ +} + +static int check_child_gpioint(struct acpi_device *adev, void *data) +{ + LIST_HEAD(res_list); + int ret; + + ret = acpi_dev_get_resources(adev, &res_list, check_gpioint_resource, NULL); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&res_list); + + return 0; +} + +static int i2c_dw_check_gpio_dependencies(struct device *dev) +{ + struct acpi_device *adev; + + adev = ACPI_COMPANION(dev); + if (!adev) + return 0; + + return acpi_dev_for_each_child(adev, check_child_gpioint, NULL); +} +#else +static int i2c_dw_check_gpio_dependencies(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_ACPI && CONFIG_GPIOLIB */ + static int dw_i2c_plat_probe(struct platform_device *pdev) { u32 flags = (uintptr_t)device_get_match_data(&pdev->dev); @@ -138,6 +214,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct dw_i2c_dev *dev; int irq, ret; + ret = i2c_dw_check_gpio_dependencies(device); + if (ret) + return ret; + irq = platform_get_irq_optional(pdev, 0); if (irq == -ENXIO) flags |= ACCESS_POLLING; From 6395789e4739aa5177bbec0fa0f07ccc38d249b0 Mon Sep 17 00:00:00 2001 From: Jose Maria Casanova Crespo Date: Mon, 27 Jul 2026 11:32:28 -0300 Subject: [PATCH 190/501] drm/vc4: Supply the overflow slot size in BPOS, not the whole bin BO size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vc4_overflow_mem_work() points BPOA at a 512KB slot inside the 16MB binner BO, but writes the size of the whole BO to BPOS. On every binner out-of-memory event the PTB is therefore authorized to write tile lists across all the other slots (which may hold the tile state, tile alloc and overflow memory of in-flight jobs) and, for any slot but the first, past the end of the binner BO into unrelated CMA memory. Since CMA pages are recycled into page cache and user allocations, this is arbitrary memory corruption by GPU DMA. In practice it shows up as GPU hangs with corrupted control list pointers, userspace heap corruption, a GPU that stays permanently wedged after the first hang, and occasional full system crashes, whenever a job overflows the initial binner slot. The bug dates back to the conversion from a dedicated overflow BO (where writing the full BO size was correct) to the slotted binner BO. Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4.8 Signed-off-by: Jose Maria Casanova Crespo Reviewed-by: Maíra Canal Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-1-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/vc4/vc4_irq.c b/drivers/gpu/drm/vc4/vc4_irq.c index 8e5141bb5075..7877d493d80e 100644 --- a/drivers/gpu/drm/vc4/vc4_irq.c +++ b/drivers/gpu/drm/vc4/vc4_irq.c @@ -104,7 +104,7 @@ vc4_overflow_mem_work(struct work_struct *work) vc4->bin_alloc_overflow = BIT(bin_bo_slot); V3D_WRITE(V3D_BPOA, bo->base.dma_addr + bin_bo_slot * vc4->bin_alloc_size); - V3D_WRITE(V3D_BPOS, bo->base.base.size); + V3D_WRITE(V3D_BPOS, vc4->bin_alloc_size); V3D_WRITE(V3D_INTCTL, V3D_INT_OUTOMEM); V3D_WRITE(V3D_INTENA, V3D_INT_OUTOMEM); spin_unlock_irqrestore(&vc4->job_lock, irqflags); From 48a570c964d8e37d353381e4195106277e17f5cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ADra=20Canal?= Date: Mon, 27 Jul 2026 11:32:29 -0300 Subject: [PATCH 191/501] drm/vc4: Zero the tile state data array before each BIN job MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The binner BO is a single 16MB buffer split into 512KB slots that are handed out to jobs at submission time and recycled as jobs complete, without ever being cleared. Each slot holds the job's Tile State Data Array (TSDA) at its start, followed by the tile allocation pool. While the tile allocation pool is only walked by the render thread through branches the binner generated during the current job, the TSDA is the PTB's own per-tile bookkeeping and is consumed by the hardware itself. Although the kernel sets the "Auto-initialise Tile State Data Array" flag in the tile binning mode configuration, the PTB demonstrably still acts on stale tile state left by the slot's previous user: the binner ends up creating invalid command streams with invalid primitive streams and branches, which can cause GPU hangs as observed in [1][2]. Zero the TSDA when the job's binning slot is configured. This clears 48 bytes per tile (~24KB for a 1080p frame) in the submission path, and guarantees the PTB never sees another job's tile state. The tile count is only checked for being non-zero today, so the 8-bit fields it comes from can describe a tile state array almost six times larger than the slot it has to live in. Bound it before the slot is handed out, since such size decides how much of the slot is left for the tile alloc pool. Link: https://github.com/raspberrypi/linux/issues/3221 [1] Link: https://github.com/raspberrypi/linux/issues/5780 [2] Fixes: 553c942f8b2c ("drm/vc4: Allow using more than 256MB of CMA memory.") Cc: stable@vger.kernel.org Reviewed-by: Iago Toral Quiroga Link: https://patch.msgid.link/20260727-vc4-bin-oom-fixes-v2-2-0d8a5eddc7c9@igalia.com Signed-off-by: Maíra Canal --- drivers/gpu/drm/vc4/vc4_validate.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/vc4/vc4_validate.c b/drivers/gpu/drm/vc4/vc4_validate.c index 7f2fadfde7a8..d2a65c968b1f 100644 --- a/drivers/gpu/drm/vc4/vc4_validate.c +++ b/drivers/gpu/drm/vc4/vc4_validate.c @@ -385,6 +385,23 @@ validate_tile_binning_config(VALIDATE_ARGS) return -EINVAL; } + /* The tile state data array is 48 bytes per tile, and we put it at + * the start of a BO containing both it and the tile alloc. + */ + tile_state_size = 48 * tile_count; + + /* Since the tile alloc array will follow us, align. */ + tile_state_size = roundup(tile_state_size, 4096); + + /* Reject configurations whose tile state would leave no room for + * the tile alloc pool that follows it in the slot. + */ + if (tile_state_size >= vc4->bin_alloc_size) { + DRM_DEBUG("Tile binning config of %dx%d too large\n", + exec->bin_tiles_x, exec->bin_tiles_y); + return -EINVAL; + } + bin_slot = vc4_v3d_get_bin_slot(vc4); if (bin_slot < 0) { if (bin_slot != -EINTR && bin_slot != -ERESTARTSYS) { @@ -400,13 +417,13 @@ validate_tile_binning_config(VALIDATE_ARGS) exec->bin_slots |= BIT(bin_slot); bin_addr = vc4->bin_bo->base.dma_addr + bin_slot * vc4->bin_alloc_size; - /* The tile state data array is 48 bytes per tile, and we put it at - * the start of a BO containing both it and the tile alloc. - */ - tile_state_size = 48 * tile_count; + exec->tile_alloc_offset = bin_addr + tile_state_size; - /* Since the tile alloc array will follow us, align. */ - exec->tile_alloc_offset = bin_addr + roundup(tile_state_size, 4096); + /* The TSDA area must be zeroed out before use, otherwise the PTB might + * consume a stale tile state. + */ + memset(vc4->bin_bo->base.vaddr + bin_slot * vc4->bin_alloc_size, 0, + tile_state_size); *(uint8_t *)(validated + 14) = ((flags & ~(VC4_BIN_CONFIG_ALLOC_INIT_BLOCK_SIZE_MASK | From a81fc9266e1c5fef9ccf675a9b44b2f4ab464923 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:12 +0100 Subject: [PATCH 192/501] netfs: clear PG_private_2 on copy-to-cache append failure netfs_pgpriv2_copy_to_cache() marks the folio with PG_private_2 before netfs_pgpriv2_copy_folio() appends it to the copy-to-cache rolling buffer. If the append fails, the folio is not queued for cache writeback, so the PG_private_2 state and its reference must be released immediately. Fixes: e2d46f2ec332 ("netfs: Change the read result collector to only use one work item") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-2-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/read_pgpriv2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index a1489aa29f78..7eacc58abadb 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -54,6 +54,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio /* Attach the folio to the rolling buffer. */ if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { + folio_end_private_2(folio); clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; } From 37a1c535c80c67d98668d190c7432f9ebda43310 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:13 +0100 Subject: [PATCH 193/501] netfs: handle single writeback rolling buffer allocation failure netfs_write_folio_single() takes an extra folio reference before appending the folio to the rolling buffer. rolling_buffer_append() can fail if it cannot allocate another folio_queue. Check the return value and drop the extra folio reference before returning the error. Fixes: 49866ce7ea8d ("netfs: Add support for caching single monolithic objects such as AFS dirs") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-3-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/write_issue.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index f2761c99795a..14efe4cb9393 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -720,6 +720,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, size_t iter_off = 0; size_t fsize = folio_size(folio), flen; loff_t fpos = folio_pos(folio); + ssize_t ret; bool to_eof = false; bool no_debug = false; @@ -748,7 +749,11 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, /* Attach the folio to the rolling buffer. */ folio_get(folio); - rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + if (ret < 0) { + folio_put(folio); + return ret; + } /* Move the submission point forward to allow for write-streaming data * not starting at the front of the page. We don't do write-streaming From 87eb3d272dcbcbbfe5c1576c10e5dc72810cf1f6 Mon Sep 17 00:00:00 2001 From: Yichong Chen Date: Mon, 27 Jul 2026 14:07:14 +0100 Subject: [PATCH 194/501] netfs: release readahead folios on iterator preparation failure netfs_prepare_read_iterator() batches readahead folios in put_batch so that the folio references can be dropped after the I/O iterator has been prepared. If rolling_buffer_load_from_ra() fails after earlier folios have been batched, the function returns immediately and leaves those references held. Release the batch before returning the error. Fixes: 06fa229ceb36 ("netfs: Abstract out a rolling folio buffer implementation") Signed-off-by: Yichong Chen Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-4-dhowells@redhat.com cc: Paulo Alcantara cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/buffered_read.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 24a8a5418e31..3d86414ee40f 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -102,8 +102,10 @@ static ssize_t netfs_prepare_read_iterator(struct netfs_io_subrequest *subreq, added = rolling_buffer_load_from_ra(&rreq->buffer, ractl, &put_batch); - if (added < 0) + if (added < 0) { + folio_batch_release(&put_batch); return added; + } rreq->submitted += added; } folio_batch_release(&put_batch); From 1d78d56c43ef3768183e8370e7367b162700e049 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 27 Jul 2026 14:07:15 +0100 Subject: [PATCH 195/501] netfs: Fix folio_queue ENOMEM in writeback by adding a mempool Fix the handling of folio_queue allocation failure in writeback by adding a mempool and passing in gfp_t flags to the rolling buffer functions that allocate memory, using the mempool if gfp != GFP_KERNEL. This is then extended upwards and the gfp to be used for a request is stored in the netfs_io_request struct and is then used for both requests and subrequests, eliminating the sleeping loops there. The failure caused: folio != NULL WARNING: fs/netfs/write_issue.c:603 at netfs_writepages+0x883/0xa10 fs/netfs/write_issue.c:603, CPU#3: syz.0.17/5919 Fixes: cd0277ed0c18 ("netfs: Use new folio_queue data type and iterator instead of xarray iter") Reported-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=0da43efa72f88bd3a8af Signed-off-by: David Howells Link: https://patch.msgid.link/20260727130716.1099906-5-dhowells@redhat.com Tested-by: syzbot+0da43efa72f88bd3a8af@syzkaller.appspotmail.com cc: Paulo Alcantara cc: Yun Zhou cc: Matthew Wilcox cc: Christoph Hellwig cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/netfs/buffered_read.c | 6 +++--- fs/netfs/internal.h | 1 + fs/netfs/main.c | 7 +++++++ fs/netfs/objects.c | 30 +++++++++++++++++------------- fs/netfs/read_pgpriv2.c | 2 +- fs/netfs/rolling_buffer.c | 22 +++++++++++++--------- fs/netfs/write_issue.c | 10 +++++----- include/linux/netfs.h | 1 + include/linux/rolling_buffer.h | 6 +++--- 9 files changed, 51 insertions(+), 34 deletions(-) diff --git a/fs/netfs/buffered_read.c b/fs/netfs/buffered_read.c index 3d86414ee40f..7fdfa4f27e34 100644 --- a/fs/netfs/buffered_read.c +++ b/fs/netfs/buffered_read.c @@ -361,7 +361,7 @@ void netfs_readahead(struct readahead_control *ractl) netfs_rreq_expand(rreq, ractl); rreq->submitted = rreq->start; - if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) + if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) goto cleanup_free; netfs_read_to_pagecache(rreq, ractl); @@ -380,10 +380,10 @@ static int netfs_create_singular_buffer(struct netfs_io_request *rreq, struct fo { ssize_t added; - if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST) < 0) + if (rolling_buffer_init(&rreq->buffer, rreq->debug_id, ITER_DEST, rreq->gfp) < 0) return -ENOMEM; - added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags); + added = rolling_buffer_append(&rreq->buffer, folio, rollbuf_flags, rreq->gfp); if (added < 0) return added; rreq->submitted = rreq->start + added; diff --git a/fs/netfs/internal.h b/fs/netfs/internal.h index d889caa401dc..420ee7b26580 100644 --- a/fs/netfs/internal.h +++ b/fs/netfs/internal.h @@ -43,6 +43,7 @@ extern struct list_head netfs_io_requests; extern spinlock_t netfs_proc_lock; extern mempool_t netfs_request_pool; extern mempool_t netfs_subrequest_pool; +extern mempool_t netfs_folioq_pool; #ifdef CONFIG_PROC_FS static inline void netfs_proc_add_rreq(struct netfs_io_request *rreq) diff --git a/fs/netfs/main.c b/fs/netfs/main.c index 73da6c9f5777..927badf3989d 100644 --- a/fs/netfs/main.c +++ b/fs/netfs/main.c @@ -28,6 +28,7 @@ static struct kmem_cache *netfs_request_slab; static struct kmem_cache *netfs_subrequest_slab; mempool_t netfs_request_pool; mempool_t netfs_subrequest_pool; +mempool_t netfs_folioq_pool; #ifdef CONFIG_PROC_FS LIST_HEAD(netfs_io_requests); @@ -108,6 +109,9 @@ static int __init netfs_init(void) { int ret = -ENOMEM; + if (mempool_init_kmalloc_pool(&netfs_folioq_pool, 100, sizeof(struct folio_queue)) < 0) + goto error_folioq_pool; + netfs_request_slab = kmem_cache_create("netfs_request", sizeof(struct netfs_io_request), 0, SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, @@ -160,6 +164,8 @@ static int __init netfs_init(void) error_reqpool: kmem_cache_destroy(netfs_request_slab); error_req: + mempool_exit(&netfs_folioq_pool); +error_folioq_pool: return ret; } fs_initcall(netfs_init); @@ -172,5 +178,6 @@ static void __exit netfs_exit(void) kmem_cache_destroy(netfs_subrequest_slab); mempool_exit(&netfs_request_pool); kmem_cache_destroy(netfs_request_slab); + mempool_exit(&netfs_folioq_pool); } module_exit(netfs_exit); diff --git a/fs/netfs/objects.c b/fs/netfs/objects.c index b8c4918d3dcd..01461a74642d 100644 --- a/fs/netfs/objects.c +++ b/fs/netfs/objects.c @@ -7,7 +7,6 @@ #include #include -#include #include "internal.h" static void netfs_free_request(struct work_struct *work); @@ -26,17 +25,23 @@ struct netfs_io_request *netfs_alloc_request(struct address_space *mapping, struct netfs_io_request *rreq; mempool_t *mempool = ctx->ops->request_pool ?: &netfs_request_pool; struct kmem_cache *cache = mempool->pool_data; + gfp_t gfp = GFP_KERNEL; int ret; - for (;;) { - rreq = mempool_alloc(mempool, GFP_KERNEL); - if (rreq) - break; - msleep(10); + /* Writeback is part of memory reclaim and must not fail due to ENOMEM. */ + if (origin == NETFS_WRITEBACK || origin == NETFS_WRITEBACK_SINGLE) { + gfp = GFP_NOFS; /* Allows use of mempools. */ + + rreq = mempool_alloc(mempool, gfp); + } else { + rreq = mempool->alloc(gfp, mempool->pool_data); + if (!rreq) + return ERR_PTR(-ENOMEM); } memset(rreq, 0, kmem_cache_size(cache)); INIT_WORK(&rreq->cleanup_work, netfs_free_request); + rreq->gfp = gfp; rreq->start = start; rreq->len = len; rreq->origin = origin; @@ -200,13 +205,12 @@ struct netfs_io_subrequest *netfs_alloc_subrequest(struct netfs_io_request *rreq mempool_t *mempool = rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool; struct kmem_cache *cache = mempool->pool_data; - for (;;) { - subreq = mempool_alloc(rreq->netfs_ops->subrequest_pool ?: &netfs_subrequest_pool, - GFP_KERNEL); - if (subreq) - break; - msleep(10); - } + if (rreq->gfp == GFP_KERNEL) + subreq = mempool->alloc(rreq->gfp, mempool->pool_data); + else + subreq = mempool_alloc(mempool, rreq->gfp); + if (!subreq) + return NULL; memset(subreq, 0, kmem_cache_size(cache)); INIT_WORK(&subreq->work, NULL); diff --git a/fs/netfs/read_pgpriv2.c b/fs/netfs/read_pgpriv2.c index 7eacc58abadb..c31190993b76 100644 --- a/fs/netfs/read_pgpriv2.c +++ b/fs/netfs/read_pgpriv2.c @@ -53,7 +53,7 @@ static void netfs_pgpriv2_copy_folio(struct netfs_io_request *creq, struct folio trace_netfs_folio(folio, netfs_folio_trace_store_copy); /* Attach the folio to the rolling buffer. */ - if (rolling_buffer_append(&creq->buffer, folio, 0) < 0) { + if (rolling_buffer_append(&creq->buffer, folio, 0, creq->gfp) < 0) { folio_end_private_2(folio); clear_bit(NETFS_RREQ_FOLIO_COPY_TO_CACHE, &creq->flags); return; diff --git a/fs/netfs/rolling_buffer.c b/fs/netfs/rolling_buffer.c index a17fbf9853a4..8c0026836f9c 100644 --- a/fs/netfs/rolling_buffer.c +++ b/fs/netfs/rolling_buffer.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -27,7 +28,10 @@ struct folio_queue *netfs_folioq_alloc(unsigned int rreq_id, gfp_t gfp, { struct folio_queue *fq; - fq = kmalloc_obj(*fq, gfp); + if (gfp == GFP_KERNEL) + fq = netfs_folioq_pool.alloc(gfp, netfs_folioq_pool.pool_data); + else + fq = mempool_alloc(&netfs_folioq_pool, gfp); if (fq) { netfs_stat(&netfs_n_folioq); folioq_init(fq, rreq_id); @@ -50,7 +54,7 @@ void netfs_folioq_free(struct folio_queue *folioq, { trace_netfs_folioq(folioq, trace); netfs_stat_d(&netfs_n_folioq); - kfree(folioq); + mempool_free(folioq, &netfs_folioq_pool); } EXPORT_SYMBOL(netfs_folioq_free); @@ -60,11 +64,11 @@ EXPORT_SYMBOL(netfs_folioq_free); * consumer. */ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, - unsigned int direction) + unsigned int direction, gfp_t gfp) { struct folio_queue *fq; - fq = netfs_folioq_alloc(rreq_id, GFP_NOFS, netfs_trace_folioq_rollbuf_init); + fq = netfs_folioq_alloc(rreq_id, gfp, netfs_trace_folioq_rollbuf_init); if (!fq) return -ENOMEM; @@ -77,14 +81,14 @@ int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, /* * Add another folio_queue to a rolling buffer if there's no space left. */ -int rolling_buffer_make_space(struct rolling_buffer *roll) +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp) { struct folio_queue *fq, *head = roll->head; if (!folioq_full(head)) return 0; - fq = netfs_folioq_alloc(head->rreq_id, GFP_NOFS, netfs_trace_folioq_make_space); + fq = netfs_folioq_alloc(head->rreq_id, gfp, netfs_trace_folioq_make_space); if (!fq) return -ENOMEM; fq->prev = head; @@ -122,7 +126,7 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, int nr, ix, to; ssize_t size = 0; - if (rolling_buffer_make_space(roll) < 0) + if (rolling_buffer_make_space(roll, GFP_KERNEL) < 0) return -ENOMEM; fq = roll->head; @@ -153,12 +157,12 @@ ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, * Append a folio to the rolling buffer. */ ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, - unsigned int flags) + unsigned int flags, gfp_t gfp) { ssize_t size = folio_size(folio); int slot; - if (rolling_buffer_make_space(roll) < 0) + if (rolling_buffer_make_space(roll, gfp) < 0) return -ENOMEM; slot = folioq_append(roll->head, folio); diff --git a/fs/netfs/write_issue.c b/fs/netfs/write_issue.c index 14efe4cb9393..2d9cfcd43658 100644 --- a/fs/netfs/write_issue.c +++ b/fs/netfs/write_issue.c @@ -108,7 +108,7 @@ struct netfs_io_request *netfs_create_write_req(struct address_space *mapping, ictx = netfs_inode(wreq->inode); if (is_cacheable) fscache_begin_write_operation(&wreq->cache_resources, netfs_i_cookie(ictx)); - if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE) < 0) + if (rolling_buffer_init(&wreq->buffer, wreq->debug_id, ITER_SOURCE, wreq->gfp) < 0) goto nomem; wreq->cleaned_to = wreq->start; @@ -167,7 +167,7 @@ void netfs_prepare_write(struct netfs_io_request *wreq, */ if (iov_iter_is_folioq(wreq_iter) && wreq_iter->folioq_slot >= folioq_nr_slots(wreq_iter->folioq)) - rolling_buffer_make_space(&wreq->buffer); + rolling_buffer_make_space(&wreq->buffer, wreq->gfp); subreq = netfs_alloc_subrequest(wreq); subreq->source = stream->source; @@ -334,7 +334,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, _enter(""); - if (rolling_buffer_make_space(&wreq->buffer) < 0) + if (rolling_buffer_make_space(&wreq->buffer, wreq->gfp) < 0) return -ENOMEM; /* netfs_perform_write() may shift i_size around the page or from out @@ -436,7 +436,7 @@ static int netfs_write_folio(struct netfs_io_request *wreq, } /* Attach the folio to the rolling buffer. */ - rolling_buffer_append(&wreq->buffer, folio, 0); + rolling_buffer_append(&wreq->buffer, folio, 0, wreq->gfp); /* Move the submission point forward to allow for write-streaming data * not starting at the front of the page. We don't do write-streaming @@ -749,7 +749,7 @@ static int netfs_write_folio_single(struct netfs_io_request *wreq, /* Attach the folio to the rolling buffer. */ folio_get(folio); - ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK); + ret = rolling_buffer_append(&wreq->buffer, folio, NETFS_ROLLBUF_PUT_MARK, wreq->gfp); if (ret < 0) { folio_put(folio); return ret; diff --git a/include/linux/netfs.h b/include/linux/netfs.h index 1bc120d61c5b..d0b62d53eea9 100644 --- a/include/linux/netfs.h +++ b/include/linux/netfs.h @@ -255,6 +255,7 @@ struct netfs_io_request { unsigned long long cleaned_to; /* Position we've cleaned folios to */ unsigned long long abandon_to; /* Position to abandon folios to */ const struct folio *no_unlock_folio; /* Don't unlock this folio after read */ + gfp_t gfp; /* GFP flags to use */ unsigned int direct_bv_count; /* Number of elements in direct_bv[] */ unsigned int debug_id; unsigned int rsize; /* Maximum read size (0 for none) */ diff --git a/include/linux/rolling_buffer.h b/include/linux/rolling_buffer.h index ac15b1ffdd83..9e5dad29669c 100644 --- a/include/linux/rolling_buffer.h +++ b/include/linux/rolling_buffer.h @@ -43,13 +43,13 @@ struct rolling_buffer_snapshot { #define ROLLBUF_MARK_2 BIT(1) int rolling_buffer_init(struct rolling_buffer *roll, unsigned int rreq_id, - unsigned int direction); -int rolling_buffer_make_space(struct rolling_buffer *roll); + unsigned int direction, gfp_t gfp); +int rolling_buffer_make_space(struct rolling_buffer *roll, gfp_t gfp); ssize_t rolling_buffer_load_from_ra(struct rolling_buffer *roll, struct readahead_control *ractl, struct folio_batch *put_batch); ssize_t rolling_buffer_append(struct rolling_buffer *roll, struct folio *folio, - unsigned int flags); + unsigned int flags, gfp_t gfp); struct folio_queue *rolling_buffer_delete_spent(struct rolling_buffer *roll); void rolling_buffer_clear(struct rolling_buffer *roll); From 79055d82772b9584f259b747fe40ff56a076678d Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 28 Jul 2026 14:26:32 +0200 Subject: [PATCH 196/501] binfmt_misc: don't let an 'F' entry pin its own instance An entry registered with 'F' opens its interpreter at registration time and holds that file until the entry is freed. Any entry nobody removes by hand only gets closed once the binfmt_misc superblock is shut down. If the interpreter lives on a mount that keeps that superblock alive the two pin each other: binfmt_misc sb -> inode -> entry -> interp_file -> vfsmount -> binfmt_misc sb TL;DR the file is never closed. Once the mount namespace is gone there is nothing left to unregister through either. There are two ways to trigger this bug: - Point the interpreter at the instance itself. Its files are regular files owned by the mounter and both bm_get_inode() and simple_fill_super() leave i_op at empty_iops. So notify_change() falls back to simple_setattr() and chmod +x works. We never set SB_I_NOEXEC and so open_exec() accepts it. - Use the instance as an overlayfs lower layer. The overlay superblock holds a clone_private_mount() of every layer until it is destroyed and that clone is in no namespace. So umount_tree() never reaches it. That's a DoS. And it isn't only the superblock that leaks. It pins the user namespace it was mounted in, so every iteration permanently eats one of the caller's user namespace charges. So let's just do the sane thing. SB_I_NOEXEC makes open_exec() fail on the instance's own files and s_stack_depth makes overlayfs reject the layer before it ever takes a clone. That also covers the ecryptfs and fuse passthrough variants. What 'F' promises is unchanged. The stable tag is narrower than the Fixes tags on purpose. Before sandboxed mounts this needed global root against the single instance everyone shares, and the change doesn't apply to those trees anyway. Note that SB_I_NODEV is implicitly raised for userns mounts but raise it explicitly here as well. Link: https://patch.msgid.link/20260728-work-binfmt_misc-selfpin-v1-1-74df5daeca5b@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org # v6.7+ Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 5de615ca7a75..47aeb2b68d3e 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -937,6 +937,10 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) if (WARN_ON(user_ns != current_user_ns())) return -EINVAL; + /* Never exec off this instance and never let anything stack on it. */ + sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV; + sb->s_stack_depth = FILESYSTEM_MAX_STACK_DEPTH; + /* * Lazily allocate a new binfmt_misc instance for this namespace, i.e. * do it here during the first mount of binfmt_misc. We don't need to From db1856ea9196cf6e015d12199a34c0b9313c7bfa Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:02 +0200 Subject: [PATCH 197/501] binfmt_misc: restore write access when removing an entry Registering an entry with the MISC_FMT_OPEN_FILE flag opens the interpreter via open_exec() which denies write access to it for as long as the entry exists. Removing the entry closes the interpreter file via filp_close() but never restores write access, leaving the inode's i_writecount permanently negative. Opening the interpreter for writing keeps failing with ETXTBSY long after the entry is gone until the inode is evicted from the inode cache. Commit 90f601b497d7 ("binfmt_misc: restore write access before closing files opened by open_exec()") fixed the same imbalance in the error path of bm_register_write() but the actual removal path has been leaking the write denial since the introduction of the flag. Restore write access in put_binfmt_handler() before closing the interpreter file. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-1-a162f7cb58d6@kernel.org Fixes: 948b701a607f ("binfmt_misc: add persistent opened binary handler for containers") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index 47aeb2b68d3e..adab06d18550 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -162,8 +162,10 @@ static Node *get_binfmt_handler(struct binfmt_misc *misc, static void put_binfmt_handler(Node *e) { if (refcount_dec_and_test(&e->users)) { - if (e->flags & MISC_FMT_OPEN_FILE) + if (e->flags & MISC_FMT_OPEN_FILE) { + exe_file_allow_write_access(e->interp_file); filp_close(e->interp_file, NULL); + } kfree(e); } } From fa5990ca8fd917003e526036bcc50413edb9722c Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:03 +0200 Subject: [PATCH 198/501] binfmt_misc: use exe_file_deny_write_access() for the interpreter clone For MISC_FMT_OPEN_FILE entries load_misc_binary() clones the registered interpreter file and denies write access to the clone via plain deny_write_access(). The clone is installed as bprm->interpreter and later released by the exec machinery through exe_file_allow_write_access() which skips the i_writecount increment for files with FMODE_FSNOTIFY_HSM set. The deny and allow side can therefore come to different conclusions when pre-content watches are in play: if a pre-content watch is added to the interpreter after registration every subsequent exec through that entry takes a write denial on the clone that is never paired with a write allowance, driving the interpreter inode's i_writecount further down with each exec and leaving the interpreter unwritable even after the entry and all its users are gone. Take the write denial via exe_file_deny_write_access() so both sides of the pairing base their decision on the same file mode, and propagate failure instead of silently ignoring it: an interpreter that is concurrently open for writing now fails the exec with ETXTBSY, exactly like an interpreter freshly opened via open_exec() would. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-2-a162f7cb58d6@kernel.org Fixes: 0357ef03c94e ("fs: don't block write during exec on pre-content watched files") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index adab06d18550..bf7d6b975825 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -249,8 +249,14 @@ static int load_misc_binary(struct linux_binprm *bprm) if (fmt->flags & MISC_FMT_OPEN_FILE) { interp_file = file_clone_open(fmt->interp_file); - if (!IS_ERR(interp_file)) - deny_write_access(interp_file); + if (!IS_ERR(interp_file)) { + int err = exe_file_deny_write_access(interp_file); + + if (err) { + fput(interp_file); + interp_file = ERR_PTR(err); + } + } } else { interp_file = open_exec(fmt->interpreter); } From 8e85d50ba1117fd446bf9a250bd8a97d48384bdc Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Fri, 10 Jul 2026 11:33:04 +0200 Subject: [PATCH 199/501] binfmt_misc: reject a flag character as the field delimiter The registration string starts with a user chosen delimiter that separates the individual fields. So that the field parsers terminate even on a truncated string create_entry() pads the buffer with that same delimiter: memset(buf + count, del, 8); Most fields are scanned for the delimiter with strchr()/scanarg() and happily stop on the padding. The flags field is different: instead of scanning for the delimiter check_special_flags() consumes the flag characters 'P', 'O', 'C' and 'F' and stops at the first byte that is none of them, relying on the trailing delimiter to end the scan. If the delimiter is itself a flag character the padding no longer acts as a terminator. The scan swallows all eight padding bytes and keeps reading past the end of the allocation until it hits a byte that is not a flag character. For example registering PaPEPPxPPiP with 'P' as the delimiter (name "a", type extension, magic "x", interpreter "i", empty flags) leaves the flag scan running off the end of the buffer. The registration is rejected in the end because the parser does not stop exactly at buf + count, but only after the out of bounds read has already happened. With an unlucky allocation layout the scan can walk into an unmapped page; under KASAN it is reported as a slab out of bounds read. binfmt_misc mounts are available to unprivileged users in a user namespace so the read is reachable without privileges. Reject a delimiter that is one of the flag characters up front. Such a registration was always rejected anyway, only after the out of bounds read, so no valid registration string changes meaning. Link: https://patch.msgid.link/20260710-work-binfmt_misc-locking-v3-3-a162f7cb58d6@kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index bf7d6b975825..a73a37b8a013 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -384,6 +384,10 @@ static Node *create_entry(const char __user *buffer, size_t count) pr_debug("register: delim: %#x {%c}\n", del, del); + /* A flag-char delimiter runs the flag scan off the buffer. */ + if (del == 'P' || del == 'O' || del == 'C' || del == 'F') + goto einval; + /* Pad the buffer with the delim to simplify parsing below. */ memset(buf + count, del, 8); From b8206f516fe7cbe785cf44bf09c17c438d7c3cad Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 28 Jul 2026 15:48:10 +0200 Subject: [PATCH 200/501] binfmt_misc: don't leak the user namespace when the mount fails bm_get_tree() takes a reference to the user namespace and hands it to get_tree_keyed() as the sget key. sget_fc() moves that reference into sb->s_fs_info and clears fc->s_fs_info, so from that point on the superblock owns it and bm_free() doesn't see it anymore. The superblock drops it in ->put_super(). But generic_shutdown_super() only calls ->put_super() from inside the if (sb->s_root) branch, so nothing releases it when bm_fill_super() fails: - The kzalloc_obj() failure leaves s_root NULL and the whole branch is skipped. - A simple_fill_super() failure in the file loop leaves s_root set, but s_op still points at simple_super_operations, which has no ->put_super(). bm_fill_super() installs s_ops only once simple_fill_super() returned success, and installing it earlier wouldn't help either because simple_fill_super() overwrites s_op. Either way vfs_get_super() calls deactivate_locked_super() and the reference is gone for good. binfmt_misc mounts are available in a user namespace and both the inode and the dentry cache are SLAB_ACCOUNT, so an unprivileged caller under a tight memory cgroup can fail simple_fill_super() on demand and leak one user namespace per attempt. Drop the reference in ->kill_sb() instead, which runs unconditionally, the same way nfsd and rpc_pipefs release their keyed s_fs_info. That also stops ->put_super() from clearing s_fs_info while the superblock is still on @fs_supers. generic_shutdown_super() leaves it there on purpose so that sget_fc() keeps finding it until kill_sb() has run, but a NULL s_fs_info makes test_keyed_super() miss it, so a concurrent mount for the same user namespace skips the grab_super() wait and creates a second superblock for a namespace that is still being torn down. Link: https://patch.msgid.link/20260728-work-binfmt_misc-usernsleak-v1-1-dbd8d5e626e7@kernel.org Fixes: 21ca59b365c0 ("binfmt_misc: enable sandboxed mounts") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) --- fs/binfmt_misc.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/fs/binfmt_misc.c b/fs/binfmt_misc.c index a73a37b8a013..c97f10b48b5b 100644 --- a/fs/binfmt_misc.c +++ b/fs/binfmt_misc.c @@ -921,18 +921,9 @@ static const struct file_operations bm_status_operations = { /* Superblock handling */ -static void bm_put_super(struct super_block *sb) -{ - struct user_namespace *user_ns = sb->s_fs_info; - - sb->s_fs_info = NULL; - put_user_ns(user_ns); -} - static const struct super_operations s_ops = { .statfs = simple_statfs, .evict_inode = bm_evict_inode, - .put_super = bm_put_super, }; static int bm_fill_super(struct super_block *sb, struct fs_context *fc) @@ -990,13 +981,12 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc) /* * When the binfmt_misc superblock for this userns is shutdown * ->enabled might have been set to false and we don't reinitialize - * ->enabled again in put_super() as someone might already be mounting - * binfmt_misc again. It also would be pointless since by the time - * ->put_super() is called we know that the binary type list for this - * bintfmt_misc mount is empty making load_misc_binary() return - * -ENOEXEC independent of whether ->enabled is true. Instead, if - * someone mounts binfmt_misc for the first time or again we simply - * reset ->enabled to true. + * ->enabled again during shutdown as someone might already be mounting + * binfmt_misc again. It also would be pointless since by then we know + * that the binary type list for this binfmt_misc mount is empty making + * load_misc_binary() return -ENOEXEC independent of whether ->enabled + * is true. Instead, if someone mounts binfmt_misc for the first time or + * again we simply reset ->enabled to true. */ misc->enabled = true; @@ -1022,6 +1012,14 @@ static const struct fs_context_operations bm_context_ops = { .get_tree = bm_get_tree, }; +static void bm_kill_sb(struct super_block *sb) +{ + struct user_namespace *user_ns = sb->s_fs_info; + + kill_anon_super(sb); + put_user_ns(user_ns); +} + static int bm_init_fs_context(struct fs_context *fc) { fc->ops = &bm_context_ops; @@ -1038,7 +1036,7 @@ static struct file_system_type bm_fs_type = { .name = "binfmt_misc", .init_fs_context = bm_init_fs_context, .fs_flags = FS_USERNS_MOUNT, - .kill_sb = kill_anon_super, + .kill_sb = bm_kill_sb, }; MODULE_ALIAS_FS("binfmt_misc"); From 00d86dd5c2034e0e139e4806137b3b43e07ddd83 Mon Sep 17 00:00:00 2001 From: Carlos Song Date: Mon, 25 May 2026 11:04:00 +0800 Subject: [PATCH 201/501] i2c: imx: mark I2C adapter when hardware is powered down On some i.MX platforms, certain I2C client drivers keep a periodic workqueue which continues to trigger I2C transfers. During system suspend/resume, there exists a time window between: - suspend_noirq and the system entering suspend - the system starting to resume and resume_noirq In this window, the I2C controller resources such as clock and pinctrl may already be disabled or not yet restored. If a workqueue triggers an I2C transfer in this period, the driver attempts to access I2C registers while the hardware resources are unavailable, which may lead to system hang. Mark the I2C adapter as suspended during noirq suspend and block new transfers until resume, ensuring that I2C transfers are only issued when hardware resources are available. Fixes: 358025ac091e ("i2c: imx: make controller available until system suspend_noirq() and from resume_noirq()") Signed-off-by: Carlos Song Cc: # v6.14+ Reviewed-by: Frank Li Acked-by: Oleksij Rempel Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260525030400.3182911-1-carlos.song@oss.nxp.com --- drivers/i2c/busses/i2c-imx.c | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d5e6e2eca3b3..d549d630b41a 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1952,6 +1952,47 @@ static int i2c_imx_runtime_resume(struct device *dev) return 0; } +static int __maybe_unused i2c_imx_suspend_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + i2c_mark_adapter_suspended(&i2c_imx->adapter); + + /* + * Cancel the slave timer before powering down to prevent + * i2c_imx_slave_timeout() from accessing hardware registers + * while the clock is disabled. + */ + hrtimer_cancel(&i2c_imx->slave_timer); + + ret = pm_runtime_force_suspend(dev); + if (ret) { + i2c_mark_adapter_resumed(&i2c_imx->adapter); + if (i2c_imx->slave) { + hrtimer_forward_now(&i2c_imx->slave_timer, I2C_IMX_CHECK_DELAY); + hrtimer_restart(&i2c_imx->slave_timer); + } + return ret; + } + + return 0; +} + +static int __maybe_unused i2c_imx_resume_noirq(struct device *dev) +{ + struct imx_i2c_struct *i2c_imx = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + i2c_mark_adapter_resumed(&i2c_imx->adapter); + + return 0; +} + static int i2c_imx_suspend(struct device *dev) { /* @@ -1985,8 +2026,8 @@ static int i2c_imx_resume(struct device *dev) } static const struct dev_pm_ops i2c_imx_pm_ops = { - NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) + NOIRQ_SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend_noirq, + i2c_imx_resume_noirq) SYSTEM_SLEEP_PM_OPS(i2c_imx_suspend, i2c_imx_resume) RUNTIME_PM_OPS(i2c_imx_runtime_suspend, i2c_imx_runtime_resume, NULL) }; From bba13ad17b1a11b3f1ed9b3a5d556191d7755a59 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Mon, 27 Jul 2026 10:36:59 +0200 Subject: [PATCH 202/501] of/address: Fix NULL bus dereference in of_pci_range_parser_one() The bus matching rework made of_match_bus() return NULL for nodes with ranges/dma-ranges but no local #address-cells. parser_init() stored that NULL bus, and the range iterator later dereferenced it. Reject such nodes in parser_init(), leaving an explicit empty iterator for callers that ignore the init return, and make of_dma_get_max_cpu_address() honour the init failure so a rejected node cannot clamp the DMA limit. Fixes: 64ee3cf096ac ("of/address: Rework bus matching to avoid warnings") Cc: stable@vger.kernel.org Signed-off-by: Carlo Caione Link: https://patch.msgid.link/20260727-of-range-parser-null-bus-v3-1-be01b708a4ce@baylibre.com Signed-off-by: Rob Herring (Arm) --- drivers/of/address.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/drivers/of/address.c b/drivers/of/address.c index cf4aab11e9b1..499d37ceae21 100644 --- a/drivers/of/address.c +++ b/drivers/of/address.c @@ -753,6 +753,7 @@ EXPORT_SYMBOL(of_property_read_reg); static int parser_init(struct of_pci_range_parser *parser, struct device_node *node, const char *name) { + const __be32 *range; int rlen; parser->node = node; @@ -761,12 +762,20 @@ static int parser_init(struct of_pci_range_parser *parser, parser->ns = of_bus_n_size_cells(node); parser->dma = !strcmp(name, "dma-ranges"); parser->bus = of_match_bus(node); + parser->range = NULL; + parser->end = NULL; - parser->range = of_get_property(node, name, &rlen); - if (parser->range == NULL) + range = of_get_property(node, name, &rlen); + if (!range) return -ENOENT; - parser->end = parser->range + rlen / sizeof(__be32); + if (!parser->bus || + !OF_CHECK_COUNTS(parser->na, parser->ns) || + !OF_CHECK_ADDR_COUNT(parser->pna)) + return -EINVAL; + + parser->range = range; + parser->end = range + rlen / sizeof(__be32); return 0; } @@ -792,7 +801,7 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, int na = parser->na; int ns = parser->ns; int np = parser->pna + na + ns; - int busflag_na = parser->bus->flag_cells; + int busflag_na; if (!range) return NULL; @@ -800,6 +809,8 @@ struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser, if (!parser->range || parser->range + np > parser->end) return NULL; + busflag_na = parser->bus->flag_cells; + range->flags = parser->bus->get_flags(parser->range); range->bus_addr = of_read_number(parser->range + busflag_na, na - busflag_na); @@ -976,8 +987,7 @@ phys_addr_t __init of_dma_get_max_cpu_address(struct device_node *np) np = of_root; ranges = of_get_property(np, "dma-ranges", &len); - if (ranges && len) { - of_dma_range_parser_init(&parser, np); + if (ranges && len && !of_dma_range_parser_init(&parser, np)) { for_each_of_range(&parser, &range) if (range.cpu_addr + range.size > cpu_end) cpu_end = range.cpu_addr + range.size - 1; From a19038a200f18d9e74ac30081797917d0886e16b Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Tue, 28 Jul 2026 08:41:40 -0700 Subject: [PATCH 203/501] hwmon: (pmbus) Fix return value from pmbus_update_byte_data() pmbus_update_byte_data() is supposed to return a negative error code or 0. However, if no change is made to the register, it actually returns the register value. This can result in problems if the calling code explicitly expects to see an error code or 0. Fix it to return 0 on success or the error code as expected. Fixes: 11c119986f270 ("hwmon: (pmbus) add helpers for byte write and read modify write") Signed-off-by: Guenter Roeck --- drivers/hwmon/pmbus/pmbus_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 0081f16c3a95..5567d37f13fe 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -513,7 +513,7 @@ int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, if (tmp != rv) rv = _pmbus_write_byte_data(client, page, reg, tmp); - return rv; + return rv < 0 ? rv : 0; } EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, "PMBUS"); From f495b6c4c8594122918552c9be2b51eb71647cd9 Mon Sep 17 00:00:00 2001 From: Norbert Szetei Date: Tue, 28 Jul 2026 14:50:01 +0200 Subject: [PATCH 204/501] ALSA: pcm: wake linked drain waiters on unlink snd_pcm_drain() on a linked stream parks an on-stack wait entry on the drained peer's runtime->sleep, and after schedule_timeout() removes it only if that peer is still found in the caller's group. If group membership changes during the wait and the sleep ends by signal or timeout (so autoremove_wake_function() does not run), finish_wait() is skipped and snd_pcm_drain() returns with the entry still queued on that stream's sleep list; a later wake_up() then walks a freed stack frame. This is reachable by unlinking either the drained or the draining stream. Unlike the close path (snd_pcm_drop() -> snd_pcm_post_stop()), snd_pcm_unlink() never wakes the sleep queues. Wake every group member under the group lock before the membership change, so a linked drainer is released and drops its entry while the streams are still grouped. The window was opened when snd_pcm_link_rwsem stopped being held across the wait and the removal became conditional on group membership (see Fixes). The later switch to finish_wait() kept that conditional removal, so the signal/timeout case remained. Fixes: f57f3df03a8e ("ALSA: pcm: More fine-grained PCM link locking") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-5 Signed-off-by: Norbert Szetei Link: https://patch.msgid.link/A0705100-D10B-4286-9980-0142ABEEAD51@doyensec.com Signed-off-by: Takashi Iwai --- sound/core/pcm_native.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index 7dc0060617f1..c9d0c2bb55b2 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -2367,6 +2367,7 @@ static void relink_to_local(struct snd_pcm_substream *substream) static int snd_pcm_unlink(struct snd_pcm_substream *substream) { + struct snd_pcm_substream *s; struct snd_pcm_group *group; bool nonatomic = substream->pcm->nonatomic; bool do_free = false; @@ -2379,6 +2380,12 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream) group = substream->group; snd_pcm_group_lock_irq(group, nonatomic); + /* release drain waiters before changing membership, else snd_pcm_drain() + * leaves its on-stack wait entry queued on a member's sleep list + */ + snd_pcm_group_for_each_entry(s, substream) + wake_up(&s->runtime->sleep); + relink_to_local(substream); refcount_dec(&group->refs); From cee046679655b4822f76efc9658f19efee9ac979 Mon Sep 17 00:00:00 2001 From: Robert Abrahamse Date: Tue, 28 Jul 2026 16:03:14 +0200 Subject: [PATCH 205/501] ALSA: usb-audio: Add quirk for Corsair Virtuoso (later revision) Add USB mixer mapping quirk for later revisions of the Corsair Virtuoso headset with USB IDs 0x1b1c:0x0a43 (wired) and 0x1b1c:0x0a44 (wireless). These devices exhibit the same mixer label collision as earlier Virtuoso variants: all controls are labelled "Headset", causing applications like PulseAudio to move the sidetone control instead of the main playback volume. Signed-off-by: Robert Abrahamse Link: https://patch.msgid.link/20260728140314.11601-1-denobyte2@gmail.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_maps.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c index 4d8dca04f87d..ce27fc871f51 100644 --- a/sound/usb/mixer_maps.c +++ b/sound/usb/mixer_maps.c @@ -638,6 +638,16 @@ static const struct usbmix_ctl_map usbmix_ctl_maps[] = { .id = USB_ID(0x1b1c, 0x0a42), .map = corsair_virtuoso_map, }, + { + /* Corsair Virtuoso (wired mode, later revision) */ + .id = USB_ID(0x1b1c, 0x0a43), + .map = corsair_virtuoso_map, + }, + { + /* Corsair Virtuoso (wireless mode, later revision) */ + .id = USB_ID(0x1b1c, 0x0a44), + .map = corsair_virtuoso_map, + }, { /* Corsair HS80 RGB Wireless (wired mode) */ .id = USB_ID(0x1b1c, 0x0a6a), From 7678e81498e20e78d7d5f64e552cd153117c1d66 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Tue, 28 Jul 2026 11:51:59 +0800 Subject: [PATCH 206/501] PCI: imx6: Fix i.MX6Q/DL boot hang caused by improper PHY power sequencing commit 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") introduced a boot hang on i.MX6Q/DL variants by reordering imx_pcie_host_init() to call imx6q_pcie_enable_ref_clk() (which powered up the PHY) before imx6q_pcie_core_reset() (which powered it back down). Before 610fa91d9863, the sequence was: 1. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 2. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 3. Link training starts with PHY powered up (TEST_PD cleared) 4. Link training succeeds After 610fa91d9863, the sequence became: 1. imx_pcie_clk_enable() - power up PHY (clear TEST_PD), set REF_CLK_EN 2. imx_pcie_assert_core_reset() - power down PHY (set TEST_PD), set REF_CLK_EN 3. imx_pcie_deassert_core_reset() - does nothing 4. Link training starts with PHY powered down (TEST_PD set) 5. Link training fails and boot hangs when PHY register accesses hang To fix this: - Remove TEST_PD PHY power control from imx6q_pcie_enable_ref_clk() - Remove REF_CLK_EN control from imx6q_pcie_core_reset() - Add TEST_PD PHY power control to imx6qp_pcie_core_reset(), which previously relied on imx6q_pcie_enable_ref_clk() to power up the PHY by clearing TEST_PD - Clear TEST_PD to power on PHY in imx_pcie_deassert_core_reset() These changes together ensure the correct sequence: 1. REF_CLK_EN set in clk_enable() (TEST_PD untouched) 2. TEST_PD set in assert_core_reset() (PHY power off) 3. TEST_PD cleared in deassert_core_reset() (PHY power on) 4. Link training starts with proper PHY state The i.MX6Q/DL PCIe PHY requires approximately 120us between TEST_PD de-assertion and link training start. Add usleep_range(200, 500) in imx6q_pcie_core_reset() after clearing TEST_PD to satisfy this requirement. Add explicit imx_pcie_assert_core_reset() calls in error paths and host_exit() to ensure no power leak. Fixes: 610fa91d9863 ("PCI: imx6: Assert PERST# before enabling regulators") Reported-by: Leonardo Costa Closes: https://lore.kernel.org/lkml/20260629143439.361560-1-leoreis.costa@gmail.com/ Signed-off-by: Richard Zhu Signed-off-by: Bjorn Helgaas Tested-by: Leonardo Costa Reviewed-by: Frank Li Link: https://patch.msgid.link/20260728035159.2702021-1-hongxing.zhu@oss.nxp.com --- drivers/pci/controller/dwc/pci-imx6.c | 43 +++++++++++++++------------ 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index dba27eac6bff..f7389b5437df 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -680,21 +680,12 @@ static int imx_pcie_attach_pd(struct device *dev) static int imx6q_pcie_enable_ref_clk(struct imx_pcie *imx_pcie, bool enable) { - if (enable) { - /* power up core phy and enable ref clock */ - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - /* - * The async reset input need ref clock to sync internally, - * when the ref clock comes after reset, internal synced - * reset time is too short, cannot meet the requirement. - * Add a ~10us delay here. - */ - usleep_range(10, 100); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - } else { - regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - } + if (enable) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_REF_CLK_EN); return 0; } @@ -825,8 +816,16 @@ static int imx6sx_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + regmap_update_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_SW_RST, assert ? IMX6Q_GPR1_PCIE_SW_RST : 0); + if (!assert) usleep_range(200, 500); @@ -835,11 +834,15 @@ static int imx6qp_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) static int imx6q_pcie_core_reset(struct imx_pcie *imx_pcie, bool assert) { - if (!assert) - return 0; + if (assert) + regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); + else + regmap_clear_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, + IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_TEST_PD); - regmap_set_bits(imx_pcie->iomuxc_gpr, IOMUXC_GPR1, IMX6Q_GPR1_PCIE_REF_CLK_EN); + if (!assert) + usleep_range(200, 500); return 0; } @@ -1445,6 +1448,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp) return 0; err_phy_off: + imx_pcie_assert_core_reset(imx_pcie); phy_power_off(imx_pcie->phy); err_phy_exit: phy_exit(imx_pcie->phy); @@ -1466,6 +1470,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp) struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct imx_pcie *imx_pcie = to_imx_pcie(pci); + imx_pcie_assert_core_reset(imx_pcie); if (imx_pcie->phy) { if (phy_power_off(imx_pcie->phy)) dev_err(pci->dev, "unable to power off PHY\n"); From 8fd62901d6bf03f274a49dd0060793cc07dd51b0 Mon Sep 17 00:00:00 2001 From: Stanislaw Pal Date: Mon, 27 Jul 2026 18:32:16 +0200 Subject: [PATCH 207/501] spi: spi-qpic-snand: write the feature value before executing SET_FEATURE qcom_spi_send_cmdaddr() programs NAND_FLASH_CMD/NAND_EXEC_CMD and submits the descriptors, which makes the controller execute the command immediately. For SPINAND_SET_FEATURE the value to be written is only placed into NAND_FLASH_FEATURES afterwards, by qcom_spi_io_op(), in a second submission - so the chip is programmed with whatever that register happened to hold from a previous operation, and the intended value is only applied by the *next* SET_FEATURE. Measured on a TP-Link Archer AX55 v1 (IPQ5018, ESMT F50L1G41LB): writing 0x40 to the configuration register (0xb0) leaves the chip at 0x00, and the subsequent write of 0x00 leaves it at 0x40 - every write lands one operation late. This stayed unnoticed until v6.18 added SPI-NAND OTP support together with OTP entries for ESMT chips. spinand_otp_rw() enables OTP mode, reads, and disables it again, and mtd_otp_nvmem_add() does this during MTD registration. With the off-by-one, the "disable" write actually applies the previously requested value, so CFG_OTP_ENABLE ends up set: the chip stays in OTP mode, every subsequent array read returns the OTP area instead of the array (UBI reports an empty device) and all writes fail with -EIO because the OTP area is write protected. On this board that makes the whole flash unusable and the device unbootable. Write the feature value into NAND_FLASH_FEATURES as part of the same transaction, before NAND_EXEC_CMD. While at it, copy only the bytes the operation actually carries - the previous code dereferenced a 4-byte pointer on a one-byte buffer (spinand->scratchbuf). With this patch the flash contents read back bit-identical to a known-good dump of the same board taken under the vendor firmware (md5-verified across partitions), and writes work. Fixes: 7304d1909080 ("spi: spi-qpic: add driver for QCOM SPI NAND flash Interface") Cc: stable@vger.kernel.org Signed-off-by: Stanislaw Pal Reviewed-by: Md Sadre Alam Link: https://patch.msgid.link/20260727163216.109938-1-kuncy7@gmail.com Signed-off-by: Mark Brown --- drivers/spi/spi-qpic-snand.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-qpic-snand.c b/drivers/spi/spi-qpic-snand.c index 66f2d1b78ade..b6c58d9cfe14 100644 --- a/drivers/spi/spi-qpic-snand.c +++ b/drivers/spi/spi-qpic-snand.c @@ -1358,6 +1358,22 @@ static int qcom_spi_send_cmdaddr(struct qcom_nand_controller *snandc, snandc->regs->addr0 = cpu_to_le32(op->addr.val); snandc->regs->addr1 = cpu_to_le32(0); + /* + * The feature value has to reach NAND_FLASH_FEATURES before the + * command is executed, otherwise the controller programs the chip + * with whatever the register happened to hold from a previous + * operation. + */ + if (opcode == SPINAND_SET_FEATURE) { + u32 ftr = 0; + + memcpy(&ftr, op->data.buf.out, + min_t(size_t, op->data.nbytes, sizeof(ftr))); + snandc->regs->flash_feature = cpu_to_le32(ftr); + qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, + NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); + } + qcom_write_reg_dma(snandc, &snandc->regs->cmd, NAND_FLASH_CMD, 3, NAND_BAM_NEXT_SGL); qcom_write_reg_dma(snandc, &snandc->regs->exec, NAND_EXEC_CMD, 1, NAND_BAM_NEXT_SGL); @@ -1395,10 +1411,8 @@ static int qcom_spi_io_op(struct qcom_nand_controller *snandc, const struct spi_ copy_ftr = true; break; case SPINAND_SET_FEATURE: - snandc->regs->flash_feature = cpu_to_le32(*(u32 *)op->data.buf.out); - qcom_write_reg_dma(snandc, &snandc->regs->flash_feature, - NAND_FLASH_FEATURES, 1, NAND_BAM_NEXT_SGL); - break; + /* fully handled by qcom_spi_send_cmdaddr() */ + return 0; case SPINAND_PROGRAM_EXECUTE: case SPINAND_WRITE_EN: case SPINAND_RESET: From 98f2e9e6d6f91a6abb43f166b244b428ba85fa2b Mon Sep 17 00:00:00 2001 From: Jonas Gorski Date: Fri, 17 Jul 2026 10:55:07 +0200 Subject: [PATCH 208/501] i2c: iproc: reset bus after timeout if START_BUSY is stuck If a transaction times out, the START_BUSY signal can stay up, and subsequent transactaction attempts will fail as the bus is still considered busy. I can easily trigger this by attempting to read from an address with no device, e.g. when running i2cdetect. After the first read times out, all subsequent read attempts return busy. To get to a working state again, the controller needs to be reset to clear the START_BUSY signal. So check for START_BUSY still asserted on a timeout, and do reset in case it is, This is also done by the original non-upstream iproc-smbus driver implementation [1]. Works around situations like: bcm-iproc-2c 1803b000.i2c: transaction timed out bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy bcm-iproc-2c 1803b000.i2c: bus is busy ... where the bus never recovers after a timeout. [1] https://github.com/opencomputeproject/onie/blob/master/patches/kernel/3.2.69/driver-iproc-smbus.patch Fixes: e6e5dd3566e0 ("i2c: iproc: Add Broadcom iProc I2C Driver") Signed-off-by: Jonas Gorski Cc: # v4.0+ Acked-by: Ray Jui Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260717085507.34209-1-jonas.gorski@bisdn.de --- drivers/i2c/busses/i2c-bcm-iproc.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index b5629cffe99b..86ca4c2221c4 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -803,6 +803,17 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c, } if (!time_left && !iproc_i2c->xfer_is_done) { + /* + * The controller may fail to clear START_BUSY after a timeout, + * reset the controller to recover in that case. + */ + if (!!(iproc_i2c_rd_reg(iproc_i2c, M_CMD_OFFSET) & + BIT(M_CMD_START_BUSY_SHIFT))) { + bcm_iproc_i2c_enable_disable(iproc_i2c, false); + bcm_iproc_i2c_init(iproc_i2c); + bcm_iproc_i2c_enable_disable(iproc_i2c, true); + } + /* flush both TX/RX FIFOs */ val = BIT(M_FIFO_RX_FLUSH_SHIFT) | BIT(M_FIFO_TX_FLUSH_SHIFT); iproc_i2c_wr_reg(iproc_i2c, M_FIFO_CTRL_OFFSET, val); From d64ec362c369bbc33833f7936d5f3a706b0d5c45 Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:28 +0800 Subject: [PATCH 209/501] i2c: imx: Fix slave registration race and error handling In i2c_imx_reg_slave(), the slave pointer was assigned before pm_runtime_resume_and_get(). If pm_runtime_resume_and_get() failed, the error path returned without clearing i2c_imx->slave, leaving it non-NULL and causing all subsequent registration attempts to fail with -EBUSY. Additionally, because this driver uses a shared IRQ, the interrupt handler i2c_imx_isr() can execute concurrently and, after acquiring slave_lock, dereference i2c_imx->slave. The previous fix attempt added a lockless i2c_imx->slave = NULL on the error path, but that could race with the ISR under the lock and still cause a NULL pointer dereference. Fix both issues by deferring the assignment of i2c_imx->slave and i2c_imx->last_slave_event to after a successful resume, and by performing the assignment inside the slave_lock critical section. This guarantees that the slave pointer is never left stale on the error path and is always valid when observed by the interrupt handler. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Reviewed-by: Frank Li Acked-by: Carlos Song Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-2-liem16213@gmail.com --- drivers/i2c/busses/i2c-imx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index d549d630b41a..49859ee494c6 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -930,9 +930,6 @@ static int i2c_imx_reg_slave(struct i2c_client *client) if (i2c_imx->slave) return -EBUSY; - i2c_imx->slave = client; - i2c_imx->last_slave_event = I2C_SLAVE_STOP; - /* Resume */ ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent); if (ret < 0) { @@ -940,6 +937,11 @@ static int i2c_imx_reg_slave(struct i2c_client *client) return ret; } + scoped_guard(spinlock_irqsave, &i2c_imx->slave_lock) { + i2c_imx->slave = client; + i2c_imx->last_slave_event = I2C_SLAVE_STOP; + } + i2c_imx_slave_init(i2c_imx); return 0; From 6ac7702b6cc2b94aaed9ef2d95bfbefcdc90061f Mon Sep 17 00:00:00 2001 From: Liem Date: Mon, 29 Jun 2026 10:38:29 +0800 Subject: [PATCH 210/501] i2c: imx: Cancel hrtimer before clearing slave pointer In i2c_imx_unreg_slave(), the slave pointer is set to NULL after disabling interrupts. However, a pending interrupt might already have started the hrtimer (i2c_imx_slave_timeout) before the pointer was cleared. If the hrtimer fires after i2c_imx->slave is set to NULL, the timer callback i2c_imx_slave_finish_op() will call i2c_imx_slave_event() with a NULL slave pointer, which results in a use-after-free / NULL pointer dereference. Fix by canceling the hrtimer and waiting for it to complete after disabling interrupts, before clearing the slave pointer. Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver") Signed-off-by: Liem Cc: # v5.11+ Acked-by: Carlos Song Reviewed-by: Frank Li Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260629023829.152651-3-liem16213@gmail.com --- drivers/i2c/busses/i2c-imx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 49859ee494c6..9477d814fde9 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -960,6 +960,7 @@ static int i2c_imx_unreg_slave(struct i2c_client *client) i2c_imx_reset_regs(i2c_imx); + hrtimer_cancel(&i2c_imx->slave_timer); i2c_imx->slave = NULL; /* Suspend */ From b08c9857aa1f5f3a81d375d6d4bb1d8b92f22ebc Mon Sep 17 00:00:00 2001 From: Wenmeng Liu Date: Thu, 25 Jun 2026 17:42:45 +0800 Subject: [PATCH 211/501] i2c: qcom-cci: drop custom suspend/resume and rely on runtime PM helpers cci_resume() unconditionally calls cci_resume_runtime() regardless of the runtime PM state. If the device is already runtime-suspended before system suspend, the clock is re-enabled while runtime_status remains RPM_SUSPENDED. As a result, pm_request_autosuspend() does not arm the timer, leaving the clock permanently enabled. Fixes: e517526195de ("i2c: Add Qualcomm CCI I2C driver") Signed-off-by: Wenmeng Liu Cc: # v5.8+ Reviewed-by: Vladimir Zapolskiy Reviewed-by: Konrad Dybcio Reviewed-by: Loic Poulain Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260625-cci-v1-1-a100cda673ce@oss.qualcomm.com --- drivers/i2c/busses/i2c-qcom-cci.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-cci.c b/drivers/i2c/busses/i2c-qcom-cci.c index 4d64895a9e9e..bdeda3979c48 100644 --- a/drivers/i2c/busses/i2c-qcom-cci.c +++ b/drivers/i2c/busses/i2c-qcom-cci.c @@ -492,24 +492,8 @@ static int __maybe_unused cci_resume_runtime(struct device *dev) return 0; } -static int __maybe_unused cci_suspend(struct device *dev) -{ - if (!pm_runtime_suspended(dev)) - return cci_suspend_runtime(dev); - - return 0; -} - -static int __maybe_unused cci_resume(struct device *dev) -{ - cci_resume_runtime(dev); - pm_request_autosuspend(dev); - - return 0; -} - static const struct dev_pm_ops qcom_cci_pm = { - SET_SYSTEM_SLEEP_PM_OPS(cci_suspend, cci_resume) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) SET_RUNTIME_PM_OPS(cci_suspend_runtime, cci_resume_runtime, NULL) }; From a8c171c107c0b61a5e7e10cedab0fb72aeaf640d Mon Sep 17 00:00:00 2001 From: Mauricio Faria de Oliveira Date: Thu, 23 Jul 2026 20:08:04 -0300 Subject: [PATCH 212/501] x86/boot: Add volatile, clobbers and zero-length test in memcmp() Add the volatile qualifier and clobbers parameter to prevent bugs with instruction reordering and optimization. Also add TEST for the zero-length case to set ZF, as, if the count register is zero, the REPE prefix does not run the CMPSB instruction, leaving the ZF flag undetermined. [ bp: Add a comment about the len==0 case. ] Fixes: 62bd0337d0c4 ("Top header file for new x86 setup code") Closes: https://sashiko.dev/#/patchset/20260701-pvh-kasan-inline-v6-0-ba99045dfa9f%40igalia.com Suggested-by: Borislav Petkov Signed-off-by: Mauricio Faria de Oliveira Signed-off-by: Borislav Petkov (AMD) Link: https://lore.kernel.org/all/20260721-pvh-kasan-inline-v7-2-38979a50cef0@igalia.com --- arch/x86/boot/string.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/arch/x86/boot/string.c b/arch/x86/boot/string.c index ac0f900ebc47..1632d40e1f54 100644 --- a/arch/x86/boot/string.c +++ b/arch/x86/boot/string.c @@ -32,8 +32,15 @@ int memcmp(const void *s1, const void *s2, size_t len) { bool diff; - asm("repe cmpsb" - : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len)); + + /* + * Make sure ZF is properly set in the len==0 case because in it, + * RCX==0 and the REPE; CMPSB won't get executed. + */ + asm volatile("test %3, %3\n\t" + "repe cmpsb" + : "=@ccnz" (diff), "+D" (s1), "+S" (s2), "+c" (len) + : : "cc", "memory"); return diff; } From 35699ae99deabdb9100c2e6b5365c996712846e7 Mon Sep 17 00:00:00 2001 From: Suresh Guttula Date: Tue, 14 Jul 2026 20:20:39 +0530 Subject: [PATCH 213/501] drm/amdgpu : update mmhub eco sec lvl for vcn5_3 This patch requests PSP to set the sec lvl for vcn and jpeg. Signed-off-by: Suresh Guttula Reviewed-by: McRae Geoffrey Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 4c8b8472f85a730a6853ab68474f210143f42b5a) --- drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 27 ++++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h | 1 + drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c | 24 +++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h | 1 + drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c | 26 +++++++++++++++++++++++ 5 files changed, 79 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index e0c0d7872e45..ca80b6194cac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -1222,6 +1222,33 @@ int psp_memory_partition(struct psp_context *psp, int mode) return ret; } +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int ret; + struct psp_context *psp = &adev->psp; + struct psp_gfx_cmd_resp *cmd = acquire_psp_cmd_buf(psp); + + cmd->cmd_id = GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL; + + ret = psp_cmd_submit_buf(psp, NULL, cmd, psp->fence_buf_mc_addr); + if (ret) { + dev_err(psp->adev->dev, + "PSP request failed to set mmuhub eco sec level with ret=%d\n", ret); + release_psp_cmd_buf(psp); + return ret; + } + + if (cmd->resp.status) { + dev_err(psp->adev->dev, + "MMHUB ECO SEC LEVEL command 0x%x failed, PSP response status: 0x%X\n", + cmd->cmd_id, cmd->resp.status); + ret = -EIO; + } + release_psp_cmd_buf(psp); + + return ret; +} + static int psp_ptl_fmt_verify(struct psp_context *psp, enum amdgpu_ptl_fmt fmt, uint32_t *ptl_fmt) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h index 2d838b1b2b11..3d7744d60fac 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.h @@ -656,5 +656,6 @@ int amdgpu_psp_reg_program_no_ring(struct psp_context *psp, uint32_t val, void amdgpu_psp_debugfs_init(struct amdgpu_device *adev); int amdgpu_psp_get_fw_type(struct amdgpu_firmware_info *ucode, enum psp_gfx_fw_type *type); +int psp_set_mmhub_eco_sec_level(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c index e7546816baba..2c3e6d9f52a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/jpeg_v5_3_0.c @@ -398,6 +398,25 @@ static void jpeg_v5_3_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx) WREG32_SOC15(JPEG, inst_idx, regUVD_JPEG_POWER_STATUS, reg_data); } +/** + * jpeg_v5_3_0_set_mmhub_eco_sec_level - set jpeg sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set secure lvl + */ +static int jpeg_v5_3_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program JPEG secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + + return r; +} + /** * jpeg_v5_3_0_start - start JPEG block * @@ -423,6 +442,11 @@ static int jpeg_v5_3_0_start(struct amdgpu_device *adev) if (r) return r; + /* program JPEG secure lvl register */ + r = jpeg_v5_3_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + /* JPEG disable CGC */ jpeg_v5_3_0_disable_clock_gating(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h index ac34bac3c839..ad618b78685f 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h +++ b/drivers/gpu/drm/amd/amdgpu/psp_gfx_if.h @@ -110,6 +110,7 @@ enum psp_gfx_cmd_id GFX_CMD_ID_PERF_HW = 0x0000004C, /* performance monitor */ GFX_CMD_ID_FB_FW_RESERV_ADDR = 0x00000050, /* Query FW reservation addr */ GFX_CMD_ID_FB_FW_RESERV_EXT_ADDR = 0x00000051, /* Query FW reservation extended addr */ + GFX_CMD_ID_SET_MMHUB_ECO_SEC_LEVEL = 0x0000005D, /* Set MMHUB ECO sec lvls on VCN block */ }; /* PSP boot config sub-commands */ diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c index 45580e9c4e0c..886e2c2074a9 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v5_0_0.c @@ -795,6 +795,27 @@ static int vcn_v5_0_0_start_dpg_mode(struct amdgpu_vcn_inst *vinst, return 0; } +/** + * vcn_v5_0_0_set_mmhub_eco_sec_level - set vcn sec lvl reg + * + * @adev: amdgpu_device pointer + * + * request psp to set sec lvl + */ +static int vcn_v5_0_0_set_mmhub_eco_sec_level(struct amdgpu_device *adev) +{ + int r = 0; + + if (amdgpu_ip_version(adev, VCN_HWIP, 0) == IP_VERSION(5, 3, 0)) { + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) { + /* Request to PSP to program VCN secure lvl */ + r = psp_set_mmhub_eco_sec_level(adev); + } + } + + return r; +} + /** * vcn_v5_0_0_start - VCN start * @@ -819,6 +840,11 @@ static int vcn_v5_0_0_start(struct amdgpu_vcn_inst *vinst) fw_shared = adev->vcn.inst[i].fw_shared.cpu_addr; + /* program VCN secure lvl register */ + r = vcn_v5_0_0_set_mmhub_eco_sec_level(adev); + if (r) + return r; + if (adev->pg_flags & AMD_PG_SUPPORT_VCN_DPG) return vcn_v5_0_0_start_dpg_mode(vinst, adev->vcn.inst[i].indirect_sram); From 6e7566ba4739dd573c331adde1c96690f7a567bd Mon Sep 17 00:00:00 2001 From: William Palacek Date: Mon, 6 Jul 2026 10:25:58 -0400 Subject: [PATCH 214/501] drm/amdkfd: Add bounds check for CRAT subtype length The CRAT parser validates that the subtype header fits within the image, but does not verify that the advertised subtype length fits. A malformed CRAT table with an oversized length field causes out-of-bounds reads when kfd_parse_subtype() casts the header to specific subtype structures. Add validation that sub_type_hdr + length does not exceed the image boundary before parsing the subtype contents. Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit 48e1d1e6e8798aef0312e68d8e586021b5b3cf4d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_crat.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 2a239f45fc24..6e0df685503d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -1412,6 +1412,15 @@ int kfd_parse_crat_table(void *crat_image, struct list_head *device_list, break; } + /* Validate subtype fits within remaining image */ + if ((char *)sub_type_hdr + sub_type_hdr->length > + (char *)crat_image + image_len) { + pr_warn("CRAT subtype length %u exceeds image bounds\n", + sub_type_hdr->length); + ret = -EINVAL; + break; + } + if (sub_type_hdr->flags & CRAT_SUBTYPE_FLAGS_ENABLED) { ret = kfd_parse_subtype(sub_type_hdr, device_list); if (ret) From 17da8410554906a95bb9ef30acbdf0c5abc1a4ef Mon Sep 17 00:00:00 2001 From: Sunil Khatri Date: Wed, 15 Jul 2026 19:32:56 +0530 Subject: [PATCH 215/501] drm/amdgpu: dont pin wptr bo instead use eviction fence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of pinning the wptr bo attach the eviction fence to the bo to make sure it remains valid all the time. Signed-off-by: Sunil Khatri Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 7264bc10c7c657a54603c7fc058bf8e15f18ce12) --- drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c | 19 +++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/mes_userqueue.c | 17 +++++------------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c index 572f2949cb64..3efe1ae27386 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_userq.c @@ -965,6 +965,7 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr) struct amdgpu_vm *vm = &fpriv->vm; unsigned long key, tmp_key; struct amdgpu_bo_va *bo_va; + struct amdgpu_usermode_queue *queue; struct amdgpu_bo *bo; struct drm_exec exec; struct xarray xa; @@ -1080,6 +1081,24 @@ amdgpu_userq_vm_validate(struct amdgpu_userq_mgr *uq_mgr) dma_fence_wait(bo_va->last_pt_update, false); dma_fence_wait(vm->last_update, false); + xa_for_each(&uq_mgr->userq_xa, tmp_key, queue) { + bo = queue->wptr_obj.obj; + if (!bo) { + ret = -EINVAL; + goto unlock_all; + } + + ret = amdgpu_ttm_alloc_gart(&bo->tbo); + if (unlikely(ret)) { + drm_file_err(uq_mgr->file, + "failed to bind wptr bo to gart on resume, qid=%lu ret=%d\n", + tmp_key, ret); + goto unlock_all; + } + + queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(bo); + } + ret = amdgpu_evf_mgr_rearm(&fpriv->evf_mgr, &exec); if (ret) drm_file_err(uq_mgr->file, "Failed to replace eviction fence\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c index 16625c31bfd3..4e44a581a78a 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_userqueue.c @@ -70,27 +70,23 @@ mes_userq_create_wptr_mapping(struct amdgpu_device *adev, ret = -EINVAL; goto fail_map; } - - /* TODO use eviction fence instead of pinning. */ - ret = amdgpu_bo_pin(wptr_obj->obj, AMDGPU_GEM_DOMAIN_GTT); + /* Keep WPTR BO under eviction-fence control instead of pinning. */ + ret = amdgpu_evf_mgr_attach_fence(&uq_mgr_to_fpriv(uq_mgr)->evf_mgr, wptr_obj->obj); if (ret) { - DRM_ERROR("Failed to pin wptr bo. ret %d\n", ret); + DRM_ERROR("Failed to attach eviction fence to wptr bo. ret %d\n", ret); goto fail_map; } ret = amdgpu_ttm_alloc_gart(&wptr_obj->obj->tbo); if (ret) { - DRM_ERROR("Failed to bind bo to GART. ret %d\n", ret); - goto fail_alloc_gart; + DRM_ERROR("Failed to bind wptr bo to GART. ret %d\n", ret); + goto fail_map; } queue->wptr_obj.gpu_addr = amdgpu_bo_gpu_offset(wptr_obj->obj); drm_exec_fini(&exec); return 0; - -fail_alloc_gart: - amdgpu_bo_unpin(wptr_obj->obj); fail_map: amdgpu_bo_unref(&wptr_obj->obj); fail_lock: @@ -468,9 +464,6 @@ static void mes_userq_mqd_destroy(struct amdgpu_usermode_queue *queue) amdgpu_bo_free_kernel(&queue->mqd.obj, &queue->mqd.gpu_addr, &queue->mqd.cpu_ptr); - amdgpu_bo_reserve(queue->wptr_obj.obj, true); - amdgpu_bo_unpin(queue->wptr_obj.obj); - amdgpu_bo_unreserve(queue->wptr_obj.obj); amdgpu_bo_unref(&queue->wptr_obj.obj); } From e3a721753f60c1d4643a729eaf5a8976d285fa0f Mon Sep 17 00:00:00 2001 From: Bob Zhou Date: Thu, 16 Jul 2026 10:44:36 +0800 Subject: [PATCH 216/501] drm/amdgpu: skip clearing empty freed VM list on GEM close amdgpu_vm_clear_freed() allocates an amdgpu_sync object and walks the VM reservation fences via amdgpu_sync_resv() before checking whether vm->freed has anything to clear. Return early when the list is empty to skip this overhead on a hot path (every GEM close and command submission). Signed-off-by: Bob Zhou Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher (cherry picked from commit 8ba869e852d4f1b1c0e5ae9225c77f7ceccbe056) --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index f224d33c4bc4..dc6a9d7dd0b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1553,6 +1553,8 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev, struct amdgpu_sync sync; int r; + if (list_empty(&vm->freed)) + return 0; /* * Implicitly sync to command submissions in the same VM before From c216b39fbbc4b007fd6984cffd85039d49a55154 Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Fri, 3 Jul 2026 09:14:49 +0800 Subject: [PATCH 217/501] drm/amd/display: Increase HDMI AV mute wait from 2 to 3 frames Some HDMI sinks need additional GCP packets to properly process the mute state before the timing generator is disabled, especially after link re-establishment with HDMI 2.0 scrambling enabled. Waiting for only 2 frames is insufficient for certain monitor firmware, resulting in garbled display output on resume from suspend. Increase the AV mute wait in dcn30_set_avmute() from 2 to 3 frames to ensure the sink receives enough GCP packets. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5167 Assisted-by: Cursor:Claude-Opus-4.6 Reviewed-by: Wayne Lin Signed-off-by: Ray Wu Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 0c0d5174b09640d8b560764aa5a177630e076e93) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c index a7c85a2302ab..2d3587a31bc8 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dcn30/dcn30_hwseq.c @@ -847,13 +847,19 @@ void dcn30_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) pipe_ctx->stream_res.stream_enc, enable); - /* Wait for two frame to make sure AV mute is sent out */ + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); - pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } } } } From 38b73293f38658a4685ffcea666462024f858ad9 Mon Sep 17 00:00:00 2001 From: Vladimir Marioukhine Date: Mon, 20 Jul 2026 11:53:30 -0400 Subject: [PATCH 218/501] drm/amdkfd: fix QID bit leak in pqm_create_queue() When MES is enabled and amdgpu_amdkfd_alloc_kernel_mem() fails during the first queue creation for a process, pqm_create_queue() returns early via 'return retval' without going through the err_create_queue cleanup label. This means clear_bit(*qid, pqm->queue_slot_bitmap) is never called, leaving the reserved QID bit permanently set in queue_slot_bitmap. Over time this leaks QID slots, potentially exhausting all available queue slots. Fix this by replacing 'return retval' with 'goto err_allocate_pqn' so that clear_bit() is always called on the error path without touching the uninitialized pqn pointer. AILIKFD-813 Reported-by: Deucher, Alexander Signed-off-by: Vladimir Marioukhine Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit a107f74c38edbb80d6ab64dcaeeb292c14e9779f) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 9ccbc6e5b27b..503176f00204 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -383,7 +383,7 @@ int pqm_create_queue(struct process_queue_manager *pqm, false); if (retval) { dev_err(dev->adev->dev, "failed to allocate process context bo\n"); - return retval; + goto err_allocate_pqn; } memset(pdd->proc_ctx_cpu_ptr, 0, AMDGPU_MES_PROC_CTX_SIZE); } From 3cbb92ca935f964f6d415cd1c0ac91d061aafa63 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 9 Jul 2026 16:33:11 -0600 Subject: [PATCH 219/501] drm/amdgpu: Fix __rcu fence pointer accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Building for ARCH=um with W=1 C=1 makes sparse report "incompatible types in comparison expression (different address spaces)" warnings in the KFD code, exposed after UML builds were enabled: - amdgpu_amdkfd_fence.c compares the __rcu-annotated dma_fence.ops pointer directly in to_amdgpu_amdkfd_fence(). - amdgpu_amdkfd_gpuvm.c compares the __rcu eviction fence pointer directly in amdgpu_amdkfd_gpuvm_restore_process_bos(). Fixes: af3f2f5db265 ("drm/amdgpu: Remove UML build exclusion from Kconfig") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607091659.SHEscT0c-lkp@intel.com/ Cc: Harry Wentland Assisted-by: Copilot:Claude-Opus-4.8 Signed-off-by: Alex Hung Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 764f241ad227bb942e5b0b8b4d9898f1a4175605) --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c index 6a364357522b..b0299d861903 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_fence.c @@ -92,7 +92,7 @@ struct amdgpu_amdkfd_fence *to_amdgpu_amdkfd_fence(struct dma_fence *f) return NULL; fence = container_of(f, struct amdgpu_amdkfd_fence, base); - if (f->ops == &amdkfd_fence_ops) + if (rcu_access_pointer(f->ops) == &amdkfd_fence_ops) return fence; return NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 35fe2c974699..f0f516a79424 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -3096,7 +3096,7 @@ int amdgpu_amdkfd_gpuvm_restore_process_bos(void *info, struct dma_fence __rcu * process_info->eviction_fence = new_fence; replace_eviction_fence(ef, dma_fence_get(&new_fence->base)); } else { - WARN_ONCE(*ef != &process_info->eviction_fence->base, + WARN_ONCE(rcu_access_pointer(*ef) != &process_info->eviction_fence->base, "KFD eviction fence doesn't match KGD process_info"); } From 43c9f7cba9f9fcd09a5e23686ab4f0f67b62d888 Mon Sep 17 00:00:00 2001 From: Alex Hung Date: Thu, 9 Jul 2026 16:33:12 -0600 Subject: [PATCH 220/501] drm/amdgpu: Pack nested ucode_info struct Building for ARCH=um with W=1 C=1 makes the "amd_sriov_msg_vf2pf_info must be 1 KB" static assertion in amdgv_sriovmsg.h fail under sparse, exposed after UML builds were enabled. Sparse does not honor #pragma pack(push, 1) for the nested ucode_info struct, so it sizes each element as 8 bytes instead of 5 and computes the surrounding structure as larger than 1 KB. The compilers get this right via the enclosing pragma, but the annotation should be explicit. Fixes: af3f2f5db265 ("drm/amdgpu: Remove UML build exclusion from Kconfig") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607091659.SHEscT0c-lkp@intel.com/ Cc: Harry Wentland Assisted-by: Copilot:Claude-Opus-4.8 Signed-off-by: Alex Hung Reviewed-by: Harry Wentland Signed-off-by: Alex Deucher (cherry picked from commit 1d8cfeb69daa863a70134b8ed6df8055c418a5b0) --- drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h index d80f01c0e754..b02561f41b58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgv_sriovmsg.h @@ -379,7 +379,7 @@ struct amd_sriov_msg_vf2pf_info { struct { uint8_t id; uint32_t version; - } ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; + } __packed ucode_info[AMD_SRIOV_MSG_RESERVE_UCODE]; uint64_t dummy_page_addr; /* FB allocated for guest MES to record UQ info */ uint64_t mes_info_addr; From 0ebf413b444ba22a6422f750c48509d4e91f8555 Mon Sep 17 00:00:00 2001 From: Fares Soliman Date: Mon, 13 Jul 2026 09:18:41 -0400 Subject: [PATCH 221/501] drm/amdgpu: Update message IDs to PMFW to correctly gather GFXOFF residency logs Updates PPSMC_MSGs and set/get functions for gathering GFXOFF logs on Van Gogh. Logs are now gathered live rather than starting then stopping logging and reading an average value afterwards. This is in accordance to changes made in PMFW. In regards to messageID 0x52, the old interface uses a start/stop parameter, and the new one doesn't. The firmware is checked to determine which method to use. v2: added firmware guard to new interface, old interface kept as fallback Signed-off-by: Fares Soliman Acked-by: Alex Deucher Signed-off-by: Alex Deucher (cherry picked from commit 482e2cadea8c34ae4e733f269a640d6b04fc9262) --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 4 +- drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h | 2 +- .../pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h | 6 ++- drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h | 4 +- .../gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 50 +++++++++++++++---- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 389bad724273..662ec01961f4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -1319,8 +1319,8 @@ static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, * @size: Number of bytes to read * @pos: Offset to seek to * - * Read the last residency value logged. It doesn't auto update, one needs to - * stop logging before getting the current value. + * Read a live GFXOFF residency sample from firmware. One needs to start logging + * before getting the current value. */ static ssize_t amdgpu_debugfs_gfxoff_residency_read(struct file *f, char __user *buf, size_t size, loff_t *pos) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index e3a89e9a9df4..64a64973fb86 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -1348,7 +1348,7 @@ struct pptable_funcs { u32 (*set_gfx_off_residency)(struct smu_context *smu, bool start); /** - * @get_gfx_off_residency: Average GFXOFF residency % during the logging interval + * @get_gfx_off_residency: Live GFXOFF residency percentage */ u32 (*get_gfx_off_residency)(struct smu_context *smu, uint32_t *residency); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h index 7471e2df2828..4206514765cd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu_v11_5_ppsmc.h @@ -110,8 +110,10 @@ #define PPSMC_MSG_GetSlowPPTLimit 0x4C #define PPSMC_MSG_GetGfxOffStatus 0x50 #define PPSMC_MSG_GetGfxOffEntryCount 0x51 -#define PPSMC_MSG_LogGfxOffResidency 0x52 -#define PPSMC_Message_Count 0x53 +#define PPSMC_MSG_GfxOffResidencyLogReadSample 0x52 +#define PPSMC_MSG_StopGfxOffResidencyLogging 0x53 +#define PPSMC_MSG_StartGfxOffResidencyLogging 0x56 +#define PPSMC_Message_Count 0x57 //Argument for PPSMC_MSG_GfxDeviceDriverReset enum { diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index 636ff90923d9..acf03838d49d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -252,7 +252,9 @@ __SMU_DUMMY_MAP(DriverMode2Reset), \ __SMU_DUMMY_MAP(GetGfxOffStatus), \ __SMU_DUMMY_MAP(GetGfxOffEntryCount), \ - __SMU_DUMMY_MAP(LogGfxOffResidency), \ + __SMU_DUMMY_MAP(StartGfxOffResidencyLogging), \ + __SMU_DUMMY_MAP(GfxOffResidencyLogReadSample), \ + __SMU_DUMMY_MAP(StopGfxOffResidencyLogging), \ __SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \ __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \ __SMU_DUMMY_MAP(AllowGpo), \ diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index 016a5c893fee..c54675b00a96 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -72,6 +72,12 @@ static const struct smu_feature_bits vangogh_dpm_features = { } }; +/* + * SMU support new GFXOFF residency log interface since version 4.63.62.00, + * use this to get live readings of GFXOFF residency + */ +#define SUPPORT_LIVE_RESIDENCY_MSG_VERSION 0x043f3e00 + static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(TestMessage, PPSMC_MSG_TestMessage, 0), MSG_MAP(GetSmuVersion, PPSMC_MSG_GetSmuVersion, 0), @@ -142,7 +148,9 @@ static struct cmn2asic_msg_mapping vangogh_message_map[SMU_MSG_MAX_COUNT] = { MSG_MAP(GetSlowPPTLimit, PPSMC_MSG_GetSlowPPTLimit, 0), MSG_MAP(GetGfxOffStatus, PPSMC_MSG_GetGfxOffStatus, 0), MSG_MAP(GetGfxOffEntryCount, PPSMC_MSG_GetGfxOffEntryCount, 0), - MSG_MAP(LogGfxOffResidency, PPSMC_MSG_LogGfxOffResidency, 0), + MSG_MAP(StartGfxOffResidencyLogging, PPSMC_MSG_StartGfxOffResidencyLogging, 0), + MSG_MAP(GfxOffResidencyLogReadSample, PPSMC_MSG_GfxOffResidencyLogReadSample, 0), + MSG_MAP(StopGfxOffResidencyLogging, PPSMC_MSG_StopGfxOffResidencyLogging, 0), }; static struct cmn2asic_mapping vangogh_feature_mask_map[SMU_FEATURE_COUNT] = { @@ -2449,19 +2457,32 @@ static int vangogh_set_power_limit(struct smu_context *smu, static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) { int ret = 0; - u32 residency; struct amdgpu_device *adev = smu->adev; if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) return 0; - ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_LogGfxOffResidency, - start, &residency); - if (ret) - return ret; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + u32 residency; - if (!start) - adev->gfx.gfx_off_residency = residency; + ret = smu_cmn_send_smc_msg_with_param(smu, SMU_MSG_GfxOffResidencyLogReadSample, + start, &residency); + if (ret) + return ret; + + if (!start) + adev->gfx.gfx_off_residency = residency; + } else { + if (start) { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StartGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_StopGfxOffResidencyLogging, NULL); + if (ret) + return ret; + } + } return ret; } @@ -2478,11 +2499,20 @@ static u32 vangogh_set_gfxoff_residency(struct smu_context *smu, bool start) */ static u32 vangogh_get_gfxoff_residency(struct smu_context *smu, uint32_t *residency) { + int ret = 0; struct amdgpu_device *adev = smu->adev; - *residency = adev->gfx.gfx_off_residency; + if (!(adev->pm.pp_feature & PP_GFXOFF_MASK)) + return 0; - return 0; + if (smu->smc_fw_version < SUPPORT_LIVE_RESIDENCY_MSG_VERSION) { + *residency = adev->gfx.gfx_off_residency; + } else { + ret = smu_cmn_send_smc_msg(smu, SMU_MSG_GfxOffResidencyLogReadSample, + residency); + } + + return ret; } /** From a9cdc85839e4fe2c760aa4ca6cc341c31ad1918a Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 21 Jul 2026 09:30:07 -0400 Subject: [PATCH 222/501] drm/amdkfd: Handle invalid event type in CRIU event restore In kfd_criu_restore_event, there was no handling for the event priv data having an invalid event type. The priv data here is untrusted and can be invalid. In that case, fail with EINVAL. Signed-off-by: David Francis Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 2e8e9963cd5c41aa14fd5316bf9ec92e7a0e3097) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 2e97da597b3d..78abfc0f7780 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -524,6 +524,9 @@ int kfd_criu_restore_event(struct file *devkfd, ret = create_other_event(p, ev, &ev_priv->event_id); break; + default: + ret = -EINVAL; + break; } mutex_unlock(&p->event_mutex); From 119b828afb87d6a0b2b4235fafb28b3bd2da9fa0 Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Thu, 16 Jul 2026 22:25:52 +0530 Subject: [PATCH 223/501] drm/amdgpu: Update driver if header for SMU V15.0.5 Update smu v15.0.0 driver if header to be v15.0.5 compatible. Signed-off-by: Kanala Ramalingeswara Reddy Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit 3ee6561f8ae0ae3c80d04429361a6e06589693fc) --- .../inc/pmfw_if/smu15_driver_if_v15_0_0.h | 38 +++ drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h | 2 +- .../drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 220 +++++++++++++++++- 3 files changed, 253 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h index 47ddf533b08f..a5d3e8d88860 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu15_driver_if_v15_0_0.h @@ -144,6 +144,44 @@ typedef struct { uint32_t MaxGfxClk; } DpmClocks_t; +//Freq in MHz +//Voltage in milli volts with 2 fractional bits +typedef struct { + uint32_t DcfClocks[NUM_DCFCLK_DPM_LEVELS]; + uint32_t DispClocks[NUM_DISPCLK_DPM_LEVELS]; + uint32_t DppClocks[NUM_DPPCLK_DPM_LEVELS]; + uint32_t SocClocks[NUM_SOCCLK_DPM_LEVELS]; + + uint32_t VPEClocks[NUM_VPE_DPM_LEVELS]; + uint32_t FclkClocks_Freq[NUM_FCLK_DPM_LEVELS]; + uint32_t FclkClocks_Voltage[NUM_FCLK_DPM_LEVELS]; + uint32_t SocVoltage[NUM_SOC_VOLTAGE_LEVELS]; + MemPstateTable_t MemPstateTable[NUM_MEM_PSTATE_LEVELS]; + + uint8_t NumDcfClkLevelsEnabled; + uint8_t NumDispClkLevelsEnabled; //Applies to both Dispclk and Dppclk + uint8_t NumSocClkLevelsEnabled; + + uint8_t VpeClkLevelsEnabled; + + uint8_t NumMemPstatesEnabled; + uint8_t NumFclkLevelsEnabled; + uint8_t Spare1; + uint8_t Spare2; + + uint8_t Spare3; + uint8_t Spare4; + uint8_t Spare5[2]; + + uint32_t MinGfxClk; + uint32_t MaxGfxClk; + + uint32_t Spare6[8]; + uint32_t Spare7[8]; + uint32_t Spare8[8]; + uint32_t Spare9[8]; +} DpmClocks_t_v15_0_5; + typedef struct { uint16_t CoreFrequency[16]; //Target core frequency [MHz] uint16_t CorePower[16]; //CAC calculated core power [mW] diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h index e6fd8be2cc4a..0fa32fd041da 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v15_0.h @@ -26,7 +26,7 @@ #include "amdgpu_smu.h" #define SMU15_DRIVER_IF_VERSION_INV 0xFFFFFFFF -#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x7 +#define SMU15_DRIVER_IF_VERSION_SMU_V15_0 0x9 #define FEATURE_MASK(feature) (1ULL << feature) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index bb8d09e73c7d..faa7d661f3bf 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -168,7 +168,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) SMU_TABLE_INIT(tables, SMU_TABLE_WATERMARKS, sizeof(Watermarks_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); - SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t), + SMU_TABLE_INIT(tables, SMU_TABLE_DPMCLOCKS, sizeof(DpmClocks_t_v15_0_5), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); SMU_TABLE_INIT(tables, SMU_TABLE_SMU_METRICS, sizeof(SmuMetrics_t), PAGE_SIZE, AMDGPU_GEM_DOMAIN_VRAM); @@ -178,7 +178,7 @@ static int smu_v15_0_0_init_smc_tables(struct smu_context *smu) goto err0_out; smu_table->metrics_time = 0; - smu_table->clocks_table = kzalloc_obj(DpmClocks_t); + smu_table->clocks_table = kzalloc_obj(DpmClocks_t_v15_0_5); if (!smu_table->clocks_table) goto err1_out; @@ -752,11 +752,48 @@ static int smu_v15_0_0_get_dpm_freq_by_index(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_freq_by_index(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t dpm_level, + uint32_t *freq) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + if (!clk_table || clk_type >= SMU_CLK_COUNT) + return -EINVAL; + + switch (clk_type) { + case SMU_SOCCLK: + if (dpm_level >= clk_table->NumSocClkLevelsEnabled) + return -EINVAL; + *freq = clk_table->SocClocks[dpm_level]; + break; + case SMU_UCLK: + case SMU_MCLK: + if (dpm_level >= clk_table->NumMemPstatesEnabled) + return -EINVAL; + *freq = clk_table->MemPstateTable[dpm_level].MemClk; + break; + case SMU_FCLK: + if (dpm_level >= clk_table->NumFclkLevelsEnabled) + return -EINVAL; + *freq = clk_table->FclkClocks_Freq[dpm_level]; + break; + default: + return -EINVAL; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_freq_by_index(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t dpm_level, uint32_t *freq) { + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); + else smu_v15_0_0_get_dpm_freq_by_index(smu, clk_type, dpm_level, freq); return 0; @@ -907,12 +944,116 @@ static int smu_v15_0_0_get_dpm_ultimate_freq(struct smu_context *smu, return ret; } +static int smu_v15_0_5_get_dpm_ultimate_freq(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *min, + uint32_t *max) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint32_t clock_limit; + uint32_t max_dpm_level, min_dpm_level; + int ret = 0; + + if (!smu_v15_0_0_clk_dpm_is_enabled(smu, clk_type)) { + switch (clk_type) { + case SMU_MCLK: + case SMU_UCLK: + clock_limit = smu->smu_table.boot_values.uclk; + break; + case SMU_FCLK: + clock_limit = smu->smu_table.boot_values.fclk; + break; + case SMU_GFXCLK: + case SMU_SCLK: + clock_limit = smu->smu_table.boot_values.gfxclk; + break; + case SMU_SOCCLK: + clock_limit = smu->smu_table.boot_values.socclk; + break; + default: + clock_limit = 0; + break; + } + + /* clock in Mhz unit */ + if (min) + *min = clock_limit / 100; + if (max) + *max = clock_limit / 100; + + return 0; + } + + if (max) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *max = clk_table->MaxGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + max_dpm_level = 0; + break; + case SMU_FCLK: + max_dpm_level = clk_table->NumFclkLevelsEnabled - 1; + break; + case SMU_SOCCLK: + max_dpm_level = clk_table->NumSocClkLevelsEnabled - 1; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + max_dpm_level, max); + if (ret) + goto failed; + } + } + + if (min) { + switch (clk_type) { + case SMU_GFXCLK: + case SMU_SCLK: + *min = clk_table->MinGfxClk; + break; + case SMU_MCLK: + case SMU_UCLK: + min_dpm_level = clk_table->NumMemPstatesEnabled - 1; + break; + case SMU_FCLK: + min_dpm_level = 0; + break; + case SMU_SOCCLK: + min_dpm_level = 0; + break; + default: + ret = -EINVAL; + goto failed; + } + + if (clk_type != SMU_GFXCLK && clk_type != SMU_SCLK) { + ret = smu_v15_0_common_get_dpm_freq_by_index(smu, clk_type, + min_dpm_level, min); + if (ret) + goto failed; + } + } + +failed: + return ret; +} + static int smu_v15_0_common_get_dpm_ultimate_freq(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *min, uint32_t *max) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_ultimate_freq(smu, clk_type, min, max); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_ultimate_freq(smu, clk_type, min, max); return 0; @@ -986,11 +1127,36 @@ static int smu_v15_0_0_get_dpm_level_count(struct smu_context *smu, return 0; } +static int smu_v15_0_5_get_dpm_level_count(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *count) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + switch (clk_type) { + case SMU_SOCCLK: + *count = clk_table->NumSocClkLevelsEnabled; + break; + case SMU_MCLK: + *count = clk_table->NumMemPstatesEnabled; + break; + case SMU_FCLK: + *count = clk_table->NumFclkLevelsEnabled; + break; + default: + break; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_level_count(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t *count) { - if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_level_count(smu, clk_type, count); + else if (clk_type != SMU_VCLK1 && clk_type != SMU_DCLK1) smu_v15_0_0_get_dpm_level_count(smu, clk_type, count); return 0; @@ -1363,9 +1529,24 @@ static int smu_v15_0_0_set_fine_grain_gfx_freq_parameters(struct smu_context *sm return 0; } +static int smu_v15_0_5_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + + smu->gfx_default_hard_min_freq = clk_table->MinGfxClk; + smu->gfx_default_soft_max_freq = clk_table->MaxGfxClk; + smu->gfx_actual_hard_min_freq = 0; + smu->gfx_actual_soft_max_freq = 0; + + return 0; +} + static int smu_v15_0_common_set_fine_grain_gfx_freq_parameters(struct smu_context *smu) { - smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_set_fine_grain_gfx_freq_parameters(smu); + else + smu_v15_0_0_set_fine_grain_gfx_freq_parameters(smu); return 0; } @@ -1405,9 +1586,36 @@ static int smu_v15_0_0_get_dpm_table(struct smu_context *smu, struct dpm_clocks return 0; } +static int smu_v15_0_5_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) +{ + DpmClocks_t_v15_0_5 *clk_table = smu->smu_table.clocks_table; + uint8_t idx; + + /* + * Only the Clock information of SOC and + * VPE is copied to provide VPE DPM settings for use. + */ + for (idx = 0; idx < NUM_SOCCLK_DPM_LEVELS; idx++) { + clock_table->SocClocks[idx].Freq = + (idx < clk_table->NumSocClkLevelsEnabled) ? clk_table->SocClocks[idx]:0; + clock_table->SocClocks[idx].Vol = 0; + } + + for (idx = 0; idx < NUM_VPE_DPM_LEVELS; idx++) { + clock_table->VPEClocks[idx].Freq = + (idx < clk_table->VpeClkLevelsEnabled) ? clk_table->VPEClocks[idx]:0; + clock_table->VPEClocks[idx].Vol = 0; + } + + return 0; +} + static int smu_v15_0_common_get_dpm_table(struct smu_context *smu, struct dpm_clocks *clock_table) { - smu_v15_0_0_get_dpm_table(smu, clock_table); + if (amdgpu_ip_version(smu->adev, MP1_HWIP, 0) == IP_VERSION(15, 0, 5)) + smu_v15_0_5_get_dpm_table(smu, clock_table); + else + smu_v15_0_0_get_dpm_table(smu, clock_table); return 0; } From d8726ef11512754a68c0ab53c57634a569b8feff Mon Sep 17 00:00:00 2001 From: Pierre-Eric Pelloux-Prayer Date: Mon, 22 Jun 2026 10:11:22 +0200 Subject: [PATCH 224/501] drm/amdgpu: move debug_vm handling to amdgpu_cs_parser_fini MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit referenced below restarts the CS if the validation is still in progress. When debug_vm is enabled, all BOs from the CS are invalidated so we will hit an infinite loop. To avoid that, defer BO invalidation to amdgpu_cs_parser_fini. Fixes: 59720bfd8c6d ("drm/amdgpu: restart the CS if some parts of the VM are still invalidated") Signed-off-by: Pierre-Eric Pelloux-Prayer Reviewed-by: Christian König Signed-off-by: Alex Deucher (cherry picked from commit 8c990ee9daa295462df24982ce6878db997a380a) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c | 30 ++++++++++++++------------ 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index e714cee2997a..5445f75741b5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -1177,19 +1177,6 @@ static int amdgpu_cs_vm_handling(struct amdgpu_cs_parser *p) job->vm_pd_addr = amdgpu_gmc_pd_addr(vm->root.bo); } - if (adev->debug_vm) { - /* Invalidate all BOs to test for userspace bugs */ - amdgpu_bo_list_for_each_entry(e, p->bo_list) { - struct amdgpu_bo *bo = e->bo; - - /* ignore duplicates */ - if (!bo) - continue; - - amdgpu_vm_bo_invalidate(bo, false); - } - } - return 0; } @@ -1378,6 +1365,8 @@ static int amdgpu_cs_submit(struct amdgpu_cs_parser *p, /* Cleanup the parser structure */ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) { + struct amdgpu_device *adev = parser->adev; + struct amdgpu_bo_list_entry *e; unsigned int i; amdgpu_sync_free(&parser->sync); @@ -1393,8 +1382,21 @@ static void amdgpu_cs_parser_fini(struct amdgpu_cs_parser *parser) if (parser->ctx) amdgpu_ctx_put(parser->ctx); - if (parser->bo_list) + if (parser->bo_list) { + if (adev->debug_vm) { + /* Invalidate all BOs to test for userspace bugs */ + amdgpu_bo_list_for_each_entry(e, parser->bo_list) { + struct amdgpu_bo *bo = e->bo; + + /* ignore duplicates */ + if (!bo) + continue; + + amdgpu_vm_bo_invalidate(bo, false); + } + } amdgpu_bo_list_put(parser->bo_list); + } for (i = 0; i < parser->nchunks; i++) kvfree(parser->chunks[i].kdata); From 99b2fe4f19e3be0a8d0a0b5ea98d855970889653 Mon Sep 17 00:00:00 2001 From: Gang Ba Date: Tue, 14 Jul 2026 15:08:57 -0400 Subject: [PATCH 225/501] drm/amdkfd: Fix missing authorization check in KFD_IOC_DBG_TRAP_DISABLE Prevent unauthorized termination of active GPU debug sessions. Previously, users with /dev/kfd access could terminate another process's debug session without proper ownership or ptrace authorization. Signed-off-by: Gang Ba Reviewed-by: Kent Russell Signed-off-by: Alex Deucher (cherry picked from commit 4db4c5ffd5585b72622ecf6ffedf2da258ee23f5) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c index c7edebd2fd8a..e1689b3d2add 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_chardev.c @@ -3109,10 +3109,14 @@ static int kfd_ioctl_set_debug_trap(struct file *filep, struct kfd_process *p, v goto out; } - /* Check if target is still PTRACED. */ + /* + * Verify debugger has permission to debug target process. + * For cross-process debugging, require active ptrace relationship. + * This applies to ALL operations to prevent unauthorized interference. + */ rcu_read_lock(); - if (target != p && args->op != KFD_IOC_DBG_TRAP_DISABLE - && ptrace_parent(target->lead_thread) != current) { + if (target != p && ptrace_parent(target->lead_thread) != current + && target->debugger_process != p) { pr_err("PID %i is not PTRACED and cannot be debugged\n", args->pid); r = -EPERM; } From a8d234a1133f02bfbbdaac18b889219949f28108 Mon Sep 17 00:00:00 2001 From: Shubhankar Milind Sardeshpande Date: Wed, 22 Jul 2026 10:51:56 +0530 Subject: [PATCH 226/501] drm/amdgpu: Enable support for PSP 15_0_5 Add PSP 15.0.5 related offsets for GFX to KMD interface and enable support for it. Co-developed-by: Pratik Vishwakarma Signed-off-by: Pratik Vishwakarma Signed-off-by: Shubhankar Milind Sardeshpande Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit b01e244c82c5d033d7424a64abe4079f3fceb869) --- drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c | 1 + drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c | 5 +- drivers/gpu/drm/amd/amdgpu/psp_v15_0.c | 93 ++++++++++++++++--- 3 files changed, 83 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c index 2860f12915c0..5e83edbd313b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_discovery.c @@ -2304,6 +2304,7 @@ static int amdgpu_discovery_set_psp_ip_blocks(struct amdgpu_device *adev) amdgpu_device_ip_block_add(adev, &psp_v14_0_ip_block); break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): amdgpu_device_ip_block_add(adev, &psp_v15_0_ip_block); break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index ca80b6194cac..7139cb3b2db5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -275,6 +275,7 @@ static int psp_early_init(struct amdgpu_ip_block *ip_block) psp->boot_time_tmr = false; break; case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): psp_v15_0_0_set_psp_funcs(psp); psp->boot_time_tmr = false; @@ -3503,8 +3504,10 @@ static int psp_load_non_psp_fw(struct psp_context *psp) amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 0) || amdgpu_ip_version(adev, MP0_HWIP, 0) == - IP_VERSION(15, 0, 8) || + IP_VERSION(15, 0, 5) || amdgpu_ip_version(adev, MP0_HWIP, 0) == + IP_VERSION(15, 0, 8) || + amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 9)) && (ucode->ucode_id == AMDGPU_UCODE_ID_SDMA1 || ucode->ucode_id == AMDGPU_UCODE_ID_SDMA2 || diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c index 2a4d91368ac6..abaa40b6def9 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v15_0.c @@ -33,9 +33,22 @@ MODULE_FIRMWARE("amdgpu/psp_15_0_0_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_0_ta.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_toc.bin"); +MODULE_FIRMWARE("amdgpu/psp_15_0_5_ta.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_toc.bin"); MODULE_FIRMWARE("amdgpu/psp_15_0_9_ta.bin"); +#define regMPASP_PCRU0_MPASP_C2PMSG_64 0x4280 +#define regMPASP_PCRU0_MPASP_C2PMSG_64_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_67 0x4283 +#define regMPASP_PCRU0_MPASP_C2PMSG_67_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_69 0x4285 +#define regMPASP_PCRU0_MPASP_C2PMSG_69_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_70 0x4286 +#define regMPASP_PCRU0_MPASP_C2PMSG_70_BASE_IDX 2 +#define regMPASP_PCRU0_MPASP_C2PMSG_71 0x4287 +#define regMPASP_PCRU0_MPASP_C2PMSG_71_BASE_IDX 2 + static int psp_v15_0_0_init_microcode(struct psp_context *psp) { struct amdgpu_device *adev = psp->adev; @@ -71,14 +84,25 @@ static int psp_v15_0_0_ring_stop(struct psp_context *psp, ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_SMN_C2PMSG_101), 0x80000000, 0x80000000, false); } else { - /* Write the ring destroy command*/ - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, - GFX_CTRL_CMD_ID_DESTROY_RINGS); - /* there might be handshake issue with hardware which needs delay */ - mdelay(20); - /* Wait for response flag (bit 31) */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } else { + /* Write the ring destroy command*/ + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64, + GFX_CTRL_CMD_ID_DESTROY_RINGS); + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + /* Wait for response flag (bit 31) */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + } } return ret; @@ -118,14 +142,44 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, 0x80000000, 0x8000FFFF, false); } else { - /* Wait for sOS ready for ring creation */ - ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), - 0x80000000, 0x80000000, false); + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); if (ret) { DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); return ret; } + /* Write low address of the ring to C2PMSG_69 */ + psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_69, psp_ring_reg); + /* Write high address of the ring to C2PMSG_70 */ + psp_ring_reg = upper_32_bits(ring->ring_mem_mc_addr); + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_70, psp_ring_reg); + /* Write size of ring to C2PMSG_71 */ + psp_ring_reg = ring->ring_size; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_71, psp_ring_reg); + /* Write the ring initialization command to C2PMSG_64 */ + psp_ring_reg = ring_type; + psp_ring_reg = psp_ring_reg << 16; + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64, psp_ring_reg); + + /* there might be handshake issue with hardware which needs delay */ + mdelay(20); + + /* Wait for response flag (bit 31) in C2PMSG_64 */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_64), + 0x80000000, 0x8000FFFF, false); + } else { + /* Wait for sOS ready for ring creation */ + ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), + 0x80000000, 0x80000000, false); + if (ret) { + DRM_ERROR("Failed to wait for trust OS ready for ring creation\n"); + return ret; + } + /* Write low address of the ring to C2PMSG_69 */ psp_ring_reg = lower_32_bits(ring->ring_mem_mc_addr); WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_69, psp_ring_reg); @@ -146,6 +200,7 @@ static int psp_v15_0_0_ring_create(struct psp_context *psp, /* Wait for response flag (bit 31) in C2PMSG_64 */ ret = psp_wait_for(psp, SOC15_REG_OFFSET(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_64), 0x80000000, 0x8000FFFF, false); + } } return ret; @@ -176,8 +231,12 @@ static uint32_t psp_v15_0_0_ring_get_wptr(struct psp_context *psp) if (amdgpu_sriov_vf(adev)) data = RREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102); - else - data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67); + else + data = RREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67); + } return data; } @@ -190,8 +249,12 @@ static void psp_v15_0_0_ring_set_wptr(struct psp_context *psp, uint32_t value) WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_102, value); WREG32_SOC15(MP0, 0, regMPASP_SMN_C2PMSG_101, GFX_CTRL_CMD_ID_CONSUME_CMD); - } else - WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } else { + if (amdgpu_ip_version(adev, MP0_HWIP, 0) == IP_VERSION(15, 0, 5)) + WREG32_SOC15(MP0, 0, regMPASP_PCRU0_MPASP_C2PMSG_67, value); + else + WREG32_SOC15(MP0, 0, regMPASP_PCRU1_MPASP_C2PMSG_67, value); + } } static const struct psp_funcs psp_v15_0_0_funcs = { From acea01861838ddac70e3a89ef9fbd5f2073f3f91 Mon Sep 17 00:00:00 2001 From: Shubhankar Milind Sardeshpande Date: Thu, 16 Jul 2026 18:17:48 +0530 Subject: [PATCH 227/501] drm/amdgpu: Fix NBIO 7.11.5 offsets Fix NBIO 7.11.5 related offsets Signed-off-by: Shubhankar Milind Sardeshpande Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit dcc27ae3092211c913a1bea04618c4faf1234d48) --- drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c | 132 ++++++++++++----------- 1 file changed, 69 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c index c78f0598637f..000516b5845a 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c +++ b/drivers/gpu/drm/amd/amdgpu/nbif_v6_3_1.c @@ -30,70 +30,76 @@ #include "ivsrcid/nbio/irqsrcs_nbif_7_4.h" #include -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 -#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10 0x4f0aeb +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10 0x4f0aec +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_0_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10 0x4f0aed +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10 0x4f0aee +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_1_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10 0x4f0aef +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10 0x4f0af0 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_2_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10 0x4f0af1 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10 0x4f0af2 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_3_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10 0x4f0af3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10 0x4f0af4 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_4_CTRL1_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10 0x4f0af5 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL_nbif_4_10_BASE_IDX 3 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10 0x4f0af6 +#define regGDC_S2A0_S2A_DOORBELL_ENTRY_5_CTRL1_nbif_4_10_BASE_IDX 3 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10 0x0021 +#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10_BASE_IDX 2 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 -#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5 0x8e13 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_HIGH_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5 0x8e14 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_BASE_LOW_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5 0x8e15 +#define regBIF_BX_PF0_DOORBELL_SELFRING_GPA_APER_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x8e4d -#define regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x8e4e -#define regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5 0x012d +#define regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5 0x012e +#define regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0xd000 -#define regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 5 +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5 0x0021 +#define regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5_BASE_IDX 2 -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5 0x8e20 -#define regBIF_BX0_BIF_FB_EN_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5 0x0100 +#define regBIF_BX1_BIF_FB_EN_nbio_7_11_5_BASE_IDX 2 -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5 0x8e11 -#define regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5 0x8e12 -#define regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5 0x00f1 +#define regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5 0x00f2 +#define regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5_BASE_IDX 2 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x8e26 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 5 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x8e27 -#define regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5 0x0106 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5_BASE_IDX 2 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5 0x0107 +#define regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5_BASE_IDX 2 -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x8e17 -#define regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 5 +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5 0x00f7 +#define regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5_BASE_IDX 2 + +//BIF_BX1_BIF_FB_EN +#define BIF_BX1_BIF_FB_EN__FB_READ_EN__SHIFT_nbio_7_11_5 0x0 +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN__SHIFT_nbio_7_11_5 0x1 +#define BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 0x00000001L +#define BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5 0x00000002L static void nbif_v6_3_1_remap_hdp_registers(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_MEM_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_MEM_FLUSH_CNTL); - WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_REMAP_HDP_REG_FLUSH_CNTL_nbio_7_11_5, adev->rmmio_remap.reg_offset + KFD_MMIO_REMAP_HDP_REG_FLUSH_CNTL); } else { WREG32_SOC15(NBIO, 0, regBIF_BX0_REMAP_HDP_MEM_FLUSH_CNTL, @@ -110,7 +116,7 @@ static u32 nbif_v6_3_1_get_rev_id(struct amdgpu_device *adev) if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 4)) tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbif_4_10); else if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); + tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP1_RCC_DEV0_EPF0_STRAP0_nbio_7_11_5); else tmp = RREG32_SOC15(NBIO, 0, regRCC_STRAP0_RCC_DEV0_EPF0_STRAP0); @@ -124,11 +130,11 @@ static void nbif_v6_3_1_mc_access_enable(struct amdgpu_device *adev, bool enable { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) { if (enable) - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, - BIF_BX0_BIF_FB_EN__FB_READ_EN_MASK | - BIF_BX0_BIF_FB_EN__FB_WRITE_EN_MASK); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, + BIF_BX1_BIF_FB_EN__FB_READ_EN_MASK_nbio_7_11_5 | + BIF_BX1_BIF_FB_EN__FB_WRITE_EN_MASK_nbio_7_11_5); else - WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN_nbio_7_11_5, 0); + WREG32_SOC15(NBIO, 0, regBIF_BX1_BIF_FB_EN_nbio_7_11_5, 0); } else { if (enable) WREG32_SOC15(NBIO, 0, regBIF_BX0_BIF_FB_EN, @@ -383,12 +389,12 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) /* setup interrupt control */ if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2_nbio_7_11_5, + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL2_nbio_7_11_5, adev->dummy_page_addr >> 8); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL2, adev->dummy_page_addr >> 8); - interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5); + interrupt_cntl = RREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5); /* * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=0 - dummy read disabled with msi, enabled without msi * BIF_BX0_INTERRUPT_CNTL__IH_DUMMY_RD_OVERRIDE_MASK=1 - dummy read controlled by IH_DUMMY_RD_EN @@ -401,7 +407,7 @@ static void nbif_v6_3_1_ih_control(struct amdgpu_device *adev) IH_REQ_NONSNOOP_EN, 0); if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); + WREG32_SOC15(NBIO, 0, regBIF_BX1_INTERRUPT_CNTL_nbio_7_11_5, interrupt_cntl); else WREG32_SOC15(NBIO, 0, regBIF_BX0_INTERRUPT_CNTL, interrupt_cntl); } @@ -427,7 +433,7 @@ nbif_v6_3_1_get_clockgating_state(struct amdgpu_device *adev, static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_REQ_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_REQ); } @@ -435,7 +441,7 @@ static u32 nbif_v6_3_1_get_hdp_flush_req_offset(struct amdgpu_device *adev) static u32 nbif_v6_3_1_get_hdp_flush_done_offset(struct amdgpu_device *adev) { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) - return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE_nbio_7_11_5); + return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF1_GPU_HDP_FLUSH_DONE_nbio_7_11_5); else return SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_GPU_HDP_FLUSH_DONE); } @@ -622,7 +628,7 @@ static void nbif_v6_3_1_set_reg_remap(struct amdgpu_device *adev) } else { if (amdgpu_ip_version(adev, NBIO_HWIP, 0) == IP_VERSION(7, 11, 5)) adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, - regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; + regBIF_BX_PF1_HDP_MEM_COHERENCY_FLUSH_CNTL_nbio_7_11_5) << 2; else adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, regBIF_BX_PF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; From 83463a96ea3c7d8ae636a4d6a0ba63c9ce410724 Mon Sep 17 00:00:00 2001 From: William Palacek Date: Mon, 20 Jul 2026 12:51:34 -0400 Subject: [PATCH 228/501] drm/amdkfd: fix uint32_t overflow in EOP ring buffer size alignment eop_ring_buffer_size in struct queue_properties is a u32. In kfd_queue_acquire_buffers() the expected EOP buffer size is computed as ALIGN(eop_ring_buffer_size, PAGE_SIZE); ALIGN uses typeof(x), so the addition is done in 32-bit. A user-supplied size of 0xFFFFF001 wraps to 0, causing kfd_queue_buffer_get() to skip its exact-size check (gated on size != 0) and accept any BO mapped at the address. On GFX8/GFX9 the MQD cp_hqd_eop_control is then programmed for an 8KB EOP ring backed by a 4KB BO, so CP EOP writes can land past the buffer and fault the GPU. Cast the operand to u64 so the alignment is computed in 64-bit; the size check in kfd_queue_buffer_get() then rejects the oversized request. Fixes: 42ea9cf2f16b ("drm/amdkfd: Relax size checking during queue buffer get") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ae443117b742c357bfef3a7bddabf76fcf86e9ef) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_queue.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c index 98a5512b701b..b249e7d1af48 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_queue.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_queue.c @@ -288,7 +288,7 @@ int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_prope } err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address, &properties->eop_buf_bo, - ALIGN(properties->eop_ring_buffer_size, PAGE_SIZE)); + ALIGN((u64)properties->eop_ring_buffer_size, PAGE_SIZE)); if (err) goto out_err_unreserve; } From c2ac5a50c1804e22d5bc05c7e16693333751b3c0 Mon Sep 17 00:00:00 2001 From: Kanala Ramalingeswara Reddy Date: Thu, 23 Jul 2026 00:10:01 +0530 Subject: [PATCH 229/501] drm/amdgpu: enable mode2 reset for SMU IP v15.0.5 Set the default reset method to mode2 for SMU 15.0.5. Signed-off-by: Kanala Ramalingeswara Reddy Reviewed-by: Pratik Vishwakarma Signed-off-by: Alex Deucher (cherry picked from commit 314d49abe315cd0d0a872a43f68f08be43a305c8) --- drivers/gpu/drm/amd/amdgpu/soc21.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index e0b80abcd075..e9d51e992a8c 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -406,6 +406,7 @@ soc21_asic_reset_method(struct amdgpu_device *adev) case IP_VERSION(14, 0, 4): case IP_VERSION(14, 0, 5): case IP_VERSION(15, 0, 0): + case IP_VERSION(15, 0, 5): case IP_VERSION(15, 0, 9): return AMD_RESET_METHOD_MODE2; default: From bb493058c35c8676e48269ab6732688ea733d23c Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Fri, 24 Jul 2026 07:41:29 +0800 Subject: [PATCH 230/501] drm/amd/pm: fix pptable use-after-free amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table after dropping adev->pm.mutex. The sysfs path then copies from that pointer. A concurrent pp_table write can replace and free the allocation during the copy, causing a use-after-free. Change the DPM interface to copy into caller-provided storage while the mutex is held. Keep the size-only query for attribute discovery without exposing the driver-owned pointer. Fixes: 1684d3ba4885 ("drm/amd/amdgpu: change pptable output format from ASCII to binary") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit f6eed7acfd30099ef7baeb6ba45bb59daad80631) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 14 +++++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 13 +++---------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index f76ba6753551..c787e772b3cc 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + char *pptable = NULL; int ret = 0; - if (!table) + if ((!table && size) || (table && !size)) return -EINVAL; if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) @@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table) mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle, - table); + &pptable); + if (ret > 0 && !pptable) { + ret = -EINVAL; + } else if (ret > 0 && table) { + ret = min_t(size_t, ret, size); + memcpy(table, pptable, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 97da01aff76c..d94ea54c33c3 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -564,25 +564,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - char *table = NULL; int size, ret; ret = amdgpu_pm_get_access_if_active(adev); if (ret) return ret; - size = amdgpu_dpm_get_pp_table(adev, &table); + size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1); amdgpu_pm_put_access(adev); if (size <= 0) return size; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, table, size); - return size; } @@ -2726,10 +2720,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(pp_table)) { int ret; - char *tmp = NULL; - ret = amdgpu_dpm_get_pp_table(adev, &tmp); - if (ret == -EOPNOTSUPP || !tmp) + ret = amdgpu_dpm_get_pp_table(adev, NULL, 0); + if (ret <= 0) *states = ATTR_STATE_UNSUPPORTED; else *states = ATTR_STATE_SUPPORTED; diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index aa3f427819a0..e95cd22a31cf 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev, int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, enum amd_pp_task task_id, enum amd_pm_state_type *user_state); -int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table); +int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, + size_t size); int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev, uint32_t type, long *input, From 048f4541b71fb19645fb79d6e62e6e4da23a4035 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Thu, 23 Jul 2026 23:18:33 +0800 Subject: [PATCH 231/501] drm/amd/pm: fix torn gpu metrics reads amdgpu_dpm_get_gpu_metrics() returns a pointer to the shared metrics cache after dropping adev->pm.mutex. The sysfs path then copies from that pointer. Another reader can refresh the cache in place during the copy and return a snapshot containing data from two generations. Pass caller-provided storage through the DPM interface and copy the metrics while the mutex is held. This keeps the cache pointer private and makes each sysfs read observe one complete sample. Fixes: 25c933b1c4fc ("drm/amd/powerplay: add new sysfs interface for retrieving gpu metrics(V2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Signed-off-by: Alex Deucher (cherry picked from commit 862333bb48693ecafcae25af0c9d9ec31015ac77) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 12 +++++++++--- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 8 +------- drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h | 3 ++- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index c787e772b3cc..6d1ad4d5b8f0 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1434,17 +1434,23 @@ int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, return ret; } -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table) +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; - int ret = 0; + void *table; + ssize_t ret; if (!pp_funcs->get_gpu_metrics) return 0; mutex_lock(&adev->pm.mutex); ret = pp_funcs->get_gpu_metrics(adev->powerplay.pp_handle, - table); + &table); + if (ret > 0) { + ret = min_t(ssize_t, ret, size); + memcpy(buf, table, ret); + } mutex_unlock(&adev->pm.mutex); return ret; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index d94ea54c33c3..763947fbf16f 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -1774,7 +1774,6 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, { struct drm_device *ddev = dev_get_drvdata(dev); struct amdgpu_device *adev = drm_to_adev(ddev); - void *gpu_metrics; ssize_t size = 0; int ret; @@ -1782,15 +1781,10 @@ static ssize_t amdgpu_get_gpu_metrics(struct device *dev, if (ret) return ret; - size = amdgpu_dpm_get_gpu_metrics(adev, &gpu_metrics); + size = amdgpu_dpm_get_gpu_metrics(adev, buf, PAGE_SIZE - 1); if (size <= 0) goto out; - if (size >= PAGE_SIZE) - size = PAGE_SIZE - 1; - - memcpy(buf, gpu_metrics, size); - out: amdgpu_pm_put_access(adev); diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index e95cd22a31cf..8d1b097a3c3c 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -518,7 +518,8 @@ int amdgpu_dpm_get_power_profile_mode(struct amdgpu_device *adev, char *buf); int amdgpu_dpm_set_power_profile_mode(struct amdgpu_device *adev, long *input, uint32_t size); -int amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void **table); +ssize_t amdgpu_dpm_get_gpu_metrics(struct amdgpu_device *adev, void *buf, + size_t size); ssize_t amdgpu_dpm_get_xcp_metrics(struct amdgpu_device *adev, int xcp_id, void *table); ssize_t amdgpu_dpm_get_temp_metrics(struct amdgpu_device *adev, From 443290d70b01e9c35830c300e3247c06581b594c Mon Sep 17 00:00:00 2001 From: Ray Wu Date: Mon, 13 Jul 2026 22:23:34 +0800 Subject: [PATCH 232/501] drm/amd/display: Add AV mute wait frames to dce110_set_avmute Port the three-frame wait logic from dcn30_set_avmute to dce110_set_avmute so that older DCN versions (1.0, 2.0) also wait for GCP packets to be sent out before proceeding. This ensures HDMI sinks properly process the mute state, preventing garbled display after link re-establishment. Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5167 Reviewed-by: Wayne Lin Signed-off-by: Ray Wu Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 414da24137ace80d8c59fefd43ba3ec9f5f854ba) Cc: stable@vger.kernel.org --- .../amd/display/dc/hwss/dce110/dce110_hwseq.c | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c index 042602c50e35..53fffe58602f 100644 --- a/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/hwss/dce110/dce110_hwseq.c @@ -1337,8 +1337,27 @@ void dce110_blank_stream(struct pipe_ctx *pipe_ctx) void dce110_set_avmute(struct pipe_ctx *pipe_ctx, bool enable) { - if (pipe_ctx != NULL && pipe_ctx->stream_res.stream_enc != NULL) + if (pipe_ctx == NULL || pipe_ctx->stream_res.stream_enc == NULL) + return; + + if (dc_is_hdmi_signal(pipe_ctx->stream->signal)) { pipe_ctx->stream_res.stream_enc->funcs->set_avmute(pipe_ctx->stream_res.stream_enc, enable); + + /* Wait for three frames to make sure AV mute is sent out. + * Some HDMI sinks need additional GCP packets to properly + * process the mute state, especially after link re-establishment + * with HDMI 2.0 scrambling enabled. + */ + if (enable && pipe_ctx->stream_res.tg->funcs->is_tg_enabled(pipe_ctx->stream_res.tg)) { + int i; + + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + for (i = 0; i < 3; i++) { + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VBLANK); + pipe_ctx->stream_res.tg->funcs->wait_for_state(pipe_ctx->stream_res.tg, CRTC_STATE_VACTIVE); + } + } + } } enum audio_dto_source translate_to_dto_source(enum controller_id crtc_id) From f327e389c07cfc3a2f6ff54f6214e1a52d457edc Mon Sep 17 00:00:00 2001 From: George Zhang Date: Thu, 16 Jul 2026 17:00:01 -0400 Subject: [PATCH 233/501] drm/amd/display: Fix divide-by-zero in calculate_mcache_setting on zero viewport If a plane reaches calculate_mcache_setting with a zero-area viewport, calculate_mcache_setting exits early with num_mcaches == 0 and mvmpg_width/height == 0. This will cause a divide-by-zero panic and can also cause an underflow on num_mcaches. Fix this by changing calculate_mcache_setting to bool and adding guards after each calculate_mcache_row_bytes call. If num_mcaches or mvmpg_width/height is zero, return a false. Callers will propagate the failure as a rejected mode, which prevents the panic. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/5302 Reviewed-by: Sun peng (Leo) Li Reviewed-by: Dillon Varone Signed-off-by: George Zhang Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 29c0f7c655f47bcbd575ff75e58480df6ec3c9da) Cc: stable@vger.kernel.org --- .../src/dml2_core/dml2_core_dcn4_calcs.c | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c index f338e733318e..b4e2264ef408 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/src/dml2_core/dml2_core_dcn4_calcs.c @@ -2452,7 +2452,7 @@ static void calculate_mcache_row_bytes( DML_ASSERT(*p->num_mcaches > 0); } -static void calculate_mcache_setting( +static bool calculate_mcache_setting( struct dml2_core_internal_scratch *scratch, struct dml2_core_calcs_calculate_mcache_setting_params *p) { @@ -2478,7 +2478,7 @@ static void calculate_mcache_setting( *p->lc_comb_mcache = 0; if (!p->dcc_enable) - return; + return true; l->is_dual_plane = dml_is_420(p->source_format) || p->source_format == dml2_rgbe_alpha; @@ -2515,7 +2515,14 @@ static void calculate_mcache_setting( l->l_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_l; calculate_mcache_row_bytes(scratch, &l->l_p); - DML_ASSERT(*p->num_mcaches_l > 0); + if (*p->num_mcaches_l == 0 || + (p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate luma viewport (num_mcaches_l=%u mvmpg_%s_l=%u) — mode not supported\n", + __func__, *p->num_mcaches_l, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_l : l->mvmpg_width_l); + return false; + } if (l->is_dual_plane) { l->c_p.num_chans = p->num_chans; @@ -2551,7 +2558,14 @@ static void calculate_mcache_setting( l->c_p.mvmpg_per_mcache_lb = &l->mvmpg_per_mcache_lb_c; calculate_mcache_row_bytes(scratch, &l->c_p); - DML_ASSERT(*p->num_mcaches_c > 0); + if (*p->num_mcaches_c == 0 || + (p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c) == 0) { + DML_LOG_VERBOSE("DML::%s: degenerate chroma viewport (num_mcaches_c=%u mvmpg_%s_c=%u) — mode not supported\n", + __func__, *p->num_mcaches_c, + p->surf_vert ? "height" : "width", + p->surf_vert ? l->mvmpg_height_c : l->mvmpg_width_c); + return false; + } } // Sharing for iMALL access @@ -2661,6 +2675,7 @@ static void calculate_mcache_setting( *p->mcache_shift_granularity_l = l->mvmpg_access_width_l; *p->mcache_shift_granularity_c = l->mvmpg_access_width_c; + return true; } static void calculate_mall_bw_overhead_factor( @@ -9457,7 +9472,10 @@ static bool dml_core_mode_support(struct dml2_core_calcs_mode_support_ex *in_out calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->ms.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->ms.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) { + mode_lib->ms.support.ModeSupport = false; + return false; + } } calculate_mall_bw_overhead_factor( @@ -10933,7 +10951,8 @@ static bool dml_core_mode_programming(struct dml2_core_calcs_mode_programming_ex calculate_mcache_setting_params->mall_comb_mcache_l = &mode_lib->mp.mall_comb_mcache_l[k]; calculate_mcache_setting_params->mall_comb_mcache_c = &mode_lib->mp.mall_comb_mcache_c[k]; calculate_mcache_setting_params->lc_comb_mcache = &mode_lib->mp.lc_comb_mcache[k]; - calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params); + if (!calculate_mcache_setting(&mode_lib->scratch, calculate_mcache_setting_params)) + return false; } calculate_mall_bw_overhead_factor( From 000acb4ce7fb9feba3072ce468ad681f6585cd5d Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Mon, 16 Feb 2026 09:32:53 -0500 Subject: [PATCH 234/501] drm/amd/display: check if dml21_add_phantom_plane() is successful Verify that the phantom plane was allocated to avoid a later segfault. Closes: https://gitlab.freedesktop.org/drm/amd/-/issues/4970 Fixes: 70839da63605 ("drm/amd/display: Add new DCN401 sources") Reviewed-by: Dillon Varone Signed-off-by: Fangzhi Zuo Tested-by: Dan Wheeler Signed-off-by: Alex Deucher (cherry picked from commit 5adb54abe5a8e82cbff7f8806db30a5f4924329f) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/dml2_0/dml21/dml21_utils.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c index 835fece1d46a..50e445275b14 100644 --- a/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c +++ b/drivers/gpu/drm/amd/display/dc/dml2_0/dml21/dml21_utils.c @@ -359,14 +359,13 @@ void dml21_handle_phantom_streams_planes(const struct dc *dc, struct dc_state *c main_plane = main_stream_status->plane_states[dc_plane_index]; /* create phantom planes for subvp enabled plane */ - dml21_add_phantom_plane(dml_ctx, - dc, - context, - phantom_stream, - main_plane, - &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index]); - - phantoms_added = true; + if (dml21_add_phantom_plane(dml_ctx, + dc, + context, + phantom_stream, + main_plane, + &dml_ctx->v21.mode_programming.programming->plane_programming[dml_plane_index])) + phantoms_added = true; } } } From ff8bc5a68a9a70bdc38d61a72c7a49c56063f9d2 Mon Sep 17 00:00:00 2001 From: William Palacek Date: Wed, 22 Jul 2026 11:20:56 -0400 Subject: [PATCH 235/501] drm/amdkfd: hold event_mutex while checkpointing CRIU events kfd_criu_checkpoint_events() counts the entries in p->event_idr via kfd_get_num_events(), allocates an array sized to that count, and then walks the same IDR to fill it. Neither the count nor the walk holds p->event_mutex. The CRIU checkpoint caller holds only p->mutex. Event create and destroy (kfd_event_create()/kfd_event_destroy()) take p->event_mutex and do not take p->mutex, so a second thread in the same process can insert or remove events between the count and the walk. If an event is inserted, the walk iterates more entries than were counted and writes past the end of the ev_privs allocation; if an event is removed, the walk dereferences an entry that is being freed. Hold p->event_mutex across the count and the walk so both observe a consistent view of p->event_idr. The lock is released before copy_to_user(), which only touches the local buffer. The caller already holds p->mutex and the create/destroy paths never take p->mutex, so the p->mutex -> p->event_mutex order is not inverted and no deadlock is introduced. Fixes: 40e8a766a761 ("drm/amdkfd: CRIU checkpoint and restore events") Signed-off-by: William Palacek Reviewed-by: Alysa Liu Signed-off-by: Alex Deucher (cherry picked from commit ff57e223ab105795b05d3ef3f3c35a5a441bcbaa) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_events.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_events.c b/drivers/gpu/drm/amd/amdkfd/kfd_events.c index 78abfc0f7780..f705c61bdb1d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_events.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_events.c @@ -548,15 +548,27 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, int ret = 0; struct kfd_event *ev; uint32_t ev_id; + uint32_t num_events; - uint32_t num_events = kfd_get_num_events(p); + /* Serialize the count and the walk below against concurrent event + * create/destroy. Those paths take only p->event_mutex, not the + * p->mutex held by the CRIU checkpoint caller, so without this the + * event_idr can grow between kfd_get_num_events() and the loop and the + * walk writes past the ev_privs allocation. + */ + mutex_lock(&p->event_mutex); - if (!num_events) + num_events = kfd_get_num_events(p); + if (!num_events) { + mutex_unlock(&p->event_mutex); return 0; + } ev_privs = kvzalloc(num_events * sizeof(*ev_privs), GFP_KERNEL); - if (!ev_privs) + if (!ev_privs) { + mutex_unlock(&p->event_mutex); return -ENOMEM; + } idr_for_each_entry(&p->event_idr, ev, ev_id) { @@ -597,6 +609,8 @@ int kfd_criu_checkpoint_events(struct kfd_process *p, i++; } + mutex_unlock(&p->event_mutex); + ret = copy_to_user(user_priv_data + *priv_data_offset, ev_privs, num_events * sizeof(*ev_privs)); if (ret) { From 8ccb87b1c9be594fc2c36b0a4006a66f08dee1c8 Mon Sep 17 00:00:00 2001 From: Alan Swanson Date: Mon, 27 Jul 2026 17:01:26 +0100 Subject: [PATCH 236/501] drm/amd/display: Silence link_dpms I2C retimer failures Commit a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C") had also changed the "Set retimer failed" messages from DC_LOG_DEBUG() to DC_LOG_ERROR(). This unfortunately can create log spam. Change those back to DC_LOG_DEBUG() only. Fixes: a4f01bf729b2 ("drm/amd/display: Refactor and fix link_dpms I2C") Closes: https://gitlab.freedesktop.org/drm/amd/-/work_items/5520 Signed-off-by: Alan Swanson Signed-off-by: Alex Deucher (cherry picked from commit da8609eef18b0a3490d0e1fa9440659fadc8194d) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/display/dc/link/link_dpms.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c index 42e7f0e9b3ce..ca5997748799 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_dpms.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_dpms.c @@ -384,7 +384,7 @@ static bool write_i2c_retimer_vga( for (size_t i = 0; i < ARRAY_SIZE(vga_data); i++) { if (!write_i2c_retimer_offset_value(link, address, vga_data[i][0], vga_data[i][1])) { - DC_LOG_ERROR("Set retimer failed, vga index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, vga index: %zu\n", i); return false; } } @@ -405,7 +405,7 @@ static bool write_i2c_retimer_byte( return true; if (!write_i2c_retimer_offset_value(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); + DC_LOG_DEBUG("Set retimer failed, 3g index: 0x%x, value: 0x%x\n", index, value); return false; } @@ -421,14 +421,14 @@ static bool write_i2c_retimer_byte( if (!link_query_ddc_data( link->ddc, address, &offset, 1, &value, 1 )) { - DC_LOG_ERROR("Set retimer failed, link_query_ddc_data\n"); + DC_LOG_DEBUG("Set retimer failed, link_query_ddc_data\n"); return false; } } value |= apply_rx_tx_change; if (!write_i2c_retimer_offset_value(link, address, offset, value)) { - DC_LOG_ERROR("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); + DC_LOG_DEBUG("Set retimer failed, 3g offset: 0x%x, value: 0x%x\n", offset, value); return false; } } @@ -449,7 +449,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, index: %zu\n", i); return false; } } @@ -460,7 +460,7 @@ static bool write_i2c_retimer_setting( uint8_t value = settings->reg_settings_6g[i].i2c_reg_val; if (!write_i2c_retimer_byte(link, address, index, value)) { - DC_LOG_ERROR("Set retimer failed, 6g index: %zu\n", i); + DC_LOG_DEBUG("Set retimer failed, 6g index: %zu\n", i); return false; } } @@ -492,7 +492,7 @@ static bool write_i2c_default_retimer_setting( for (size_t i = 0; i < ARRAY_SIZE(data); i++) { if (!write_i2c_retimer_offset_value(link, address, data[i][0], data[i][1])) { - DC_LOG_ERROR("Set default retimer failed, index: %zu\n", i); + DC_LOG_DEBUG("Set default retimer failed, index: %zu\n", i); return false; } } @@ -524,7 +524,7 @@ static bool write_i2c_redriver_setting( ); if (!success) - DC_LOG_ERROR("Set redriver failed"); + DC_LOG_DEBUG("Set redriver failed"); return success; } From f931c54b241ce2f36bfc34955aec43a188276b8d Mon Sep 17 00:00:00 2001 From: Candice Li Date: Tue, 21 Jul 2026 21:38:58 +0800 Subject: [PATCH 237/501] drm/amdgpu: restore UMD profile pstate after runtime resume Runtime suspend runs GFX hw_fini and clears perfmon clock gating while the UMD profile DPM level remains set in software. Re-apply stable pstate after a successful runtime resume when a profile mode is active. Signed-off-by: Candice Li Reviewed-by: Hawking Zhang Reviewed-by: Yang Wang Signed-off-by: Alex Deucher (cherry picked from commit 138531c8850cc247aa12b104bb29ea387bcdcbb1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 4c0c77eafbd1..1aed121f4ddb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2908,6 +2908,19 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev) return 0; } +static void amdgpu_restore_umd_profile_pstate_after_runpm(struct amdgpu_device *adev) +{ + enum amd_dpm_forced_level level; + uint32_t profile_mode_mask = AMD_DPM_FORCED_LEVEL_PROFILE_STANDARD | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_SCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_MIN_MCLK | + AMD_DPM_FORCED_LEVEL_PROFILE_PEAK; + + level = amdgpu_dpm_get_performance_level(adev); + if (level & profile_mode_mask) + amdgpu_asic_update_umd_stable_pstate(adev, true); +} + static int amdgpu_pmops_runtime_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2952,6 +2965,8 @@ static int amdgpu_pmops_runtime_resume(struct device *dev) if (adev->pm.rpm_mode == AMDGPU_RUNPM_PX) drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; + + amdgpu_restore_umd_profile_pstate_after_runpm(adev); adev->in_runpm = false; return 0; } From 1849a64165ccc23d3e5fc22b8be19227c21c1871 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Mon, 27 Jul 2026 12:23:17 +0800 Subject: [PATCH 238/501] drm/amd/pm: use milliwatts for GPU power sensors GPU average and input power backends report a mix of whole watts, milliwatts, Q24.8 watts and decimal-packed fractions. Q24.8 is inherited from the legacy PowerPlay sensor format. Milliwatts are a more natural unit for the hwmon and pm_info consumers in amdgpu_pm.c. A common decoder cannot distinguish these formats, and converting native milliwatts through Q24.8 also loses precision. Use milliwatts as the internal unit across all PPT and PowerPlay backends. Decode Q24.8 only at the legacy smu7 input boundary and encode it only for the raw amdgpu_sensors debugfs interface. This gives hwmon, pm_info and the sensor ioctl one unambiguous unit while preserving the format used by UMR. Fixes: 5b79d0482f3c ("drm/amd/pp: Remove struct pp_gpu_power") Fixes: 01992b121fb6 ("drm/amd/pm: fix amdgpu_pm_info power display units") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reported-by: Lars Nieradzik Signed-off-by: Alex Deucher (cherry picked from commit 757ba0790bafec47a507e9662bf380f2e027d420) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c | 9 +++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 4 ++-- drivers/gpu/drm/amd/include/kgd_pp_interface.h | 6 ++++-- drivers/gpu/drm/amd/pm/amdgpu_pm.c | 8 +++----- drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c | 4 ++-- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c | 4 ++-- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c | 2 +- drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c | 5 +++-- drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h | 5 +++++ drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c | 6 ++---- drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c | 8 ++++---- drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c | 7 ++++--- drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c | 9 +++------ drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c | 5 +++-- drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c | 4 ++-- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c | 3 ++- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c | 3 +-- drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c | 2 +- drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c | 3 +-- drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c | 3 ++- 27 files changed, 65 insertions(+), 51 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c index 662ec01961f4..df2588642817 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_debugfs.c @@ -43,6 +43,11 @@ #if defined(CONFIG_DEBUG_FS) +/* Encode milliwatts in the raw Q24.8 sensor report format used by UMR. */ +#define AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(power_mw) \ + DIV_ROUND_CLOSEST_ULL((u64)(power_mw) * BIT(8), \ + MILLIWATT_PER_WATT) + /** * amdgpu_debugfs_process_reg_op - Handle MMIO register reads/writes * @@ -1104,6 +1109,10 @@ static ssize_t amdgpu_debugfs_sensor_read(struct file *f, char __user *buf, return r; } + if (idx == AMDGPU_PP_SENSOR_GPU_AVG_POWER || + idx == AMDGPU_PP_SENSOR_GPU_INPUT_POWER) + values[0] = AMDGPU_DEBUGFS_PWR_MW_TO_Q24_8(values[0]); + if (size > valuesize) { amdgpu_virt_disable_access_debugfs(adev); return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 71272f40feef..8bb9c198ddec 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -1210,7 +1210,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return -EINVAL; } } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_GPU_INPUT_POWER: /* get input GPU power */ @@ -1219,7 +1219,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) (void *)&ui32, &ui32_size)) { return -EINVAL; } - ui32 >>= 8; + ui32 /= MILLIWATT_PER_WATT; break; case AMDGPU_INFO_SENSOR_VDDNB: /* get VDDNB in millivolts */ diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 6371f292f2d8..606d48115301 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -24,6 +24,8 @@ #ifndef __KGD_PP_INTERFACE_H__ #define __KGD_PP_INTERFACE_H__ +#include + extern const struct amdgpu_ip_block_version pp_smu_ip_block; extern const struct amdgpu_ip_block_version smu_v11_0_ip_block; extern const struct amdgpu_ip_block_version smu_v12_0_ip_block; @@ -150,8 +152,8 @@ enum amd_pp_sensors { AMDGPU_PP_SENSOR_MEM_TEMP, AMDGPU_PP_SENSOR_VCE_POWER, AMDGPU_PP_SENSOR_UVD_POWER, - AMDGPU_PP_SENSOR_GPU_AVG_POWER, - AMDGPU_PP_SENSOR_GPU_INPUT_POWER, + AMDGPU_PP_SENSOR_GPU_AVG_POWER, /* milliwatts */ + AMDGPU_PP_SENSOR_GPU_INPUT_POWER, /* milliwatts */ AMDGPU_PP_SENSOR_SS_APU_SHARE, AMDGPU_PP_SENSOR_SS_DGPU_SHARE, AMDGPU_PP_SENSOR_STABLE_PSTATE_SCLK, diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index 763947fbf16f..85b089a3dc41 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -41,8 +41,6 @@ #define DEVICE_ATTR_IS(_name) (attr_id == device_attr_id__##_name) -#define power_2_mwatt(power) (((power) >> 8) * 1000 + ((power) & 0xff)) - struct od_attribute { struct kobj_attribute attribute; struct list_head entry; @@ -3351,7 +3349,7 @@ static int amdgpu_hwmon_get_power(struct device *dev, return r; /* convert to microwatts */ - return power_2_mwatt(query) * 1000; + return query * 1000; } static ssize_t amdgpu_hwmon_show_power_avg(struct device *dev, @@ -4919,7 +4917,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a seq_printf(m, "\t%u mV (VDDNB)\n", value); size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_AVG_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (average SoC including CPU)\n", centiwatt / 100, centiwatt % 100); @@ -4928,7 +4926,7 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a } size = sizeof(uint32_t); if (!amdgpu_dpm_read_sensor(adev, AMDGPU_PP_SENSOR_GPU_INPUT_POWER, (void *)&query, &size)) { - mwatt = power_2_mwatt(query); + mwatt = query; centiwatt = DIV_ROUND_CLOSEST(mwatt, 10); if (adev->flags & AMD_IS_APU) seq_printf(m, "\t%u.%02u W (current SoC including CPU)\n", centiwatt / 100, centiwatt % 100); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index bc82ba2e4c9b..f8a5648095d1 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -4062,7 +4062,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) (adev->asic_type != CHIP_FIJI) && (adev->asic_type != CHIP_TONGA)) { smum_send_msg_to_smc_with_parameter(hwmgr, PPSMC_MSG_GetCurrPkgPwr, 0, &tmp); - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); if (tmp != 0) return 0; @@ -4081,7 +4081,7 @@ static int smu7_get_gpu_power(struct pp_hwmgr *hwmgr, u32 *query) if (tmp != 0) break; } - *query = tmp; + *query = PP_PWR_Q24_8_TO_MW(tmp); return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index 4b92b52aba2b..c283309efe87 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -3934,8 +3934,8 @@ static int vega10_get_gpu_power(struct pp_hwmgr *hwmgr, if (ret) return ret; - /* SMC returning actual watts, keep consistent with legacy asics, low 8 bit as 8 fractional bits */ - *query = value << 8; + /* SMC returns whole Watts, while power sensors use milliwatts. */ + *query = value * MILLIWATT_PER_WATT; return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index 69a9074058ce..b466b08e4a67 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -1419,7 +1419,7 @@ static int vega12_get_gpu_power(struct pp_hwmgr *hwmgr, uint32_t *query) if (ret) return ret; - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index 7b8f4c1b80eb..e9c194056e32 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -2154,12 +2154,13 @@ static int vega20_get_gpu_power(struct pp_hwmgr *hwmgr, int idx, switch (idx) { case AMDGPU_PP_SENSOR_GPU_AVG_POWER: if (hwmgr->smu_version == 0x282e00) - *query = metrics_table.AverageSocketPower << 8; + *query = metrics_table.AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; case AMDGPU_PP_SENSOR_GPU_INPUT_POWER: - *query = metrics_table.CurrSocketPower << 8; + *query = metrics_table.CurrSocketPower * MILLIWATT_PER_WATT; break; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index ca71efaa1656..368200f7cf16 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -35,6 +35,11 @@ struct pp_hwmgr; struct phm_fan_speed_info; struct pp_atomctrl_voltage_table; +/* Decode legacy unsigned Q24.8 watts to internal milliwatts. */ +#define PP_PWR_Q24_8_TO_MW(power) \ + DIV_ROUND_CLOSEST_ULL((u64)(power) * MILLIWATT_PER_WATT, \ + BIT(8)) + #define VOLTAGE_SCALE 4 #define VOLTAGE_VID_OFFSET_SCALE1 625 #define VOLTAGE_VID_OFFSET_SCALE2 100 diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c index 54d3dba7d354..cfad8cd74773 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/arcturus_ppt.c @@ -658,7 +658,7 @@ static int arcturus_get_smu_metrics_data(struct smu_context *smu, *value = metrics->VcnActivityPercentage; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c index e6e009df9840..bb9620666dae 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/cyan_skillfish_ppt.c @@ -158,12 +158,10 @@ cyan_skillfish_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.MemclkFrequency; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index cd0457e13f54..6dfc3b585c6b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -602,7 +602,7 @@ static int navi10_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -691,7 +691,7 @@ static int navi10_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -777,7 +777,7 @@ static int navi12_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * @@ -866,7 +866,7 @@ static int navi12_get_smu_metrics_data(struct smu_context *smu, *value = metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->TemperatureEdge * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c index f799e489b481..dccca6ddd5ad 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/sienna_cichlid_ppt.c @@ -855,9 +855,10 @@ static int sienna_cichlid_get_smu_metrics_data(struct smu_context *smu, metrics->AverageUclkActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = use_metrics_v3 ? metrics_v3->AverageSocketPower << 8 : - use_metrics_v2 ? metrics_v2->AverageSocketPower << 8 : - metrics->AverageSocketPower << 8; + *value = use_metrics_v3 ? metrics_v3->AverageSocketPower : + use_metrics_v2 ? metrics_v2->AverageSocketPower : + metrics->AverageSocketPower; + *value *= MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = (use_metrics_v3 ? metrics_v3->TemperatureEdge : diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c index c54675b00a96..3e3c68448ba9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/vangogh_ppt.c @@ -318,8 +318,7 @@ static int vangogh_get_legacy_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / - 1000 ; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * @@ -387,12 +386,10 @@ static int vangogh_get_smu_metrics_data(struct smu_context *smu, *value = metrics->Current.UvdActivity; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->Average.CurrentSocketPower << 8) / - 1000; + *value = metrics->Average.CurrentSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->Current.CurrentSocketPower << 8) / - 1000; + *value = metrics->Current.CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->Current.GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c index 75335da224c7..175c44190caa 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu12/renoir_ppt.c @@ -1215,9 +1215,10 @@ static int renoir_get_smu_metrics_data(struct smu_context *smu, ((amdgpu_ip_version(adev, MP1_HWIP, 0) == IP_VERSION(12, 0, 0)) && (adev->pm.fw_version >= 0x373200))) - *value = metrics->CurrentSocketPower << 8; + *value = metrics->CurrentSocketPower * + MILLIWATT_PER_WATT; else - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = (metrics->GfxTemperature / 100) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c index 9d8b1227388f..18c0b43b93b9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/aldebaran_ppt.c @@ -677,7 +677,8 @@ static int aldebaran_get_smu_metrics_data(struct smu_context *smu, case METRICS_AVERAGE_SOCKETPOWER: /* Valid power data is available only from primary die */ if (aldebaran_is_primary(smu)) - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * + MILLIWATT_PER_WATT; else ret = -EOPNOTSUPP; break; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index acbd7046d8a5..0be34fbdac26 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -807,7 +807,7 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c index fe929bd89058..8d476a60d62f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_12_ppt.c @@ -434,7 +434,8 @@ int smu_v13_0_12_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c index ba91bf590eed..a9d558f5b739 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_4_ppt.c @@ -338,10 +338,10 @@ static int smu_v13_0_4_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_AVERAGE_SOCKETPOWER: - *value = (metrics->AverageSocketPower << 8) / 1000; + *value = metrics->AverageSocketPower; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c index 27372a60e83d..f95b15af436d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_5_ppt.c @@ -295,7 +295,7 @@ static int smu_v13_0_5_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index b12388134489..41608574bc61 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -1321,7 +1321,8 @@ static int smu_v13_0_6_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(GET_METRIC_FIELD(DramBandwidthUtilization, version)); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) << 8; + *value = SMUQ10_ROUND(GET_METRIC_FIELD(SocketPower, version)) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(GET_METRIC_FIELD(MaxSocketTemperature, version)) * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index 42c9ceeb4f7d..5d11ed8be4c3 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -817,7 +817,7 @@ static int smu_v13_0_7_get_smu_metrics_data(struct smu_context *smu, *value = smu_safe_u16_nn(metrics->AverageUclkActivity); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c index 7bf88ffd311b..edc3fedca9f8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/yellow_carp_ppt.c @@ -372,7 +372,7 @@ static int yellow_carp_get_smu_metrics_data(struct smu_context *smu, *value = metrics->UvdActivity / 100; break; case METRICS_CURR_SOCKETPOWER: - *value = (metrics->CurrentSocketPower << 8) / 1000; + *value = metrics->CurrentSocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c index 3d73f2050bbe..568665265a13 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_0_ppt.c @@ -312,8 +312,7 @@ static int smu_v14_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c index a6a88e7b2668..bd054cbfbae0 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu14/smu_v14_0_2_ppt.c @@ -695,7 +695,7 @@ static int smu_v14_0_2_get_smu_metrics_data(struct smu_context *smu, metrics->Vcn1ActivityPercentage); break; case METRICS_AVERAGE_SOCKETPOWER: - *value = metrics->AverageSocketPower << 8; + *value = metrics->AverageSocketPower * MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->AvgTemperature[TEMP_EDGE] * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c index faa7d661f3bf..0da1ffb3a567 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_0_ppt.c @@ -378,8 +378,7 @@ static int smu_v15_0_0_get_smu_metrics_data(struct smu_context *smu, break; case METRICS_AVERAGE_SOCKETPOWER: case METRICS_CURR_SOCKETPOWER: - *value = (metrics->SocketPower / 1000 << 8) + - (metrics->SocketPower % 1000 / 10); + *value = metrics->SocketPower; break; case METRICS_TEMPERATURE_EDGE: *value = metrics->GfxTemperature / 100 * diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c index 2105a1d7bb34..118eda5429ff 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu15/smu_v15_0_8_ppt.c @@ -411,7 +411,8 @@ static int smu_v15_0_8_get_smu_metrics_data(struct smu_context *smu, *value = SMUQ10_ROUND(metrics->DramBandwidthUtilization); break; case METRICS_CURR_SOCKETPOWER: - *value = SMUQ10_ROUND(metrics->SocketPower) << 8; + *value = SMUQ10_ROUND(metrics->SocketPower) * + MILLIWATT_PER_WATT; break; case METRICS_TEMPERATURE_HOTSPOT: *value = SMUQ10_ROUND(metrics->MaxSocketTemperature) * From 5e70f6804b4d6256058c360b10e044ee04ea4a4e Mon Sep 17 00:00:00 2001 From: Harkirat Gill Date: Mon, 27 Jul 2026 14:37:56 -0400 Subject: [PATCH 239/501] drm/amdgpu: cap GTT size to physical RAM on APUs On APUs, the GTT pool is backed by system RAM, but its size is not bound to the non-carveout memory that actually backs it. A user can end up with GTT + VRAM exceeding total physical memory through the following sequence: - Have a large non-carveout memory space (~128GB) and accordingly set a large GTT (~100GB) via the ttm module parameter. - Lower the non-carveout memory space in BIOS by increasing the UMA Frame Buffer Size (VRAM) to 64GB. - The previously set GTT value (~100GB) persists, even though the new non-carveout space (64GB) can no longer back it. This leads to a case where kernel reports GTT (100GB) + VRAM (64GB) despite the sum being greater than total physical memory (128GB). Cap the GTT size to totalram_pages() on APUs. totalram_pages() already excludes the VRAM carveout, so the resulting GTT can never exceed the system RAM that actually backs it. Signed-off-by: Harkirat Gill Reviewed-by: David Francis Assisted-by: Claude:claude-opus-4 Signed-off-by: Alex Deucher (cherry picked from commit 5dafdd649280c7dc6c22c8f877da3f54fcc441e1) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index eb8bbfc7e6d9..5fc91f45db5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -2173,6 +2173,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) gtt_size = configured_size; } + /* Cap GTT so that it does not exceed total physical RAM. */ + if (adev->flags & AMD_IS_APU) { + u64 phys_ram = (u64)totalram_pages() << PAGE_SHIFT; + + if (gtt_size > phys_ram) { + gtt_size = phys_ram; + dev_info(adev->dev, + "Capping GTT to %uM to not exceed available system memory\n", + (unsigned int)(gtt_size / (1024 * 1024))); + } + } + /* Initialize GTT memory pool */ r = amdgpu_gtt_mgr_init(adev, gtt_size); if (r) { From 114b42507b6a23d9d24e24e4ef165233332c64d4 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Thu, 23 Jul 2026 06:25:48 +0200 Subject: [PATCH 240/501] drm/amd/display: use proper context for logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same as the rest of the code, get_ss_info_from_atombios() uses calc_pll_cs->ctx->logger for logging. But calc_pll_cs->ctx is initialized only later in calc_pll_max_vco_construct(). Therefore, any output using DC_LOG_SYNC() leads to a NULL pointer deference in get_ss_info_from_atombios(). According to Sashiko, the very same problem exists in dce112_get_pix_clk_dividers() and dcn3_get_pix_clk_dividers() too. To avoid accessing the NULL context, use clk_src->base.ctx->logger everywhere. That context in base is initialized earlier in dce110_clk_src_construct() and dce112_clk_src_construct(). Before get_ss_info_from_atombios() or Sashiko's get_pix_clk_dividers functions above are actually called. This is done by redefining DC_LOGGER to CTX->logger. Before: dce110_clk_src_construct() did: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses calc_pll_cs->ctx # BOOM -> calc_pll_max_vco_construct() <- sets calc_pll_cs->ctx After: dce110_clk_src_construct() does: -> sets clk_src->base.ctx = ctx; -> ss_info_from_atombios_create() -> get_ss_info_from_atombios() <- uses clk_src->base.ctx Closes: https://bugzilla.suse.com/show_bug.cgi?id=1271175 Closes: https://lore.kernel.org/all/a9ee54e6-2413-4156-9bde-d528ae3c63a3@kernel.org/ Fixes: 1296423bf23c ("drm/amd/display: define DC_LOGGER for logger") Reviewed-by: Bhawanpreet Lakha Signed-off-by: Jiri Slaby (SUSE) Cc: Lakha, Bhawanpreet Cc: Harry Wentland Cc: Leo Li Cc: Rodrigo Siqueira Cc: Alex Deucher Cc: "Christian König" Cc: David Airlie Cc: Simona Vetter Cc: amd-gfx@lists.freedesktop.org Signed-off-by: Alex Deucher (cherry picked from commit 6f16fcbb0c46a87e3d9685407e906573d60104b0) Cc: stable@vger.kernel.org --- .../drm/amd/display/dc/dce/dce_clock_source.c | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c index ecb8493ec523..8e3862f6dac2 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_clock_source.c @@ -45,9 +45,7 @@ clk_src->base.ctx #define DC_LOGGER \ - calc_pll_cs->ctx->logger -#define DC_LOGGER_INIT() \ - struct calc_pll_clock_source *calc_pll_cs = &clk_src->calc_pll + CTX->logger #undef FN #define FN(reg_name, field_name) \ @@ -287,6 +285,7 @@ static bool calc_pll_dividers_in_range( } static uint32_t calculate_pixel_clock_pll_dividers( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct pll_settings *pll_settings) { @@ -474,7 +473,7 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( { uint32_t field = 0; uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); + /* Check if reference clock is external (not pcie/xtalin) * HW Dce80 spec: * 00 - PCIE_REFCLK, 01 - XTALIN, 02 - GENERICA, 03 - GENERICB @@ -517,12 +516,14 @@ static uint32_t dce110_get_pix_clk_dividers_helper ( /*Calculate Dividers by HDMI object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll_hdmi, pll_settings); else /*Calculate Dividers by default object, no SS case or SS case */ pll_calc_error = calculate_pixel_clock_pll_dividers( + clk_src, &clk_src->calc_pll, pll_settings); @@ -568,7 +569,6 @@ static uint32_t dce110_get_pix_clk_dividers( { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); uint32_t pll_calc_error = MAX_PLL_CALC_ERROR; - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -600,7 +600,6 @@ static uint32_t dce112_get_pix_clk_dividers( struct pll_settings *pll_settings) { struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { @@ -1442,8 +1441,6 @@ static uint32_t dcn3_get_pix_clk_dividers( unsigned long long actual_pix_clk_100Hz = pix_clk_params ? pix_clk_params->requested_pix_clk_100hz : 0; struct dce110_clk_src *clk_src = TO_DCE110_CLK_SRC(cs); - DC_LOGGER_INIT(); - if (pix_clk_params == NULL || pll_settings == NULL || pix_clk_params->requested_pix_clk_100hz == 0) { DC_LOG_ERROR( @@ -1513,7 +1510,6 @@ static const struct clock_source_funcs dce110_clk_src_funcs = { .get_dp_dto_frequency_100hz = get_dp_dto_frequency_100hz }; - static void get_ss_info_from_atombios( struct dce110_clk_src *clk_src, enum as_signal_type as_signal, @@ -1526,7 +1522,7 @@ static void get_ss_info_from_atombios( struct spread_spectrum_info *ss_info_cur; struct spread_spectrum_data *ss_data_cur; uint32_t i; - DC_LOGGER_INIT(); + if (ss_entries_num == NULL) { DC_LOG_SYNC( "Invalid entry !!!\n"); @@ -1657,6 +1653,7 @@ static void ss_info_from_atombios_create( } static bool calc_pll_max_vco_construct( + struct dce110_clk_src *clk_src, struct calc_pll_clock_source *calc_pll_cs, struct calc_pll_clock_source_init_data *init_data) { @@ -1808,6 +1805,7 @@ bool dce110_clk_src_construct( ss_info_from_atombios_create(clk_src); if (!calc_pll_max_vco_construct( + clk_src, &clk_src->calc_pll, &calc_pll_cs_init_data)) { ASSERT_CRITICAL(false); @@ -1822,7 +1820,7 @@ bool dce110_clk_src_construct( if (!calc_pll_max_vco_construct( - &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { + clk_src, &clk_src->calc_pll_hdmi, &calc_pll_cs_init_data_hdmi)) { ASSERT_CRITICAL(false); goto unexpected_failure; } From 1833ce36b35426504c64600c94f322437ea44bb2 Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Thu, 16 Jul 2026 06:42:18 -0700 Subject: [PATCH 241/501] mm: memcg: initialize *locked in memcg1_oom_prepare() stub mem_cgroup_oom() passes an uninitialized "locked" to memcg1_oom_prepare() and reads it back in memcg1_oom_finish(): bool locked, ret; ... if (!memcg1_oom_prepare(memcg, &locked)) return false; ret = mem_cgroup_out_of_memory(memcg, mask, order); memcg1_oom_finish(memcg, locked); This relies on memcg1_oom_prepare() setting *locked whenever it returns true. The CONFIG_MEMCG_V1=y version does, but the stub used when CONFIG_MEMCG_V1=n returns true without touching *locked, so memcg1_oom_finish() consumes an uninitialized value. On a memcg OOM this is reported by UBSAN: UBSAN: invalid-load in mm/memcontrol.c:1932:27 load of value 0 is not a valid value for type 'bool' (aka '_Bool') Initialize *locked to false in the stub; with cgroup v1 compiled out there is no OOM lock to take. Link: https://lore.kernel.org/20260716-memcg-oom-uninit-locked-v2-1-63631d878eb4@debian.org Fixes: e93d4166b40a ("mm: memcg: put cgroup v1-specific code under a config option") Signed-off-by: Breno Leitao Reviewed-by: Joshua Hahn Acked-by: Johannes Weiner Reviewed-by: SeongJae Park Acked-by: Shakeel Butt Cc: Michal Hocko Cc: Muchun Song Cc: Roman Gushchin Cc: Shakeel Butt Cc: Signed-off-by: Andrew Morton --- mm/memcontrol-v1.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mm/memcontrol-v1.h b/mm/memcontrol-v1.h index f92f81108d5e..4fa6e2bc8413 100644 --- a/mm/memcontrol-v1.h +++ b/mm/memcontrol-v1.h @@ -107,7 +107,11 @@ static inline void memcg1_remove_from_trees(struct mem_cgroup *memcg) {} static inline void memcg1_soft_limit_reset(struct mem_cgroup *memcg) {} static inline void memcg1_css_offline(struct mem_cgroup *memcg) {} -static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) { return true; } +static inline bool memcg1_oom_prepare(struct mem_cgroup *memcg, bool *locked) +{ + *locked = false; + return true; +} static inline void memcg1_oom_finish(struct mem_cgroup *memcg, bool locked) {} static inline void memcg1_oom_recover(struct mem_cgroup *memcg) {} From 4bd0c3515a041d9aa1558c4e8ea0efdb56509f9a Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 17 Jul 2026 00:44:59 -0600 Subject: [PATCH 242/501] mm: decrement MTHP_STAT_NR_ANON in free_zone_device_folio() Patch series "mm: fix PMD level mTHP accounting bugs", v2. While running selftests I noticed the PMD level per-mTHP stats (nr_anon) remained elevated after each run. After further investigation I noticed this accounting error occurs for both the migration.private_anon_htlb_test and the HMM tests. In the HMM case this is due to folio_add_new_anon_rmap() incrementing the mTHP stats, but never containing a corresponding decrement in free_zone_device_folio(). We solve this by making sure to decrement the counter when freeing device memory. In the migration case, we are incrementing this counter without first checking whether this folio is a hugetlb folio, which relies on a separate accounting system. We solve this by adding the proper hugetlb check before incrementing this counter. With these changes in place, the two tests no longer cause elevated PMD level accounting issues. This patch (of 2): When a zone device folio is mapped as anonymous, folio_add_new_anon_rmap() increments MTHP_STAT_NR_ANON. The corresponding decrement lives in __free_pages_prepare() in page_alloc.c, but zone device folios are freed via free_zone_device_folio() which never calls __free_pages_prepare(). This causes nr_anon to remain permanently elevated after zone device folios are freed. Add the missing mod_mthp_stat() decrement to free_zone_device_folio() so that the counter is properly balanced. Link: https://lore.kernel.org/20260717064502.1980173-1-npache@redhat.com Link: https://lore.kernel.org/20260717064502.1980173-2-npache@redhat.com Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size") Co-developed-by: David Hildenbrand Signed-off-by: David Hildenbrand Signed-off-by: Nico Pache Reviewed-by: Zi Yan Cc: Alistair Popple Cc: Barry Song Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Oscar Salvador Cc: Rakie Kim Cc: Baolin Wang Signed-off-by: Andrew Morton --- mm/memremap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mm/memremap.c b/mm/memremap.c index 81766d822400..accba23aef28 100644 --- a/mm/memremap.c +++ b/mm/memremap.c @@ -425,6 +425,7 @@ void free_zone_device_folio(struct folio *folio) mem_cgroup_uncharge(folio); if (folio_test_anon(folio)) { + mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, -1); for (i = 0; i < nr; i++) __ClearPageAnonExclusive(folio_page(folio, i)); } From d09a8fd521476950079ee01a1408e6f5b7a0aa3e Mon Sep 17 00:00:00 2001 From: Nico Pache Date: Fri, 17 Jul 2026 00:45:00 -0600 Subject: [PATCH 243/501] mm/migrate: exclude hugetlb folios from MTHP_STAT_NR_ANON accounting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __folio_migrate_mapping() increments MTHP_STAT_NR_ANON for the destination folio when `folio_test_anon(folio) && folio_test_large(folio)` is true. However, hugetlb folios satisfy both conditions despite having a completely separate accounting system — they use hugetlb_add_anon_rmap() which does not touch mTHP stats, and their free path also bypasses the mTHP decrement in __free_pages_prepare(). This causes MTHP_STAT_NR_ANON to be incremented on each hugetlb migration without a corresponding decrement, permanently inflating the nr_anon counter. Add a !folio_test_hugetlb() check to __folio_migrate_mapping() so that only actual mTHP folios are counted. Link: https://lore.kernel.org/20260717064502.1980173-3-npache@redhat.com Fixes: 5d65c8d758f2 ("mm: count the number of anonymous THPs per size") Co-developed-by: David Hildenbrand Signed-off-by: David Hildenbrand Signed-off-by: Nico Pache Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Cc: Alistair Popple Cc: Barry Song Cc: Byungchul Park Cc: Gregory Price Cc: "Huang, Ying" Cc: Joshua Hahn Cc: Lorenzo Stoakes Cc: Matthew Brost Cc: Matthew Wilcox (Oracle) Cc: Oscar Salvador Cc: Rakie Kim Signed-off-by: Andrew Morton --- mm/migrate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mm/migrate.c b/mm/migrate.c index d9b23909d716..dd15a84b2a52 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -590,7 +590,8 @@ static int __folio_migrate_mapping(struct address_space *mapping, /* No turning back from here */ newfolio->index = folio->index; newfolio->mapping = folio->mapping; - if (folio_test_anon(folio) && folio_test_large(folio)) + if (folio_test_anon(folio) && folio_test_large(folio) && + !folio_test_hugetlb(folio)) mod_mthp_stat(folio_order(folio), MTHP_STAT_NR_ANON, 1); if (folio_test_swapbacked(folio)) __folio_set_swapbacked(newfolio); From 45ebe4540817cb540a59e4dc04014ac5dddc9990 Mon Sep 17 00:00:00 2001 From: Jiakai Xu Date: Thu, 16 Jul 2026 11:53:25 +0000 Subject: [PATCH 244/501] riscv/mm: use physical alignment for vmemmap_start_pfn RISC-V computes vmemmap_start_pfn by rounding phys_ram_base down to VMEMMAP_ADDR_ALIGN. That alignment must therefore be expressed in the physical-address domain. Commit 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size") attempted to account for the maximal folio alignment by feeding MAX_FOLIO_VMEMMAP_ALIGN directly into VMEMMAP_ADDR_ALIGN. However, MAX_FOLIO_VMEMMAP_ALIGN is measured in bytes of struct page storage, whereas VMEMMAP_ADDR_ALIGN is used to align a physical address. The mask-based compound_info encoding requires pfn_to_page(0) to be naturally aligned to MAX_FOLIO_VMEMMAP_ALIGN. Commit 9f94db4c7eaa ("mm/sparse: check memmap alignment for compound_info_has_mask()") added a check for that requirement and exposed the unit mismatch on systems such as QEMU virt, where the DRAM base is not aligned to MAX_FOLIO_NR_PAGES * PAGE_SIZE. Here is the log: [ 0.000000][ C0] ------------[ cut here ]------------ [ 0.000000][ C0] WARNING: mm/sparse.c:365 at sparse_init+0x58a/0x6fe, CPU#0: swapper/0 [ 0.000000][ C0] Modules linked in: [ 0.000000][ C0] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 7.2.0-rc3-g1d8304bdd65f #2 PREEMPT [ 0.000000][ C0] Hardware name: riscv-virtio,qemu (DT) [ 0.000000][ C0] epc : sparse_init+0x58a/0x6fe [ 0.000000][ C0] ra : sparse_init+0x58a/0x6fe [ 0.000000][ C0] epc : ffffffff86851c88 ra : ffffffff86851c88 sp : ffffffff88807a30 [ 0.000000][ C0] gp : ffffffff8a3bf240 tp : ffffffff88842080 t0 : ff600000ffab6000 [ 0.000000][ C0] t1 : 000000017fab6000 t2 : 65203a6573726363 s0 : ffffffff88807bc0 [ 0.000000][ C0] s1 : 000000000e000000 a0 : 0000000000000007 a1 : 0000000000000000 [ 0.000000][ C0] a2 : 0000000000000002 a3 : ffffffff86851c88 a4 : 0000000000000000 [ 0.000000][ C0] a5 : ffffffff88843080 a6 : 0000000000000003 a7 : 0000000000000000 [ 0.000000][ C0] s2 : ff60000000000000 s3 : 0040000000000000 s4 : 0004000000000000 [ 0.000000][ C0] s5 : ffffffff8a4d92e0 s6 : ff600000ffab55e0 s7 : ffffffff88384d00 [ 0.000000][ C0] s8 : 0000000000000003 s9 : ffffffff88384cc1 s10: ffffffff88384cc0 [ 0.000000][ C0] s11: ffffffff8a4daae0 t3 : ffffffff915e8b20 t4 : ffffffff915e8b20 [ 0.000000][ C0] t5 : ffffffff915e8b20 t6 : ffffffff915e8bc8 ssp : 0000000000000000 [ 0.000000][ C0] status: 0000000200000100 badaddr: ffffffff86851c88 cause: 0000000000000003 [ 0.000000][ C0] [] sparse_init+0x58a/0x6fe [ 0.000000][ C0] [] mm_core_init_early+0x116/0x1e30 [ 0.000000][ C0] [] start_kernel+0xd2/0x848 Convert MAX_FOLIO_VMEMMAP_ALIGN to the equivalent physical alignment before using it in VMEMMAP_ADDR_ALIGN. This keeps the existing round_down() logic while making the resulting vmemmap base satisfy the mask-alignment requirement. Link: https://lore.kernel.org/20260716115326.3466926-1-xujiakai2025@iscas.ac.cn Fixes: 476849b0fba4 ("riscv/mm: align vmemmap to maximal folio size") Signed-off-by: Jiakai Xu Reviewed-by: Kiryl Shutsemau (Meta) Cc: Albert Ou Cc: Alexandre Ghiti Cc: David Hildenbrand Cc: Guo Ren Cc: Mike Rapoport Cc: Muchun Song Cc: Nam Cao Cc: Palmer Dabbelt Cc: Vishal Moola (Oracle) Assisted-by: YuanSheng:DeepSeek-V4-Flash Cc: Signed-off-by: Andrew Morton --- arch/riscv/mm/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 3e450890be07..422efa11824b 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -63,7 +63,8 @@ EXPORT_SYMBOL(phys_ram_base); #ifdef CONFIG_SPARSEMEM_VMEMMAP #define VMEMMAP_ADDR_ALIGN max(1ULL << SECTION_SIZE_BITS, \ - MAX_FOLIO_VMEMMAP_ALIGN) + PFN_PHYS(MAX_FOLIO_VMEMMAP_ALIGN / \ + sizeof(struct page))) unsigned long vmemmap_start_pfn __ro_after_init; EXPORT_SYMBOL(vmemmap_start_pfn); From e923bd21058ea02fd0dcd3549d151d143fd036e5 Mon Sep 17 00:00:00 2001 From: "Kiryl Shutsemau (Meta)" Date: Thu, 16 Jul 2026 10:54:24 +0100 Subject: [PATCH 245/501] mm/huge_memory: unlock i_mmap_rwsem before releasing after-split folios __folio_split() keeps dereferencing the mapping after the split: shmem_uncharge(mapping->host) and remap_page() while the folios are still frozen/locked, and i_mmap_unlock_read(mapping) at the very end, after the after-split folios have been unlocked and freed. Nothing holds an inode reference across that. The split relies on @folio -- which the beyond-EOF drop loop never removes, as it starts at folio_next(folio) -- staying locked and in the page cache to hold off eviction. But the unlock loop unlocks @folio before i_mmap_unlock_read() runs. If the caller's @lock_at is a tail beyond EOF, as memory_failure() passes when splitting a poisoned tail of a shmem THP that reaches past i_size during truncation, it too is gone from the page cache; so once @folio is unlocked no locked, in-cache folio pins the inode, and a concurrent final iput() can evict and RCU-free it before i_mmap_unlock_read() touches i_mmap_rwsem: BUG: KASAN: slab-use-after-free in __up_read+0x634/0x790 i_mmap_unlock_read include/linux/fs.h:537 [inline] __folio_split+0x732/0x1640 mm/huge_memory.c:4100 try_to_split_thp_page+0xab/0x390 mm/memory-failure.c:1675 memory_failure+0x1394/0x26e0 mm/memory-failure.c:2470 Freed by task 4601: shmem_free_in_core_inode+0x54/0xb0 mm/shmem.c:5177 evict+0x57f/0xac0 fs/inode.c:870 Do every mapping dereference while @folio still pins the inode: drop i_mmap_rwsem right after remap_page(), before the loop that unlocks and frees the after-split folios, and clear @mapping so the exit path does not unlock it again. shmem_uncharge() and remap_page() already run before that point, so after this nothing past the unlock loop touches the inode or the mapping. This is now a rule the split depends on, alongside keeping @folio frozen until the page cache is updated: no inode or mapping dereference once the after-split folios start being unlocked. Link: https://lore.kernel.org/20260716095424.471052-1-kirill@shutemov.name Fixes: baa355fd3314 ("thp: file pages support for split_huge_page()") Signed-off-by: Kiryl Shutsemau (Meta) Reported-by: Hao Zhang Closes: https://lore.kernel.org/linux-mm/20260710071344.GA106129@zh-pc Co-developed-by: Hao Zhang Signed-off-by: Hao Zhang Acked-by: David Hildenbrand (Arm) Reviewed-by: Zi Yan Reviewed-by: Baolin Wang Reviewed-by: Miaohe Lin Cc: Baolin Wang Cc: Barry Song Cc: Dev Jain Cc: Lance Yang Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Naoya Horiguchi Cc: Nico Pache Cc: Ryan Roberts Cc: Signed-off-by: Andrew Morton --- mm/huge_memory.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mm/huge_memory.c b/mm/huge_memory.c index 032702a4637b..58cabe6af33d 100644 --- a/mm/huge_memory.c +++ b/mm/huge_memory.c @@ -4113,6 +4113,18 @@ static int __folio_split(struct folio *folio, unsigned int new_order, remap_page(folio, 1 << old_order, ttu_flags); + /* + * Drop the mapping while the inode is still pinned. @folio stays + * locked and present in the page cache until the loop below, so + * eviction cannot free the inode yet; @lock_at is not enough, it may + * be a tail beyond EOF that the split already dropped from the page + * cache. Nothing past this point may touch the inode or the mapping. + */ + if (mapping) { + i_mmap_unlock_read(mapping); + mapping = NULL; + } + /* * Unlock all after-split folios except the one containing * @lock_at page. If @folio is not split, it will be kept locked. From 598356522b3a7c337fbf643561ee9b2ba868840e Mon Sep 17 00:00:00 2001 From: Burak Emir Date: Sat, 18 Jul 2026 17:13:03 +0200 Subject: [PATCH 246/501] MAINTAINERS: update address for Burak Emir Update MAINTAINERS and .mailmap to point to my gmail.com address: burak.emir@gmail.com. Link: https://lore.kernel.org/20260718151303.2649-1-burak.emir@gmail.com Signed-off-by: Burak Emir Cc: Alice Ryhl Cc: Jakub Kacinski Cc: Martin Kepplinger Cc: Yury Norov (NVIDIA) Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index a8be42f87e02..de68ca3cbd23 100644 --- a/.mailmap +++ b/.mailmap @@ -176,6 +176,7 @@ Brian Cain Brian King Brian Silverman Bryan Tan +Burak Emir Cai Huoqing Casey Connolly Casey Connolly diff --git a/MAINTAINERS b/MAINTAINERS index 2f9472c1a090..39c9413355f3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4634,7 +4634,7 @@ F: rust/helpers/cpumask.c BITMAP API [RUST] M: Alice Ryhl -M: Burak Emir +M: Burak Emir R: Yury Norov S: Maintained F: lib/find_bit_benchmark_rust.rs From 1fcac73551425c6924cca5cc29f10a5caf80907b Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Sat, 18 Jul 2026 10:29:23 -0700 Subject: [PATCH 247/501] arm64, mailmap: update email address for Peter Collingbourne I am no longer at Google. Link: https://lore.kernel.org/20260718172923.8297-1-peter@pcc.me.uk Signed-off-by: Peter Collingbourne Cc: Catalin Marinas Cc: Ian Rogers Cc: Jakub Kacinski Cc: Martin Kepplinger Cc: Nick Desaulniers Cc: Will Deacon Signed-off-by: Andrew Morton --- .mailmap | 1 + arch/arm64/kernel/pi/relocate.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index de68ca3cbd23..c9c3a6b2b1f5 100644 --- a/.mailmap +++ b/.mailmap @@ -696,6 +696,7 @@ Paulo Alcantara Paulo Alcantara Pavankumar Kondeti Peter A Jonsson +Peter Collingbourne Peter Hilber Peter Oruba Peter Oruba diff --git a/arch/arm64/kernel/pi/relocate.c b/arch/arm64/kernel/pi/relocate.c index 2407d2696398..82592f3a5c1c 100644 --- a/arch/arm64/kernel/pi/relocate.c +++ b/arch/arm64/kernel/pi/relocate.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright 2023 Google LLC // Authors: Ard Biesheuvel -// Peter Collingbourne +// Peter Collingbourne #include #include From 8c7372c5b155fff151a111f03ec0fb87c4c063fe Mon Sep 17 00:00:00 2001 From: "Nico Pache (Red Hat)" Date: Tue, 21 Jul 2026 04:18:11 -0600 Subject: [PATCH 248/501] MAINTAINERS: update Nico Pache's email address Switch my entry in MAINTAINERS and .mailmap to my @linux.dev email address Link: https://lore.kernel.org/20260721101811.115954-1-nico.pache@linux.dev Signed-off-by: Nico Pache Signed-off-by: Nico Pache (Red Hat) Acked-by: David Hildenbrand (Arm) Signed-off-by: Andrew Morton --- .mailmap | 1 + MAINTAINERS | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.mailmap b/.mailmap index c9c3a6b2b1f5..ca6dc2575802 100644 --- a/.mailmap +++ b/.mailmap @@ -649,6 +649,7 @@ Nicholas Piggin Nicholas Piggin Nicholas Piggin Nicolas Ferre +Nico Pache Nicolas Pitre Nicolas Pitre Nicolas Saenz Julienne diff --git a/MAINTAINERS b/MAINTAINERS index 39c9413355f3..48b2baa1541f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17253,7 +17253,7 @@ M: Lorenzo Stoakes R: Zi Yan R: Baolin Wang R: Liam R. Howlett -R: Nico Pache +R: Nico Pache R: Ryan Roberts R: Dev Jain R: Barry Song From dc37771a43d4a8762f05060c28463b0c20e6dc9b Mon Sep 17 00:00:00 2001 From: Richard Chang Date: Mon, 20 Jul 2026 04:41:03 +0000 Subject: [PATCH 249/501] mm: vmscan: abort proactive reclaim early when freezing for suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proactive reclaim (triggered via memory.reclaim or node sysfs) checks for pending signals in its outer loop in user_proactive_reclaim(). However, the inner reclaim loops—specifically scanning cgroups in shrink_many() and evicting/aging folios in try_to_shrink_lruvec()—can run for a long time before returning to the outer loop, especially on systems with many cgroups or large memory sizes. During system suspend, the PM freezer attempts to freeze all tasks by sending fake signals (setting TIF_SIGPENDING). Because the inner loops do not check for pending signals, the proactive reclaim task can remain stuck in kernel space for seconds, failing to enter the refrigerator in a timely manner. This leads to suspend failures due to freeze timeouts, a behavior observed on Android devices. This latency issue is specific to proactive reclaim because of its large, user-defined reclaim targets (could be gigabytes). Since commit 287d5fedb377 ("mm: memcg: use larger batches for proactive reclaim"), proactive reclaim uses larger decaying batch sizes (starting at 1/4 of the remaining target) to maintain throughput. This keeps the task in the inner reclaim loop for extended periods. In contrast, reactive reclaim (global/memcg) uses small targets (SWAP_CLUSTER_MAX, typically 32 pages), allowing it to return to the outer loop and check signals frequently. To fix this, add a signal_pending() check to should_abort_scan() for proactive reclaim paths. Since should_abort_scan() is called within the inner scanning and eviction loops, this allows proactive reclaim to abort early and return to the outer loop in user_proactive_reclaim(). Additionally, return -ERESTARTSYS instead of -EINTR in user_proactive_reclaim(). When interrupted by system suspend, returning -ERESTARTSYS allows the task to enter the refrigerator and automatically restart the syscall upon resume, making the freezer transparent to userspace. For real signals, the signal layer will either restart the syscall (if SA_RESTART is set) or return -EINTR to userspace. This fix specifically targets Multi-Gen LRU (MGLRU). Classic LRU's scan targets per iteration are strictly bounded by get_scan_count(), which ensures it returns to the outer loop more frequently. The check in should_abort_scan() is limited to proactive reclaim (sc->proactive) to avoid inadvertently affecting reactive reclaim paths, and is wrapped in unlikely() as it is a slow path. Link: https://lore.kernel.org/20260720044103.905191-1-richardycc@google.com Fixes: 287d5fedb377 ("mm: memcg: use larger batches for proactive reclaim") Fixes: 94968384dde1 ("memcg: introduce per-memcg reclaim interface") Suggested-by: Michal Hocko Suggested-by: Oleg Nesterov Signed-off-by: Richard Chang Acked-by: Michal Hocko Cc: Axel Rasmussen Cc: Barry Song Cc: David Hildenbrand Cc: Johannes Weiner Cc: Kairui Song Cc: Lorenzo Stoakes Cc: Martin Liu Cc: Minchan Kim Cc: Shakeel Butt Cc: Suren Baghdasaryan Cc: T.J. Mercier Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- mm/vmscan.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mm/vmscan.c b/mm/vmscan.c index 1a142c58700d..56708d1d2dfd 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -4926,6 +4926,9 @@ static bool should_abort_scan(struct lruvec *lruvec, struct scan_control *sc) int i; enum zone_watermarks mark; + if (unlikely(sc->proactive && signal_pending(current))) + return true; + if (sc->nr_reclaimed >= max(sc->nr_to_reclaim, compact_gap(sc->order))) return true; @@ -7906,8 +7909,15 @@ int user_proactive_reclaim(char *buf, unsigned long batch_size = (nr_to_reclaim - nr_reclaimed) / 4; unsigned long reclaimed; + /* + * Return -ERESTARTSYS to allow the freezer to interrupt the + * task. The syscall will be transparently restarted upon + * resume. For real signals, it either restarts the syscall + * (if SA_RESTART is set) or is converted to -EINTR by the + * signal layer. + */ if (signal_pending(current)) - return -EINTR; + return -ERESTARTSYS; /* * This is the final attempt, drain percpu lru caches in the From 0b45f6927a14914ff685fe0e6f9d11232a1e03df Mon Sep 17 00:00:00 2001 From: Link Lin Date: Tue, 21 Jul 2026 00:55:33 +0000 Subject: [PATCH 250/501] mm/page_reporting: use system_freezable_wq to fix UAF during suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During PM freeze (e.g. S3 suspend or S4 hibernation), device drivers like virtio_balloon reset their underlying virtio devices and delete their virtqueues via vdev->config->del_vqs(). However, page reporting work (page_reporting_process) was scheduled on the global system_wq. Because system_wq lacks the WQ_FREEZABLE flag, the PM freezer skips it, leaving page_reporting_process active during suspend. If pages are freed into the buddy allocator while suspending (for example, when core MM invokes the balloon shrinker during S4 hibernation image saving), page reporting triggers virtballoon_free_page_report() on deleted virtqueues, resulting in a Use-After-Free / General Protection Fault: [ 196.795226] general protection fault, probably for non-canonical address 0xaa1436fe70dae6df: 0000 [#1] SMP NOPTI [ 196.825967] Workqueue: events page_reporting_process [ 196.831038] RIP: 0010:virtqueue_add_split+0x233/0x4c0 [virtio_ring] [ 196.927073] virtballoon_free_page_report+0x3a/0xe0 [virtio_balloon] [ 196.946943] page_reporting_process+0x370/0x4f0 Fix this by switching page reporting work to system_freezable_wq. This ensures that the PM freezer pauses page_reporting_process before device drivers destroy their reporting virtqueues. Because the reporting worker is frozen, memory reclamation/freeing (e.g. via shrinker execution) can safely return pages to MM during freeze without triggering unfrozen reporting work on deleted virtqueues. This aligns with the driver's existing design. The comment in virtballoon_freeze() states: /* * The workqueue is already frozen by the PM core before this * function is called. */ Testing: I have verified these fixes using Google’s virtualization infrastructure by running continuous suspend/resume iterations (40+ cycles) while churning memory using stress-ng (`stress-ng --vm 4 --vm-bytes 60% --timeout 1`) to constantly create free pages for the buddy allocator. We also set the `page_reporting_order` parameter to 0 to make the page reporting worker highly sensitive, forcing it to pick up any 4K free pages. This confirmed that the UAF crashes are no longer reproducible. Link: https://lore.kernel.org/20260721005603.1710551-1-linkl@google.com Fixes: 36e66c554b5c ("mm: introduce Reported pages") Signed-off-by: Link Lin Suggested-by: David Hildenbrand (Arm) Suggested-by: Michael S. Tsirkin Acked-by: David Rientjes Acked-by: David Hildenbrand (Arm) Acked-by: Michael S. Tsirkin Cc: Alexander Duyck Cc: Greg Thelen Cc: James Houghton Cc: Jason Wang Cc: Jiaqi Yan Cc: Vlastimil Babka Cc: Xuan Zhuo Cc: Signed-off-by: Andrew Morton --- mm/page_reporting.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mm/page_reporting.c b/mm/page_reporting.c index 942e84b6908a..3e30731b940e 100644 --- a/mm/page_reporting.c +++ b/mm/page_reporting.c @@ -80,7 +80,8 @@ __page_reporting_request(struct page_reporting_dev_info *prdev) * now we are limiting this to running no more than once every * couple of seconds. */ - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } /* notify prdev of free page reporting request */ @@ -340,7 +341,8 @@ static void page_reporting_process(struct work_struct *work) */ state = atomic_cmpxchg(&prdev->state, state, PAGE_REPORTING_IDLE); if (state == PAGE_REPORTING_REQUESTED) - schedule_delayed_work(&prdev->work, PAGE_REPORTING_DELAY); + queue_delayed_work(system_freezable_wq, &prdev->work, + PAGE_REPORTING_DELAY); } static DEFINE_MUTEX(page_reporting_mutex); From 8f6f9fd93cd7a5dd607ad5cd910476dd68fff3ed Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:45 +0200 Subject: [PATCH 251/501] selftests/clone3: fix wild pointer access of getline due to missing init Patch series "selftests: Add missing initalization of pointer passed to getline", v2. This patch (of 2): Clone3_set_tid uses getline(&line, ...) in a loop to read the child's process status. The code expects that getline allocates the buffer for the line on the first loop iteration. According to the Open Group Spec[1], char *line has to be null pointer for this: > ssize_t getline(char **restrict lineptr, ...); > If *lineptr is a null pointer or if the object pointed to by *lineptr > is of insufficient size, an object shall be allocated as if by malloc() > or the object shall be reallocated as if by realloc()[...]. However, char *line is only declared, leading to an undefined value that is potentially non-null. In an example run with Musl v1.2.6, the realloc call[2] of getdelim, which implements getline, triggers a segfault: ./run_kselftest.sh --test clone3:clone3_set_tid [ 1366.165898] kselftest: Running tests in clone3 ... [ 1367.799244] clone3_set_tid[811]: unhandled signal 11 code 0x1 at 0x0000000000000000 in libc.so[68184,3fbf69f000+4c000] [ 1367.802808] CPU: 0 UID: 0 PID: 811 Comm: clone3_set_tid Not tainted .. [ 1367.804188] epc: 0x0000003fbf6b0184 [ 1367.804188] ra : 0x0000003fbf6d4664 [ 1367.804188] sp : 0x0000003fce5f2e40 [ 1367.805314] gp : 0x0000002aaab0dfb8 [ 1367.805314] tp : 0x0000003fbf6f14a8 [ 1367.805314] t0 : 0x0000003fbf63d000 ... Looking at the realloc implementation, Musl mallocs for a null pointer memory. But for a non-null pointer, it assumes it's passed a valid pointer to the heap and tries to access its meta-data. This leads to the segfault we see: void *realloc(void *p, size_t n) { if (!p) return malloc(n); if (size_overflows(n)) return 0; struct meta *g = get_meta(p); ... } Fix this by properly initializing the line pointer to NULL. Link: https://lore.kernel.org/20260722130246.2135563-1-christian.gellermann@codasip.com Link: https://lore.kernel.org/20260722130246.2135563-2-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Link: https://git.musl-libc.org/cgit/musl/tree/src/stdio/getdelim.c#n38 [2] Fixes: 41585bbeeef9 ("selftests: add tests for clone3() with *set_tid") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/clone3/clone3_set_tid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/clone3/clone3_set_tid.c b/tools/testing/selftests/clone3/clone3_set_tid.c index 5c944aee6b41..485efa7c9eed 100644 --- a/tools/testing/selftests/clone3/clone3_set_tid.c +++ b/tools/testing/selftests/clone3/clone3_set_tid.c @@ -141,7 +141,7 @@ int main(int argc, char *argv[]) { FILE *f; char buf; - char *line; + char *line = NULL; int status; int ret = -1; size_t len = 0; From 9f1d75a4ce04095afdb63d8e540092ff8151dacf Mon Sep 17 00:00:00 2001 From: Chris Gellermann Date: Wed, 22 Jul 2026 15:02:46 +0200 Subject: [PATCH 252/501] selftests/mm: fix potential wild pointer access of getline due to missing init This is another occurrence of using getline where the code assumes that getline allocates memory to store the line, but the pointer passed to it is uninitialized and potentially a non-null pointer. This violates the Open Group Spec[1] and caused a segfault in a similar situation in selftest/clone3/clone3_set_tid. Fix it by initializing the line pointer to NULL. The issue has been found by simply grepping through the selftest code after running into the issue in clone3_set_tid. Whether it segfaults in its current state is unknown to me. But it's good to be addressed due to defensive reasons. Link: https://lore.kernel.org/20260722130246.2135563-3-christian.gellermann@codasip.com Link: https://pubs.opengroup.org/onlinepubs/9799919799/functions/getline.html [1] Fixes: 26b4224d9961 ("selftests: expanding more mlock selftest") Signed-off-by: Chris Gellermann Acked-by: David Hildenbrand (arm) Reviewed-by: Lorenzo Stoakes Cc: Christian Brauner Cc: Liam R. Howlett Cc: Michal Hocko Cc: Mike Rapoport Cc: Shuah Khan Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Signed-off-by: Andrew Morton --- tools/testing/selftests/mm/mlock-random-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/mm/mlock-random-test.c b/tools/testing/selftests/mm/mlock-random-test.c index 9d349c151360..16294bc7dae6 100644 --- a/tools/testing/selftests/mm/mlock-random-test.c +++ b/tools/testing/selftests/mm/mlock-random-test.c @@ -84,7 +84,7 @@ int get_proc_locked_vm_size(void) int get_proc_page_size(unsigned long addr) { FILE *smaps; - char *line; + char *line = NULL; unsigned long mmupage_size = 0; size_t size; From 2e1d2e65e773d67dab163127f11a47dab0fbca9f Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 9 Jul 2026 10:01:33 +0900 Subject: [PATCH 253/501] ata: libata-scsi: terminate deferred commands on time out If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary SCSI EH task wake-up additional time by scheduling a retry of all waiting deferred QCs, using the eh_timed_out SCSI host template operation. The function ata_scsi_eh_timed_out() is introduced to implement this operation. However, terminating deferred commands with DID_REQUEUE to force a retry by calling the function ata_scsi_requeue_deferred_qc() may still keep the SCSI host in a busy state because the block layer may immediately re-issue these commands. The solution to this is to schedule libata EH for the port which suffered the command timeout to prevent accepting any new command. ata_scsi_requeue_deferred_qc() is modified to add a call to ata_port_schedule_eh() for this purpose. In addition to this change, ata_scsi_requeue_deferred_qc() is also modified to take a new timedout_scmd scsi command argument which indicates the SCSI command that timed out. With this additional argument, ata_scsi_requeue_deferred_qc() can now also terminate with DID_TIME_OUT any timed out deferred qc, which simplifies ata_scsi_cmd_error_handler(). In this case, ata_scsi_requeue_deferred_qc() returns SCSI_EH_DONE, with this return value propagated back to the ata_scsi_eh_timed_out() operation to indicate to scsi_timeout() that the timed out command was handled and no further processing is needed. For non-timed out deferred qc that need to be retried, ata_scsi_requeue_deferred_qc() returns SCSI_EH_NOT_HANDLED, thus indicating to scsi_timeout() that the timed out command needs to go through the SCSI EH (and libata EH) processing by adding it to the EH work queue with scsi_eh_scmd_add(). One side effect of these changes is that the function atapi_qc_complete() needs to be modified to ensure that a deferred ATAPI command that needs to be retried is completed with DID_REQUEUE instead of the default SAM_STAT_GOOD status, and a command that timed out is completed with DID_TIME_OUT instead of SAM_STAT_CHECK_CONDITION. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: Igor Pylypiv Tested-by: Igor Pylypiv Reviewed-by: Niklas Cassel Reviewed-by: Martin K. Petersen --- drivers/ata/libata-eh.c | 29 ++-------- drivers/ata/libata-scsi.c | 112 ++++++++++++++++++++++++++++++++++---- drivers/ata/libata.h | 3 +- include/linux/libata.h | 2 + 4 files changed, 111 insertions(+), 35 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 46947ed0c657..c154103d892c 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -658,29 +658,12 @@ int ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap, set_host_byte(scmd, DID_OK); ata_qc_for_each_raw(ap, qc, i) { - if (qc->scsicmd != scmd) - continue; - if ((qc->flags & ATA_QCFLAG_ACTIVE) || - qc == qc->dev->link->deferred_qc) + if (qc->scsicmd == scmd && + qc->flags & ATA_QCFLAG_ACTIVE) break; } - if (i < ATA_MAX_QUEUE && qc == qc->dev->link->deferred_qc) { - /* - * This is a deferred command that timed out while - * waiting for the command queue to drain. Since the qc - * is not active yet (deferred_qc is still set, so the - * deferred qc work has not issued the command yet), - * simply signal the timeout by finishing the SCSI - * command and clear the deferred qc to prevent the - * deferred qc work from issuing this qc. - */ - WARN_ON_ONCE(qc->flags & ATA_QCFLAG_ACTIVE); - qc->dev->link->deferred_qc = NULL; - cancel_work(&qc->dev->link->deferred_qc_work); - set_host_byte(scmd, DID_TIME_OUT); - scsi_eh_finish_cmd(scmd, &ap->eh_done_q); - } else if (i < ATA_MAX_QUEUE) { + if (i < ATA_MAX_QUEUE) { /* the scmd has an associated qc */ if (!(qc->flags & ATA_QCFLAG_EH)) { /* which hasn't failed yet, timeout */ @@ -956,10 +939,10 @@ static void ata_eh_set_pending(struct ata_port *ap, bool fastdrain) ap->pflags |= ATA_PFLAG_EH_PENDING; /* - * If we have a deferred qc, requeue it so that it is retried once EH - * completes. + * If we have deferred QCs, requeue them so that the SCSI EH task can + * run. */ - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); if (!fastdrain) return; diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 5868526301a2..02b04de925f9 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1685,26 +1685,80 @@ void ata_scsi_deferred_qc_work(struct work_struct *work) spin_unlock_irqrestore(ap->lock, flags); } -void ata_scsi_requeue_deferred_qc(struct ata_port *ap) +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *timedout_scmd) { + enum scsi_timeout_action action = SCSI_EH_NOT_HANDLED; + struct ata_queued_cmd *qc; struct ata_link *link; + u32 host_byte; lockdep_assert_held(ap->lock); /* - * If we have a deferred qc when a reset occurs or NCQ commands fail, - * do not try to be smart about what to do with this deferred command - * and simply requeue it by completing it with DID_REQUEUE. + * If we have deferred QCs when a reset, a timeout or an NCQ command + * fails, do not try to be smart about what to do with the deferred + * commands and simply terminate them and let the SCSI layer decide + * what to do. */ ata_for_each_link(link, ap, PMP_FIRST) { - struct ata_queued_cmd *qc = link->deferred_qc; + qc = link->deferred_qc; + if (!qc) + continue; - if (qc) { - link->deferred_qc = NULL; - cancel_work(&link->deferred_qc_work); - ata_scsi_qc_done(qc, true, DID_REQUEUE << 16); + /* + * Clear the deferred QC so that the deferred work does not try + * to issue it. + */ + link->deferred_qc = NULL; + cancel_work(&link->deferred_qc_work); + + /* + * We are going to complete some scsi command, either with + * DID_TIME_OUT if the command timed out while waiting for being + * issued, or with DID_REQUEUE if another command timed out or + * we had a failed command. However, the block layer may re-issue + * these commands immediately, keeping the scsi host busy and + * thus preventing the SCSI EH task from running. + * So schedule EH on the port to prevent accepting new commands + * until everything is sorted out with the error or timeout that + * got us here in the first place. Note that we set EH pending + * on the port before calling ata_port_schedule_eh() so that we + * do not reenter this function from ata_eh_set_pending() with + * timedout_scmd being NULL and erroneously retry deferred QCs + * that have timed out on other links. + */ + if (!ata_port_eh_scheduled(ap)) { + ap->pflags |= ATA_PFLAG_EH_PENDING; + ata_port_schedule_eh(ap); } + + /* + * If we are being called from scsi_timeout(), then we have a + * non-NULL timedout_scmd. If the timed out command is for a + * deferred QC, terminate that deferred QC with DID_TIME_OUT and + * requeue all other deferred QCs. In this case we need to + * return SCSI_EH_DONE, because the timed out command was + * handled. + * If the timed out command is not for a deferred QC, we need to + * requeue all deferred QCs, and return SCSI_EH_NOT_HANDLED so + * that the timed out command gets added to the EH work queue + * with scsi_eh_scmd_add(), for later handling with libata EH + * ata_scsi_cmd_error_handler(). + * If timedout_scmd is NULL, we simply need to requeue all + * deferred QCs and the return value does not matter as we were + * not called from scsi_timeout(). + */ + if (timedout_scmd && qc->scsicmd == timedout_scmd) { + host_byte = DID_TIME_OUT; + action = SCSI_EH_DONE; + } else { + host_byte = DID_REQUEUE; + } + ata_scsi_qc_done(qc, true, host_byte << 16); } + + return action; } static void ata_scsi_schedule_deferred_qc(struct ata_link *link) @@ -1723,13 +1777,41 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) return; if (ata_port_eh_scheduled(ap)) { - ata_scsi_requeue_deferred_qc(ap); + ata_scsi_requeue_deferred_qc(ap, NULL); return; } if (!ap->ops->qc_defer(qc)) queue_work(system_highpri_wq, &link->deferred_qc_work); } +static enum scsi_timeout_action +ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) +{ + enum scsi_timeout_action action; + unsigned long flags; + + spin_lock_irqsave(ap->lock, flags); + action = ata_scsi_requeue_deferred_qc(ap, scmd); + spin_unlock_irqrestore(ap->lock, flags); + + return action; +} + +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) +{ + struct ata_port *ap = ata_shost_to_port(scmd->device->host); + + /* + * ata_scsi_cmd_error_handler() takes care of commands that timed out + * while executing. However, if we have deferred QCs while a timeout + * triggers, we must requeue these commands for retry so that we do not + * unnecessarily delay starting the SCSI EH task until these deferred + * commands also time out. + */ + return ata_scsi_retry_deferred_qc(ap, scmd); +} +EXPORT_SYMBOL_GPL(ata_scsi_eh_timed_out); + static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) { struct ata_link *link = qc->dev->link; @@ -2936,7 +3018,15 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) if (qc->cdb[0] == ALLOW_MEDIUM_REMOVAL && qc->dev->sdev) qc->dev->sdev->locked = 0; - ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); + if (cmd->result) + ata_scsi_qc_done(qc, false, 0); + else + ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); + return; + } + + if (cmd->result) { + ata_scsi_qc_done(qc, false, 0); return; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 700627596ce1..8dacc1daabf0 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -180,7 +180,8 @@ enum scsi_qc_status __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_port *ap) __must_hold(ap->lock); void ata_scsi_deferred_qc_work(struct work_struct *work); -void ata_scsi_requeue_deferred_qc(struct ata_port *ap); +enum scsi_timeout_action ata_scsi_requeue_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd); /* libata-eh.c */ extern unsigned int ata_internal_cmd_timeout(struct ata_device *dev, u8 cmd); diff --git a/include/linux/libata.h b/include/linux/libata.h index 6c0d385b2cde..f10b3287b507 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1153,6 +1153,7 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #endif extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); #else @@ -1464,6 +1465,7 @@ extern const struct attribute_group *ata_common_sdev_groups[]; .ioctl = ata_scsi_ioctl, \ ATA_SCSI_COMPAT_IOCTL \ .queuecommand = ata_scsi_queuecmd, \ + .eh_timed_out = ata_scsi_eh_timed_out, \ .dma_need_drain = ata_scsi_dma_need_drain, \ .this_id = ATA_SHT_THIS_ID, \ .emulated = ATA_SHT_EMULATED, \ From e7468b3f9b404ac7c1329ef07c146c3356dfbd01 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 23 Jul 2026 07:42:26 +0900 Subject: [PATCH 254/501] ata: libata-scsi: schedule deferred atapi command Modify atapi_qc_complete() to call ata_scsi_schedule_deferred_qc() to ensure that any deferred queued command can execute. This is similar to ata_scsi_qc_complete() function for regular ATA devices. Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal --- drivers/ata/libata-scsi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 02b04de925f9..cd64787c4772 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -2993,6 +2993,7 @@ static void atapi_fixup_inquiry(struct scsi_cmnd *cmd) static void atapi_qc_complete(struct ata_queued_cmd *qc) { + struct ata_link *link = qc->dev->link; struct scsi_cmnd *cmd = qc->scsicmd; unsigned int err_mask = qc->err_mask; @@ -3022,12 +3023,12 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) ata_scsi_qc_done(qc, false, 0); else ata_scsi_qc_done(qc, true, SAM_STAT_CHECK_CONDITION); - return; + goto schedule_deferred; } if (cmd->result) { ata_scsi_qc_done(qc, false, 0); - return; + goto schedule_deferred; } /* successful completion path */ @@ -3035,6 +3036,9 @@ static void atapi_qc_complete(struct ata_queued_cmd *qc) atapi_fixup_inquiry(cmd); ata_scsi_qc_done(qc, true, SAM_STAT_GOOD); + +schedule_deferred: + ata_scsi_schedule_deferred_qc(link); } /** * atapi_xlat - Initialize PACKET taskfile From de202d2251bc181c6019b1ad3c0ba8133e5ec68d Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Thu, 9 Jul 2026 16:02:23 +0900 Subject: [PATCH 255/501] scsi: libsas: terminate deferred commands on time out If a command times out while we have deferred non-NCQ commands waiting to be issued, the SCSI EH task is not immediately woken up as the waiting deferred commands are never issued nor completed, thus leaving the SCSI host in a busy state (shost->host_failed != scsi_host_busy(shost)) which prevents the SCSI EH task from being woken up. Eventually, when the deferred commands also time out, the SCSI EH task is woken up and the timeout processing occurs. Avoid this unnecessary additional SCSI EH wake up time with the same method as implemented in libata-scsi, using the eh_timed_out SCSI host template operation. The function sas_eh_timed_out() implements this operation and executes the function ata_scsi_retry_deferred_qc() for SATA devices. Co-developed-by: Igor Pylypiv Signed-off-by: Igor Pylypiv Fixes: 0ea84089dbf6 ("ata: libata-scsi: avoid Non-NCQ command starvation") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Reviewed-by: John Garry Reviewed-by: Hannes Reinecke Tested-by: Igor Pylypiv Reviewed-by: Niklas Cassel Reviewed-by: Martin K. Petersen --- drivers/ata/libata-scsi.c | 5 +++-- drivers/scsi/libsas/sas_scsi_host.c | 15 +++++++++++++++ include/linux/libata.h | 2 ++ include/scsi/libsas.h | 2 ++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index cd64787c4772..d2160ee7ca7d 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -1784,8 +1784,8 @@ static void ata_scsi_schedule_deferred_qc(struct ata_link *link) queue_work(system_highpri_wq, &link->deferred_qc_work); } -static enum scsi_timeout_action -ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) +enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd) { enum scsi_timeout_action action; unsigned long flags; @@ -1796,6 +1796,7 @@ ata_scsi_retry_deferred_qc(struct ata_port *ap, struct scsi_cmnd *scmd) return action; } +EXPORT_SYMBOL_GPL(ata_scsi_retry_deferred_qc); enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *scmd) { diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c index c83282733ec4..97ac3db5dccb 100644 --- a/drivers/scsi/libsas/sas_scsi_host.c +++ b/drivers/scsi/libsas/sas_scsi_host.c @@ -502,6 +502,21 @@ int sas_eh_target_reset_handler(struct scsi_cmnd *cmd) } EXPORT_SYMBOL_GPL(sas_eh_target_reset_handler); +/* + * Handle deferred QCs in case of a command timeout. + * See ata_scsi_eh_timed_out() for details. + */ +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd) +{ + struct domain_device *dev = cmd_to_domain_dev(cmd); + + if (dev_is_sata(dev)) + return ata_scsi_retry_deferred_qc(dev->sata_dev.ap, cmd); + + return SCSI_EH_NOT_HANDLED; +} +EXPORT_SYMBOL_GPL(sas_eh_timed_out); + /* Try to reset a device */ static int try_to_reset_cmd_device(struct scsi_cmnd *cmd) { diff --git a/include/linux/libata.h b/include/linux/libata.h index f10b3287b507..1827502b9cf2 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1153,6 +1153,8 @@ extern int ata_scsi_ioctl(struct scsi_device *dev, unsigned int cmd, #endif extern enum scsi_qc_status ata_scsi_queuecmd(struct Scsi_Host *h, struct scsi_cmnd *cmd); +enum scsi_timeout_action ata_scsi_retry_deferred_qc(struct ata_port *ap, + struct scsi_cmnd *scmd); enum scsi_timeout_action ata_scsi_eh_timed_out(struct scsi_cmnd *cmd); #if IS_REACHABLE(CONFIG_ATA) bool ata_scsi_dma_need_drain(struct request *rq); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 163f23c92b41..c7017ae76c61 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -705,6 +705,7 @@ void sas_task_abort(struct sas_task *); int sas_eh_abort_handler(struct scsi_cmnd *cmd); int sas_eh_device_reset_handler(struct scsi_cmnd *cmd); int sas_eh_target_reset_handler(struct scsi_cmnd *cmd); +enum scsi_timeout_action sas_eh_timed_out(struct scsi_cmnd *cmd); extern void sas_target_destroy(struct scsi_target *); extern int sas_sdev_init(struct scsi_device *); @@ -743,6 +744,7 @@ void sas_notify_phy_event(struct asd_sas_phy *phy, enum phy_event event, .this_id = -1, \ .eh_device_reset_handler = sas_eh_device_reset_handler, \ .eh_target_reset_handler = sas_eh_target_reset_handler, \ + .eh_timed_out = sas_eh_timed_out, \ .target_destroy = sas_target_destroy, \ .ioctl = sas_ioctl, \ From 9c33222bd387312874fbe36ca8002e5c945b9653 Mon Sep 17 00:00:00 2001 From: TanZheng Date: Fri, 24 Jul 2026 15:58:50 +0800 Subject: [PATCH 256/501] scsi: target: iblock: Fix wrong PR ops NULL check for PREEMPT/RELEASE In the iblock_execute_pr_out() function, PRO_PREEMPT, PRO_PREEMPT_AND_ABORT, and PRO_RELEASE all perform callback capability checks through ops->pr_clear. The error check allows unimplemented hooks to pass through the gate, resulting dereferencing a NULL function pointer. Check whether the hooks that need to be called are supported. Fixes: 394f81184882 ("scsi: target: Add block PR support to iblock") Signed-off-by: TanZheng Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260724075850.280699-1-kensanya@163.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_iblock.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 1087d1d17c36..ea65d39fef88 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -906,7 +906,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, break; case PRO_PREEMPT: case PRO_PREEMPT_AND_ABORT: - if (!ops->pr_clear) { + if (!ops->pr_preempt) { pr_err("block_device does not support pr_preempt.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } @@ -916,8 +916,8 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, sa == PRO_PREEMPT_AND_ABORT); break; case PRO_RELEASE: - if (!ops->pr_clear) { - pr_err("block_device does not support pr_pclear.\n"); + if (!ops->pr_release) { + pr_err("block_device does not support pr_release.\n"); return TCM_UNSUPPORTED_SCSI_OPCODE; } From 93dde0bf2f39a0f9f57fd610aa3201ce5b753433 Mon Sep 17 00:00:00 2001 From: Ibrahim Hashimov Date: Sun, 12 Jul 2026 20:37:39 +0200 Subject: [PATCH 257/501] scsi: scsi_debug: Fix REPORT ZONES alloc_len underflow OOB write resp_report_zones() sizes the reply buffer from the CDB allocation length. The v3 fix rounds alloc_len up with ALIGN() before deriving the descriptor count: rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> ilog2(RZONES_DESC_HD); arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); For alloc_len in 0xFFFFFFC1..0xFFFFFFFF, ALIGN() rounds up to 0x100000000, so arr_len is 4 GB. On 32-bit, kzalloc()'s size_t is 32-bit and truncates 0x100000000 to 0; kzalloc(0) returns ZERO_SIZE_PTR, which passes the !arr check, and desc = arr + 64 is then dereferenced in the loop -> out-of-bounds write / panic. Clamp rep_max_zones to devip->nr_zones. The loop already stops at sdebug_capacity (after nr_zones zones), so a report can never hold more than nr_zones descriptors; the clamp does not change the report, it only bounds arr_len to (nr_zones + 1) * RZONES_DESC_HD, a real device property that can never reach 0x100000000. Fixes: 7db0e0c8190a ("scsi: scsi_debug: Fix buffer size of REPORT ZONES command") Suggested-by: Damien Le Moal Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov Assisted-by: AuditCode-AI:2026.07 Reviewed-by: Damien Le Moal Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260712183739.83915-1-security@auditcode.ai Signed-off-by: Martin K. Petersen --- drivers/scsi/scsi_debug.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 9d1c9c41d0f9..643051332132 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -5890,6 +5890,7 @@ static int resp_report_zones(struct scsi_cmnd *scp, u32 alloc_len, rep_opts, rep_len; bool partial; u64 lba, zs_lba; + u64 arr_len; u8 *arr = NULL, *desc; u8 *cmd = scp->cmnd; struct sdeb_zone_state *zsp = NULL; @@ -5911,9 +5912,12 @@ static int resp_report_zones(struct scsi_cmnd *scp, return check_condition_result; } - rep_max_zones = (alloc_len - 64) >> ilog2(RZONES_DESC_HD); + rep_max_zones = (ALIGN((u64)alloc_len, RZONES_DESC_HD) - RZONES_DESC_HD) >> + ilog2(RZONES_DESC_HD); + rep_max_zones = min_t(unsigned int, rep_max_zones, devip->nr_zones); + arr_len = (u64)RZONES_DESC_HD * (rep_max_zones + 1); - arr = kzalloc(alloc_len, GFP_ATOMIC | __GFP_NOWARN); + arr = kzalloc(arr_len, GFP_ATOMIC | __GFP_NOWARN); if (!arr) { mk_sense_buffer(scp, ILLEGAL_REQUEST, INSUFF_RES_ASC, INSUFF_RES_ASCQ); From f71b4a30983b846b4075bf544e835121e70e6a43 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Wed, 15 Jul 2026 01:27:26 +0800 Subject: [PATCH 258/501] scsi: ufs: core: Cancel RTC work in active-active suspend UFS RTC support schedules ufs_rtc_update_work to periodically update the device RTC. The work can issue query commands and access the UFS host controller. A previous change moved the RTC work cancellation before the PRE_CHANGE vendor suspend callback to close a race in the common suspend path. However, the active-active path jumps directly to vops_suspend after flushing exception handling work and therefore bypasses the cancellation. If the RTC work runs while the vendor suspend callback is gating or otherwise changing hardware state, it can access the controller during suspend and trigger an SError. Cancel the RTC work before entering the vendor suspend callback in the active-active path. Since this path now cancels the work, move the RTC work scheduling outside the device and link state restoration block in the resume path. This restarts RTC updates after an active-active suspend and resume cycle. Fixes: b0bd84c39289 ("scsi: ufs: core: Fix SError in ufshcd_rtc_work() during UFS suspend") Signed-off-by: Guangshuo Li Reviewed-by: Peter Wang Reviewed-by: Bean Huo Reviewed-by: Bart Van Assche Link: https://patch.msgid.link/20260714172726.1736967-1-lgs201920130244@gmail.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index d3044a3089b5..c3b105b2678e 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10269,6 +10269,7 @@ static int __ufshcd_wl_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op) req_link_state == UIC_LINK_ACTIVE_STATE) { ufshcd_disable_auto_bkops(hba); flush_work(&hba->eeh_work); + cancel_delayed_work_sync(&hba->ufs_rtc_update_work); goto vops_suspend; } @@ -10478,10 +10479,11 @@ static int __ufshcd_wl_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) if (ret) goto set_old_link_state; ufshcd_set_timestamp_attr(hba); - schedule_delayed_work(&hba->ufs_rtc_update_work, - msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); } + schedule_delayed_work(&hba->ufs_rtc_update_work, + msecs_to_jiffies(UFS_RTC_UPDATE_INTERVAL_MS)); + if (ufshcd_keep_autobkops_enabled_except_suspend(hba)) ufshcd_enable_auto_bkops(hba); else From 8a309036f557d3ff4efb2beea5132ba91172d934 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Wed, 15 Jul 2026 10:12:28 -0700 Subject: [PATCH 259/501] scsi: ufs: core: Revert "Delegate the interrupt service routine to a threaded IRQ handler" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There have been multiple reports of performance regressions caused by commit 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler"). Hence this revert. This patch reverts most of the following commits: * 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") * 6475cfb81fc4 ("scsi: ufs: core: Avoid IRQ thread wakeup during active UIC command") This patch preserves the following commits: * 034d319c8899 ("scsi: ufs: core: Fix interrupt handling for MCQ Mode") * eabcac808ca3 ("scsi: ufs: core: Fix IRQ lock inversion for the SCSI host lock") Cc: Neil Armstrong Cc: 孙魁 (Kui Sun) Cc: André Draszik Cc: Gregory CLEMENT Cc: Sebastian Andrzej Siewior Fixes: 3c7ac40d7322 ("scsi: ufs: core: Delegate the interrupt service routine to a threaded IRQ handler") Signed-off-by: Bart Van Assche Reviewed-by: Sebastian Andrzej Siewior Tested-by: André Draszik # on Pixel 6 Reviewed-by: André Draszik Link: https://patch.msgid.link/b70eb60a01f971bed68c42c5b555929db5f835df.1784135511.git.bvanassche@acm.org Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index c3b105b2678e..34228beb3f59 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -7357,7 +7357,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) } /** - * ufshcd_threaded_intr - Threaded interrupt service routine + * ufshcd_intr - Main interrupt service routine * @irq: irq number * @__hba: pointer to adapter instance * @@ -7365,7 +7365,7 @@ static irqreturn_t ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) * IRQ_HANDLED - If interrupt is valid * IRQ_NONE - If invalid interrupt */ -static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) +static irqreturn_t ufshcd_intr(int irq, void *__hba) { u32 last_intr_status, intr_status, enabled_intr_status = 0; irqreturn_t retval = IRQ_NONE; @@ -7404,38 +7404,6 @@ static irqreturn_t ufshcd_threaded_intr(int irq, void *__hba) return retval; } -/** - * ufshcd_intr - Main interrupt service routine - * @irq: irq number - * @__hba: pointer to adapter instance - * - * Return: - * IRQ_HANDLED - If interrupt is valid - * IRQ_WAKE_THREAD - If handling is moved to threaded handled - * IRQ_NONE - If invalid interrupt - */ -static irqreturn_t ufshcd_intr(int irq, void *__hba) -{ - struct ufs_hba *hba = __hba; - u32 intr_status, enabled_intr_status; - - /* - * Handle interrupt in thread if MCQ or ESI is disabled, - * and no active UIC command. - */ - if ((!hba->mcq_enabled || !hba->mcq_esi_enabled) && - !hba->active_uic_cmd) - return IRQ_WAKE_THREAD; - - intr_status = ufshcd_readl(hba, REG_INTERRUPT_STATUS); - enabled_intr_status = intr_status & ufshcd_readl(hba, REG_INTERRUPT_ENABLE); - - ufshcd_writel(hba, intr_status, REG_INTERRUPT_STATUS); - - /* Directly handle interrupts since MCQ ESI handlers does the hard job */ - return ufshcd_sl_intr(hba, enabled_intr_status); -} - static int ufshcd_clear_tm_cmd(struct ufs_hba *hba, int tag) { int err = 0; @@ -11238,8 +11206,7 @@ int ufshcd_init(struct ufs_hba *hba, void __iomem *mmio_base, unsigned int irq) ufshcd_readl(hba, REG_INTERRUPT_ENABLE); /* IRQ registration */ - err = devm_request_threaded_irq(dev, irq, ufshcd_intr, ufshcd_threaded_intr, - IRQF_ONESHOT | IRQF_SHARED, UFSHCD, hba); + err = devm_request_irq(dev, irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba); if (err) { dev_err(hba->dev, "request irq failed\n"); goto out_disable; From b601fa590e667bd9643feed8c869b6b3e418480d Mon Sep 17 00:00:00 2001 From: Benjamin Block Date: Mon, 20 Jul 2026 09:27:36 +0200 Subject: [PATCH 260/501] scsi: zfcp: Fix memory leak during adapter release by destroying gid_pn_req When releasing an adapter we don't free the mempool 'gid_pn_req' that is allocated during the enqueue. This leaks memory: unreferenced object 0xd8d29297de700 (size 256): comm "(udev-worker)", pid 2105, jiffies 4294945794 hex dump (first 32 bytes): 00 00 00 00 de ad 4e ad ff ff ff ff 00 00 00 00 ......N......... ff ff ff ff ff ff ff ff 00 0d c4 5f 67 9d 99 e0 ..........._g... backtrace (crc 4a5b5da2): [<000dc45f64da418c>] kmemleak_alloc+0x6c/0xa0 [<000dc45f62b430aa>] __kmalloc_cache_node_noprof+0x36a/0x4d0 [<000dc45f629a535a>] mempool_create_node_noprof+0xaa/0x150 [<000dc45ee2c065e6>] zfcp_allocate_low_mem_buffers+0x96/0x370 [zfcp] [<000dc45ee2c070f8>] zfcp_adapter_enqueue+0x598/0xd40 [zfcp] [<000dc45ee2c08eb0>] zfcp_ccw_set_online+0x160/0x210 [zfcp] [<000dc45f643d4762>] ccw_device_set_online+0x232/0xd80 [<000dc45f643d53d4>] online_store_recog_and_online+0x124/0x390 [<000dc45f643d8238>] online_store+0x298/0x5b0 [<000dc45f62eb0a04>] kernfs_fop_write_iter+0x2c4/0x480 [<000dc45f62c81150>] new_sync_write+0x370/0x4b0 [<000dc45f62c87abe>] vfs_write+0x43e/0x5b0 [<000dc45f62c87ff4>] ksys_write+0x114/0x1f0 [<000dc45f621c4a16>] do_syscall+0x2f6/0x430 [<000dc45f64d9d5d8>] __do_syscall+0xc8/0x1c0 [<000dc45f64dc2224>] system_call+0x74/0xa0 Fix this by destroying the mempool during the adapter's release. Fixes: 799b76d09aee ("[SCSI] zfcp: Decouple gid_pn requests from erp") Signed-off-by: Benjamin Block Tested-by: M Nikhil Acked-by: M Nikhil Reviewed-by: Chinmaya Kajagar Reviewed-by: Nihar Panda Link: https://patch.msgid.link/20260720072736.3381816-2-niharp@linux.ibm.com Signed-off-by: Martin K. Petersen --- drivers/s390/scsi/zfcp_aux.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 8ff7db7921b5..fea573e00f9d 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -253,6 +253,7 @@ static int zfcp_allocate_low_mem_buffers(struct zfcp_adapter *adapter) static void zfcp_free_low_mem_buffers(struct zfcp_adapter *adapter) { mempool_destroy(adapter->pool.erp_req); + mempool_destroy(adapter->pool.gid_pn_req); mempool_destroy(adapter->pool.scsi_req); mempool_destroy(adapter->pool.scsi_abort); mempool_destroy(adapter->pool.qtcb_pool); From a8ddfd2425bbbafadae8700d63ed8a61a4109878 Mon Sep 17 00:00:00 2001 From: Leon Romanovsky Date: Wed, 22 Jul 2026 09:30:10 +0300 Subject: [PATCH 261/501] scsi: target: Clear cmd_cnt when initial counter enrollment fails When target_get_sess_cmd() fails during session shutdown because percpu_ref_tryget_live() returns false, the command keeps the se_cmd->cmd_cnt pointer that __target_init_cmd() assigned earlier without owning a reference. Final release through target_release_cmd_kref() then issues an unmatched percpu_ref_put(). Commit 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") moved the cmd_cnt assignment ahead of the reference acquisition. Clear se_cmd->cmd_cnt whenever the initial target_get_sess_cmd() fails in target_init_cmd() and target_submit_tmr(), so release performs exactly one matching put per acquired reference. Fixes: 8e288be8606a ("scsi: target: Pass in cmd counter to use during cmd setup") Signed-off-by: Leon Romanovsky Reviewed-by: Mike Christie Link: https://patch.msgid.link/20260722-reference-count-underflow-in-target-v1-1-63ab664f12fd@nvidia.com Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index fad03a15c969..dcfe94594916 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -1734,6 +1734,7 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, u32 data_length, int task_attr, int data_dir, int flags) { struct se_portal_group *se_tpg; + int ret; se_tpg = se_sess->se_tpg; BUG_ON(!se_tpg); @@ -1763,7 +1764,11 @@ int target_init_cmd(struct se_cmd *se_cmd, struct se_session *se_sess, * necessary for fabrics using TARGET_SCF_ACK_KREF that expect a second * kref_put() to happen during fabric packet acknowledgement. */ - return target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); + if (ret) + se_cmd->cmd_cnt = NULL; + + return ret; } EXPORT_SYMBOL_GPL(target_init_cmd); @@ -2039,8 +2044,10 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, * allocation failure. */ ret = core_tmr_alloc_req(se_cmd, fabric_tmr_ptr, tm_type, gfp); - if (ret < 0) + if (ret < 0) { + se_cmd->cmd_cnt = NULL; return -ENOMEM; + } if (tm_type == TMR_ABORT_TASK) se_cmd->se_tmr_req->ref_task_tag = tag; @@ -2048,6 +2055,7 @@ int target_submit_tmr(struct se_cmd *se_cmd, struct se_session *se_sess, /* See target_submit_cmd for commentary */ ret = target_get_sess_cmd(se_cmd, flags & TARGET_SCF_ACK_KREF); if (ret) { + se_cmd->cmd_cnt = NULL; core_tmr_release_req(se_cmd->se_tmr_req); return ret; } From 356d8241cf3c7b07a4a491dbab43b5a41513ca86 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:13:11 +0200 Subject: [PATCH 262/501] ata: libata-core: Disable LPM on some WD drives According to a user report WDC WD100EFGX-68CPLN0 and WDC WD102KFBX-68M95N0 have problems with LPM. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220693 Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index c43bd28b20b1..15781b4f0489 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4413,6 +4413,14 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { { "WDC WD3000JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, { "WDC WD3200JD-*", NULL, ATA_QUIRK_WD_BROKEN_LPM }, + /* + * WD drives with LPM issues (irrespective of supported SATA speeds). + * (Unlike ATA_QUIRK_WD_BROKEN_LPM, which is only applied if the drive + * exposes SATA Gen1 speed support, and SATA Gen1 speed support only.) + */ + { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM }, + { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM }, + /* * This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY * log page is accessed. Ensure we never ask for this log page with From d549df8a0ace611bff0a336a907f93420648b462 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:13:12 +0200 Subject: [PATCH 263/501] ata: libata-core: Disable LPM on WD Green 2.5 480GB According to a user report, the WD Green 2.5 480GB has problems with LPM. Link: https://lore.kernel.org/linux-ide/CAGiKK17Fg4SgS+y0GzD3new44QzD_yPZU5V3ZeHdUr9mFnfn1Q@mail.gmail.com/ Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 15781b4f0489..961d03fbc4d9 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4420,6 +4420,7 @@ static const struct ata_dev_quirks_entry __ata_dev_quirks[] = { */ { "WDC WD100EFGX-68CPLN0", NULL, ATA_QUIRK_NOLPM }, { "WDC WD102KFBX-68M95N0", NULL, ATA_QUIRK_NOLPM }, + { "WD Green 2.5 480GB", NULL, ATA_QUIRK_NOLPM }, /* * This sata dom device goes on a walkabout when the ATA_LOG_DIRECTORY From 3fd70e96914d761c17c376aadd0b0d1a3c9badba Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Tue, 28 Jul 2026 13:38:42 +0200 Subject: [PATCH 264/501] ata: libata-sata: fix ata_scsi_lpm_supported() iteration The inner loop of ata_scsi_lpm_supported() uses the wrong variable when iterating. It should obviously use the link that we are currently iterating over, rather than always using the host link. ata_scsi_lpm_supported() is used to control if a user should be allowed to change lpm policy (from the default) via sysfs. Thus, this bug could potentially disallow users to change the LPM policy for certain SATA devices via sysfs. Cc: stable@vger.kernel.org Fixes: 0060beec0bfa ("ata: libata-sata: Add link_power_management_supported sysfs attribute") Reported-by: Sashiko Link: https://lore.kernel.org/linux-ide/20260728112200.B99F21F000E9@smtp.kernel.org/ Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-sata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index b0706c30da05..ad40f516c6d4 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -913,7 +913,7 @@ static bool ata_scsi_lpm_supported(struct ata_port *ap) return false; ata_for_each_link(link, ap, EDGE) { - ata_for_each_dev(dev, &ap->link, ENABLED) { + ata_for_each_dev(dev, link, ENABLED) { if (dev->quirks & ATA_QUIRK_NOLPM) return false; } From ccff8c92571500fcfed21281e33daaf645bf692f Mon Sep 17 00:00:00 2001 From: Chandrakanth Patil Date: Fri, 24 Jul 2026 23:22:31 +0530 Subject: [PATCH 265/501] scsi: mpi3mr: Fix potential deadlock in mpi3mr_fault_uevent_emit mpi3mr_fault_uevent_emit() runs from the fault watchdog and reset paths where host I/O may already be blocked. GFP_KERNEL allocations here, both the local kzalloc_obj() and the ones inside kobject_uevent_env() itself, can trigger reclaim that waits on that blocked I/O and deadlock. Use memalloc_noio_save()/restore() to cover the whole call instead of just the local allocation. Fixes: ec54b348f274 ("scsi: mpi3mr: Record and report controller firmware faults") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260724164630.924288-1-chandrakanth.patil%40broadcom.com Signed-off-by: Chandrakanth Patil Link: https://patch.msgid.link/20260724175231.935192-1-chandrakanth.patil@broadcom.com Signed-off-by: Martin K. Petersen --- drivers/scsi/mpi3mr/mpi3mr_fw.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index 31b19ed1528e..681868716ebd 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -9,6 +9,7 @@ #include "mpi3mr.h" #include +#include static int mpi3mr_issue_reset(struct mpi3mr_ioc *mrioc, u16 reset_type, u16 reset_reason); @@ -1287,11 +1288,14 @@ static void mpi3mr_alloc_ioctl_dma_memory(struct mpi3mr_ioc *mrioc) static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) { struct kobj_uevent_env *env; + unsigned int noio_flag; int ret; + noio_flag = memalloc_noio_save(); + env = kzalloc_obj(*env); if (!env) - return; + goto out_restore; ret = add_uevent_var(env, "DRIVER=%s", mrioc->driver_name); if (ret) @@ -1326,7 +1330,8 @@ static void mpi3mr_fault_uevent_emit(struct mpi3mr_ioc *mrioc) out_free: kfree(env); - +out_restore: + memalloc_noio_restore(noio_flag); } /** From 0279fd451a9971c0d5b959fc59f3e11b55e1694e Mon Sep 17 00:00:00 2001 From: Ao Sun Date: Thu, 23 Jul 2026 03:45:30 +0000 Subject: [PATCH 266/501] scsi: ufs: core: Initialize hba->rpmbs list in ufshcd Initialize the hba->rpmbs list in ufshcd_alloc_host() to prevent NULL pointer dereference in the device teardown path if ufs_rpmb_probe() fails. Fixes: b06b8c421485 ("scsi: ufs: core: Add OP-TEE based RPMB driver for UFS devices") Co-developed-by: Jiazi Li Signed-off-by: Jiazi Li Signed-off-by: Ao Sun Reviewed-by: Bean Huo Link: https://patch.msgid.link/20260723034440.217-1-ao.sun@transsion.com Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufs-rpmb.c | 2 -- drivers/ufs/core/ufshcd.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/ufs/core/ufs-rpmb.c b/drivers/ufs/core/ufs-rpmb.c index ffad049872b9..62120dc2e9da 100644 --- a/drivers/ufs/core/ufs-rpmb.c +++ b/drivers/ufs/core/ufs-rpmb.c @@ -152,8 +152,6 @@ int ufs_rpmb_probe(struct ufs_hba *hba) return -EINVAL; } - INIT_LIST_HEAD(&hba->rpmbs); - struct rpmb_descr descr = { .type = RPMB_TYPE_UFS, .route_frames = ufs_rpmb_route_frames, diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 34228beb3f59..13e6357a8bbb 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -10958,6 +10958,7 @@ int ufshcd_alloc_host(struct device *dev, struct ufs_hba **hba_handle) hba->nop_out_timeout = NOP_OUT_TIMEOUT; ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry)); INIT_LIST_HEAD(&hba->clk_list_head); + INIT_LIST_HEAD(&hba->rpmbs); spin_lock_init(&hba->outstanding_lock); *hba_handle = hba; From cf6c993c0feca7984797e634deba3c80342e199a Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Sat, 25 Jul 2026 16:00:04 +0800 Subject: [PATCH 267/501] fscrypt: use the mount idmap for the owner check in fscrypt_ioctl_set_policy() fscrypt_ioctl_set_policy() calls inode_owner_or_capable() with &nop_mnt_idmap before allowing an encryption policy to be set, instead of the idmap of the mount the ioctl was issued on. fscrypt is used by filesystems that support idmapped mounts (e.g. ext4, f2fs), so on such a mount this compares the caller's fsuid against the unmapped on-disk owner rather than the mapped owner: the actual owner can be wrongly denied with -EACCES and an unrelated caller wrongly allowed. Use file_mnt_idmap(filp) instead. Fixes: 14f3db5542e6 ("ext4: support idmapped mounts") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng Link: https://patch.msgid.link/20260725080004.929328-1-zhanxusheng1024@gmail.com Signed-off-by: Eric Biggers --- fs/crypto/policy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/crypto/policy.c b/fs/crypto/policy.c index 9915e39362db..c80b24a941ad 100644 --- a/fs/crypto/policy.c +++ b/fs/crypto/policy.c @@ -534,7 +534,7 @@ int fscrypt_ioctl_set_policy(struct file *filp, const void __user *arg) return -EFAULT; policy.version = version; - if (!inode_owner_or_capable(&nop_mnt_idmap, inode)) + if (!inode_owner_or_capable(file_mnt_idmap(filp), inode)) return -EACCES; ret = mnt_want_write_file(filp); From d2f96bcb89d36d488a10e3bcf819b98536968286 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Mon, 27 Jul 2026 11:43:52 +0200 Subject: [PATCH 268/501] fs,fsverity: remove check for fsverity being enabled in setattr_prepare() The check that fs-verity is available in the kernel is not necessary here. Filesystems could have fsverity files even without fs-verity enabled. In that case, truncate on fsverity file will succeed, what this check is trying to prevent. Fixes: e9734653c523 ("fs,fsverity: reject size changes on fsverity files in setattr_prepare") Cc: stable@vger.kernel.org Signed-off-by: Andrey Albershteyn Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260727094352.1734826-1-aalbersh@kernel.org Signed-off-by: Eric Biggers --- fs/attr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/attr.c b/fs/attr.c index 4f437fabb7f0..71888ac903c2 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -176,7 +176,7 @@ int setattr_prepare(struct mnt_idmap *idmap, struct dentry *dentry, * covered by the open-time check because sys_truncate() takes a * path, not an open file. */ - if (IS_ENABLED(CONFIG_FS_VERITY) && IS_VERITY(inode)) + if (IS_VERITY(inode)) return -EPERM; error = inode_newsize_ok(inode, attr->ia_size); From d0199ae1666ff9ae2d1d568d64c3430d4c47f0e5 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:17:16 +0000 Subject: [PATCH 269/501] ALSA: usb-audio: Fix DMA buffer out-of-bounds write when fill_max is set When a USB audio endpoint requests full packet transfers via the fill_max descriptor flag, data_ep_set_params() promotes ep->curpacksize to ep->maxpacksize. However, maxsize is left at the original sample-rate derived value. Since u->buffer_size is allocated as maxsize * packets, the resulting DMA buffer is far too small for the requested transfer length. When the USB host controller streams up to curpacksize bytes per packet, it writes past the end of the buffer via DMA, corrupting kernel heap memory. Update maxsize to curpacksize when fill_max is set so that the allocated DMA buffer size matches the actual transfer request size. [ changed to reassign maxsize only when ep->fill_max is set -- tiwai ] Fixes: 8fdff6a319e7 ("ALSA: snd-usb: implement new endpoint streaming model") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728201716.2347726-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 24cd7692bd01..91611d0a4e48 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1168,10 +1168,12 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep) << (16 - ep->datainterval); } - if (ep->fill_max) + if (ep->fill_max) { ep->curpacksize = ep->maxpacksize; - else + maxsize = ep->curpacksize; + } else { ep->curpacksize = maxsize; + } if (snd_usb_get_speed(chip->dev) != USB_SPEED_FULL) { packs_per_ms = 8 >> ep->datainterval; From 8d7a30c50c2e58a6839634ed0acde14466d1dc61 Mon Sep 17 00:00:00 2001 From: Sonali Pradhan Date: Tue, 28 Jul 2026 20:24:32 +0000 Subject: [PATCH 270/501] ALSA: usb-audio: Clamp frame size in implicit-feedback mode snd_usb_handle_sync_urb() scales received sync packet sizes by the sender's stride and stores the result directly in out_packet->packet_size[i]. If a connected USB device sends an oversized sync packet, this frame count can exceed ep->maxframesize. The un-clamped frame count then propagates to the playback endpoint queue, potentially driving packet transfers beyond the endpoint's hardware frame limits. Cap the calculated frame count against ep->maxframesize in snd_usb_handle_sync_urb() to prevent oversized packets from entering the playback queue. Fixes: 28acb12014fb ("ALSA: usb-audio: use sender stride for implicit feedback") Cc: stable@vger.kernel.org Assisted-by: Jetski:Gemini-3.6-Flash Signed-off-by: Sonali Pradhan Link: https://patch.msgid.link/20260728202432.2354994-1-sonalipradhan@google.com Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 91611d0a4e48..dca06ba1c67e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -1819,11 +1819,13 @@ static void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, out_packet->packets = in_ctx->packets; for (i = 0; i < in_ctx->packets; i++) { - if (urb->iso_frame_desc[i].status == 0) - out_packet->packet_size[i] = + if (urb->iso_frame_desc[i].status == 0) { + unsigned int frames = urb->iso_frame_desc[i].actual_length / sender->stride; - else + out_packet->packet_size[i] = min(frames, ep->maxframesize); + } else { out_packet->packet_size[i] = 0; + } } spin_unlock_irqrestore(&ep->lock, flags); From e93bb6f3acec5b70a5567efe78f1a96148a7291f Mon Sep 17 00:00:00 2001 From: Mikhail Gavrilov Date: Wed, 29 Jul 2026 03:22:39 +0500 Subject: [PATCH 271/501] ALSA: usb-audio: Add GET_SAMPLE_RATE quirk for C-Media CM6206 The C-Media CM6206 (0d8c:0102) truncates the three-byte sample rate it returns for UAC_GET_CUR to its two low bytes. After the rate has been set to 96000 (0x017700) the device reports back 30464 (0x007700). At probe time the driver initializes every altsetting to its maximum rate, so altsetting 5 is set to 96000 and the warning appears on each plug-in, before anything has opened the device: usb 3-1.3: 1:5 Set sample rate 96000, clock 0 usb 3-1.3: current rate 30464 is different from the runtime rate 96000 That altsetting is the one parse_audio_format_rates_v1() already fixes up for this chip, so this affects every CM6206. Only the read-back is broken, the rate itself is applied: a 1 kHz sine rendered at 96 kHz is recovered at 1000.2 Hz, and a silent fallback to 48000 would have been reported as 0x00bb80 rather than as the low half of the requested rate. Add a QUIRK_FLAG_GET_SAMPLE_RATE entry for the device so the read-back is skipped. Setting the same flag through the quirk_flags module parameter makes the warning disappear while the 96000 init still happens. Signed-off-by: Mikhail Gavrilov Link: https://patch.msgid.link/20260728222239.62749-1-mikhail.v.gavrilov@gmail.com 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 40aa40fccb46..d2a111ee50ab 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2347,6 +2347,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(0x0d8c, 0x0102, /* C-Media CM6206 */ + QUIRK_FLAG_GET_SAMPLE_RATE), DEVICE_FLG(0x0e0b, 0xfa01, /* Feaulle Rainbow */ QUIRK_FLAG_GET_SAMPLE_RATE | QUIRK_FLAG_MIXER_PLAYBACK_MIN_MUTE), DEVICE_FLG(0x0ecb, 0x205c, /* JBL Quantum610 Wireless */ From 630c45e92db44b738d1e9224c5f3c032c76a33ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Pragosa?= Date: Tue, 28 Jul 2026 23:11:25 +0100 Subject: [PATCH 272/501] ALSA: hda/realtek: Add mute LED quirk for HP Victus 16-e0xxx (MB 88ED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add subsystem ID 103c:88ed to the existing HP Victus 16-e0xxx mute LED quirk list. The HP Victus 16-e0xxx with subsystem ID 103c:88ed uses the same mute LED coefficient configuration as the already supported 103c:88eb variant. The mute LED was verified by manually toggling coefficient index 0x0b (bit 3) using hda-verb. After adding the quirk, the LED is registered as hda::mute and follows the audio mute state. Signed-off-by: André Pragosa Link: https://patch.msgid.link/20260728221129.14680-2-pragosa512@gmail.com Signed-off-by: Takashi Iwai --- sound/hda/codecs/realtek/alc269.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sound/hda/codecs/realtek/alc269.c b/sound/hda/codecs/realtek/alc269.c index f620cd429f20..48a2e7f2ebb1 100644 --- a/sound/hda/codecs/realtek/alc269.c +++ b/sound/hda/codecs/realtek/alc269.c @@ -7222,6 +7222,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x88d1, "HP Pavilion 15-eh1xxx (mainboard 88D1)", ALC245_FIXUP_HP_MUTE_LED_V1_COEFBIT), SND_PCI_QUIRK(0x103c, 0x88dd, "HP Pavilion 15z-ec200", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x88eb, "HP Victus 16-e0xxx", ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), + SND_PCI_QUIRK(0x103c, 0x88ed, "HP Victus 16-e0xxx (MB 88ED)", + ALC245_FIXUP_HP_MUTE_LED_V2_COEFBIT), SND_PCI_QUIRK(0x103c, 0x88ee, "HP Victus 16-e0xxx (MB 88EE)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT), SND_PCI_QUIRK(0x103c, 0x8902, "HP OMEN 16", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x890e, "HP 255 G8 Notebook PC", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2), From 26cb8ebbfaf713c82e142d08828d4d765057633b Mon Sep 17 00:00:00 2001 From: Chao Shi Date: Mon, 27 Jul 2026 16:12:57 -0400 Subject: [PATCH 273/501] block: stop the timeout timer when releasing a never added disk disk_release() undoes blk_mq_init_allocated_queue() for a disk whose probe failed before add_disk(), but it only calls blk_mq_exit_queue(). Nothing there stops q->timeout, and that timer rolls forward: it stays pending until it next expires, not until the last request completes. So if the driver issued any I/O before adding the disk, the request_queue is freed while still linked into a timer wheel bucket. Commit 6f8191fdf41d ("block: simplify disk shutdown") dropped the blk_cleanup_queue() call that used to stop it. __del_gendisk() and blk_mq_destroy_queue() still do; only the probe failure path lost it. nvme gets there because nvme_update_ns_info() submits Report Zones or FDP io-mgmt-recv on ns->queue before the disk is added, so a later failure - a concurrent reset setting NVME_CTRL_FROZEN, or device_add_disk() failing - lands in put_disk() with the timer armed: BUG: KASAN: slab-use-after-free in detach_if_pending+0x30c/0x340 Write of size 8 at addr ffff888004d71310 by task kworker/u8:2/37 __timer_delete_sync+0x156/0x240 kernel/time/timer.c:1621 blk_sync_queue+0x22/0x40 block/blk-core.c:222 nvme_sync_queues+0x100/0x150 drivers/nvme/host/core.c:5362 nvme_reset_work+0x138/0x930 drivers/nvme/host/pci.c:3264 Allocated by task 34: __blk_mq_alloc_disk+0x33/0x100 block/blk-mq.c:4462 nvme_alloc_ns+0x290/0x3870 drivers/nvme/host/core.c:4146 Freed by task 0: blk_free_queue_rcu+0x3a/0x50 block/blk-core.c:254 rcu_core+0xc10/0x1730 kernel/rcu/tree.c:2857 The queue being synced there is ctrl->admin_q, only a victim sharing a timer wheel bucket with the freed queue's dangling entry; other runs tripped in enqueue_timer(), __run_timers() or blk_mq_timeout_work(). Failing nvme_alloc_ns() with a debug patch makes it deterministic: one leaked timer trips KASAN within seconds, while 1987 patched releases produced no splat. Stop the timer and the queue work items before blk_mq_exit_queue(), like blk_mq_destroy_queue() does. Found by FuzzNvme. Fixes: 6f8191fdf41d ("block: simplify disk shutdown") Acked-by: Weidong Zhu Signed-off-by: Chao Shi Reviewed-by: Christoph Hellwig Link: https://patch.msgid.link/20260727201257.211635-1-coshi036@gmail.com Signed-off-by: Jens Axboe --- block/genhd.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index df2c3c69b467..e8ce0cabf392 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1281,14 +1281,18 @@ static void disk_release(struct device *dev) /* * To undo the all initialization from blk_mq_init_allocated_queue in * case of a probe failure where add_disk is never called we have to - * call blk_mq_exit_queue here. We can't do this for the more common - * teardown case (yet) as the tagset can be gone by the time the disk - * is released once it was added. + * call blk_mq_exit_queue here, after stopping the timer and work items + * that I/O issued before add_disk may have left pending. We can't do + * this for the more common teardown case (yet) as the tagset can be + * gone by the time the disk is released once it was added. */ if (queue_is_mq(disk->queue) && test_bit(GD_OWNS_QUEUE, &disk->state) && - !test_bit(GD_ADDED, &disk->state)) + !test_bit(GD_ADDED, &disk->state)) { + blk_sync_queue(disk->queue); + blk_mq_cancel_work_sync(disk->queue); blk_mq_exit_queue(disk->queue); + } blkcg_exit_disk(disk); From e40e20ac089e32f1d910636155dc82e61e61dcf3 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Wed, 10 Jun 2026 21:38:25 +0200 Subject: [PATCH 274/501] drm/dp: Read the PCON max FRL bandwidth only for HDMI DFPs The PCON max FRL bandwidth field lives in byte 2 of the DFP Detailed Capability Info (DPCD 0x82 for the first DFP). The DP standard defines the meaning of descriptor bytes 1-3 strictly per DFP type, and for a DisplayPort type DFP all of them are reserved, with "read all 0s" semantics (DP v2.0, section 2.12.3, Table 2-183). The FRL bandwidth field is an HDMI DFP extension added by the VESA DP-to-HDMI PCON specification. drm_dp_get_pcon_max_frl_bw() however parses the byte without checking the DFP type, the branch presence or DETAILED_CAP_INFO_AVAILABLE. Without the latter the port descriptors are one byte wide and port_cap[2] is not even the right register. All neighbouring helpers parsing the same descriptor are scoped by the DFP type already, see for instance drm_dp_downstream_max_bpc() reading the same byte and returning 0 for a DP type DFP. amdgpu's DC parses the field only for HDMI(/DP++) detailed types as well. This is not theoretical. A Synaptics VMM7100 based USB-C to HDMI adapter with a macOS targeted firmware advertises a DisplayPort type DFP with the type byte replicated across the whole descriptor (08 08 08 08). i915 decodes that as "PCON limited to 18 Gbps FRL" and prunes every mode above ~750 MHz dotclock, including all the 4k@100/120 modes the sink EDID offers, while macOS drives 4k@120 through the same adapter just fine via DP DSC (and amdgpu's type-scoped parser would ignore the bogus field as well). Only parse the field for an HDMI DFP behind a DPCD 1.1+ branch device that reports detailed cap info, matching the type-scoped field layout of the spec and the rest of the helpers. Fixes: ce32a6239de6 ("drm/dp_helper: Add Helpers for FRL Link Training support for DP-HDMI2.1 PCON") Cc: Ankit Nautiyal Cc: Uma Shankar (v2) Cc: Jani Nikula Cc: Maarten Lankhorst Cc: dri-devel@lists.freedesktop.org Cc: # v5.12+ Signed-off-by: Alexander Kaplan Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260610193825.2933-1-alexander.kaplan@sms-medipool.de --- drivers/gpu/drm/display/drm_dp_helper.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index 9c31e14cc413..e623ccb4c1d8 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -3686,6 +3686,18 @@ int drm_dp_get_pcon_max_frl_bw(const u8 dpcd[DP_RECEIVER_CAP_SIZE], int bw; u8 buf; + if (!drm_dp_is_branch(dpcd)) + return 0; + + if (dpcd[DP_DPCD_REV] < 0x11) + return 0; + + if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DETAILED_CAP_INFO_AVAILABLE) == 0) + return 0; + + if ((port_cap[0] & DP_DS_PORT_TYPE_MASK) != DP_DS_PORT_TYPE_HDMI) + return 0; + buf = port_cap[2]; bw = buf & DP_PCON_MAX_FRL_BW; From 3e191eddbdcb8bf7beb1e9b58209073bd5450719 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Tue, 28 Jul 2026 17:31:45 +0200 Subject: [PATCH 275/501] drm/mediatek: mtk_dsi: Enable HS clock only at pre-enable Commit 76255024cadb ("drm/mediatek: mtk_dsi: enable hs clock during pre-enable") rightfully moves the HS clock enablement to before atomic_enable(), but it's moving it to mtk_dsi_poweron(), which is not only called in the .atomic_pre_enable() callback for the DRM bridge, but also in the MediaTek DRM's .ddp_start() callback, which happens way before the bridge ones. The HS clock enablement should be done at just the right time, otherwise some bridge chips (or some Display Driver ICs) may not work correctly: this is seen at least with a Parade DSI to eDP bridge (PS8640) on the MT8173 Elm Chromebook. This resolves a regression that was seen on the aforementioned machine, which was happening only after a suspend-resume cycle. Cc: # 7.1.x Fixes: 76255024cadb ("drm/mediatek: mtk_dsi: enable hs clock during pre-enable") Signed-off-by: AngeloGioacchino Del Regno Tested-by: Adam Thiede Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260728153145.578404-1-angelogioacchino.delregno@collabora.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_dsi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/mediatek/mtk_dsi.c b/drivers/gpu/drm/mediatek/mtk_dsi.c index eb325e68aa59..49dfc0f825cb 100644 --- a/drivers/gpu/drm/mediatek/mtk_dsi.c +++ b/drivers/gpu/drm/mediatek/mtk_dsi.c @@ -743,8 +743,6 @@ static int mtk_dsi_poweron(struct mtk_dsi *dsi) mtk_dsi_set_vm_cmd(dsi); mtk_dsi_config_vdo_timing(dsi); mtk_dsi_set_interrupt_enable(dsi); - mtk_dsi_lane_ready(dsi); - mtk_dsi_clk_hs_mode(dsi, 1); return 0; err_disable_engine_clk: @@ -858,6 +856,9 @@ static void mtk_dsi_bridge_atomic_pre_enable(struct drm_bridge *bridge, ret = mtk_dsi_poweron(dsi); if (ret < 0) drm_err(drm, "failed to power on dsi\n"); + + mtk_dsi_lane_ready(dsi); + mtk_dsi_clk_hs_mode(dsi, 1); } static void mtk_dsi_bridge_atomic_post_disable(struct drm_bridge *bridge, From 533e3469a57996905cdb95f178e7efe38c21aeb2 Mon Sep 17 00:00:00 2001 From: Myeonghun Pak Date: Wed, 22 Jul 2026 00:22:42 +0900 Subject: [PATCH 276/501] drm/mediatek: ovl_adaptor: balance component registrations The OVL adaptor registers both an aggregate driver for its child devices and a component for the main DRM aggregate. Probe currently ignores an error from registering the child aggregate and leaves that aggregate registered if registering the DRM component fails. The remove callback also leaves the DRM component registered. These imbalances can leave component framework entries referring to a device whose probe failed or whose driver has been detached. The aggregate unbind callback also fails to undo component_bind_all(), leaving its child components marked as bound when the aggregate is removed. Check the aggregate registration result, unwind it when the component registration fails, and unregister the component before the aggregate on remove. Keep runtime PM enabled until both framework registrations have been removed, and unbind all child components from the aggregate unbind callback. Fixes: 453c3364632a ("drm/mediatek: Add ovl_adaptor support for MT8195") Cc: stable@vger.kernel.org # 6.4+ Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak Reviewed-by: CK Hu Link: https://patchwork.kernel.org/project/linux-mediatek/patch/20260721152242.47138-1-mhun512@gmail.com/ Signed-off-by: Chun-Kuang Hu --- drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c index c0af3e3b51d5..6a259872b0a9 100644 --- a/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl_adaptor.c @@ -625,6 +625,7 @@ static void mtk_disp_ovl_adaptor_master_unbind(struct device *dev) struct mtk_disp_ovl_adaptor *priv = dev_get_drvdata(dev); priv->children_bound = false; + component_unbind_all(dev, priv->mmsys_dev); } static const struct component_master_ops mtk_disp_ovl_adaptor_master_ops = { @@ -651,12 +652,15 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) priv->mmsys_dev = pdev->dev.platform_data; - component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + ret = component_master_add_with_match(dev, &mtk_disp_ovl_adaptor_master_ops, match); + if (ret) + return dev_err_probe(dev, ret, "Failed to add component master\n"); pm_runtime_enable(dev); ret = component_add(dev, &mtk_disp_ovl_adaptor_comp_ops); if (ret != 0) { + component_master_del(dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(dev); return dev_err_probe(dev, ret, "Failed to add component\n"); } @@ -666,6 +670,7 @@ static int mtk_disp_ovl_adaptor_probe(struct platform_device *pdev) static void mtk_disp_ovl_adaptor_remove(struct platform_device *pdev) { + component_del(&pdev->dev, &mtk_disp_ovl_adaptor_comp_ops); component_master_del(&pdev->dev, &mtk_disp_ovl_adaptor_master_ops); pm_runtime_disable(&pdev->dev); } From 8891e39e89042e285fd82fdde325d1311ec750a1 Mon Sep 17 00:00:00 2001 From: Alexander Kaplan Date: Sat, 18 Jul 2026 12:52:07 +0200 Subject: [PATCH 277/501] drm/i915/dp: Ignore the sink's DSC max FRL rate without a PCON DSC encoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit intel_dp_hdmi_sink_max_frl() limits the sink's max FRL rate by its DSC max FRL rate whenever the sink supports DSC 1.2. However, the DSC max FRL rate (HF-VSDB DSC_Max_FRL_Rate) only applies to compressed video transport, which requires a DSC 1.2 encoder in the PCON (configured via intel_dp_pcon_dsc_configure()). Without such an encoder the HDMI link always carries uncompressed video, for which the regular Max_FRL_Rate is the correct limit. Applying the DSC limit unconditionally trains the FRL link at a lower rate than both the PCON and the sink support. E.g. an LG OLED G4 (Max_FRL_Rate 48 Gbps, DSC_Max_FRL_Rate 24 Gbps) behind a Synaptics VMM7100 PCON (PCON max FRL bw 48 Gbps, no DSC encoder): Sink max rate from EDID = 24 Gbps FRL trained with : 24 Gbps while Windows/macOS train the same hardware at 40/48 Gbps. The too low FRL rate needlessly constrains the formats available to the sink. Only apply the sink's DSC max FRL rate if the PCON has a DSC 1.2 encoder, matching the gate in intel_dp_pcon_dsc_configure(). PCONs with a DSC encoder keep the current conservative behavior, since the link is trained once and compressed transport may be used for any subsequent mode. With this the setup above trains at 48 Gbps. Tested on PTL (xe) with the above PCON/sink combo. Fixes: 10fec80b48c5 ("drm/i915/display: Configure PCON for DSC1.1 to DSC1.2 encoding") Cc: Ankit Nautiyal Cc: Ville Syrjälä Reviewed-by: Ankit Nautiyal Signed-off-by: Alexander Kaplan Signed-off-by: Ankit Nautiyal Link: https://patch.msgid.link/20260718105207.5565-3-alexander.kaplan@sms-medipool.de (cherry picked from commit 71b57dd92f94569dca4bdf883fbd8ca5d4ed4bae) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_dp.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 7ff5712f8b19..5733d2e7ac7f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4242,7 +4242,14 @@ static int intel_dp_hdmi_sink_max_frl(struct intel_dp *intel_dp) rate_per_lane = info->hdmi.max_frl_rate_per_lane; max_frl_rate = max_lanes * rate_per_lane; - if (info->hdmi.dsc_cap.v_1p2) { + /* + * The sink's DSC max FRL rate only applies to compressed video + * transport, which requires a DSC 1.2 encoder in the PCON. Without + * one the HDMI link always carries uncompressed video, for which + * the regular max FRL rate is the limit. + */ + if (drm_dp_pcon_enc_is_dsc_1_2(intel_dp->pcon_dsc_dpcd) && + info->hdmi.dsc_cap.v_1p2) { max_dsc_lanes = info->hdmi.dsc_cap.max_lanes; dsc_rate_per_lane = info->hdmi.dsc_cap.max_frl_rate_per_lane; if (max_dsc_lanes && dsc_rate_per_lane) From c256bd486855d8948aff29e0c97d50712da4e85e Mon Sep 17 00:00:00 2001 From: Brajesh Gupta Date: Fri, 24 Jul 2026 20:12:12 +0530 Subject: [PATCH 278/501] drm/imagination: Update the trace point pvr_job_submit_fw() Trace point pvr_job_submit_fw() is used to trace job submission to the FW. Currently it is recorded when a command is written to the Client circular buffer. Move trace recording after writing command to the Kernel circular buffer to better represent command submission to the FW. Fixes: c1079aebb4de ("drm/imagination: Add support for trace points") Signed-off-by: Brajesh Gupta Reviewed-by: Alessio Belle Link: https://patch.msgid.link/20260724-b4-tracepoint-fix-v3-1-8f8e5e8179d3@imgtec.com Signed-off-by: Alessio Belle --- drivers/gpu/drm/imagination/pvr_queue.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/imagination/pvr_queue.c b/drivers/gpu/drm/imagination/pvr_queue.c index 941c017399fc..d13726401d92 100644 --- a/drivers/gpu/drm/imagination/pvr_queue.c +++ b/drivers/gpu/drm/imagination/pvr_queue.c @@ -726,8 +726,6 @@ static void pvr_queue_submit_job_to_cccb(struct pvr_job *job) cmd->partial_render_geom_frag_fence.value = job->done_fence->seqno - 1; } - trace_pvr_job_submit_fw(job); - /* Submit job to FW */ pvr_cccb_write_command_with_header(cccb, job->fw_ccb_cmd_type, job->cmd_len, job->cmd, job->id, job->id); @@ -802,6 +800,9 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) job->hwrt, frag_job->fw_ccb_cmd_type == ROGUE_FWIF_CCB_CMD_TYPE_FRAG_PR); + + trace_pvr_job_submit_fw(geom_job); + trace_pvr_job_submit_fw(frag_job); } else { struct pvr_queue *queue = container_of(job->base.sched, struct pvr_queue, scheduler); @@ -809,6 +810,8 @@ static struct dma_fence *pvr_queue_run_job(struct drm_sched_job *sched_job) pvr_cccb_send_kccb_kick(pvr_dev, &queue->cccb, pvr_context_get_fw_addr(job->ctx) + queue->ctx_offset, job->hwrt); + + trace_pvr_job_submit_fw(job); } return dma_fence_get(job->done_fence); From 537601771adb97a603503bc7837d39eaa44023e1 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 27 Jul 2026 15:58:14 -0300 Subject: [PATCH 279/501] ASoC: dt-bindings: realtek,rt5640: Make interrupts optional The RT5640 GPIO1/IRQ pin can be configured either as GPIO1 or as the codec interrupt output. Some boards, such as the Firefly-RK3399, do not connect the codec interrupt output. This causes the following binding validation warning: 'interrupts' is a required property Make the interrupts property optional to support such hardware configurations. Signed-off-by: Fabio Estevam Link: https://patch.msgid.link/20260727185814.2599488-1-festevam@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/realtek,rt5640.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml index 2eb631950963..922fe161f4d7 100644 --- a/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml +++ b/Documentation/devicetree/bindings/sound/realtek,rt5640.yaml @@ -135,7 +135,6 @@ properties: required: - compatible - reg - - interrupts unevaluatedProperties: false From d94f82d57e7a86def6946f22b34eb53f96628f8a Mon Sep 17 00:00:00 2001 From: Himal Prasad Ghimiray Date: Tue, 28 Jul 2026 11:29:17 +0530 Subject: [PATCH 280/501] drm/xe/pt: check no-DMA huge-pte cases before DMA segment test On a non-range clear, curs.size is never set, so the segment test (next - va_curs_start > curs->size) returns false for every level > 0 before the clear_pt short-circuit is reached. The clear then descends to level 0 instead of forming a huge zero-leaf, wasting page tables and risking -ENOMEM on unbind. Move the null-VMA, purged-BO and clear_pt short-circuits above the curs->size test. The bind path always sets curs.size, so it is unaffected. v2 - Also set curs.size on the clear path so the cursor stays meaningful during the walk. clear_pt is only reached with range == NULL, so assert that invariant. (Matthew Brost) Cc: Matthew Brost Fixes: 5b658b7e89c3 ("drm/xe: Clear scratch page on vm_bind") Reported-by: Sashiko Reviewed-by: Matthew Brost Link: https://patch.msgid.link/20260728055916.593707-2-himal.prasad.ghimiray@intel.com Signed-off-by: Himal Prasad Ghimiray (cherry picked from commit 04eeeb45cb61b8a3e9d785003457e550c920ba49) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/xe/xe_pt.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_pt.c b/drivers/gpu/drm/xe/xe_pt.c index 39c9c8f0ea2d..d0a1e6c2b55f 100644 --- a/drivers/gpu/drm/xe/xe_pt.c +++ b/drivers/gpu/drm/xe/xe_pt.c @@ -443,10 +443,6 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (!xe_pt_covers(addr, next, level, &xe_walk->base)) return false; - /* Does the DMA segment cover the whole pte? */ - if (next - xe_walk->va_curs_start > xe_walk->curs->size) - return false; - /* null VMA's and purged BO's do not have dma addresses */ if (xe_vma_is_null(xe_walk->vma) || (bo && xe_bo_is_purged(bo))) return true; @@ -455,6 +451,10 @@ static bool xe_pt_hugepte_possible(u64 addr, u64 next, unsigned int level, if (xe_walk->clear_pt) return true; + /* Does the DMA segment cover the whole pte? */ + if (next - xe_walk->va_curs_start > xe_walk->curs->size) + return false; + /* Is the DMA address huge PTE size aligned? */ size = next - addr; dma = addr - xe_walk->va_curs_start + xe_res_dma(xe_walk->curs); @@ -775,8 +775,11 @@ xe_pt_stage_bind(struct xe_tile *tile, struct xe_vma *vma, } xe_walk.needs_64K = (vm->flags & XE_VM_FLAG_64K); - if (clear_pt) + if (clear_pt) { + xe_assert(xe, !range); + curs.size = xe_vma_size(vma); goto walk_pt; + } if (vma->gpuva.flags & XE_VMA_ATOMIC_PTE_BIT) { xe_walk.default_vram_pte = xe_atomic_for_vram(vm, vma) ? XE_USM_PPGTT_PTE_AE : 0; From 6e9f539e4f01153651dd77609b5ccadd44b74df8 Mon Sep 17 00:00:00 2001 From: Ralf Lici Date: Wed, 29 Jul 2026 15:41:30 +0200 Subject: [PATCH 281/501] ovpn: add missing rtnl_link_ops->get_size callback ovpn_fill_info emits IFLA_OVPN_MODE inside IFLA_INFO_DATA, but ovpn_link_ops does not provide a get_size callback. Consequently, rtnetlink's size estimate for ovpn link messages does not include the nested mode attribute. Available skb tailroom may hide this mismatch. When the remaining space is insufficient, however, ovpn_fill_info returns -EMSGSIZE and message construction fails. Add the callback and account for IFLA_OVPN_MODE. Fixes: c2d950c4672a ("ovpn: add basic interface creation/destruction/management routines") Signed-off-by: Ralf Lici Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 9993c1dfe471..9d9a0ff690d6 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -210,6 +210,12 @@ static int ovpn_newlink(struct net_device *dev, return register_netdevice(dev); } +static size_t ovpn_get_size(const struct net_device *dev) +{ + /* IFLA_OVPN_MODE */ + return nla_total_size(sizeof(u8)); +} + static int ovpn_fill_info(struct sk_buff *skb, const struct net_device *dev) { struct ovpn_priv *ovpn = netdev_priv(dev); @@ -228,6 +234,7 @@ static struct rtnl_link_ops ovpn_link_ops = { .policy = ovpn_policy, .maxtype = IFLA_OVPN_MAX, .newlink = ovpn_newlink, + .get_size = ovpn_get_size, .fill_info = ovpn_fill_info, }; From b921b8613790a3f9e78ab64017fa7149ef0b750c Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Mon, 20 Jul 2026 13:49:17 +0200 Subject: [PATCH 282/501] drm/panthor: validate firmware interface structure sizes iface_fw_to_cpu_addr() only checks that the firmware-provided MCU virtual address points inside the shared section. The returned pointer is later used as a full firmware interface structure, so accepting an address near the end of the shared section can still lead to out-of-bounds accesses. Pass the expected object size to iface_fw_to_cpu_addr() and reject ranges that do not fit entirely in the shared section. Fixes: 2718d91816ee ("drm/panthor: Add the FW logical block") Cc: stable@vger.kernel.org Signed-off-by: Osama Abdelkader Reviewed-by: Steven Price Signed-off-by: Steven Price Link: https://patch.msgid.link/20260720114918.15973-1-osama.abdelkader@gmail.com --- drivers/gpu/drm/panthor/panthor_fw.c | 36 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/drivers/gpu/drm/panthor/panthor_fw.c b/drivers/gpu/drm/panthor/panthor_fw.c index 0623d37ac617..e45933e333b5 100644 --- a/drivers/gpu/drm/panthor/panthor_fw.c +++ b/drivers/gpu/drm/panthor/panthor_fw.c @@ -864,18 +864,24 @@ static int panthor_fw_load(struct panthor_device *ptdev) * iface_fw_to_cpu_addr() - Turn an MCU address into a CPU address * @ptdev: Device. * @mcu_va: MCU address. + * @size: Size of the object pointed to by @mcu_va. * - * Return: NULL if the address is not part of the shared section, non-NULL otherwise. + * Return: NULL if the object is not part of the shared section, non-NULL otherwise. */ -static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va) +static void *iface_fw_to_cpu_addr(struct panthor_device *ptdev, u32 mcu_va, size_t size) { u64 shared_mem_start = panthor_kernel_bo_gpuva(ptdev->fw->shared_section->mem); - u64 shared_mem_end = shared_mem_start + - panthor_kernel_bo_size(ptdev->fw->shared_section->mem); - if (mcu_va < shared_mem_start || mcu_va >= shared_mem_end) + size_t shared_mem_size = panthor_kernel_bo_size(ptdev->fw->shared_section->mem); + u64 offset; + + if (mcu_va < shared_mem_start) return NULL; - return ptdev->fw->shared_section->mem->kmap + (mcu_va - shared_mem_start); + offset = mcu_va - shared_mem_start; + if (offset > shared_mem_size || size > shared_mem_size - offset) + return NULL; + + return ptdev->fw->shared_section->mem->kmap + offset; } static int panthor_init_cs_iface(struct panthor_device *ptdev, @@ -897,8 +903,10 @@ static int panthor_init_cs_iface(struct panthor_device *ptdev, spin_lock_init(&cs_iface->lock); cs_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va); - cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va); + cs_iface->input = iface_fw_to_cpu_addr(ptdev, cs_iface->control->input_va, + sizeof(*cs_iface->input)); + cs_iface->output = iface_fw_to_cpu_addr(ptdev, cs_iface->control->output_va, + sizeof(*cs_iface->output)); if (!cs_iface->input || !cs_iface->output) { drm_err(&ptdev->base, "Invalid stream control interface input/output VA"); @@ -948,8 +956,10 @@ static int panthor_init_csg_iface(struct panthor_device *ptdev, spin_lock_init(&csg_iface->lock); csg_iface->control = ptdev->fw->shared_section->mem->kmap + iface_offset; - csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va); - csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va); + csg_iface->input = iface_fw_to_cpu_addr(ptdev, csg_iface->control->input_va, + sizeof(*csg_iface->input)); + csg_iface->output = iface_fw_to_cpu_addr(ptdev, csg_iface->control->output_va, + sizeof(*csg_iface->output)); if (csg_iface->control->stream_num < MIN_CS_PER_CSG || csg_iface->control->stream_num > MAX_CS_PER_CSG) @@ -1006,8 +1016,10 @@ static int panthor_fw_init_ifaces(struct panthor_device *ptdev) return -EINVAL; } - glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va); - glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va); + glb_iface->input = iface_fw_to_cpu_addr(ptdev, glb_iface->control->input_va, + sizeof(*glb_iface->input)); + glb_iface->output = iface_fw_to_cpu_addr(ptdev, glb_iface->control->output_va, + sizeof(*glb_iface->output)); if (!glb_iface->input || !glb_iface->output) { drm_err(&ptdev->base, "Invalid global control interface input/output VA"); return -EINVAL; From f27f6976ea269219c1259a7c2f8c6dfe782540a3 Mon Sep 17 00:00:00 2001 From: Hongyan Xu Date: Wed, 29 Jul 2026 18:01:16 +0800 Subject: [PATCH 283/501] hwmon: (npcm750-pwm-fan): stop fan timer on device detach When a fan tach channel is present, npcm7xx_pwm_fan_probe() starts fan_timer. The timer callback polls tach state and rearms the timer, but the driver has no remove callback or devm cleanup action to stop it. On device detach, the devm-managed driver data and I/O mappings can be released while the timer is still pending or running. Register a devm cleanup action before starting the timer and shut the timer down synchronously from that action. This issue was found by a static analysis tool. Fixes: f1fd4a4db777 ("hwmon: Add NPCM7xx PWM and Fan driver") Cc: stable@vger.kernel.org Signed-off-by: Hongyan Xu Link: https://lore.kernel.org/r/20260729100116.790-1-getshell@seu.edu.cn Signed-off-by: Guenter Roeck --- drivers/hwmon/npcm750-pwm-fan.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index aea0b8659f5f..6ff2a6bc06c1 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -358,6 +358,11 @@ static void npcm7xx_fan_polling(struct timer_list *t) add_timer(&data->fan_timer); } +static void npcm7xx_fan_cleanup(void *timer) +{ + timer_shutdown_sync(timer); +} + static inline void npcm7xx_fan_compute(struct npcm7xx_pwm_fan_data *data, u8 fan, u8 cmp, u8 fan_id, u8 flag_int, u8 flag_mode, u8 flag_clear) @@ -1020,6 +1025,12 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) msecs_to_jiffies(NPCM7XX_FAN_POLL_TIMER_200MS); timer_setup(&data->fan_timer, npcm7xx_fan_polling, 0); + ret = devm_add_action_or_reset(dev, + npcm7xx_fan_cleanup, + &data->fan_timer); + if (ret) + return ret; + add_timer(&data->fan_timer); break; } From 779e0eb18c774b81a462a6ee89cfbc9eb2d3cee5 Mon Sep 17 00:00:00 2001 From: Nam Cao Date: Wed, 29 Jul 2026 11:43:28 -0600 Subject: [PATCH 284/501] riscv: time: Add missing __iomem in get_cycles() and get_cycles_hi() __iomem is missing while calling readl_relaxed() in get_cycles() and get_cycles_hi() and sparse complains. Add __iomem to silence the sparse warnings. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607160619.14G8GHp5-lkp@intel.com/ Signed-off-by: Nam Cao Link: https://patch.msgid.link/20260716053319.2178937-1-namcao@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/include/asm/timex.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/timex.h b/arch/riscv/include/asm/timex.h index a06697846e69..d41acfb3959d 100644 --- a/arch/riscv/include/asm/timex.h +++ b/arch/riscv/include/asm/timex.h @@ -22,13 +22,13 @@ static inline cycles_t get_cycles(void) #else /* !CONFIG_64BIT */ static inline u32 get_cycles(void) { - return readl_relaxed(((u32 *)clint_time_val)); + return readl_relaxed(((u32 __iomem *)clint_time_val)); } #define get_cycles get_cycles static inline u32 get_cycles_hi(void) { - return readl_relaxed(((u32 *)clint_time_val) + 1); + return readl_relaxed(((u32 __iomem *)clint_time_val) + 1); } #define get_cycles_hi get_cycles_hi #endif /* CONFIG_64BIT */ From 9a22a1542ca03381dcbf000d7a264cbee61e7203 Mon Sep 17 00:00:00 2001 From: Yong-Xuan Wang Date: Wed, 29 Jul 2026 11:43:28 -0600 Subject: [PATCH 285/501] riscv/sifive: remove warning in errata The alternative patching of sifive vendor extensions also calls the sifive_errata_patch_func(), but the patch_id of the vendor extension (ext + RISCV_VENDOR_EXT_ALTERNATIVES_BASE) is always larger than ERRATA_SIFIVE_NUMBER. Remove this unnecessary warning. Signed-off-by: Yong-Xuan Wang Link: https://patch.msgid.link/20260503-sifive_errata-v1-1-6f12a81bc267@sifive.com [pjw@kernel.org: drop unnecessary braces] Signed-off-by: Paul Walmsley --- arch/riscv/errata/sifive/errata.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/arch/riscv/errata/sifive/errata.c b/arch/riscv/errata/sifive/errata.c index d0c61f86cba3..df80c9614df1 100644 --- a/arch/riscv/errata/sifive/errata.c +++ b/arch/riscv/errata/sifive/errata.c @@ -93,10 +93,8 @@ void sifive_errata_patch_func(struct alt_entry *begin, struct alt_entry *end, for (alt = begin; alt < end; alt++) { if (alt->vendor_id != SIFIVE_VENDOR_ID) continue; - if (alt->patch_id >= ERRATA_SIFIVE_NUMBER) { - WARN(1, "This errata id:%d is not in kernel errata list", alt->patch_id); + if (alt->patch_id >= ERRATA_SIFIVE_NUMBER) continue; - } tmp = (1U << alt->patch_id); if (cpu_req_errata & tmp) { From cfca5a48b03fbd33c8cb84cb73ee2e34467f3a33 Mon Sep 17 00:00:00 2001 From: Troy Mitchell Date: Mon, 27 Jul 2026 01:08:44 -0700 Subject: [PATCH 286/501] riscv: mm: fix SWIOTLB initialization for systems with DRAM above 4GB On RISC-V platforms where the entire physical memory (DRAM) resides above the 32-bit address space (i.e., above dma32_phys_limit), the current SWIOTLB initialization logic fails. This patch addresses two interconnected issues on such platforms: 1. Incorrect 32-bit DMA bounce assumption: The existing condition `max_pfn > PFN_DOWN(dma32_phys_limit)` assumes that a 32-bit DMA bounce buffer is required simply because the maximum PFN exceeds the 32-bit limit. However, if all DRAM starts above 4GB, no memory exists below the limit to satisfy this allocation. Fix this by adding a check to ensure `memblock_start_of_DRAM()` is actually below the 32-bit limit before enforcing 32-bit SWIOTLB. 2. kmalloc() bounce buffer allocation failure on non-coherent systems: For non-coherent DMA, kmalloc() buffers whose sizes are not cache-line-aligned still require bouncing, even if 32-bit DMA bouncing is skipped. Without the `SWIOTLB_ANY` flag, swiotlb_init() defaults to allocating from low memory, which fails completely when DRAM only exists in high memory. By appending `SWIOTLB_ANY` to swiotlb_flags, the allocator is permitted to allocate this bounce buffer from high memory. With this patch, systems with non-coherent DMA and DRAM entirely above 4GB can successfully map the software IO TLB in high memory and boot normally. Tested-by: Anirudh Srinivasan Signed-off-by: Troy Mitchell Link: https://patch.msgid.link/20260727-fix-riscv-swiotlb-v3-1-59479b23736c@linux.dev Reviewed-by: Drew Fustini Signed-off-by: Paul Walmsley --- arch/riscv/mm/init.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 3e450890be07..395ed9ab578e 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -164,7 +164,9 @@ static void print_vm_layout(void) { } void __init arch_mm_preinit(void) { - bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit); + bool swiotlb = max_pfn > PFN_DOWN(dma32_phys_limit) && + memblock_start_of_DRAM() < dma32_phys_limit; + unsigned int swiotlb_flags = SWIOTLB_VERBOSE; #ifdef CONFIG_FLATMEM BUG_ON(!mem_map); #endif /* CONFIG_FLATMEM */ @@ -172,17 +174,22 @@ void __init arch_mm_preinit(void) if (IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) && !swiotlb && dma_cache_alignment != 1) { /* - * If no bouncing needed for ZONE_DMA, allocate 1MB swiotlb - * buffer per 1GB of RAM for kmalloc() bouncing on - * non-coherent platforms. + * No 32-bit DMA bouncing needed (either all DRAM is within + * the 32-bit limit, or it all starts above it), but + * kmalloc() buffers whose sizes are not cache-line-aligned + * still require bouncing for non-coherent DMA. Use + * SWIOTLB_ANY so that the buffer can be allocated from high + * memory when DRAM starts above dma32_phys_limit. Allocate + * ~1 MB per 1 GB of RAM. */ unsigned long size = DIV_ROUND_UP(memblock_phys_mem_size(), 1024); swiotlb_adjust_size(min(swiotlb_size_or_default(), size)); swiotlb = true; + swiotlb_flags |= SWIOTLB_ANY; } - swiotlb_init(swiotlb, SWIOTLB_VERBOSE); + swiotlb_init(swiotlb, swiotlb_flags); print_vm_layout(); } From c786d2bdf1f3964deee192ad942dee2a741c1e2c Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:49:51 +0900 Subject: [PATCH 287/501] tracing/mmiotrace: Reset dropped_count in mmio_reset_data() mmio_reset_data() is called during tracer initialization, reset, and start. While it resets overrun_detected and prev_overruns, it neglects to reset dropped_count. Consequently, dropped event counts from prior tracing sessions persist in dropped_count and corrupt overrun reports in subsequent runs. Fix this by explicitly calling atomic_set(&dropped_count, 0) in mmio_reset_data(). Link: https://patch.msgid.link/178524299122.56416.16277704230639425172.stgit@devnote2 Fixes: 173ed24ee2d6 ("mmiotrace: count events lost due to not recording") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index b88b8d9923ad..e064ba3f28cb 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -29,6 +29,7 @@ static void mmio_reset_data(struct trace_array *tr) { overrun_detected = false; prev_overruns = 0; + atomic_set(&dropped_count, 0); tracing_reset_online_cpus(&tr->array_buffer); } From 12b80cdbc54cf615b4717a4e8180063408091ea2 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 28 Jul 2026 21:50:00 +0900 Subject: [PATCH 288/501] tracing/mmiotrace: Add NULL check for mmio_trace_array in logging functions mmio_trace_rw() and mmio_trace_mapping() retrieve mmio_trace_array into tr and pass it to __trace_mmiotrace_rw() and __trace_mmiotrace_map(). If these functions are invoked while mmio_trace_array is NULL (e.g. before initialization or after disabled), accessing tr->array_buffer.buffer will result in a NULL pointer dereference crash. Fix this by adding an explicit NULL check for tr at the beginning of __trace_mmiotrace_rw() and __trace_mmiotrace_map(). Link: https://patch.msgid.link/178524300062.56416.8362487250709962380.stgit@devnote2 Fixes: f984b51e0779 ("ftrace: add mmiotrace plugin") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_mmiotrace.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c index e064ba3f28cb..df8692c2dea8 100644 --- a/kernel/trace/trace_mmiotrace.c +++ b/kernel/trace/trace_mmiotrace.c @@ -294,11 +294,15 @@ device_initcall(init_mmio_trace); static void __trace_mmiotrace_rw(struct trace_array *tr, struct mmiotrace_rw *rw) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_rw *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_RW, sizeof(*entry), trace_ctx); @@ -321,11 +325,15 @@ void mmio_trace_rw(struct mmiotrace_rw *rw) static void __trace_mmiotrace_map(struct trace_array *tr, struct mmiotrace_map *map) { - struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_buffer *buffer; struct ring_buffer_event *event; struct trace_mmiotrace_map *entry; unsigned int trace_ctx; + if (!tr) + return; + + buffer = tr->array_buffer.buffer; trace_ctx = tracing_gen_ctx_flags(0); event = trace_buffer_lock_reserve(buffer, TRACE_MMIO_MAP, sizeof(*entry), trace_ctx); From ac8719969e6c3c54e939834df812bc41f25453cf Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:27:58 +0900 Subject: [PATCH 289/501] tracing: Check return value of __register_event() in trace_module_add_events() trace_module_add_events() ignores the return value of __register_event() and unconditionally calls __add_event_to_tracers() for each event. If __register_event() fails (for example, if event_init() fails), the trace_event_call is not added to ftrace_events list, but __add_event_to_tracers() still creates a trace_event_file pointing to it. If module loading subsequently fails and module memory is freed, tracing state retains a stale trace_event_call pointer in trace_event_file, leading to a use-after-free when tracefs or tracing subsystem operations are later executed. Fix this by checking the return value of __register_event() and only calling __add_event_to_tracers() if event registration succeeded. Fixes: ae63b31e4d0e ("tracing: Separate out trace events from global variables") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528487878.124250.14170824576025743236.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_events.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 956692856fa8..c01b10b99f67 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -3933,8 +3933,8 @@ static void trace_module_add_events(struct module *mod) end = mod->trace_events + mod->num_trace_events; for_each_event(call, start, end) { - __register_event(*call, mod); - __add_event_to_tracers(*call); + if (!__register_event(*call, mod)) + __add_event_to_tracers(*call); } update_cache_events(mod); From c22c7b735f9810ad276014f788f9aa5c879ec238 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Wed, 29 Jul 2026 09:28:07 +0900 Subject: [PATCH 290/501] tracing/filters: Fix false positive match in regex_match_full() regex_match_full() calls strncmp(str, r->pattern, len) where len is the target field buffer size. When len is smaller than r->len (the filter pattern length), strncmp() checks only len bytes of r->pattern against str. If those len bytes match, strncmp() returns 0, resulting in a false-positive match where a shorter string in a fixed-size field matches a longer filter pattern. For example, a 4-byte static string field containing "abcd" matched the filter pattern "abcdefgh" because strncmp("abcd", "abcdefgh", 4) returned 0. In this case, @len does NOT include '\0' because it is fixed-size array. Fix this by returning 0 (no match) early when len < r->len. Fixes: 1889d20922d1 ("tracing/filters: Provide basic regex support") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/178528488779.124250.5571741156199253769.stgit@devnote2 Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Masami Hiramatsu (Google) Signed-off-by: Steven Rostedt --- kernel/trace/trace_events_filter.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c index 6385cd662d8d..2b46ca536045 100644 --- a/kernel/trace/trace_events_filter.c +++ b/kernel/trace/trace_events_filter.c @@ -1027,6 +1027,9 @@ static int regex_match_full(char *str, struct regex *r, int len) if (!len) return strcmp(str, r->pattern) == 0; + if (len < r->len) + return 0; + return strncmp(str, r->pattern, len) == 0; } From f51fed61eea0daba2f95f1a6074085e4cd513c7b Mon Sep 17 00:00:00 2001 From: Anirudh Srinivasan Date: Fri, 12 Jun 2026 11:24:43 -0500 Subject: [PATCH 291/501] riscv: drop __init from vec_check_unaligned_access_speed_all_cpus This function runs within a kthread and need not necessarily finish before system finishes boot and free_initmem() unmaps the .init.text section. This function makes calls to SBI for probing unaligned access speed, and if this is slow for some reason (say some debug prints were added to SBI), the kthread can still be running at this point and result in an instruction page fault when trying to fetch from the freed region. [ 25.642087] Unable to handle kernel paging request at virtual address ffffffff80a04ef8 [ 25.646694] Current vec_check_unali pgtable: 4K pagesize, 48-bit VAs, pgdp=0x00004000316e9000 [ 25.653170] [ffffffff80a04ef8] pgd=000010004be7e401, p4d=000010004be7e401, pud=000010004be7e001, pmd=000010000c3000e3 [ 25.661244] Oops [#1] [ 25.662997] Modules linked in: [ 25.665357] CPU: 3 UID: 0 PID: 42 Comm: vec_check_unali Not tainted 7.0.0-tt-blackhole-asrinivasan-00007-g30ff73f18211 #570 PREEMPTLAZY [ 25.674669] Hardware name: Tenstorrent Blackhole (DT) [ 25.678545] epc : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.683458] ra : vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.688372] epc : ffffffff80a04ef8 ra : ffffffff80a04ef8 sp : ffff8f8000203e20 [ 25.693874] gp : ffffffff814dc168 tp : ffffaf8001ad9900 t0 : 0000000000000000 [ 25.699401] t1 : fffffffffffffff0 t2 : ffffaf8001ad9a10 s0 : ffff8f8000203e30 [ 25.704912] s1 : ffffaf80018dc780 a0 : 0000000000000000 a1 : 0000000000000002 [ 25.710407] a2 : 00000000000001f0 a3 : 0000000000000018 a4 : 0000000000000000 [ 25.715917] a5 : 0000000000000000 a6 : ffffaf8001c03d98 a7 : ffffaf8001c03e30 [ 25.721419] s2 : ffff8f8000023c98 s3 : ffffaf8001aa1240 s4 : ffffffff80a04ee0 [ 25.726937] s5 : 0000000000000000 s6 : 0000000000000000 s7 : 0000000000000000 [ 25.732450] s8 : 0000000000000000 s9 : 0000000000000000 s10: 0000000000000000 [ 25.737944] s11: 0000000000000000 t3 : 0000000000000002 t4 : 0000000000000402 [ 25.743481] t5 : 0000000000000040 t6 : 0000000000000004 ssp : 0000000000000000 [ 25.749024] status: 0000000200000120 badaddr: ffffffff80a04ef8 cause: 000000000000000c [ 25.755060] [] vec_check_unaligned_access_speed_all_cpus+0x18/0x2c [ 25.760964] [] kthread+0xd8/0xfc [ 25.764660] [] ret_from_fork_kernel+0x18/0x1c4 [ 25.769220] [] ret_from_fork_kernel_asm+0x16/0x18 [ 25.774018] Code: cccc cccc cccc cccc cccc cccc cccc cccc cccc cccc (cccc) cccc Drop __init from its signature so that this doesn't happen. Fixes: a00e022be531 ("riscv: Annotate unaligned access init functions") Signed-off-by: Anirudh Srinivasan Assisted-by: Claude:claude-opus-4-6 Link: https://patch.msgid.link/20260612-vec_unaligned_drop_init-v1-1-df969210ae34@oss.tenstorrent.com Signed-off-by: Paul Walmsley --- arch/riscv/kernel/unaligned_access_speed.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/kernel/unaligned_access_speed.c b/arch/riscv/kernel/unaligned_access_speed.c index bb57eb5d19df..5a5aa22124e7 100644 --- a/arch/riscv/kernel/unaligned_access_speed.c +++ b/arch/riscv/kernel/unaligned_access_speed.c @@ -289,7 +289,7 @@ static void check_vector_unaligned_access(struct work_struct *work __always_unus } /* Measure unaligned access speed on all CPUs present at boot in parallel. */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { schedule_on_each_cpu(check_vector_unaligned_access); riscv_hwprobe_complete_async_probe(); @@ -297,7 +297,7 @@ static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __alway return 0; } #else /* CONFIG_RISCV_PROBE_VECTOR_UNALIGNED_ACCESS */ -static int __init vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) +static int vec_check_unaligned_access_speed_all_cpus(void *unused __always_unused) { return 0; } From 52075128273ace53e6254e37899a47d40d4baf45 Mon Sep 17 00:00:00 2001 From: Pratik Vishwakarma Date: Wed, 29 Jul 2026 05:54:59 +0000 Subject: [PATCH 292/501] x86/CPU/AMD: Carve out a Zen5 models range Family 0x1a, model 0xd0..0xd7 belongs to the Zen5 generation. Carve it out from the larger, Zen6 range where former doesn't belong. [ bp: Rewrite commit message, add tags. ] Fixes: b5f53e6d3d32 ("x86/CPU/AMD: Add more Zen6 models") Signed-off-by: Pratik Vishwakarma Signed-off-by: Borislav Petkov (AMD) Cc: Link: https://patch.msgid.link/20260729055459.15904-1-Pratik.Vishwakarma@amd.com --- arch/x86/kernel/cpu/amd.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 487ac147e11f..d61df70d6875 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -515,11 +515,13 @@ static void bsp_init_amd(struct cpuinfo_x86 *c) case 0x00 ... 0x2f: case 0x40 ... 0x4f: case 0x60 ... 0x7f: + case 0xd0 ... 0xd7: setup_force_cpu_cap(X86_FEATURE_ZEN5); break; case 0x50 ... 0x5f: case 0x80 ... 0xaf: - case 0xc0 ... 0xef: + case 0xc0 ... 0xcf: + case 0xd8 ... 0xef: setup_force_cpu_cap(X86_FEATURE_ZEN6); break; default: From f865c143629d4094866a811dba5f329250bad486 Mon Sep 17 00:00:00 2001 From: Zhan Xusheng Date: Sat, 18 Jul 2026 13:09:22 +0800 Subject: [PATCH 293/501] audit: fix potential integer overflow in audit_log_n_string() audit_log_n_string() computes new_len as "slen + 3" (enclosing quotes plus the NUL terminator) and stores it into an int, while slen is a size_t. For a sufficiently large slen the addition can overflow and/or the result be truncated when assigned to the int new_len, so the "new_len > avail" check can be bypassed and the subsequent memcpy(ptr, string, slen) can write past the skb tail. This is the same class of bug that was fixed for the hex sibling in commit 65dfde57d1e2 ("audit: fix potential integer overflow in audit_log_n_hex()"); both helpers are reached through audit_log_n_untrustedstring() with the same length source. Make new_len a size_t and use check_add_overflow() to catch the overflow, mirroring the audit_log_n_hex() fix. No functional change for the in-tree callers, which all pass bounded lengths. Cc: stable@vger.kernel.org Fixes: 168b7173959f ("AUDIT: Clean up logging of untrusted strings") Signed-off-by: Zhan Xusheng Signed-off-by: Paul Moore --- kernel/audit.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/kernel/audit.c b/kernel/audit.c index 562476937fa7..9412af9144bc 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -2120,7 +2120,8 @@ void audit_log_n_hex(struct audit_buffer *ab, const unsigned char *buf, void audit_log_n_string(struct audit_buffer *ab, const char *string, size_t slen) { - int avail, new_len; + int avail; + size_t new_len; unsigned char *ptr; struct sk_buff *skb; @@ -2130,7 +2131,13 @@ void audit_log_n_string(struct audit_buffer *ab, const char *string, BUG_ON(!ab->skb); skb = ab->skb; avail = skb_tailroom(skb); - new_len = slen + 3; /* enclosing quotes + null terminator */ + + /* enclosing quotes + null terminator */ + if (check_add_overflow(slen, 3, &new_len)) { + audit_log_format(ab, "?"); + return; + } + if (new_len > avail) { avail = audit_expand(ab, new_len); if (!avail) From 246df90b5f1a8a6e6abbd2f058b029558720adec Mon Sep 17 00:00:00 2001 From: Luxiao Xu Date: Tue, 21 Jul 2026 23:37:41 +0800 Subject: [PATCH 294/501] audit: fix potential use-after-free in audit_del_rule() `audit_del_rule()` destroys `e->rule.exe` via `audit_remove_mark_rule()` before unlinking the rule from RCU-visible filter lists and waiting for a grace period. Concurrent readers in `audit_filter()` and `audit_filter_rules()` still dereference `e->rule.exe`, while the fsnotify mark can be freed on an independent lifetime path. This creates a use-after-free window during rule deletion. Fix this by unlinking the rule from the RCU-visible lists and invoking `synchronize_rcu()` before calling `audit_remove_mark_rule()` (and other rule removal helpers). This ensures that all existing RCU readers have exited the critical section before any underlying resources are destroyed. Cc: stable@vger.kernel.org Fixes: 34d99af52ad4 ("audit: implement audit by executable") Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Luxiao Xu Signed-off-by: Ren Wei Signed-off-by: Paul Moore --- kernel/auditfilter.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index 4401119b5275..7f791afe5791 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1045,6 +1045,10 @@ int audit_del_rule(struct audit_entry *entry) goto out; } + list_del_rcu(&e->list); + list_del(&e->rule.list); + synchronize_rcu(); + if (e->rule.watch) audit_remove_watch_rule(&e->rule); @@ -1062,8 +1066,6 @@ int audit_del_rule(struct audit_entry *entry) audit_signals--; #endif - list_del_rcu(&e->list); - list_del(&e->rule.list); call_rcu(&e->rcu, audit_free_rule_rcu); out: From d6c075f797a672a6e3bd2fd44aee713801698ec2 Mon Sep 17 00:00:00 2001 From: Muhammad Bilal Date: Thu, 18 Jun 2026 02:25:20 +0500 Subject: [PATCH 295/501] accel/qaic: use sizeof(*trans_hdr) for transaction length check In encode_message() the per-transaction lower-bound check compares trans_hdr->len against sizeof(trans_hdr), i.e. the size of the pointer, instead of sizeof(*trans_hdr), the size of struct qaic_manage_trans_hdr. Every other length check in this file (encode_message() at the loop guard, decode_message(), etc.) correctly uses sizeof(*trans_hdr), so this is an inconsistency. On 64-bit builds the pointer and the struct are both 8 bytes, so the check is correct by coincidence and there is no behavioural change. On 32-bit builds the pointer is 4 bytes, which weakens the minimum-length check below the 8-byte header size. Use sizeof(*trans_hdr) so the check validates against the actual transaction header size on all builds. Fixes: ea33cb6fc278 ("accel/qaic: tighten bounds checking in encode_message()") Signed-off-by: Muhammad Bilal Reviewed-by: Jeff Hugo Signed-off-by: Jeff Hugo Link: https://patch.msgid.link/20260617212520.59801-1-meatuni001@gmail.com --- drivers/accel/qaic/qaic_control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/accel/qaic/qaic_control.c b/drivers/accel/qaic/qaic_control.c index bb94d3556904..50bf3340e49c 100644 --- a/drivers/accel/qaic/qaic_control.c +++ b/drivers/accel/qaic/qaic_control.c @@ -786,7 +786,7 @@ static int encode_message(struct qaic_device *qdev, struct manage_msg *user_msg, break; } trans_hdr = (struct qaic_manage_trans_hdr *)(user_msg->data + user_len); - if (trans_hdr->len < sizeof(trans_hdr) || + if (trans_hdr->len < sizeof(*trans_hdr) || size_add(user_len, trans_hdr->len) > user_msg->len) { ret = -EINVAL; break; From a3ba349af8e1bb7e0efdcd2dd53d69282f18478a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 13 Jul 2026 14:58:38 +0200 Subject: [PATCH 296/501] arm64: dts: qcom: eliza: Fix DSI1 phy reference clock rate The DSI PHY CXO clock input is the SoC CXO divided by two. DSI0 already uses correct one, but DSI1 got copy-paste from SM8750, which had same problem and copy-pasted code from SM8650. Wrong clock parent will cause incorrect DSI1 PHY PLL frequencies to be used making the DSI panel non-working, although there is no upstream user of DSI1. Fixes: 159d252ed800 ("arm64: dts: qcom: eliza: Add display (MDSS) with Display CC") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20260713125837.727632-2-krzysztof.kozlowski@oss.qualcomm.com Signed-off-by: Bjorn Andersson --- arch/arm64/boot/dts/qcom/eliza.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/boot/dts/qcom/eliza.dtsi b/arch/arm64/boot/dts/qcom/eliza.dtsi index 977de44b816e..72b786fec195 100644 --- a/arch/arm64/boot/dts/qcom/eliza.dtsi +++ b/arch/arm64/boot/dts/qcom/eliza.dtsi @@ -2559,7 +2559,7 @@ mdss_dsi1_phy: phy@ae97000 { "dsi_pll"; clocks = <&dispcc DISP_CC_MDSS_AHB_CLK>, - <&rpmhcc RPMH_CXO_CLK>; + <&bi_tcxo_div2>; clock-names = "iface", "ref"; From a65f5179d3f0c93da31b450aeb416eaa22f1912b Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Wed, 29 Jul 2026 15:56:46 +0800 Subject: [PATCH 297/501] drm/amd/pm: hide pp_table sysfs on APUs APUs use firmware-owned DPM tables and do not support replacement through pp_table. Generic callbacks can nevertheless expose the sysfs file and accept an upload before resetting the power management stack. Treat pp_table as unsupported on APUs. Use the same platform check in the get and set paths to hide the file and reject uploads. Fixes: 289921b03fe5 ("drm/amd/powerplay: implement sysfs of pp_table for smu11 (v2)") Signed-off-by: Yang Wang Reviewed-by: Kenneth Feng Reviewed-by: Asad Kamal Signed-off-by: Alex Deucher (cherry picked from commit 74f28db2db69777cd2f059d50fe34e365ddd5add) Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/pm/amdgpu_dpm.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 6d1ad4d5b8f0..e5e89294958a 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -1183,6 +1183,13 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev, return ret; } +static bool amdgpu_dpm_is_pp_table_allowed(struct amdgpu_device *adev) +{ + return !amdgpu_sriov_vf(adev) && + !(adev->flags & AMD_IS_APU) && + !adev->scpm_enabled; +} + int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, size_t size) { @@ -1193,7 +1200,8 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table, if ((!table && size) || (table && !size)) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->get_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); @@ -1717,7 +1725,8 @@ int amdgpu_dpm_set_pp_table(struct amdgpu_device *adev, if (!buf || !size) return -EINVAL; - if (amdgpu_sriov_vf(adev) || !pp_funcs->set_pp_table || adev->scpm_enabled) + if (!amdgpu_dpm_is_pp_table_allowed(adev) || + !pp_funcs->set_pp_table) return -EOPNOTSUPP; mutex_lock(&adev->pm.mutex); From 8419331e64d92a8de5fc4feef0e305f201fb8b33 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Tue, 28 Jul 2026 13:02:47 -0400 Subject: [PATCH 298/501] drm/amd/display: Exit idle optimizations before programming [Why] We need to exit PSR/IPS before programming. Before calling DC for programming in amdgpu_dm_commit_planes(), there's a vblank_control_workqueue flush. This waits for IPS and PSR exit. (See drm_vblank_on/off() > amdgpu_dm_crtc_set_vblank() --queue_work()-> amdgpu_dm_crtc_vblank_control_worker()) Prior to the tagged "Fixes:" change, drm_vblank_get() was called before the workqueue flush. This ordering ensures that PSR exit occurred before programming. After the "Fixes:" change, drm_vblank_get() is called after the workqueue flush, leading to programming while idle optimizations are still active. This can lead to incorrect flip_pending detection used by vblank event delivery. [How] Split the vblank_get() component of `dm_arm_vblank_event()` into `dm_arm_vblank_event_pre_programming()`, which is called before programming. Call it before the vblank_control_workqueue flush. Includes a drive-by cleanup of prepare_flip_isr(): the only caller is dm_arm_vblank_event() and it's simple enough to roll-in. v2: Fix checkpatch formatting warning on drm_arm_vblank_event_pre_programming() arg alignment. Fixes: 48ab86360af1 ("drm/amd/display: check GRPH_FLIP status before sending event") Cc: stable@vger.kernel.org Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/4141#note_3583205 Link: https://gitlab.freedesktop.org/drm/amd/-/work_items/5527 Assisted-by: Codex:gpt-5.6-sol Assisted-by: Claude:opus-5 Suggested-by: David Weber Signed-off-by: Leo Li Reviewed-by: Mario Limonciello (AMD) Signed-off-by: Alex Deucher (cherry picked from commit 05984e29520a28c27f5a2388742c957a6a87ee7a) --- .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index eaf19ec843e8..1820547b1dde 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -9903,25 +9903,6 @@ static void remove_stream(struct amdgpu_device *adev, acrtc->enabled = false; } -static void prepare_flip_isr(struct amdgpu_crtc *acrtc) -{ - - assert_spin_locked(&acrtc->base.dev->event_lock); - WARN_ON(acrtc->event); - - acrtc->event = acrtc->base.state->event; - - /* Set the flip status */ - acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; - - /* Mark this event as consumed */ - acrtc->base.state->event = NULL; - - drm_dbg_state(acrtc->base.dev, - "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", - acrtc->crtc_id); -} - static void update_freesync_state_on_stream( struct amdgpu_display_manager *dm, struct dm_crtc_state *new_crtc_state, @@ -10274,17 +10255,47 @@ static void dm_arm_vblank_event(struct amdgpu_crtc *acrtc, return; if (pflip_update) { - drm_crtc_vblank_get(&acrtc->base); WARN_ON(acrtc->pflip_status != AMDGPU_FLIP_NONE); - /* Arm flip completion handling and event delivery after programming. */ - prepare_flip_isr(acrtc); + WARN_ON(acrtc->event); + + acrtc->pflip_status = AMDGPU_FLIP_SUBMITTED; + acrtc->event = acrtc->base.state->event; + acrtc->base.state->event = NULL; + + drm_dbg_state(acrtc->base.dev, + "crtc:%d, pflip_stat:AMDGPU_FLIP_SUBMITTED\n", + acrtc->crtc_id); } else if (cursor_update) { - drm_crtc_vblank_get(&acrtc->base); acrtc->event = acrtc->base.state->event; acrtc->base.state->event = NULL; } } +/** + * dm_arm_vblank_event_pre_programming - Prepare for programming + * @acrtc: The amdgpu CRTC to prepare + * @acrtc_state: The new CRTC state + * @pflip_update: Whether a page flip is being programmed + * @cursor_update: Whether a cursor update is being programmed + * + * Grab a reference on the vblank counter if a page flip or cursor update is to + * be programmed. Do this before programming so the HW is not in any + * idle-optimized state (such as PSR). + */ +static void dm_arm_vblank_event_pre_programming(struct amdgpu_crtc *acrtc, + struct dm_crtc_state *acrtc_state, + bool pflip_update, + bool cursor_update) +{ + assert_spin_locked(&acrtc->base.dev->event_lock); + + if (!acrtc->base.state->event || acrtc_state->active_planes == 0) + return; + + if (pflip_update || cursor_update) + drm_crtc_vblank_get(&acrtc->base); +} + static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, struct drm_device *dev, struct amdgpu_display_manager *dm, @@ -10550,16 +10561,19 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_commit *state, } } - /* - * DCE depends on a combination of GRPH_FLIP, VLINE0, and VUPDATE for - * event delivery. Only GRPH_FLIP handler can send pflip events, and it - * only fires if HW latched to the flip. Maintain legacy behavior by - * arming event before programming. - */ - if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { - scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + scoped_guard(spinlock_irqsave, &pcrtc->dev->event_lock) { + dm_arm_vblank_event_pre_programming(acrtc_attach, acrtc_state, + pflip_present, + cursor_update); + /* + * DCE depends on a combination of GRPH_FLIP, VLINE0, and + * VUPDATE for event delivery. Only GRPH_FLIP handler can send + * pflip events, and it only fires if HW latched to the flip. + * Maintain legacy behavior by arming event before programming. + */ + if (amdgpu_ip_version(dm->adev, DCE_HWIP, 0) == 0) { dm_arm_vblank_event(acrtc_attach, acrtc_state, - pflip_present, cursor_update); + pflip_present, cursor_update); } } From d0ecbedd6a70f2fe768d46de740265dc8a4424e3 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Tue, 28 Jul 2026 17:01:11 +0800 Subject: [PATCH 299/501] ASoC: rt722: reset codec to fix abnormal sound The audio output may become abnormal after a warm reboot from Windows. Reset the codec once during hardware initialization to restore it to a known state and prevent the issue. BugLink: https://github.com/thesofproject/linux/issues/5845 Signed-off-by: Shuming Fan Link: https://patch.msgid.link/20260728090111.3676617-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt722-sdca-sdw.c | 4 ++++ sound/soc/codecs/rt722-sdca.c | 14 ++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index e68aa0350a5b..7482a9a8d198 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -113,6 +113,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case 0x2000090 ... 0x2000094: case 0x20000b1: case 0x20000b4: + case 0x3010000: case 0x3110000: case 0x5300000 ... 0x5300300: case 0x5400002: @@ -196,15 +197,18 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x2000007: case 0x200000d: case 0x2000019: + case 0x200001a: case 0x2000020: case 0x2000030: case 0x2000046: case 0x2000067: case 0x2000084: case 0x2000086: + case 0x3010000: case 0x3110000: case 0x5800003: case 0x5810000: + case 0x6100008: case 0x44011000 ... 0x440115ff: case 0x44012000: case 0x44012021: diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 1b6729f363fc..6b509eb65d77 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -1851,6 +1851,17 @@ static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) } } +static void rt722_sdca_reset(struct rt722_sdca_priv *rt722) +{ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_LDO1_CTL, RT722_HIDDEN_REG_SW_RESET, + RT722_HIDDEN_REG_SW_RESET); + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_RESET_CTL, 0x1, 0x1); + if (rt722->hw_vid == RT722_VA) + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_LDO1_CTL, 0xb091); +} + int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); @@ -1888,6 +1899,9 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) rt722->hw_vid = (val & 0x0f00) >> 8; dev_dbg(&slave->dev, "%s hw_vid=0x%x\n", __func__, rt722->hw_vid); + if (!rt722->first_hw_init) + rt722_sdca_reset(rt722); + rt722_sdca_dmic_preset(rt722); rt722_sdca_amp_preset(rt722); rt722_sdca_jack_preset(rt722); From bab4d538f8485e0d48538fcb82b285df3779278e Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sat, 25 Jul 2026 14:57:22 -0700 Subject: [PATCH 300/501] ARM: dts: BCM5301X: fix PCIe controller 2 second interrupt PCIe controller 2 has interrupts 0-4 mapping to GIC SPI 138-142. The mapping for interrupt 1 was incorrectly set to 138 due to a copy-paste error. Fix it to 139. Assisted-by: opencode:big-pickle Signed-off-by: Rosen Penev Link: https://lore.kernel.org/r/20260725215722.9323-1-rosenp@gmail.com Fixes: 3b3e35b279be ("ARM: dts: BCM5301X: Relicense AXI interrupts code to the GPL 2.0+ / MIT") Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/broadcom/bcm-ns.dtsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi index 392a25713669..bd52de0faa3e 100644 --- a/arch/arm/boot/dts/broadcom/bcm-ns.dtsi +++ b/arch/arm/boot/dts/broadcom/bcm-ns.dtsi @@ -141,7 +141,7 @@ axi@18000000 { /* PCIe Controller 2 */ <0x00014000 0 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>, - <0x00014000 1 &gic GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>, + <0x00014000 1 &gic GIC_SPI 139 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 2 &gic GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 3 &gic GIC_SPI 141 IRQ_TYPE_LEVEL_HIGH>, <0x00014000 4 &gic GIC_SPI 142 IRQ_TYPE_LEVEL_HIGH>, From 6de6732c4c784ce64e2457630ca800dfe5efb774 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Sun, 28 Jun 2026 16:10:49 -0700 Subject: [PATCH 301/501] ARM: dts: BCM5301X: EA9200: fix NVRAM size Fixes: [ 0.182121] WARNING: CPU: 0 PID: 1 at drivers/nvmem/brcm_nvram.c:85 brcm_nvram_probe+0x400/0x480 [ 0.182159] Unexpected (big) NVRAM size: 1056112 B Fixes: 90a77291ac09 ("ARM: dts: BCM5301X: R8000 add NVRAM with MAC address for WAN port") Signed-off-by: Rosen Penev Link: https://lore.kernel.org/r/20260628231049.1248899-1-rosenp@gmail.com Signed-off-by: Florian Fainelli --- arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts index 5bbc2ba0f959..87569408bb69 100644 --- a/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts +++ b/arch/arm/boot/dts/broadcom/bcm4709-linksys-ea9200.dts @@ -26,7 +26,7 @@ memory@0 { nvram@1c080000 { compatible = "brcm,nvram"; - reg = <0x1c080000 0x180000>; + reg = <0x1c080000 0x100000>; et2macaddr: et2macaddr { #nvmem-cell-cells = <1>; From e5a259d98823a93459643b239b71fed7589ab669 Mon Sep 17 00:00:00 2001 From: Yafang Shao Date: Tue, 28 Jul 2026 10:49:50 +0800 Subject: [PATCH 302/501] kbuild: rpm-pkg: Preserve BTF sections in kernel modules during debuginfo stripping After switching to the kernel's default package scripts for our local kernel RPM builds, we noticed that module BTF entries were missing: $ ls /sys/kernel/btf/ vmlinux <<<< only vmlinux, no module BTF Root cause: find-debuginfo.sh (from the debugedit package) prefers eu-strip over strip when elfutils is installed, which is the common case on RHEL 9. eu-strip removes non-allocated ELF sections, including the .BTF section that contains BPF Type Format information for kernel modules. Without .BTF, BPF tools (bpftool, bcc, bpftrace) cannot resolve kernel types at runtime, and /sys/kernel/btf/ entries are not created when modules are loaded. Additionally, since commit 8646db238997 ("libbpf,bpf: Share BTF relocate-related code with kernel"), modules contain a .BTF.base section that maps distilled type IDs to vmlinux types. If .BTF.base is stripped, btf_parse_module() falls back to vmlinux BTF directly, causing type ID mismatches and rejecting the module's BTF entirely. Fix by passing --keep-section .BTF and --keep-section .BTF.base via _find_debuginfo_opts, which adds -K .BTF and -K .BTF.base to the eu-strip/strip command, preserving both sections while allowing normal debuginfo extraction to proceed. After this change, all module BTF files are properly generated: $ ls /sys/kernel/btf/ aesni_intel drm i2c_i801 mfd_core ahci drm_client_lib i2c_mux net_failover backlight drm_kms_helper i2c_smbus pcspkr ccp drm_shmem_helper input_leds qemu_fw_cfg dm_log failover intel_rapl_common sch_fq_codel dm_mirror fat intel_rapl_msr serio_raw dm_mod fuse irqbypass sunrpc dm_region_hash gf128mul iTCO_wdt vfat virtio_balloon virtio_console virtio_dma_buf virtio_gpu virtio_net virtio_rng virtio_blk vmlinux xfs Suggested-by: Nathan Chancellor Signed-off-by: Yafang Shao Cc: Alan Maguire Link: https://patch.msgid.link/20260728024950.44946-1-laoar.shao@gmail.com Signed-off-by: Nathan Chancellor --- scripts/package/kernel.spec | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/package/kernel.spec b/scripts/package/kernel.spec index c732415662ef..46e80970f723 100644 --- a/scripts/package/kernel.spec +++ b/scripts/package/kernel.spec @@ -67,7 +67,18 @@ This package provides debug information for the kernel image and modules from th %undefine _unique_debug_srcs %undefine _debugsource_packages %undefine _debuginfo_subpackages + +# Preserve .BTF and .BTF.base sections in kernel modules during debuginfo +# stripping. find-debuginfo.sh uses eu-strip which removes non-allocated ELF +# sections like .BTF by default. .BTF.base is required for BTF distillation +# support; without it, module BTF validation fails. +%global with_keep_section %(%{__find_debuginfo} --help 2>&1 | grep -c keep-section) +%if %{with_keep_section} +%global _find_debuginfo_opts -r --keep-section .BTF --keep-section .BTF.base +%else %global _find_debuginfo_opts -r +%endif + %global _missing_build_ids_terminate_build 1 %global _no_recompute_build_ids 1 %{debug_package} From a0188cc133696627857d16054e43f9ebc7efc821 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Wed, 29 Jul 2026 03:21:32 +0200 Subject: [PATCH 303/501] riscv: mm: Fix out-of-bounds page-table walk during memory hot-remove remove_pud_mapping() and remove_p4d_mapping() obtain a child table base with pud_offset(p4dp, 0) and p4d_offset(pgd, 0), then add the index for addr. RISC-V folds page-table levels at runtime. When a level is folded, its offset helper returns the parent entry itself, but the index can still be nonzero. Adding it walks past the parent table. Sv48 folds P4D, while Sv39 folds both P4D and PUD, so memory hot-remove can descend into unrelated memory and pass an invalid page to __free_pages(). This can trigger: kernel BUG at include/linux/mm.h:1810! VM_BUG_ON_PAGE(page_ref_count(page) == 0) arch_remove_memory+0x1e/0x5c try_remove_memory+0x15e/0x200 remove_memory+0x24/0x3c Only add the index when the corresponding page-table level is enabled, matching p4d_offset() and pud_offset(). Fixes: c75a74f4ba19 ("riscv: mm: Add memory hotplugging support") Assisted-by: Claude:claude-fable-5 Signed-off-by: Karl Mehltretter Link: https://patch.msgid.link/20260729012132.24882-1-kmehltretter@gmail.com Signed-off-by: Paul Walmsley --- arch/riscv/mm/init.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/mm/init.c b/arch/riscv/mm/init.c index 395ed9ab578e..b6d149e989c8 100644 --- a/arch/riscv/mm/init.c +++ b/arch/riscv/mm/init.c @@ -1625,7 +1625,7 @@ static void __meminit remove_pud_mapping(pud_t *pud_base, unsigned long addr, un for (; addr < end; addr = next) { next = pud_addr_end(addr, end); - pudp = pud_base + pud_index(addr); + pudp = pgtable_l4_enabled ? pud_base + pud_index(addr) : pud_base; pud = pudp_get(pudp); if (!pud_present(pud)) continue; @@ -1656,7 +1656,7 @@ static void __meminit remove_p4d_mapping(p4d_t *p4d_base, unsigned long addr, un for (; addr < end; addr = next) { next = p4d_addr_end(addr, end); - p4dp = p4d_base + p4d_index(addr); + p4dp = pgtable_l5_enabled ? p4d_base + p4d_index(addr) : p4d_base; p4d = p4dp_get(p4dp); if (!p4d_present(p4d)) continue; From 78cd56c2a9d2e8da763cea3b06b636266ca66911 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Wed, 29 Jul 2026 14:36:09 +0100 Subject: [PATCH 304/501] ring-buffer: Fix reader page read offset for remote buffers A page swapped in by __rb_get_reader_page_from_remote() retains its stale read offset, causing subsequent reads to skip events or read past valid data. Fix it. Link: https://patch.msgid.link/20260729133609.4022734-1-vdonnefort@google.com Fixes: fbd1743ecba1 ("ring-buffer: Add non-consuming read for ring-buffer remotes") Signed-off-by: Vincent Donnefort Reviewed-by: Keir Fraser Tested-by: Keir Fraser Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 804ccae694d2..78d3875a47a5 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5783,6 +5783,7 @@ __rb_get_reader_page_from_remote(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->head_page = new_head; cpu_buffer->reader_page = new_reader; + cpu_buffer->reader_page->read = 0; cpu_buffer->pages = &new_head->list; cpu_buffer->read_stamp = new_reader->page->time_stamp; cpu_buffer->lost_events = cpu_buffer->meta_page->reader.lost_events; From c052927905710de1ab7364bf1925efbd12517ea3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Thu, 9 Jul 2026 08:49:00 +0200 Subject: [PATCH 305/501] riscv: vdso: Only try to install vDSO when present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vdso.so.dbg is only built with CONFIG_MMU. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202607090258.iSAUYlO1-lkp@intel.com/ Fixes: f157d411a9eb ("riscv: add missing vdso_install target") Fixes: 3edf39916977 ("vDSO, kbuild: Provide vDSO debug variants at runtime") Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260709-riscv-install-vdso-v1-1-0ba4345419ca@linutronix.de Signed-off-by: Paul Walmsley --- arch/riscv/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile index ce0cc737f870..1363e5bef35c 100644 --- a/arch/riscv/Makefile +++ b/arch/riscv/Makefile @@ -168,7 +168,7 @@ vdso_prepare: prepare0 endif endif -vdso-install-y += arch/riscv/kernel/vdso/vdso.so.dbg +vdso-install-$(CONFIG_MMU) += arch/riscv/kernel/vdso/vdso.so.dbg vdso-install-$(CONFIG_RISCV_USER_CFI) += arch/riscv/kernel/vdso_cfi/vdso-cfi.so.dbg vdso-install-$(CONFIG_COMPAT) += arch/riscv/kernel/compat_vdso/compat_vdso.so.dbg From 935dc3bb20e0fee89b598e10d1f5f1f500ddc79a Mon Sep 17 00:00:00 2001 From: Marco Baffo Date: Wed, 22 Jul 2026 06:47:56 +0200 Subject: [PATCH 306/501] ovpn: limit keepalive values to one day Large keepalive values can overflow the delayed-work delay on 32-bit systems, causing the keepalive worker to be repeatedly scheduled. A correct configuration should not require such large keepalive values, and an upper limit of one day is already generous and unnecessary in practice. Limit both the keepalive interval and timeout to 86400 seconds. Signed-off-by: Marco Baffo Signed-off-by: Antonio Quartulli --- Documentation/netlink/specs/ovpn.yaml | 4 ++++ drivers/net/ovpn/netlink-gen.c | 20 ++++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/Documentation/netlink/specs/ovpn.yaml b/Documentation/netlink/specs/ovpn.yaml index b0c782e59a32..ac50d1d7c00a 100644 --- a/Documentation/netlink/specs/ovpn.yaml +++ b/Documentation/netlink/specs/ovpn.yaml @@ -118,12 +118,16 @@ attribute-sets: doc: >- The number of seconds after which a keep alive message is sent to the peer + checks: + max: 86400 - name: keepalive-timeout type: u32 doc: >- The number of seconds from the last activity after which the peer is assumed dead + checks: + max: 86400 - name: del-reason type: u32 diff --git a/drivers/net/ovpn/netlink-gen.c b/drivers/net/ovpn/netlink-gen.c index 2147cec7c2c5..92d2fdc17c2e 100644 --- a/drivers/net/ovpn/netlink-gen.c +++ b/drivers/net/ovpn/netlink-gen.c @@ -16,6 +16,14 @@ static const struct netlink_range_validation ovpn_a_peer_id_range = { .max = 16777215ULL, }; +static const struct netlink_range_validation ovpn_a_peer_keepalive_interval_range = { + .max = 86400ULL, +}; + +static const struct netlink_range_validation ovpn_a_peer_keepalive_timeout_range = { + .max = 86400ULL, +}; + static const struct netlink_range_validation ovpn_a_peer_tx_id_range = { .max = 16777215ULL, }; @@ -68,8 +76,8 @@ const struct nla_policy ovpn_peer_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_PORT] = NLA_POLICY_MIN(NLA_BE16, 1), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_DEL_REASON] = NLA_POLICY_MAX(NLA_U32, 4), [OVPN_A_PEER_VPN_RX_BYTES] = { .type = NLA_UINT, }, [OVPN_A_PEER_VPN_TX_BYTES] = { .type = NLA_UINT, }, @@ -97,8 +105,8 @@ const struct nla_policy ovpn_peer_new_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; @@ -112,8 +120,8 @@ const struct nla_policy ovpn_peer_set_input_nl_policy[OVPN_A_PEER_TX_ID + 1] = { [OVPN_A_PEER_VPN_IPV6] = NLA_POLICY_EXACT_LEN(16), [OVPN_A_PEER_LOCAL_IPV4] = { .type = NLA_BE32, }, [OVPN_A_PEER_LOCAL_IPV6] = NLA_POLICY_EXACT_LEN(16), - [OVPN_A_PEER_KEEPALIVE_INTERVAL] = { .type = NLA_U32, }, - [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = { .type = NLA_U32, }, + [OVPN_A_PEER_KEEPALIVE_INTERVAL] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_interval_range), + [OVPN_A_PEER_KEEPALIVE_TIMEOUT] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_keepalive_timeout_range), [OVPN_A_PEER_TX_ID] = NLA_POLICY_FULL_RANGE(NLA_U32, &ovpn_a_peer_tx_id_range), }; From 33ec10567fe14456063daf549fdf1a4f53448e4c Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:47 +0200 Subject: [PATCH 307/501] ovpn: skip rehash for peers already removed from by_id ovpn_nl_peer_set_doit() resolves the target peer via ovpn_peer_get_by_id() before taking ovpn->lock. In the window between the lookup (which only takes a refcount) and the subsequent spin_lock_bh(&ovpn->lock), a concurrent OVPN_CMD_PEER_DEL, keepalive expiry, or socket teardown can take ovpn->lock first, run ovpn_peer_remove() to unhash the peer from all four tables (by_id, by_vpn_addr4/6, by_transp_addr) and release the lock. set_doit then acquires ovpn->lock and calls ovpn_peer_hash_vpn_ip(), which re-inserts the now-removed peer back into the rehashing tables. The same race affects the float path: ovpn_peer_endpoints_update() holds only a refcount and acquires ovpn->lock very late (after async AEAD decrypt and a netlink notification), then rehashes the peer in the by_transp_addr table. The resurrected peer becomes reachable again from the RX lookup (ovpn_peer_get_by_transp_addr) and the TX VPN-IP lookup, even though userspace believes it is gone. Once the data-path refcount drops the peer is freed via call_rcu while the hash entries embedded in it remain linked, opening a UAF window. Bail out of the rehash when hash_entry_id is unhashed, mirroring the sentinel already used by ovpn_peer_remove() to detect the already-removed state. The check is safe under ovpn->lock, which serializes every mutation of hash_entry_id, and is a no-op for the add path because ovpn_peer_add_mp() inserts hash_entry_id before calling ovpn_peer_hash_vpn_ip(). Fixes: 1d36a36f6d53 ("ovpn: implement peer add/get/dump/delete via netlink") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 73 ++++++++++++++++++++++++----------------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a21d02ac715e..68021c0c1783 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -297,40 +297,46 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) /* rehashing is required only in MP mode as P2P has one peer * only and thus there is no hashtable */ - if (peer->ovpn->mode == OVPN_MODE_MP) { - spin_lock_bh(&peer->ovpn->lock); - spin_lock_bh(&peer->lock); - bind = rcu_dereference_protected(peer->bind, - lockdep_is_held(&peer->lock)); - if (unlikely(!bind)) { - spin_unlock_bh(&peer->lock); - spin_unlock_bh(&peer->ovpn->lock); - return; - } + if (peer->ovpn->mode != OVPN_MODE_MP) + return; - /* This function may be invoked concurrently, therefore another - * float may have happened in parallel: perform rehashing - * using the peer->bind->remote directly as key - */ + spin_lock_bh(&peer->ovpn->lock); + spin_lock_bh(&peer->lock); + bind = rcu_dereference_protected(peer->bind, + lockdep_is_held(&peer->lock)); + if (unlikely(!bind)) + goto unlock2; - switch (bind->remote.in4.sin_family) { - case AF_INET: - salen = sizeof(*sa); - break; - case AF_INET6: - salen = sizeof(*sa6); - break; - } + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (unlikely(hlist_unhashed(&peer->hash_entry_id))) + goto unlock2; - /* remove old hashing */ - hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); - /* re-add with new transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); - hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); - spin_unlock_bh(&peer->lock); - spin_unlock_bh(&peer->ovpn->lock); + /* This function may be invoked concurrently, therefore another + * float may have happened in parallel: perform rehashing + * using the peer->bind->remote directly as key + */ + + switch (bind->remote.in4.sin_family) { + case AF_INET: + salen = sizeof(*sa); + break; + case AF_INET6: + salen = sizeof(*sa6); + break; } + + /* remove old hashing */ + hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); + /* re-add with new transport address */ + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, + &bind->remote, salen); + hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); +unlock2: + spin_unlock_bh(&peer->lock); + spin_unlock_bh(&peer->ovpn->lock); return; unlock: spin_unlock_bh(&peer->lock); @@ -906,6 +912,13 @@ void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer) if (peer->ovpn->mode != OVPN_MODE_MP) return; + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (hlist_unhashed(&peer->hash_entry_id)) + return; + if (peer->vpn_addrs.ipv4.s_addr != htonl(INADDR_ANY)) { /* remove potential old hashing */ hlist_nulls_del_init_rcu(&peer->hash_entry_addr4); From cc12f7240c8c4dee557749d33237542613992f14 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:48 +0200 Subject: [PATCH 308/501] ovpn: rehash peer in by_transp_addr table on CMD_PEER_SET When userspace updates a peer's remote endpoint via OVPN_CMD_PEER_SET, ovpn_nl_peer_modify() installs a new ovpn_bind through ovpn_peer_reset_sockaddr(), but ovpn_nl_peer_set_doit() only calls ovpn_peer_hash_vpn_ip() to refresh the VPN-IP hashtables. The peer is left in the bucket of peers->by_transp_addr corresponding to its old remote address. As a consequence, datagrams arriving at the UDP RX path from the newly configured remote hash to a different slot and the lockless lookup in ovpn_peer_get_by_transp_addr() (called from ovpn_udp_encap_recv()) does not find the peer, until either a float event or a peer re-add fixes the bucket. Introduce ovpn_peer_hash_transp_addr() (modeled after ovpn_peer_hash_vpn_ip()) and invoke it from ovpn_nl_peer_set_doit() whenever the request carried a new remote address. The helper bails out in P2P mode and on peers without a bind (TCP), and relies on hlist_nulls_del_init_rcu()'s pprev==NULL short-circuit to handle the case of an entry not currently linked in the table. Fixes: 1d36a36f6d53 ("ovpn: implement peer add/get/dump/delete via netlink") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/netlink.c | 6 +++ drivers/net/ovpn/peer.c | 105 +++++++++++++++++++++++++------------ drivers/net/ovpn/peer.h | 1 + 3 files changed, 79 insertions(+), 33 deletions(-) diff --git a/drivers/net/ovpn/netlink.c b/drivers/net/ovpn/netlink.c index 4c66c1ec497e..4dad85294198 100644 --- a/drivers/net/ovpn/netlink.c +++ b/drivers/net/ovpn/netlink.c @@ -534,6 +534,12 @@ int ovpn_nl_peer_set_doit(struct sk_buff *skb, struct genl_info *info) */ if (ret > 0) ovpn_peer_hash_vpn_ip(peer); + /* if the remote endpoint was updated, the by_transp_addr hash bucket + * also needs to be refreshed, otherwise incoming packets from the new + * remote address would fail the lockless lookup + */ + if (attrs[OVPN_A_PEER_REMOTE_IPV4] || attrs[OVPN_A_PEER_REMOTE_IPV6]) + ovpn_peer_hash_transp_addr(peer); spin_unlock_bh(&ovpn->lock); ovpn_peer_put(peer); diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 68021c0c1783..a330892e82bf 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -189,6 +189,9 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, &(*__tbl1)[ovpn_get_hash_slot(*__tbl1, _key, _key_len)];\ }) +static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, + const struct ovpn_bind *bind); + /** * ovpn_peer_endpoints_update - update remote or local endpoint for peer * @peer: peer to update the remote endpoint for @@ -196,7 +199,6 @@ int ovpn_peer_reset_sockaddr(struct ovpn_peer *peer, */ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) { - struct hlist_nulls_head *nhead; struct sockaddr_storage ss; struct sockaddr_in6 *sa6; bool reset_cache = false; @@ -295,46 +297,23 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) ovpn_nl_peer_float_notify(peer, &ss); /* rehashing is required only in MP mode as P2P has one peer - * only and thus there is no hashtable + * only and thus there is no hashtable. + * + * This function may be invoked concurrently, so re-read peer->bind + * under the proper locks and rehash against its current value. */ if (peer->ovpn->mode != OVPN_MODE_MP) return; + /* This function may be invoked concurrently, therefore another + * float may have happened in parallel: re-acquire the locks and + * rehash using the peer->bind->remote directly as key + */ spin_lock_bh(&peer->ovpn->lock); spin_lock_bh(&peer->lock); bind = rcu_dereference_protected(peer->bind, lockdep_is_held(&peer->lock)); - if (unlikely(!bind)) - goto unlock2; - - /* peer may have been concurrently removed between the caller's - * initial lookup and our acquisition of ovpn->lock; skip the - * rehash so we don't re-insert a removed peer - */ - if (unlikely(hlist_unhashed(&peer->hash_entry_id))) - goto unlock2; - - /* This function may be invoked concurrently, therefore another - * float may have happened in parallel: perform rehashing - * using the peer->bind->remote directly as key - */ - - switch (bind->remote.in4.sin_family) { - case AF_INET: - salen = sizeof(*sa); - break; - case AF_INET6: - salen = sizeof(*sa6); - break; - } - - /* remove old hashing */ - hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); - /* re-add with new transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); - hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); -unlock2: + __ovpn_peer_hash_transp_addr(peer, bind); spin_unlock_bh(&peer->lock); spin_unlock_bh(&peer->ovpn->lock); return; @@ -902,6 +881,66 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, return match; } +/* Move @peer to the by_transp_addr bucket matching its current bind. + * + * Caller must hold both peer->ovpn->lock and peer->lock, and must have + * already dereferenced a valid (non-NULL) peer->bind, passed in as @bind. + */ +static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, + const struct ovpn_bind *bind) +{ + struct hlist_nulls_head *nhead; + size_t salen; + + lockdep_assert_held(&peer->ovpn->lock); + lockdep_assert_held(&peer->lock); + + if (WARN_ON_ONCE(!bind)) + return; + + /* peer may have been concurrently removed between the caller's + * initial lookup and our acquisition of ovpn->lock; skip the + * rehash so we don't re-insert a removed peer + */ + if (unlikely(hlist_unhashed(&peer->hash_entry_id))) + return; + + switch (bind->remote.in4.sin_family) { + case AF_INET: + salen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + salen = sizeof(struct sockaddr_in6); + break; + default: + return; + } + + /* remove old hashing (no-op if entry is not currently linked) */ + hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); + /* re-add with current transport address */ + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, + &bind->remote, salen); + hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); +} + +void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer) +{ + struct ovpn_bind *bind; + + lockdep_assert_held(&peer->ovpn->lock); + + /* rehashing makes sense only in multipeer mode */ + if (peer->ovpn->mode != OVPN_MODE_MP) + return; + + spin_lock_bh(&peer->lock); + bind = rcu_dereference_protected(peer->bind, + lockdep_is_held(&peer->lock)); + __ovpn_peer_hash_transp_addr(peer, bind); + spin_unlock_bh(&peer->lock); +} + void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer) { struct hlist_nulls_head *nhead; diff --git a/drivers/net/ovpn/peer.h b/drivers/net/ovpn/peer.h index 86c8cffada6d..dfa5c0037e02 100644 --- a/drivers/net/ovpn/peer.h +++ b/drivers/net/ovpn/peer.h @@ -150,6 +150,7 @@ struct ovpn_peer *ovpn_peer_get_by_id(struct ovpn_priv *ovpn, u32 peer_id); struct ovpn_peer *ovpn_peer_get_by_dst(struct ovpn_priv *ovpn, struct sk_buff *skb); void ovpn_peer_hash_vpn_ip(struct ovpn_peer *peer); +void ovpn_peer_hash_transp_addr(struct ovpn_peer *peer); bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, struct ovpn_peer *peer); From 59aed1eb60d70678a53acccb0cb337a26ce6680e Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:50 +0200 Subject: [PATCH 309/501] ovpn: ensure socket is owned by ovpn before deref sk_user_data Some subsystems, like BPF SOCKMAP, set sk_user_data without actually setting the encap_type. For this reason, we must make sure that the type is the one ovpn expects before dereferencing sk_user_data. Failing to do so may lead to out-of-bounds reads. Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/socket.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ovpn/socket.c b/drivers/net/ovpn/socket.c index 517caa64a4fe..6cbeb2caaeec 100644 --- a/drivers/net/ovpn/socket.c +++ b/drivers/net/ovpn/socket.c @@ -162,6 +162,15 @@ struct ovpn_socket *ovpn_socket_new(struct socket *sock, struct ovpn_peer *peer) rcu_read_lock(); ovpn_sock = rcu_dereference_sk_user_data(sk); if (ovpn_sock) { + /* something else filled the sk_user_data without + * setting the encap_type. Reject the socket. + */ + if (!type) { + ovpn_sock = ERR_PTR(-EBUSY); + rcu_read_unlock(); + goto sock_release; + } + /* socket owned by another ovpn instance, we can't use it */ if (ovpn_sock->ovpn != peer->ovpn) { ovpn_sock = ERR_PTR(-EBUSY); From 3f012bdbabe211ccbc0c50ea5a1dbc60f8af1532 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:51 +0200 Subject: [PATCH 310/501] ovpn: zero-initialize sockaddr before learning a floated endpoint ovpn_peer_endpoints_update() builds the new remote endpoint in an on-stack struct sockaddr_storage that is left uninitialized. For IPv4 only sin_family/sin_addr/sin_port are written, leaving the 8-byte sin_zero padding as stack garbage (for IPv6, sin6_flowinfo is left uninitialized likewise). ovpn_peer_reset_sockaddr() -> ovpn_bind_from_sockaddr() then memcpy()s sizeof(struct sockaddr_in)/sizeof(struct sockaddr_in6) bytes - padding included - into bind->remote. That buffer is later hashed with jhash() over the same length to place the peer in the by_transp_addr table, so the garbage padding lands the floated peer in an essentially random bucket. Lockless lookups in ovpn_peer_get_by_transp_addr() build their key from a zero-initialized sockaddr_storage, compute a different bucket and fail to find the peer. This is also a plain use of uninitialized stack memory in jhash(). Build the floated endpoint with a designated initializer so the padding (sin_zero for IPv4, sin6_flowinfo for IPv6) is zeroed as part of the assignment. This keeps the padding out of the by_transp_addr hash key without memset-ing the whole sockaddr_storage on every received packet. Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP endpoint") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index a330892e82bf..33fb0a75e600 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -222,9 +222,16 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) */ local_ip = &ip_hdr(skb)->daddr; sa = (struct sockaddr_in *)&ss; - sa->sin_family = AF_INET; - sa->sin_addr.s_addr = ip_hdr(skb)->saddr; - sa->sin_port = udp_hdr(skb)->source; + /* use a designated initializer so the sin_zero padding + * is zeroed (it ends up in the by_transp_addr hash key) + * without memset-ing the whole sockaddr_storage on the + * RX fast path + */ + *sa = (struct sockaddr_in) { + .sin_family = AF_INET, + .sin_addr.s_addr = ip_hdr(skb)->saddr, + .sin_port = udp_hdr(skb)->source, + }; salen = sizeof(*sa); reset_cache = true; break; @@ -250,11 +257,19 @@ void ovpn_peer_endpoints_update(struct ovpn_peer *peer, struct sk_buff *skb) */ local_ip = &ipv6_hdr(skb)->daddr; sa6 = (struct sockaddr_in6 *)&ss; - sa6->sin6_family = AF_INET6; - sa6->sin6_addr = ipv6_hdr(skb)->saddr; - sa6->sin6_port = udp_hdr(skb)->source; - sa6->sin6_scope_id = ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, - skb->skb_iif); + /* use a designated initializer so the sin6_flowinfo + * padding is zeroed (it ends up in the by_transp_addr + * hash key) without memset-ing the whole + * sockaddr_storage on the RX fast path + */ + *sa6 = (struct sockaddr_in6) { + .sin6_family = AF_INET6, + .sin6_addr = ipv6_hdr(skb)->saddr, + .sin6_port = udp_hdr(skb)->source, + .sin6_scope_id = + ipv6_iface_scope_id(&ipv6_hdr(skb)->saddr, + skb->skb_iif), + }; salen = sizeof(*sa6); reset_cache = true; break; From b47a52dcd598a50207a33df304acdf45348a690f Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:52 +0200 Subject: [PATCH 311/501] ovpn: hash floated peer by transport identity only The by_transp_addr table is keyed on the peer's remote transport address, but the float rehash hashed bind->remote directly, while the two other sites that touch the table build a clean key first: ovpn_peer_add_mp() and the lookup in ovpn_peer_get_by_transp_addr() both hash a sockaddr holding only family/address/port. For a link-local IPv6 peer, bind->remote carries sin6_scope_id (set from ipv6_iface_scope_id() when the endpoint is learned), and that field is folded into the jhash() over sizeof(struct sockaddr_in6). The lookup never sets sin6_scope_id, so after such a peer floats it is rehashed into a scope_id-dependent bucket that lookups (scope_id 0) never visit, making the peer unreachable through the by_transp_addr fallback. ovpn_peer_transp_match() only compares address and port, so the hash was keying on a field the match ignores. sin6_scope_id must stay in bind->remote because the TX path uses it as flowi6_oif, so it cannot just be cleared there. Instead build the hash key from family/address/port only, exactly like ovpn_peer_add_mp() and the lookup, so all three sites agree on the bucket. Fixes: f0281c1d3732 ("ovpn: add support for updating local or remote UDP endpoint") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index 33fb0a75e600..eada414a9d92 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -904,7 +904,10 @@ bool ovpn_peer_check_by_src(struct ovpn_priv *ovpn, struct sk_buff *skb, static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, const struct ovpn_bind *bind) { + struct sockaddr_storage sa = {}; struct hlist_nulls_head *nhead; + struct sockaddr_in6 *sa6; + struct sockaddr_in *sa4; size_t salen; lockdep_assert_held(&peer->ovpn->lock); @@ -920,12 +923,26 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, if (unlikely(hlist_unhashed(&peer->hash_entry_id))) return; + /* Build the hash key from the transport identity only + * (family/address/port), matching ovpn_peer_add_mp() and the lookup + * in ovpn_peer_get_by_transp_addr(). Hashing bind->remote directly + * would fold in sin6_scope_id (set on the float path but never by the + * lookup), scattering the peer into a bucket lookups cannot reach. + */ switch (bind->remote.in4.sin_family) { case AF_INET: - salen = sizeof(struct sockaddr_in); + sa4 = (struct sockaddr_in *)&sa; + sa4->sin_family = AF_INET; + sa4->sin_addr.s_addr = bind->remote.in4.sin_addr.s_addr; + sa4->sin_port = bind->remote.in4.sin_port; + salen = sizeof(*sa4); break; case AF_INET6: - salen = sizeof(struct sockaddr_in6); + sa6 = (struct sockaddr_in6 *)&sa; + sa6->sin6_family = AF_INET6; + sa6->sin6_addr = bind->remote.in6.sin6_addr; + sa6->sin6_port = bind->remote.in6.sin6_port; + salen = sizeof(*sa6); break; default: return; @@ -934,8 +951,8 @@ static void __ovpn_peer_hash_transp_addr(struct ovpn_peer *peer, /* remove old hashing (no-op if entry is not currently linked) */ hlist_nulls_del_init_rcu(&peer->hash_entry_transp_addr); /* re-add with current transport address */ - nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, - &bind->remote, salen); + nhead = ovpn_get_hash_head(peer->ovpn->peers->by_transp_addr, &sa, + salen); hlist_nulls_add_head_rcu(&peer->hash_entry_transp_addr, nhead); } From 0301aa324941698bec3dd455df1c5abc7afb10db Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:53 +0200 Subject: [PATCH 312/501] ovpn: disable IPv4 redirects on MP interfaces ovpn_mp_alloc() tried to disable SEND_REDIRECTS on a multipeer interface, but it runs from ovpn_net_init() (->ndo_init), which register_netdevice() invokes before the NETDEV_REGISTER notifier chain. The IPv4 in_device is only created when that notifier reaches inetdev_event() -> inetdev_init(), so __in_dev_get_rtnl() always returned NULL at ndo_init time and the whole redirect-disabling block (both the per-device and the per-netns IPV4_DEVCONF_ALL write) was dead. MP interfaces therefore kept emitting ICMP redirects. Disabling redirects only once is not enough either: the IPv4 in_device is destroyed and recreated when the interface is moved to a different network namespace (NETDEV_UNREGISTER/NETDEV_REGISTER), and the newly created in_device inherits the destination namespace defaults, silently re-enabling SEND_REDIRECTS. Disable redirects from ovpn_net_open() (->ndo_open) instead: it runs every time the interface is brought up, including after the in_device has been recreated, so the setting is always re-applied. This mirrors what wireguard does in wg_open(). RTNL is held on the ndo_open() path, so __in_dev_get_rtnl() is safe. Fixes: 05003b408c20 ("ovpn: implement multi-peer support") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 50 ++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 9d9a0ff690d6..3a04757d5c31 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -35,25 +35,11 @@ static void ovpn_priv_free(struct net_device *net) static int ovpn_mp_alloc(struct ovpn_priv *ovpn) { - struct in_device *dev_v4; int i; if (ovpn->mode != OVPN_MODE_MP) return 0; - dev_v4 = __in_dev_get_rtnl(ovpn->dev); - if (dev_v4) { - /* disable redirects as Linux gets confused by ovpn - * handling same-LAN routing. - * This happens because a multipeer interface is used as - * relay point between hosts in the same subnet, while - * in a classic LAN this would not be needed because the - * two hosts would be able to talk directly. - */ - IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); - IPV4_DEVCONF_ALL(dev_net(ovpn->dev), SEND_REDIRECTS) = false; - } - /* the peer container is fairly large, therefore we allocate it only in * MP mode */ @@ -97,9 +83,38 @@ static void ovpn_net_uninit(struct net_device *dev) gro_cells_destroy(&ovpn->gro_cells); } +static int ovpn_net_open(struct net_device *dev) +{ + struct ovpn_priv *ovpn = netdev_priv(dev); + struct in_device *dev_v4; + + /* the IPv4 in_device (and thus its config) is recreated whenever the + * interface is moved to a new netns, so redirects must be disabled on + * every bring-up rather than once at creation time, otherwise the + * setting is silently lost after such a move + */ + if (ovpn->mode == OVPN_MODE_MP) { + dev_v4 = __in_dev_get_rtnl(dev); + if (dev_v4) { + /* disable redirects as Linux gets confused by ovpn + * handling same-LAN routing. + * This happens because a multipeer interface is used as + * relay point between hosts in the same subnet, while + * in a classic LAN this would not be needed because the + * two hosts would be able to talk directly. + */ + IN_DEV_CONF_SET(dev_v4, SEND_REDIRECTS, false); + IPV4_DEVCONF_ALL(dev_net(dev), SEND_REDIRECTS) = false; + } + } + + return 0; +} + static const struct net_device_ops ovpn_netdev_ops = { .ndo_init = ovpn_net_init, .ndo_uninit = ovpn_net_uninit, + .ndo_open = ovpn_net_open, .ndo_start_xmit = ovpn_net_xmit, }; @@ -183,6 +198,7 @@ static int ovpn_newlink(struct net_device *dev, struct ovpn_priv *ovpn = netdev_priv(dev); struct nlattr **data = params->data; enum ovpn_mode mode = OVPN_MODE_P2P; + int ret; if (data && data[IFLA_OVPN_MODE]) { mode = nla_get_u8(data[IFLA_OVPN_MODE]); @@ -207,7 +223,11 @@ static int ovpn_newlink(struct net_device *dev, else netif_carrier_off(dev); - return register_netdevice(dev); + ret = register_netdevice(dev); + if (ret < 0) + return ret; + + return 0; } static size_t ovpn_get_size(const struct net_device *dev) From 4680c0ebd958fc18e53c8b91d80436b236a8fc09 Mon Sep 17 00:00:00 2001 From: Antonio Quartulli Date: Tue, 28 Jul 2026 13:48:54 +0200 Subject: [PATCH 313/501] ovpn: ensure TCP vars are initialized first Netlink calls may access TCP global vars (i.e. when attaching a TCP socket), therefore we need to make sure the latters are initialized beforehand. For this reason move the global TCP initialization at the top of the module init function. Fixes: 11851cbd60ea ("ovpn: implement TCP transport") Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/main.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/net/ovpn/main.c b/drivers/net/ovpn/main.c index 3a04757d5c31..168cfe9b59a9 100644 --- a/drivers/net/ovpn/main.c +++ b/drivers/net/ovpn/main.c @@ -260,8 +260,11 @@ static struct rtnl_link_ops ovpn_link_ops = { static int __init ovpn_init(void) { - int err = rtnl_link_register(&ovpn_link_ops); + int err; + ovpn_tcp_init(); + + err = rtnl_link_register(&ovpn_link_ops); if (err) { pr_err("ovpn: can't register rtnl link ops: %d\n", err); return err; @@ -273,8 +276,6 @@ static int __init ovpn_init(void) goto unreg_rtnl; } - ovpn_tcp_init(); - return 0; unreg_rtnl: From 26ba17d845193dac4921ae1ab280d28d1938052e Mon Sep 17 00:00:00 2001 From: Qingfang Deng Date: Fri, 24 Jul 2026 09:49:26 +0800 Subject: [PATCH 314/501] ovpn: fix incorrect use of rcu_access_pointer() rcu_access_pointer() should only be used to test the value of a pointer, not to dereference it. As it's in a spin_lock_bh() critical section, use rcu_dereference_bh() instead, avoiding an extra rcu_read_lock(). Fixes: f6226ae7a0cd ("ovpn: introduce the ovpn_socket object") Signed-off-by: Qingfang Deng Signed-off-by: Antonio Quartulli --- drivers/net/ovpn/peer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ovpn/peer.c b/drivers/net/ovpn/peer.c index eada414a9d92..b0519f9840d8 100644 --- a/drivers/net/ovpn/peer.c +++ b/drivers/net/ovpn/peer.c @@ -1249,7 +1249,7 @@ static void ovpn_peer_release_p2p(struct ovpn_priv *ovpn, struct sock *sk, } if (sk) { - ovpn_sock = rcu_access_pointer(peer->sock); + ovpn_sock = rcu_dereference_bh(peer->sock); if (!ovpn_sock || ovpn_sock->sk != sk) { spin_unlock_bh(&ovpn->lock); return; From 1842bf97af109f5ebf830175c9725bf81ebb78b1 Mon Sep 17 00:00:00 2001 From: Gabriele Monaco Date: Fri, 22 May 2026 14:58:33 +0200 Subject: [PATCH 315/501] sched/deadline: Use revised wakeup rule only for running dl_server Commit 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") applies the revised wakeup rule to any server, as a result servers that are not running (dl_defer_running == 0) and start with a deadline overflow get enqueued and can boost tasks as if they were running, invalidating the defer rule and the documented state model. Apply the revised wakeup rule only for deferrable servers that are marked as running. Fixes: 14a857056466 ("sched/deadline: Use revised wakeup rule for dl_server") Signed-off-by: Gabriele Monaco Signed-off-by: Peter Zijlstra (Intel) Acked-by: Juri Lelli Tested-by: Andrea Righi Link: https://patch.msgid.link/20260522125833.264145-1-gmonaco@redhat.com --- kernel/sched/deadline.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c index 0f858b98c9aa..200300043fa5 100644 --- a/kernel/sched/deadline.c +++ b/kernel/sched/deadline.c @@ -1017,7 +1017,8 @@ 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) || dl_se->dl_defer) && + if (unlikely((!dl_is_implicit(dl_se) || + (dl_se->dl_defer && dl_se->dl_defer_running)) && !dl_time_before(dl_se->deadline, rq_clock(rq)) && !is_dl_boosted(dl_se))) { update_dl_revised_wakeup(dl_se, rq); From 9c19d60fea9f46ed0c3394653ef4941f1a459968 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:08 +0800 Subject: [PATCH 316/501] spi: spi-nxp-fspi: add per-SoC SDR/DTR clock rate limits for all supported SoCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The commit f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") introduced a global 166MHz cap for DTR mode (RXCLKSRC=3), based on the i.MX8MN datasheet timing specification (Section 3.9.9, page 65). After reviewing the FlexSPI timing parameters in the datasheets for all supported SoCs, the following corrections and additions are needed: 1. SDR mode (RXCLKSRC=0) limits vary per SoC: - i.MX8MN/MM/MP/95: 66MHz (IMX8MNCEC §3.9.9, IMX8MMCEC §3.9.10, IMX8MPCEC, IMX95CEC Rev.8 §4.11.7) - i.MX8QXP/QM/DXL/ULP: 60MHz (IMX8QXPCEC, IMX8QMCEC, IMX8DXLCEC, IMX8ULPCEC §7.3.1 ND mode) - LX2160A: 100MHz (LX2160ACEC FlexSPI timing parameters) 2. DTR mode (RXCLKSRC=3) limits vary per SoC: - i.MX8MN/MM/MP/ULP: 166MHz - i.MX8QXP/QM/DXL: 200MHz (same FlexSPI IP across this family) - i.MX95: 200MHz (IMX95CEC §4.11.7.3.2.3 Table 106) - LX2160A: DTR disabled (FSPI_QUIRK_DISABLE_DTR) Update related platform data with correct speed limation according to datasheet. Fixes: f43579ef3500 ("spi: spi-nxp-fspi: limit the clock rate for different sample clock source selection") Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260728-fspi-clock-v2-1-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 83 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 1e36ae084dd8..39c1eaaf9e0a 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -340,6 +340,18 @@ struct nxp_fspi_devtype_data { unsigned int quirks; unsigned int lut_num; bool little_endian; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in SDR mode (RXCLKSRC=0). Defaults to 66MHz if zero. + * Some SoCs (e.g. LX2160A) support up to 100MHz in SDR mode. + */ + unsigned long max_sdr_rate; + /* + * The max clock rate (Hz) that FlexSPI can output to the device + * in DTR mode (RXCLKSRC=3). Defaults to 166MHz if zero. + * Some SoCs (e.g. i.MX95, i.MX8QM, i.MX8DXL) support up to 200MHz. + */ + unsigned long max_dtr_rate; }; static struct nxp_fspi_devtype_data lx2160a_data = { @@ -349,6 +361,10 @@ static struct nxp_fspi_devtype_data lx2160a_data = { .quirks = FSPI_QUIRK_DISABLE_DTR, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * LX2160ACEC: SDR RXCLKSRC=0 max 100MHz, DTR disabled via quirk. + */ + .max_sdr_rate = 100000000, }; static struct nxp_fspi_devtype_data imx8mm_data = { @@ -358,6 +374,21 @@ static struct nxp_fspi_devtype_data imx8mm_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* IMX8MMCEC §3.9.10: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx8mp_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* IMX8MPCEC: SDR RXCLKSRC=0 max 66MHz, DDR RXCLKSRC=3 max 166MHz */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 166000000, }; static struct nxp_fspi_devtype_data imx8qxp_data = { @@ -367,6 +398,12 @@ static struct nxp_fspi_devtype_data imx8qxp_data = { .quirks = 0, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8QXPCEC: SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 200MHz. + * i.MX8QM and i.MX8DXL share the same FlexSPI IP and limits. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8dxl_data = { @@ -376,6 +413,12 @@ static struct nxp_fspi_devtype_data imx8dxl_data = { .quirks = FSPI_QUIRK_USE_IP_ONLY, .lut_num = 32, .little_endian = true, /* little-endian */ + /* + * IMX8DXLCEC (i.MX 8XLite): SDR RXCLKSRC=0 max 60MHz, + * DDR RXCLKSRC=3 max 200MHz. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 200000000, }; static struct nxp_fspi_devtype_data imx8ulp_data = { @@ -385,6 +428,29 @@ static struct nxp_fspi_devtype_data imx8ulp_data = { .quirks = 0, .lut_num = 16, .little_endian = true, /* little-endian */ + /* + * IMX8ULPCEC §7.3.1, Normal Drive (ND, 1.0V) mode: + * SDR RXCLKSRC=0 max 60MHz, DDR RXCLKSRC=3 max 166MHz. + * Note: Overdrive (OD, 1.05V) allows up to 180MHz DTR + * but is not the default use case. + */ + .max_sdr_rate = 60000000, + .max_dtr_rate = 166000000, +}; + +static struct nxp_fspi_devtype_data imx95_data = { + .rxfifo = SZ_512, /* (64 * 64 bits) */ + .txfifo = SZ_1K, /* (128 * 64 bits) */ + .ahb_buf_size = SZ_2K, /* (256 * 64 bits) */ + .quirks = 0, + .lut_num = 32, + .little_endian = true, /* little-endian */ + /* + * IMX95CEC Rev.8 §4.11.7: SDR RXCLKSRC=0 max 66MHz, + * DDR RXCLKSRC=3 max 200MHz (Nominal/Overdrive mode). + */ + .max_sdr_rate = 66000000, + .max_dtr_rate = 200000000, }; struct nxp_fspi { @@ -691,10 +757,20 @@ static void nxp_fspi_select_rx_sample_clk_source(struct nxp_fspi *f, reg = fspi_readl(f, f->iobase + FSPI_MCR0); if (op_is_dtr) { reg |= FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 166000000; + /* + * Use the SoC-specific DTR max rate if provided, otherwise + * fall back to 166MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_dtr_rate ? + f->devtype_data->max_dtr_rate : 166000000; } else { /*select mode 0 */ reg &= ~FSPI_MCR0_RXCLKSRC(3); - f->max_rate = 66000000; + /* + * Use the SoC-specific SDR max rate if provided, otherwise + * fall back to 66MHz (limit from IMX8MN datasheet §3.9.9). + */ + f->max_rate = f->devtype_data->max_sdr_rate ? + f->devtype_data->max_sdr_rate : 66000000; } fspi_writel(f, reg, f->iobase + FSPI_MCR0); } @@ -1444,10 +1520,11 @@ static const struct dev_pm_ops nxp_fspi_pm_ops = { static const struct of_device_id nxp_fspi_dt_ids[] = { { .compatible = "nxp,lx2160a-fspi", .data = (void *)&lx2160a_data, }, { .compatible = "nxp,imx8mm-fspi", .data = (void *)&imx8mm_data, }, - { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mm_data, }, + { .compatible = "nxp,imx8mp-fspi", .data = (void *)&imx8mp_data, }, { .compatible = "nxp,imx8qxp-fspi", .data = (void *)&imx8qxp_data, }, { .compatible = "nxp,imx8dxl-fspi", .data = (void *)&imx8dxl_data, }, { .compatible = "nxp,imx8ulp-fspi", .data = (void *)&imx8ulp_data, }, + { .compatible = "nxp,imx95-fspi", .data = (void *)&imx95_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, nxp_fspi_dt_ids); From b4bde5cfff8e43e948219f0a598e4bf057ecfba4 Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:09 +0800 Subject: [PATCH 317/501] spi: spi-nxp-fspi: enter stop mode before reconfiguring MCR0 and DLL In nxp_fspi_select_mem() the RX sample clock source (MCR0[RXCLKSRC]) and the DLL control registers (DLLxCR) are reconfigured while the FlexSPI module is still enabled. According to the FlexSPI reference manual initialization sequence, MCR0 and the DLL control registers should be programmed while the module is in stop mode, i.e. with MCR0[MDIS] set to 1, and the module re-enabled (MCR0[MDIS] = 0) afterwards. Wrap the RX sample clock source selection and the DLL calibration/ override reconfiguration in a stop-mode window to align with the RM and avoid reconfiguring timing-critical registers while the module is active. Signed-off-by: Haibo Chen Reviewed-by: Frank Li Link: https://patch.msgid.link/20260728-fspi-clock-v2-2-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 39c1eaaf9e0a..54355295f919 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -867,6 +867,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, unsigned long rate = op->max_freq; int ret; uint64_t size_kb; + u32 reg; /* * Return when following condition all meet, @@ -896,6 +897,15 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, dev_dbg(f->dev, "Target device [CS:%x] selected\n", spi_get_chipselect(spi, 0)); + /* + * Per the FlexSPI reference manual (initialization sequence), MCR0 and + * the DLL control registers should be configured while the module is in + * stop mode (MCR0[MDIS] = 1). Enter stop mode before reconfiguring the + * RX sample clock source and the DLL, then exit stop mode afterwards. + */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg | FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + nxp_fspi_select_rx_sample_clk_source(f, op_is_dtr); rate = min(f->max_rate, op->max_freq); @@ -928,6 +938,10 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, else nxp_fspi_dll_override(f); + /* Exit stop mode now that MCR0 and the DLL have been reconfigured. */ + reg = fspi_readl(f, f->iobase + FSPI_MCR0); + fspi_writel(f, reg & ~FSPI_MCR0_MDIS, f->iobase + FSPI_MCR0); + f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); From b5902b9779796d515b7d65eb9205994b7a8d00cb Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 28 Jul 2026 18:18:10 +0800 Subject: [PATCH 318/501] spi: spi-nxp-fspi: propagate clock reconfig failures in nxp_fspi_select_mem() nxp_fspi_select_mem() disables the FlexSPI clocks before calling clk_set_rate() and re-enabling them. If clk_set_rate() or the clock re-enable fails, the function returned early (as void) leaving both the serial root clock and the register interface clock disabled. As the function returned void, nxp_fspi_exec_op() had no way to know about the failure and continued to access FlexSPI registers (LUT setup, data transfer, AHB buffer invalidation). Accessing the controller registers while its clock is gated off results in a synchronous external abort. Make nxp_fspi_select_mem() return an error code and have nxp_fspi_exec_op() bail out on failure before any further register access, including nxp_fspi_invalid(). Signed-off-by: Haibo Chen Link: https://patch.msgid.link/20260728-fspi-clock-v2-3-dbe786a4a6eb@nxp.com Signed-off-by: Mark Brown --- drivers/spi/spi-nxp-fspi.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/spi/spi-nxp-fspi.c b/drivers/spi/spi-nxp-fspi.c index 54355295f919..4425132432ec 100644 --- a/drivers/spi/spi-nxp-fspi.c +++ b/drivers/spi/spi-nxp-fspi.c @@ -859,8 +859,8 @@ static void nxp_fspi_dll_override(struct nxp_fspi *f) * Value for rest of the CS FLSHxxCR0 register would be zero. * */ -static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, - const struct spi_mem_op *op) +static int nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, + const struct spi_mem_op *op) { /* flexspi only support one DTR mode: 8D-8D-8D */ bool op_is_dtr = op->cmd.dtr && op->addr.dtr && op->dummy.dtr && op->data.dtr; @@ -881,7 +881,7 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, if ((f->selected == spi_get_chipselect(spi, 0)) && (!!(f->flags & FSPI_DTR_MODE) == op_is_dtr) && (f->pre_op_rate == op->max_freq)) - return; + return 0; /* Reset FLSHxxCR0 registers */ fspi_writel(f, 0, f->iobase + FSPI_FLSHA1CR0); @@ -922,12 +922,19 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, nxp_fspi_clk_disable_unprep(f); ret = clk_set_rate(f->clk, rate); - if (ret) - return; + if (ret) { + /* + * clk_set_rate() failed with the clocks already disabled. + * Re-enable them so the enable count matches what the caller's + * pm_runtime_put() (runtime_suspend) will drop. + */ + nxp_fspi_clk_prep_enable(f); + return ret; + } ret = nxp_fspi_clk_prep_enable(f); if (ret) - return; + return ret; /* * If clock rate > 100MHz, then switch from DLL override mode to @@ -945,6 +952,8 @@ static void nxp_fspi_select_mem(struct nxp_fspi *f, struct spi_device *spi, f->pre_op_rate = op->max_freq; f->selected = spi_get_chipselect(spi, 0); + + return 0; } static int nxp_fspi_read_ahb(struct nxp_fspi *f, const struct spi_mem_op *op) @@ -1132,7 +1141,16 @@ static int nxp_fspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op) FSPI_STS0_ARB_IDLE, 1, POLL_TOUT, true); WARN_ON(err); - nxp_fspi_select_mem(f, mem->spi, op); + err = nxp_fspi_select_mem(f, mem->spi, op); + if (err) { + /* + * On failure the FlexSPI clock may be left disabled, so avoid + * any further register access (which would trigger a synchronous + * external abort) and bail out. + */ + pm_runtime_put_autosuspend(f->dev); + return err; + } nxp_fspi_prepare_lut(f, op); /* From c77ffbc980efb337fd750c337d8157d532ea14e5 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 26 Jul 2026 08:12:38 -0600 Subject: [PATCH 319/501] io_uring/net: initialize mshot_len for send Commit: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") changed how io_mshot_prep_retry() set sr->len, and added the same initialization in io_mshot_prep_retry(). But it neglected to touch the send path, which may also uses the mshot retry path. Ensure that sr->mshot_len always gets initialized correctly. Fixes: 6a8afb9fff64 ("io_uring/net: allow multishot receive per-invocation cap") Cc: stable@vger.kernel.org Reported-by: Sung Keum Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe --- io_uring/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/io_uring/net.c b/io_uring/net.c index 00a7df803b99..a74d15f7b7d2 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -445,6 +445,7 @@ int io_sendmsg_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe) req->flags |= REQ_F_NOWAIT; if (req->flags & REQ_F_BUFFER_SELECT) sr->buf_group = req->buf_index; + sr->mshot_total_len = sr->mshot_len = 0; if (sr->flags & IORING_RECVSEND_BUNDLE) { if (req->opcode == IORING_OP_SENDMSG) return -EINVAL; From 0b88f470589960591f1cb3f238124939d0a7331d Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sun, 26 Jul 2026 08:13:09 -0600 Subject: [PATCH 320/501] io_uring/kbuf: cap buffer selection length at MAX_RW_COUNT io_ring_buffers_peek() builds an iovec array from provided buffers, and that in turn can be handed off to a lower level provider. Be prudent and cap the total size to MAX_RW_COUNT, which is the Linux default for how much IO do to in a single call. No bugs here, but it's a good preventative measure to avoid truncation issues. Reviewed-by: Gabriel Krisman Bertazi Signed-off-by: Jens Axboe --- io_uring/kbuf.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/io_uring/kbuf.c b/io_uring/kbuf.c index de0129bceaba..1cf5be62bb65 100644 --- a/io_uring/kbuf.c +++ b/io_uring/kbuf.c @@ -266,6 +266,9 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, if (unlikely(!nr_avail)) return -ENOBUFS; + /* MAX_RW_COUNT is the universal Linux per-call IO maximum */ + arg->max_len = min_t(size_t, arg->max_len, MAX_RW_COUNT); + buf = io_ring_head_to_buf(br, head, bl->mask); if (arg->max_len) { u32 len = READ_ONCE(buf->len); @@ -295,7 +298,7 @@ static int io_ring_buffers_peek(struct io_kiocb *req, struct buf_sel_arg *arg, /* set it to max, if not set, so we can use it unconditionally */ if (!arg->max_len) - arg->max_len = INT_MAX; + arg->max_len = MAX_RW_COUNT; req->buf_index = READ_ONCE(buf->bid); do { From 9db03d2116ab9e19051641d75c8270c84a9311ea Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Thu, 30 Jul 2026 10:33:14 -0600 Subject: [PATCH 321/501] Revert "io_uring/epoll: disallow adding an epoll file to an epoll context" Turns out that my naive assumption that surely nobody is using io_uring to manage nested epoll instances was wrong - libuv is in fact doing that very thing. Since we have users in the wild using that feature, we cannot remove it from upstream. Revert the commit that introduced that limitation. This reverts commit cfa1539b24aff18ecb71c6334e7270f810d145bb. Link: https://lore.kernel.org/io-uring/605939add4dfb674bd7ab39ef8dbd3fd22e754e4.camel@xry111.site/ Reported-by: Xi Ruoyao Signed-off-by: Jens Axboe --- io_uring/epoll.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/io_uring/epoll.c b/io_uring/epoll.c index eecd748cad01..b9db8bde27ec 100644 --- a/io_uring/epoll.c +++ b/io_uring/epoll.c @@ -62,9 +62,6 @@ int io_epoll_ctl(struct io_kiocb *req, unsigned int issue_flags) CLASS(fd, tf)(ie->fd); if (fd_empty(tf)) return -EBADF; - /* disallow adding an epoll context to another epoll context */ - if (ie->op == EPOLL_CTL_ADD && is_file_epoll(fd_file(tf))) - return -EINVAL; key.file = fd_file(tf); key.fd = ie->fd; From fa1ac3b9eb62918f039276d4d11cc02f682bf93c Mon Sep 17 00:00:00 2001 From: Woraphat Khiaodaeng Date: Thu, 30 Jul 2026 16:27:41 +0000 Subject: [PATCH 322/501] io_uring/zcrx: don't clear master_ctx from the import path import_zcrx() attaches an existing ifq to another ring. It never calls zcrx_set_ring_ctx() and so never takes the ->master_ctx reference, but its error path still passes @ctx to zcrx_unregister(), which clears ->master_ctx and drops its percpu_ref whenever ifq->master_ctx == ctx. That condition is reachable. A ring that registers an ifq with a non-zero event type_mask gets ->master_ctx pointed at itself, and nothing stops it from exporting that ifq with ZCRX_CTRL_EXPORT and importing the resulting fd back into the same ring. Failing the import after the refcount bumps -- an argument page mapped PROT_READ makes the copy_to_user() in import_zcrx() return -EFAULT -- then clears the ->master_ctx owned by the original registration, which is still live. Refcounts stay balanced and nothing is freed early, so there is no splat. The ring silently stops receiving ZCRX_EVENT_ALLOC_FAIL and ZCRX_EVENT_COPY: zcrx_send_notif() returns early on a NULL ->master_ctx, and ->master_ctx is only ever set on a freshly allocated ifq, so it cannot be restored without tearing the ring down. Pass NULL instead, matching zcrx_box_release() and the zcrx_export() error path. io_register_zcrx() only gets away with passing @ctx because zcrx_set_ring_ctx() runs after its last goto err. Fixes: 00d91481279f ("io_uring/zcrx: share an ifq between rings") Signed-off-by: Woraphat Khiaodaeng Link: https://patch.msgid.link/20260730162741.1125-1-worapat.kd2@gmail.com Reviewed-by: Pavel Begunkov [axboe: add pavel edit] Signed-off-by: Jens Axboe --- io_uring/zcrx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index 76b9b0d54af9..f1464ea8ca64 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -808,7 +808,8 @@ static int import_zcrx(struct io_ring_ctx *ctx, scoped_guard(mutex, &ctx->mmap_lock) xa_erase(&ctx->zcrx_ctxs, id); err: - zcrx_unregister(ifq, ctx); + /* the import path never set the ->master_ctx ref, don't drop it */ + zcrx_unregister(ifq, NULL); return ret; } From 1afb8eaeec44fd011f2b93ccd9fd426d753d963b Mon Sep 17 00:00:00 2001 From: Jerome Tollet Date: Wed, 20 May 2026 07:55:44 +0530 Subject: [PATCH 323/501] drm/i915/hdmi: Poll for 200 msec for TMDS_Scrambler_Status HDMI 2.0 section 6.1.3.1 specifies that after enabling Scrambling_Enable and starting scrambled video transmission, the source should poll Scrambling_Status until it reads 1 or until a timeout of 200 ms expires. Add a polling step after enabling the HDMI port to check the scrambling status when HDMI scrambling is enabled. On some HDMI 2.0 sinks, omitting this check can result in 4K@60Hz (594 MHz) failing to come up correctly because the sink has not yet finished its scrambling setup. In practice, waiting for the scrambling status here fixes such sinks. While this synchronous polling is not itself explicitly required for correct modeset sequencing, HDMI 2.0 section 6.1.3.1 does recommend it as the way for the source to verify that the TMDS link is functioning correctly with scrambling enabled. v3: - Add explicit HDMI 2.0 section reference in code comment - Clarify commit message around the observed sink fix v2: - Poll TMDS_Scrambler_Status for up to 200 ms instead of using a fixed delay Reported-by: Jerome Tollet Closes: https://gitlab.freedesktop.org/drm/xe/kernel/-/issues/6868 Link: https://lore.kernel.org/dri-devel/20251230091037.5603-1-jerome.tollet@gmail.com/ Signed-off-by: Jerome Tollet Signed-off-by: Ankit Nautiyal Reviewed-by: Arun R Murthy Link: https://patch.msgid.link/20260520022544.3097252-1-ankit.k.nautiyal@intel.com (cherry picked from commit b7d51d65e4f12a48392d260613108ec262bc7774) Fixes: 15953637886d ("drm/i915: enable scrambling") Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 ++ drivers/gpu/drm/i915/display/intel_hdmi.c | 26 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdmi.h | 2 ++ 3 files changed, 30 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6296635c4e79..95008e0c0eb8 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9076c2b176ec..b9d11fb8559d 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2728,6 +2728,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ drm_connector_attach_max_bpc_property(&connector->base, 8, 12); } +/* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + /* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index be2fad57e4ad..0fa3661568e8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ From d14b5d0e97fccd27974fedc03b903408872907fd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 27 Jul 2026 20:30:58 -0500 Subject: [PATCH 324/501] selinux: reject a permission value exceeding the class permission count perm_read() bounds a permission value by SEL_VEC_MAX but never by the nprim of the owning class or common, which is taken verbatim from the policy image. security_get_permissions() then writes perms[value - 1] into an nprim-sized kcalloc() array, so a class declaring fewer permissions than its largest permission value drives an out-of-bounds heap write. The top-level symbol tables are validated this way; the nested per-class permission table is not. Reject a permission whose value exceeds nprim, which is already set when perm_read() runs. Well-formed policies are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley [PM: tweak comment for line length] Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index ead504a639e3..5ff4d095ec2e 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1175,6 +1175,9 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f rc = -EINVAL; if (perdatum->value < 1 || perdatum->value > SEL_VEC_MAX) goto bad; + /* indexes an nprim-sized array in security_get_permissions() */ + if (perdatum->value > s->nprim) + goto bad; rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) From 9a82dcd98b6e6e11cfd162410967951f12152528 Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Mon, 27 Jul 2026 20:30:59 -0500 Subject: [PATCH 325/501] selinux: reject a class permission count below its inherited common security_get_permissions() maps an inherited common's permissions into an array sized by the class's own permissions.nprim, but class_read() takes that nprim verbatim from the policy image and never checks that it covers the common. A class that inherits a common of N permissions while declaring a smaller nprim is accepted, and on load the common's permissions are written past the class-sized array -- an out-of-bounds heap write. Reject a class whose permission count is below its inherited common's. Well-formed policies, where the class count already includes the inherited permissions, are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 5ff4d095ec2e..69777e885ae7 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1422,6 +1422,18 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * cladatum->comkey); goto bad; } + + /* + * security_get_permissions() maps the common's permissions + * into an array sized by this class's nprim, so a class must + * declare at least as many as the common it inherits. + */ + if (cladatum->permissions.nprim < + cladatum->comdatum->permissions.nprim) { + pr_err("SELinux: class %s has fewer permissions than common %s\n", + key, cladatum->comkey); + goto bad; + } } for (i = 0; i < nel; i++) { rc = perm_read(p, &cladatum->permissions, fp); From bc0e8faf90e776a2f1f3967a04e8091e6bdb4977 Mon Sep 17 00:00:00 2001 From: Kyumin Lee Date: Fri, 31 Jul 2026 04:27:34 +0900 Subject: [PATCH 326/501] io_uring: preserve task restrictions across exec Per-task restrictions apply to all rings created by a task. Once installed, they should not be dropped across exec. For a task that has used io_uring, the exec cancellation path calls __io_uring_free(). This frees both the task context and the per-task restriction, so a ring created after exec is unrestricted. Split task context cleanup into io_uring_free_tctx(), and use it from the exec cancellation path. Keep __io_uring_free() for final task cleanup, where both the context and restriction are released. Fixes: ed82f35b926b ("io_uring: allow registration of per-task restrictions") Cc: stable@vger.kernel.org # 7.1+ Signed-off-by: Kyumin Lee Link: https://patch.msgid.link/20260730192734.459247-1-fyonglkm@gmail.com Signed-off-by: Jens Axboe --- io_uring/cancel.c | 2 +- io_uring/tctx.c | 7 ++++++- io_uring/tctx.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/io_uring/cancel.c b/io_uring/cancel.c index 8c6fa6f367e4..7d7820eab878 100644 --- a/io_uring/cancel.c +++ b/io_uring/cancel.c @@ -660,6 +660,6 @@ __cold void io_uring_cancel_generic(bool cancel_all, struct io_sq_data *sqd) */ atomic_dec(&tctx->in_cancel); /* for exec all current's requests should be gone, kill tctx */ - __io_uring_free(current); + io_uring_free_tctx(current); } } diff --git a/io_uring/tctx.c b/io_uring/tctx.c index cc3bf2b3bdbc..466b7300e208 100644 --- a/io_uring/tctx.c +++ b/io_uring/tctx.c @@ -43,7 +43,7 @@ static struct io_wq *io_init_wq_offload(struct io_ring_ctx *ctx, return io_wq_create(concurrency, &data); } -void __io_uring_free(struct task_struct *tsk) +void io_uring_free_tctx(struct task_struct *tsk) { struct io_uring_task *tctx = tsk->io_uring; struct io_tctx_node *node; @@ -67,6 +67,11 @@ void __io_uring_free(struct task_struct *tsk) kfree(tctx); tsk->io_uring = NULL; } +} + +void __io_uring_free(struct task_struct *tsk) +{ + io_uring_free_tctx(tsk); if (tsk->io_uring_restrict) { io_put_bpf_filters(tsk->io_uring_restrict); kfree(tsk->io_uring_restrict); diff --git a/io_uring/tctx.h b/io_uring/tctx.h index 2310d2a0c46d..76ad1ad4594e 100644 --- a/io_uring/tctx.h +++ b/io_uring/tctx.h @@ -12,6 +12,7 @@ void io_uring_del_tctx_node(unsigned long index); int __io_uring_add_tctx_node(struct io_ring_ctx *ctx); int __io_uring_add_tctx_node_from_submit(struct io_ring_ctx *ctx); void io_uring_clean_tctx(struct io_uring_task *tctx); +void io_uring_free_tctx(struct task_struct *tsk); void io_uring_unreg_ringfd(void); int io_ringfd_register(struct io_ring_ctx *ctx, void __user *__arg, From 3860d8748af315bfee6fe669fddc1fc17d3214db Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Fri, 24 Jul 2026 17:30:36 +0800 Subject: [PATCH 327/501] net: hns3: fix speed configuration residue after driver reload After setting a 100G optical port to 40G via ethtool and reloading the driver, the port remains at 40G instead of reverting to the firmware default speed of 100G. The commit referenced in Fixes: added two overwrites in hclge_init_ae_dev() for non-copper media, so that optical ports connected to forced-mode remotes inherit the firmware-preset autoneg and speed instead of the hardcoded defaults: req_autoneg = mac.autoneg req_speed = mac.speed (when autoneg disabled) The autoneg overwrite keeps existing behavior: hclge_set_autoneg_speed_dup() already uses mac.autoneg (not req_autoneg) since it was introduced, so autoneg inheritance from firmware was already in place. This part is kept. The speed overwrite, however, introduces the residue: mac.speed reflects whatever was last programmed into the MAC, and after unload firmware does not restore the MAC speed to the flash default. So if the user changed speed via ethtool in a prior load, mac.speed still carries that value on reload and req_speed inherits it. Fix by dropping the req_speed overwrite only. req_speed keeps the firmware default value set in hclge_configure() (cfg.default_speed), so a reload reverts the speed to default, matching the expectation that a driver reload resets link configuration. Trade-off: on optical ports whose firmware default speed does not match a forced-mode remote, reload now drops the link and the user must re-apply ethtool configuration. This is acceptable: a driver reload is expected to reset link configuration, not to inherit runtime state from before unload. The autoneg inheritance is left in place as established behavior; changing it is out of scope for this patch and would itself be a user-perceivable behavior change. Fixes: d9d349c4e8a0 ("net: hns3: differentiate autoneg default values between copper and fiber") Signed-off-by: Jijie Shao Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260724093036.426631-1-shaojijie@huawei.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index fc8587c80813..164c3ecf195c 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -9498,12 +9498,8 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_ptp_uninit; - if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) { + if (hdev->hw.mac.media_type != HNAE3_MEDIA_TYPE_COPPER) hdev->hw.mac.req_autoneg = hdev->hw.mac.autoneg; - if (hdev->hw.mac.autoneg == AUTONEG_DISABLE && - hdev->hw.mac.speed != SPEED_UNKNOWN) - hdev->hw.mac.req_speed = hdev->hw.mac.speed; - } ret = hclge_set_autoneg_speed_dup(hdev); if (ret) { From 1881f2efbf7f78dc0a79a387b29fde6ff56d3731 Mon Sep 17 00:00:00 2001 From: Fan Ye Date: Mon, 27 Jul 2026 12:29:48 +0000 Subject: [PATCH 328/501] Revert "net: thunderbolt: Enable end-to-end flow control also in transmit" This reverts commit a8065af3346ebd7c76ebc113451fb3ba94cf7769. Per the USB4 spec, a Transmit Descriptor Ring with E2E flow control disabled does not require any credits to be available before the Host Interface Adapter Layer transmits a tunneled packet from it. Once E2E is enabled on that ring the controller must first obtain end-to-end credits. The ASMedia ASM4242 USB4 host router (PCI 1b21:2425) never delivers those credits. The controller does accept the configuration: reading the ring OPTIONS register back right after tb_ring_start() returns exactly what was written, including RING_FLAG_E2E_FLOW_CONTROL (bit 28) and the E2E HopID field. No credit ever arrives though, so the Tx ring's hardware consumer index never advances and the link carries no traffic at all. Measured on two hosts connected point to point, onboard ASM4242 on MSI X870E and X870, v6.17, stock drivers/net/thunderbolt/main.c with only this revert applied on top: before: 100% packet loss to the peer; thunderbolt0 is up and the XDomain handshake completes ("new host found"), but iperf3 fails with "No route to host" once the neighbour entry expires after: 0% packet loss, 0.28 ms RTT; iperf3 4.21 Gb/s one way and 5.17 Gb/s the other (5 runs each, stddev <= 0.02), 1 retransmit in 10 s An instrumented build additionally showed a frozen-Tx-consumer watchdog firing ~30k times in a 10 s window before this change. Rx-side E2E is not touched by this revert, so peers that do return credits keep receive-side flow control. ASMedia does not look like an isolated case. The out-of-tree thunderbolt-ibverbs project disables native E2E on AMD NHI by default, noting that "Strix Halo has reproduced TX completion wedges with multiple native E2E rings active" -- the same failure mode, on a different vendor. Since the driver has no way to tell in advance which host router returns the credits, going back to the previous behaviour looks safer than adding a quirk per affected part; Tx-side E2E can be reintroduced as an opt-in for controllers that are known to implement the credit return. Note that the reverted commit was not fixing a reported problem, it was derived from the spec wording alone, so this revert is not expected to regress a known workload. Cc'ing the original author in case there was one. Fixes: a8065af3346e ("net: thunderbolt: Enable end-to-end flow control also in transmit") Cc: zhangjianrong Signed-off-by: Fan Ye Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260727123002.25225-1-fy15309206903@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/thunderbolt/main.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index 02a91650561a..c5670d61820c 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -925,12 +925,8 @@ static int tbnet_open(struct net_device *dev) netif_carrier_off(dev); - flags = RING_FLAG_FRAME; - /* Only enable full E2E if the other end supports it too */ - if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E) - flags |= RING_FLAG_E2E; - - ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags); + ring = tb_ring_alloc_tx(xd->tb->nhi, -1, TBNET_RING_SIZE, + RING_FLAG_FRAME); if (!ring) { netdev_err(dev, "failed to allocate Tx ring\n"); return -ENOMEM; @@ -949,6 +945,11 @@ static int tbnet_open(struct net_device *dev) sof_mask = BIT(TBIP_PDF_FRAME_START); eof_mask = BIT(TBIP_PDF_FRAME_END); + flags = RING_FLAG_FRAME; + /* Only enable full E2E if the other end supports it too */ + if (tbnet_e2e && net->svc->prtcstns & TBNET_E2E) + flags |= RING_FLAG_E2E; + ring = tb_ring_alloc_rx(xd->tb->nhi, -1, TBNET_RING_SIZE, flags, net->tx_ring.ring->hop, sof_mask, eof_mask, tbnet_start_poll, net); From 683c6ba6e58e6ed1037831ea97dd58d9c0e76b8d Mon Sep 17 00:00:00 2001 From: "Xiang Mei (Microsoft)" Date: Sat, 25 Jul 2026 23:39:30 +0000 Subject: [PATCH 329/501] bonding: alb: re-check primary_is_promisc under RTNL in bond_alb_monitor bond_alb_monitor() reads primary_is_promisc under RCU, then drops RCU and takes RTNL via rtnl_trylock() before undoing the promiscuity it set on the active slave. In that window the active slave can change under RTNL (RTM_DELLINK -> __bond_release_one() -> bond_alb_handle_active_change()), which already drops the promiscuity and clears primary_is_promisc. The monitor still acts on the stale decision: if the slave was removed with no failover, curr_active_slave is now NULL and the deref faults; if it failed over, the stale dev_set_promiscuity(-1) underflows the new slave's promiscuity counter and pins it in IFF_PROMISC. Oops: general protection fault, probably for non-canonical address ... KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Workqueue: b42 bond_alb_monitor RIP: 0010:bond_alb_monitor (drivers/net/bonding/bond_alb.c:1600) process_one_work (kernel/workqueue.c:3322) worker_thread (kernel/workqueue.c:3486) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) Kernel panic - not syncing: Fatal exception Re-check primary_is_promisc (and curr_active_slave) after taking RTNL so the monitor only undoes an increment it still owns. The other bonding monitors already re-read state under RTNL in their commit phase (bond_miimon_commit/bond_ab_arp_commit); bond_alb_monitor() was the only one acting on the pre-trylock decision. Fixes: d0e81b7e2246 ("bonding: Acquire correct locks in alb for promisc change") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) Reviewed-by: Nikolay Aleksandrov Acked-by: Jay Vosburgh Link: https://patch.msgid.link/20260725233930.2957317-1-xmei5@asu.edu Signed-off-by: Jakub Kicinski --- drivers/net/bonding/bond_alb.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 2d37b07c8215..839f7482dc18 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -1534,8 +1534,8 @@ void bond_alb_monitor(struct work_struct *work) struct bonding *bond = container_of(work, struct bonding, alb_work.work); struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct slave *slave, *curr; struct list_head *iter; - struct slave *slave; if (!bond_has_slaves(bond)) { atomic_set(&bond_info->tx_rebalance_counter, 0); @@ -1597,9 +1597,11 @@ void bond_alb_monitor(struct work_struct *work) * because a slave was disabled then * it can now leave promiscuous mode. */ - dev_set_promiscuity(rtnl_dereference(bond->curr_active_slave)->dev, - -1); - bond_info->primary_is_promisc = 0; + curr = rtnl_dereference(bond->curr_active_slave); + if (bond_info->primary_is_promisc && curr) { + dev_set_promiscuity(curr->dev, -1); + bond_info->primary_is_promisc = 0; + } rtnl_unlock(); rcu_read_lock(); From 53a43508ee332d8bffe40590c3d189c92a551f9f Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Mon, 27 Jul 2026 12:19:37 +0100 Subject: [PATCH 330/501] net: devmem: prevent net-iov / page mixing We should either have net_iov or page backed frags in a single skb, otherwise it blows up down the stack. Don't allow mixing in zerocopy_fill_skb_from_devmem(). Fixes: bd61848900bff ("net: devmem: Implement TX path") Cc: stable@vger.kernel.org Signed-off-by: Pavel Begunkov Acked-by: Stanislav Fomichev Reviewed-by: Mina Almasry Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/e3199788c4732545627a4721097ebb71ad737bab.1785150502.git.asml.silence@gmail.com Signed-off-by: Jakub Kicinski --- net/core/datagram.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/core/datagram.c b/net/core/datagram.c index c285c6465923..173b5d97bd40 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -712,6 +712,9 @@ zerocopy_fill_skb_from_devmem(struct sk_buff *skb, struct iov_iter *from, size_t virt_addr, size, off; struct net_iov *niov; + if (i && skb_frags_readable(skb)) + return -EFAULT; + /* Devmem filling works by taking an IOVEC from the user where the * iov_addrs are interpreted as an offset in bytes into the dma-buf to * send from. We do not support other iter types. From c2707480cfbf19c7619acc9c089d17f20869821f Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Fri, 24 Jul 2026 16:40:15 +0200 Subject: [PATCH 331/501] net/packet: reset the MAC header on the packet-socket transmit path packet_parse_headers() resets the MAC header only for a SOCK_RAW frame whose socket did not bind a protocol. A protocol-bound SOCK_RAW socket, any SOCK_DGRAM frame, and the legacy SOCK_PACKET path therefore leave skb->mac_header unset here. For frames sent via __dev_queue_xmit() this is harmless: it resets the MAC header unconditionally. But the packet-socket PACKET_QDISC_BYPASS path uses dev_direct_xmit(), which does not, so the frame reaches ndo_start_xmit() with the MAC header unset. A driver that reads eth_hdr(skb) on transmit then dereferences skb->head + (u16)~0, an out-of-bounds access ~64 KiB past the head -- the same class fixed for one consumer in commit f5089008f90c ("macsec: do not read an unset MAC header in macsec_encrypt()"). packet_parse_headers() runs only on the transmit path, where skb->data points at the start of the L2 header for every packet-socket type regardless of its length: SOCK_RAW and SOCK_PACKET carry a user-supplied header and SOCK_DGRAM has one built by dev_hard_header(). Reset the MAC header unconditionally, mirroring __dev_queue_xmit(), so the frame is anchored on the bypass path too. Found by 0sec (https://0sec.ai) using automated source analysis; verified against source and matched to the macsec KASAN report in f5089008f90c. Compile-tested. Fixes: 75c65772c3d1 ("net/packet: Ask driver for protocol if not provided by user") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260724144015.63219-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e75d2932475a..5ae0511e89e3 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1924,11 +1924,12 @@ static void packet_parse_headers(struct sk_buff *skb, struct socket *sock) { int depth; + /* On TX skb->data is the L2 header; anchor it for all socket types. */ + skb_reset_mac_header(skb); + if ((!skb->protocol || skb->protocol == htons(ETH_P_ALL)) && - sock->type == SOCK_RAW) { - skb_reset_mac_header(skb); + sock->type == SOCK_RAW) skb->protocol = dev_parse_header_protocol(skb); - } /* Move network header to the right position for VLAN tagged packets */ if (likely(skb->dev->type == ARPHRD_ETHER) && From ec680ea4ba1bca92a767fb7e7869758bfdd886e3 Mon Sep 17 00:00:00 2001 From: Satish Kharat Date: Mon, 27 Jul 2026 23:26:30 -0700 Subject: [PATCH 332/501] enic: fix tx_hang_reset use-after-free on device removal enic_remove() cancels the reset and change_mtu_work items but does not cancel tx_hang_reset. A TX timeout that fires while the device is being removed can schedule enic_tx_hang_reset() so that it runs after free_netdev(), resulting in a use-after-free. cancel_work_sync() alone is not sufficient here: the still-live watchdog and notify paths can re-schedule these work items in the window between the cancel and unregister_netdev(). Use disable_work_sync(), which cancels the work and blocks any subsequent schedule_work() from requeuing it, and apply it to the reset and change_mtu_work items as well so the same requeue race is closed for all teardown work. Fixes: 937317c7c109 ("enic: do hang reset only in case of tx timeout") Signed-off-by: Satish Kharat Link: https://patch.msgid.link/20260728062730.2394873-1-satishkh@cisco.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/cisco/enic/enic_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cisco/enic/enic_main.c b/drivers/net/ethernet/cisco/enic/enic_main.c index e7125b818087..d98f7e7ccab9 100644 --- a/drivers/net/ethernet/cisco/enic/enic_main.c +++ b/drivers/net/ethernet/cisco/enic/enic_main.c @@ -3011,8 +3011,9 @@ static void enic_remove(struct pci_dev *pdev) if (netdev) { struct enic *enic = netdev_priv(netdev); - cancel_work_sync(&enic->reset); - cancel_work_sync(&enic->change_mtu_work); + disable_work_sync(&enic->reset); + disable_work_sync(&enic->tx_hang_reset); + disable_work_sync(&enic->change_mtu_work); unregister_netdev(netdev); enic_dev_deinit(enic); vnic_dev_close(enic->vdev); From 6ddfba2ea98db21b001e0e5c472499156224650c Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Tue, 28 Jul 2026 07:43:38 +0300 Subject: [PATCH 333/501] net/mlx5e: TC, Check if flow is PEER before acquiring devcom lock In case __mlx5e_add_fdb_flow() fails in lower levels, the flow is deleted via mlx5e_tc_del_flow(), and mlx5e_tc_del_flow() is acquiring ESW devcom lock without condition. In addition, in case of peer_flow, __mlx5e_add_fdb_flow() is called while holding ESW devcom comp lock. This results in an AA deadlock. To fix this, introduce a new PEER flag that is set on flows created as peer flows (the duplicate flows on peer devices), and check it in mlx5e_tc_del_flow() before acquiring ESW devcom lock. Lockdep splat: ============================================ WARNING: possible recursive locking detected ============================================ Possible unsafe locking scenario: CPU0 ---- lock(&comp->lock_key#2); lock(&comp->lock_key#2); *** DEADLOCK *** Call Trace: dump_stack_lvl+0x69/0xa0 print_deadlock_bug.cold+0xbd/0xca __lock_acquire+0x1671/0x2ec0 lock_acquire+0x10e/0x2e0 down_read+0x95/0x430 mlx5_devcom_for_each_peer_begin+0x4e/0xe0 [mlx5_core] mlx5e_tc_del_flow+0x11d/0xa70 [mlx5_core] mlx5e_flow_put+0x99/0x100 [mlx5_core] __mlx5e_add_fdb_flow+0x409/0xf00 [mlx5_core] mlx5e_configure_flower+0x2a86/0x4100 [mlx5_core] mlx5e_rep_setup_tc_cls_flower+0x12f/0x1b0 [mlx5_core] mlx5e_rep_setup_tc_cb+0x153/0x750 [mlx5_core] tc_setup_cb_add+0x1dc/0x470 fl_change+0x2f4d/0x626d [cls_flower] tc_new_tfilter+0x79b/0x2310 rtnetlink_rcv_msg+0x778/0xad0 do_syscall_64+0x70/0x960 entry_SYSCALL_64_after_hwframe+0x4b/0x53 Fixes: 04de7dda7394 ("net/mlx5e: Infrastructure for duplicated offloading of TC flows") Signed-off-by: Shay Drory Reviewed-by: Cosmin Ratiu Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260728044338.2271143-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h | 1 + drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h index 28cab4bf525c..7bfe7cdc5770 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_priv.h @@ -30,6 +30,7 @@ enum { MLX5E_TC_FLOW_FLAG_FAILED = MLX5E_TC_FLOW_BASE + 9, MLX5E_TC_FLOW_FLAG_SAMPLE = MLX5E_TC_FLOW_BASE + 10, MLX5E_TC_FLOW_FLAG_USE_ACT_STATS = MLX5E_TC_FLOW_BASE + 11, + MLX5E_TC_FLOW_FLAG_PEER = MLX5E_TC_FLOW_BASE + 12, }; struct mlx5e_tc_flow_parse_attr { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 1bc7b9019124..b290beb4369a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -2161,7 +2161,8 @@ static void mlx5e_tc_del_flow(struct mlx5e_priv *priv, if (mlx5e_is_eswitch_flow(flow)) { struct mlx5_devcom_comp_dev *devcom = flow->priv->mdev->priv.eswitch->devcom; - if (!mlx5_devcom_for_each_peer_begin(devcom)) { + if (flow_flag_test(flow, PEER) || + !mlx5_devcom_for_each_peer_begin(devcom)) { mlx5e_tc_del_fdb_flow(priv, flow); return; } @@ -4628,6 +4629,7 @@ static int mlx5e_tc_add_fdb_peer_flow(struct flow_cls_offload *f, else in_mdev = priv->mdev; + flow_flags |= BIT(MLX5E_TC_FLOW_FLAG_PEER); parse_attr = flow->attr->parse_attr; peer_flow = __mlx5e_add_fdb_flow(peer_priv, f, flow_flags, parse_attr->filter_dev, From cd09971dcc1c499ae0879010a00e9dba87abdc4f Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Mon, 27 Jul 2026 16:45:48 +0000 Subject: [PATCH 334/501] pds_core: keep the health thread stopped during reset Commit d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") stops the health thread with cancel_work_sync() before a reset, but a devcmd timeout during pdsc_fw_down() re-queues health_work, so pdsc_health_thread() runs again mid-reset and double allocates the core DMA queues via pdsc_fw_up(). Only the reset path is affected: on remove PDSC_S_STOPPING_DRIVER gates the health thread and the workqueue is destroyed. Use disable_work_sync() to cancel health_work and block further queue_work() on it, and enable_work() in pdsc_restart_health_thread() to re-allow it after the reset. disable_work_sync() keeps a disable depth, so every disable must be matched by one enable. pdsc_reset_prepare() stops the health thread and pdsc_reset_done() restarts it. On the AER path pdsc_pci_error_detected() calls pdsc_reset_prepare(), then pdsc_pci_error_resume() re-inits via pci_reset_function_locked() (pds_core has no .slot_reset handler), which runs the pair again - stopping the thread twice but restarting it once. Gate the disable and enable on a health_stopped flag so each fires at most once per stopped/running transition. Fixes: d9407ff11809 ("pds_core: Prevent health thread from running during reset/remove") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260629200358.2626129-1-nikhil.rao%40amd.com?part=2 Signed-off-by: Nikhil P. Rao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260727164548.359562-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/core.h | 1 + drivers/net/ethernet/amd/pds_core/main.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/amd/pds_core/core.h b/drivers/net/ethernet/amd/pds_core/core.h index b7fe9ad73349..a1d41329209f 100644 --- a/drivers/net/ethernet/amd/pds_core/core.h +++ b/drivers/net/ethernet/amd/pds_core/core.h @@ -171,6 +171,7 @@ struct pdsc { struct timer_list wdtimer; unsigned int wdtimer_period; struct work_struct health_work; + bool health_stopped; struct devlink_health_reporter *fw_reporter; u32 fw_recoveries; diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 8d94a4d70395..71a1d4b001c2 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -470,8 +470,10 @@ static void pdsc_stop_health_thread(struct pdsc *pdsc) return; timer_shutdown_sync(&pdsc->wdtimer); - if (pdsc->health_work.func) - cancel_work_sync(&pdsc->health_work); + if (pdsc->health_work.func && !pdsc->health_stopped) { + disable_work_sync(&pdsc->health_work); + pdsc->health_stopped = true; + } } static void pdsc_restart_health_thread(struct pdsc *pdsc) @@ -479,6 +481,10 @@ static void pdsc_restart_health_thread(struct pdsc *pdsc) if (pdsc->pdev->is_virtfn) return; + if (pdsc->health_stopped) { + enable_work(&pdsc->health_work); + pdsc->health_stopped = false; + } timer_setup(&pdsc->wdtimer, pdsc_wdtimer_cb, 0); mod_timer(&pdsc->wdtimer, jiffies + 1); } From 57d635329d799b79096155cdf47ee0013d6780d1 Mon Sep 17 00:00:00 2001 From: "Nikhil P. Rao" Date: Mon, 27 Jul 2026 17:00:30 +0000 Subject: [PATCH 335/501] pds_core: cancel pending PCI reset work on AER recovery pdsc_check_pci_health() queues pci_reset_work when it sees a broken PCI connection, and nothing cancels it. When the PCI core starts AER recovery, pdsc_pci_error_detected() runs pdsc_reset_prepare() and recovers the device, but a pci_reset_work queued just before is left pending. If it runs after recovery released the device lock, it resets a device the driver now considers healthy, bouncing the link for no reason. Cancel pci_reset_work in pdsc_pci_error_detected() after pdsc_reset_prepare(), which has already stopped the health thread so it cannot requeue the work. cancel_work_sync() is safe under the device lock here because pdsc_pci_reset_thread() uses pci_try_reset_function(), which returns instead of blocking on the lock. Only PFs initialize pci_reset_work, so guard the cancel with !is_virtfn. Fixes: 81665adf25d2 ("pds_core: Fix pdsc_check_pci_health function to use work thread") Reported-by: sashiko-bot Closes: https://sashiko.dev/#/patchset/20260714180223.1642792-2-nikhil.rao%40amd.com?part=1 Signed-off-by: Nikhil P. Rao Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260727170030.361116-1-nikhil.rao@amd.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/amd/pds_core/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/ethernet/amd/pds_core/main.c b/drivers/net/ethernet/amd/pds_core/main.c index 71a1d4b001c2..9a2c64198d03 100644 --- a/drivers/net/ethernet/amd/pds_core/main.c +++ b/drivers/net/ethernet/amd/pds_core/main.c @@ -561,7 +561,11 @@ static pci_ers_result_t pdsc_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t error) { if (error == pci_channel_io_frozen) { + struct pdsc *pdsc = pci_get_drvdata(pdev); + pdsc_reset_prepare(pdev); + if (!pdev->is_virtfn) + cancel_work_sync(&pdsc->pci_reset_work); return PCI_ERS_RESULT_NEED_RESET; } From e7188199eff46a636f3436356f0aae039be6dd66 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Wed, 22 Jul 2026 10:04:19 +0900 Subject: [PATCH 336/501] ksmbd: fix use-after-free in __close_file_table_ids() A ksmbd_file can remain alive after logical close while another session holds a temporary reference obtained through ksmbd_lookup_fd_inode(). ksmbd_close_fd() currently marks the file closed and drops the idr-owned reference, but leaves the pointer published in the closing session's idr until the final reference is dropped. If the foreign holder performs the final ksmbd_fd_put(), __put_fd_final() supplies the foreign session's file table to __ksmbd_close_fd(). The object is then freed without being removed from its owner's idr, and the owner session later dereferences the stale pointer during file-table teardown. Remove the volatile id from the owner's idr while ksmbd_close_fd() still holds that table's lock, and clear volatile_id before dropping the idr-owned reference. A later foreign final put then only performs physical destruction and cannot remove the object from the wrong table. Fixes: 8510a043d334 ("ksmbd: increment reference count of parent fp") Reported-by: Yunseong Kim Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/vfs_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/smb/server/vfs_cache.c b/fs/smb/server/vfs_cache.c index d95c405eab11..a141025581af 100644 --- a/fs/smb/server/vfs_cache.c +++ b/fs/smb/server/vfs_cache.c @@ -697,6 +697,8 @@ int ksmbd_close_fd(struct ksmbd_work *work, u64 id) fp = NULL; else { fp->f_state = FP_CLOSED; + idr_remove(ft->idr, id); + fp->volatile_id = KSMBD_NO_FID; closed = true; if (!atomic_dec_and_test(&fp->refcount)) fp = NULL; From cb469993b3a61a72653770856d37af616d72d05f Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Thu, 23 Jul 2026 23:07:14 +0900 Subject: [PATCH 337/501] ksmbd: reject repeated SMB2 NEGOTIATE requests Unauthenticated client can send multiple successful SMB2 NEGOTIATE requests on one connection before SESSION_SETUP. While the connection is in KSMBD_SESS_NEED_SETUP, smb2_handle_negotiate() accepts another SMB3.1.1 NEGOTIATE and overwrites conn->preauth_info with a new allocation. Only the final allocation is freed when the connection is released, leaking one object for every additional successful request. A repeated SMB2 NEGOTIATE after a dialect has been selected is a protocol violation. MS-SMB2 section 3.3.5.4 requires the server to disconnect without replying in this case. Set the connection exiting when rejecting the request, in addition to suppressing the response. Reject SMB2 NEGOTIATE unless the connection is new or is waiting for the SMB2 NEGOTIATE that follows an SMB1 multi-protocol negotiate. Serialize both SMB1 and SMB2 negotiation paths under conn->srv_mutex, since they update connection-wide dialect and negotiation state. Move the locking contract to ksmbd_smb_negotiate_common(), where the state and dialect are selected, and add ksmbd_conn_new() for consistent state access. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Reported-by: Runa Takemoto Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/connection.h | 5 +++++ fs/smb/server/smb2pdu.c | 10 ++-------- fs/smb/server/smb_common.c | 37 ++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/fs/smb/server/connection.h b/fs/smb/server/connection.h index ec75633b7da0..2a194ee36fb4 100644 --- a/fs/smb/server/connection.h +++ b/fs/smb/server/connection.h @@ -200,6 +200,11 @@ void ksmbd_conn_r_count_dec(struct ksmbd_conn *conn); * This is a hack. We will move status to a proper place once we land * a multi-sessions support. */ +static inline bool ksmbd_conn_new(struct ksmbd_conn *conn) +{ + return READ_ONCE(conn->status) == KSMBD_SESS_NEW; +} + static inline bool ksmbd_conn_good(struct ksmbd_conn *conn) { return READ_ONCE(conn->status) == KSMBD_SESS_GOOD; diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index c1ba5e01aa7f..404a4203f7da 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1325,6 +1325,8 @@ static __le32 deassemble_neg_contexts(struct ksmbd_conn *conn, * smb2_handle_negotiate() - handler for smb2 negotiate command * @work: smb work containing smb request buffer * + * The caller holds conn->srv_mutex. + * * Return: 0 */ int smb2_handle_negotiate(struct ksmbd_work *work) @@ -1338,13 +1340,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_debug(SMB, "Received negotiate request\n"); conn->need_neg = false; - if (ksmbd_conn_good(conn)) { - pr_err("conn->tcp_status is already in CifsGood State\n"); - work->send_no_response = 1; - return rc; - } - - ksmbd_conn_lock(conn); smb2_buf_len = get_rfc1002_len(work->request_buf); smb2_neg_size = offsetof(struct smb2_negotiate_req, Dialects); if (smb2_neg_size > smb2_buf_len) { @@ -1495,7 +1490,6 @@ int smb2_handle_negotiate(struct ksmbd_work *work) ksmbd_conn_set_need_setup(conn); err_out: - ksmbd_conn_unlock(conn); if (rc) rsp->hdr.Status = STATUS_INSUFFICIENT_RESOURCES; diff --git a/fs/smb/server/smb_common.c b/fs/smb/server/smb_common.c index 7de73223189a..080fbc9eb470 100644 --- a/fs/smb/server/smb_common.c +++ b/fs/smb/server/smb_common.c @@ -608,23 +608,46 @@ int ksmbd_smb_negotiate_common(struct ksmbd_work *work, unsigned int command) struct ksmbd_conn *conn = work->conn; int ret; - conn->dialect = - ksmbd_negotiate_smb_dialect(work->request_buf); - ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); - if (command == SMB2_NEGOTIATE_HE) { + /* + * An SMB2 NEGOTIATE is valid for a new connection, or after an + * SMB1 multi-protocol negotiate has selected SMB2. Do not allow + * a second SMB2 NEGOTIATE to replace connection-wide state + * while a session setup is pending. KSMBD_SESS_NEED_RECONNECT + * is a transient session state and does not restart transport + * negotiation. + */ + ksmbd_conn_lock(conn); + if (!ksmbd_conn_new(conn) && + !ksmbd_conn_need_negotiate(conn)) { + work->send_no_response = 1; + ksmbd_conn_set_exiting(conn); + ksmbd_conn_unlock(conn); + return 0; + } + + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); ret = smb2_handle_negotiate(work); + ksmbd_conn_unlock(conn); return ret; } if (command == SMB_COM_NEGOTIATE) { + ksmbd_conn_lock(conn); + conn->dialect = + ksmbd_negotiate_smb_dialect(work->request_buf); + ksmbd_debug(SMB, "conn->dialect 0x%x\n", conn->dialect); if (__smb2_negotiate(conn)) { init_smb3_11_server(conn); - init_smb2_neg_rsp(work); + ret = init_smb2_neg_rsp(work); ksmbd_debug(SMB, "Upgrade to SMB2 negotiation\n"); - return 0; + } else { + ret = smb_handle_negotiate(work); } - return smb_handle_negotiate(work); + ksmbd_conn_unlock(conn); + return ret; } pr_err("Unknown SMB negotiation command: %u\n", command); From e8bb506e6ef749ac0336f3e579d8d02396b7d832 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Mon, 20 Jul 2026 10:32:01 +0900 Subject: [PATCH 338/501] ksmbd: use memcmp() to compare ClientGUIDs ClientGUID is a fixed-size binary value and can contain embedded NUL bytes. strncmp() stops comparing at the first NUL byte, so different ClientGUID values can incorrectly be treated as equal. Use memcmp() in SMB3 multichannel session binding and FSCTL_VALIDATE_NEGOTIATE_INFO to compare all SMB2_CLIENT_GUID_SIZE bytes. Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel") Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: Samu Suggested-by: Samu Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 404a4203f7da..76f63f9adc72 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -1969,7 +1969,7 @@ int smb2_sess_setup(struct ksmbd_work *work) goto out_err; } - if (strncmp(conn->ClientGUID, sess->ClientGUID, + if (memcmp(conn->ClientGUID, sess->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { rc = -ENOENT; goto out_err; @@ -8690,7 +8690,7 @@ static int fsctl_validate_negotiate_info(struct ksmbd_conn *conn, goto err_out; } - if (strncmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { + if (memcmp(neg_req->Guid, conn->ClientGUID, SMB2_CLIENT_GUID_SIZE)) { ret = -EINVAL; goto err_out; } From 2195424c3da2ef1829a63b807e3a900a90e57d85 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Mon, 27 Jul 2026 07:03:42 +0900 Subject: [PATCH 339/501] net/x25: fix use-after-free of the socket by its timers The x25 timers are armed with mod_timer() and cancelled with timer_delete(), so a pending timer holds no reference on the socket and a cancel does not wait for a callback already running on another CPU. x25_heartbeat_expiry() also rearms unconditionally, so it can reinstall sk->sk_timer after __x25_destroy_socket() has passed its cancel point. The following __sock_put() frees the socket while the timer is still queued, and the next expiry uses freed memory. KASAN reports a slab-use-after-free on the kmalloc-2k object freed by close(). timer_delete_sync() cannot be used here: x25_heartbeat_expiry() and x25_timer_expiry() both reach the cancels from inside the timer they would wait on, through __x25_destroy_socket() and x25_disconnect(). Arm the timers with sk_reset_timer() and cancel them with sk_stop_timer() so that an armed timer owns a reference, and release it in both expiry handlers. Rearm the heartbeat only while sk_hashed(sk) is still true, since __x25_destroy_socket() unlinks the socket before dropping it. Arm the deferred destroy timer the same way and drop its reference in x25_destroy_timer(). Reproduced on net with KASAN, with the heartbeat period shortened so the window recurs. With this patch the reproducer no longer triggers a report and /proc/net/x25 drains. Discovered by XBOW, triaged by Baul Lee Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Link: https://patch.msgid.link/20260726220342.47245-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/x25/af_x25.c | 4 ++-- net/x25/x25_timer.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 8aae9273b7c1..033e7d059f58 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -363,6 +363,7 @@ static void x25_destroy_timer(struct timer_list *t) struct sock *sk = timer_container_of(sk, t, sk_timer); x25_destroy_socket_from_timer(sk); + sock_put(sk); } /* @@ -398,9 +399,8 @@ static void __x25_destroy_socket(struct sock *sk) if (sk_has_allocations(sk)) { /* Defer: outstanding buffers */ - sk->sk_timer.expires = jiffies + 10 * HZ; sk->sk_timer.function = x25_destroy_timer; - add_timer(&sk->sk_timer); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 10 * HZ); } else { /* drop last reference so sock_put will free */ __sock_put(sk); diff --git a/net/x25/x25_timer.c b/net/x25/x25_timer.c index 2ec63a1f4c6d..7896cd43f1cc 100644 --- a/net/x25/x25_timer.c +++ b/net/x25/x25_timer.c @@ -36,45 +36,45 @@ void x25_init_timers(struct sock *sk) void x25_start_heartbeat(struct sock *sk) { - mod_timer(&sk->sk_timer, jiffies + 5 * HZ); + sk_reset_timer(sk, &sk->sk_timer, jiffies + 5 * HZ); } void x25_stop_heartbeat(struct sock *sk) { - timer_delete(&sk->sk_timer); + sk_stop_timer(sk, &sk->sk_timer); } void x25_start_t2timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t2); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t2); } void x25_start_t21timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t21); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t21); } void x25_start_t22timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t22); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t22); } void x25_start_t23timer(struct sock *sk) { struct x25_sock *x25 = x25_sk(sk); - mod_timer(&x25->timer, jiffies + x25->t23); + sk_reset_timer(sk, &x25->timer, jiffies + x25->t23); } void x25_stop_timer(struct sock *sk) { - timer_delete(&x25_sk(sk)->timer); + sk_stop_timer(sk, &x25_sk(sk)->timer); } unsigned long x25_display_timer(struct sock *sk) @@ -108,7 +108,7 @@ static void x25_heartbeat_expiry(struct timer_list *t) sock_flag(sk, SOCK_DEAD))) { bh_unlock_sock(sk); x25_destroy_socket_from_timer(sk); - return; + goto out; } break; @@ -120,8 +120,14 @@ static void x25_heartbeat_expiry(struct timer_list *t) break; } restart_heartbeat: - x25_start_heartbeat(sk); + /* Do not rearm once __x25_destroy_socket() has unlinked the socket: + * it is past its cancel point and owns the teardown from there on. + */ + if (sk_hashed(sk)) + x25_start_heartbeat(sk); bh_unlock_sock(sk); +out: + sock_put(sk); } /* @@ -166,4 +172,5 @@ static void x25_timer_expiry(struct timer_list *t) } else x25_do_timer_expiry(sk); bh_unlock_sock(sk); + sock_put(sk); } From cc679d7a6303e84d769f2afcde1fc51c51f127cd Mon Sep 17 00:00:00 2001 From: Breno Leitao Date: Wed, 29 Jul 2026 07:44:40 -0700 Subject: [PATCH 340/501] uprobes: Fix NULL pointer dereference in hprobe_expire() Forking a task that has a pending uretprobe can oops the kernel with a NULL pointer dereference in the clone() path: BUG: kernel NULL pointer dereference, address: 0000000000000018 Oops: 0002 [#1] SMP NOPTI RIP: 0010:hprobe_expire CR2: 0000000000000018 Call Trace: uprobe_copy_process copy_process kernel_clone __x64_sys_clone do_syscall_64 entry_SYSCALL_64_after_hwframe This was found on real hosts on Meta fleet. I've got the impression that this is what is happening: CPU 1 CPU 2 (traced task) ----- ------------------- hit uprobe, prepare_uretprobe(): hprobe LEASED, refcount >= 1 uprobe_unregister() put_uprobe(): refcount -> 0 fork() -> dup_utask() hprobe_expire(hprobe, true) try_get_uprobe() -> NULL get_uprobe(NULL) <-- Oops Only take the extra reference when the uprobe is non-NULL; a NULL means it is gone and is the correct value to return. Fixes: dd1a7567784e ("uprobes: SRCU-protect uretprobe lifetime (with timeout)") Signed-off-by: Breno Leitao Signed-off-by: Peter Zijlstra (Intel) Acked-by: Oleg Nesterov Acked-by: Andrii Nakryiko Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260729-uprobe-v1-1-61896b87c867@debian.org --- kernel/events/uprobes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 4084e926e284..6300b216012c 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -830,7 +830,7 @@ static struct uprobe *hprobe_expire(struct hprobe *hprobe, bool get) if (try_cmpxchg(&hprobe->state, &hstate, uprobe ? HPROBE_STABLE : HPROBE_GONE)) { /* We won the race, we are the ones to unlock SRCU */ __srcu_read_unlock(&uretprobes_srcu, hprobe->srcu_idx); - return get ? get_uprobe(uprobe) : uprobe; + return get && uprobe ? get_uprobe(uprobe) : uprobe; } /* From c679ce3be6cb63763d68ab9b5d9d73ddc0a40762 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Mon, 29 Jun 2026 14:52:29 +0200 Subject: [PATCH 341/501] iomap: add a separate bio_set for iomap_split_ioend iomap_split_ioend can split bios that already come from iomap_ioend_bioset and thus deadlock when the bioset is exhausted. Add a separate bio_set to avoid this deadlock. Christian Brauner says: Mark iomap_ioend_split_bioset static as it is only used in ioend.c, fixing the sparse warning reported by the kernel test robot. Fixes: 5fcbd555d483 ("iomap: split bios to zone append limits in the submission handlers") Signed-off-by: Christoph Hellwig Link: https://patch.msgid.link/20260629125229.3400726-1-hch@lst.de Signed-off-by: Christian Brauner (Amutable) --- fs/iomap/ioend.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/fs/iomap/ioend.c b/fs/iomap/ioend.c index 30468d51b5ad..fb636dce43af 100644 --- a/fs/iomap/ioend.c +++ b/fs/iomap/ioend.c @@ -13,6 +13,7 @@ struct bio_set iomap_ioend_bioset; EXPORT_SYMBOL_GPL(iomap_ioend_bioset); +static struct bio_set iomap_ioend_split_bioset; struct iomap_ioend *iomap_init_ioend(struct inode *inode, struct bio *bio, loff_t file_offset, u16 ioend_flags) @@ -488,7 +489,8 @@ struct iomap_ioend *iomap_split_ioend(struct iomap_ioend *ioend, sector_offset = ALIGN_DOWN(sector_offset << SECTOR_SHIFT, i_blocksize(ioend->io_inode)) >> SECTOR_SHIFT; - split = bio_split(bio, sector_offset, GFP_NOFS, &iomap_ioend_bioset); + split = bio_split(bio, sector_offset, GFP_NOFS, + &iomap_ioend_split_bioset); if (IS_ERR(split)) return ERR_CAST(split); split->bi_private = bio->bi_private; @@ -511,8 +513,23 @@ EXPORT_SYMBOL_GPL(iomap_split_ioend); static int __init iomap_ioend_init(void) { - return bioset_init(&iomap_ioend_bioset, 4 * (PAGE_SIZE / SECTOR_SIZE), + const unsigned int nr_mempool_entries = 4 * (PAGE_SIZE / SECTOR_SIZE); + int error; + + error = bioset_init(&iomap_ioend_bioset, nr_mempool_entries, offsetof(struct iomap_ioend, io_bio), BIOSET_NEED_BVECS); + if (error) + return error; + error = bioset_init(&iomap_ioend_split_bioset, nr_mempool_entries, + offsetof(struct iomap_ioend, io_bio), + BIOSET_NEED_BVECS); + if (error) + goto out_exit_ioend_bioset; + return 0; + +out_exit_ioend_bioset: + bioset_exit(&iomap_ioend_bioset); + return error; } fs_initcall(iomap_ioend_init); From b7ab86bdc65eadcfc43a0e3faf682a3f750cfb96 Mon Sep 17 00:00:00 2001 From: Niklas Schnelle Date: Wed, 22 Jul 2026 15:08:34 +0200 Subject: [PATCH 342/501] s390/pci: Fix s390_pci_mmio_write syscall error return without MIO On a machine without PCI memory-I/O (MIO) support or when running with pci=nomio the s390 specific PCI MMIO write syscall checks if the MMIO cookie is above ZPCI_IOMAP_ADDR_BASE as a sanity check before even trying to perform the MMIO. If this check fails the return value was left unchanged and thus 0 from prior operations falsely indicating success. This could potentially confuse user-space into falsely believing the MMIO, on a mapping not valid for MMIO was successful. Fix this by setting the return value to -EFAULT prior to the check following the same pattern as elsewhere in the same function. Cc: stable@vger.kernel.org Reviewed-by: Julian Ruess Reviewed-by: Farhan Ali Fixes: a67a88b0b8de ("s390/pci: remove races against pte updates") Signed-off-by: Niklas Schnelle Signed-off-by: Vasily Gorbik --- arch/s390/pci/pci_mmio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/s390/pci/pci_mmio.c b/arch/s390/pci/pci_mmio.c index 51e7a28af899..f3f79ba78410 100644 --- a/arch/s390/pci/pci_mmio.c +++ b/arch/s390/pci/pci_mmio.c @@ -188,6 +188,7 @@ SYSCALL_DEFINE3(s390_pci_mmio_write, unsigned long, mmio_addr, goto out_unlock_mmap; } + ret = -EFAULT; io_addr = (void __iomem *)((args.pfn << PAGE_SHIFT) | (mmio_addr & ~PAGE_MASK)); From 06afe425d5283b9764303de47f554da5a808ce8a Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:15 +0200 Subject: [PATCH 343/501] s390/zcrypt: Validate length for CCA AES cipher key requests cca_cipher2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.4+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 84936a795b95..db6a4211a97e 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1261,6 +1261,9 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey, } __packed * prepparm; int keytoklen = ((struct cipherkeytoken *)ckey)->len; + if (keytoklen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); From a9ae0f6dd45c3ccc1d69363f7aea8af179122730 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 29 Jul 2026 11:36:16 +0200 Subject: [PATCH 344/501] s390/zcrypt: Validate length for CCA ECC private key requests cca_ecc2protkey() derives the copy length for the CPRB parameter block directly from the length field in the key token. Reject the request early if the token length exceeds the available space in the parameter block. Fixes: fa6999e326fe ("s390/pkey: support CCA and EP11 secure ECC private keys") Signed-off-by: Holger Dengler Cc: stable@vger.kernel.org # 5.10+ Reviewed-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index db6a4211a97e..322677a8d320 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1428,6 +1428,9 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key, } __packed * prepparm; int keylen = ((struct eccprivkeytoken *)key)->len; + if (keylen > PARMBSIZE - sizeof(struct aureqparm)) + return -EINVAL; + /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, &preqcblk, &prepcblk, xflags); From 36b230835b8a008266aad22168ca52afacc8a58d Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 13:40:09 +0200 Subject: [PATCH 345/501] s390/zcrypt: Fix buffer over-read in cca_cipher2protkey Add validation of both the actual key buffer size and token length fields in all the cca_check_sec*token() functions. Additionally check in cca_gencipherkey() for possible underflow with returned key size. The CCA token structures contain user-controlled len fields that were used in operations without proper validation against both the actual buffer size and minimum token structure size. An attacker could set this field larger than the actual buffer size, leading to reading beyond buffer boundaries. This may result in a kernel crash or exposure of memory via sending this as part of a request down to the crypto card. Also an attacker could have used a very small len value and thus enforce a buffer under-run which may produce similar effects as a over-read. So now a key must - key buf length must be at least sizeof the token struct - the key len field inside the token must fit into the range of sizeof key token struct ... key buf length Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Ingo Franzki Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/pkey_cca.c | 15 +++---- drivers/s390/crypto/zcrypt_ccamisc.c | 64 +++++++++++++++++++++++----- drivers/s390/crypto/zcrypt_ccamisc.h | 6 +-- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/drivers/s390/crypto/pkey_cca.c b/drivers/s390/crypto/pkey_cca.c index 4bc47952324e..e8dc2f77c137 100644 --- a/drivers/s390/crypto/pkey_cca.c +++ b/drivers/s390/crypto/pkey_cca.c @@ -236,22 +236,16 @@ static int cca_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns, if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_AES) { /* CCA AES data key */ - if (keylen < sizeof(struct secaeskeytoken)) - return -EINVAL; - if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0)) + if (cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL && hdr->version == TOKVER_CCA_VLSC) { /* CCA AES cipher key */ - if (keylen < hdr->len) - return -EINVAL; if (cca_check_secaescipherkey(pkey_dbf_info, - 3, key, 0, 1)) + 3, key, keylen, 0, 1)) return -EINVAL; } else if (hdr->type == TOKTYPE_CCA_INTERNAL_PKA) { /* CCA ECC (private) key */ - if (keylen < sizeof(struct eccprivkeytoken)) - return -EINVAL; if (cca_check_sececckeytoken(pkey_dbf_info, 3, key, keylen, 1)) return -EINVAL; } else { @@ -484,7 +478,7 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_AES) { struct secaeskeytoken *t = (struct secaeskeytoken *)key; - rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, 0); + rc = cca_check_secaeskeytoken(pkey_dbf_info, 3, key, keylen, 0); if (rc) goto out; *keytype = PKEY_TYPE_CCA_DATA; @@ -512,7 +506,8 @@ static int cca_verifykey(const u8 *key, u32 keylen, hdr->version == TOKVER_CCA_VLSC) { struct cipherkeytoken *t = (struct cipherkeytoken *)key; - rc = cca_check_secaescipherkey(pkey_dbf_info, 3, key, 0, 1); + rc = cca_check_secaescipherkey(pkey_dbf_info, 3, + key, keylen, 0, 1); if (rc) goto out; *keytype = PKEY_TYPE_CCA_CIPHER; diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 322677a8d320..f797786107b9 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -62,12 +62,18 @@ static DEFINE_MUTEX(dev_status_mem_mutex); * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize) + const u8 *token, u32 keysize, int keybitsize) { struct secaeskeytoken *t = (struct secaeskeytoken *)token; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -101,14 +107,20 @@ EXPORT_SYMBOL(cca_check_secaeskeytoken); * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport) + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport) { struct cipherkeytoken *t = (struct cipherkeytoken *)token; bool keybitsizeok = true; #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -121,6 +133,18 @@ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, __func__, (int)t->version, TOKVER_CCA_VLSC); return -EINVAL; } + if (t->len > keysize) { + if (dbg) + DBF("%s token check failed, len %d > keysize %u\n", + __func__, (int)t->len, keysize); + return -EINVAL; + } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->algtype != 0x02) { if (dbg) DBF("%s token check failed, algtype 0x%02x != 0x02\n", @@ -195,6 +219,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, #define DBF(...) debug_sprintf_event(dbg, dbflvl, ##__VA_ARGS__) + if (keysize < sizeof(*t)) { + if (dbg) + DBF("%s keysize %u < min token size %zu\n", + __func__, keysize, sizeof(*t)); + return -EINVAL; + } if (t->type != TOKTYPE_CCA_INTERNAL_PKA) { if (dbg) DBF("%s token check failed, type 0x%02x != 0x%02x\n", @@ -207,6 +237,12 @@ int cca_check_sececckeytoken(debug_info_t *dbg, int dbflvl, __func__, (int)t->len, keysize); return -EINVAL; } + if (t->len < sizeof(*t)) { + if (dbg) + DBF("%s token check failed, len %d < min token size %zu\n", + __func__, (int)t->len, sizeof(*t)); + return -EINVAL; + } if (t->secid != 0x20) { if (dbg) DBF("%s token check failed, secid 0x%02x != 0x20\n", @@ -443,7 +479,8 @@ int cca_genseckey(u16 cardnr, u16 domain, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -582,7 +619,8 @@ int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize, /* check secure key token */ rc = cca_check_secaeskeytoken(zcrypt_dbf_info, DBF_ERR, - prepparm->lv3.keyblock.tok, 8 * keysize); + prepparm->lv3.keyblock.tok, + seckeysize, 8 * keysize); if (rc) { rc = -EIO; goto out; @@ -842,6 +880,7 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } kb; } __packed * prepparm; struct cipherkeytoken *t; + u32 keybuflen; /* get already prepared memory for 2 cprbs with param block each */ rc = alloc_and_prep_cprbmem(PARMBSIZE, &mem, @@ -936,23 +975,28 @@ int cca_gencipherkey(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, } /* and some checks on the generated key */ + t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; + if (prepparm->kb.tlv1.len < 2 * sizeof(uint16_t) + sizeof(*t)) { + rc = -EIO; + goto out; + } + keybuflen = prepparm->kb.tlv1.len - 2 * sizeof(uint16_t); rc = cca_check_secaescipherkey(zcrypt_dbf_info, DBF_ERR, prepparm->kb.tlv1.gen_key, - keybitsize, 1); + keybuflen, keybitsize, 1); if (rc) { rc = -EIO; goto out; } /* copy the generated vlsc key token */ - t = (struct cipherkeytoken *)prepparm->kb.tlv1.gen_key; if (keybuf) { - if (*keybufsize >= t->len) - memcpy(keybuf, t, t->len); + if (*keybufsize >= keybuflen) + memcpy(keybuf, t, keybuflen); else rc = -EINVAL; } - *keybufsize = t->len; + *keybufsize = keybuflen; out: free_cprbmem(mem, PARMBSIZE, false, xflags); diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 07bbb1c20022..a4534f8b2f1d 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -136,7 +136,7 @@ struct eccprivkeytoken { * also checked. Returns 0 on success or errno value on failure. */ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize); + const u8 *token, u32 keysize, int keybitsize); /* * Simple check if the token is a valid CCA secure AES cipher key @@ -146,8 +146,8 @@ int cca_check_secaeskeytoken(debug_info_t *dbg, int dbflvl, * Returns 0 on success or errno value on failure. */ int cca_check_secaescipherkey(debug_info_t *dbg, int dbflvl, - const u8 *token, int keybitsize, - int checkcpacfexport); + const u8 *token, u32 keysize, + int keybitsize, int checkcpacfexport); /* * Simple check if the token is a valid CCA secure ECC private From 983279d7f86ade73db86f886e09172dd567031b5 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 23 Jul 2026 11:54:52 +0200 Subject: [PATCH 346/501] s390/zcrypt: Fix wrong domain value verification with EP11 CPRBs There is a wrong upper limit check for the domain value when an EP11 CPRB is processed for sending to a crypto card. This check is only active on custom device nodes but may lead to access heap memory behind perms->adm when an administrative CPRB is sent. Add correct limit (AP_DOMAINS = 256) checking to fix this. Fixes: cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices") Cc: stable@vger.kernel.org Reviewed-by: Finn Callies Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index f57189c2b839..81eefdeae248 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -1077,7 +1077,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms, print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1, ap_msg.msg, ap_msg.len, false); - if (perms != &ap_perms && domain < AUTOSEL_DOM) { + if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; From e935cd525af4c6ed2e2c6404aa27ca19c7f39ddb Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Thu, 23 Jul 2026 11:54:53 +0200 Subject: [PATCH 347/501] s390/zcrypt: Close speculative mem read possibility The domain value is extracted from a given CCA or EP11 ioctl struct when a CPRB is about to be sent. Thus this is a user controlled value. Under some special conditions (custom device node used, administrative load) this value is used as an array index after bounds checking, but without speculation barrier. Add the missing array_index_nospec() call to prevent speculative execution where this domain value is used. Fixes: cfd68b33094e ("s390/zcrypt: Filter admin CPRBs on custom devices") Cc: stable@vger.kernel.org Reported-by: Christian Borntraeger Reviewed-by: Finn Callies Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_api.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 81eefdeae248..ec6a4c2f9f04 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -879,6 +879,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms, if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + domain = array_index_nospec(domain, AP_DOMAINS); if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; goto out; @@ -1079,6 +1080,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms, if (perms != &ap_perms && domain < AP_DOMAINS) { if (ap_msg.flags & AP_MSG_FLAG_ADMIN) { + domain = array_index_nospec(domain, AP_DOMAINS); if (!test_bit_inv(domain, perms->adm)) { rc = -ENODEV; goto out; From 01476391aecef36a3b789ee844357b22fbc90665 Mon Sep 17 00:00:00 2001 From: Harald Freudenberger Date: Wed, 29 Jul 2026 16:01:34 +0200 Subject: [PATCH 348/501] s390/zcrypt: Fix missing mem scrub at clear key import in cca_clr2cipherkey() The helper function _ip_cprb_helper() uses internal buffer memory for building and processing CPRBs. After use this buffer was never scrubbed which could lead to leaving for example clear key material in memory which could be exposed via tricky reuse of this same memory. Extend the _ip_cprb_helper() function with another parameter 'scrub' used to steer scrubbing of this buffer. So now the caller has the opportunity to decide if scrubbing is needed or not. Extend the clear key to secure key token import process in function cca_clr2cipherkey() to tell the helper function from above to scrub the cprb buffer when the clear key value is part of the request data. Add explicit scrubbing on return from function cca_clr2cipherkey() for the random EXOR buffer and the cprb buffer. Overall this cleans the internal used buffer in case of clear key import to prevent sensitive data to get exposed. Fixes: 4bc123b18ce6 ("s390/zcrypt: Add low level functions for CCA AES cipher keys") Cc: stable@vger.kernel.org Reviewed-by: Holger Dengler Signed-off-by: Harald Freudenberger Signed-off-by: Vasily Gorbik --- drivers/s390/crypto/zcrypt_ccamisc.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index f797786107b9..86d2ee78c9f4 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -1015,7 +1015,8 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, int clr_key_bit_size, u8 *key_token, int *key_token_size, - u32 xflags) + u32 xflags, + bool scrub) { int rc, n; u8 *mem, *ptr; @@ -1155,7 +1156,7 @@ static int _ip_cprb_helper(u16 cardnr, u16 domain, *key_token_size = t->len; out: - free_cprbmem(mem, PARMBSIZE, false, xflags); + free_cprbmem(mem, PARMBSIZE, scrub, xflags); return rc; } @@ -1206,28 +1207,32 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, * 4/4 COMPLETE the secure cipher key import */ rc = _ip_cprb_helper(card, dom, "AES ", "FIRST ", "MIN3PART", - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 1/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - clrkey, keybitsize, token, &tokensize, xflags); + clrkey, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 2/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "ADD-PART", NULL, - exorbuf, keybitsize, token, &tokensize, xflags); + exorbuf, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 3/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); goto out; } rc = _ip_cprb_helper(card, dom, "AES ", "COMPLETE", NULL, - NULL, keybitsize, token, &tokensize, xflags); + NULL, keybitsize, token, &tokensize, + xflags, true); if (rc) { ZCRYPT_DBF_ERR("%s clear key import 4/4 with CSNBKPI2 failed, rc=%d\n", __func__, rc); @@ -1244,6 +1249,8 @@ int cca_clr2cipherkey(u16 card, u16 dom, u32 keybitsize, u32 keygenflags, *keybufsize = tokensize; out: + memzero_explicit(exorbuf, sizeof(exorbuf)); + memzero_explicit(mem, CPRB_MEMPOOL_ITEM_SIZE); mempool_free(mem, cprb_mempool); return rc; } From 558f67f1340f803a346ecd14a69c49653111c5f4 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Wed, 29 Jul 2026 21:56:59 +0800 Subject: [PATCH 349/501] ipvs: stop estimator after disabled calc phase IPVS estimator kthread 0 starts with zeroed chain and tick limits until its initial calculation phase completes. If network namespace teardown clears ipvs->enable during that phase, ip_vs_est_calc_phase() can return without installing positive limits. The kthread can then continue into its main loop and drain est_temp_list with zero chain_max, tick_max and est_max_count values. Each enqueue consumes one available tick row, but est_count never reaches the zero est_max_count value. After all rows are consumed, the row lookup returns IPVS_EST_NTICKS and ip_vs_enqueue_estimator() writes past the ticks and tick_len arrays. Exit kthread 0 after the calculation phase if the kthread is stopping or IPVS has been disabled. That keeps temporary estimators from being drained after the limits failed to initialize. Estimator kthreads can now self-exit before teardown or reload stops kd->task. Keep an extra task reference after creation and release it with kthread_stop_put(), so kd->task remains valid until the stop paths consume that reference. Fixes: 705dd3444081 ("ipvs: use kthreads for stats estimation") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_est.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/net/netfilter/ipvs/ip_vs_est.c b/net/netfilter/ipvs/ip_vs_est.c index ab09f5182951..05a216a47b45 100644 --- a/net/netfilter/ipvs/ip_vs_est.c +++ b/net/netfilter/ipvs/ip_vs_est.c @@ -191,8 +191,11 @@ static int ip_vs_estimation_kthread(void *data) } /* kthread 0 will handle the calc phase */ - if (ipvs->est_calc_phase) + if (ipvs->est_calc_phase) { ip_vs_est_calc_phase(ipvs); + if (kthread_should_stop() || !READ_ONCE(ipvs->enable)) + return 0; + } } while (1) { @@ -270,6 +273,7 @@ int ip_vs_est_kthread_start(struct netns_ipvs *ipvs, kd->task = NULL; goto out; } + get_task_struct(kd->task); set_user_nice(kd->task, sysctl_est_nice(ipvs)); if (sysctl_est_preferred_cpulist(ipvs)) @@ -286,7 +290,7 @@ void ip_vs_est_kthread_stop(struct ip_vs_est_kt_data *kd) { if (kd->task) { pr_info("stopping estimator thread %d...\n", kd->id); - kthread_stop(kd->task); + kthread_stop_put(kd->task); kd->task = NULL; } } @@ -526,7 +530,7 @@ static void ip_vs_est_kthread_destroy(struct ip_vs_est_kt_data *kd) if (kd) { if (kd->task) { pr_info("stop unused estimator thread %d...\n", kd->id); - kthread_stop(kd->task); + kthread_stop_put(kd->task); } ip_vs_stats_free(kd->calc_stats); kfree(kd); From 30825970339c107bacaf7f61af90fcdb1f597ca1 Mon Sep 17 00:00:00 2001 From: Chengfeng Ye Date: Thu, 30 Jul 2026 01:31:00 +0800 Subject: [PATCH 350/501] netfilter: ebt_nflog: pin the NFLOG backend nf_log_unregister() runs after the per-net teardown so its final RCU grace period also drains readers that obtained the logger from a per-net binding. However, ebt_nflog passes an explicit ULOG log type to nf_log_packet() without holding a reference on the selected logger module, unlike the xt_NFLOG and nft_log frontends. An ebtables nflog rule can therefore remain callable while nfnetlink_log is unloaded. The resulting interleaving is: CPU 0 CPU 1 nfnetlink_log_fini() unregister_pernet_subsys() kfree(nfnl_log_pernet(net)) ebt_nflog_tg() nf_log_packet() nfulnl_log_packet() instance_lookup_get_rcu() The global ULOG logger is still registered at this point, so CPU 1 dereferences the per-net state after CPU 0 has freed it. KASAN reported: BUG: KASAN: slab-use-after-free in instance_lookup_get_rcu Read of size 8 at addr ff110001052e6210 by task poc/92 Call Trace: instance_lookup_get_rcu+0x1ce/0x1f0 [nfnetlink_log] nfulnl_log_packet+0x248/0x2fb0 [nfnetlink_log] nf_log_packet+0x204/0x300 ebt_nflog_tg+0x351/0x550 ebt_do_table+0xedf/0x22b0 Allocated by task 90: __kmalloc_noprof+0x186/0x470 ops_init+0x6d/0x420 register_pernet_operations+0x2f6/0x670 register_pernet_subsys+0x23/0x40 Freed by task 93: kfree+0x131/0x3c0 ops_undo_list+0x3e3/0x700 unregister_pernet_operations+0x232/0x490 unregister_pernet_subsys+0x1c/0x30 nfnetlink_log_fini+0x34/0x450 [nfnetlink_log] Acquire the ULOG logger module reference when an ebt_nflog rule is validated and release it when the rule is destroyed. Request the NFLOG backend for legacy callers when needed, matching xt_NFLOG. This prevents module teardown until all ebt_nflog rules have stopped using the logger. Fixes: c83fa19603bd ("netfilter: nf_log: don't call synchronize_rcu in nf_log_unset") Cc: stable@vger.kernel.org Signed-off-by: Chengfeng Ye Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_nflog.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/bridge/netfilter/ebt_nflog.c b/net/bridge/netfilter/ebt_nflog.c index 61bf8f4465ab..426f8adc912c 100644 --- a/net/bridge/netfilter/ebt_nflog.c +++ b/net/bridge/netfilter/ebt_nflog.c @@ -41,11 +41,25 @@ ebt_nflog_tg(struct sk_buff *skb, const struct xt_action_param *par) static int ebt_nflog_tg_check(const struct xt_tgchk_param *par) { struct ebt_nflog_info *info = par->targinfo; + int ret; if (info->flags & ~EBT_NFLOG_MASK) return -EINVAL; info->prefix[EBT_NFLOG_PREFIX_SIZE - 1] = '\0'; - return 0; + + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); + if (ret != 0 && !par->nft_compat) { + request_module("%s", "nfnetlink_log"); + + ret = nf_logger_find_get(par->family, NF_LOG_TYPE_ULOG); + } + + return ret; +} + +static void ebt_nflog_tg_destroy(const struct xt_tgdtor_param *par) +{ + nf_logger_put(par->family, NF_LOG_TYPE_ULOG); } static struct xt_target ebt_nflog_tg_reg __read_mostly = { @@ -54,6 +68,7 @@ static struct xt_target ebt_nflog_tg_reg __read_mostly = { .family = NFPROTO_BRIDGE, .target = ebt_nflog_tg, .checkentry = ebt_nflog_tg_check, + .destroy = ebt_nflog_tg_destroy, .targetsize = sizeof(struct ebt_nflog_info), .me = THIS_MODULE, }; From 8e5fd2a55e2468f6225ef5af01ae58cdbcbae8cf Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 30 Jul 2026 20:38:49 +0200 Subject: [PATCH 351/501] netfilter: ipset: rework cidr bookkeeping According to sashiko, the current bookkeeping of cidr values are unsafe on weakly-ordered architectures. Replace the in-place updating with an RCU based method: create the new bookeeping structure, update and replace the old one with the new. Downside that we need to allocate memory when deleting a cidr entry - in case of memory pressure fall back to leave holes which possibility is taken into account at evaluation time. Thanks to Pablo (Pablo Neira Ayuso ) and Cyntia (Cynthia ) for helping me in debugging which resulted the patch "netfilter: ipset: allocate the proper memory for the generic hash structure" on which this very patch depends. Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 237 +++++++++++++------ net/netfilter/ipset/ip_set_hash_ipportnet.c | 4 +- net/netfilter/ipset/ip_set_hash_net.c | 4 +- net/netfilter/ipset/ip_set_hash_netiface.c | 4 +- net/netfilter/ipset/ip_set_hash_netnet.c | 12 +- net/netfilter/ipset/ip_set_hash_netport.c | 4 +- net/netfilter/ipset/ip_set_hash_netportnet.c | 12 +- 7 files changed, 183 insertions(+), 94 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index b2d77973272d..dd31992c915c 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -99,9 +99,15 @@ struct htable { #endif /* Book-keeping of the prefixes added to the set */ +struct net_prefix { + u8 cidr; /* the cidr value */ + u32 count; /* number of elements of this cidr */ +}; + struct net_prefixes { - u32 nets[IPSET_NET_COUNT]; /* number of elements for this cidr */ - u8 cidr[IPSET_NET_COUNT]; /* the cidr value */ + struct rcu_head rcu; + u8 len; + struct net_prefix nets[] __counted_by(len); }; /* Compute the hash table size */ @@ -127,11 +133,6 @@ htable_size(u8 hbits) #else #define __CIDR(cidr, i) (cidr) #endif - -/* cidr + 1 is stored in net_prefixes to support /0 */ -#define NCIDR_PUT(cidr) ((cidr) + 1) -#define NCIDR_GET(cidr) ((cidr) - 1) - #ifdef IP_SET_HASH_WITH_NETS_PACKED /* When cidr is packed with nomatch, cidr - 1 is stored in the data entry */ #define DCIDR_PUT(cidr) ((cidr) - 1) @@ -141,21 +142,11 @@ htable_size(u8 hbits) #define DCIDR_GET(cidr, i) __CIDR(cidr, i) #endif -#define INIT_CIDR(cidr, host_mask) \ - DCIDR_PUT(((cidr) ? NCIDR_GET(cidr) : host_mask)) +#define INIT_CIDR(n, host_mask) ({ \ + const struct net_prefixes *__n = rcu_dereference(n); \ + DCIDR_PUT((__n)->len ? (__n)->nets[0].cidr : host_mask);\ +}) -#ifdef IP_SET_HASH_WITH_NET0 -/* cidr from 0 to HOST_MASK value and c = cidr + 1 */ -#define NLEN (HOST_MASK + 1) -#define CIDR_POS(c) ((c) - 1) -#else -/* cidr from 1 to HOST_MASK value and c = cidr + 1 */ -#define NLEN HOST_MASK -#define CIDR_POS(c) ((c) - 2) -#endif - -#else -#define NLEN 0 #endif /* IP_SET_HASH_WITH_NETS */ #define SET_ELEM_EXPIRED(set, d) \ @@ -292,6 +283,7 @@ static const union nf_inet_addr zeromask = {}; /* The generic hash structure */ struct htype { struct htable __rcu *table; /* the hash table */ + struct net_prefixes __rcu *rnets[IPSET_NET_COUNT]; /* cidr prefixes */ struct htable_gc gc; /* gc workqueue */ u32 maxelem; /* max elements in the hash */ u32 initval; /* random jhash init value */ @@ -302,9 +294,6 @@ struct htype { #if defined(IP_SET_HASH_WITH_NETMASK) || defined(IP_SET_HASH_WITH_BITMASK) u8 netmask; /* netmask value for subnets to store */ union nf_inet_addr bitmask; /* stores bitmask */ -#endif -#ifdef IP_SET_HASH_WITH_NETS - struct net_prefixes nets[NLEN]; /* book-keeping of prefixes */ #endif /* Because 'next' is IPv4/IPv6 dependent, no elements of this * structure and referred in create() may come after 'next'. @@ -326,50 +315,92 @@ struct mtype_resize_ad { /* Network cidr size book keeping when the hash stores different * sized networks. cidr == real cidr + 1 to support /0. */ -static void +static int mtype_add_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n) { - int i, j; + struct net_prefixes *nets, *tmp; + int i, j, found, len = 0, ret = 0; spin_lock_bh(&set->lock); + nets = __ipset_dereference(h->rnets[n]); /* Add in increasing prefix order, so larger cidr first */ - for (i = 0, j = -1; i < NLEN && h->nets[i].cidr[n]; i++) { - if (j != -1) { + for (i = 0, found = -1; i < nets->len; i++) { + if (nets->nets[i].count) + len++; + if (found != -1) { continue; - } else if (h->nets[i].cidr[n] < cidr) { - j = i; - } else if (h->nets[i].cidr[n] == cidr) { - h->nets[CIDR_POS(cidr)].nets[n]++; + } else if (nets->nets[i].cidr < cidr) { + found = i; + } else if (nets->nets[i].cidr == cidr) { + nets->nets[i].count++; goto unlock; } } - if (j != -1) { - for (; i > j; i--) - h->nets[i].cidr[n] = h->nets[i - 1].cidr[n]; + len++; + tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC); + if (!tmp) { + ret = -ENOMEM; + goto unlock; } - h->nets[i].cidr[n] = cidr; - h->nets[CIDR_POS(cidr)].nets[n] = 1; + + tmp->len = len; + for (i = 0, j = 0; i < nets->len; i++) { + if (i == found) { + tmp->nets[j].cidr = cidr; + tmp->nets[j++].count = 1; + } + if (!nets->nets[i].count) + continue; + tmp->nets[j].cidr = nets->nets[i].cidr; + tmp->nets[j++].count = nets->nets[i].count; + } + if (found == -1) { + tmp->nets[j].cidr = cidr; + tmp->nets[j].count = 1; + } + rcu_assign_pointer(h->rnets[n], tmp); + kfree_rcu(nets, rcu); unlock: spin_unlock_bh(&set->lock); + return ret; } static void mtype_del_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n) { - u8 i, j, net_end = NLEN - 1; + struct net_prefixes *nets, *tmp; + u8 i, j, len = 0; + int found; spin_lock_bh(&set->lock); - for (i = 0; i < NLEN; i++) { - if (h->nets[i].cidr[n] != cidr) - continue; - h->nets[CIDR_POS(cidr)].nets[n]--; - if (h->nets[CIDR_POS(cidr)].nets[n] > 0) - goto unlock; - for (j = i; j < net_end && h->nets[j].cidr[n]; j++) - h->nets[j].cidr[n] = h->nets[j + 1].cidr[n]; - h->nets[j].cidr[n] = 0; - goto unlock; + nets = __ipset_dereference(h->rnets[n]); + for (i = 0, found = -1; i < nets->len; i++) { + if (nets->nets[i].count) + len++; + if (nets->nets[i].cidr == cidr) + found = i; } + if (unlikely(found == -1)) + goto unlock; + + nets->nets[found].count--; + if (nets->nets[found].count) + goto unlock; + len--; + tmp = kzalloc_flex(*tmp, nets, len, GFP_ATOMIC); + if (!tmp) + /* Leave a hole */ + goto unlock; + + tmp->len = len; + for (i = 0, j = 0; i < nets->len; i++) { + if (!nets->nets[i].count || i == found) + continue; + tmp->nets[j].cidr = nets->nets[i].cidr; + tmp->nets[j++].count = nets->nets[i].count; + } + rcu_assign_pointer(h->rnets[n], tmp); + kfree_rcu(nets, rcu); unlock: spin_unlock_bh(&set->lock); } @@ -402,6 +433,9 @@ static void mtype_flush(struct ip_set *set) { struct htype *h = set->data; +#ifdef IP_SET_HASH_WITH_NETS + struct net_prefixes *nets, *tmp; +#endif struct htable *t; struct hbucket *n; u32 r, i; @@ -425,7 +459,19 @@ mtype_flush(struct ip_set *set) spin_unlock_bh(&t->hregion[r].lock); } #ifdef IP_SET_HASH_WITH_NETS - memset(h->nets, 0, sizeof(h->nets)); + for (i = 0; i < IPSET_NET_COUNT; i++) { + nets = ipset_dereference_nfnl(h->rnets[i]); + tmp = kzalloc_obj(*tmp, GFP_ATOMIC); + if (!tmp) { + u8 j; + + for (j = 0; j < nets->len; j++) + nets->nets[j].count = 0; + } else { + rcu_assign_pointer(h->rnets[i], tmp); + kfree_rcu(nets, rcu); + } + } #endif } @@ -433,6 +479,9 @@ mtype_flush(struct ip_set *set) static void mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) { +#ifdef IP_SET_HASH_WITH_NETS + struct htype *h = set->data; +#endif struct hbucket *n; u32 i; @@ -446,6 +495,11 @@ mtype_ahash_destroy(struct ip_set *set, struct htable *t, bool ext_destroy) kfree(n); } +#ifdef IP_SET_HASH_WITH_NETS + if (ext_destroy) + for (i = 0; i < IPSET_NET_COUNT; i++) + kfree(rcu_dereference_raw(h->rnets[i])); +#endif ip_set_free(t->hregion); ip_set_free(t); } @@ -519,8 +573,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) #ifdef IP_SET_HASH_WITH_NETS for (k = 0; k < IPSET_NET_COUNT; k++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(data->cidr, k)), - k); + DCIDR_GET(data->cidr, k), k); #endif t->hregion[r].elements--; ip_set_ext_destroy(set, data); @@ -950,8 +1003,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(data->cidr, i)), - i); + DCIDR_GET(data->cidr, i), i); #endif ip_set_ext_destroy(set, data); t->hregion[r].elements--; @@ -996,7 +1048,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, t->hregion[r].elements++; #ifdef IP_SET_HASH_WITH_NETS for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_add_cidr(set, h, NCIDR_PUT(DCIDR_GET(d->cidr, i)), i); + mtype_add_cidr(set, h, DCIDR_GET(d->cidr, i), i); #endif memcpy(data, d, sizeof(struct mtype_elem)); overwrite_extensions: @@ -1110,7 +1162,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, #ifdef IP_SET_HASH_WITH_NETS for (j = 0; j < IPSET_NET_COUNT; j++) mtype_del_cidr(set, h, - NCIDR_PUT(DCIDR_GET(d->cidr, j)), j); + DCIDR_GET(d->cidr, j), j); #endif ip_set_ext_destroy(set, data); @@ -1193,28 +1245,37 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, { struct htype *h = set->data; struct htable *t = rcu_dereference_bh(h->table); + struct net_prefixes *nets0; struct hbucket *n; struct mtype_elem *data; #if IPSET_NET_COUNT == 2 + struct net_prefixes *nets1; struct mtype_elem orig = *d; - int ret, i, j = 0, k; + int ret, i, j, k; #else - int ret, i, j = 0; + int ret, i, j; #endif u32 key, multi = 0; u8 pos; pr_debug("test by nets\n"); - for (; j < NLEN && h->nets[j].cidr[0] && !multi; j++) { + rcu_read_lock_bh(); + nets0 = rcu_dereference_bh(h->rnets[0]); +#if IPSET_NET_COUNT == 2 + nets1 = rcu_dereference_bh(h->rnets[1]); +#endif + for (j = 0; j < nets0->len && !multi; j++) { + if (!nets0->nets[j].count) + continue; #if IPSET_NET_COUNT == 2 mtype_data_reset_elem(d, &orig); - mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0]), false); - for (k = 0; k < NLEN && h->nets[k].cidr[1] && !multi; - k++) { - mtype_data_netmask(d, NCIDR_GET(h->nets[k].cidr[1]), - true); + mtype_data_netmask(d, nets0->nets[j].cidr, false); + for (k = 0; k < nets1->len && !multi; k++) { + if (!nets1->nets[k].count) + continue; + mtype_data_netmask(d, nets1->nets[k].cidr, true); #else - mtype_data_netmask(d, NCIDR_GET(h->nets[j].cidr[0])); + mtype_data_netmask(d, nets0->nets[j].cidr); #endif key = HKEY(d, h->initval, t->htable_bits); n = rcu_dereference_bh(hbucket(t, key)); @@ -1229,7 +1290,7 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, continue; ret = mtype_data_match(data, ext, mext, set, flags); if (ret != 0) - return ret; + goto unlock; #ifdef IP_SET_HASH_WITH_MULTI /* No match, reset multiple match flag */ multi = 0; @@ -1239,7 +1300,10 @@ mtype_test_cidrs(struct ip_set *set, struct mtype_elem *d, } #endif } - return 0; + ret = 0; +unlock: + rcu_read_unlock_bh(); + return ret; } #endif @@ -1504,6 +1568,9 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, int ret __attribute__((unused)) = 0; u8 netmask = set->family == NFPROTO_IPV4 ? 32 : 128; union nf_inet_addr bitmask = onesmask; +#endif +#ifdef IP_SET_HASH_WITH_NETS + struct net_prefixes *nets; #endif size_t hsize; struct htype *h; @@ -1604,21 +1671,25 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, */ hbits = fls(hashsize - 1); hsize = htable_size(hbits); - if (hsize == 0) { - kfree(h); - return -ENOMEM; - } + if (hsize == 0) + goto free_h; t = ip_set_alloc(hsize); - if (!t) { - kfree(h); - return -ENOMEM; - } + if (!t) + goto free_h; t->hregion = ip_set_alloc(ahash_sizeof_regions(hbits)); - if (!t->hregion) { - ip_set_free(t); - kfree(h); - return -ENOMEM; + if (!t->hregion) + goto free_t; +#ifdef IP_SET_HASH_WITH_NETS + for (i = 0; i < IPSET_NET_COUNT; i++) { + nets = kzalloc_obj(*nets); + if (!nets) { + while (i > 0) + kfree(rcu_dereference_raw(h->rnets[--i])); + goto free_hregion; + } + RCU_INIT_POINTER(h->rnets[i], nets); } +#endif h->gc.set = set; spin_lock_init(&h->gc.lock); for (i = 0; i < ahash_numof_locks(hbits); i++) @@ -1682,6 +1753,16 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, t->htable_bits, h->maxelem, set->data, t); return 0; + +#ifdef IP_SET_HASH_WITH_NETS +free_hregion: + ip_set_free(t->hregion); +#endif +free_t: + ip_set_free(t); +free_h: + kfree(h); + return -ENOMEM; } #endif /* IP_SET_EMIT_CREATE */ diff --git a/net/netfilter/ipset/ip_set_hash_ipportnet.c b/net/netfilter/ipset/ip_set_hash_ipportnet.c index 2d6652d43199..195853a25b06 100644 --- a/net/netfilter/ipset/ip_set_hash_ipportnet.c +++ b/net/netfilter/ipset/ip_set_hash_ipportnet.c @@ -138,7 +138,7 @@ hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -398,7 +398,7 @@ hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_ipportnet6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_ipportnet6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_net.c b/net/netfilter/ipset/ip_set_hash_net.c index ce0a9ce5a91f..092f3c9281b8 100644 --- a/net/netfilter/ipset/ip_set_hash_net.c +++ b/net/netfilter/ipset/ip_set_hash_net.c @@ -117,7 +117,7 @@ hash_net4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -291,7 +291,7 @@ hash_net6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_net6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_net6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index 30a655e5c4fd..b44b95f766b7 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -161,7 +161,7 @@ hash_netiface4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), .elem = 1, }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -382,7 +382,7 @@ hash_netiface6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netiface6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netiface6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), .elem = 1, }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netnet.c b/net/netfilter/ipset/ip_set_hash_netnet.c index 8fbe649c9dd3..f7c8a1cc30fc 100644 --- a/net/netfilter/ipset/ip_set_hash_netnet.c +++ b/net/netfilter/ipset/ip_set_hash_netnet.c @@ -149,8 +149,10 @@ hash_netnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; @@ -388,8 +390,10 @@ hash_netnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; diff --git a/net/netfilter/ipset/ip_set_hash_netport.c b/net/netfilter/ipset/ip_set_hash_netport.c index d1a0628df4ef..5de4b511de76 100644 --- a/net/netfilter/ipset/ip_set_hash_netport.c +++ b/net/netfilter/ipset/ip_set_hash_netport.c @@ -133,7 +133,7 @@ hash_netport4_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport4 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport4_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); @@ -353,7 +353,7 @@ hash_netport6_kadt(struct ip_set *set, const struct sk_buff *skb, const struct hash_netport6 *h = set->data; ipset_adtfn adtfn = set->variant->adt[adt]; struct hash_netport6_elem e = { - .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), + .cidr = INIT_CIDR(h->rnets[0], HOST_MASK), }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); diff --git a/net/netfilter/ipset/ip_set_hash_netportnet.c b/net/netfilter/ipset/ip_set_hash_netportnet.c index bf4f91b78e1d..6291532be7a5 100644 --- a/net/netfilter/ipset/ip_set_hash_netportnet.c +++ b/net/netfilter/ipset/ip_set_hash_netportnet.c @@ -157,8 +157,10 @@ hash_netportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netportnet4_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(e.cidr[0]) * 8)) | HOST_MASK; @@ -452,8 +454,10 @@ hash_netportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, struct hash_netportnet6_elem e = { }; struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); - e.cidr[0] = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK); - e.cidr[1] = INIT_CIDR(h->nets[0].cidr[1], HOST_MASK); + rcu_read_lock_bh(); + e.cidr[0] = INIT_CIDR(h->rnets[0], HOST_MASK); + e.cidr[1] = INIT_CIDR(h->rnets[1], HOST_MASK); + rcu_read_unlock_bh(); if (adt == IPSET_TEST) e.ccmp = (HOST_MASK << (sizeof(u8) * 8)) | HOST_MASK; From 712a6f545c359b427daa9a5a782e30d2f8331e25 Mon Sep 17 00:00:00 2001 From: Jozsef Kadlecsik Date: Thu, 30 Jul 2026 20:38:50 +0200 Subject: [PATCH 352/501] netfilter: ipset: switch ext_size to atomic64_t The hash types do not acquire set->lock, they use 'region locking' where only part of the hash table is locked. Parallel inserts and deletes are possible and CPUs can race on ->ext_size update. Switch to atomic64_t. This leaves another bug unresolved: there still can be a race on comment extension re-init. This will be handled in a later commit when converting to rhashtable backend. Fixes: f66ee0410b1c ("netfilter: ipset: Fix "INFO: rcu detected stall in hash_xxx" reports") Signed-off-by: Jozsef Kadlecsik Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 2 +- net/netfilter/ipset/ip_set_bitmap_gen.h | 4 ++-- net/netfilter/ipset/ip_set_core.c | 6 +++--- net/netfilter/ipset/ip_set_hash_gen.h | 2 +- net/netfilter/ipset/ip_set_list_set.c | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index b98331572ad2..cadae9b2578f 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -273,7 +273,7 @@ struct ip_set { /* Number of elements (vs timeout) */ u32 elements; /* Size of the dynamic extensions (vs timeout) */ - size_t ext_size; + atomic64_t ext_size; /* Element data size */ size_t dsize; /* Offsets to extensions in elements */ diff --git a/net/netfilter/ipset/ip_set_bitmap_gen.h b/net/netfilter/ipset/ip_set_bitmap_gen.h index bb9b5bed10e1..226fdf17b683 100644 --- a/net/netfilter/ipset/ip_set_bitmap_gen.h +++ b/net/netfilter/ipset/ip_set_bitmap_gen.h @@ -77,7 +77,7 @@ mtype_flush(struct ip_set *set) mtype_ext_cleanup(set); bitmap_zero(map->members, map->elements); set->elements = 0; - set->ext_size = 0; + atomic64_set(&set->ext_size, 0); } /* Calculate the actual memory size of the set data */ @@ -93,7 +93,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) { const struct mtype *map = set->data; struct nlattr *nested; - size_t memsize = mtype_memsize(map, set->dsize) + set->ext_size; + size_t memsize = mtype_memsize(map, set->dsize) + atomic64_read(&set->ext_size); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 6cfad152d7d1..822a53a7f502 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -350,7 +350,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, size_t len = ext->comment ? strlen(ext->comment) : 0; if (unlikely(c)) { - set->ext_size -= sizeof(*c) + strlen(c->str) + 1; + atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, NULL); kfree_rcu(c, rcu); } @@ -362,7 +362,7 @@ ip_set_init_comment(struct ip_set *set, struct ip_set_comment *comment, if (unlikely(!c)) return; strscpy(c->str, ext->comment, len + 1); - set->ext_size += sizeof(*c) + strlen(c->str) + 1; + atomic64_add(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, c); } EXPORT_SYMBOL_GPL(ip_set_init_comment); @@ -392,7 +392,7 @@ ip_set_comment_free(struct ip_set *set, void *ptr) c = rcu_dereference_protected(comment->c, 1); if (unlikely(!c)) return; - set->ext_size -= sizeof(*c) + strlen(c->str) + 1; + atomic64_sub(sizeof(*c) + strlen(c->str) + 1, &set->ext_size); rcu_assign_pointer(comment->c, NULL); kfree_rcu(c, rcu); } diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index dd31992c915c..8841daf28f01 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -1373,7 +1373,7 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); mtype_ext_size(set, &elements, &ext_size); - memsize = mtype_ahash_memsize(h, t) + ext_size + set->ext_size; + memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size); htable_bits = t->htable_bits; rcu_read_unlock_bh(); diff --git a/net/netfilter/ipset/ip_set_list_set.c b/net/netfilter/ipset/ip_set_list_set.c index 1cef84f15e8c..ca3ef9479e83 100644 --- a/net/netfilter/ipset/ip_set_list_set.c +++ b/net/netfilter/ipset/ip_set_list_set.c @@ -421,7 +421,7 @@ list_set_flush(struct ip_set *set) list_for_each_entry_safe(e, n, &map->members, list) list_set_del(set, e); set->elements = 0; - set->ext_size = 0; + atomic64_set(&set->ext_size, 0); } static void @@ -455,7 +455,7 @@ list_set_head(struct ip_set *set, struct sk_buff *skb) { const struct list_set *map = set->data; struct nlattr *nested; - size_t memsize = list_set_memsize(map, set->dsize) + set->ext_size; + size_t memsize = list_set_memsize(map, set->dsize) + atomic64_read(&set->ext_size); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) From c266769e9ede96b95f9cffc90274da1e25f7a74e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:51 +0200 Subject: [PATCH 353/501] netfilter: ipset: add small wrappers for hash and bucket sizes Preparation patch. Once the ipset hash table is replaced with rhashtable these functions are needed. Add them in extra commit to have reviewable chunks. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 39 +++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index 8841daf28f01..ef586b486f51 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -201,6 +201,8 @@ static const union nf_inet_addr zeromask = {}; #undef mtype_same_set #undef mtype_kadt #undef mtype_uadt +#undef mtype_bucket_size +#undef mtype_hash_size #undef mtype_add #undef mtype_del @@ -246,6 +248,8 @@ static const union nf_inet_addr zeromask = {}; #define mtype_same_set IPSET_TOKEN(MTYPE, _same_set) #define mtype_kadt IPSET_TOKEN(MTYPE, _kadt) #define mtype_uadt IPSET_TOKEN(MTYPE, _uadt) +#define mtype_bucket_size IPSET_TOKEN(MTYPE, _bucket_size) +#define mtype_hash_size IPSET_TOKEN(MTYPE, _hash_size) #define mtype_add IPSET_TOKEN(MTYPE, _add) #define mtype_del IPSET_TOKEN(MTYPE, _del) @@ -1358,6 +1362,24 @@ mtype_test(struct ip_set *set, void *value, const struct ip_set_ext *ext, return ret; } +static u32 mtype_hash_size(const struct htype *h) +{ + const struct htable *t; + u8 htable_bits; + + rcu_read_lock(); + t = rcu_dereference(h->table); + htable_bits = t->htable_bits; + rcu_read_unlock(); + + return jhash_size(htable_bits); +} + +static u32 mtype_bucket_size(const struct htype *h) +{ + return h->bucketsize; +} + /* Reply a HEADER request: fill out the header part of the set */ static int mtype_head(struct ip_set *set, struct sk_buff *skb) @@ -1368,21 +1390,20 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) size_t memsize; u32 elements = 0; size_t ext_size = 0; - u8 htable_bits; rcu_read_lock_bh(); t = rcu_dereference_bh(h->table); mtype_ext_size(set, &elements, &ext_size); memsize = mtype_ahash_memsize(h, t) + ext_size + atomic64_read(&set->ext_size); - htable_bits = t->htable_bits; rcu_read_unlock_bh(); nested = nla_nest_start(skb, IPSET_ATTR_DATA); if (!nested) goto nla_put_failure; - if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, - htonl(jhash_size(htable_bits))) || - nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) + + if (nla_put_net32(skb, IPSET_ATTR_HASHSIZE, htonl(mtype_hash_size(h)))) + goto nla_put_failure; + if (nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem))) goto nla_put_failure; #ifdef IP_SET_HASH_WITH_BITMASK /* if netmask is set to anything other than HOST_MASK we know that the user supplied netmask @@ -1406,8 +1427,9 @@ mtype_head(struct ip_set *set, struct sk_buff *skb) goto nla_put_failure; #endif if (set->flags & IPSET_CREATE_FLAG_BUCKETSIZE) { - if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, h->bucketsize) || - nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval))) + if (nla_put_u8(skb, IPSET_ATTR_BUCKETSIZE, mtype_bucket_size(h))) + goto nla_put_failure; + if (nla_put_net32(skb, IPSET_ATTR_INITVAL, htonl(h->initval))) goto nla_put_failure; } if (nla_put_net32(skb, IPSET_ATTR_REFERENCES, htonl(set->ref)) || @@ -1721,6 +1743,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, INIT_LIST_HEAD(&t->ad); RCU_INIT_POINTER(h->table, t); set->data = h; + #ifndef IP_SET_PROTO_UNDEF if (set->family == NFPROTO_IPV4) { #endif @@ -1749,7 +1772,7 @@ IPSET_TOKEN(HTYPE, _create)(struct net *net, struct ip_set *set, #endif } pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", - set->name, jhash_size(t->htable_bits), + set->name, mtype_hash_size(h), t->htable_bits, h->maxelem, set->data, t); return 0; From cdd97fae0e96fc0f78418b93ca864f98cd63e99a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:52 +0200 Subject: [PATCH 354/501] netfilter: ipset: add and use mtype_del_cidr_all helper Reduces size of upcoming rhashtable conversion. Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipset/ip_set_hash_gen.h | 34 +++++++++++++-------------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h index ef586b486f51..f00c82acd7f0 100644 --- a/net/netfilter/ipset/ip_set_hash_gen.h +++ b/net/netfilter/ipset/ip_set_hash_gen.h @@ -195,6 +195,7 @@ static const union nf_inet_addr zeromask = {}; #undef mtype_ext_cleanup #undef mtype_add_cidr #undef mtype_del_cidr +#undef mtype_del_cidr_all #undef mtype_ahash_memsize #undef mtype_flush #undef mtype_destroy @@ -242,6 +243,7 @@ static const union nf_inet_addr zeromask = {}; #define mtype_ext_cleanup IPSET_TOKEN(MTYPE, _ext_cleanup) #define mtype_add_cidr IPSET_TOKEN(MTYPE, _add_cidr) #define mtype_del_cidr IPSET_TOKEN(MTYPE, _del_cidr) +#define mtype_del_cidr_all IPSET_TOKEN(MTYPE, _del_cidr_all) #define mtype_ahash_memsize IPSET_TOKEN(MTYPE, _ahash_memsize) #define mtype_flush IPSET_TOKEN(MTYPE, _flush) #define mtype_destroy IPSET_TOKEN(MTYPE, _destroy) @@ -410,6 +412,17 @@ mtype_del_cidr(struct ip_set *set, struct htype *h, u8 cidr, u8 n) } #endif +static void +mtype_del_cidr_all(struct ip_set *set, struct htype *h, const struct mtype_elem *data) +{ +#ifdef IP_SET_HASH_WITH_NETS + int k; + + for (k = 0; k < IPSET_NET_COUNT; k++) + mtype_del_cidr(set, h, DCIDR_GET(data->cidr, k), k); +#endif +} + /* Calculate the actual memory size of the set data */ static size_t mtype_ahash_memsize(const struct htype *h, const struct htable *t) @@ -551,9 +564,6 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) struct mtype_elem *data; u32 i, j, d; size_t dsize = set->dsize; -#ifdef IP_SET_HASH_WITH_NETS - u8 k; -#endif u8 pos, htable_bits = t->htable_bits; spin_lock_bh(&t->hregion[r].lock); @@ -574,11 +584,7 @@ mtype_gc_do(struct ip_set *set, struct htype *h, struct htable *t, u32 r) pr_debug("expired %u/%u\n", i, j); clear_bit(j, n->used); smp_mb__after_atomic(); -#ifdef IP_SET_HASH_WITH_NETS - for (k = 0; k < IPSET_NET_COUNT; k++) - mtype_del_cidr(set, h, - DCIDR_GET(data->cidr, k), k); -#endif + mtype_del_cidr_all(set, h, data); t->hregion[r].elements--; ip_set_ext_destroy(set, data); d++; @@ -1004,11 +1010,7 @@ mtype_add(struct ip_set *set, void *value, const struct ip_set_ext *ext, j = 0; data = ahash_data(n, j, set->dsize); if (!deleted) { -#ifdef IP_SET_HASH_WITH_NETS - for (i = 0; i < IPSET_NET_COUNT; i++) - mtype_del_cidr(set, h, - DCIDR_GET(data->cidr, i), i); -#endif + mtype_del_cidr_all(set, h, data); ip_set_ext_destroy(set, data); t->hregion[r].elements--; } @@ -1163,11 +1165,7 @@ mtype_del(struct ip_set *set, void *value, const struct ip_set_ext *ext, if (i + 1 == pos) smp_store_release(&n->pos, --pos); t->hregion[r].elements--; -#ifdef IP_SET_HASH_WITH_NETS - for (j = 0; j < IPSET_NET_COUNT; j++) - mtype_del_cidr(set, h, - DCIDR_GET(d->cidr, j), j); -#endif + mtype_del_cidr_all(set, h, d); ip_set_ext_destroy(set, data); if (t->resizing && ext && ext->target) { From 7defddefa95b3699b05b23bf3ceea94d872509bb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 30 Jul 2026 20:38:53 +0200 Subject: [PATCH 355/501] netfilter: ipset: switch to rcu work In the initial ipset rhashtable conversion RFC series syzbot reported following splat: BUG: sleeping function [..] at kernel/irq_work.c:289 in_atomic(): 1, [..] irq_work_sync.. kernel/irq_work.c:289 rhashtable_free_and_destroy.. lib/rhashtable.c:1295 hash_netport4_destroy.. net/netfilter/ipset/ip_set_hash_gen.h:420 ip_set_destroy_set_rcu.. net/netfilter/ipset/ip_set_core.c:1169 rcu_core.. kernel/rcu/tree.c:2897 This is because post-rhashtable-conversion hash implementation needs to schedule in the destroy callback. At this time this isn't allowed. Replace existing call_rcu() based destruction with rcu_work api. Also allows to undo split of set destruction and gc work cancelling in a future patch. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/ipset/ip_set.h | 4 +-- net/netfilter/ipset/ip_set_core.c | 46 ++++++++++++++++---------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h index cadae9b2578f..c46864cc6623 100644 --- a/include/linux/netfilter/ipset/ip_set.h +++ b/include/linux/netfilter/ipset/ip_set.h @@ -244,8 +244,8 @@ extern void ip_set_type_unregister(struct ip_set_type *set_type); /* A generic IP set */ struct ip_set { - /* For call_cru in destroy */ - struct rcu_head rcu; + /* for set destruction */ + struct rcu_work rwork; /* The name of the set */ char name[IPSET_MAXNAMELEN]; /* Lock protecting the set data */ diff --git a/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c index 822a53a7f502..543851a923d0 100644 --- a/net/netfilter/ipset/ip_set_core.c +++ b/net/netfilter/ipset/ip_set_core.c @@ -25,6 +25,7 @@ static LIST_HEAD(ip_set_type_list); /* all registered set types */ static DEFINE_MUTEX(ip_set_type_mutex); /* protects ip_set_type_list */ static DEFINE_RWLOCK(ip_set_ref_lock); /* protects the set refs */ +static struct workqueue_struct *ipset_destroy_wq; struct ip_set_net { struct ip_set * __rcu *ip_set_list; /* all individual sets */ @@ -1178,22 +1179,26 @@ ip_set_setname_policy[IPSET_ATTR_CMD_MAX + 1] = { .len = IPSET_MAXNAMELEN - 1 }, }; -/* In order to return quickly when destroying a single set, it is split - * into two stages: - * - Cancel garbage collector - * - Destroy the set itself via call_rcu() - */ - static void -ip_set_destroy_set_rcu(struct rcu_head *head) +destroy_and_free_set(struct ip_set *set) { - struct ip_set *set = container_of(head, struct ip_set, rcu); - set->variant->destroy(set); module_put(set->type->me); kfree(set); } +/* In order to return quickly when destroying a single set, + * destruction is done asynchronously via work queues. + */ +static void +ip_set_destroy_set_work(struct work_struct *work) +{ + struct ip_set *set = container_of(to_rcu_work(work), + struct ip_set, rwork); + + destroy_and_free_set(set); +} + static void _destroy_all_sets(struct ip_set_net *inst) { @@ -1283,7 +1288,8 @@ static int ip_set_destroy(struct sk_buff *skb, const struct nfnl_info *info, /* Must wait for flush to be really finished */ rcu_barrier(); } - call_rcu(&s->rcu, ip_set_destroy_set_rcu); + INIT_RCU_WORK(&s->rwork, ip_set_destroy_set_work); + queue_rcu_work(ipset_destroy_wq, &s->rwork); } return 0; out: @@ -2421,18 +2427,23 @@ static struct pernet_operations ip_set_net_ops = { static int __init ip_set_init(void) { - int ret = register_pernet_subsys(&ip_set_net_ops); + int ret; + ipset_destroy_wq = alloc_ordered_workqueue("ipset_destroy_wq", 0); + if (!ipset_destroy_wq) + return -ENOMEM; + + ret = register_pernet_subsys(&ip_set_net_ops); if (ret) { pr_err("ip_set: cannot register pernet_subsys.\n"); - return ret; + goto out_wq; } ret = nfnetlink_subsys_register(&ip_set_netlink_subsys); if (ret != 0) { pr_err("ip_set: cannot register with nfnetlink.\n"); unregister_pernet_subsys(&ip_set_net_ops); - return ret; + goto out_wq; } ret = nf_register_sockopt(&so_set); @@ -2440,10 +2451,13 @@ ip_set_init(void) pr_err("SO_SET registry failed: %d\n", ret); nfnetlink_subsys_unregister(&ip_set_netlink_subsys); unregister_pernet_subsys(&ip_set_net_ops); - return ret; + goto out_wq; } return 0; +out_wq: + destroy_workqueue(ipset_destroy_wq); + return ret; } static void __exit @@ -2453,9 +2467,7 @@ ip_set_fini(void) nfnetlink_subsys_unregister(&ip_set_netlink_subsys); unregister_pernet_subsys(&ip_set_net_ops); - /* Wait for call_rcu() in destroy */ - rcu_barrier(); - + destroy_workqueue(ipset_destroy_wq); pr_debug("these are the famous last words\n"); } From 646922a0379496154e8c8faca4f8e2fd9100cacc Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 30 Jul 2026 21:35:05 +0300 Subject: [PATCH 356/501] ipvs: avoid out-of-bounds write in ip_vs_nat_icmp Sashiko warns that local attacker can modify the packet while it is processed by IPVS. Some places read the IP ihl field multiple times which can cause out-of-bounds access. One such place is ip_vs_nat_icmp where we can write after the validated area. Fix it by providing ciph argument just like it is done for IPv6 and use ciph->len as offset to the embedded transport header. Modify some IPv4 header checks by reading the ihl field only once. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Link: https://sashiko.dev/#/patchset/20260722101517.36313-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 2 +- net/netfilter/ipvs/ip_vs_core.c | 69 +++++++++++++++++---------------- net/netfilter/ipvs/ip_vs_xmit.c | 2 +- 3 files changed, 37 insertions(+), 36 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index e6ca930a3507..1235f1934e94 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -2062,7 +2062,7 @@ static inline bool ip_vs_conn_use_hash2(struct ip_vs_conn *cp) void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int dir, unsigned int toff, - bool has_ports); + bool has_ports, struct ip_vs_iphdr *ciph); #ifdef CONFIG_IP_VS_IPV6 void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 6b79e0c4d9e2..0bdaeb4ed61e 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -925,28 +925,27 @@ static int ip_vs_route_me_harder(struct netns_ipvs *ipvs, int af, */ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, struct ip_vs_conn *cp, int inout, unsigned int toff, - bool has_ports) + bool has_ports, struct ip_vs_iphdr *ciph) { struct iphdr *iph = ip_hdr(skb); struct icmphdr *icmph = (struct icmphdr *)(skb->data + toff); - struct iphdr *ciph = (struct iphdr *)(icmph + 1); - unsigned int coff __maybe_unused = toff + sizeof(struct icmphdr); + struct iphdr *cih = (struct iphdr *)(icmph + 1); if (inout) { iph->saddr = cp->vaddr.ip; ip_send_check(iph); - ciph->daddr = cp->vaddr.ip; - ip_send_check(ciph); + cih->daddr = cp->vaddr.ip; + ip_send_check(cih); } else { iph->daddr = cp->daddr.ip; ip_send_check(iph); - ciph->saddr = cp->daddr.ip; - ip_send_check(ciph); + cih->saddr = cp->daddr.ip; + ip_send_check(cih); } /* the TCP/UDP/SCTP port */ if (has_ports) { - __be16 *ports = (void *)ciph + ciph->ihl*4; + __be16 *ports = (void *)(skb->data + ciph->len); if (inout) ports[1] = cp->vport; @@ -960,10 +959,10 @@ void ip_vs_nat_icmp(struct sk_buff *skb, struct ip_vs_protocol *pp, skb->ip_summed = CHECKSUM_UNNECESSARY; if (inout) - IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered outgoing ICMP"); else - IP_VS_DBG_PKT(11, AF_INET, pp, skb, coff, + IP_VS_DBG_PKT(11, AF_INET, pp, skb, ciph->off, "Forwarding altered incoming ICMP"); } @@ -1056,7 +1055,7 @@ static int handle_response_icmp(int af, struct sk_buff *skb, ip_vs_nat_icmp_v6(skb, pp, cp, 1, toff, has_ports, ciph); else #endif - ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports); + ip_vs_nat_icmp(skb, pp, cp, 1, toff, has_ports, ciph); if (ip_vs_route_me_harder(cp->ipvs, af, skb, hooknum)) goto out; @@ -1092,7 +1091,7 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, struct ip_vs_iphdr ciph; struct ip_vs_conn *cp; struct ip_vs_protocol *pp; - unsigned int offset, ihl; + unsigned int offset; union nf_inet_addr snet; *related = 1; @@ -1105,7 +1104,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; } - ihl = ipvsh->len; offset = ipvsh->len; ic = skb_header_pointer(skb, offset, sizeof(_icmph), &_icmph); if (ic == NULL) @@ -1131,11 +1129,15 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, /* Now find the contained IP header */ offset += sizeof(_icmph); - cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) + if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph)) return NF_ACCEPT; /* The packet looks wrong, ignore */ - pp = ip_vs_proto_get(cih->protocol); + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); + if (!(cih && cih->version == 4 && + ciph.len - ciph.off >= sizeof(struct iphdr))) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + + pp = ip_vs_proto_get(ciph.protocol); if (!pp) return NF_ACCEPT; @@ -1146,8 +1148,6 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, "Checking outgoing ICMP for"); - ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, true, &ciph); - /* The embedded headers contain source and dest in reverse order */ cp = INDIRECT_CALL_1(pp->conn_out_get, ip_vs_conn_out_get_proto, ipvs, AF_INET, skb, &ciph); @@ -1155,8 +1155,8 @@ static int ip_vs_out_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, return NF_ACCEPT; snet.ip = ipvsh->saddr.ip; - return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, ihl, - hooknum); + return handle_response_icmp(AF_INET, skb, &snet, cp, pp, &ciph, + ipvsh->len, hooknum); } #ifdef CONFIG_IP_VS_IPV6 @@ -1803,10 +1803,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Now find the contained IP header */ offset += sizeof(_icmph); cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) + if (!cih) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + hlen_ipip = cih->ihl * 4; + if (!(cih->version == 4 && hlen_ipip >= sizeof(struct iphdr))) return NF_ACCEPT; /* The packet looks wrong, ignore */ raddr = (union nf_inet_addr *)&cih->daddr; - hlen_ipip = cih->ihl * 4; /* Special case for errors for IPIP/UDP/GRE tunnel packets */ tunnel = false; @@ -1823,9 +1825,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (!dest || dest->tun_type != IP_VS_CONN_F_TUNNEL_TYPE_IPIP) return NF_ACCEPT; offset += hlen_ipip; - cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); - if (!(cih && cih->version == 4 && cih->ihl >= 5)) - return NF_ACCEPT; /* The packet looks wrong, ignore */ tunnel = true; } else if ((cih->protocol == IPPROTO_UDP || /* Can be UDP encap */ cih->protocol == IPPROTO_GRE) && /* Can be GRE encap */ @@ -1850,21 +1849,25 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, /* Skip IP and UDP/GRE tunnel headers */ offset = offset2 + ulen; /* Now we should be at the original IP header */ - cih = skb_header_pointer(skb, offset, sizeof(_ciph), - &_ciph); - if (cih && cih->version == 4 && cih->ihl >= 5 && - iproto == IPPROTO_IPIP) + if (iproto == IPPROTO_IPIP) tunnel = true; else return NF_ACCEPT; } } - pd = ip_vs_proto_data_get(ipvs, cih->protocol); + if (!ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph)) + return NF_ACCEPT; + pd = ip_vs_proto_data_get(ipvs, ciph.protocol); if (!pd) return NF_ACCEPT; pp = pd->pp; + cih = skb_header_pointer(skb, offset, sizeof(_ciph), &_ciph); + if (!(cih && cih->version == 4 && + ciph.len - ciph.off >= sizeof(struct iphdr))) + return NF_ACCEPT; /* The packet looks wrong, ignore */ + /* Is the embedded protocol header present? */ if (unlikely(cih->frag_off & htons(IP_OFFSET) && !pp->dont_defrag)) return NF_ACCEPT; @@ -1872,9 +1875,6 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, IP_VS_DBG_PKT(11, AF_INET, pp, skb, offset, "Checking incoming ICMP for"); - offset2 = offset; - ip_vs_fill_iph_skb_icmp(AF_INET, skb, offset, !tunnel, &ciph); - /* The embedded headers contain source and dest in reverse order. * For IPIP/UDP/GRE tunnel this is error for request, not for reply. */ @@ -1904,11 +1904,12 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, } if (tunnel) { - unsigned int hlen_orig = cih->ihl * 4; + unsigned int hlen_orig = ciph.len - ciph.off; __be32 info = ic->un.gateway; __u8 type = ic->type; __u8 code = ic->code; + offset2 = offset; /* Update the MTU */ if (ic->type == ICMP_DEST_UNREACH && ic->code == ICMP_FRAG_NEEDED) { diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 0b0c5304993a..c4508f3f43dd 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -1580,7 +1580,7 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, if (skb_cow(skb, rt->dst.dev->hard_header_len)) goto tx_error; - ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports); + ip_vs_nat_icmp(skb, pp, cp, 0, toff, has_ports, ciph); /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; From 99609cb0aa789c8d071050ce8579989551882cc6 Mon Sep 17 00:00:00 2001 From: Julian Anastasov Date: Thu, 30 Jul 2026 21:35:06 +0300 Subject: [PATCH 357/501] ipvs: return the csum validation for forward hook Sashiko notes that playing games with the skb dst and rt flags instead of providing hooknum is not a good idea when validating the checksums. Also, skipping checksum validation for FORWARD packets risk silent data corruption, even if the only user is the FTP-CMD packets coming from the real server. Sashiko also noticed that by using common checksum helper in the previous commit we actually fixed old bug where the TCP/UDP checksum for IPv6 on CHECKSUM_COMPLETE was not validated correctly. Fixes: e876b75b9020 ("ipvs: fix the checksum validations") Link: https://sashiko.dev/#/patchset/20260722211420.153933-1-pablo%40netfilter.org Link: https://sashiko.dev/#/patchset/20260727185024.67534-1-ja%40ssi.bg Link: https://sashiko.dev/#/patchset/20260728202520.59179-1-ja%40ssi.bg Signed-off-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- include/net/ip_vs.h | 19 +++++-------------- net/netfilter/ipvs/ip_vs_proto_sctp.c | 2 +- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 1235f1934e94..d2813eb795be 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -25,9 +25,7 @@ #include /* for union nf_inet_addr */ #include #include /* for struct ipv6hdr */ -#include #include -#include #if IS_ENABLED(CONFIG_NF_CONNTRACK) #include #endif @@ -2095,30 +2093,23 @@ static inline __wsum ip_vs_check_diff2(__be16 old, __be16 new, __wsum oldsum) return csum_partial(diff, sizeof(diff), oldsum); } -static inline bool ip_vs_checksum_needed(struct sk_buff *skb, int af) +static inline bool ip_vs_checksum_needed(struct sk_buff *skb) { /* Checksum unnecessary or already validated? */ if (skb_csum_unnecessary(skb)) return false; - /* LOCAL_OUT ? */ - if (!skb->dev || skb->dev->flags & IFF_LOOPBACK) + /* Locally generated ? */ + if (!skb->dev) return false; - /* !LOCAL_IN (FORWARD) ? */ - if (af == AF_INET6) { - if (!(dst_rt6_info(skb_dst(skb))->rt6i_flags & RTF_LOCAL)) - return false; - } else { - if (!(skb_rtable(skb)->rt_flags & RTCF_LOCAL)) - return false; - } return true; } static inline bool ip_vs_checksum_common_check(struct sk_buff *skb, int offset, int proto, int af) { - if (!ip_vs_checksum_needed(skb, af)) + if (!ip_vs_checksum_needed(skb)) return true; + /* Validate csum even for FORWARD */ return !nf_checksum(skb, NF_INET_LOCAL_IN, offset, proto, af); } diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 3dbd3096e163..c80567c73469 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -193,7 +193,7 @@ sctp_csum_check(int af, struct sk_buff *skb, struct ip_vs_protocol *pp, struct sctphdr *sh; __le32 cmp, val; - if (!ip_vs_checksum_needed(skb, af)) + if (!ip_vs_checksum_needed(skb)) return 1; sh = (struct sctphdr *)(skb->data + sctphoff); cmp = sh->checksum; From dcba277d119af323c267d1f27f61b868dac62753 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 27 Jul 2026 16:28:38 +0200 Subject: [PATCH 358/501] s390/dasd: Fix path verification interrupted by concurrent dasd_sleep_on_immediatly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When all channel paths to a DASD device are lost and subsequently recovered, the path event handler starts one IO per path via dasd_sleep_on_immediatly() to execute read configuration data (RCD) with high priority. dasd_sleep_on_immediatly() works by terminating the currently running request before inserting the new request. If a concurrent caller, such as the attention handler dasd_eckd_check_attention_work() or the summary unit check handler summary_unit_check_handling_work(), also calls dasd_sleep_on_immediatly() while a path verification RCD is in progress, the RCD gets terminated. The problem is that a terminated request transitions from CLEARED to TERMINATED without going through the normal retry path in __dasd_device_process_ccw_queue. The RCD therefore returns -EIO, and the affected paths remain non-operational after recovery. RCD CQRs used for path verification already carry the DASD_CQR_VERIFY_PATH flag. Extend _dasd_term_running_cqr() to check this flag: instead of terminating such a request, return -EAGAIN. In dasd_sleep_on_immediatly(), loop on -EAGAIN with a short sleep, waiting for the path verification request to complete before inserting the new request. This is consistent with the already indefinite wait_event() that dasd_sleep_on_immediatly() uses for its own request, and all other callers (attention handler, summary unit check handler, reserve/release/steal-lock) benefit automatically without requiring changes. Reviewed-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-2-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 3181c06d91ce..d8d912a3b3fe 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -2511,6 +2512,13 @@ static inline int _dasd_term_running_cqr(struct dasd_device *device) if (list_empty(&device->ccw_queue)) return 0; cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); + /* + * Path verification requests must not be terminated. They are critical + * for bringing paths back online. Terminating them would cause rc=-EIO + * because CLEARED requests skip the retry path. + */ + if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + return -EAGAIN; rc = device->discipline->term_IO(cqr); if (!rc) /* @@ -2535,7 +2543,11 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) return -EIO; } spin_lock_irq(get_ccwdev_lock(device->cdev)); - rc = _dasd_term_running_cqr(device); + while ((rc = _dasd_term_running_cqr(device)) == -EAGAIN) { + spin_unlock_irq(get_ccwdev_lock(device->cdev)); + msleep(1); + spin_lock_irq(get_ccwdev_lock(device->cdev)); + } if (rc) { spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; From 9973026f572db6b67570cadc30942f3014e41079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20H=C3=B6ppner?= Date: Mon, 27 Jul 2026 16:28:39 +0200 Subject: [PATCH 359/501] s390/dasd: Fix potential NULL pointer dereference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dasd_release_space() checks the implementation of the is_ese() discipline function before calling it to determine if a given device is an ESE DASD. The current usage of the logical AND operator will lead to a NULL pointer dereference as the function is called even if the function pointer is NULL. Fix this by using the logical OR operator. Fixes: 91dc4a197569 ("s390/dasd: Add new ioctl to release space") Cc: stable@vger.kernel.org # v5.3+ Reported-by: Vasily Gorbik Acked-by: Eduard Shishkin Reviewed-by: Stefan Haberland Signed-off-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-3-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_ioctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c index c85ee42732a3..e5b8b413f5ab 100644 --- a/drivers/s390/block/dasd_ioctl.c +++ b/drivers/s390/block/dasd_ioctl.c @@ -324,7 +324,7 @@ static int dasd_ioctl_check_format(struct block_device *bdev, void __user *argp) static int dasd_release_space(struct dasd_device *device, struct format_data_t *rdata) { - if (!device->discipline->is_ese && !device->discipline->is_ese(device)) + if (!device->discipline->is_ese || !device->discipline->is_ese(device)) return -ENOTSUPP; if (!device->discipline->release_space) return -ENOTSUPP; From 7f40b346462f563a0d6e841a77b5163d2a882a04 Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Mon, 27 Jul 2026 16:28:40 +0200 Subject: [PATCH 360/501] s390/dasd: Fix undersized format-check buffer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fmt_buffer_size in dasd_eckd_check_device_format() is declared as int, even though one of the multiplicands, sizeof(struct eckd_count), is a size_t. The expression trkcount * rpt_max * sizeof(struct eckd_count) is therefore correctly evaluated at 64-bit width, but the result is silently truncated when it is stored back into the 32-bit fmt_buffer_size variable. For a sufficiently large track range (start_unit/stop_unit are caller-controlled) this truncation yields a buffer size far smaller than the number of tracks actually requested. kzalloc() then succeeds with an undersized allocation, while the subsequent channel program build still operates on the untruncated track count and writes past the end of that buffer. Compute the buffer size with check_mul_overflow() and keep it in a size_t, so that a value that no longer fits results in -EINVAL instead of a silently truncated allocation size. Fixes: 8fd575200db5 ("s390/dasd: Add new ioctl BIODASDCHECKFMT") Cc: stable@vger.kernel.org #4.7 Reviewed-by: Jan Höppner Signed-off-by: Stefan Haberland Link: https://patch.msgid.link/20260727142840.567286-4-sth@linux.ibm.com Signed-off-by: Jens Axboe --- drivers/s390/block/dasd_eckd.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 74fe73b5738a..d356a9f8f016 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -3475,11 +3476,11 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, { struct dasd_eckd_private *private = base->private; struct eckd_count *fmt_buffer; - struct irb irb; + size_t fmt_buffer_size; + unsigned int trkcount; int rpt_max, rpt_exp; - int fmt_buffer_size; + struct irb irb; int trk_per_cyl; - int trkcount; int tpm = 0; int rc; @@ -3490,7 +3491,9 @@ static int dasd_eckd_check_device_format(struct dasd_device *base, rpt_exp = recs_per_track(&private->rdc_data, 0, cdata->expect.blksize); trkcount = cdata->expect.stop_unit - cdata->expect.start_unit + 1; - fmt_buffer_size = trkcount * rpt_max * sizeof(struct eckd_count); + if (check_mul_overflow(trkcount, rpt_max, &fmt_buffer_size) || + check_mul_overflow(fmt_buffer_size, sizeof(struct eckd_count), &fmt_buffer_size)) + return -EINVAL; fmt_buffer = kzalloc(fmt_buffer_size, GFP_KERNEL | GFP_DMA); if (!fmt_buffer) From 28254722a459938d97150d3b0712b81e06d0645e Mon Sep 17 00:00:00 2001 From: Carlos Llamas Date: Thu, 30 Jul 2026 22:15:07 +0000 Subject: [PATCH 361/501] selinux: bpf: check SBLABEL_MNT before isec init selinux_inode_init_security() marks the isec as initialized before checking if mount labeling is supported (SBLABEL_MNT). This was fine until commit 9722955b5430 ("bpf: Add simple xattr support to bpffs"), where genfscon bpffs mounts fail the SBLABEL_MNT check as expected (no xattrs) and yet leave the isec->initialized. This breaks subsequent calls to inode_doinit_with_dentry(). Do the SBLABEL_MNT check before the inode security is initialized. Cc: stable@vger.kernel.org Closes: https://lore.kernel.org/all/akWdcp6P0FkNDzBk@google.com/ Fixes: 9722955b5430 ("bpf: Add simple xattr support to bpffs") Acked-by: Stephen Smalley Signed-off-by: Carlos Llamas Signed-off-by: Paul Moore --- security/selinux/hooks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8d6945edae7a..18dd28b2bb13 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2974,6 +2974,10 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, sbsec = selinux_superblock(dir->i_sb); + if (!selinux_initialized() || + !(sbsec->flags & SBLABEL_MNT)) + return -EOPNOTSUPP; + newsid = crsec->create_sid; newsclass = inode_mode_to_security_class(inode->i_mode); rc = selinux_determine_inode_label(crsec, dir, qstr, newsclass, &newsid); @@ -2988,10 +2992,6 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, isec->initialized = LABEL_INITIALIZED; } - if (!selinux_initialized() || - !(sbsec->flags & SBLABEL_MNT)) - return -EOPNOTSUPP; - xattr = lsm_get_xattr_slot(xattrs, xattr_count); if (xattr) { rc = security_sid_to_context_force(newsid, From e053b624f5d36669756990743346157be9f68c34 Mon Sep 17 00:00:00 2001 From: Achilles Gaikwad Date: Tue, 7 Jul 2026 11:23:05 -0400 Subject: [PATCH 362/501] NFSv4.2: fix nfs4_listxattr size accounting A call to listxattr() with a buffer size of 0 returns the actual size of the buffer needed for a subsequent call. On an NFSv4.2 mount this triggers the following oops: [ 399.768687] BUG: kernel NULL pointer dereference, address: 0000000000000000 [ 399.768705] RIP: 0010:_copy_from_pages+0x44/0xe0 [ 399.768722] Call Trace: [ 399.768723] nfs4_xattr_alloc_entry+0x1bf/0x1e0 [ 399.768730] nfs4_xattr_cache_set_list+0x43/0x1f0 [ 399.768731] nfs4_listxattr+0x21f/0x250 [ 399.768733] vfs_listxattr+0x55/0xa0 [ 399.768736] listxattr+0x23/0x160 [ 399.768737] path_listxattrat+0xba/0x1e0 [ 399.768739] do_syscall_64+0xe2/0x680 security_inode_listsecurity() (via the xattr_list_one() helper) now decrements the remaining size even when the buffer pointer is NULL, so in the size-query case, 'left' underflows to a huge size_t value. As a result, nfs4_listxattr_nfs4_user() treats the NULL buffer as a real one, leading to a NULL pointer dereference in _copy_from_pages(). security_inode_listsecurity() does not return the number of bytes it added to the list, so the code derived it as 'size - error - left'. That is also wrong in the size-query case: the generic_listxattr() contribution is only subtracted from 'left' when a buffer is present. Thus, the query result comes up short by exactly that contribution (e.g., "system.nfs4_acl" on a mount with ACL support), and a caller that allocates the returned size gets -ERANGE on the subsequent call. Declare 'left' as ssize_t, use a scratch copy to measure security hook consumption, and only decrement 'left' if a buffer is present. Fixes: f71ece9712b7 ("security,fs,nfs,net: update security_inode_listsecurity() interface") Suggested-by: Paul Moore Signed-off-by: Achilles Gaikwad Reviewed-by: Paul Moore Signed-off-by: Anna Schumaker --- fs/nfs/nfs4proc.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 7d98e9a98580..5709c6fea85b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -10596,7 +10596,8 @@ const struct nfs4_minor_version_ops *nfs_v4_minor_ops[] = { static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) { ssize_t error, error2, error3; - size_t left = size; + ssize_t left = size; + ssize_t left2; error = generic_listxattr(dentry, list, left); if (error < 0) @@ -10606,10 +10607,13 @@ static ssize_t nfs4_listxattr(struct dentry *dentry, char *list, size_t size) left -= error; } - error2 = security_inode_listsecurity(d_inode(dentry), &list, &left); + left2 = left; + error2 = security_inode_listsecurity(d_inode(dentry), &list, &left2); if (error2 < 0) return error2; - error2 = size - error - left; + error2 = left - left2; + if (list) + left -= error2; error3 = nfs4_listxattr_nfs4_user(d_inode(dentry), list, left); if (error3 < 0) From 5cb5c2f4a8373eb4c8fdbfdc857bb28f28f71fe0 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Wed, 22 Jul 2026 23:56:46 +0000 Subject: [PATCH 363/501] mshv: Use kfree_rcu in mshv_portid_free mshv_portid_free() uses synchronize_rcu() followed by kfree() to reclaim port table entries. This blocks the caller until a full RCU grace period elapses, which is unnecessary since the same module already uses the non-blocking kfree_rcu() pattern in mshv_port_table_fini(). Replace with kfree_rcu() to avoid the blocking wait and keep the reclamation strategy consistent across the file. Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_portid_table.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/hv/mshv_portid_table.c b/drivers/hv/mshv_portid_table.c index 6f59b3e37624..0d632507ed21 100644 --- a/drivers/hv/mshv_portid_table.c +++ b/drivers/hv/mshv_portid_table.c @@ -62,8 +62,7 @@ mshv_portid_free(int port_id) WARN_ON(!info); idr_unlock(&port_table_idr); - synchronize_rcu(); - kfree(info); + kfree_rcu(info, portbl_rcu); } int From 0762262ac3e70f65b3bb843fe892f8bac1562d08 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:15 +0000 Subject: [PATCH 364/501] mshv: Fix race in mshv_irqfd_deassign mshv_irqfd_deactivate() and the hlist traversal of pt_irqfds_list require pt->pt_irqfds_lock to be held, but mshv_irqfd_deassign() omits it. This races with the EPOLLHUP path in mshv_irqfd_wakeup(), which does take the lock before calling mshv_irqfd_deactivate(). Additionally, mshv_irqfd_deactivate() uses hlist_del() which poisons the node pointers rather than resetting them. Since mshv_irqfd_is_active() relies on hlist_unhashed() (checks pprev == NULL), a poisoned node still appears active. If a concurrent path calls mshv_irqfd_deactivate() again on the same irqfd, the guard fails to prevent a double hlist_del() on poisoned pointers. Fix both issues: - Add the missing spin_lock_irq/spin_unlock_irq around the list traversal in mshv_irqfd_deassign(), matching mshv_irqfd_release(). - Use hlist_del_init() instead of hlist_del() so the node is properly marked as unhashed after removal, making the is_active guard reliable. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 90959f639dc3..5995a62aff8d 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -284,7 +284,7 @@ static void mshv_irqfd_deactivate(struct mshv_irqfd *irqfd) if (!mshv_irqfd_is_active(irqfd)) return; - hlist_del(&irqfd->irqfd_hnode); + hlist_del_init(&irqfd->irqfd_hnode); queue_work(irqfd_cleanup_wq, &irqfd->irqfd_shutdown); } @@ -541,13 +541,14 @@ static int mshv_irqfd_deassign(struct mshv_partition *pt, if (IS_ERR(eventfd)) return PTR_ERR(eventfd); + spin_lock_irq(&pt->pt_irqfds_lock); hlist_for_each_entry_safe(irqfd, n, &pt->pt_irqfds_list, irqfd_hnode) { if (irqfd->irqfd_eventfd_ctx == eventfd && irqfd->irqfd_irqnum == args->gsi) - mshv_irqfd_deactivate(irqfd); } + spin_unlock_irq(&pt->pt_irqfds_lock); eventfd_ctx_put(eventfd); From 0289a67cd70bf9d3807e289f4efd643e16a6c6b4 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:43:43 +0000 Subject: [PATCH 365/501] mshv: Fix level-triggered check on uninitialized data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mshv_irqfd_assign(), the level-triggered validation for resample irqfds checks irqfd_lapic_irq.lapic_control.level_triggered before mshv_irqfd_update() has populated the field. Since the irqfd struct is zero-allocated, level_triggered is always 0 at that point, causing the check to always reject resample irqfds with -EINVAL. This makes level-triggered interrupt resampling — used to avoid interrupt storms with assigned devices — completely non-functional. Move the check after the mshv_irqfd_update() call, which resolves the IRQ routing entry and populates irqfd_lapic_irq with the actual trigger mode. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 5995a62aff8d..047e5bd43238 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -473,18 +473,6 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, init_poll_funcptr(&irqfd->irqfd_polltbl, mshv_irqfd_queue_proc); spin_lock_irq(&pt->pt_irqfds_lock); -#if IS_ENABLED(CONFIG_X86) - if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && - !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { - /* - * Resample Fd must be for level triggered interrupt - * Otherwise return with failure - */ - spin_unlock_irq(&pt->pt_irqfds_lock); - ret = -EINVAL; - goto fail; - } -#endif ret = 0; hlist_for_each_entry(tmp, &pt->pt_irqfds_list, irqfd_hnode) { if (irqfd->irqfd_eventfd_ctx != tmp->irqfd_eventfd_ctx) @@ -497,6 +485,21 @@ static int mshv_irqfd_assign(struct mshv_partition *pt, idx = srcu_read_lock(&pt->pt_irq_srcu); mshv_irqfd_update(pt, irqfd); + +#if IS_ENABLED(CONFIG_X86) + if (args->flags & BIT(MSHV_IRQFD_BIT_RESAMPLE) && + !irqfd->irqfd_lapic_irq.lapic_control.level_triggered) { + /* + * Resample Fd must be for level triggered interrupt + * Otherwise return with failure + */ + spin_unlock_irq(&pt->pt_irqfds_lock); + srcu_read_unlock(&pt->pt_irq_srcu, idx); + ret = -EINVAL; + goto fail; + } +#endif + hlist_add_head(&irqfd->irqfd_hnode, &pt->pt_irqfds_list); spin_unlock_irq(&pt->pt_irqfds_lock); From f546be6a19d24d02be576d8617cb26c7acb61594 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:44:37 +0000 Subject: [PATCH 366/501] mshv: Fix missing error code on VP allocation failure In mshv_partition_ioctl_create_vp(), when kzalloc for the VP struct fails, the code jumps to the cleanup path without setting ret. At that point ret is 0 from the preceding successful mshv_vp_stats_map() call, so the function returns success to userspace despite having failed to create the VP. No fd is installed and no VP is registered in pt_vp_array, but userspace has no way to know the operation failed. Set ret to -ENOMEM before jumping to the cleanup path. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 146726cc4e9b..644f9b10cbba 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1117,8 +1117,10 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, goto unmap_ghcb_page; vp = kzalloc_obj(*vp); - if (!vp) + if (!vp) { + ret = -ENOMEM; goto unmap_stats_pages; + } vp->vp_partition = mshv_partition_get(partition); if (!vp->vp_partition) { From b098dc869219c15dc49bf9cf63fb5fc1481d3373 Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 23 Jul 2026 00:28:11 +0000 Subject: [PATCH 367/501] mshv: Order pt_vp_array publish against irqfd assertion path mshv_partition_ioctl_create_vp() initialises a VP struct (allocations, mutex_init, init_waitqueue_head, page mappings) and then publishes the pointer into partition->pt_vp_array. Several ISR paths read this array locklessly: the intercept ISR, the two scheduler ISRs, and mshv_try_assert_irq_fast() on the irqfd fast path. Of these, only mshv_try_assert_irq_fast() can structurally race the publish. It runs from an eventfd waker without holding pt_mutex, and MSHV_IRQFD does not require the target lapic_apic_id (== vp_index) to refer to an existing VP at registration time. A user can therefore register an irqfd targeting a yet-to-be-created VP, then trigger mshv_try_assert_irq_fast() concurrently with MSHV_CREATE_VP for the same index. On weakly-ordered architectures the reader can observe a non-NULL pointer in pt_vp_array before the initialising stores to the VP struct become visible, leading to use of partially-initialised fields (e.g. vp_register_page). The other ISR readers cannot reach this race: the hypervisor will not generate intercept or scheduler messages for a VP that has never been told to run, and the user can only call MSHV_RUN_VP on the VP fd returned by MSHV_CREATE_VP, which by construction is returned after the publish. Leave those readers as plain loads. Use smp_store_release() in mshv_partition_ioctl_create_vp() to publish the pointer, and pair it with smp_load_acquire() in mshv_try_assert_irq_fast(). On x86 these compile to plain accesses under TSO; on ARM64 they emit one-instruction acquire/release barriers, acceptable on this fast path. The destroy-side path (destroy_partition() clearing pt_vp_array[i] to NULL after kfree(vp)) has a separate ordering and lifetime concern that is out of scope here. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_eventfd.c | 9 ++++++++- drivers/hv/mshv_root_main.c | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/hv/mshv_eventfd.c b/drivers/hv/mshv_eventfd.c index 047e5bd43238..06aef99c8298 100644 --- a/drivers/hv/mshv_eventfd.c +++ b/drivers/hv/mshv_eventfd.c @@ -169,7 +169,14 @@ static int mshv_try_assert_irq_fast(struct mshv_irqfd *irqfd) return -EOPNOTSUPP; #endif - vp = partition->pt_vp_array[irq->lapic_apic_id]; + /* + * Pairs with smp_store_release() in mshv_partition_ioctl_create_vp(). + * MSHV_IRQFD does not require the target lapic_apic_id to refer to an + * existing VP, so this read can race a concurrent VP creation; the + * acquire ensures that a non-NULL pointer implies the VP's + * initialising stores are visible. + */ + vp = smp_load_acquire(&partition->pt_vp_array[irq->lapic_apic_id]); if (!vp->vp_register_page) return -EOPNOTSUPP; diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 644f9b10cbba..8a15448e2ace 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1157,7 +1157,13 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; - partition->pt_vp_array[args.vp_index] = vp; + /* + * Pairs with smp_load_acquire() in mshv_try_assert_irq_fast(), which + * can run concurrently from an irqfd waker without holding pt_mutex. + * The release ensures the VP's initialising stores are visible to any + * reader that observes a non-NULL pointer in pt_vp_array. + */ + smp_store_release(&partition->pt_vp_array[args.vp_index], vp); goto out; From 92d0593128023cf93ae61b7728dcc3062f8d514f Mon Sep 17 00:00:00 2001 From: Hardik Garg Date: Fri, 17 Jul 2026 00:18:37 +0000 Subject: [PATCH 368/501] Drivers: hv: vmbus: add VTL2 redirect connection ID VMBus sends CHANNELMSG_INITIATE_CONTACT through a Hyper-V message connection ID. Older protocol versions use VMBUS_MESSAGE_CONNECTION_ID, while protocol version 5.0 and newer normally use VMBUS_MESSAGE_CONNECTION_ID_4. For a VTL2 kernel using VMBus protocol 5.0 or newer, the host may expect INITIATE_CONTACT on either the redirect connection ID or VMBUS_MESSAGE_CONNECTION_ID_4. There is no capability indication that identifies which ID is active, so the driver must determine it at runtime. During VMBus negotiation, the redirect ID is tried first because it is used by VTL2 configurations with VMBus redirection enabled. If the redirect ID is unavailable, the host rejects it synchronously with HV_STATUS_INVALID_CONNECTION_ID, allowing fallback to the standard ID. Return a distinct error for an invalid Initiate Contact connection ID so this fallback does not mask other post-message failures or protocol-version rejections. Preserve the existing connection ID selection for older protocol versions or when running below VTL2. Signed-off-by: Hardik Garg Reviewed-by: Tianyu Lan Reviewed-by: Saurabh Sengar Reviewed-by: Naman Jain Reviewed-by: Michael Kelley Signed-off-by: Wei Liu --- drivers/hv/connection.c | 47 +++++++++++++++++++++++---------------- drivers/hv/hyperv_vmbus.h | 2 ++ 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index b5b322ce16df..0fd50d4cb573 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -72,7 +72,8 @@ module_param(max_version, uint, S_IRUGO); MODULE_PARM_DESC(max_version, "Maximal VMBus protocol version which can be negotiated"); -int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +static int vmbus_try_connection_id(struct vmbus_channel_msginfo *msginfo, + u32 version, u32 connection_id) { int ret = 0; struct vmbus_channel_initiate_contact *msg; @@ -87,20 +88,20 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) msg->vmbus_version_requested = version; /* - * VMBus protocol 5.0 (VERSION_WIN10_V5) and higher require that we must - * use VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate Contact Message, - * and for subsequent messages, we must use the Message Connection ID - * field in the host-returned Version Response Message. And, with - * VERSION_WIN10_V5 and higher, we don't use msg->interrupt_page, but we - * tell the host explicitly that we still use VMBUS_MESSAGE_SINT(2) for - * compatibility. + * For VMBus protocol 5.0 (VERSION_WIN10_V5) and higher, use the + * caller-supplied connection_id for the Initiate Contact message so + * the caller can implement the required retry scheme. For subsequent + * messages, use the Message Connection ID field in the host-returned + * Version Response message. With VERSION_WIN10_V5 and higher, we don't + * use msg->interrupt_page, but tell the host explicitly that we still + * use VMBUS_MESSAGE_SINT(2) for compatibility. * * On old hosts, we should always use VMBUS_MESSAGE_CONNECTION_ID (1). */ if (version >= VERSION_WIN10_V5) { msg->msg_sint = VMBUS_MESSAGE_SINT; msg->msg_vtl = ms_hyperv.vtl; - vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID_4; + vmbus_connection.msg_conn_id = connection_id; } else { msg->interrupt_page = virt_to_phys(vmbus_connection.int_page); vmbus_connection.msg_conn_id = VMBUS_MESSAGE_CONNECTION_ID; @@ -165,6 +166,22 @@ int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) return ret; } +int vmbus_negotiate_version(struct vmbus_channel_msginfo *msginfo, u32 version) +{ + int ret; + + /* Try the redirect ID first for VTL2 with VMBus protocol 5.0+. */ + if (version >= VERSION_WIN10_V5 && ms_hyperv.vtl == 2) { + ret = vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT); + if (ret != -ENXIO) + return ret; + } + + return vmbus_try_connection_id(msginfo, version, + VMBUS_MESSAGE_CONNECTION_ID_4); +} + /* * vmbus_connect - Sends a connect request on the partition service connection */ @@ -457,18 +474,10 @@ int vmbus_post_msg(void *buffer, size_t buflen, bool can_sleep) switch (ret) { case HV_STATUS_INVALID_CONNECTION_ID: - /* - * See vmbus_negotiate_version(): VMBus protocol 5.0 - * and higher require that we must use - * VMBUS_MESSAGE_CONNECTION_ID_4 for the Initiate - * Contact message, but on old hosts that only - * support VMBus protocol 4.0 or lower, here we get - * HV_STATUS_INVALID_CONNECTION_ID and we should - * return an error immediately without retrying. - */ + /* Allow INITIATE_CONTACT to try another connection ID. */ hdr = buffer; if (hdr->msgtype == CHANNELMSG_INITIATE_CONTACT) - return -EINVAL; + return -ENXIO; /* * We could get this if we send messages too * frequently. diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index eb8bdd8bb1f5..33923621a5a3 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -110,6 +110,8 @@ struct hv_input_post_message { enum { VMBUS_MESSAGE_CONNECTION_ID = 1, VMBUS_MESSAGE_CONNECTION_ID_4 = 4, + /* VTL2 redirect connection ID for INITIATE_CONTACT. */ + VMBUS_MESSAGE_CONNECTION_ID_REDIRECT = 0x800074, VMBUS_MESSAGE_PORT_ID = 1, VMBUS_EVENT_CONNECTION_ID = 2, VMBUS_EVENT_PORT_ID = 2, From 72e3b0311aa90568a4b42a64445d1d3e1dc5a34d Mon Sep 17 00:00:00 2001 From: Stanislav Kinsburskii Date: Thu, 7 May 2026 15:44:32 +0000 Subject: [PATCH 369/501] mshv: Publish VP to pt_vp_array before installing the file descriptor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mshv_partition_ioctl_create_vp() called anon_inode_getfd() before publishing the new VP into partition->pt_vp_array. anon_inode_getfd() includes fd_install(), so the fd was live in current->files before the publish ran. A concurrent MSHV_RUN_VP ioctl on that fd does not serialise against the in-progress MSHV_CREATE_VP — it takes vp->vp_mutex, not the partition mutex. Once the VP starts running and traps, mshv_intercept_isr() can look up partition->pt_vp_array[vp_index] and observe NULL, silently dropping the intercept message. Split the fd creation: reserve an fd with get_unused_fd_flags(), create the file with anon_inode_getfile(), publish the VP via smp_store_release(), and finally call fd_install() as the userspace-visibility commit point. Fixes: 621191d709b14 ("Drivers: hv: Introduce mshv_root module to expose /dev/mshv to VMMs") Signed-off-by: Stanislav Kinsburskii Reviewed-by: Anirudh Rayabharam (Microsoft) Signed-off-by: Wei Liu --- drivers/hv/mshv_root_main.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 8a15448e2ace..cc2cfce2aefd 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -1072,6 +1072,8 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, struct mshv_vp *vp; struct page *intercept_msg_page, *register_page, *ghcb_page; struct hv_stats_page *stats_pages[2]; + struct file *file; + int fd; long ret; if (copy_from_user(&args, arg, sizeof(args))) @@ -1146,14 +1148,18 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, if (ret) goto put_partition; - /* - * Keep anon_inode_getfd last: it installs fd in the file struct and - * thus makes the state accessible in user space. - */ - ret = anon_inode_getfd("mshv_vp", &mshv_vp_fops, vp, - O_RDWR | O_CLOEXEC); - if (ret < 0) + fd = get_unused_fd_flags(O_RDWR | O_CLOEXEC); + if (fd < 0) { + ret = fd; goto remove_debugfs_vp; + } + + file = anon_inode_getfile("mshv_vp", &mshv_vp_fops, vp, + O_RDWR | O_CLOEXEC); + if (IS_ERR(file)) { + ret = PTR_ERR(file); + goto put_unused_vp_fd; + } /* already exclusive with the partition mutex for all ioctls */ partition->pt_vp_count++; @@ -1165,8 +1171,17 @@ mshv_partition_ioctl_create_vp(struct mshv_partition *partition, */ smp_store_release(&partition->pt_vp_array[args.vp_index], vp); + /* + * fd_install() is the userspace-visibility commit point. Must be the + * last operation that can fail or be observed. + */ + fd_install(fd, file); + ret = fd; + goto out; +put_unused_vp_fd: + put_unused_fd(fd); remove_debugfs_vp: mshv_debugfs_vp_remove(vp); put_partition: From 22dfdc17ceadd2783a609947af621a7f697b7765 Mon Sep 17 00:00:00 2001 From: Yi Xie Date: Thu, 9 Jul 2026 10:19:47 +0800 Subject: [PATCH 370/501] mshv_vtl: bounds-check cpu index in vtl mmap fault handler cpu is taken from pgoff & 0xffff. cpu_online() does not reject cpu >= nr_cpu_ids, and per_cpu_ptr() can then walk off __per_cpu_offset. Signed-off-by: Yi Xie Reviewed-by: Naman Jain Signed-off-by: Wei Liu --- drivers/hv/mshv_vtl_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hv/mshv_vtl_main.c b/drivers/hv/mshv_vtl_main.c index 5ba1efb3b4e7..6e3c11c68171 100644 --- a/drivers/hv/mshv_vtl_main.c +++ b/drivers/hv/mshv_vtl_main.c @@ -802,7 +802,7 @@ static vm_fault_t mshv_vtl_fault(struct vm_fault *vmf) int cpu = vmf->pgoff & MSHV_PG_OFF_CPU_MASK; int real_off = vmf->pgoff >> MSHV_REAL_OFF_SHIFT; - if (!cpu_online(cpu)) + if (cpu >= nr_cpu_ids || !cpu_online(cpu)) return VM_FAULT_SIGBUS; /* * CPU Hotplug is not supported in VTL2 in OpenHCL, where this kernel driver exists. From 976245094925bab9bc39366b2e9ab44ffcde61d0 Mon Sep 17 00:00:00 2001 From: Mahanta Jambigi Date: Wed, 29 Jul 2026 15:01:53 +0200 Subject: [PATCH 371/501] net/smc: fix qentry overwrite for CONFIRM_LINK and ADD_LINK_CONT in smc_llc_event_handler() The SMC_LLC_CONFIRM_LINK / SMC_LLC_ADD_LINK_CONT branch in smc_llc_event_handler() stores an incoming qentry into the local LLC flow without first checking whether a qentry is already pending. If a malicious or buggy peer sends a second CONFIRM_LINK or ADD_LINK_CONT request while a flow is active and flow->qentry is already set, smc_llc_flow_qentry_set() overwrites the pointer without freeing the previous allocation, leaking one kmalloc-96 object per spurious message. The sibling SMC_LLC_DELETE_LINK branch already has the correct !flow->qentry guard. Apply the same guard to the CONFIRM_LINK/ADD_LINK_CONT branch so that a duplicate message when qentry is already occupied falls through to break and is freed by the kfree(qentry) at the out: label, rather than silently leaking the existing allocation. The response direction (smc_llc_rx_response()) is unaffected: it already guards with flow->qentry at the equivalent site and drops duplicate responses correctly. Fixes: 0fb0b02bd6fd ("net/smc: adapt SMC client code to use the LLC flow") Signed-off-by: Mahanta Jambigi Reviewed-by: Hidayath Khan Reviewed-by: Sidraya Jayagond Reviewed-by: Dust Li Link: https://patch.msgid.link/20260729130153.970800-1-mjambigi@linux.ibm.com Signed-off-by: Jakub Kicinski --- net/smc/smc_llc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_llc.c b/net/smc/smc_llc.c index 954b2ff1815c..aa6d83af55ed 100644 --- a/net/smc/smc_llc.c +++ b/net/smc/smc_llc.c @@ -1927,7 +1927,8 @@ static void smc_llc_event_handler(struct smc_llc_qentry *qentry) return; case SMC_LLC_CONFIRM_LINK: case SMC_LLC_ADD_LINK_CONT: - if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE) { + if (lgr->llc_flow_lcl.type != SMC_LLC_FLOW_NONE && + !lgr->llc_flow_lcl.qentry) { /* a flow is waiting for this message */ smc_llc_flow_qentry_set(&lgr->llc_flow_lcl, qentry); wake_up(&lgr->llc_msg_waiter); From 63488dba65ef91373ef616575b32eb0eb21459f4 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Wed, 29 Jul 2026 22:19:41 +0900 Subject: [PATCH 372/501] net: bridge: mrp: fix uninitialised bytes on the wire br_mrp_alloc_test_skb() builds MRP test frames on an skb from dev_alloc_skb(), which does not clear the linear data area. On the MRA ring-role branch the sub-option TLV header is appended with sub_tlv = skb_put(skb, sizeof(*sub_tlv)); sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; so sub_tlv->length is never written, and the two trailing alignment bytes are appended with a bare skb_put() that does not clear them either. The neighbouring oui and sub_opt regions are explicitly zeroed, so three uninitialised bytes are left in every MRA MRP_Test frame that goes out. Put the sub-option TLV header and the alignment padding in a single skb_put_zero(), which clears both. The AUTO_MGR sub-TLV carries no payload, so the zeroed length field is already the value it should have. Fixes: f7458934b079 ("net: bridge: mrp: Update the Test frames for MRA") Suggested-by: Nikolay Aleksandrov Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Acked-by: Nikolay Aleksandrov Link: https://patch.msgid.link/20260729131941.10254-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/bridge/br_mrp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/net/bridge/br_mrp.c b/net/bridge/br_mrp.c index 179d2470b724..ef16d0703924 100644 --- a/net/bridge/br_mrp.c +++ b/net/bridge/br_mrp.c @@ -224,11 +224,9 @@ static struct sk_buff *br_mrp_alloc_test_skb(struct br_mrp *mrp, sub_opt = skb_put(skb, sizeof(*sub_opt)); memset(sub_opt, 0x0, sizeof(*sub_opt)); - sub_tlv = skb_put(skb, sizeof(*sub_tlv)); - sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; - /* 32 bit alligment shall be ensured therefore add 2 bytes */ - skb_put(skb, MRP_OPT_PADDING); + sub_tlv = skb_put_zero(skb, sizeof(*sub_tlv) + MRP_OPT_PADDING); + sub_tlv->type = BR_MRP_SUB_TLV_HEADER_TEST_AUTO_MGR; } br_mrp_skb_tlv(skb, BR_MRP_TLV_HEADER_END, 0x0); From 260b20d9b78bf002f89088fb62d60e8dee98f6f8 Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Fri, 31 Jul 2026 23:16:46 +0900 Subject: [PATCH 373/501] ring-buffer: Fix subbuf_ids memory leak in rb_allocate_cpu_buffer() error path In rb_allocate_cpu_buffer(), cpu_buffer->subbuf_ids is allocated using kcalloc() when buffer->remote is non-NULL. If a subsequent page allocation fails (e.g., ring_buffer_desc_page() returns NULL or rb_allocate_pages() fails), execution jumps to fail_free_reader. While __free(kfree) automatically frees the outer cpu_buffer structure at scope exit, kfree(cpu_buffer) does not recursively free nested heap pointers such as cpu_buffer->subbuf_ids, resulting in a memory leak. Fix this by explicitly freeing cpu_buffer->subbuf_ids in the fail_free_reader error unwinding path when cpu_buffer->remote is set. Link: https://patch.msgid.link/178550740672.380917.6067449683620196150.stgit@devnote2 Fixes: 2e67fabd8b77 ("ring-buffer: Introduce ring-buffer remotes") Assisted-by: Antigravity:gemini-3.6-flash Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Vincent Donnefort Signed-off-by: Steven Rostedt --- kernel/trace/ring_buffer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 78d3875a47a5..8e2485bb3aa8 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2599,6 +2599,7 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) return_ptr(cpu_buffer); fail_free_reader: + kfree(cpu_buffer->subbuf_ids); free_buffer_page(cpu_buffer->reader_page); return NULL; From 653d7ddf6cba867777a3d14c4f83ace008c5ad13 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Tue, 28 Jul 2026 01:23:29 +0800 Subject: [PATCH 374/501] inet: frags: publish queues before arming timer inet_frag_create() arms the fragment queue timer before inserting the queue into the fqdir rhashtable. If the namespace fragment timeout is zero or negative, the timer can run before the queue is published. The timer callback then marks the queue complete, tries to remove a node that is not in the hash table yet, and drops the anticipated hash reference. Creation can subsequently publish the completed queue without restoring that reference, leaving a stale hash node after the caller drops the remaining reference. Publish the queue first and arm the timer while holding the queue lock. This makes timer expiry wait until the queue is visible in the hash table, so inet_frag_kill() can remove the node and balance the hash reference. Fixes: 648700f76b03 ("inet: frags: use rhashtables for reassembly units") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Signed-off-by: Ren Wei Link: https://patch.msgid.link/bf66785e7c0c139d7a1900e2f01faeeab344b960.1784948849.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv4/inet_fragment.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 848c0f0c2ed9..fc0cb993959f 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -393,8 +393,8 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir, *prev = ERR_PTR(-ENOMEM); return NULL; } - mod_timer(&q->timer, jiffies + fqdir->timeout); + spin_lock_bh(&q->lock); *prev = rhashtable_lookup_get_insert_key(&fqdir->rhashtable, &q->key, &q->node, f->rhash_params); if (*prev) { @@ -402,13 +402,13 @@ static struct inet_frag_queue *inet_frag_create(struct fqdir *fqdir, * we need to cancel what inet_frag_alloc() * anticipated. */ - int refs = 1; - q->flags |= INET_FRAG_COMPLETE; - inet_frag_kill(q, &refs); - inet_frag_putn(q, refs); + spin_unlock_bh(&q->lock); + inet_frag_putn(q, 2); return NULL; } + mod_timer(&q->timer, jiffies + fqdir->timeout); + spin_unlock_bh(&q->lock); return q; } From 47d7f7051253bdc02b1d245d87e38f16d31a74df Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Wed, 29 Jul 2026 05:44:11 -0400 Subject: [PATCH 375/501] net/sched: cls_route: fix fastmap use-after-free on filter The route4 classifier maintains a 16-slot fastmap cache that stores raw struct route4_filter pointers indexed by (id, iif). The reader (route4_classify) populates this cache via route4_set_fastmap() for every classified packet that hits a filter. The writer (route4_delete, route4_change) clears the cache via route4_reset_fastmap() before RCU-deferred kfree of the filter. This creates a UAF race: 1. Reader walks the RCU-protected bucket chain, finds filter f 2. Writer unlinks f, calls route4_reset_fastmap(), then tcf_queue_work() 3. Reader calls route4_set_fastmap() and writes f into the cache *after* the writer's reset, caching a pointer about to be freed 4. After the RCU grace period, kfree(f) executes 5. Next classified packet on the same (id, iif) tuple hits the stale fastmap entry and reads f->res from freed memory Reproduced with an mdelay(100) accelerator in route4_set_fastmap() and a concurrent add/delete stress test (provided by both zdi and Santosh). Both triggered KASAN slab-use-after-free reports in the route4 fastmap paths. Fix: Introduce a per-filter boolean dying flag to suppress stale fastmap republishing by in-flight readers. Fixes: 1109c00547fc ("net: sched: RCU cls_route") Reported-by: zdi-disclosures@trendmicro.com Reported-by: Santosh Kalluri Suggested-by: Paolo Abeni Tested-by: Victor Nogueira Tested-by: Santosh Kalluri Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260729094411.46257-1-jhs@mojatatu.com Signed-off-by: Jakub Kicinski --- net/sched/cls_route.c | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index bd6f945bd388..eded7aacd3f7 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -52,6 +52,7 @@ struct route4_filter { struct tcf_result res; struct tcf_exts exts; u32 handle; + bool dying; struct route4_bucket *bkt; struct tcf_proto *tp; struct rcu_work rwork; @@ -66,9 +67,11 @@ static inline int route4_fastmap_hash(u32 id, int iif) static DEFINE_SPINLOCK(fastmap_lock); static void -route4_reset_fastmap(struct route4_head *head) +route4_reset_fastmap(struct route4_head *head, struct route4_filter *f) { spin_lock_bh(&fastmap_lock); + if (f) + f->dying = true; memset(head->fastmap, 0, sizeof(head->fastmap)); spin_unlock_bh(&fastmap_lock); } @@ -81,9 +84,11 @@ route4_set_fastmap(struct route4_head *head, u32 id, int iif, /* fastmap updates must look atomic to aling id, iff, filter */ spin_lock_bh(&fastmap_lock); - head->fastmap[h].id = id; - head->fastmap[h].iif = iif; - head->fastmap[h].filter = f; + if (f == ROUTE4_FAILURE || !f->dying) { + head->fastmap[h].id = id; + head->fastmap[h].iif = iif; + head->fastmap[h].filter = f; + } spin_unlock_bh(&fastmap_lock); } @@ -297,6 +302,13 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, next = rtnl_dereference(f->next); RCU_INIT_POINTER(b->ht[h2], next); tcf_unbind_filter(tp, &f->res); + /* Mark the filter dying under fastmap_lock so + * any in-flight reader that still holds it + * will skip the republish in route4_set_fastmap(). + */ + spin_lock_bh(&fastmap_lock); + f->dying = true; + spin_unlock_bh(&fastmap_lock); if (tcf_exts_get_net(&f->exts)) route4_queue_work(f); else @@ -307,6 +319,11 @@ static void route4_destroy(struct tcf_proto *tp, bool rtnl_held, kfree_rcu(b, rcu); } } + + /* All filters are unlinked and marked dying, so no in-flight + * reader can republish a stale entry after this reset. + */ + route4_reset_fastmap(head, NULL); kfree_rcu(head, rcu); } @@ -334,11 +351,11 @@ static int route4_delete(struct tcf_proto *tp, void *arg, bool *last, /* unlink it */ RCU_INIT_POINTER(*fp, rtnl_dereference(f->next)); - /* Remove any fastmap lookups that might ref filter - * notice we unlink'd the filter so we can't get it - * back in the fastmap. + /* Clear any fastmap entries that may ref this filter and + * mark it dying so in-flight readers can't republish it + * after the reset. */ - route4_reset_fastmap(head); + route4_reset_fastmap(head, f); /* Delete it */ tcf_unbind_filter(tp, &f->res); @@ -558,7 +575,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, } } - route4_reset_fastmap(head); + route4_reset_fastmap(head, fold); *arg = f; if (fold) { tcf_unbind_filter(tp, &fold->res); From f307a7dc32097c11413178fca437a10d20890bc2 Mon Sep 17 00:00:00 2001 From: Jiawen Liu <1298662399@qq.com> Date: Tue, 28 Jul 2026 12:17:10 +0400 Subject: [PATCH 376/501] net: hisilicon: hix5hd2_gmac: remove redundant NAPI delete hix5hd2_dev_remove() calls netif_napi_del() before unregister_netdev(). This is not needed because free_netdev() deletes all NAPI instances attached to the net_device. Remove the redundant call and let the networking core tear down the NAPI instance during unregister_netdev(). The probe error path still keeps its explicit netif_napi_del(), because the device has not been registered there. Fixes: 57c5bc9ad7d7 ("net: hisilicon: add hix5hd2 mac driver") Signed-off-by: Jiawen Liu <1298662399@qq.com> Link: https://patch.msgid.link/tencent_5FFD37A252B4FEA6A80AD25B17C8E904F005@qq.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/hisilicon/hix5hd2_gmac.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c index e3e7f2270560..02282dc86faf 100644 --- a/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c +++ b/drivers/net/ethernet/hisilicon/hix5hd2_gmac.c @@ -1282,7 +1282,6 @@ static void hix5hd2_dev_remove(struct platform_device *pdev) struct net_device *ndev = platform_get_drvdata(pdev); struct hix5hd2_priv *priv = netdev_priv(ndev); - netif_napi_del(&priv->napi); unregister_netdev(ndev); mdiobus_unregister(priv->bus); mdiobus_free(priv->bus); From 1c4dac9bf1d2ac31da63b794bdec697777cbd0fd Mon Sep 17 00:00:00 2001 From: Or Har-Toov Date: Wed, 29 Jul 2026 11:06:00 +0300 Subject: [PATCH 377/501] devlink: fix net namespace reference leak in reload devlink_nl_reload_doit() calls devlink_netns_get(), which returns a net with a held reference. When the requested namespace differs from the current one and the reload action is not DRIVER_REINIT, the function returns -EOPNOTSUPP without releasing the reference. Add the missing put_net() on this error path. Fixes: 2edd92570441 ("devlink: don't allow to change net namespace for FW_ACTIVATE reload action") Signed-off-by: Or Har-Toov Reviewed-by: Jiri Pirko Signed-off-by: Tariq Toukan Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260729080600.2427721-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- net/devlink/dev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/devlink/dev.c b/net/devlink/dev.c index 57b2b8f03543..fd5633fa88ec 100644 --- a/net/devlink/dev.c +++ b/net/devlink/dev.c @@ -578,6 +578,7 @@ int devlink_nl_reload_doit(struct sk_buff *skb, struct genl_info *info) action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) { NL_SET_ERR_MSG_MOD(info->extack, "Changing namespace is only supported for reinit action"); + put_net(dest_net); return -EOPNOTSUPP; } } From 43e970d961ad3cc0c23e89db255c7fa4a353eff9 Mon Sep 17 00:00:00 2001 From: Chris Mi Date: Wed, 29 Jul 2026 10:16:22 +0300 Subject: [PATCH 378/501] net/mlx5: SF, Handle function changed event When host is powered off, firmware does not send vhca_state event for every probed host SF on the DPU because it may have deployed thousands of SFs to the host. Instead it sends a function changed event. Currently, only VFs handle this event. This commit extends support to SFs. When DPU user deactivates[1] SFs, mlx5 expects vhca_state event and leaves the SF in dangling state[2]. When DPU user deletes[3] SFs, mlx5 also expects vhca_state event and destroys the SF resources[4]. Fix it by changing SF to the right state and freeing SF resources when the function changed event is received. When this event is received, driver checks all SF states. - If state is in_use, change it to active. - If state is teardown_request, change it to allocated. And SF hardware table entry is freed if it is pending for delete. [1] # devlink port function set en3f0c1pf0sf0 state inactive [2] # devlink port function set en3f0c1pf0sf0 state active Error: mlx5_core: SF is inactivated but it is still attached. kernel answers: Device or resource busy [3] # devlink port show pci/0000:03:00.0/229376: type eth netdev en3f0c1pf0sf0 \ flavour pcisf controller 1 pfnum 0 sfnum 0 splittable false function: hw_addr 00:00:00:00:00:00 state active opstate attached \ roce enable trust off max_uc_macs 4096 max_io_eqs 8 # devlink port del en3f0c1pf0sf0 [4] # devlink port add pci/0000:03:00.0 flavour pcisf pfnum 0 sfnum 0 \ controller 1 Error: mlx5_core: SF already exist. Choose different sfnum. kernel answers: File exists Fixes: 6a3273217469 ("net/mlx5: SF, Port function state change support") Signed-off-by: Chris Mi Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260729071622.2423270-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../mellanox/mlx5/core/eswitch_offloads.c | 10 +++++-- .../ethernet/mellanox/mlx5/core/sf/devlink.c | 29 +++++++++++++++++++ .../mlx5/core/sf/diag/sf_tracepoint.h | 8 +++++ .../ethernet/mellanox/mlx5/core/sf/hw_table.c | 22 ++++++++++++++ .../net/ethernet/mellanox/mlx5/core/sf/sf.h | 12 ++++++++ 5 files changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c index 907ee83a722d..247e5d85ec36 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch_offloads.c @@ -3986,7 +3986,7 @@ static void esw_offloads_steering_cleanup(struct mlx5_eswitch *esw) mutex_destroy(&esw->fdb_table.offloads.vports.lock); } -static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) +static void esw_changed_event_handler(struct mlx5_eswitch *esw) { struct mlx5_esw_pf_info host_pf_info; u16 new_num_vfs; @@ -3999,6 +3999,11 @@ static void esw_vfs_changed_event_handler(struct mlx5_eswitch *esw) host_pf_info = mlx5_esw_get_host_pf_info(esw->dev, out); new_num_vfs = host_pf_info.num_of_vfs; + if (host_pf_info.pf_disabled) { + mlx5_sf_table_esw_changed_event_handler(esw->dev); + mlx5_sf_hw_table_esw_changed_event_handler(esw->dev); + } + if (new_num_vfs == esw->esw_funcs.num_vfs || host_pf_info.pf_disabled) goto free; @@ -4091,8 +4096,7 @@ int mlx5_esw_funcs_changed_handler(struct notifier_block *nb, esw_funcs = mlx5_nb_cof(nb, struct mlx5_esw_functions, nb); esw = container_of(esw_funcs, struct mlx5_eswitch, esw_funcs); - ret = mlx5_esw_add_work(esw, esw_vfs_changed_event_handler, - GFP_ATOMIC); + ret = mlx5_esw_add_work(esw, esw_changed_event_handler, GFP_ATOMIC); if (ret) return NOTIFY_DONE; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c index b6cecbcc392d..4564d460416b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/devlink.c @@ -561,3 +561,32 @@ bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev) return xa_empty(&table->function_ids); } + +void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_table *table = dev->priv.sf_table; + unsigned long index; + struct mlx5_sf *sf; + + trace_mlx5_sf_host_pf_disabled(dev); + + if (!table) + return; + + mutex_lock(&table->sf_state_lock); + xa_for_each(&table->function_ids, index, sf) { + if (!sf->controller) + continue; + + if (sf->hw_state == MLX5_VHCA_STATE_IN_USE) + sf->hw_state = MLX5_VHCA_STATE_ACTIVE; + else if (sf->hw_state == MLX5_VHCA_STATE_TEARDOWN_REQUEST) + sf->hw_state = MLX5_VHCA_STATE_ALLOCATED; + else + continue; + trace_mlx5_sf_update_state(table->dev, sf->port_index, + sf->controller, sf->hw_fn_id, + sf->hw_state); + } + mutex_unlock(&table->sf_state_lock); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h index 302ce00da5a9..9dc88a7e30ca 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/diag/sf_tracepoint.h @@ -11,6 +11,14 @@ #include #include "sf/vhca_event.h" +TRACE_EVENT(mlx5_sf_host_pf_disabled, + TP_PROTO(const struct mlx5_core_dev *dev), + TP_ARGS(dev), + TP_STRUCT__entry(__string(devname, dev_name(dev->device))), + TP_fast_assign(__assign_str(devname);), + TP_printk("(%s)\n", __get_str(devname)) +); + TRACE_EVENT(mlx5_sf_add, TP_PROTO(const struct mlx5_core_dev *dev, unsigned int port_index, diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c index 0bc9146a3598..95a8b1e64ba4 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/hw_table.c @@ -459,3 +459,25 @@ bool mlx5_sf_hw_table_supported(const struct mlx5_core_dev *dev) { return !!dev->priv.sf_hw_table; } + +void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ + struct mlx5_sf_hw_table *table; + struct mlx5_sf_hwc_table *hwc; + int i; + + table = dev->priv.sf_hw_table; + if (!table) + return; + + mutex_lock(&table->table_lock); + hwc = &table->hwc[MLX5_SF_HWC_EXT_HOST]; + for (i = 0; i < hwc->max_fn; i++) { + struct mlx5_sf_hw *sf_hw; + + sf_hw = &hwc->sfs[i]; + if (sf_hw->allocated && sf_hw->pending_delete) + mlx5_sf_hw_table_hwc_sf_free(dev, hwc, i); + } + mutex_unlock(&table->table_lock); +} diff --git a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h index d8a934a0e968..ed784682b9b1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/sf/sf.h @@ -15,12 +15,14 @@ void mlx5_sf_hw_table_cleanup(struct mlx5_core_dev *dev); int mlx5_sf_hw_notifier_init(struct mlx5_core_dev *dev); void mlx5_sf_hw_notifier_cleanup(struct mlx5_core_dev *dev); void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev); +void mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev); int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev); int mlx5_sf_table_init(struct mlx5_core_dev *dev); void mlx5_sf_notifiers_cleanup(struct mlx5_core_dev *dev); void mlx5_sf_table_cleanup(struct mlx5_core_dev *dev); bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev); +void mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev); int mlx5_devlink_sf_port_new(struct devlink *devlink, const struct devlink_port_new_attrs *add_attr, @@ -60,6 +62,11 @@ static inline void mlx5_sf_hw_table_destroy(struct mlx5_core_dev *dev) { } +static inline void +mlx5_sf_hw_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ +} + static inline int mlx5_sf_notifiers_init(struct mlx5_core_dev *dev) { return 0; @@ -83,6 +90,11 @@ static inline bool mlx5_sf_table_empty(const struct mlx5_core_dev *dev) return true; } +static inline void +mlx5_sf_table_esw_changed_event_handler(struct mlx5_core_dev *dev) +{ +} + #endif #endif From af39eb111ce6b5eba9c08513b62c4868eb7e7fd5 Mon Sep 17 00:00:00 2001 From: Michael Guralnik Date: Wed, 29 Jul 2026 11:04:02 +0300 Subject: [PATCH 379/501] net/mlx5: fw_tracer, return NULL on create error Tracer creation can fail by returning either NULL or ERR_PTR. The return value is stored without a check on the device, and users treat ERR_PTR and NULL the same way. This also causes a crash in the core dump logic, which is missing the ERR_PTR check and ends up dereferencing it, as shown in the trace below. Switch tracer creation to return NULL on failure only, so callers only need a single NULL check. Internal error: Oops: 0000000096000006 [#1] SMP Modules linked in: mlx5_ib ib_uverbs ib_core ipv6 mlx5_core CPU: 1 UID: 0 PID: 12 Comm: kworker/u16:0 Not tainted 6.19.7 #1 PREEMPT(none) Workqueue: mlx5_health0001:01:00.0 mlx5_fw_reporter_err_work [mlx5_core] pstate: a3400009 (NzCv daif +PAN -UAO +TCO +DIT -SSBS BTYPE=--) pc : mlx5_fw_tracer_trigger_core_dump_general+0x58/0xe0 [mlx5_core] lr : mlx5_fw_tracer_trigger_core_dump_general+0x40/0xe0 [mlx5_core] sp : ffff800081cf3c40 x29: ffff800081cf3c90 x28: 0000000000000000 x27: 0000000000000000 x26: ffff000080018828 x25: 0000000000000000 x24: ffff000080304a05 x23: ffff800081cf3d80 x22: ffff0000847e01a0 x21: 0000000000000000 x20: ffff0000847e01a0 x19: ffffffffffffffa1 x18: ffff80008310bbf0 x17: ffff800080119650 x16: ffff80008010df54 x15: ffff80008010d4ac x14: ffff800079c202e4 x13: ffff80008002fe60 x12: ffff800080119650 x11: ffff80008010df54 x10: ffff80008010d4ac x9 : ffff800079c203d8 x8 : ffff800081cf3c88 x7 : 0000000000000000 x6 : 0000000000000000 x5 : 0000000000000000 x4 : 0000000000000008 x3 : 0000000000000030 x2 : 0000000000000008 x1 : 0000000000000000 x0 : 00000000c5c4000e Call trace: mlx5_fw_tracer_trigger_core_dump_general+0x58/0xe0 [mlx5_core] (P) mlx5_fw_reporter_dump+0x30/0x2e0 [mlx5_core] devlink_health_do_dump+0x9c/0x160 devlink_health_report+0x1c0/0x288 mlx5_fw_reporter_err_work+0xac/0xc0 [mlx5_core] process_one_work+0x15c/0x3d8 worker_thread+0x18c/0x320 kthread+0x148/0x228 ret_from_fork+0x10/0x20 Code: b9400000 5ac00800 7a401800 540003ca (3940a260) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception SMP: stopping secondary CPUs Kernel Offset: disabled CPU features: 0x000000,00078031,75fce5a1,35fffe67 Memory Limit: none ---[ end Kernel panic - not syncing: Oops: Fatal exception ]--- Fixes: fd1483fe1f9f ("net/mlx5: Add support for FW reporter dump") Signed-off-by: Michael Guralnik Reviewed-by: Shay Drori Signed-off-by: Tariq Toukan Link: https://patch.msgid.link/20260729080402.2427184-1-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- .../ethernet/mellanox/mlx5/core/diag/fw_tracer.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c index adcc73e2a5b3..1493d8106c1a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/diag/fw_tracer.c @@ -1025,13 +1025,11 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev) tracer = kvzalloc_obj(*tracer); if (!tracer) - return ERR_PTR(-ENOMEM); + return NULL; tracer->work_queue = create_singlethread_workqueue("mlx5_fw_tracer"); - if (!tracer->work_queue) { - err = -ENOMEM; + if (!tracer->work_queue) goto free_tracer; - } tracer->dev = dev; @@ -1073,7 +1071,7 @@ struct mlx5_fw_tracer *mlx5_fw_tracer_create(struct mlx5_core_dev *dev) destroy_workqueue(tracer->work_queue); free_tracer: kvfree(tracer); - return ERR_PTR(err); + return NULL; } static int fw_tracer_event(struct notifier_block *nb, unsigned long action, void *data); @@ -1084,7 +1082,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer) struct mlx5_core_dev *dev; int err; - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return 0; if (!tracer->str_db.loaded) @@ -1134,7 +1132,7 @@ int mlx5_fw_tracer_init(struct mlx5_fw_tracer *tracer) /* Stop tracer + Cleanup HW resources */ void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer) { - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return; mutex_lock(&tracer->state_lock); @@ -1163,7 +1161,7 @@ void mlx5_fw_tracer_cleanup(struct mlx5_fw_tracer *tracer) /* Free software resources (Buffers, etc ..) */ void mlx5_fw_tracer_destroy(struct mlx5_fw_tracer *tracer) { - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return; mlx5_core_dbg(tracer->dev, "FWTracer: Destroy\n"); @@ -1215,7 +1213,7 @@ int mlx5_fw_tracer_reload(struct mlx5_fw_tracer *tracer) struct mlx5_core_dev *dev; int err; - if (IS_ERR_OR_NULL(tracer)) + if (!tracer) return 0; dev = tracer->dev; From 6d4514ca9cdf61fec4ec634cf50386f6f7e69748 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Tue, 21 Jul 2026 00:31:48 +0000 Subject: [PATCH 380/501] futex: Prevent robust futex exit race some more A robust futex unlock stores 0 over the whole futex value - wiping FUTEX_WAITERS - and wakes a single waiter. That wakeup is a one-shot notification: the protocol relies on its recipient to either acquire the futex (and eventually unlock while aware of the remaining contention) or re-arm FUTEX_WAITERS before sleeping again. If the woken waiter is killed before it can do either, the kernel must jump in and wake the next task down the line. This is a known complication of the futex protocol with a previous partial fix in commit ca16d5bee598 ("futex: Prevent robust futex exit race"). Unfortunately, that fix is insufficient. If a third task re-acquired the futex through the uncontended fast path in the meantime, the notification is lost: robust exit processing sees that it is owned by another task and does nothing, while the new owner sees no FUTEX_WAITERS when it unlocks and wakes nobody. The remaining waiters sleep forever behind a free futex: A owns the futex, B and C sleep in FUTEX_WAIT uval == A | FUTEX_WAITERS A robust unlock: store 0, FUTEX_WAKE(1) wakes B uval == 0 D fast path acquire: cmpxchg(0 -> D) uval == D, no FUTEX_WAITERS B killed before acting on the wakeup B exit walk, pending op: owner D != B -> no action D unlock: no FUTEX_WAITERS -> no wake C sleeps forever This is clearly a shortcoming in the implementation, which fails to keep the FUTEX_WAITERS bit consistent. Work around this by augmenting the robust list exit processing to also perform the extra wakeup if the futex word is owned by another thread but FUTEX_WAITERS is not set. This does not fix the problem of a non-contended take over/release and free sequence, which has been discussed for years and has been addressed by commit 3ca9595d9fb6 ("futex: Add support for unlocking robust futexes") and subsequent changes, but failed to take the problem described above into account. A more complete solution which is based on the in kernel unlock of contended robust futexes has been discussed in the context of this change and should show up in mainline sooner than later. [ tglx: Amend change log slightly and fixup coding style ] Fixes: ca16d5bee598 ("futex: Prevent robust futex exit race") Signed-off-by: Keno Fischer Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar Assisted-by: ClaudeCode:claude-fable-5 tla+ Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260730194705.38981-1-keno@juliacomputing.com --- kernel/futex/core.c | 85 +++++++++++++++++++++++++++++++-------------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/kernel/futex/core.c b/kernel/futex/core.c index 179b26e9c934..2650d1e52803 100644 --- a/kernel/futex/core.c +++ b/kernel/futex/core.c @@ -982,8 +982,11 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, return -1; /* - * Special case for regular (non PI) futexes. The unlock path in - * user space has two race scenarios: + * Special case for regular (non PI) futexes. Ordinarily, we do + * not perform any processing here unless the current thread was + * the owner of the futex (by the TID check below). + * + * However, the unlock path has three race scenarios: * * 1. The unlock path releases the user space futex value and * before it can execute the futex() syscall to wake up @@ -992,42 +995,70 @@ static int handle_futex_death(u32 __user *uaddr, struct task_struct *curr, * 2. A woken up waiter is killed before it can acquire the * futex in user space. * - * In the second case, the wake up notification could be generated - * by the unlock path in user space after setting the futex value - * to zero or by the kernel after setting the OWNER_DIED bit below. + * 3. A woken up waiter is killed in user space after another + * thread has acquired the futex, but before it can set + * FUTEX_WAITERS. * - * In both cases the TID validation below prevents a wakeup of - * potential waiters which can cause these waiters to block - * forever. + * Note that, if userspace uses the FUTEX_ROBUST_UNLOCK flag, we + * will not see case 1 here. * - * In both cases the following conditions are met: + * In the second and third case, the wake up notification could + * be generated from any of: * - * 1) task->futex.robust_list->list_op_pending != NULL - * @pending_op == true - * 2) The owner part of user space futex value == 0 + * i. An ordinary futex wakeup after unlock (with or + * without FUTEX_ROBUST_UNLOCK) + * ii. A robust wakeup from another thread's death + * iii. A previous round through this special case + * + * As a result, the futex world will be in one of four states: + * + * A. The futex word is 0 (unlocked) + * B. The futex word is owned by another thread + * (FUTEX_WAITERS is not set) + * C. The futex word is owned by another thread + * (FUTEX_WAITERS set) + * D. The futex's owner died and OWNER_DIED is set + * (the owner part of the word is 0) + * + * The key issue is that the kernel usually (at least from + * sources ii. and iii. or when so requested by userspace from + * source i.) only ever wakes *one* waiter at a time. If this + * waiter dies before acquiring the futex (or setting the + * FUTEX_WAITERS bit), the kernel *must* still wake the next + * waiter down the line to uphold the futex invariants and + * avoid lost wakeups. Note we do not need to handle state C, + * as it does not matter to us whether *we* successfully set + * the bit or a third thread did so in the meantime. + * + * Therefore, in these cases we must issue an additional + * futex_wake(). Note however that we *must not* set OWNER_DIED + * here. Our thread is *not* the owner of the futex. + * + * Thus to summarize, the conditions for needing the additional + * futex_wake() are: + * + * 1) @pending_op == true (the thread has not finished the + * mutex operation) + * 2) The futex word is in one of the states A, B or D * 3) Regular futex: @pi == false * - * If these conditions are met, it is safe to attempt waking up a - * potential waiter without touching the user space futex value and - * trying to set the OWNER_DIED bit. If the futex value is zero, - * the rest of the user space mutex state is consistent, so a woken - * waiter will just take over the uncontended futex. Setting the - * OWNER_DIED bit would create inconsistent state and malfunction - * of the user space owner died handling. Otherwise, the OWNER_DIED - * bit is already set, and the woken waiter is expected to deal with - * this. + * Note in particular that in all of the states A-D the owner + * portion of the futex word differs from our thread's TID + * (unless the actual owner has the same TID in another PID + * namespace, but we cannot currently distinguish that + * scenario), so this can be a special-case wakeup in the bail + * path of the ordinary TID check. */ owner = uval & FUTEX_TID_MASK; - if (pending_op && !pi && !owner) { - futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1, - FUTEX_BITSET_MATCH_ANY); + if (owner != task_pid_vnr(curr)) { + if (pending_op && !pi && (!owner || !(uval & FUTEX_WAITERS))) { + futex_wake(uaddr, FLAGS_SIZE_32 | FLAGS_SHARED, NULL, 1, + FUTEX_BITSET_MATCH_ANY); + } return 0; } - if (owner != task_pid_vnr(curr)) - return 0; - /* * Ok, this dying thread is truly holding a futex * of interest. Set the OWNER_DIED bit atomically From 2fd9b4cfcefe30cb506072f78f2cd3b6dc8a29b1 Mon Sep 17 00:00:00 2001 From: Tao Cui Date: Tue, 28 Jul 2026 17:57:07 +0800 Subject: [PATCH 381/501] Docs/admin-guide/cgroup-v2: document io.latency rotational vs non-rotational behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit io.latency is documented only in terms of average latency and the avg_lat stat, which matches rotational devices. On non-rotational devices a group misses its target once enough of the IOs in the window individually exceed it, and io.stat reports missed/total rather than avg_lat/win. Describe both cases: how a miss is detected, note that the avg_lat tuning guidance is rotational-only, and update the io.stat field list (mark avg_lat/win as rotational-only, document missed/total). Acked-by: Michal Koutný Signed-off-by: Tao Cui Signed-off-by: Tejun Heo --- Documentation/admin-guide/cgroup-v2.rst | 50 ++++++++++++++++++------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/Documentation/admin-guide/cgroup-v2.rst b/Documentation/admin-guide/cgroup-v2.rst index 14b8c571c0d1..aed195a71cbf 100644 --- a/Documentation/admin-guide/cgroup-v2.rst +++ b/Documentation/admin-guide/cgroup-v2.rst @@ -2239,9 +2239,12 @@ IO Latency ~~~~~~~~~~ This is a cgroup v2 controller for IO workload protection. You provide a group -with a latency target, and if the average latency exceeds that target the -controller will throttle any peers that have a lower latency target than the -protected workload. +with a latency target, and if the group misses its target the controller will +throttle any peers that have a lower latency target than the protected +workload. How a miss is detected depends on the device: on rotational devices +the average latency over the window must exceed the target, while on +non-rotational devices a miss is counted once enough of the IOs in the window +individually exceed the target. The limits are only applied at the peer level in the hierarchy. This means that in the diagram below, only groups A, B, and C will influence each other, and @@ -2258,10 +2261,12 @@ So the ideal way to configure this is to set io.latency in groups A, B, and C. Generally you do not want to set a value lower than the latency your device supports. Experiment to find the value that works best for your workload. Start at higher than the expected latency for your device and, with -blkcg_debug_stats enabled, watch the avg_lat value in io.stat for your -workload group to get an idea of the latency you see during normal operation. -Use the avg_lat value as a basis for your real setting, setting at 10-15% -higher than the value in io.stat. +blkcg_debug_stats enabled, observe io.stat for your workload group to get an +idea of the latency you see during normal operation. On rotational devices, +use the avg_lat value as a basis for your real setting, setting it 10-15% +higher. On non-rotational devices io.stat reports no average latency; set +the target based on your device and use the missed/total fields to verify it +is being met. How IO Latency Throttling Works ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -2303,19 +2308,36 @@ IO Latency Interface Files the blkcg_debug_stats module parameter is enabled (it is disabled by default). + The reported latency fields depend on the device. Rotational devices + report avg_lat and win; non-rotational devices report missed and total + instead. missed and total are live counters for the current window and + may change between reads. + depth This is the current queue depth for the group. avg_lat - This is an exponential moving average with a decay rate of 1/exp - bound by the sampling interval. The decay rate interval can be - calculated by multiplying the win value in io.stat by the - corresponding number of samples based on the win value. + (Rotational devices only.) This is an exponential moving + average with a decay rate of 1/exp bound by the sampling + interval. The decay rate interval can be calculated by + multiplying the win value in io.stat by the corresponding number + of samples based on the win value. win - The sampling window size in milliseconds. This is the minimum - duration of time between evaluation events. Windows only elapse - with IO activity. Idle periods extend the most recent window. + (Rotational devices only.) The sampling window size in + milliseconds. This is the minimum duration of time between + evaluation events. Windows only elapse with IO activity. Idle + periods extend the most recent window. + + missed + (Non-rotational devices only.) The number of IOs in the + current window whose latency exceeded the target. A group is + considered to be missing its target once missed reaches a + certain ratio of total. + + total + (Non-rotational devices only.) The total number of IOs + accounted in the current window. IO Priority ~~~~~~~~~~~ From 075b74841bd0065a3bda3440873c747938e69b68 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 2 Aug 2026 16:24:24 -0700 Subject: [PATCH 382/501] Linux 7.2-rc6 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 060834cd0a4d..902f3f3d54b7 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 7 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc6 NAME = Baby Opossum Posse # *DOCUMENTATION* From b2d5a81dae385333f9734910277fbf94c78bd17f Mon Sep 17 00:00:00 2001 From: Lin Jiapeng Date: Tue, 28 Jul 2026 15:19:10 +0800 Subject: [PATCH 383/501] xfs: fix exchange-range reflink flag clearing issue with INO1_WRITTEN When exchanging two full-file ranges, xmi_can_exchange_reflink_flags() can move the reflink inode flag from the file that currently has it to the other file, as long as exactly one side is marked. This assumes that the file contents, and therefore all shared extents, are exchanged. That assumption is not true when XFS_EXCHMAPS_INO1_WRITTEN is set. xfs_exchmaps_can_skip_mapping() can skip hole and unwritten mappings from file1, so an exchange can complete without moving every mapping that the earlier flag-swap decision accounted for. In that case the post-operation cleanup can clear the reflink flag from an inode that still owns shared written extents. Later writes then take the non-reflink write path and may update blocks that should still have been protected by CoW, which shows up as data corruption between reflink-related files. Fix this by disabling the reflink flag exchange whenever XFS_EXCHMAPS_INO1_WRITTEN is requested. The contents exchange can still proceed; the conservative outcome is that both inodes keep the reflink flag. The regular reflink flag cleanup path can drop the extra flag later once the inode no longer has shared extents. Reported-by: Lin Jiapeng (TencentOS Red Team) Fixes: 966ceafc7a43 ("xfs: create deferred log items for file mapping exchanges") Cc: stable@vger.kernel.org # v6.10 Reviewed-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Lin Jiapeng Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_exchmaps.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/fs/xfs/libxfs/xfs_exchmaps.c b/fs/xfs/libxfs/xfs_exchmaps.c index dcd0bd0b13b4..3efed37cb98a 100644 --- a/fs/xfs/libxfs/xfs_exchmaps.c +++ b/fs/xfs/libxfs/xfs_exchmaps.c @@ -959,6 +959,16 @@ xmi_can_exchange_reflink_flags( { struct xfs_mount *mp = req->ip1->i_mount; + /* + * The INO1_WRITTEN optimization can skip exchanging hole and + * unwritten mappings, which means we cannot guarantee that all + * shared extents actually moved to the other file. Clearing the + * reflink flag of an inode that still holds shared extents breaks + * the CoW write path, so refuse to exchange the flags in that case. + */ + if (req->flags & XFS_EXCHMAPS_INO1_WRITTEN) + return false; + if (hweight32(reflink_state) != 1) return false; if (req->startoff1 != 0 || req->startoff2 != 0) From fb082235c67551d526704e07a4efb82ac3a9ba48 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Wed, 22 Jul 2026 11:17:59 +0200 Subject: [PATCH 384/501] xfs: fix inverted clearance of inode junk flags sashiko.dev noticed that these checks clear all the valid flags instead of invalid. This probably was never hit as it only executed on invalid flag presence. Fixes: 2d295fe65776 ("xfs: repair inode records") Signed-off-by: Andrey Albershteyn Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/inode_repair.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/inode_repair.c b/fs/xfs/scrub/inode_repair.c index b88427a4460c..8bc508336aa5 100644 --- a/fs/xfs/scrub/inode_repair.c +++ b/fs/xfs/scrub/inode_repair.c @@ -1797,7 +1797,7 @@ xrep_inode_flags( /* Clear junk flags */ if (sc->ip->i_diflags & ~XFS_DIFLAG_ANY) - sc->ip->i_diflags &= ~XFS_DIFLAG_ANY; + sc->ip->i_diflags &= XFS_DIFLAG_ANY; /* NEWRTBM only applies to realtime bitmaps */ if (I_INO(sc->ip) == sc->mp->m_sb.sb_rbmino) @@ -1828,7 +1828,7 @@ xrep_inode_flags( /* Clear junk flags. */ if (sc->ip->i_diflags2 & ~XFS_DIFLAG2_ANY) - sc->ip->i_diflags2 &= ~XFS_DIFLAG2_ANY; + sc->ip->i_diflags2 &= XFS_DIFLAG2_ANY; /* No reflink flag unless we support it and it's a file. */ if (!xfs_has_reflink(sc->mp) || !S_ISREG(mode)) From 97efed1091a50e4dbf31307015138b43a972d2e4 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:22:59 -0700 Subject: [PATCH 385/501] xfs: only check mergeability of bnobt records In the cntbt (free space by block count) btree, records are not supposed to be in startblock order. Hence the mergeability check is pointless. Remove it, since it does nothing, as LOLLM points out. Cc: stable@vger.kernel.org # v6.4 Fixes: d5784ae82778d9 ("xfs: flag free space btree records that could be merged") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/alloc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c index 48edaa2cb1e0..c666be69f164 100644 --- a/fs/xfs/scrub/alloc.c +++ b/fs/xfs/scrub/alloc.c @@ -136,7 +136,7 @@ xchk_allocbt_rec( const union xfs_btree_rec *rec) { struct xfs_alloc_rec_incore irec; - struct xchk_alloc *ca = bs->private; + struct xchk_alloc *ca = bs->private; xfs_alloc_btrec_to_irec(rec, &irec); if (xfs_alloc_check_irec(to_perag(bs->cur->bc_group), &irec) != NULL) { @@ -144,7 +144,8 @@ xchk_allocbt_rec( return 0; } - xchk_allocbt_mergeable(bs, ca, &irec); + if (bs->sc->sm->sm_type == XFS_SCRUB_TYPE_BNOBT) + xchk_allocbt_mergeable(bs, ca, &irec); xchk_allocbt_xref(bs->sc, &irec); return 0; From 5fc643fb86599e29b38e7b2c2680b4b15bf8f772 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:15 -0700 Subject: [PATCH 386/501] xfs: don't double-lock when deleting a self-referential directory LOLLM notices that the dirtree scrubber can detect a directory that refers to itself. In this case, it's not correct for the directory tree repair code to try to iolock/ilock both sc->ip and dp, because they're the same inode. Fix this by detecting that corner case and handling it appropriately. Cc: stable@vger.kernel.org # v6.10 Fixes: 3f31406aef493b ("xfs: fix corruptions in the directory tree") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/dirtree_repair.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/fs/xfs/scrub/dirtree_repair.c b/fs/xfs/scrub/dirtree_repair.c index 1c0d7ea4a5be..bbf6acf6fd40 100644 --- a/fs/xfs/scrub/dirtree_repair.c +++ b/fs/xfs/scrub/dirtree_repair.c @@ -349,6 +349,8 @@ xrep_dirtree_unlink_iolock( ASSERT(sc->ilock_flags & XFS_IOLOCK_EXCL); + if (sc->ip == dp) + return 0; if (xfs_ilock_nowait(dp, XFS_IOLOCK_EXCL)) return 0; @@ -400,8 +402,18 @@ xrep_dirtree_unlink( * directory code can handle a reservationless update. */ resblks = xfs_remove_space_res(mp, step->name_len); - error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, - &resblks, &sc->tp, &dontcare); + if (sc->ip == dp) { +again: + error = xfs_trans_alloc_inode(dp, &M_RES(mp)->tr_remove, + resblks, 0, false, &sc->tp); + if ((error == -ENOSPC || error == -EDQUOT) && resblks > 0) { + resblks = 0; + goto again; + } + } else { + error = xfs_trans_alloc_dir(dp, &M_RES(mp)->tr_remove, sc->ip, + &resblks, &sc->tp, &dontcare); + } if (error) goto out_iolock; @@ -489,9 +501,11 @@ xrep_dirtree_unlink( xchk_trans_cancel(sc); out_ilock: xfs_iunlock(sc->ip, XFS_ILOCK_EXCL); - xfs_iunlock(dp, XFS_ILOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_ILOCK_EXCL); out_iolock: - xfs_iunlock(dp, XFS_IOLOCK_EXCL); + if (dp != sc->ip) + xfs_iunlock(dp, XFS_IOLOCK_EXCL); return error; } From 8af9cd79cdf6ee96ec610d707db22244fa21eb40 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:31 -0700 Subject: [PATCH 387/501] xfs: don't return EFSCORRUPTED when scrubbing corrupt parent pointers LOLLM noticed that scrub sets the CORRUPT flag when xfs_parent_from_attr thinks it's been given a corrupt parent pointer. This eliminates the potential to repair the filesystem because that error code is bubbled up the call stack. Fix this by collapsing them all to ECANCELED in xchk_parent_pptr, which doesn't have that trait. Cc: stable@vger.kernel.org # v6.10 Fixes: 0d29a20fbdba89 ("xfs: scrub parent pointers") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/parent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/parent.c b/fs/xfs/scrub/parent.c index a8c4807e1d94..99b60773b715 100644 --- a/fs/xfs/scrub/parent.c +++ b/fs/xfs/scrub/parent.c @@ -485,7 +485,7 @@ xchk_parent_scan_attr( valuelen, &parent_ino, NULL); if (error) { xchk_fblock_set_corrupt(sc, XFS_ATTR_FORK, 0); - return error; + return -ECANCELED; } /* No self-referential parent pointers. */ From 5ee37132ea81abd36213b31a72140660c2aac54b Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:23:46 -0700 Subject: [PATCH 388/501] xfs: don't zap the attr fork on repair when there are queued pptr updates LOLLM noticed that xrep_xattr_rebuild_tree doesn't check for queued parent pointer updates when it decides that it's going to zap the attr fork. This is obviously incorrect, so fix that. We hold the IOLOCK and the ILOCK of sc->ip at that point in time, so we can't race with any /new/ operations. Cc: stable@vger.kernel.org # v6.10 Fixes: e5d7ce0364d8ee ("xfs: replay unlocked parent pointer updates that accrue during xattr repair") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/attr_repair.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/xfs/scrub/attr_repair.c b/fs/xfs/scrub/attr_repair.c index be627ab655ad..6e6af142f1fb 100644 --- a/fs/xfs/scrub/attr_repair.c +++ b/fs/xfs/scrub/attr_repair.c @@ -1427,7 +1427,8 @@ xrep_xattr_rebuild_tree( * If we didn't find any attributes to salvage, repair the file by * zapping its attr fork. */ - if (rx->attrs_found == 0) { + if (rx->attrs_found == 0 && + (!xfs_has_parent(sc->mp) || xfarray_length(rx->pptr_recs) == 0)) { xfs_trans_ijoin(sc->tp, sc->ip, 0); error = xrep_xattr_reset_fork(sc); if (error) From b1a296fc2241f724ef8f14da6a4efa800d444dac Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:02 -0700 Subject: [PATCH 389/501] xfs: nlink scrub must take IOLOCK before determining ILOCK state In xchk_nlinks_ilock_dir, take the IOLOCK before accessing internal inode state to figure out if we need to take ILOCK shared or exclusive. That way we can't race with directory updates. LOLLM pointed out that the code was initially correct w.r.t. the IOLOCK, but then I broke it. Cc: stable@vger.kernel.org # v6.18 Fixes: f477af0cfa0487 ("xfs: fix locking in xchk_nlinks_collect_dir") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/nlinks.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/xfs/scrub/nlinks.c b/fs/xfs/scrub/nlinks.c index 355ab6de23ea..bcedb8c3e4e6 100644 --- a/fs/xfs/scrub/nlinks.c +++ b/fs/xfs/scrub/nlinks.c @@ -382,6 +382,12 @@ xchk_nlinks_ilock_dir( { uint lock_mode = XFS_ILOCK_SHARED; + /* + * Take the IOLOCK so that other threads cannot start a directory + * update while we're scanning. + */ + xfs_ilock(ip, XFS_IOLOCK_SHARED); + /* * We're going to scan the directory entries, so we must be ready to * pull the data fork mappings into memory if they aren't already. @@ -397,13 +403,8 @@ xchk_nlinks_ilock_dir( xfs_need_iread_extents(&ip->i_af)) lock_mode = XFS_ILOCK_EXCL; - /* - * Take the IOLOCK so that other threads cannot start a directory - * update while we're scanning. - */ - lock_mode |= XFS_IOLOCK_SHARED; xfs_ilock(ip, lock_mode); - return lock_mode; + return lock_mode | XFS_IOLOCK_SHARED; } /* Walk a directory to bump the observed link counts of the children. */ From 0c88e10d12de9ca7cbed1467bb1b52310101bff8 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:17 -0700 Subject: [PATCH 390/501] xfs: avoid UAF on sc->tempip in xrep_tempfile_create LOLLM noticed a potential UAF if the tempfile creation code fails after it set sc->tempip. Fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: 84c14ee39dd388 ("xfs: create temporary files and directories for online repair") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/tempfile.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/scrub/tempfile.c b/fs/xfs/scrub/tempfile.c index e0c630f888cf..98820003b929 100644 --- a/fs/xfs/scrub/tempfile.c +++ b/fs/xfs/scrub/tempfile.c @@ -174,6 +174,7 @@ xrep_tempfile_create( xfs_iunlock(sc->tempip, XFS_ILOCK_EXCL); xfs_finish_inode_setup(sc->tempip); xchk_irele(sc, sc->tempip); + sc->tempip = NULL; } out_release_dquots: xfs_qm_dqrele(udqp); From 7cdafd8f10ebdf745ba6046b9fa67490c343a17f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:33 -0700 Subject: [PATCH 391/501] xfs: hoist per-bucket unlinked list check to helper In the next patch we're going to make this loop more exciting, so hoist the code to a helper function to reduce clutter in the resulting code. Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 62 +++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 21 deletions(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 62ed5eaf08fb..cecf034ef989 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -932,6 +932,42 @@ xchk_agi_xref( /* scrub teardown will take care of sc->sa for us */ } +/* + * Walk the incore unlinked list for a particular AGI bucket to construct + * the unlinked inode bitmap for later reconstruction of the unlinked list. + * Returns 1 if we should keep checking, or 0 to stop checking. + */ +static int +xchk_iunlink_bucket( + struct xfs_scrub *sc, + unsigned int bucket, + xfs_agino_t agino) +{ + while (agino != NULLAGINO) { + struct xfs_inode *ip; + + if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + ip = xfs_iunlink_lookup(sc->sa.pag, agino); + if (!ip) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + if (!xfs_inode_on_unlinked_list(ip)) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + return 0; + } + + agino = ip->i_next_unlinked; + } + + return 1; +} + /* * Check the unlinked buckets for links to bad inodes. We hold the AGI, so * there cannot be any threads updating unlinked list pointers in this AG. @@ -942,30 +978,14 @@ xchk_iunlink( struct xfs_agi *agi) { unsigned int i; - struct xfs_inode *ip; for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { - xfs_agino_t agino = be32_to_cpu(agi->agi_unlinked[i]); + int ret; - while (agino != NULLAGINO) { - if (agino % XFS_AGI_UNLINKED_BUCKETS != i) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - ip = xfs_iunlink_lookup(sc->sa.pag, agino); - if (!ip) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - if (!xfs_inode_on_unlinked_list(ip)) { - xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return; - } - - agino = ip->i_next_unlinked; - } + ret = xchk_iunlink_bucket(sc, i, + be32_to_cpu(agi->agi_unlinked[i])); + if (ret < 1) + return; } } From 527eaaefddb6ec5c83a06c9a1559960dd6361753 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:24:48 -0700 Subject: [PATCH 392/501] xfs: don't livelock in scrub on a circular unlinked list LOLLM points out that online fsck can livelock if an unlinked inode list contains a loop. Use a bitmap to detect cycles. Cc: stable@vger.kernel.org # v4.15 Fixes: a12890aebb8959 ("xfs: scrub the AGI") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader.c | 44 +++++++++++++++++++++++++++------- fs/xfs/scrub/agheader_repair.c | 17 +++++++++++-- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index cecf034ef989..1fa66aa68e16 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -18,6 +18,8 @@ #include "xfs_inode.h" #include "scrub/scrub.h" #include "scrub/common.h" +#include "scrub/bitmap.h" +#include "scrub/agino_bitmap.h" int xchk_setup_agheader( @@ -935,7 +937,8 @@ xchk_agi_xref( /* * Walk the incore unlinked list for a particular AGI bucket to construct * the unlinked inode bitmap for later reconstruction of the unlinked list. - * Returns 1 if we should keep checking, or 0 to stop checking. + * Returns 1 if we should keep checking, 0 to stop checking, or a negative + * errno. */ static int xchk_iunlink_bucket( @@ -943,36 +946,57 @@ xchk_iunlink_bucket( unsigned int bucket, xfs_agino_t agino) { + struct xagino_bitmap seen; + int ret; + + xagino_bitmap_init(&seen); + while (agino != NULLAGINO) { struct xfs_inode *ip; + unsigned int len = 1; if (agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; + } + + if (xagino_bitmap_test(&seen, agino, &len)) { + xchk_block_set_corrupt(sc, sc->sa.agi_bp); + goto bad; } ip = xfs_iunlink_lookup(sc->sa.pag, agino); if (!ip) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; } if (!xfs_inode_on_unlinked_list(ip)) { xchk_block_set_corrupt(sc, sc->sa.agi_bp); - return 0; + goto bad; } + ret = xagino_bitmap_set(&seen, agino, 1); + if (ret) + goto out_bitmap; + agino = ip->i_next_unlinked; } + ret = 1; - return 1; +out_bitmap: + xagino_bitmap_destroy(&seen); + return ret; +bad: + ret = 0; + goto out_bitmap; } /* * Check the unlinked buckets for links to bad inodes. We hold the AGI, so * there cannot be any threads updating unlinked list pointers in this AG. */ -STATIC void +STATIC int xchk_iunlink( struct xfs_scrub *sc, struct xfs_agi *agi) @@ -985,8 +1009,10 @@ xchk_iunlink( ret = xchk_iunlink_bucket(sc, i, be32_to_cpu(agi->agi_unlinked[i])); if (ret < 1) - return; + return ret; } + + return 0; } /* Scrub the AGI. */ @@ -1073,7 +1099,9 @@ xchk_agi( if (pag->pagi_freecount != be32_to_cpu(agi->agi_freecount)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); - xchk_iunlink(sc, agi); + error = xchk_iunlink(sc, agi); + if (error) + goto out; xchk_agi_xref(sc); out: diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 2554494847ff..13074d5e319c 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1080,18 +1080,22 @@ xrep_iunlink_walk_ondisk_bucket( struct xrep_agi *ragi, unsigned int bucket) { + struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; struct xfs_agi *agi = sc->sa.agi_bp->b_addr; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino; int error = 0; + xagino_bitmap_init(&seen); + next_agino = be32_to_cpu(agi->agi_unlinked[bucket]); while (next_agino != NULLAGINO) { xfs_agino_t agino = next_agino; + unsigned int len = 1; if (xchk_should_terminate(ragi->sc, &error)) - return error; + goto out_bitmap; trace_xrep_iunlink_walk_ondisk_bucket(sc->sa.pag, bucket, prev_agino, agino); @@ -1099,15 +1103,24 @@ xrep_iunlink_walk_ondisk_bucket( if (bucket != agino % XFS_AGI_UNLINKED_BUCKETS) break; + if (xagino_bitmap_test(&seen, agino, &len)) + break; + next_agino = xrep_iunlink_next(sc, agino); if (!next_agino) next_agino = xrep_iunlink_reload_next(ragi, prev_agino, agino); + error = xagino_bitmap_set(&seen, agino, 1); + if (error) + goto out_bitmap; + prev_agino = agino; } - return 0; +out_bitmap: + xagino_bitmap_destroy(&seen); + return error; } /* Decide if this is an unlinked inode in this AG. */ From 1e96a00e0d3a00be6e4f368b2f18e2d345f813ce Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:04 -0700 Subject: [PATCH 393/501] xfs: don't walk off the end of a null sc->sa.agi_bp in AGI repair LOLLM noticed a longstanding bug where xrep_iunlink_walk_ondisk_bucket tries to walk ragi->sc->sa.agi_bp to rebuild the unlinked inode lists. Unfortunately, it's possible for agi_bp to be null if the buffer verifier fails, so we have to use ragi->agi_bp (which skips verifier checks) instead. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 13074d5e319c..39468b8fa986 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1082,7 +1082,7 @@ xrep_iunlink_walk_ondisk_bucket( { struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; - struct xfs_agi *agi = sc->sa.agi_bp->b_addr; + struct xfs_agi *agi = ragi->agi_bp->b_addr; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino; int error = 0; From 6b9cd540138a06660a843a519facc147060acbef Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:19 -0700 Subject: [PATCH 394/501] xfs: load next_agino from the correct xfarray in xrep_iunlink_relink_prev LOLLM notices that xrep_iunlink_relink_prev has the comment "set the forward pointer..." but then loads the value from the xfarray that stores pointers to the previous inode in the unlinked list. That's wrong, so fix the variable access. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 39468b8fa986..5204eab09c64 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1619,7 +1619,7 @@ xrep_iunlink_relink_prev( want_rele = true; /* Set the forward pointer since this just came off disk. */ - error = xfarray_load(ragi->iunlink_prev, agino, &next_agino); + error = xfarray_load(ragi->iunlink_next, agino, &next_agino); if (error) goto out_rele; From 5644fab990fc72406dddc91cbb8304659d77f3f1 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:35 -0700 Subject: [PATCH 395/501] xfs: pass runtime errors from xrep_iunlink_mark_ondisk_rec up to callers LOLLM points out that the only error that xrep_iunlink_mark_ondisk_rec returns is ENOMEM, but we ignore that, and can end up writing a garbage AGI based on incomplete information. We shouldn't do that, though here we must be screen out EFSCORRUPTED/EFSBASDCRC because we haven't checked the inobt yet. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 5204eab09c64..7f1955484303 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1309,7 +1309,7 @@ xrep_iunlink_mark_ondisk_rec( * iunlink_bmp. We haven't checked the inobt yet, so we don't error out if * the btree is corrupt. */ -STATIC void +STATIC int xrep_iunlink_mark_ondisk( struct xrep_agi *ragi) { @@ -1321,6 +1321,14 @@ xrep_iunlink_mark_ondisk( cur = xfs_inobt_init_cursor(sc->sa.pag, sc->tp, agi_bp); error = xfs_btree_query_all(cur, xrep_iunlink_mark_ondisk_rec, ragi); xfs_btree_del_cursor(cur, error); + + /* + * Don't proceed if we couldn't set a bit in the bitmap. All other + * errors we ignore because we haven't actually checked the inobt yet. + */ + if (error == -ENOMEM) + return -ENOMEM; + return 0; } /* @@ -1508,7 +1516,9 @@ xrep_iunlink_rebuild_buckets( * If there are ondisk inodes that are unlinked and are not been loaded * into cache, record them in iunlink_bmp. */ - xrep_iunlink_mark_ondisk(ragi); + error = xrep_iunlink_mark_ondisk(ragi); + if (error) + return error; /* * Walk each iunlink bucket to (re)construct as much of the incore list From 2daf3ed5d059dec79c123aec42eb8d28e0c016d4 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:25:50 -0700 Subject: [PATCH 396/501] xfs: check xfarray iteration errors when committing unlinked inode lists LOLLM noticed that we neglect to check for xfarray_iter itself returning errors when writing a new AGI. Fix that. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 7f1955484303..8514d2d7e3ce 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1675,6 +1675,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Fix all the back links */ idx = XFARRAY_CURSOR_INIT; @@ -1683,6 +1685,8 @@ xrep_iunlink_commit( if (error) return error; } + if (error < 0) + return error; /* Copy the staged iunlink buckets to the new AGI. */ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { From 68ab37650ce5195b4f4f8466444a36a78207840c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:06 -0700 Subject: [PATCH 397/501] xfs: fix allocated inodes that show up in the unlinked list If an allocated inode shows up in the unlinked list, we need to get it completely off the list. Set the corrected next/prev pointers such that the inode will not look like it should be on an unlinked list at all. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 63 ++++++++++++++++++++++++++++++---- fs/xfs/scrub/trace.h | 1 + 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 8514d2d7e3ce..41de5cf87352 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -979,6 +979,13 @@ xrep_agi_calc_from_btrees( return error; } +/* + * Magic value that means "not unlinked" because xfarrays don't support storing + * totally zeroed elements. There can't be a cluster that starts in daddr 0 so + * there can't be an inode #1 either. + */ +#define LINKED_AGINO (0x1) + /* * Record a forwards unlinked chain pointer from agino -> next_agino in our * staging information. @@ -1362,6 +1369,35 @@ xrep_iunlink_resolve_bucket( break; } + if (VFS_I(ip)->i_nlink != 0) { + /* + * Inode is linked somewhere! Blow out both unlinked + * list pointers, advance the list, and pretend we + * didn't see this inode. Clear it from iunlink_bmp + * because it's linked. + */ + trace_xrep_iunlink_resolve_allocated(sc->sa.pag, + bucket, prev_agino, next_agino); + + error = xrep_iunlink_store_next(ragi, next_agino, + NULLAGINO); + if (error) + return error; + + error = xrep_iunlink_store_prev(ragi, next_agino, + LINKED_AGINO); + if (error) + return error; + + error = xagino_bitmap_clear(&ragi->iunlink_bmp, + next_agino, 1); + if (error) + return error; + + next_agino = ip->i_next_unlinked; + continue; + } + if (next_agino % XFS_AGI_UNLINKED_BUCKETS != bucket) { /* * Inode is in the wrong bucket. Advance the list, @@ -1540,6 +1576,24 @@ xrep_iunlink_rebuild_buckets( xrep_iunlink_add_lost_inodes, ragi); } +static inline void +set_inode_prev_unlinked( + struct xfs_inode *ip, + xfs_agino_t prev_agino) +{ + /* + * Magic value that means "not unlinked" because xfarrays don't support + * storing totally zeroed elements. + */ + if (prev_agino == LINKED_AGINO) + prev_agino = 0; + + if (ip->i_prev_unlinked != prev_agino) { + trace_xrep_iunlink_relink_prev(ip, prev_agino); + ip->i_prev_unlinked = prev_agino; + } +} + /* Update i_next_iunlinked for the inode @agino. */ STATIC int xrep_iunlink_relink_next( @@ -1573,8 +1627,7 @@ xrep_iunlink_relink_next( if (error) goto out_rele; - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; + set_inode_prev_unlinked(ip, prev_agino); } /* Update the forward pointer. */ @@ -1641,11 +1694,7 @@ xrep_iunlink_relink_prev( ip->i_next_unlinked = next_agino; } - /* Update the backward pointer. */ - if (ip->i_prev_unlinked != prev_agino) { - trace_xrep_iunlink_relink_prev(ip, prev_agino); - ip->i_prev_unlinked = prev_agino; - } + set_inode_prev_unlinked(ip, prev_agino); out_rele: /* diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index d5d39d82749e..00fbe1b9c235 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -3542,6 +3542,7 @@ DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_ok); +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_allocated); TRACE_EVENT(xrep_iunlink_relink_next, TP_PROTO(struct xfs_inode *ip, xfs_agino_t next_agino), From 6d67c6b99f1fc07c64b97fcbc974c6f1ada7f622 Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:21 -0700 Subject: [PATCH 398/501] xfs: fix another iunlink infinite loop bug in online fsck xrep_iunlink_resolve_bucket is supposed to reconstruct as much of the incore prev and next unlinked list pointers based on what it finds on disk and in memory before we move on to relinking the truly lost inodes back into the unlinked list. However, it's still vulnerable to infinite loops that come in via the next_unlinked pointers. Fix this problem by remembering which inodes we've already seen and checking new agino pointers against that. If a bit is already set, either this is a loop or the inode has nonzero link count. We'll deal with the second case in a subsequent patch. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 37 +++++++++++++++++++++++++--------- fs/xfs/scrub/trace.h | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 41de5cf87352..65b9a8befce9 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1348,15 +1348,32 @@ xrep_iunlink_resolve_bucket( struct xrep_agi *ragi, unsigned int bucket) { + struct xagino_bitmap seen; struct xfs_scrub *sc = ragi->sc; struct xfs_inode *ip; xfs_agino_t prev_agino = NULLAGINO; xfs_agino_t next_agino = ragi->iunlink_heads[bucket]; int error = 0; + xagino_bitmap_init(&seen); + while (next_agino != NULLAGINO) { + unsigned int len = 1; + if (xchk_should_terminate(ragi->sc, &error)) - return error; + goto out_bitmap; + + /* Inode already seen? We're stuck in a loop */ + if (xagino_bitmap_test(&seen, next_agino, &len)) { + trace_xrep_iunlink_resolve_infinite_loop(sc->sa.pag, + bucket, prev_agino, next_agino); + next_agino = NULLAGINO; + break; + } + + error = xagino_bitmap_set(&seen, next_agino, 1); + if (error) + goto out_bitmap; /* Find the next inode in the chain. */ ip = xfs_iunlink_lookup(sc->sa.pag, next_agino); @@ -1382,17 +1399,17 @@ xrep_iunlink_resolve_bucket( error = xrep_iunlink_store_next(ragi, next_agino, NULLAGINO); if (error) - return error; + goto out_bitmap; error = xrep_iunlink_store_prev(ragi, next_agino, LINKED_AGINO); if (error) - return error; + goto out_bitmap; error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1); if (error) - return error; + goto out_bitmap; next_agino = ip->i_next_unlinked; continue; @@ -1433,20 +1450,20 @@ xrep_iunlink_resolve_bucket( */ error = xagino_bitmap_clear(&ragi->iunlink_bmp, next_agino, 1); if (error) - return error; + goto out_bitmap; /* Remember the previous inode's next pointer. */ if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } /* Remember this inode's previous pointer. */ error = xrep_iunlink_store_prev(ragi, next_agino, prev_agino); if (error) - return error; + goto out_bitmap; /* Advance the list and remember this inode. */ prev_agino = next_agino; @@ -1457,10 +1474,12 @@ xrep_iunlink_resolve_bucket( if (prev_agino != NULLAGINO) { error = xrep_iunlink_store_next(ragi, prev_agino, next_agino); if (error) - return error; + goto out_bitmap; } - return 0; +out_bitmap: + xagino_bitmap_destroy(&seen); + return error; } /* Reinsert this unlinked inode into the head of the staged bucket list. */ diff --git a/fs/xfs/scrub/trace.h b/fs/xfs/scrub/trace.h index 00fbe1b9c235..14aa0ec1f09e 100644 --- a/fs/xfs/scrub/trace.h +++ b/fs/xfs/scrub/trace.h @@ -3538,6 +3538,7 @@ DEFINE_EVENT(xrep_iunlink_resolve_class, name, \ TP_PROTO(const struct xfs_perag *pag, unsigned int bucket, \ xfs_agino_t prev_agino, xfs_agino_t next_agino), \ TP_ARGS(pag, bucket, prev_agino, next_agino)) +DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_infinite_loop); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_uncached); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_wronglist); DEFINE_REPAIR_IUNLINK_RESOLVE_EVENT(xrep_iunlink_resolve_nolist); From af146cb7ff8ff5c54162f35c238f3ff1d5ad110f Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:37 -0700 Subject: [PATCH 399/501] xfs: set the prev pointer when reinserting an inode on the unlinked list If we find a rogue free inode and decide to reinsert it into the unlinked list, we need to set the prev pointer to NULLAGINO so that the incore list gets updated. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 65b9a8befce9..4f1f235349c5 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1504,6 +1504,10 @@ xrep_iunlink_add_to_bucket( if (error) return error; + error = xrep_iunlink_store_prev(ragi, agino, NULLAGINO); + if (error) + return error; + /* Remember the head inode's previous pointer. */ if (current_head != NULLAGINO) { error = xrep_iunlink_store_prev(ragi, current_head, agino); From 0052633527158b49762ab427e73924e4f8d25e6c Mon Sep 17 00:00:00 2001 From: "Darrick J. Wong" Date: Sun, 26 Jul 2026 22:26:52 -0700 Subject: [PATCH 400/501] xfs: don't ignore runtime errors in xrep_iunlink_reload_next LOLLM complained that this function ignores runtime errors being returned by xrep_iunlink_store_*. Rework the function signature so that we can return runtime errors to abort the repair. Cc: stable@vger.kernel.org # v6.10 Fixes: ab97f4b1c03075 ("xfs: repair AGI unlinked inode bucket lists") Signed-off-by: Darrick J. Wong Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig Signed-off-by: Carlos Maiolino --- fs/xfs/scrub/agheader_repair.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 4f1f235349c5..2104512f1ee1 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -1041,31 +1041,40 @@ xrep_iunlink_next( * the chain or if we should stop walking the chain due to corruption; or a * per-AG inode number. */ -STATIC xfs_agino_t +STATIC int xrep_iunlink_reload_next( struct xrep_agi *ragi, xfs_agino_t prev_agino, - xfs_agino_t agino) + xfs_agino_t agino, + xfs_agino_t *next_agino) { struct xfs_scrub *sc = ragi->sc; struct xfs_inode *ip; - xfs_agino_t ret = NULLAGINO; int error; + *next_agino = NULLAGINO; + error = xchk_iget(ragi->sc, xfs_agino_to_ino(sc->sa.pag, agino), &ip); if (error) - return ret; + return 0; trace_xrep_iunlink_reload_next(ip, prev_agino); /* If this is a linked inode, stop processing the chain. */ if (VFS_I(ip)->i_nlink != 0) { - xrep_iunlink_store_next(ragi, agino, NULLAGINO); + error = xrep_iunlink_store_next(ragi, agino, NULLAGINO); + if (error) + return error; + + error = xrep_iunlink_store_prev(ragi, agino, LINKED_AGINO); + if (error) + return error; + goto rele; } ip->i_prev_unlinked = prev_agino; - ret = ip->i_next_unlinked; + *next_agino = ip->i_next_unlinked; /* * Drop the inode reference that we just took. We hold the AGI, so @@ -1074,7 +1083,7 @@ xrep_iunlink_reload_next( */ rele: xchk_irele(sc, ip); - return ret; + return 0; } /* @@ -1114,9 +1123,12 @@ xrep_iunlink_walk_ondisk_bucket( break; next_agino = xrep_iunlink_next(sc, agino); - if (!next_agino) - next_agino = xrep_iunlink_reload_next(ragi, prev_agino, - agino); + if (!next_agino) { + error = xrep_iunlink_reload_next(ragi, prev_agino, + agino, &next_agino); + if (error) + break; + } error = xagino_bitmap_set(&seen, agino, 1); if (error) From 63320a0f70f66f311f4bccff3af0719c2119f46c Mon Sep 17 00:00:00 2001 From: Long Li Date: Mon, 27 Jul 2026 10:38:48 +0800 Subject: [PATCH 401/501] xfs: fix ilock leak on error in xfs_dq_get_next_id xfs_dq_get_next_id() takes the quota inode ILOCK before calling xfs_iread_extents(). If xfs_iread_extents() fails, the function returns immediately without releasing the lock, leaking the quota inode ILOCK. This can leave the quota inode locked and cause subsequent quota operations to hang. Fix this by jumping to a common unlock path on error instead of returning directly. Fixes: bda250dbaf39f ("xfs: rewrite xfs_dq_get_next_id using xfs_iext_lookup_extent") Cc: stable@vger.kernel.org # v4.12 Signed-off-by: Long Li Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_dquot.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index c311f61d9554..b4f6c594808c 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -778,7 +778,7 @@ xfs_dq_get_next_id( lock_flags = xfs_ilock_data_map_shared(quotip); error = xfs_iread_extents(NULL, quotip, XFS_DATA_FORK); if (error) - return error; + goto out_unlock; if (xfs_iext_lookup_extent(quotip, "ip->i_df, start, &cur, &got)) { /* contiguous chunk, bump startoff for the id calculation */ @@ -789,6 +789,7 @@ xfs_dq_get_next_id( error = -ENOENT; } +out_unlock: xfs_iunlock(quotip, lock_flags); return error; From e2b4a856085e9bd939bde2dee0d08b1d41babde9 Mon Sep 17 00:00:00 2001 From: Long Li Date: Mon, 27 Jul 2026 10:38:49 +0800 Subject: [PATCH 402/501] xfs: don't swallow dquot recovery verification errors xlog_recover_dquot_commit_pass2() validates the recovered dquot with xfs_dqblk_verify() and, on failure, sets error = -EFSCORRUPTED and jumps to out_release. But out_release unconditionally returns 0, so the corruption error is discarded: the caller xlog_recover_items_pass2() sees success, log recovery proceeds as if the dquot were valid, and the corrupt quota buffer can be written back to disk. Fixes: 9c235dfc3d3f ("xfs: dquot recovery does not validate the recovered dquot") Cc: stable@vger.kernel.org # v6.8 Signed-off-by: Long Li Reviewed-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_dquot_item_recover.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/xfs/xfs_dquot_item_recover.c b/fs/xfs/xfs_dquot_item_recover.c index fe419b28de22..63bc9ab7d947 100644 --- a/fs/xfs/xfs_dquot_item_recover.c +++ b/fs/xfs/xfs_dquot_item_recover.c @@ -173,7 +173,7 @@ xlog_recover_dquot_commit_pass2( out_release: xfs_buf_relse(bp); - return 0; + return error; } const struct xlog_recover_item_ops xlog_dquot_item_ops = { From 63de19199342e2598373cbb99186fa93e9116603 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:02:48 +0200 Subject: [PATCH 403/501] xfs: add a separate bio_set for spliting GC writes Allocating the new bio for a split from the same pool as the original one can deadlock under memory pressure as the origin bio could be the last one from the mempool. Add a separate pool for splitting GC write bios to avoid this. Fixes: 080d01c41d44 ("xfs: implement zoned garbage collection") Signed-off-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_zone_gc.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index f76a09130852..e4f70e024632 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -130,6 +130,9 @@ struct xfs_zone_gc_data { /* bioset used to allocate the gc_bios */ struct bio_set bio_set; + /* bioset used when writes need to be split to hardware limits */ + struct bio_set split_bio_set; + /* * Scratchpad to buffer GC data, organized as a ring buffer over * discontiguous folios. scratch_head is where the buffer is filled, @@ -221,6 +224,9 @@ xfs_zone_gc_data_alloc( if (bioset_init(&data->bio_set, 16, offsetof(struct xfs_gc_bio, bio), BIOSET_NEED_BVECS)) goto out_free_recs; + if (bioset_init(&data->split_bio_set, 16, + offsetof(struct xfs_gc_bio, bio), 0)) + goto out_exit_bio_set; for (i = 0; i < XFS_GC_NR_BUFS; i++) { data->scratch_folios[i] = folio_alloc(GFP_KERNEL, get_order(XFS_GC_BUF_SIZE)); @@ -238,6 +244,8 @@ xfs_zone_gc_data_alloc( out_free_scratch: while (--i >= 0) folio_put(data->scratch_folios[i]); + bioset_exit(&data->split_bio_set); +out_exit_bio_set: bioset_exit(&data->bio_set); out_free_recs: kfree(data->iter.recs); @@ -254,6 +262,7 @@ xfs_zone_gc_data_free( for (i = 0; i < XFS_GC_NR_BUFS; i++) folio_put(data->scratch_folios[i]); + bioset_exit(&data->split_bio_set); bioset_exit(&data->bio_set); kfree(data->iter.recs); kfree(data); @@ -810,7 +819,8 @@ xfs_zone_gc_split_write( data->mp->m_sb.sb_blocksize) >> SECTOR_SHIFT; split_len = split_sectors << SECTOR_SHIFT; - split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, &data->bio_set); + split = bio_split(&chunk->bio, split_sectors, GFP_NOFS, + &data->split_bio_set); split_chunk = container_of(split, struct xfs_gc_bio, bio); split_chunk->data = data; ihold(VFS_I(chunk->ip)); From ec6978e6bf68fb6f51edd4f700f9a554dc0da894 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:02:49 +0200 Subject: [PATCH 404/501] xfs: add a comment to describe xfs_gc_bio.victim_rtg All other fields have comments describing them, add one for this field as well. Signed-off-by: Christoph Hellwig Reviewed-by: Damien Le Moal Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/xfs_zone_gc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/xfs/xfs_zone_gc.c b/fs/xfs/xfs_zone_gc.c index e4f70e024632..7ab8f2218c6a 100644 --- a/fs/xfs/xfs_zone_gc.c +++ b/fs/xfs/xfs_zone_gc.c @@ -103,6 +103,7 @@ struct xfs_gc_bio { /* Open Zone being written to */ struct xfs_open_zone *oz; + /* Realtime group currently being reclaimed */ struct xfs_rtgroup *victim_rtg; /* Bio used for reads and writes, including the bvec used by it */ From eb6b2cc1fc8ad566d746d128a559989ff0bba5cc Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 29 Jul 2026 15:00:58 +0200 Subject: [PATCH 405/501] xfs: check v5 superblock features early When working on a new features that reuses the existing pad in the superblock, I noticed that mounting such a file system on an old kernel logs a rather confusing warning: XFS (vdc): Metadir superblock padding fields must be zero. This is because we only validate the various feature fields in v5 superblocks after the common superblock validation helper is called. Fix this by calling the feature validation first. Fixes: eca383fcd63b ("xfs: refactor superblock verifiers") Cc: # v4.19 Signed-off-by: Christoph Hellwig Reviewed-by: Darrick J. Wong Signed-off-by: Carlos Maiolino --- fs/xfs/libxfs/xfs_sb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/xfs/libxfs/xfs_sb.c b/fs/xfs/libxfs/xfs_sb.c index 47322adb7690..75f2a021ee6d 100644 --- a/fs/xfs/libxfs/xfs_sb.c +++ b/fs/xfs/libxfs/xfs_sb.c @@ -1118,10 +1118,10 @@ xfs_sb_read_verify( * because _verify_common checks the on-disk values. */ __xfs_sb_from_disk(&sb, dsb, false); - error = xfs_validate_sb_common(mp, bp, &sb); + error = xfs_validate_sb_read(mp, &sb); if (error) goto out_error; - error = xfs_validate_sb_read(mp, &sb); + error = xfs_validate_sb_common(mp, bp, &sb); out_error: if (error == -EFSCORRUPTED || error == -EFSBADCRC) From 1ed35ac7f3fe2b4396bdd29ac3a7f0ebc0829e94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linfeng=20Sun=C2=A0?= Date: Sat, 20 Jun 2026 21:00:05 +0800 Subject: [PATCH 406/501] vhost_iotlb: bound map allocation in add_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vhost_iotlb_add_range_ctx() only retires an old entry when the table has a non-zero limit, has exactly reached that limit and has VHOST_IOTLB_FLAG_RETIRE set. Non-retiring tables can keep allocating entries after reaching their configured limit. Existing vhost devices allocate their IOTLB with max_iotlb_entries from vhost.c, which defaults to 2048 and is tunable by module parameter. Use the caller-provided limit at the allocation point instead of adding a separate default in the common IOTLB helper, and reject non-positive values in vhost paths that can report an error. Other vhost IOTLB users should not create zero-limit tables when entries can be populated from userspace or guest-controlled requests. Add caller-side max_iotlb_entries parameters for mlx5 vDPA, VDUSE and vhost-vDPA. Reject non-positive VDUSE and vhost-vDPA values, and require at least two entries for vdpa_sim and mlx5 vDPA paths that install full-range mappings, since those mappings are split into two IOTLB entries. Handle full-range mappings in the common helper by checking that the IOTLB can hold both split entries before inserting the first half. This avoids returning an error after leaving a half mapping behind. When the table is full, keep the existing retire behavior for retiring tables and return -ENOSPC for non-retiring tables. Reuse the retired map node instead of freeing it and allocating a replacement, so a stream of IOTLB updates cannot keep forcing GFP_ATOMIC allocations after the table has reached its limit. If a zero-limit IOTLB still reaches the common helper, treat it as a configuration error and return -EINVAL. I found this bug myself, though the patch was written with AI assistance. Fixes: 0bbe30668d89 ("vhost: factor out IOTLB") Assisted-by: OpenAI-Codex:GPT-5 Signed-off-by: Linfeng Sun  Message-ID: Signed-off-by: Michael S. Tsirkin --- drivers/vdpa/mlx5/core/mlx5_vdpa.h | 2 ++ drivers/vdpa/mlx5/core/mr.c | 5 ++- drivers/vdpa/mlx5/core/resources.c | 11 ++++++- drivers/vdpa/vdpa_sim/vdpa_sim.c | 10 ++++-- drivers/vdpa/vdpa_user/iova_domain.c | 11 ++++++- drivers/vhost/iotlb.c | 47 +++++++++++++++++++--------- drivers/vhost/vdpa.c | 9 +++++- drivers/vhost/vhost.c | 8 +++++ 8 files changed, 82 insertions(+), 21 deletions(-) diff --git a/drivers/vdpa/mlx5/core/mlx5_vdpa.h b/drivers/vdpa/mlx5/core/mlx5_vdpa.h index 2cedf7e2dbc4..42f2f44b383c 100644 --- a/drivers/vdpa/mlx5/core/mlx5_vdpa.h +++ b/drivers/vdpa/mlx5/core/mlx5_vdpa.h @@ -11,6 +11,8 @@ #define MLX5V_ETH_HARD_MTU (ETH_HLEN + VLAN_HLEN + ETH_FCS_LEN) +extern int mlx5_vdpa_max_iotlb_entries; + struct mlx5_vdpa_direct_mr { u64 start; u64 end; diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 6d02ccf9eb91..7d681961a5cc 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -776,6 +776,9 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, { int err; + if (mlx5_vdpa_max_iotlb_entries < 2) + return -EINVAL; + if (iotlb) err = create_user_mr(mvdev, mr, iotlb); else @@ -784,7 +787,7 @@ static int _mlx5_vdpa_create_mr(struct mlx5_vdpa_dev *mvdev, if (err) return err; - mr->iotlb = vhost_iotlb_alloc(0, 0); + mr->iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0); if (!mr->iotlb) { err = -ENOMEM; goto err_mr; diff --git a/drivers/vdpa/mlx5/core/resources.c b/drivers/vdpa/mlx5/core/resources.c index aeae31d0cefa..28a4d7a35bf4 100644 --- a/drivers/vdpa/mlx5/core/resources.c +++ b/drivers/vdpa/mlx5/core/resources.c @@ -3,8 +3,14 @@ #include #include +#include #include "mlx5_vdpa.h" +int mlx5_vdpa_max_iotlb_entries = 2048; +module_param_named(max_iotlb_entries, mlx5_vdpa_max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + static int alloc_pd(struct mlx5_vdpa_dev *dev, u32 *pdn, u16 uid) { struct mlx5_core_dev *mdev = dev->mdev; @@ -229,7 +235,10 @@ int mlx5_vdpa_destroy_mkey(struct mlx5_vdpa_dev *mvdev, u32 mkey) static int init_ctrl_vq(struct mlx5_vdpa_dev *mvdev) { - mvdev->cvq.iotlb = vhost_iotlb_alloc(0, 0); + if (mlx5_vdpa_max_iotlb_entries < 2) + return -EINVAL; + + mvdev->cvq.iotlb = vhost_iotlb_alloc(mlx5_vdpa_max_iotlb_entries, 0); if (!mvdev->cvq.iotlb) return -ENOMEM; diff --git a/drivers/vdpa/vdpa_sim/vdpa_sim.c b/drivers/vdpa/vdpa_sim/vdpa_sim.c index 8cb1cc2ea139..4d116644851d 100644 --- a/drivers/vdpa/vdpa_sim/vdpa_sim.c +++ b/drivers/vdpa/vdpa_sim/vdpa_sim.c @@ -34,7 +34,7 @@ MODULE_PARM_DESC(batch_mapping, "Batched mapping 1 -Enable; 0 - Disable"); static int max_iotlb_entries = 2048; module_param(max_iotlb_entries, int, 0444); MODULE_PARM_DESC(max_iotlb_entries, - "Maximum number of iotlb entries for each address space. 0 means unlimited. (default: 2048)"); + "Maximum number of iotlb entries for each address space. (default: 2048)"); static bool use_va = true; module_param(use_va, bool, 0444); @@ -201,6 +201,8 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr, if (!dev_attr->alloc_size) return ERR_PTR(-EINVAL); + if (max_iotlb_entries < 2) + return ERR_PTR(-EINVAL); if (config->mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES)) { if (config->device_features & @@ -261,8 +263,10 @@ struct vdpasim *vdpasim_create(struct vdpasim_dev_attr *dev_attr, for (i = 0; i < vdpasim->dev_attr.nas; i++) { vhost_iotlb_init(&vdpasim->iommu[i], max_iotlb_entries, 0); - vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, 0, - VHOST_MAP_RW); + ret = vhost_iotlb_add_range(&vdpasim->iommu[i], 0, ULONG_MAX, + 0, VHOST_MAP_RW); + if (ret) + goto err_iommu; vdpasim->iommu_pt[i] = true; } diff --git a/drivers/vdpa/vdpa_user/iova_domain.c b/drivers/vdpa/vdpa_user/iova_domain.c index 4dc76c0d0d13..b6c958224b7c 100644 --- a/drivers/vdpa/vdpa_user/iova_domain.c +++ b/drivers/vdpa/vdpa_user/iova_domain.c @@ -12,11 +12,17 @@ #include #include #include +#include #include #include #include "iova_domain.h" +static int max_iotlb_entries = 2048; +module_param(max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + static int vduse_iotlb_add_range(struct vduse_iova_domain *domain, u64 start, u64 last, u64 addr, unsigned int perm, @@ -622,11 +628,14 @@ vduse_domain_create(unsigned long iova_limit, size_t bounce_size) if (iova_limit <= bounce_size) return NULL; + if (max_iotlb_entries <= 0) + return NULL; + domain = kzalloc_obj(*domain); if (!domain) return NULL; - domain->iotlb = vhost_iotlb_alloc(0, 0); + domain->iotlb = vhost_iotlb_alloc(max_iotlb_entries, 0); if (!domain->iotlb) goto err_iotlb; diff --git a/drivers/vhost/iotlb.c b/drivers/vhost/iotlb.c index e1414c774c34..a1d4376a5b87 100644 --- a/drivers/vhost/iotlb.c +++ b/drivers/vhost/iotlb.c @@ -20,6 +20,14 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map, rb, __u64, __subtree_last, START, LAST, static inline, vhost_iotlb_itree); +static void vhost_iotlb_map_unlink(struct vhost_iotlb *iotlb, + struct vhost_iotlb_map *map) +{ + vhost_iotlb_itree_remove(map, &iotlb->root); + list_del(&map->link); + iotlb->nmaps--; +} + /** * vhost_iotlb_map_free - remove a map node and free it * @iotlb: the IOTLB @@ -28,10 +36,8 @@ INTERVAL_TREE_DEFINE(struct vhost_iotlb_map, void vhost_iotlb_map_free(struct vhost_iotlb *iotlb, struct vhost_iotlb_map *map) { - vhost_iotlb_itree_remove(map, &iotlb->root); - list_del(&map->link); + vhost_iotlb_map_unlink(iotlb, map); kfree(map); - iotlb->nmaps--; } EXPORT_SYMBOL_GPL(vhost_iotlb_map_free); @@ -57,14 +63,25 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb, if (last < start) return -EFAULT; + if (!iotlb->limit) + return -EINVAL; + /* If the range being mapped is [0, ULONG_MAX], split it into two entries * otherwise its size would overflow u64. */ if (start == 0 && last == ULONG_MAX) { u64 mid = last / 2; - int err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr, - perm, opaque); + int err; + if (iotlb->limit < 2) + return -ENOSPC; + + if (!(iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) && + iotlb->nmaps > iotlb->limit - 2) + return -ENOSPC; + + err = vhost_iotlb_add_range_ctx(iotlb, start, mid, addr, + perm, opaque); if (err) return err; @@ -72,17 +89,19 @@ int vhost_iotlb_add_range_ctx(struct vhost_iotlb *iotlb, start = mid + 1; } - if (iotlb->limit && - iotlb->nmaps == iotlb->limit && - iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { - map = list_first_entry(&iotlb->list, typeof(*map), link); - vhost_iotlb_map_free(iotlb, map); + if (iotlb->nmaps >= iotlb->limit) { + if (iotlb->flags & VHOST_IOTLB_FLAG_RETIRE) { + map = list_first_entry(&iotlb->list, typeof(*map), link); + vhost_iotlb_map_unlink(iotlb, map); + } else { + return -ENOSPC; + } + } else { + map = kmalloc_obj(*map, GFP_ATOMIC); + if (!map) + return -ENOMEM; } - map = kmalloc_obj(*map, GFP_ATOMIC); - if (!map) - return -ENOMEM; - map->start = start; map->size = last - start + 1; map->last = last; diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ac55275fa0d0..ef642bc9f97e 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -34,6 +34,11 @@ enum { #define VHOST_VDPA_DEV_MAX (1U << MINORBITS) +static int max_iotlb_entries = 2048; +module_param(max_iotlb_entries, int, 0444); +MODULE_PARM_DESC(max_iotlb_entries, + "Maximum number of iotlb entries. (default: 2048)"); + #define VHOST_VDPA_IOTLB_BUCKETS 16 struct vhost_vdpa_as { @@ -109,12 +114,14 @@ static struct vhost_vdpa_as *vhost_vdpa_alloc_as(struct vhost_vdpa *v, u32 asid) if (asid >= v->vdpa->nas) return NULL; + if (max_iotlb_entries <= 0) + return NULL; as = kmalloc_obj(*as); if (!as) return NULL; - vhost_iotlb_init(&as->iotlb, 0, 0); + vhost_iotlb_init(&as->iotlb, max_iotlb_entries, 0); as->id = asid; hlist_add_head(&as->hash_link, head); diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index 4c525b3e16ea..ae29a7ef7baa 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1137,6 +1137,9 @@ EXPORT_SYMBOL_GPL(vhost_dev_set_owner); static struct vhost_iotlb *iotlb_alloc(void) { + if (max_iotlb_entries <= 0) + return NULL; + return vhost_iotlb_alloc(max_iotlb_entries, VHOST_IOTLB_FLAG_RETIRE); } @@ -1981,6 +1984,8 @@ static long vhost_set_memory(struct vhost_dev *d, struct vhost_memory __user *m) return -EOPNOTSUPP; if (mem.nregions > max_mem_regions) return -E2BIG; + if (max_iotlb_entries <= 0) + return -EINVAL; newmem = kvzalloc_flex(*newmem, regions, mem.nregions); if (!newmem) return -ENOMEM; @@ -2275,6 +2280,9 @@ int vhost_init_device_iotlb(struct vhost_dev *d) struct vhost_iotlb *niotlb, *oiotlb; int i; + if (max_iotlb_entries <= 0) + return -EINVAL; + niotlb = iotlb_alloc(); if (!niotlb) return -ENOMEM; From 0619aaa34c0c2a2dcb07f0e9c8a34e7efb8c4cdf Mon Sep 17 00:00:00 2001 From: Yousef Alhouseen Date: Wed, 24 Jun 2026 15:02:02 -0700 Subject: [PATCH 407/501] vhost/vdpa: reject overflowing PA map page counts on 32-bit vhost_vdpa_pa_map() adds the IOVA page offset to the user-controlled map size before computing the number of pages to pin. On 32-bit systems, where unsigned long is narrower than u64, that addition can overflow and the code can pin and map fewer pages than the requested IOTLB range. Reject sizes that overflow the unsigned long page-count calculation. Fixes: 22af48cf91aa ("vdpa: factor out vhost_vdpa_pa_map() and vhost_vdpa_pa_unmap()") Acked-by: Michael S. Tsirkin Signed-off-by: Yousef Alhouseen Signed-off-by: Michael S. Tsirkin Message-ID: --- drivers/vhost/vdpa.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/vdpa.c b/drivers/vhost/vdpa.c index ef642bc9f97e..c3d913bd7cac 100644 --- a/drivers/vhost/vdpa.c +++ b/drivers/vhost/vdpa.c @@ -1109,6 +1109,7 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v, unsigned int gup_flags = FOLL_LONGTERM; unsigned long npages, cur_base, map_pfn, last_pfn = 0; unsigned long lock_limit, sz2pin, nchunks, i; + unsigned long page_offset; u64 start = iova; long pinned; int ret = 0; @@ -1121,7 +1122,13 @@ static int vhost_vdpa_pa_map(struct vhost_vdpa *v, if (perm & VHOST_ACCESS_WO) gup_flags |= FOLL_WRITE; - npages = PFN_UP(size + (iova & ~PAGE_MASK)); + page_offset = iova & ~PAGE_MASK; + if (size > ULONG_MAX - page_offset) { + ret = -EINVAL; + goto free; + } + + npages = PFN_UP(size + page_offset); if (!npages) { ret = -EINVAL; goto free; From 727e1f569855df83579edbd73dcb4a0723543a12 Mon Sep 17 00:00:00 2001 From: Christian Borntraeger Date: Mon, 6 Jul 2026 16:15:37 +0200 Subject: [PATCH 408/501] vdpa/mlx5: Fix buffer length in create_direct_keys() We have seen in our CI the following KASAN message: BUG: KASAN: slab-out-of-bounds in cmd_exec+0x550/0xca0 [mlx5_core] Read of size 272 at addr 0000000176795020 by task qemu-system-s39/82764 [...] [<000011388ab3a7a0>] cmd_exec+0x550/0xca0 [mlx5_core] [<000011388ab3b61c>] mlx5_cmd_exec_cb+0x25c/0x4f0 [mlx5_core] [<000011388b21e82e>] mlx5_vdpa_exec_async_cmds+0x22e/0x5e0 [mlx5_vdpa] [<000011388b21fd44>] create_direct_keys+0x954/0xef0 [mlx5_vdpa] [...] The buggy address is located 4128 bytes inside of allocated 4384-byte region [0000000176794000, 0000000176795120) So in essence we read 16 bytes beyond 4384-byte allocation. create_direct_keys calculates the pointer and length for in and out buffers. The size calculation for in includes the entire structure size (out + in + mtt[]) but the pointer passed to cmd_exec points only to the 'in' field, skipping the 'out' field. This causes mlx5_copy_to_msg() to read beyond the allocated buffer by sizeof(out) bytes when copying command data. Properly calculate the input size to match the pointer and allocation size. Fixes: 0071b138d44a ("vdpa/mlx5: Create direct MKEYs in parallel") Signed-off-by: Christian Borntraeger Tested-by: Dragos Tatulea Reviewed-by: Dragos Tatulea Signed-off-by: Michael S. Tsirkin Message-ID: <20260706141537.3510294-1-borntraeger@linux.ibm.com> --- drivers/vdpa/mlx5/core/mr.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vdpa/mlx5/core/mr.c b/drivers/vdpa/mlx5/core/mr.c index 7d681961a5cc..77a479aeaa85 100644 --- a/drivers/vdpa/mlx5/core/mr.c +++ b/drivers/vdpa/mlx5/core/mr.c @@ -233,7 +233,8 @@ static int create_direct_keys(struct mlx5_vdpa_dev *mvdev, struct mlx5_vdpa_mr * cmds[i].out = cmd_mem->out; cmds[i].outlen = sizeof(cmd_mem->out); cmds[i].in = cmd_mem->in; - cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount); + cmds[i].inlen = struct_size(cmd_mem, mtt, mttcount) - + offsetof(struct mlx5_create_mkey_mem, in); fill_create_direct_mr(mvdev, dmr, cmd_mem); From e5c0235a3c4e9eb047a16cd02323fe4ecf2f570e Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:09 -0500 Subject: [PATCH 409/501] selinux: do not cancel a policy conversion that never started sel_write_load() calls selinux_policy_cancel() when sel_make_policy_nodes() fails, and that helper dereferences the outgoing policy to cancel its sidtab conversion. On the first policy load there is no outgoing policy: security_load_policy() returns early for that case, before it converts anything, and state->policy is still NULL. A first load that fails while building the selinuxfs tree therefore takes a NULL dereference in selinux_policy_cancel(), reached from a write(2) to /sys/fs/selinux/load. Skip the cancel when there is no old policy, mirroring the check security_load_policy() already makes before it converts. Cc: stable@vger.kernel.org Fixes: 02a52c5c8c3b ("selinux: move policy commit after updating selinuxfs") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 2d828548f3db..90e81186cb2e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2221,7 +2221,9 @@ void selinux_policy_cancel(struct selinux_load_state *load_state) oldpolicy = rcu_dereference_protected(state->policy, lockdep_is_held(&state->policy_mutex)); - sidtab_cancel_convert(oldpolicy->sidtab); + /* a first load has no outgoing policy and converted nothing */ + if (oldpolicy) + sidtab_cancel_convert(oldpolicy->sidtab); selinux_policy_free(load_state->policy); kfree(load_state->convert_data); } From b98a8ac50775540f3804397ed08f61ef9910bcab Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:10 -0500 Subject: [PATCH 410/501] selinux: require a class's permission values to cover its permission count security_get_permissions() sizes an array by the class's permissions.nprim and fills it at value - 1, from the inherited common's permission table and then the class's own. A value no permission defines leaves a NULL that sel_make_perm_files() passes to d_alloc_name(), an oops inside sel_write_load() that strands selinux_state.policy_mutex and leaves every later load in uninterruptible sleep; two permissions sharing a value overwrite the first kstrdup(). Bounding each value by nprim catches neither, and neither would a count: the symbol table is keyed on the permission name, so duplicates pass. Track the values each permission table claims and require them to cover exactly what its count declares, rejecting a count no value can reach. Conforming policies are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 51 ++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 69777e885ae7..d358200817bd 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1154,7 +1154,18 @@ int str_read(char **strp, gfp_t flags, struct policy_file *fp, u32 len) return 0; } -static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *fp) +/* + * Bitmap of the permission values a symtab has claimed. Values are 1-based + * and bounded by SEL_VEC_MAX, the width of an access vector, so the whole set + * fits in a u32 and the callers reject an nprim past that width. + */ +static u32 perm_claimed_mask(u32 nprim) +{ + return nprim ? U32_MAX >> (SEL_VEC_MAX - nprim) : 0; +} + +static int perm_read(struct policydb *p, struct symtab *s, + struct policy_file *fp, u32 *claimed) { char *key = NULL; struct perm_datum *perdatum; @@ -1178,6 +1189,10 @@ static int perm_read(struct policydb *p, struct symtab *s, struct policy_file *f /* indexes an nprim-sized array in security_get_permissions() */ if (perdatum->value > s->nprim) goto bad; + /* two permissions cannot share one slot of that array */ + if (*claimed & (1U << (perdatum->value - 1))) + goto bad; + *claimed |= 1U << (perdatum->value - 1); rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) @@ -1198,7 +1213,7 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file char *key = NULL; struct common_datum *comdatum; __le32 buf[4]; - u32 i, len, nel; + u32 i, len, nel, claimed = 0; int rc; comdatum = kzalloc_obj(*comdatum); @@ -1225,17 +1240,28 @@ static int common_read(struct policydb *p, struct symtab *s, struct policy_file if (rc) goto bad; comdatum->permissions.nprim = le32_to_cpu(buf[2]); + /* no permission value can reach a slot past SEL_VEC_MAX */ + rc = -EINVAL; + if (comdatum->permissions.nprim > SEL_VEC_MAX) + goto bad; rc = str_read(&key, GFP_KERNEL, fp, len); if (rc) goto bad; for (i = 0; i < nel; i++) { - rc = perm_read(p, &comdatum->permissions, fp); + rc = perm_read(p, &comdatum->permissions, fp, &claimed); if (rc) goto bad; } + rc = -EINVAL; + if (claimed != perm_claimed_mask(comdatum->permissions.nprim)) { + pr_err("SELinux: common %s does not define every permission it declares\n", + key); + goto bad; + } + hash_eval(&comdatum->permissions.table, "common_permissions", key); rc = symtab_insert(s, key, comdatum); @@ -1369,7 +1395,7 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * char *key = NULL; struct class_datum *cladatum; __le32 buf[6]; - u32 i, len, len2, ncons, nel, val; + u32 i, len, len2, ncons, nel, val, claimed = 0, inherited = 0; int rc; cladatum = kzalloc_obj(*cladatum); @@ -1402,6 +1428,10 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * if (rc) goto bad; cladatum->permissions.nprim = le32_to_cpu(buf[3]); + /* no permission value can reach a slot past SEL_VEC_MAX */ + rc = -EINVAL; + if (cladatum->permissions.nprim > SEL_VEC_MAX) + goto bad; ncons = le32_to_cpu(buf[5]); @@ -1436,11 +1466,22 @@ static int class_read(struct policydb *p, struct symtab *s, struct policy_file * } } for (i = 0; i < nel; i++) { - rc = perm_read(p, &cladatum->permissions, fp); + rc = perm_read(p, &cladatum->permissions, fp, &claimed); if (rc) goto bad; } + /* the class's own permissions must claim the slots the common leaves */ + if (cladatum->comdatum) + inherited = cladatum->comdatum->permissions.nprim; + rc = -EINVAL; + if (claimed != (perm_claimed_mask(cladatum->permissions.nprim) & + ~perm_claimed_mask(inherited))) { + pr_err("SELinux: class %s does not define every permission it declares\n", + key); + goto bad; + } + hash_eval(&cladatum->permissions.table, "class_permissions", key); rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); From 22b05fec62c0fe9864cfceb52f7d0f3a34d9b1dd Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:11 -0500 Subject: [PATCH 411/501] selinux: reject an unclaimed class value in security_get_classes() security_get_classes() sizes an array by p_classes.nprim and fills it at value - 1, so a class value the policy never defines leaves a NULL. sel_make_classes() passes every entry to sel_make_dir(), reaching the same d_alloc_name() dereference as the permission array. The class symbol table is allowed to be sparse (policydb_class_isvalid() exists to absorb that), but this getter builds its own array straight from the hash table and has no such predicate. Fail the lookup when a value went unclaimed instead of handing out the NULL. Conforming policies define every class they declare and are unaffected. Cc: stable@vger.kernel.org Fixes: 55fcf09b3fe4 ("selinux: add support for querying object classes and permissions from the running policy") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 90e81186cb2e..7afce975436e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -3304,6 +3304,7 @@ int security_get_classes(struct selinux_policy *policy, char ***classes, u32 *nclasses) { struct policydb *policydb; + u32 i; int rc; policydb = &policy->policydb; @@ -3316,16 +3317,29 @@ int security_get_classes(struct selinux_policy *policy, rc = hashtab_map(&policydb->p_classes.table, get_classes_callback, *classes); - if (rc) { - u32 i; + if (rc) + goto err; - for (i = 0; i < *nclasses; i++) - kfree((*classes)[i]); - kfree(*classes); + /* + * The class symtab may be sparse, which policydb_class_isvalid() exists + * to absorb; the callback fills this array by value, so an unclaimed + * one leaves a NULL that sel_make_classes() hands to sel_make_dir(). + */ + for (i = 0; i < *nclasses; i++) { + if (!(*classes)[i]) { + rc = -EINVAL; + goto err; + } } out: return rc; + +err: + for (i = 0; i < *nclasses; i++) + kfree((*classes)[i]); + kfree(*classes); + return rc; } static int get_permissions_callback(void *k, void *d, void *args) From a93d37a09b863810653f93d371fb197457d59deb Mon Sep 17 00:00:00 2001 From: Bryam Vargas Date: Fri, 31 Jul 2026 12:44:12 -0500 Subject: [PATCH 412/501] selinux: require every boolean value to be defined p_bools.nprim comes from the policy image independently of how many booleans follow it, and cond_index_bool() fills bool_val_to_struct[] at value - 1, so a count larger than the values present leaves NULL entries. Every user of that array then walks it by index and dereferences each entry: cond_evaluate_expr() on the access-vector path, security_get_bools() and security_get_bool_value() behind selinuxfs, and security_set_bools(). A sparse class value is absorbed by policydb_class_isvalid() and its siblings; booleans have no such predicate, and no consumer that could use one. Reject a boolean value that no boolean defines, once, where the array is built. Conforming policies define every boolean they declare and are unaffected. Cc: stable@vger.kernel.org Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Bryam Vargas Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index d358200817bd..d88713201be9 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -719,6 +719,7 @@ static inline void symtab_hash_eval(struct symtab *s) static int policydb_index(struct policydb *p) { int i, rc; + u32 v; if (p->mls_enabled) pr_debug( @@ -769,6 +770,24 @@ static int policydb_index(struct policydb *p) if (rc) goto out; } + + /* + * A sparse class value is absorbed by policydb_class_isvalid() and + * its siblings, but no such predicate exists for booleans: every + * user of bool_val_to_struct[] walks it by index and dereferences + * each entry -- cond_evaluate_expr(), the two getters and + * security_set_bools() -- so an unclaimed one has no consumer that + * can tolerate it. + */ + for (v = 0; v < p->p_bools.nprim; v++) { + if (!p->bool_val_to_struct[v]) { + pr_err("SELinux: boolean %u is declared but not defined\n", + v + 1); + rc = -EINVAL; + goto out; + } + } + rc = 0; out: return rc; From 0e125ecfe20c077625cf0be8d750d5c3abc0dce9 Mon Sep 17 00:00:00 2001 From: Nathan Gao Date: Fri, 24 Jul 2026 20:08:06 -0700 Subject: [PATCH 413/501] tcp: do not change rcv_ssthresh in tcp_measure_rcv_mss() Commit f5da7c45188e ("tcp: adjust rcvq_space after updating scaling ratio") replaced the direct window_clamp update in tcp_measure_rcv_mss() with a call to tcp_set_window_clamp(), a helper that implements the TCP_WINDOW_CLAMP setsockopt. As a side effect, the helper also shrinks rcv_ssthresh via __tcp_adjust_rcv_ssthresh(). As a result, each scaling_ratio decrease detected by tcp_measure_rcv_mss() also cuts rcv_ssthresh. Elsewhere in TCP, rcv_ssthresh is usually cut under memory pressure and grows via tcp_grow_window(). Flows whose segment sizes vary keep scaling_ratio oscillating, which leads to an unstable rcv_ssthresh: a dip of rcv_ssthresh only recovers via tcp_grow_window(), keeping the advertised window at a relatively low level even after the ratio itself has recovered, and can even stall the sender. Observed on a customer's proxy gateway after upgrading from kernel 6.1 to 6.12: in the worst case, rcv_ssthresh was cut in half by a scaling_ratio dip. P99 latency jumped from <10ms on 6.1 to ~100ms on 6.12, and almost returned to the 6.1 level with this patch applied. Restore the plain WRITE_ONCE() update of window_clamp, as introduced in commit a2cbb1603943 ("tcp: Update window clamping condition"), and keep the rcvq_space.space adjustment. Now rcv_ssthresh is decoupled from scaling_ratio changes in tcp_measure_rcv_mss(). Fixes: f5da7c45188e ("tcp: adjust rcvq_space after updating scaling ratio") Signed-off-by: Nathan Gao Link: https://patch.msgid.link/20260725030806.28135-1-zcgao@amazon.com Signed-off-by: Jakub Kicinski --- net/ipv4/tcp_input.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index daff93d51342..5b6378b94701 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -252,7 +252,7 @@ static void tcp_measure_rcv_mss(struct sock *sk, const struct sk_buff *skb) struct tcp_sock *tp = tcp_sk(sk); val = tcp_win_from_space(sk, sk->sk_rcvbuf); - tcp_set_window_clamp(sk, val); + WRITE_ONCE(tp->window_clamp, val); if (tp->window_clamp < tp->rcvq_space.space) tp->rcvq_space.space = tp->window_clamp; From 9f2cf069a9a72a2d6b97ca8b4c70e714aac99749 Mon Sep 17 00:00:00 2001 From: Baul Lee Date: Thu, 30 Jul 2026 01:00:28 +0900 Subject: [PATCH 414/501] sctp: keep chunk->transport in step with the list it is queued on __sctp_outq_flush_rtx() moves a gap-acked chunk onto another transport's transmitted list without updating chunk->transport: if (chunk->tsn_gap_acked) { list_move_tail(&chunk->transmitted_list, &transport->transmitted); continue; } The chunk then sits on a live transport's list while chunk->transport still names a different one. If that transport is removed - sctp_assoc_rm_peer() from an ASCONF Delete-IP - sctp_transport_free() RCU-frees it and the chunk is left with a dangling pointer. sctp_assoc_rm_peer() scrubs peer->transmitted and asoc->outqueue.out_chunk_list, but the chunk is on neither. The pointer is not followed while tsn_gap_acked is set. A SACK that reneges on the TSN clears the flag, and the next SACK reaches tchunk->transport->flight_size -= sctp_data_size(tchunk); inside the freed transport. KASAN reports a slab-use-after-free read in sctp_check_transmitted(), freed from sctp_assoc_rm_peer(). Both the removal and the SACKs come from the association peer. Set chunk->transport at the move. The ordinary resend path needs nothing: it reaches its list_move_tail() only after sctp_packet_append_chunk() returned SCTP_XMIT_OK, and __sctp_packet_append_chunk() has rebound the chunk by then. Discovered by XBOW, triaged by Baul Lee Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Signed-off-by: Baul Lee Acked-by: Xin Long Link: https://patch.msgid.link/20260729160028.54546-1-baul.lee@xbow.com Signed-off-by: Jakub Kicinski --- net/sctp/outqueue.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index f6b8c13dafa4..e92fb9da4647 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -650,6 +650,7 @@ static int __sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, if (chunk->tsn_gap_acked) { list_move_tail(&chunk->transmitted_list, &transport->transmitted); + chunk->transport = transport; continue; } From d1ad8fb2ac6a1afb71dc22d9ae8efb4dda96c824 Mon Sep 17 00:00:00 2001 From: Yuejie Shi Date: Thu, 30 Jul 2026 11:52:32 +0800 Subject: [PATCH 415/501] ipv6: fix Route Information option length validation rt6_route_rcv() validates the Route Information option (RFC 4191) length against the prefix length, but both checks are off by one. rinfo->length is the ND option length in units of 8 octets and it *includes* the 8-byte option header, so an option carrying N bytes of prefix has length == 1 + N/8. RFC 4191 section 2.3 requires length 3 when Prefix Length is greater than 64, and 2 or 3 when it is greater than 0. The code accepts length >= 2 and length >= 1 respectively. ipv6_addr_prefix() then copies prefix_len/8 bytes out of rinfo->prefix, so a Router Advertisement with (prefix_len=128, length=2) or (prefix_len=64, length=1) makes the kernel read up to 8 bytes past the end of the option. Those bytes end up in the prefix of the route that gets installed, so they are visible to userspace: # RA with a Route Information option (prefix_len=128, length=2) # followed by a source link-layer address option, 01 01 de ad be ef ca fe $ ip -6 route show 2001:db8:dead:beef:101:dead:beef:cafe via fe80::1234 dev veth0 proto ra ^^^^^^^^^^^^^^^^^^ the next option, read out of bounds When the Route Information option is the last one in the packet, those eight bytes come from the skb tail room instead. Reject the option lengths RFC 4191 does not allow. Fixes: 70ceb4f53929 ("[IPV6]: ROUTE: Add experimental support for Route Information Option in RA (RFC4191).") Cc: stable@vger.kernel.org Signed-off-by: Yuejie Shi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260730035310.74584-1-syjcnss@gmail.com Signed-off-by: Jakub Kicinski --- net/ipv6/route.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/ipv6/route.c b/net/ipv6/route.c index fc42d67e5822..5968ce5ad150 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -988,13 +988,13 @@ int rt6_route_rcv(struct net_device *dev, u8 *opt, int len, } else if (rinfo->prefix_len > 128) { return -EINVAL; } else if (rinfo->prefix_len > 64) { - if (rinfo->length < 2) { + /* RFC 4191: Length MUST be 3 when Prefix Length > 64 */ + if (rinfo->length < 3) return -EINVAL; - } } else if (rinfo->prefix_len > 0) { - if (rinfo->length < 1) { + /* RFC 4191: Length MUST be 2 or 3 when Prefix Length > 0 */ + if (rinfo->length < 2) return -EINVAL; - } } pref = rinfo->route_pref; From d1000fd7995e51deec872d154e0a40d82f7a539f Mon Sep 17 00:00:00 2001 From: Will Chen Date: Wed, 29 Jul 2026 15:01:31 -0700 Subject: [PATCH 416/501] bnxt: fix memory leak in bnxt_queue_mem_alloc error cases There is a small memory leak in bnxt_queue_mem_alloc: when bnxt_alloc_rx_agg_bmap() succeeds but bnxt_alloc_one_tpa_info() later fails, the rx_agg_bmap allocated by bnxt_alloc_rx_agg_bmap() is not freed in the fallthrough cleanup cases. Free the rx_agg_bmap in the err_free_rx_agg_ring case and initialize clone->rx_agg_bmap = NULL earlier in the function to allow for safe fallthrough. Fixes: bd649c5cc958 ("bnxt_en: handle tpa_info in queue API implementation") Signed-off-by: Will Chen Reviewed-by: Joe Damato Reviewed-by: Michael Chan Link: https://patch.msgid.link/20260729220132.1256924-1-will.chen.tty@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 7513618793da..004ef99f4530 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -16217,6 +16217,7 @@ static int bnxt_queue_mem_alloc(struct net_device *dev, clone->rx_next_cons = 0; clone->need_head_pool = false; clone->rx_page_size = qcfg->rx_page_size; + clone->rx_agg_bmap = NULL; rc = bnxt_alloc_rx_page_pool(bp, clone, rxr->page_pool->p.nid); if (rc) @@ -16269,6 +16270,8 @@ static int bnxt_queue_mem_alloc(struct net_device *dev, bnxt_free_one_tpa_info(bp, clone); err_free_rx_agg_ring: bnxt_free_ring(bp, &clone->rx_agg_ring_struct.ring_mem); + kfree(clone->rx_agg_bmap); + clone->rx_agg_bmap = NULL; err_free_rx_ring: bnxt_free_ring(bp, &clone->rx_ring_struct.ring_mem); err_rxq_info_unreg: From ebac8f6b1ef0e9278afe204b8692a7479988dace Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 29 Jul 2026 12:16:54 -0700 Subject: [PATCH 417/501] vsock/virtio: read virtqueues under worker locks Commit bd50c5dc182b ("vsock/virtio: add support for device suspend/resume") made the *_run flags transition from false to true when restore installs replacement virtqueues. The RX, TX and event workers read their virtqueue before locking and checking the corresponding flag, so a worker delayed across freeze and restore can observe the replacement queue's running state while retaining a pointer to the deleted queue. Read each virtqueue under its mutex after checking the run flag, keeping the pointer and state in the same queue generation. Fixes: bd50c5dc182b ("vsock/virtio: add support for device suspend/resume") Cc: stable@vger.kernel.org Reported-by: Xiang Mei Link: https://lore.kernel.org/r/20260727035804.1860862-1-bestswngs@gmail.com Signed-off-by: Weiming Shi Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/e79f68ad9284c983364fc3ac46904b6d9ef50231.1785352330.git.bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/virtio_transport.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index 57f2d6ec3ffc..a8e1dd95ba8c 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -346,12 +346,13 @@ static void virtio_transport_tx_work(struct work_struct *work) struct virtqueue *vq; bool added = false; - vq = vsock->vqs[VSOCK_VQ_TX]; mutex_lock(&vsock->tx_lock); if (!vsock->tx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_TX]; + do { struct sk_buff *skb; unsigned int len; @@ -451,13 +452,13 @@ static void virtio_transport_event_work(struct work_struct *work) container_of(work, struct virtio_vsock, event_work); struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_EVENT]; - mutex_lock(&vsock->event_lock); if (!vsock->event_run) goto out; + vq = vsock->vqs[VSOCK_VQ_EVENT]; + do { struct virtio_vsock_event *event; unsigned int len; @@ -634,13 +635,13 @@ static void virtio_transport_rx_work(struct work_struct *work) container_of(work, struct virtio_vsock, rx_work); struct virtqueue *vq; - vq = vsock->vqs[VSOCK_VQ_RX]; - mutex_lock(&vsock->rx_lock); if (!vsock->rx_run) goto out; + vq = vsock->vqs[VSOCK_VQ_RX]; + do { virtqueue_disable_cb(vq); for (;;) { From a31e0ad444698d8aa7534a0f89fda543730f97a5 Mon Sep 17 00:00:00 2001 From: Weiming Shi Date: Wed, 29 Jul 2026 12:16:55 -0700 Subject: [PATCH 418/501] vsock/virtio: avoid refilling the RX queue after teardown Commit b917507e5ad9 ("vsock/virtio: stop workers during the .remove()") made the RX worker jump to its common exit when rx_run is clear. That exit still refills the RX queue when the buffer count is low, so work queued across virtio_vsock_vqs_del() can add buffers after the virtqueues have been deleted. BUG: KASAN: slab-use-after-free in virtqueue_add_sgs Read of size 4 by task kworker/0:1 Workqueue: virtio_vsock virtio_transport_rx_work Call Trace: virtqueue_add_sgs (drivers/virtio/virtio_ring.c:2796) virtio_vsock_rx_fill (net/vmw_vsock/virtio_transport.c:332) virtio_transport_rx_work (net/vmw_vsock/virtio_transport.c:701) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3478) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) ... Freed by task 141: kfree (mm/slub.c:6566) vp_del_vq (drivers/virtio/virtio_pci_common.c:259) vp_del_vqs (drivers/virtio/virtio_pci_common.c:285) virtio_vsock_freeze (net/vmw_vsock/virtio_transport.c:912) virtio_device_freeze (drivers/virtio/virtio.c:658) virtio_pci_freeze (drivers/virtio/virtio_pci_common.c:601) pci_pm_freeze (drivers/pci/pci-driver.c:1098) device_suspend (drivers/base/power/main.c:1968) Kernel panic - not syncing: KASAN: panic_on_warn set ... Jump to a no-refill exit when rx_run is clear, leaving the normal exit to replenish a running queue. Fixes: b917507e5ad9 ("vsock/virtio: stop workers during the .remove()") Cc: stable@vger.kernel.org Reported-by: Xiang Mei Link: https://lore.kernel.org/r/20260727035804.1860862-1-bestswngs@gmail.com Suggested-by: Stefano Garzarella Signed-off-by: Weiming Shi Reviewed-by: Bobby Eshleman Link: https://patch.msgid.link/f9c8c1d64cad9d262f305d02ffe164c2f900fadf.1785352330.git.bestswngs@gmail.com Signed-off-by: Jakub Kicinski --- net/vmw_vsock/virtio_transport.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/vmw_vsock/virtio_transport.c b/net/vmw_vsock/virtio_transport.c index a8e1dd95ba8c..96c9fe8d357c 100644 --- a/net/vmw_vsock/virtio_transport.c +++ b/net/vmw_vsock/virtio_transport.c @@ -638,7 +638,7 @@ static void virtio_transport_rx_work(struct work_struct *work) mutex_lock(&vsock->rx_lock); if (!vsock->rx_run) - goto out; + goto out_nofill; vq = vsock->vqs[VSOCK_VQ_RX]; @@ -692,6 +692,7 @@ static void virtio_transport_rx_work(struct work_struct *work) out: if (vsock->rx_buf_nr < vsock->rx_buf_max_nr / 2) virtio_vsock_rx_fill(vsock); +out_nofill: mutex_unlock(&vsock->rx_lock); } From 1bb30b181d9f0484e141f8411e15ed906d5c6780 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:54 -0700 Subject: [PATCH 419/501] xsk: require at least 16 bytes of TX metadata AF_XDP accepts a TX metadata length as small as eight bytes, but every supported request needs the flags plus at least one eight-byte request field. Such short metadata also lets the kernel read beyond the registered area. Require 16 bytes rather than sizeof(struct xsk_tx_metadata) to preserve compatibility with applications that do not use launch-time metadata. Fixes: 341ac980eab9 ("xsk: Support tx_metadata_len") Reported-by: AutonomousCodeSecurity@microsoft.com Reported-by: Cen Zhang (Microsoft) Link: https://lore.kernel.org/netdev/20260720155210.34229-1-blbllhy@gmail.com/ Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-2-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- net/xdp/xdp_umem.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c index 58da2f4f4397..cd8643360eb3 100644 --- a/net/xdp/xdp_umem.c +++ b/net/xdp/xdp_umem.c @@ -210,6 +210,8 @@ static int xdp_umem_reg(struct xdp_umem *umem, struct xdp_umem_reg *mr) if (mr->flags & XDP_UMEM_TX_METADATA_LEN) { if (mr->tx_metadata_len >= 256 || mr->tx_metadata_len % 8) return -EINVAL; + if (mr->tx_metadata_len < 16) + return -EINVAL; umem->tx_metadata_len = mr->tx_metadata_len; } From 19366db6dfccac9b0867a151678cd7b89fb8fd99 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:55 -0700 Subject: [PATCH 420/501] xsk: pass TX metadata pointer by reference Completion handling needs to know whether a timestamp was requested when the metadata was processed. Let xsk_tx_metadata_request() update the caller's metadata pointer so that decision can be carried forward without rereading user-controlled flags. This only changes the interface; behavior remains unchanged. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-3-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en.h | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c | 14 +++++++++----- drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h | 4 ++-- .../net/ethernet/mellanox/mlx5/core/en/xsk/tx.c | 2 +- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +- include/net/libeth/xsk.h | 2 +- include/net/xdp_sock.h | 8 +++++--- 8 files changed, 21 insertions(+), 15 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index b3883a5a7d7a..beaea1479485 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) meta_req.tx_buffer = bi; meta_req.meta = meta; meta_req.used_desc = 0; - xsk_tx_metadata_request(meta, &igc_xsk_tx_metadata_ops, + xsk_tx_metadata_request(&meta, &igc_xsk_tx_metadata_ops, &meta_req); /* xsk_tx_metadata_request() may have updated next_to_use */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en.h b/drivers/net/ethernet/mellanox/mlx5/core/en.h index 6867a5aed42c..822859c360e7 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en.h @@ -483,7 +483,7 @@ typedef int (*mlx5e_fp_xmit_xdp_frame_check)(struct mlx5e_xdpsq *); typedef bool (*mlx5e_fp_xmit_xdp_frame)(struct mlx5e_xdpsq *, struct mlx5e_xmit_data *, int, - struct xsk_tx_metadata *); + struct xsk_tx_metadata **); struct mlx5e_xdpsq { /* data path */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index d8c7cb8837d7..dac5e9d4c8bd 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -452,11 +452,11 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta); + int check_result, struct xsk_tx_metadata **meta); INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta) + int check_result, struct xsk_tx_metadata **meta) { struct mlx5e_tx_mpwqe *session = &sq->mpwqe; struct mlx5e_xdpsq_stats *stats = sq->stats; @@ -504,7 +504,10 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx * and it's safe to complete it at any time. */ mlx5e_xdp_mpwqe_session_start(sq); - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth); + if (meta) + xsk_tx_metadata_request(meta, + &mlx5e_xsk_tx_metadata_ops, + &session->wqe->eth); } mlx5e_xdp_mpwqe_add_dseg(sq, p, stats); @@ -535,7 +538,7 @@ INDIRECT_CALLABLE_SCOPE int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq) INDIRECT_CALLABLE_SCOPE bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, - int check_result, struct xsk_tx_metadata *meta) + int check_result, struct xsk_tx_metadata **meta) { struct mlx5e_xmit_data_frags *xdptxdf = container_of(xdptxd, struct mlx5e_xmit_data_frags, xd); @@ -649,7 +652,8 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, sq->pc += num_wqebbs; - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); + if (meta) + xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); sq->doorbell_cseg = cseg; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h index 3c54f8962664..5b8f4094d553 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.h @@ -114,11 +114,11 @@ extern const struct xsk_tx_metadata_ops mlx5e_xsk_tx_metadata_ops; INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, int check_result, - struct xsk_tx_metadata *meta)); + struct xsk_tx_metadata **meta)); INDIRECT_CALLABLE_DECLARE(bool mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, int check_result, - struct xsk_tx_metadata *meta)); + struct xsk_tx_metadata **meta)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check_mpwqe(struct mlx5e_xdpsq *sq)); INDIRECT_CALLABLE_DECLARE(int mlx5e_xmit_xdp_frame_check(struct mlx5e_xdpsq *sq)); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c index 8aeab4b21035..3d19dad8f868 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c @@ -105,7 +105,7 @@ bool mlx5e_xsk_tx(struct mlx5e_xdpsq *sq, unsigned int budget) ret = INDIRECT_CALL_2(sq->xmit_xdp_frame, mlx5e_xmit_xdp_frame_mpwqe, mlx5e_xmit_xdp_frame, sq, &xdptxd, - check_result, meta); + check_result, &meta); if (unlikely(!ret)) { if (sq->mpwqe.wqe) mlx5e_xdp_mpwqe_complete(sq); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3801f9d45278..7c3a7c56a81a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2748,7 +2748,7 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) meta_req.set_ic = &set_ic; meta_req.tbs = tx_q->tbs; meta_req.edesc = &tx_q->dma_entx[entry]; - xsk_tx_metadata_request(meta, &stmmac_xsk_tx_metadata_ops, + xsk_tx_metadata_request(&meta, &stmmac_xsk_tx_metadata_ops, &meta_req); if (set_ic) { tx_q->tx_count_frames = 0; diff --git a/include/net/libeth/xsk.h b/include/net/libeth/xsk.h index 82b5d21aae87..e2fa6bf6b1b3 100644 --- a/include/net/libeth/xsk.h +++ b/include/net/libeth/xsk.h @@ -205,7 +205,7 @@ __libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc, BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo)); tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo; - xsk_tx_metadata_request(ctx.meta, tmo, &desc); + xsk_tx_metadata_request(&ctx.meta, tmo, &desc); return desc; } diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 8b51876efbed..06c081feff42 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -153,17 +153,19 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, /** * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission * and call appropriate xsk_tx_metadata_ops operation. - * @meta: pointer to AF_XDP metadata area + * @pmeta: pointer to pointer to AF_XDP metadata area * @ops: pointer to struct xsk_tx_metadata_ops * @priv: pointer to driver-private aread * * This function should be called by the networking device when * it prepares AF_XDP egress packet. */ -static inline void xsk_tx_metadata_request(const struct xsk_tx_metadata *meta, +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, const struct xsk_tx_metadata_ops *ops, void *priv) { + const struct xsk_tx_metadata *meta = *pmeta; + if (!meta) return; @@ -231,7 +233,7 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, { } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata *meta, +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, const struct xsk_tx_metadata_ops *ops, void *priv) { From 9f60a67df8d3c862503bee62bada8e7089cba438 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:56 -0700 Subject: [PATCH 421/501] xsk: clear metadata pointer when no timestamp is requested User space can change metadata flags after request processing. Rereading them during completion can therefore make the kernel write a timestamp that was not requested when the packet was submitted. Clear the metadata pointer during request processing unless timestamp completion is requested. Completion handling can then use the pointer itself instead of rereading the flags. On the mlx5 multi-packet WQE path metadata is evaluated per batch: xsk_tx_metadata_request() runs only for the descriptor that starts a session, just like the checksum offload that is applied once through the shared WQE. Only that descriptor's pointer is reset, so completion handling can record a timestamp for the other descriptors of the session regardless of their own XDP_TXMD_FLAGS_TIMESTAMP bit. The write stays inside the metadata area; the single-WQE, other zero-copy, and generic paths reset the pointer per descriptor and are unaffected. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-4-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 14 ++++++++++---- net/xdp/xsk.c | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 06c081feff42..2b2eb9b9d580 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -141,13 +141,16 @@ INDIRECT_CALLABLE_DECLARE(void xsk_destruct_skb(struct sk_buff *)); static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, struct xsk_tx_metadata_compl *compl) { + compl->tx_timestamp = NULL; + if (!meta) return; - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) - compl->tx_timestamp = &meta->completion.tx_timestamp; - else - compl->tx_timestamp = NULL; + /* we can only arrive here if the completion timestamp has been + * requested via XDP_TXMD_FLAGS_TIMESTAMP, see xsk_tx_metadata_request + */ + + compl->tx_timestamp = &meta->completion.tx_timestamp; } /** @@ -182,6 +185,9 @@ static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) ops->tmo_request_checksum(meta->request.csum_start, meta->request.csum_offset, priv); + + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + *pmeta = NULL; } /** diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index f906d51b6699..fcc6f17f3576 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -998,6 +998,8 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) skb->skb_mstamp_ns = meta->request.launch_time; + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + meta = NULL; xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta); return 0; From 439ce2dddf3d22129b9113a7881637256a35e936 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:57 -0700 Subject: [PATCH 422/501] xsk: validate launch-time metadata size Launch-time metadata extends beyond the first 16 bytes of struct xsk_tx_metadata. Reject the request when the registered metadata area does not contain the complete field. Snapshot the validated flags for the generic transmit path and use that snapshot for request and completion processing, avoiding inconsistent decisions if user space changes the flags concurrently. Note that only xsk_skb_metadata is properly using the flags, __xsk_buff_get_metadata ignores them. Next commits address that. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-5-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock_drv.h | 17 +++++++++++++---- net/xdp/xsk.c | 11 ++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 46797645a0c2..68a787601357 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -260,21 +260,28 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) 0) static inline bool -xsk_buff_valid_tx_metadata(const struct xsk_tx_metadata *meta) +xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, + const struct xsk_tx_metadata *meta, u64 *flags) { - return !(meta->flags & ~XDP_TXMD_FLAGS_VALID); + *flags = READ_ONCE(meta->flags); + if (*flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + if (pool->tx_metadata_len < + offsetofend(struct xsk_tx_metadata, request.launch_time)) + return false; + return !(*flags & ~XDP_TXMD_FLAGS_VALID); } static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { struct xsk_tx_metadata *meta; + u64 flags; if (!pool->tx_metadata_len) return NULL; meta = data - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(meta))) + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) return NULL; /* no way to signal the error to the user */ return meta; @@ -469,7 +476,9 @@ xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) return (struct xdp_desc_ctx){ }; } -static inline bool xsk_buff_valid_tx_metadata(struct xsk_tx_metadata *meta) +static inline bool +xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, + const struct xsk_tx_metadata *meta, u64 *flags) { return false; } diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c index fcc6f17f3576..7855ee09c4b6 100644 --- a/net/xdp/xsk.c +++ b/net/xdp/xsk.c @@ -967,15 +967,16 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, { struct xsk_tx_metadata *meta = NULL; u16 csum_start, csum_offset; + u64 flags; if (unlikely(pool->tx_metadata_len == 0)) return -EINVAL; meta = buffer - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(meta))) + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) return -EINVAL; - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) { + if (flags & XDP_TXMD_FLAGS_CHECKSUM) { csum_start = READ_ONCE(meta->request.csum_start); csum_offset = READ_ONCE(meta->request.csum_offset); @@ -996,9 +997,9 @@ static int xsk_skb_metadata(struct sk_buff *skb, void *buffer, } } - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - skb->skb_mstamp_ns = meta->request.launch_time; - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + skb->skb_mstamp_ns = READ_ONCE(meta->request.launch_time); + if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP)) meta = NULL; xsk_tx_metadata_to_compl(meta, &skb_shinfo(skb)->xsk_meta); From ddd0d6c5bfe2fef7c7cf31f62265f29b7b9eb9ef Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:58 -0700 Subject: [PATCH 423/501] xsk: move xsk_tx_metadata_request() to xdp_sock_drv.h xsk_tx_metadata_request() must validate metadata with xsk_buff_valid_tx_metadata(), which is defined in xdp_sock_drv.h. Move the helper there before adding that dependency. All callers already include the destination header, so this has no functional effect. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-6-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- include/net/xdp_sock.h | 43 -------------------------------------- include/net/xdp_sock_drv.h | 43 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/include/net/xdp_sock.h b/include/net/xdp_sock.h index 2b2eb9b9d580..6e70b320b399 100644 --- a/include/net/xdp_sock.h +++ b/include/net/xdp_sock.h @@ -153,43 +153,6 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, compl->tx_timestamp = &meta->completion.tx_timestamp; } -/** - * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission - * and call appropriate xsk_tx_metadata_ops operation. - * @pmeta: pointer to pointer to AF_XDP metadata area - * @ops: pointer to struct xsk_tx_metadata_ops - * @priv: pointer to driver-private aread - * - * This function should be called by the networking device when - * it prepares AF_XDP egress packet. - */ -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) -{ - const struct xsk_tx_metadata *meta = *pmeta; - - if (!meta) - return; - - if (ops->tmo_request_launch_time) - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - ops->tmo_request_launch_time(meta->request.launch_time, - priv); - - if (ops->tmo_request_timestamp) - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) - ops->tmo_request_timestamp(priv); - - if (ops->tmo_request_checksum) - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) - ops->tmo_request_checksum(meta->request.csum_start, - meta->request.csum_offset, priv); - - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) - *pmeta = NULL; -} - /** * xsk_tx_metadata_complete - Evaluate AF_XDP TX metadata at completion * and call appropriate xsk_tx_metadata_ops operation. @@ -239,12 +202,6 @@ static inline void xsk_tx_metadata_to_compl(struct xsk_tx_metadata *meta, { } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) -{ -} - static inline void xsk_tx_metadata_complete(struct xsk_tx_metadata_compl *compl, const struct xsk_tx_metadata_ops *ops, void *priv) diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index 68a787601357..f87c4215673e 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -271,6 +271,43 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return !(*flags & ~XDP_TXMD_FLAGS_VALID); } +/** + * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission + * and call appropriate xsk_tx_metadata_ops operation. + * @pmeta: pointer to pointer to AF_XDP metadata area + * @ops: pointer to struct xsk_tx_metadata_ops + * @priv: pointer to driver-private aread + * + * This function should be called by the networking device when + * it prepares AF_XDP egress packet. + */ +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, + void *priv) +{ + const struct xsk_tx_metadata *meta = *pmeta; + + if (!meta) + return; + + if (ops->tmo_request_launch_time) + if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + ops->tmo_request_launch_time(meta->request.launch_time, + priv); + + if (ops->tmo_request_timestamp) + if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) + ops->tmo_request_timestamp(priv); + + if (ops->tmo_request_checksum) + if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) + ops->tmo_request_checksum(meta->request.csum_start, + meta->request.csum_offset, priv); + + if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + *pmeta = NULL; +} + static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { @@ -483,6 +520,12 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return false; } +static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, + void *priv) +{ +} + static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { From 849b1664dbda1cf6c63e0fd4f9dec23782b8c851 Mon Sep 17 00:00:00 2001 From: Stanislav Fomichev Date: Mon, 27 Jul 2026 09:19:59 -0700 Subject: [PATCH 424/501] xsk: validate metadata when processing requests The zero-copy path validates TX metadata while obtaining the descriptor context, then reads it again later when preparing the hardware request. User space can change the metadata between those operations and bypass the original validation. Validate the metadata in xsk_tx_metadata_request() and use the resulting flags snapshot for every feature check. Read request fields once so all zero-copy drivers process only values observed after successful validation. Fixes: ca4419f15abd ("xsk: Add launch time hardware offload support to XDP Tx metadata") Cc: Cen Zhang (Microsoft) Signed-off-by: Stanislav Fomichev Link: https://patch.msgid.link/20260727161959.885642-7-sdf@fomichev.me Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 2 +- .../net/ethernet/mellanox/mlx5/core/en/xdp.c | 5 +- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- include/net/libeth/xsk.h | 2 +- include/net/xdp_sock_drv.h | 51 ++++++++++--------- net/xdp/xsk_buff_pool.c | 6 +-- 6 files changed, 37 insertions(+), 33 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index beaea1479485..16fcc8cf6191 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -3082,7 +3082,7 @@ static void igc_xdp_xmit_zc(struct igc_ring *ring) meta_req.tx_buffer = bi; meta_req.meta = meta; meta_req.used_desc = 0; - xsk_tx_metadata_request(&meta, &igc_xsk_tx_metadata_ops, + xsk_tx_metadata_request(pool, &meta, &igc_xsk_tx_metadata_ops, &meta_req); /* xsk_tx_metadata_request() may have updated next_to_use */ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index dac5e9d4c8bd..77ea51bfbaae 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -505,7 +505,7 @@ mlx5e_xmit_xdp_frame_mpwqe(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptx */ mlx5e_xdp_mpwqe_session_start(sq); if (meta) - xsk_tx_metadata_request(meta, + xsk_tx_metadata_request(sq->xsk_pool, meta, &mlx5e_xsk_tx_metadata_ops, &session->wqe->eth); } @@ -653,7 +653,8 @@ mlx5e_xmit_xdp_frame(struct mlx5e_xdpsq *sq, struct mlx5e_xmit_data *xdptxd, sq->pc += num_wqebbs; if (meta) - xsk_tx_metadata_request(meta, &mlx5e_xsk_tx_metadata_ops, eseg); + xsk_tx_metadata_request(sq->xsk_pool, meta, + &mlx5e_xsk_tx_metadata_ops, eseg); sq->doorbell_cseg = cseg; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 7c3a7c56a81a..97b28cfa14c2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2748,8 +2748,8 @@ static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) meta_req.set_ic = &set_ic; meta_req.tbs = tx_q->tbs; meta_req.edesc = &tx_q->dma_entx[entry]; - xsk_tx_metadata_request(&meta, &stmmac_xsk_tx_metadata_ops, - &meta_req); + xsk_tx_metadata_request(pool, &meta, + &stmmac_xsk_tx_metadata_ops, &meta_req); if (set_ic) { tx_q->tx_count_frames = 0; stmmac_set_tx_ic(priv, tx_desc); diff --git a/include/net/libeth/xsk.h b/include/net/libeth/xsk.h index e2fa6bf6b1b3..5dcc0d7f65b7 100644 --- a/include/net/libeth/xsk.h +++ b/include/net/libeth/xsk.h @@ -205,7 +205,7 @@ __libeth_xsk_xmit_fill_buf_md(const struct xdp_desc *xdesc, BUILD_BUG_ON(!__builtin_constant_p(tmo == libeth_xsktmo)); tmo = tmo == libeth_xsktmo ? &__libeth_xsktmo : tmo; - xsk_tx_metadata_request(&ctx.meta, tmo, &desc); + xsk_tx_metadata_request(sq->pool, &ctx.meta, tmo, &desc); return desc; } diff --git a/include/net/xdp_sock_drv.h b/include/net/xdp_sock_drv.h index f87c4215673e..b344789f5df8 100644 --- a/include/net/xdp_sock_drv.h +++ b/include/net/xdp_sock_drv.h @@ -245,7 +245,7 @@ static inline void *xsk_buff_raw_get_data(struct xsk_buff_pool *pool, u64 addr) * details. * * Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata - * pointer, if it is present and valid (initialized to %NULL otherwise). + * pointer, if it is present (initialized to %NULL otherwise). */ static inline struct xdp_desc_ctx xsk_buff_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) @@ -274,54 +274,56 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, /** * xsk_tx_metadata_request - Evaluate AF_XDP TX metadata at submission * and call appropriate xsk_tx_metadata_ops operation. + * @pool: pointer to AF_XDP buffer pool, used to validate the metadata * @pmeta: pointer to pointer to AF_XDP metadata area * @ops: pointer to struct xsk_tx_metadata_ops - * @priv: pointer to driver-private aread + * @priv: pointer to driver-private area * * This function should be called by the networking device when * it prepares AF_XDP egress packet. */ -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) +static inline void +xsk_tx_metadata_request(const struct xsk_buff_pool *pool, + struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, void *priv) { const struct xsk_tx_metadata *meta = *pmeta; + u64 flags; if (!meta) return; + if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) { + *pmeta = NULL; + return; /* no way to signal the error to the user */ + } + if (ops->tmo_request_launch_time) - if (meta->flags & XDP_TXMD_FLAGS_LAUNCH_TIME) - ops->tmo_request_launch_time(meta->request.launch_time, - priv); + if (flags & XDP_TXMD_FLAGS_LAUNCH_TIME) + ops->tmo_request_launch_time( + READ_ONCE(meta->request.launch_time), priv); if (ops->tmo_request_timestamp) - if (meta->flags & XDP_TXMD_FLAGS_TIMESTAMP) + if (flags & XDP_TXMD_FLAGS_TIMESTAMP) ops->tmo_request_timestamp(priv); if (ops->tmo_request_checksum) - if (meta->flags & XDP_TXMD_FLAGS_CHECKSUM) - ops->tmo_request_checksum(meta->request.csum_start, - meta->request.csum_offset, priv); + if (flags & XDP_TXMD_FLAGS_CHECKSUM) + ops->tmo_request_checksum( + READ_ONCE(meta->request.csum_start), + READ_ONCE(meta->request.csum_offset), priv); - if (!(meta->flags & XDP_TXMD_FLAGS_TIMESTAMP)) + if (!(flags & XDP_TXMD_FLAGS_TIMESTAMP)) *pmeta = NULL; } static inline struct xsk_tx_metadata * __xsk_buff_get_metadata(const struct xsk_buff_pool *pool, void *data) { - struct xsk_tx_metadata *meta; - u64 flags; - if (!pool->tx_metadata_len) return NULL; - meta = data - pool->tx_metadata_len; - if (unlikely(!xsk_buff_valid_tx_metadata(pool, meta, &flags))) - return NULL; /* no way to signal the error to the user */ - - return meta; + return data - pool->tx_metadata_len; } static inline struct xsk_tx_metadata * @@ -520,9 +522,10 @@ xsk_buff_valid_tx_metadata(const struct xsk_buff_pool *pool, return false; } -static inline void xsk_tx_metadata_request(struct xsk_tx_metadata **pmeta, - const struct xsk_tx_metadata_ops *ops, - void *priv) +static inline void +xsk_tx_metadata_request(const struct xsk_buff_pool *pool, + struct xsk_tx_metadata **pmeta, + const struct xsk_tx_metadata_ops *ops, void *priv) { } diff --git a/net/xdp/xsk_buff_pool.c b/net/xdp/xsk_buff_pool.c index a4089480b22b..78c14f106395 100644 --- a/net/xdp/xsk_buff_pool.c +++ b/net/xdp/xsk_buff_pool.c @@ -765,11 +765,11 @@ EXPORT_SYMBOL(xp_raw_get_dma); * @addr: desc address (from userspace) * * Helper for getting desc's DMA address and metadata pointer, if present. - * Saves one call on hotpath, double calculation of the actual address, - * and inline checks for metadata presence and sanity. + * Saves one call on hotpath and double calculation of the actual address. + * Metadata is validated later by xsk_tx_metadata_request(). * * Return: new &xdp_desc_ctx struct containing desc's DMA address and metadata - * pointer, if it is present and valid (initialized to %NULL otherwise). + * pointer, if it is present (initialized to %NULL otherwise). */ struct xdp_desc_ctx xp_raw_get_ctx(const struct xsk_buff_pool *pool, u64 addr) { From 1f428e30947395d9b9aacee03e25a4e6cfcad7a4 Mon Sep 17 00:00:00 2001 From: Yi Cong Date: Wed, 29 Jul 2026 11:04:36 +0800 Subject: [PATCH 425/501] net: usb: ax88179_178a: fix skb leak in ax88179_tx_fixup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the interface has NETIF_F_SG enabled and skb_linearize() fails in ax88179_tx_fixup(), the function returns NULL without freeing the skb. usbnet_start_xmit() treats a NULL return from tx_fixup() as a drop (info->flags does not set FLAG_MULTI_PACKET for this driver), jumping to the "drop" label where it does `if (skb) dev_kfree_skb_any(skb)`. Because tx_fixup() returned NULL, the local skb variable in usbnet_start_xmit() is NULL, so the original skb is never freed — a memory leak on every TX frame whose linearization fails (i.e. under memory pressure). Free the skb before returning, matching the error handling already used for the pskb_expand_head() failure path in the same function. Fixes: 16b1c4e01c89 ("net: usb: ax88179_178a: add TSO feature") Cc: stable@vger.kernel.org Signed-off-by: Yi Cong Link: https://patch.msgid.link/20260729030436.3420477-1-cong.yi@linux.dev Signed-off-by: Jakub Kicinski --- drivers/net/usb/ax88179_178a.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 98f899ea2e94..81d8412ce8e2 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c @@ -1487,8 +1487,10 @@ ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) headroom = skb_headroom(skb) - 8; - if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) + if ((dev->net->features & NETIF_F_SG) && skb_linearize(skb)) { + dev_kfree_skb_any(skb); return NULL; + } if ((skb_header_cloned(skb) || headroom < 0) && pskb_expand_head(skb, headroom < 0 ? 8 : 0, 0, GFP_ATOMIC)) { From 1cb4298810e27e037d3ca07286ecbb97e89ba58d Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Fri, 31 Jul 2026 12:22:59 -0700 Subject: [PATCH 426/501] bnge: Fix NULL pointer dereference in aux device release If allocation of auxr_dev fails during auxiliary device setup, the error path calls auxiliary_device_uninit(), which eventually invokes bnge_aux_dev_release(). The release callback unconditionally dereferences aux_priv->auxr_dev->pdev to retrieve the parent bnge_dev. Since auxr_dev has not yet been allocated on this failure path, the dereference results in a NULL pointer exception Retrieve the parent bnge_dev from the auxiliary device's parent instead of auxr_dev, and free auxr_dev only when it was successfully allocated. This allows the release callback to correctly clean up partially initialized auxiliary devices. Fixes: 8ac050ec3b1c ("bng_en: Add RoCE aux device support") Signed-off-by: Alok Tiwari Reviewed-by: Bhargava Marreddy Link: https://patch.msgid.link/20260731192301.1427645-1-alok.a.tiwari@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_auxr.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c index 67e93e17d4d9..0955b488b6fe 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_auxr.c @@ -141,12 +141,15 @@ static void bnge_aux_dev_release(struct device *dev) { struct bnge_auxr_priv *aux_priv = container_of(dev, struct bnge_auxr_priv, aux_dev.dev); - struct bnge_dev *bd = pci_get_drvdata(aux_priv->auxr_dev->pdev); + struct bnge_auxr_dev *auxr_dev = aux_priv->auxr_dev; + struct bnge_dev *bd = pci_get_drvdata(to_pci_dev(dev->parent)); ida_free(&bnge_aux_dev_ids, aux_priv->id); - kfree(aux_priv->auxr_dev->auxr_info); + if (auxr_dev) { + kfree(auxr_dev->auxr_info); + kfree(auxr_dev); + } bd->auxr_dev = NULL; - kfree(aux_priv->auxr_dev); kfree(aux_priv); bd->aux_priv = NULL; } From 5d9686af2976741bbd79b150d1c9e60b81e7f12e Mon Sep 17 00:00:00 2001 From: Daming Li Date: Thu, 30 Jul 2026 22:55:52 +0800 Subject: [PATCH 427/501] net: smc: fix splice entry lifetime imbalance in smc_rx_splice smc_rx_splice() passes pages to splice_to_pipe() before taking the references that cover the lifetime of each splice entry. In the VM-backed RMB path, splice_to_pipe() may drop unqueued entries through smc_rx_spd_release(), while queued entries are released later via the pipe buffer callback. The old post-splice accounting also derives the number of queued VM pages from an offset mutated while building the descriptor, and a multi-page splice pairs one sock_hold() with multiple sock_put() calls. Take the page and socket references for every candidate entry before splice_to_pipe(), and drop the matching private state, page reference, and socket reference from smc_rx_spd_release() for entries that never get queued. This fixes a refcount imbalance that can underflow page refcounts and trigger a use-after-free. Fixes: 9014db202cb7 ("smc: add support for splice()") Cc: stable@vger.kernel.org Reported-by: Vega Co-developed-by: Xiao Liu Signed-off-by: Xiao Liu Signed-off-by: Daming Li Signed-off-by: Ren Wei Reviewed-by: Dust Li Reviewed-by: Sidraya Jayagond Link: https://patch.msgid.link/20260730145552.360287-2-enjou1224z@gmail.com Signed-off-by: Jakub Kicinski --- net/smc/smc_rx.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index c1d9b923938d..5c9e4d8b57de 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -150,7 +150,12 @@ static const struct pipe_buf_operations smc_pipe_ops = { static void smc_rx_spd_release(struct splice_pipe_desc *spd, unsigned int i) { + struct smc_spd_priv *priv = (struct smc_spd_priv *)spd->partial[i].private; + struct sock *sk = &priv->smc->sk; + + kfree(priv); put_page(spd->pages[i]); + sock_put(sk); } static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, @@ -209,6 +214,10 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, offset = 0; } } + for (i = 0; i < nr_pages; i++) { + get_page(pages[i]); + sock_hold(&smc->sk); + } spd.nr_pages_max = nr_pages; spd.nr_pages = nr_pages; spd.pages = pages; @@ -217,16 +226,8 @@ static int smc_rx_splice(struct pipe_inode_info *pipe, char *src, size_t len, spd.spd_release = smc_rx_spd_release; bytes = splice_to_pipe(pipe, &spd); - if (bytes > 0) { - sock_hold(&smc->sk); - if (!lgr->is_smcd && smc->conn.rmb_desc->is_vm) { - for (i = 0; i < PAGE_ALIGN(bytes + offset) / PAGE_SIZE; i++) - get_page(pages[i]); - } else { - get_page(smc->conn.rmb_desc->pages); - } + if (bytes > 0) atomic_add(bytes, &smc->conn.splice_pending); - } kfree(priv); kfree(partial); kfree(pages); From 4ff9548d84945d2cbf9e4c207288063a200ea397 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Thu, 30 Jul 2026 12:59:26 +0000 Subject: [PATCH 428/501] ipv4: Fix fib_nlmsg_size() for RTA_VIA nexthops fib_nlmsg_size() still estimates nexthop space as if every gateway is encoded as an IPv4 RTA_GATEWAY attribute. IPv4 routes can also carry an IPv6 gateway, which fib_nexthop_info() dumps as RTA_VIA. As a result, route notifications can allocate an skb that is too small. fib_dump_info() then fails with -EMSGSIZE and rtmsg_fib() hits the WARN_ON() that marks such failures as a fib_nlmsg_size() bug. With panic_on_warn set, this becomes a kernel panic. Mirror the actual nexthop dump layout in fib_nlmsg_size(): account for IPv6 nexthop gateways dumped as RTA_VIA, for the no-header rtnexthop layout used inside RTA_MULTIPATH, and for RTA_FLOW only when it is actually present. Fixes: d15662682db2 ("ipv4: Allow ipv6 gateway with ipv4 routes") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/6f53fa797fcaeb26966432ed7ae9bb87c4961f37.1785411220.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv4/fib_semantics.c | 67 +++++++++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 4f3c0740dde9..78f84ae3ee12 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -490,6 +490,34 @@ int ip_fib_check_default(__be32 gw, struct net_device *dev) return -1; } +static size_t fib_nexthop_nlmsg_size(const struct fib_nh_common *nhc, + bool skip_oif) +{ + size_t nhsize = 0; + + switch (nhc->nhc_gw_family) { + case AF_INET: + nhsize += nla_total_size(4); /* RTA_GATEWAY */ + break; + case AF_INET6: + nhsize += nla_total_size(sizeof(struct rtvia) + + sizeof(struct in6_addr)); + break; + } + + if (!skip_oif && nhc->nhc_dev) + nhsize += nla_total_size(4); /* RTA_OIF */ + + if (nhc->nhc_lwtstate) { + /* RTA_ENCAP */ + nhsize += lwtunnel_get_encap_size(nhc->nhc_lwtstate); + /* RTA_ENCAP_TYPE */ + nhsize += nla_total_size(2); + } + + return nhsize; +} + size_t fib_nlmsg_size(struct fib_info *fi) { size_t payload = NLMSG_ALIGN(sizeof(struct rtmsg)) @@ -507,32 +535,35 @@ size_t fib_nlmsg_size(struct fib_info *fi) payload += nla_total_size(4); /* RTA_NH_ID */ if (nhs) { - size_t nh_encapsize = 0; - /* Also handles the special case nhs == 1 */ - - /* each nexthop is packed in an attribute */ - size_t nhsize = nla_total_size(sizeof(struct rtnexthop)); + size_t mpsize = 0; unsigned int i; - /* may contain flow and gateway attribute */ - nhsize += 2 * nla_total_size(4); - - /* grab encap info */ for (i = 0; i < fib_info_num_path(fi); i++) { struct fib_nh_common *nhc = fib_info_nhc(fi, i); + size_t nhsize; - if (nhc->nhc_lwtstate) { - /* RTA_ENCAP_TYPE */ - nh_encapsize += lwtunnel_get_encap_size( - nhc->nhc_lwtstate); - /* RTA_ENCAP */ - nh_encapsize += nla_total_size(2); + nhsize = fib_nexthop_nlmsg_size(nhc, nhs != 1); + + if (nhs != 1) + nhsize += NLA_ALIGN(sizeof(struct rtnexthop)); + +#ifdef CONFIG_IP_ROUTE_CLASSID + if (nhc->nhc_family == AF_INET) { + struct fib_nh *nh; + + nh = container_of(nhc, struct fib_nh, nh_common); + if (nh->nh_tclassid) + nhsize += nla_total_size(4); } +#endif + if (nhs == 1) + payload += nhsize; + else + mpsize += nhsize; } - /* all nexthops are packed in a nested attribute */ - payload += nla_total_size((nhs * nhsize) + nh_encapsize); - + if (nhs != 1) + payload += nla_total_size(mpsize); } return payload; From d141f087b1af656f055d7c5793a3e87817ba0bbe Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 30 Jul 2026 16:22:16 +0200 Subject: [PATCH 429/501] s390/qeth: validate user buffer length in SNMP and ARP query ioctls qeth_snmp_command() and qeth_l3_arp_query() allocate a buffer sized by a user-supplied length (udata_len) without checking a lower bound, then set udata_offset to a fixed non-zero value and pass both to a reply callback. The callback bounds-checks the copy with if ((udata_len - udata_offset) < len) Both fields are u32, so a udata_len smaller than udata_offset makes the subtraction wrap and the check pass, and the following memcpy() writes past the allocation. A udata_len of 0 also yields ZERO_SIZE_PTR from kzalloc(), which the existing NULL check does not catch. Reject buffers smaller than udata_offset before allocating, so the callback subtraction can no longer underflow. Fixes: 4a71df50047f ("qeth: new qeth device driver") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Reviewed-by: Joe Damato Link: https://patch.msgid.link/20260730142216.218309-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/qeth_core_main.c | 3 +++ drivers/s390/net/qeth_l3_main.c | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f18eed9df3c7..c3257b213360 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -4710,6 +4710,9 @@ static int qeth_snmp_command(struct qeth_card *card, char __user *udata) if (req_len > QETH_BUFSIZE) return -EINVAL; + if (qinfo.udata_len < sizeof(struct qeth_snmp_ureq_hdr)) + return -EINVAL; + iob = qeth_get_adapter_cmd(card, IPA_SETADP_SET_SNMP_CONTROL, req_len); if (!iob) return -ENOMEM; diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 1542bfc9f561..f1ac9950dcb4 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1415,6 +1415,11 @@ static int qeth_l3_arp_query(struct qeth_card *card, char __user *udata) rc = -EFAULT; goto out; } + + if (qinfo.udata_len < QETH_QARP_ENTRIES_OFFSET) { + rc = -EINVAL; + goto out; + } qinfo.udata = kzalloc(qinfo.udata_len, GFP_KERNEL); if (!qinfo.udata) { rc = -ENOMEM; From d0f86fb36eb260abd10007b62c9dcc1028e03e61 Mon Sep 17 00:00:00 2001 From: Xuanqiang Luo Date: Thu, 30 Jul 2026 17:35:54 +0800 Subject: [PATCH 430/501] udp: fix potential use-after-free in tunnel segmentation __skb_udp_tunnel_segment() gets the UDP header before ensuring the tunnel header is in the skb head. If the pull reallocates skb->head, the saved UDP header pointer is no longer valid. Get the UDP header after the pull to avoid a potential use-after-free. Fixes: dbef491ebe7f ("udp: Use uh->len instead of skb->len to compute checksum in segmentation") Signed-off-by: Xuanqiang Luo Reviewed-by: Antoine Tenart Link: https://patch.msgid.link/20260730093554.68127-1-xuanqiang.luo@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/udp_offload.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 29651b1a0bc7..abcd3cc2d9a3 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -178,17 +178,19 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); bool remcsum, need_csum, offload_csum, gso_partial; struct sk_buff *segs = ERR_PTR(-EINVAL); - struct udphdr *uh = udp_hdr(skb); u16 mac_offset = skb->mac_header; __be16 protocol = skb->protocol; u16 mac_len = skb->mac_len; int udp_offset, outer_hlen; + struct udphdr *uh; __wsum partial; bool need_ipsec; if (unlikely(!pskb_may_pull(skb, tnl_hlen))) goto out; + uh = udp_hdr(skb); + /* Adjust partial header checksum to negate old length. * We cannot rely on the value contained in uh->len as it is * possible that the actual value exceeds the boundaries of the From 2a33516f9ef59ad11844d4fc152f889449b5daf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toke=20H=C3=B8iland-J=C3=B8rgensen?= Date: Wed, 29 Jul 2026 21:14:16 +0200 Subject: [PATCH 431/501] net/sched: sch_cake: drop WARN_ON(1) for malformed packets in ACK filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sch_cake ACK filter parses packets to find the TCP header and filter duplicated ACKs if the flow is backlogged. The parsing code contains a WARN_ON(1) which can be triggered by a malformed IP header in certain cases. Depending on the system configuration, this leads either to either spamming dmesg with warnings, or a panic if panic_on_warn is set. The code already correctly skips the offending packet in the branch that triggers the warning, so the WARN_ON itself doesn't really serve any purpose. So just drop it altogether to avoid the inconvenient side effects. Fixes: 8b7138814f29 ("sch_cake: Add optional ACK filter") Reported-by: Zhiling Zou Reported-by: Ren Wei Signed-off-by: Toke Høiland-Jørgensen Link: https://patch.msgid.link/20260729191417.45665-1-toke@toke.dk Signed-off-by: Jakub Kicinski --- net/sched/sch_cake.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c index f64be54ead49..f25f60978631 100644 --- a/net/sched/sch_cake.c +++ b/net/sched/sch_cake.c @@ -1287,7 +1287,6 @@ static struct sk_buff *cake_ack_filter(struct cake_sched_data *q, seglen = ipv6_payload_len(skb, ipv6h_check); } else { - WARN_ON(1); /* shouldn't happen */ continue; } From d876c493fc4b811941bfeb4c80beb2dfc4bf025e Mon Sep 17 00:00:00 2001 From: Linfeng Sun Date: Mon, 27 Jul 2026 16:18:41 +0800 Subject: [PATCH 432/501] vhost-scsi: Validate T10 PI scatterlist counts When T10 PI is negotiated, vhost-scsi splits protection bytes from the data iterator before mapping the request scatterlists. A malformed request can claim protection bytes that cover or exceed the full payload length. The former leaves no data bytes to map, while the latter underflows exp_data_len before advancing the iterator. Both cases can let a zero data SGL count reach sg_alloc_table_chained(), which triggers BUG_ON(!nents). Reject protection lengths that cover or exceed the payload before subtracting prot_bytes and advancing the iterator. Also propagate negative errors from the protection SGL calculation before calling the allocator, matching the data SGL path. Fixes: bca939d5bcd0 ("vhost-scsi: Dynamically allocate scatterlists") Suggested-by: Jia Jia Signed-off-by: Jia Jia Assisted-by: OpenAI-Codex:GPT-5 Signed-off-by: Linfeng Sun Message-ID: <20260727081841.923151-1-slf@hdu.edu.cn> Signed-off-by: Michael S. Tsirkin --- drivers/vhost/scsi.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index 9a1253b9d8c5..c79197edb163 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -972,6 +972,9 @@ vhost_scsi_mapal(struct vhost_scsi *vs, struct vhost_scsi_cmd *cmd, if (prot_bytes) { sgl_count = vhost_scsi_calc_sgls(prot_iter, prot_bytes, VHOST_SCSI_PREALLOC_PROT_SGLS); + if (sgl_count < 0) + return sgl_count; + cmd->prot_table.sgl = cmd->prot_sgl; ret = sg_alloc_table_chained(&cmd->prot_table, sgl_count, cmd->prot_table.sgl, @@ -1416,6 +1419,11 @@ vhost_scsi_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) * actual data payload length. */ if (prot_bytes) { + if (prot_bytes >= exp_data_len) { + vq_err(vq, "Protection data exceeds payload length\n"); + goto err; + } + exp_data_len -= prot_bytes; prot_iter = data_iter; iov_iter_truncate(&prot_iter, prot_bytes); From de845981da67a6b049080c87e605130b0c30adc5 Mon Sep 17 00:00:00 2001 From: Jun Yang Date: Mon, 3 Aug 2026 09:45:14 +0800 Subject: [PATCH 433/501] vhost: reset the vring metadata cache on vring reconfiguration vq->meta_iotlb[] caches the vhost_iotlb_map that backs each vring metadata region, and iotlb_access_ok() returns early on a cache hit, taking the hit as proof that the region has already been validated: if (vhost_vq_meta_fetch(vq, addr, len, type)) return true; The cache is reset on VHOST_IOTLB_UPDATE and VHOST_IOTLB_INVALIDATE, on device IOTLB (re)initialisation and on vq reset, but not when VHOST_SET_VRING_ADDR replaces vq->desc, vq->avail and vq->used, nor when VHOST_SET_VRING_NUM changes the region sizes. With a device IOTLB attached both ioctls are accepted while the vq is live, and neither validates the addresses at ioctl time: vq_access_ok() and vq_log_used_access_ok() return true early because the addresses are GIOVAs, deferring validation to prefetch time. Once the cache has been populated that deferred validation no longer runs -- vq_meta_prefetch() hits the stale entry and returns true -- and vhost_vq_meta_fetch() keeps translating through the old mapping as map->addr + addr - map->start for an address the mapping no longer covers. vhost_copy_to_user() and vhost_copy_from_user() consume the result with __copy_to_user() and __copy_from_user(), which do not check it either, so a subsequent used ring update or descriptor fetch accesses memory outside the region the IOTLB actually maps. Reset the metadata cache whenever the vring is reconfigured, so the new addresses are pushed back through iotlb_access_ok()'s slow path. Fixes: f88949138058 ("vhost: introduce O(1) vq metadata cache") Cc: stable@vger.kernel.org Assisted-by: tencentos-corvus-ai:kimi-k3 Signed-off-by: Jun Yang Message-ID: <20260803014823.68623-1-juny24602@gmail.com> Signed-off-by: Michael S. Tsirkin --- drivers/vhost/vhost.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index ae29a7ef7baa..269efad90369 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -2131,6 +2131,14 @@ static long vhost_vring_set_num_addr(struct vhost_dev *d, BUG(); } + /* + * The metadata cache holds the IOTLB mapping that backed the previous + * desc/avail/used addresses and vring size, both of which are being + * replaced here. iotlb_access_ok() takes a cache hit as proof that the + * region was validated, so the stale entries have to go. + */ + __vhost_vq_meta_reset(vq); + mutex_unlock(&vq->mutex); return r; From 22598f55a4c2b510b3df5e69e563387a963222ae Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Fri, 24 Jul 2026 14:09:19 +0800 Subject: [PATCH 434/501] vhost-scsi: flush backend after device ioctls vhost-scsi translates guest response descriptors into userspace iovecs when commands are submitted. Target-core completes those commands asynchronously, so VHOST_SET_MEM_TABLE can replace the memory table while an in-flight command still retains response iovecs translated through the old table. If the old mapping is reused after VHOST_SET_MEM_TABLE returns, command completion can write the response to an unrelated userspace object. Flush the vhost-scsi backend after vhost_dev_ioctl() handles a device ioctl. This waits for in-flight commands that can still use the old response iovecs before the ioctl returns. Signed-off-by: Jia Jia Signed-off-by: Michael S. Tsirkin Message-ID: <20260724060919.1569170-1-physicalmtea@gmail.com> --- drivers/vhost/scsi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index c79197edb163..aae1164e1ca9 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -2434,9 +2434,10 @@ vhost_scsi_ioctl(struct file *f, default: mutex_lock(&vs->dev.mutex); r = vhost_dev_ioctl(&vs->dev, ioctl, argp); - /* TODO: flush backend after dev ioctl. */ if (r == -ENOIOCTLCMD) r = vhost_vring_ioctl(&vs->dev, ioctl, argp); + else + vhost_scsi_flush(vs); mutex_unlock(&vs->dev.mutex); return r; } From 42bc45df5905e2b7dccb72adaf7730f66cfbe03f Mon Sep 17 00:00:00 2001 From: Jia Jia Date: Sun, 26 Jul 2026 22:43:14 +0800 Subject: [PATCH 435/501] vhost-scsi: reject feature changes after endpoint vhost_scsi_setup_vq_cmds() runs from VHOST_SCSI_SET_ENDPOINT and allocates each command's protection scatterlist array (prot_sgl) according to the acknowledged VIRTIO_SCSI_F_T10_PI bit. The command pools are not rebuilt when VHOST_SET_FEATURES changes that bit later. Although virtio feature bits must not change after feature negotiation, vhost_scsi_set_features() currently accepts such a request after the endpoint is active and updates acked_features. Enabling T10-PI after endpoint setup therefore leaves prot_sgl NULL while the I/O path follows the new feature bit. For a 129-page protection payload, vhost_scsi_mapal() passes the missing first chunk to sg_alloc_table_chained(): sg_alloc_table_chained(table, 129, first_chunk=NULL, nents_first_chunk=inline_sg_cnt) sg_pool_index() then hits: BUG_ON(nents > SG_CHUNK_SIZE); /* 129 > 128 */ The kernel reported the following call trace and register state: Call Trace: ? __sg_alloc_table+0x1d8/0x250 ? __pfx_vhost_run_work_list+0x10/0x10 [vhost] sg_alloc_table_chained+0x59/0xf0 ? __pfx_sg_pool_alloc+0x10/0x10 ? vhost_scsi_calc_sgls.constprop.0+0x43/0x60 [vhost_scsi] vhost_scsi_handle_vq+0xf02/0x1700 [vhost_scsi] ? __pfx_vhost_scsi_handle_vq+0x10/0x10 [vhost_scsi] vhost_scsi_handle_kick+0x37/0x50 [vhost_scsi] vhost_run_work_list+0x8e/0xd0 [vhost] vhost_task_fn+0xe1/0x210 ret_from_fork+0x348/0x540 RIP: 0010:0x4 CR2 = 0x4 RSP: 0018:ffffc90000dbf940 EFLAGS: 00010202 RAX: ffffffff82396810 RBX: ffff88811dc28b80 RCX: 0000000000000000 RDX: 0000000000000000 RSI: 0000000000000820 RDI: 0000000000000081 VHOST_F_LOG_ALL is a vhost-specific runtime feature and remains the only exception. Reject changes to any feature other than VHOST_F_LOG_ALL while the endpoint is active. This preserves the existing runtime log toggle while preventing feature-dependent command resources and data-path state from becoming inconsistent. Userspace must clear the endpoint before changing any other negotiated feature and set the endpoint up again afterward. Fixes: bf2d650391be ("vhost-scsi: Allocate T10 PI structs only when enabled") Signed-off-by: Jia Jia Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael S. Tsirkin Message-ID: <20260726144314.1652934-1-physicalmtea@gmail.com> --- drivers/vhost/scsi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index aae1164e1ca9..7a1f39a327da 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -2227,6 +2227,7 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) { struct vhost_virtqueue *vq; bool is_log, was_log; + u64 old_features; int i; if (features & ~VHOST_SCSI_FEATURES) @@ -2242,6 +2243,14 @@ static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features) if (!vs->dev.nvqs) goto out; + old_features = vs->vqs[0].vq.acked_features; + if (vs->vs_tpg && + ((features ^ old_features) & + ~(1ULL << VHOST_F_LOG_ALL))) { + mutex_unlock(&vs->dev.mutex); + return -EBUSY; + } + is_log = features & (1 << VHOST_F_LOG_ALL); /* * All VQs should have same feature. From 6cdd8cbbf89611da6c948d58daed1c53f4bf8321 Mon Sep 17 00:00:00 2001 From: Billy Tsai Date: Thu, 23 Jul 2026 10:33:09 +0800 Subject: [PATCH 436/501] MAINTAINERS: add Ryan Chen and Billy Tsai as reviewer for ARM/ASPEED Add Ryan Chen and myself as a reviewer for the ARM/ASPEED MACHINE SUPPORT entry to reflect ongoing review and contribution work on AST2xxx/AST27xx platform support. Signed-off-by: Billy Tsai Signed-off-by: Andrew Jeffery --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 15011f5752a9..791a5d5865d1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2676,6 +2676,8 @@ F: drivers/irqchip/irq-aspeed-i2c-ic.c ARM/ASPEED MACHINE SUPPORT M: Joel Stanley M: Andrew Jeffery +R: Ryan Chen +R: Billy Tsai L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) S: Supported From 1a35da325cac4d5bcad76a2aa943408a6f1d9000 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Wed, 29 Jul 2026 09:16:53 +0000 Subject: [PATCH 437/501] packet: synchronize pressure clearing with ring reconfiguration packet_set_ring() updates the RX ring state under sk_receive_queue.lock, but used to publish the tpacket receive mode through po->prot_hook.func after releasing that lock. packet_poll() and packet_recvmsg() can then run the pressure clearing path after the ring has been cleared while still seeing tpacket_rcv, causing __packet_rcv_has_room() to dereference stale or NULL ring storage. Move the existing receive hook assignment into the same sk_receive_queue.lock section as the ring state update. Keep the assignment otherwise unchanged, including on TX ring reconfiguration, to avoid adding behavior changes that are not required for the fix. Serialize packet_recvmsg() pressure clearing with the same queue lock only after PACKET_SOCK_PRESSURE has been observed. If the flag is clear and the socket has moved away from tpacket_rcv, packet_set_ring() has already detached the socket and waited for synchronize_net(), so no new packet input can set the flag again. packet_poll() already holds sk_receive_queue.lock, so it uses the new unlocked helper directly. Fixes: 2ccdbaa6d55b ("packet: rollover lock contention avoidance") Cc: stable@vger.kernel.org Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zihan Xi Link: https://patch.msgid.link/f90b5688311fa278d1361ea8c6be0bf25967d591.1785247446.git.zihanx@nebusec.ai Signed-off-by: Paolo Abeni --- net/packet/af_packet.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 5ae0511e89e3..0e1355be89f6 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1315,13 +1315,25 @@ static int packet_rcv_has_room(struct packet_sock *po, struct sk_buff *skb) return ret; } -static void packet_rcv_try_clear_pressure(struct packet_sock *po) +static void __packet_rcv_try_clear_pressure(struct packet_sock *po) { if (packet_sock_flag(po, PACKET_SOCK_PRESSURE) && __packet_rcv_has_room(po, NULL) == ROOM_NORMAL) packet_sock_flag_set(po, PACKET_SOCK_PRESSURE, false); } +static void packet_rcv_try_clear_pressure(struct packet_sock *po) +{ + struct sock *sk = &po->sk; + + if (!packet_sock_flag(po, PACKET_SOCK_PRESSURE)) + return; + + spin_lock_bh(&sk->sk_receive_queue.lock); + __packet_rcv_try_clear_pressure(po); + spin_unlock_bh(&sk->sk_receive_queue.lock); +} + static void packet_sock_destruct(struct sock *sk) { skb_queue_purge(&sk->sk_error_queue); @@ -4305,7 +4317,7 @@ static __poll_t packet_poll(struct file *file, struct socket *sock, TP_STATUS_KERNEL)) mask |= EPOLLIN | EPOLLRDNORM; } - packet_rcv_try_clear_pressure(po); + __packet_rcv_try_clear_pressure(po); spin_unlock_bh(&sk->sk_receive_queue.lock); spin_lock_bh(&sk->sk_write_queue.lock); if (po->tx_ring.pg_vec) { @@ -4545,14 +4557,14 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, rb->frame_max = (req->tp_frame_nr - 1); rb->head = 0; rb->frame_size = req->tp_frame_size; + po->prot_hook.func = (po->rx_ring.pg_vec) ? + tpacket_rcv : packet_rcv; spin_unlock_bh(&rb_queue->lock); swap(rb->pg_vec_order, order); swap(rb->pg_vec_len, req->tp_block_nr); rb->pg_vec_pages = req->tp_block_size/PAGE_SIZE; - po->prot_hook.func = (po->rx_ring.pg_vec) ? - tpacket_rcv : packet_rcv; skb_queue_purge(rb_queue); if (atomic_long_read(&po->mapped)) pr_err("packet_mmap: vma is busy: %ld\n", From cf6f8b29befb92173659bcef6a441d274947bfae Mon Sep 17 00:00:00 2001 From: "Cen Zhang (Microsoft)" Date: Thu, 30 Jul 2026 18:20:06 -0400 Subject: [PATCH 438/501] net/openvswitch: check Ethernet header length in key_extract() When a packet arrives on an ARPHRD_NONE device (e.g. TUN), ovs_flow_key_extract() trusts the user-provided skb->protocol field: if it is ETH_P_TEB, the packet is classified as MAC_PROTO_ETHERNET and key_extract() is called without ensuring the skb has ETH_HLEN (14) bytes of linear data. key_extract() unconditionally pulls 2 * ETH_ALEN bytes for MAC addresses and parse_ethertype() pulls 2 more, either of which triggers a kernel BUG in __skb_pull() when the linear area is too small. kernel BUG at include/linux/skbuff.h:2848! RIP: 0010:key_extract+0xa7e/0xd90 net/openvswitch/flow.c:933 ovs_flow_key_extract+0x419/0xa70 ovs_vport_receive+0x222/0x390 netdev_frame_hook+0x3e0/0x630 tun_get_user+0x2d0c/0x38e0 Fixed by calling check_header() in key_extract() before accessing the Ethernet header. Fixes: 217ac77a3c25 ("openvswitch: allow L3 netdev ports") Reported-by: AutonomousCodeSecurity@microsoft.com Reviewed-by: Eelco Chaudron Signed-off-by: Cen Zhang (Microsoft) Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/20260730222006.118652-1-blbllhy@gmail.com Signed-off-by: Paolo Abeni --- net/openvswitch/flow.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 66366982f604..46c1d66aad8c 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -889,8 +889,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key) * Ethernet header * @key: output flow key * - * The caller must ensure that skb->len >= ETH_HLEN. - * * Initializes @skb header fields as follows: * * - skb->mac_header: the L2 header. @@ -910,8 +908,6 @@ static int key_extract_l3l4(struct sk_buff *skb, struct sw_flow_key *key) */ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) { - struct ethhdr *eth; - /* Flags are always used as part of stats */ key->tp.flags = 0; @@ -926,6 +922,13 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) skb_reset_network_header(skb); key->eth.type = skb->protocol; } else { + struct ethhdr *eth; + int err; + + err = check_header(skb, ETH_HLEN); + if (unlikely(err)) + return err; + eth = eth_hdr(skb); ether_addr_copy(key->eth.src, eth->h_source); ether_addr_copy(key->eth.dst, eth->h_dest); From a347304b2ca1a5377d5bd2d8a72e4b4f12afe648 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sat, 1 Aug 2026 08:56:32 -0400 Subject: [PATCH 439/501] net/sched: cls_api: Always acquire rtnl_lock when destroying locked classifiers Another challenge with unlocked filters. There is a short window in tc_new_tfilter where a tcf_proto can be found and briefly referenced by a totally unrelated, unlocked classifier's request and cause a race. Feng created a poc which created this race with two threads, one creating a u32 filter and other a flower filter in the same chain/prio: 1. Both threads enter tc_new_tfilter, both find the chain empty, both drop filter_chain_lock 2. u32 finishes tcf_proto_create("u32") first, calls tcf_chain_tp_insert_unique() -> inserts u32_tp into the chain 3. flower finishes tcf_proto_create("flower") later, calls tcf_chain_tp_insert_unique() -> tcf_chain_tp_find() now sees u32_tp already there, takes a reference on it, destroys flower's own tp_new and returns u32_tp to the caller. Flower then hits the kind mismatch check (because it requested for kind "flower" but tp->ops->kind is "u32") and goes through the errout path which calls tcf_proto_put() on u32_tp. If the u32 thread has already gone through its own errout (its change() call failed on the PoC's empty options) and dropped its create and insert refs, flower's put is the last one and drops u32_tp's refcnt to zero. At this point tp->ops->destroy() runs in a context that never took rtnl_lock. When that happens, it might cause a UAF like the following (illustrated by the PoC): [ +0.000710] BUG: KASAN: slab-use-after-free in u32_init (net/sched/cls_u32.c:393) [ +0.000281] Read of size 8 at addr ffff888120022f00 by task poc_feng_xue/524 Call Trace: u32_init (net/sched/cls_u32.c:393) tc_new_tfilter (net/sched/cls_api.c:2378) Allocated by task 526: u32_init (net/sched/cls_u32.c:378) tc_new_tfilter (net/sched/cls_api.c:2378) Freed by task 522: kfree u32_destroy (net/sched/cls_u32.c:662) tcf_proto_destroy (net/sched/cls_api.c:446) tcf_proto_put (net/sched/cls_api.c:459) tc_new_tfilter (net/sched/cls_api.c:2459) Fix this by having tcf_proto_destroy() take rtnl_lock around tp->ops->destroy() for locked classifiers whenever rtnl is not held. To explain why I used a temp variable "not_lockless" I'd like to point to a semi-related note on rtnl_held vs TCF_PROTO_OPS_DOIT_UNLOCKED (adding here for future cleanup if deemed necessary): The rtnl_held parameter and the TCF_PROTO_OPS_DOIT_UNLOCKED flag are redundant sources of truth for whether rtnl_lock is held. Among the nine classifier destroy(..rtnl_held..) callbacks, only flower consults the rtnl_held parameter which it propagates to tc_setup_cb_destroy() and tc_setup_cb_call(). The other eight (u32, flow, bpf, cgroup, route, basic, fw, mall) ignore it entirely;-> those that call tc_setup_cb_destroy() (u32, bpf, mall) hardcode true always instead of forwarding the parameter. A future cleanup should remove the rtnl_held parameter from the destroy callback signature entirely and have callers rely solely on their knowledge whether they are running in an unlocked context. Fixes: 12db03b65c2b ("net: sched: extend proto ops to support unlocked classifiers") Reported-by: Feng Xue Tested-by: Victor Nogueira Signed-off-by: Jamal Hadi Salim Link: https://patch.msgid.link/20260801125632.360365-1-jhs@mojatatu.com Signed-off-by: Paolo Abeni --- net/sched/cls_api.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index fee4524adc98..4e6a2812a4f3 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -443,7 +443,22 @@ static void tcf_chain_put(struct tcf_chain *chain); static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held, bool sig_destroy, struct netlink_ext_ack *extack) { - tp->ops->destroy(tp, rtnl_held, extack); + /* A locked classifier's destroy callback (e.g. u32_destroy) uses + * rtnl_dereference() and mutates shared structures (e.g. the + * tc_u_common hash list) that are only safe under rtnl_lock. When an + * unlocked classifier's request (e.g. flower on ingress) loses the + * tcf_chain_tp_insert_unique() race and ends up dropping the last + * reference on a locked classifier's proto, destroy() would run + * without rtnl held. Take it here in that case. + */ + bool not_lockless = !rtnl_held && + !(tp->ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED); + + if (not_lockless) + rtnl_lock(); + tp->ops->destroy(tp, rtnl_held || not_lockless, extack); + if (not_lockless) + rtnl_unlock(); tcf_proto_count_usesw(tp, false); if (sig_destroy) tcf_proto_signal_destroyed(tp->chain, tp); From 9c1cc4a7f79275ef93746f6247685763b475bfb0 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 4 Aug 2026 09:57:00 -0400 Subject: [PATCH 440/501] selinux: check level category sets once at load time As reported by Jiri Vozar, commit 7edea6e8c8e8 ("selinux: beef up isvalid checks") introduces a new loop in mls_level_isvalid() that causes ~89-94% throughput regression in System V IPC message queue operations (msgsnd/msgrcv). Move the expensive part of the ebitmap checking to policy load time instead as the reporter suggested. Link: https://lore.kernel.org/selinux/CAMgFczCi2Z011dNf84Amc0Q-qnTt0+VUjWY+Y7zPyXdaH35Jvw@mail.gmail.com/ Fixes: 7edea6e8c8e8 ("selinux: beef up isvalid checks") Reported-by: Jiri Vozar Suggested-by: Jiri Vozar Signed-off-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/mls.c | 24 +++++++----------------- security/selinux/ss/policydb.c | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index 3cd36e2015fa..40c62600650e 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -160,9 +160,6 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l) { const char *name; const struct level_datum *levdatum; - struct ebitmap_node *node; - u32 bit; - int rc; if (!l->sens || l->sens > p->p_levels.nprim) return false; @@ -176,21 +173,14 @@ bool mls_level_isvalid(const struct policydb *p, const struct mls_level *l) return false; /* - * Validate that all bits set in l->cat are also be set in - * levdatum->level->cat and no bit in l->cat is larger than - * p->p_cats.nprim. + * l is valid iff every bit in l->cat is set in levdatum->level.cat + * and no bit in l->cat is larger than p->p_cats.nprim. + * policydb_index() has already verified that every bit set in + * levdatum->level.cat names a defined category, so containment is + * sufficient here. */ - rc = ebitmap_contains(&levdatum->level.cat, &l->cat, - p->p_cats.nprim); - if (!rc) - return false; - - ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) { - if (!sym_name(p, SYM_CATS, bit)) - return false; - } - - return true; + return ebitmap_contains(&levdatum->level.cat, &l->cat, + p->p_cats.nprim); } bool mls_range_isvalid(const struct policydb *p, const struct mls_range *r) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index d88713201be9..8a32666c0ba2 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -665,6 +665,23 @@ static int cat_index(void *key, void *datum, void *datap) return 0; } +static int sens_cat_index_check(void *key, void *datum, void *datap) +{ + struct policydb *p = datap; + struct level_datum *levdatum = datum; + struct ebitmap_node *node; + u32 bit; + + ebitmap_for_each_positive_bit(&levdatum->level.cat, node, bit) { + if (bit >= p->p_cats.nprim || !sym_name(p, SYM_CATS, bit)) { + pr_err("SELinux: sensitivity %s allows undefined category %u\n", + (const char *)key, bit + 1); + return -EINVAL; + } + } + return 0; +} + /* clang-format off */ static int (*const index_f[SYM_NUM])(void *key, void *datum, void *datap) = { common_index, @@ -788,6 +805,12 @@ static int policydb_index(struct policydb *p) } } + if (p->mls_enabled) { + rc = hashtab_map(&p->p_levels.table, sens_cat_index_check, p); + if (rc) + goto out; + } + rc = 0; out: return rc; From 5ff232d31106f45ac87c3b64e1d35a0667777797 Mon Sep 17 00:00:00 2001 From: Lincoln Wallace Date: Mon, 3 Aug 2026 10:50:21 -0300 Subject: [PATCH 441/501] ima: fix out-of-bounds read in xattr_verify() The digest-length check in xattr_verify() mixes int and size_t: if (xattr_len - sizeof(xattr_value->type) - hash_start >= iint->ima_hash->length) sizeof() yields size_t, so the usual arithmetic conversions promote the whole left-hand side to unsigned 64-bit before the subtraction runs. For a truncated xattr this underflows instead of going negative: a 1-byte IMA_XATTR_DIGEST_NG xattr (xattr_len == 1, hash_start == 1) turns "1 - 1 - 1" into SIZE_MAX, which is trivially >= ima_hash->length. The check then passes and the following memcmp() reads iint->ima_hash->length bytes starting past the end of the buffer vfs_getxattr_alloc() allocated for it. Nothing upstream clamps xattr_len back into a safe range first: ima_get_hash_algo() only special-cases xattr_len < 2 to pick a default algorithm, and evm_verifyxattr() returns INTEGRITY_UNKNOWN rather than failing when no HMAC key is loaded, so a truncated security.ima value reaches the length check as-is. Rewrite the comparison so every operand stays a signed int and no implicit conversion to size_t can occur. Fixes: 3ea7a56067e6 ("ima: provide hash algo info in the xattr") Cc: stable@vger.kernel.org Signed-off-by: Lincoln Wallace Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 18d0d9154317..ced2e131b061 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -274,8 +274,13 @@ static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint, } else { set_bit(IMA_DIGSIG, &iint->atomic_flags); } - if (xattr_len - sizeof(xattr_value->type) - hash_start >= - iint->ima_hash->length) + /* + * Use addition, not subtraction: sizeof() forces unsigned + * math and a short xattr_len would wrap around, bypassing + * this bounds check. + */ + if (xattr_len >= (int)sizeof(xattr_value->type) + hash_start + + (int)iint->ima_hash->length) /* * xattr length may be longer. md5 hash in previous * version occupied 20 bytes in xattr, instead of 16 From b80bed5c871a80151351342c065579405ce77145 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 27 Jul 2026 20:39:41 -0400 Subject: [PATCH 442/501] ima: Instantiate file_truncate and path_truncate hooks Instantiate the file_truncate and path_truncate LSM hooks to reset the action cache flags (IMA_DONE_MASK) as soon as truncation is requested, so the file, based on policy, is re-collected, re-measured, re-audited, and re-appraised on next access. Tested-by: Frederick Lawler Cc: stable@vger.kernel.org Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_main.c | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 5cea53fc36df..ff52becc3031 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -687,6 +687,43 @@ static int ima_file_check(struct file *file, int mask) MAY_APPEND), FILE_CHECK, 0, false); } +/* + * ima_reset_action_flags - invalidate action flags after a content change + * @inode: inode of the file whose content is about to be truncated + * + * Clear IMA_DONE_MASK so the file is re-collected, re-measured, + * re-audited, and re-appraised on next access. + */ +static void ima_reset_action_flags(struct inode *inode) +{ + struct ima_iint_cache *iint; + + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) + return; + + iint = ima_iint_find(inode); + if (!iint) + return; + + mutex_lock(&iint->mutex); + iint->flags &= ~IMA_DONE_MASK; + iint->measured_pcrs = 0; + mutex_unlock(&iint->mutex); + return; +} + +static int ima_path_truncate(const struct path *path) +{ + ima_reset_action_flags(path->dentry->d_inode); + return 0; +} + +static int ima_file_truncate(struct file *file) +{ + ima_reset_action_flags(file_inode(file)); + return 0; +} + static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf, size_t buf_size) { @@ -1300,11 +1337,13 @@ static struct security_hook_list ima_hooks[] __ro_after_init = { LSM_HOOK_INIT(file_release, ima_file_free), LSM_HOOK_INIT(mmap_file, ima_file_mmap), LSM_HOOK_INIT(file_mprotect, ima_file_mprotect), + LSM_HOOK_INIT(file_truncate, ima_file_truncate), LSM_HOOK_INIT(kernel_load_data, ima_load_data), LSM_HOOK_INIT(kernel_post_load_data, ima_post_load_data), LSM_HOOK_INIT(kernel_read_file, ima_read_file), LSM_HOOK_INIT(kernel_post_read_file, ima_post_read_file), LSM_HOOK_INIT(path_post_mknod, ima_post_path_mknod), + LSM_HOOK_INIT(path_truncate, ima_path_truncate), #ifdef CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS LSM_HOOK_INIT(key_post_create_or_update, ima_post_key_create_or_update), #endif From 0e243671bc7b8eaf00f83dd2f4367436dc0cff98 Mon Sep 17 00:00:00 2001 From: Kyle Zeng Date: Mon, 3 Aug 2026 12:27:57 +0000 Subject: [PATCH 443/501] ipv6: prevent in6_dev_get() from resurrecting inet6_dev in6_dev_get() reads dev->ip6_ptr under RCU and then unconditionally increments its refcount. Device teardown can clear the pointer and drop the last reference between these operations. The increment then resurrects an object whose RCU free has already been queued, so callers can use it after it is freed. Use refcount_inc_not_zero() and return NULL when the object has already reached zero. RCU keeps the memory accessible through the attempted reference acquisition, and a successful increment pins the object for the caller. An independent run on the exact unpatched 6f5156d7a31a (v7.2-rc3) kernel reproduced the invalid reference acquisition as UID 1000: refcount_t: addition on 0; use-after-free. ip6_mc_source+0xef4/0x17e0 It was followed by the corresponding reference underflow in ip6_mc_source(). The supplied trace from the same unpatched revision additionally shows the access after the RCU read-side section ends: BUG: KASAN: slab-use-after-free in mutex_lock+0x76/0xe0 Write of size 8 at addr ffff888015b50240 by task poc/1219 Bug found and triaged by OpenAI Security Research and validated by Trail of Bits. Fixes: 8814c4b53381 ("[IPV6] ADDRCONF: Convert addrconf_lock to RCU.") Cc: stable@vger.kernel.org Signed-off-by: Kyle Zeng Co-developed-by: David Lee Signed-off-by: David Lee Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/20260803122758.666112-1-david.lee@trailofbits.com Signed-off-by: Jakub Kicinski --- include/net/addrconf.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/net/addrconf.h b/include/net/addrconf.h index 8ced27a8229b..e6764245995f 100644 --- a/include/net/addrconf.h +++ b/include/net/addrconf.h @@ -405,8 +405,8 @@ static inline struct inet6_dev *in6_dev_get(const struct net_device *dev) rcu_read_lock(); idev = rcu_dereference(dev->ip6_ptr); - if (idev) - refcount_inc(&idev->refcnt); + if (idev && !refcount_inc_not_zero(&idev->refcnt)) + idev = NULL; rcu_read_unlock(); return idev; } From 6e3abef2a27e7402a94111c9eff85d887e64a309 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Tue, 4 Aug 2026 21:46:35 +0200 Subject: [PATCH 444/501] selftests/ftrace: refactor eprobes test to fix argument checks The add/remove eprobe test installs an eprobe for the openat syscall and runs ls. It checks the filenames that were opened by ls against a whitelist and a blacklist. Commit 206b25c09080 ("tracing: eprobe: read the complete FILTER_PTR_STRING pointer") fixed access to some string fields in eprobes. This triggers test failures as the blacklist does not allow relative paths for the openat parameters. What makes this test unstable is the fact that the openat calls vary a lot between different systems. Refactor the test to make it more robust. "cd " will issue a chdir syscall with the target directory as parameter. Set an eprobe on the sys_enter_chdir event and filter for the exact directory name. Allow (fault) as fallback. Link: https://lore.kernel.org/all/20260804194705.760893-1-martin@kaiser.cx/ Fixes: 206b25c09080 ("tracing: eprobe: read the complete FILTER_PTR_STRING pointer") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202607151010.b68428e1-lkp@intel.com Signed-off-by: Martin Kaiser Signed-off-by: Masami Hiramatsu (Google) --- .../ftrace/test.d/dynevent/add_remove_eprobe.tc | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc index c300eb020262..e2322693d0c3 100644 --- a/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc +++ b/tools/testing/selftests/ftrace/test.d/dynevent/add_remove_eprobe.tc @@ -1,16 +1,16 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: Generic dynamic event - add/remove eprobe events -# requires: dynamic_events events/syscalls/sys_enter_openat ". []":README +# requires: dynamic_events events/syscalls/sys_enter_chdir ". []":README echo 0 > events/enable clear_dynamic_events SYSTEM="syscalls" -EVENT="sys_enter_openat" +EVENT="sys_enter_chdir" FIELD="filename" -EPROBE="eprobe_open" +EPROBE="eprobe_chdir" OPTIONS="file=+0(\$filename):ustring" echo "e:$EPROBE $SYSTEM/$EVENT $OPTIONS" >> dynamic_events @@ -18,20 +18,14 @@ grep -q "$EPROBE" dynamic_events test -d events/eprobes/$EPROBE echo 1 > events/eprobes/$EPROBE/enable -ls +cd /sys/kernel/tracing echo 0 > events/eprobes/$EPROBE/enable -content=`grep '^ *ls-' trace | grep 'file='` -nocontent=`grep '^ *ls-' trace | grep 'file=' | grep -v -e '"/' -e '"."' -e '(fault)' ` || true - +content=`grep -e 'file="/sys/kernel/tracing"\|(fault)' trace` if [ -z "$content" ]; then exit_fail fi -if [ ! -z "$nocontent" ]; then - exit_fail -fi - echo "-:$EPROBE" >> dynamic_events ! grep -q "$EPROBE" dynamic_events From 06232cb44bc8e81adc2f1d40a01bed830b607ea2 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Mon, 3 Aug 2026 11:51:56 +0200 Subject: [PATCH 445/501] net: stmmac: resume PHY before hardware setup when opening the interface Since the referenced commit, changing the MTU on a running interface no longer disconnects and reconnects the PHY; __stmmac_release() merely stops phylink, which also suspends the PHY (BMCR power-down) when WoL is not enabled. __stmmac_open() then performs the DMA software reset in stmmac_hw_setup() before phylink_start() resumes the PHY again. IEEE 802.3 22.2.4.1.5 allows a PHY to stop its receive clock while powered down, and stmmac requires a running receive clock for the DMA software reset to complete (the phylink config sets mac_requires_rxc). On such setups, e.g. the RK3566-based Home Assistant Green with an RTL8211F-VD PHY in RGMII mode, any runtime MTU change now times out and leaves the interface dead: rk_gmac-dwmac fe010000.ethernet end0: Failed to reset the dma rk_gmac-dwmac fe010000.ethernet end0: stmmac_hw_setup: DMA engine initialization failed rk_gmac-dwmac fe010000.ethernet end0: __stmmac_open: Hw setup failed rk_gmac-dwmac fe010000.ethernet end0: failed reopening the interface after MTU change In the field this is triggered by NetworkManager applying an MTU while activating the connection, breaking networking entirely. The same regression has also been reported on i.MX8MP and reproduced on SoCFPGA based systems. Resume the PHY in __stmmac_open() before the hardware setup, making it the counterpart of the phylink_stop() in __stmmac_release(), like stmmac_resume() already does for the same reason. phylink_start() also resumes the PHY, but only after stmmac_hw_setup(), and it cannot be moved before the hardware setup since it may bring the link up immediately from a workqueue, racing with the initialization (see the comment in stmmac_resume()). For the regular ndo_open path the PHY has just been attached and is not suspended, in which case phylink_prepare_resume() does nothing. Fixes: db299a0c09e9 ("net: stmmac: move PHY handling out of __stmmac_open()/release()") Link: https://github.com/home-assistant/operating-system/issues/4858 Tested-by: Alexander Stein Signed-off-by: Stefan Agner Tested-by: Maxime Chevallier Reviewed-by: Maxime Chevallier Link: https://patch.msgid.link/20260803095156.132827-1-stefan@agner.ch Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 97b28cfa14c2..a71f0df26378 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -4134,6 +4134,15 @@ static int __stmmac_open(struct net_device *dev, dma_conf->tx_queue[i].tbs = priv->dma_conf.tx_queue[i].tbs; memcpy(&priv->dma_conf, dma_conf, sizeof(*dma_conf)); + /* The PHY is suspended when the interface is reopened without + * disconnecting the PHY, e.g. on MTU change. IEEE 802.3 allows PHYs + * to stop their receive clock while powered down, but the DMA + * software reset in stmmac_hw_setup() requires a running receive + * clock, and phylink_start() below resumes the PHY only after the + * hardware setup. Resume a suspended PHY here first. + */ + phylink_prepare_resume(priv->phylink); + stmmac_reset_queues_param(priv); ret = stmmac_hw_setup(dev); From 452636ea5410a96e02ebaaf80b21e3620b98e0dd Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Sun, 2 Aug 2026 23:46:00 +0800 Subject: [PATCH 446/501] net: atlantic: free stranded TX buffers on ring deinit aq_vec_deinit() drains the TX rings with a single aq_ring_tx_clean() call, which frees at most AQ_CFG_TX_CLEAN_BUDGET (256) descriptors and stops at hw_head, which no longer moves once aq_vec_stop() has stopped the hardware and NAPI. Completed descriptors beyond the budget and everything still posted in [hw_head, sw_tail) keep their skb or xdp_frame when the interface goes down: aq_vec_ring_free() then frees the buffer ring and the references are lost for good. Today this is a silent memory leak on every interface down under TX/XDP_TX load. With the conversion of the RX path to page_pool posted for net-next it becomes much more visible: XDP_TX frames carry fragment references on the RX ring's page_pool, so a single stranded frame keeps the pool's inflight count above zero forever. page_pool_destroy() then never completes, the pool is leaked together with its pages, and "page_pool_release_retry() stalled pool shutdown" is warned every 60 seconds from that point on, on every ifdown, XDP detach or ring resize under XDP_TX load. Bring back aq_ring_tx_deinit() as it was before the removal and use it for teardown again, with one extension: TX rings can hold xdp_frames nowadays, so release those too. They are returned with xdp_return_frame() since this runs in process context. Fixes: eb36bedf28be ("net: aquantia: remove function aq_ring_tx_deinit") Cc: stable@vger.kernel.org # v4.11+ Reviewed-by: Sukhdeep Singh Signed-off-by: Yangyu Chen Acked-by: Mina Almasry Link: https://patch.msgid.link/tencent_EEDC35FAF2750A3A6A0B39BAE0E2C484860A@qq.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/aquantia/atlantic/aq_ring.c | 29 +++++++++++++++++++ .../net/ethernet/aquantia/atlantic/aq_ring.h | 1 + .../net/ethernet/aquantia/atlantic/aq_vec.c | 2 +- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 8ff07de2bd52..81685a4dc5a6 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -360,6 +360,35 @@ bool aq_ring_tx_clean(struct aq_ring_s *self) return !!budget; } +void aq_ring_tx_deinit(struct aq_ring_s *self) +{ + if (!self) + return; + + for (; self->sw_head != self->sw_tail; + self->sw_head = aq_ring_next_dx(self, self->sw_head)) { + struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; + struct device *ndev = aq_nic_get_dev(self->aq_nic); + + if (buff->is_mapped) { + if (buff->is_sop) { + dma_unmap_single(ndev, buff->pa, buff->len, + DMA_TO_DEVICE); + } else { + dma_unmap_page(ndev, buff->pa, buff->len, + DMA_TO_DEVICE); + } + } + + if (buff->is_eop) { + if (buff->skb) + dev_kfree_skb_any(buff->skb); + else if (buff->xdpf) + xdp_return_frame(buff->xdpf); + } + } +} + static void aq_rx_checksum(struct aq_ring_s *self, struct aq_ring_buff_s *buff, struct sk_buff *skb) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index a70b880ada67..6431cc62962f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h @@ -202,6 +202,7 @@ void aq_ring_update_queue_state(struct aq_ring_s *ring); void aq_ring_queue_wake(struct aq_ring_s *ring); void aq_ring_queue_stop(struct aq_ring_s *ring); bool aq_ring_tx_clean(struct aq_ring_s *self); +void aq_ring_tx_deinit(struct aq_ring_s *self); int aq_xdp_xmit(struct net_device *dev, int num_frames, struct xdp_frame **frames, u32 flags); int aq_ring_rx_clean(struct aq_ring_s *self, diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index 2f9033ceed8c..05814fea0f5f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c @@ -275,7 +275,7 @@ void aq_vec_deinit(struct aq_vec_s *self) for (i = 0U; self->tx_rings > i; ++i) { ring = self->ring[i]; - aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]); + aq_ring_tx_deinit(&ring[AQ_VEC_TX_ID]); aq_ring_rx_deinit(&ring[AQ_VEC_RX_ID]); } From e8e7471ef686b6c002218fee9671cc61992ae01a Mon Sep 17 00:00:00 2001 From: Yangyu Chen Date: Sun, 2 Aug 2026 23:46:38 +0800 Subject: [PATCH 447/501] net: atlantic: free RX pages of consumed but not refilled buffers aq_ring_rx_deinit() only walks [sw_head, sw_tail), the region posted to hardware. Since the page reuse strategy was added, a cleaned RX buffer keeps its page (and its DMA mapping) in the ring for reuse, and refill is batched: aq_ring_rx_fill() returns early until AQ_CFG_RX_REFILL_THRES slots are free. Slots that were consumed but not yet reposted therefore sit in the complementary [sw_tail, sw_head) gap with a live page, and the deinit walk never visits them: up to a refill batch worth of pages and DMA mappings leak on every interface down. Walk the whole ring instead and release whatever is still there. Also bail out if the buffer ring is already gone: a partial aq_ptp_ring_alloc() failure frees the ring but leaves aq_nic set, so aq_ptp_ring_deinit() still gets here on the unwind path. Cc: stable@vger.kernel.org # v5.2+ Fixes: 46f4c29d9de6 ("net: aquantia: optimize rx performance by page reuse strategy") Reviewed-by: Sukhdeep Singh Signed-off-by: Yangyu Chen Acked-by: Mina Almasry Link: https://patch.msgid.link/tencent_607CBA8237DA438E36B844318B21538DE008@qq.com Signed-off-by: Jakub Kicinski --- .../net/ethernet/aquantia/atlantic/aq_ring.c | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 81685a4dc5a6..e1193c6719d9 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c @@ -950,15 +950,29 @@ int aq_ring_rx_fill(struct aq_ring_s *self) void aq_ring_rx_deinit(struct aq_ring_s *self) { - if (!self) + unsigned int i; + + if (!self || !self->buff_ring) return; - for (; self->sw_head != self->sw_tail; - self->sw_head = aq_ring_next_dx(self, self->sw_head)) { - struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head]; + /* Release every page still owned by the ring. + * + * Walking [sw_head, sw_tail) is not enough: refill is batched + * (aq_ring_rx_fill() waits for AQ_CFG_RX_REFILL_THRES free slots), + * so slots that were cleaned but not yet reposted accumulate in the + * [sw_tail, sw_head) gap, and they keep their page for reuse. Walk + * the whole ring and release whatever is left. + */ + for (i = 0; i < self->size; i++) { + struct aq_ring_buff_s *buff = &self->buff_ring[i]; + + if (!buff->rxdata.page) + continue; aq_free_rxpage(&buff->rxdata, aq_nic_get_dev(self->aq_nic)); } + + self->sw_head = self->sw_tail; } void aq_ring_free(struct aq_ring_s *self) From 2cbd8a4e5e09aa232a1f8d56ce3d070b18ab2b10 Mon Sep 17 00:00:00 2001 From: Alok Tiwari Date: Sat, 1 Aug 2026 03:09:20 -0700 Subject: [PATCH 448/501] bnge: use int for bnge_fix_rings_count() return value bnge_fix_rings_count() returns 0 on success or a negative errno on failure However, bnge_adjust_rings() stores its return value in a u16 variable, causing negative error codes such as -ENOMEM to be converted to a large positive value. Use an int for the return code variable so that error values are preserved and propagated correctly. Fixes: 627c67f038d2 ("bng_en: Add resource management support") Signed-off-by: Alok Tiwari Reviewed-by: Bhargava Marreddy Link: https://patch.msgid.link/20260801100923.1498570-1-alok.a.tiwari@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_resc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c index 0e94f092813e..4711dd4945ff 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_resc.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_resc.c @@ -163,7 +163,8 @@ static int bnge_adjust_rings(struct bnge_dev *bd, u16 *rx, u16 tx_chunks = bnge_num_tx_to_cp(bd, *tx); if (tx_chunks != *tx) { - u16 tx_saved = tx_chunks, rc; + u16 tx_saved = tx_chunks; + int rc; rc = bnge_fix_rings_count(rx, &tx_chunks, max_nq, sh); if (rc) From 1505b2cb6ae1c7e8ac0c6e4590a204ffc3ab2b24 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Fri, 31 Jul 2026 11:27:11 +0200 Subject: [PATCH 449/501] usbnet: cap max_mtu for drivers without bind callback usbnet_probe() initializes max_mtu to ETH_MAX_MTU and only caps it inside the if (info->bind) block. Drivers without a bind callback never enter this block, so max_mtu stays at ETH_MAX_MTU. QEMU's usb-net device (0x0525/0xa4a2) is claimed by the cdc_subset driver which has no bind callback. The guest accepts any MTU from DHCP (e.g. 65520 from passt), leading to TCP segments that exceed the device's 2048-byte receive buffer and are silently dropped. Initialize max_mtu to net->mtu at probe time and update it inside the bind block. Fixes: f77f0aee4da4 ("net: use core MTU range checking in USB NIC drivers") Cc: jarod@redhat.com Cc: stable@vger.kernel.org Link: https://gitlab.com/qemu-project/qemu/-/issues/3268 Link: https://bugs.passt.top/show_bug.cgi?id=189 Signed-off-by: Laurent Vivier Link: https://patch.msgid.link/20260731092711.857684-1-lvivier@redhat.com Signed-off-by: Jakub Kicinski --- drivers/net/usb/usbnet.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 25518635b7b7..a19ecf718f36 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -1794,7 +1794,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod) */ dev->hard_mtu = net->mtu + net->hard_header_len; net->min_mtu = 0; - net->max_mtu = ETH_MAX_MTU; + net->max_mtu = net->mtu; net->netdev_ops = &usbnet_netdev_ops; net->watchdog_timeo = TX_TIMEOUT_JIFFIES; @@ -1804,6 +1804,7 @@ usbnet_probe(struct usb_interface *udev, const struct usb_device_id *prod) // allow device-specific bind/init procedures // NOTE net->name still not usable ... if (info->bind) { + net->max_mtu = ETH_MAX_MTU; status = info->bind(dev, udev); if (status < 0) goto out1; From 5d1c224dd914579524a183a514c12b95095d12ce Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 08:29:36 +0800 Subject: [PATCH 450/501] net: openvswitch: reallocate update replies for mismatched IDs ovs_flow_cmd_new() preallocates the optional reply skb before it takes ovs_mutex and before it knows which existing flow will be updated. That is normally fine because the skb is sized from the request flow identifier. That identifier also becomes the inserted flow's identifier. For updates, however, a request with a UFID may miss the UFID lookup and then fall back to the flow key lookup. That lookup can legitimately find an existing key-identified flow. UFIDs are optional and the flow key is the primary identifier. For echoed replies, ovs_flow_cmd_fill_info() writes the matched flow's identifier, not the request identifier used for the preallocation. A short request UFID can therefore leave too little room for the key identifier. The fill can then fail with -EMSGSIZE and hit the BUG_ON(error < 0) in the update path. Once the update target has been resolved, reallocate the reply skb if the matched flow needs a larger reply than the request identifier allowed. Do this before replacing the actions so the request can still fail cleanly if the rare extra allocation fails. Fixes: 74ed7ab9264c ("openvswitch: Add support for unique flow IDs.") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Reviewed-by: Ilya Maximets Link: https://patch.msgid.link/f7bbd3c30ce81a39156e226b3872d73abed21d2f.1785644623.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/openvswitch/datapath.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index eaf332b156d7..ae69b2cabab9 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -1113,9 +1113,8 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) error = -EEXIST; goto err_unlock_ovs; } - /* The flow identifier has to be the same for flow updates. - * Look for any overlapping flow. - */ + + /* Look for any overlapping flow. */ if (unlikely(!ovs_flow_cmp(flow, &match))) { if (ovs_identifier_is_key(&flow->id)) flow = ovs_flow_tbl_lookup_exact(&dp->table, @@ -1127,6 +1126,30 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info) goto err_unlock_ovs; } } + + if (unlikely(reply)) { + size_t cur, req; + + cur = ovs_flow_cmd_msg_size(acts, &new_flow->id, + ufid_flags); + req = ovs_flow_cmd_msg_size(acts, &flow->id, + ufid_flags); + if (cur < req) { + struct sk_buff *resized; + + resized = ovs_flow_cmd_alloc_info(acts, + &flow->id, + info, false, + ufid_flags); + if (IS_ERR(resized)) { + error = PTR_ERR(resized); + goto err_unlock_ovs; + } + kfree_skb(reply); + reply = resized; + } + } + /* Update actions. */ old_acts = ovsl_dereference(flow->sf_acts); rcu_assign_pointer(flow->sf_acts, acts); From e7386770be1bf810bcd6af39d1e4bfeab3408430 Mon Sep 17 00:00:00 2001 From: Bobby Eshleman Date: Mon, 3 Aug 2026 16:47:29 -0700 Subject: [PATCH 451/501] net/mlx5e: fix BQL reset on SQ re-activation mlx5e_queue_start() deactivates and re-activates all channels but closes only the queue being restarted. mlx5e_activate_txqsq() then unconditionally calls netdev_tx_reset_queue(), zeroing the BQL counters of channels that kept their in-flight TX WQEs. The next completion then over-charges and trips the BUG_ON() in dql_completed(): kernel BUG at lib/dynamic_queue_limits.c:99! RIP: 0010:dql_completed+0x23d/0x280 Call Trace: mlx5e_poll_tx_cq+0x668/0xa60 mlx5e_napi_poll+0x5b/0x7b0 net_rx_action+0x15a/0x580 Reset BQL only when the SQ has no bytes in flight (sq->cc == sq->pc). In the case that reset is skipped, the outstanding WQEs will eventually complete and rebalance the dql. The dql->limit is carried across the reset. Fixes: b2588ea40ec9 ("net/mlx5e: Implement queue mgmt ops and single channel swap") Signed-off-by: Bobby Eshleman Reviewed-by: Tariq Toukan Link: https://patch.msgid.link/20260803-mlx5-bql-v3-1-a30d4c66fe1d@meta.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 7d47a1da8b6b..f0407a850ea8 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -1939,8 +1939,10 @@ int mlx5e_open_txqsq(struct mlx5e_channel *c, u32 tisn, int txq_ix, void mlx5e_activate_txqsq(struct mlx5e_txqsq *sq) { sq->txq = netdev_get_tx_queue(sq->netdev, sq->txq_ix); + /* Reset BQL only when the SQ has no bytes in flight. */ + if (sq->cc == sq->pc) + netdev_tx_reset_queue(sq->txq); set_bit(MLX5E_SQ_STATE_ENABLED, &sq->state); - netdev_tx_reset_queue(sq->txq); netif_tx_start_queue(sq->txq); netif_queue_set_napi(sq->netdev, sq->txq_ix, NETDEV_QUEUE_TYPE_TX, sq->cq.napi); } From 1d0fc6c7ea49994b8ff50d02979d1e4207ec6c4f Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:33 -0700 Subject: [PATCH 452/501] bnxt_en: Move RSS table fill outside __bnxt_hwrm_vnic_set_rss() This is a refactor patch with no change in behavior. The caller will now fill the RSS table before calling __bnxt_hwrm_vnic_set_rss(). In the next patch, we'll add code to determine the default ring for the VNIC when we fill the RSS table. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-2-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 004ef99f4530..4599b6e0d219 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6706,6 +6706,9 @@ static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG); u16 i, j; + if (!vnic->rss_table) + return; + /* Fill the RSS indirection table with ring group ids */ for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) { if (!no_rss) @@ -6745,13 +6748,8 @@ static void __bnxt_hwrm_vnic_set_rss(struct bnxt *bp, struct hwrm_vnic_rss_cfg_input *req, struct bnxt_vnic_info *vnic) { - if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { - bnxt_fill_hw_rss_tbl_p5(bp, vnic); - if (bp->flags & BNXT_FLAG_CHIP_P7) - req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT; - } else { - bnxt_fill_hw_rss_tbl(bp, vnic); - } + if (bp->flags & BNXT_FLAG_CHIP_P7) + req->flags |= VNIC_RSS_CFG_REQ_FLAGS_IPSEC_HASH_TYPE_CFG_SUPPORT; if (bp->rss_hash_delta) { req->hash_type = cpu_to_le32(bp->rss_hash_delta); @@ -6803,6 +6801,7 @@ static int bnxt_hwrm_vnic_set_rss_p5(struct bnxt *bp, if (!set_rss) return hwrm_req_send(bp, req); + bnxt_fill_hw_rss_tbl_p5(bp, vnic); __bnxt_hwrm_vnic_set_rss(bp, req, vnic); ring_tbl_map = vnic->rss_table_dma_addr; nr_ctxs = bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings); @@ -10866,6 +10865,7 @@ static int __bnxt_setup_vnic(struct bnxt *bp, struct bnxt_vnic_info *vnic) } skip_rss_ctx: + bnxt_fill_hw_rss_tbl(bp, vnic); /* configure default vnic, ring grp */ rc = bnxt_hwrm_vnic_cfg(bp, vnic); if (rc) { From 53f01cd594e223aabb538d5288e60111523c96f2 Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:34 -0700 Subject: [PATCH 453/501] bnxt_en: Determine and store default RX ring in vnic structure Each VNIC has a default RX ring. The purpose of the default RX ring is to provide a destination for any packets that cannot be parsed by the RSS logic. Up until now, the default RX ring is always Ring 0. We neglected to take care of this default RX ring when adding the queue restart feature. If ring 0 (default ring) is re-started, it may now have a new FW ring ID after freeing the old one and allocating a new one. The VNIC now may have a stale default ring and it may generate an internal exception. This exception may appear in dmesg: FW reported unknown error type 10 The best way to resolve this issue is to use a more appropriate ring for the default ring instead of always ring 0. Ring 0 may not even be in the RSS table, especially on a new RSS context. This patch adds the logic to determine and store the proper default RX ring for a VNIC. For an RSS VNIC, the default ring is the lowest ring number in the RSS table. The next patch will add proper logic to update the VNIC if the default ring changes after queue restart. Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-3-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 33 +++++++++++++++-------- drivers/net/ethernet/broadcom/bnxt/bnxt.h | 1 + 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 4599b6e0d219..8b1f4e9fc780 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -6704,25 +6704,36 @@ int bnxt_get_nr_rss_ctxs(struct bnxt *bp, int rx_rings) static void bnxt_fill_hw_rss_tbl(struct bnxt *bp, struct bnxt_vnic_info *vnic) { bool no_rss = !(vnic->flags & BNXT_VNIC_RSS_FLAG); - u16 i, j; + u16 i, j, min_j = bp->rx_nr_rings - 1; if (!vnic->rss_table) - return; + goto skip_rss_tbl; /* Fill the RSS indirection table with ring group ids */ for (i = 0, j = 0; i < HW_HASH_INDEX_SIZE; i++) { if (!no_rss) j = bp->rss_indir_tbl[i]; + min_j = min(j, min_j); vnic->rss_table[i] = cpu_to_le16(vnic->fw_grp_ids[j]); } + +skip_rss_tbl: + if (vnic->rss_table && !no_rss) + vnic->default_rx_ring = min_j; + else if (vnic->flags & BNXT_VNIC_RFS_FLAG) + vnic->default_rx_ring = vnic->vnic_id - 1; + else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp)) + vnic->default_rx_ring = bp->rx_nr_rings - 1; + else + vnic->default_rx_ring = 0; } static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic) { + u16 tbl_size, i, min_j = bp->rx_nr_rings - 1; __le16 *ring_tbl = vnic->rss_table; struct bnxt_rx_ring_info *rxr; - u16 tbl_size, i; tbl_size = bnxt_get_rxfh_indir_size(bp->dev); @@ -6735,6 +6746,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, j = ethtool_rxfh_context_indir(vnic->rss_ctx)[i]; else j = bp->rss_indir_tbl[i]; + min_j = min(j, min_j); rxr = &bp->rx_ring[j]; ring_id = rxr->rx_ring_struct.fw_ring_id; @@ -6742,6 +6754,7 @@ static void bnxt_fill_hw_rss_tbl_p5(struct bnxt *bp, ring_id = bnxt_cp_ring_for_rx(bp, rxr); *ring_tbl++ = cpu_to_le16(ring_id); } + vnic->default_rx_ring = min_j; } static void @@ -6938,8 +6951,9 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic) return rc; if (bp->flags & BNXT_FLAG_CHIP_P5_PLUS) { - struct bnxt_rx_ring_info *rxr = &bp->rx_ring[0]; + struct bnxt_rx_ring_info *rxr; + rxr = &bp->rx_ring[vnic->default_rx_ring]; req->default_rx_ring_id = cpu_to_le16(rxr->rx_ring_struct.fw_ring_id); req->default_cmpl_ring_id = @@ -6972,13 +6986,7 @@ int bnxt_hwrm_vnic_cfg(struct bnxt *bp, struct bnxt_vnic_info *vnic) req->cos_rule = cpu_to_le16(0xffff); } - if (vnic->flags & BNXT_VNIC_RSS_FLAG) - ring = 0; - else if (vnic->flags & BNXT_VNIC_RFS_FLAG) - ring = vnic->vnic_id - 1; - else if ((vnic->vnic_id == 1) && BNXT_CHIP_TYPE_NITRO_A0(bp)) - ring = bp->rx_nr_rings - 1; - + ring = vnic->default_rx_ring; grp_idx = bp->rx_ring[ring].bnapi->index; req->dflt_ring_grp = cpu_to_le16(bp->grp_info[grp_idx].fw_grp_id); req->lb_rule = cpu_to_le16(0xffff); @@ -11171,6 +11179,9 @@ static int bnxt_setup_nitroa0_vnic(struct bnxt *bp) return rc; } + /* Setup the proper default RX ring */ + bnxt_fill_hw_rss_tbl(bp, vnic); + rc = bnxt_hwrm_vnic_cfg(bp, vnic); if (rc) { netdev_err(bp->dev, "Cannot allocate special vnic for NS2 A0: %x\n", diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.h b/drivers/net/ethernet/broadcom/bnxt/bnxt.h index 6335dfc14c98..44ea8fc7b766 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.h +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.h @@ -1334,6 +1334,7 @@ struct bnxt_vnic_info { #define BNXT_VNIC_RSSCTX_FLAG 0x40 struct ethtool_rxfh_context *rss_ctx; u32 vnic_id; + u16 default_rx_ring; }; struct bnxt_rss_ctx { From 0b137529a8997caf67190ca1d71ba8bbdb44fbfb Mon Sep 17 00:00:00 2001 From: Shravya KN Date: Fri, 31 Jul 2026 12:09:35 -0700 Subject: [PATCH 454/501] bnxt_en: Refresh VNIC default ring on queue restart if needed When a queue is restarted, refresh VNIC_CFG for all VNICs whose default RX ring is the restarted ring. This will eliminate this possible FW warning caused by a stale default ring in the VNIC: FW reported unknown error type 10 Fixes: 5ac066b7b062 ("bnxt_en: Fix queue start to update vnic RSS table") Reviewed-by: Kalesh AP Reviewed-by: Pavan Chebbi Signed-off-by: Shravya KN Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-4-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 8b1f4e9fc780..6deb8f348d07 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -11098,6 +11098,11 @@ static int bnxt_set_vnic_mru_p5(struct bnxt *bp, struct bnxt_vnic_info *vnic, vnic->vnic_id, rc); return rc; } + if (rxr_id == vnic->default_rx_ring) { + rc = bnxt_hwrm_vnic_cfg(bp, vnic); + if (rc) + return rc; + } } vnic->mru = mru; bnxt_hwrm_vnic_update(bp, vnic, From c3faf548a00f4c17100cc9204746975fa46a73b9 Mon Sep 17 00:00:00 2001 From: Michael Chan Date: Fri, 31 Jul 2026 12:09:36 -0700 Subject: [PATCH 455/501] bnxt_en: Disable EOP for TPA on all chips to prevent data corruption EOP (End of frame padding) on the AGG ring may cause overlapping of zero padding at the end of one segment with the next segment's data. If Relaxed Ordering (RO) is enabled, the zero padding may overwrite valid data in the next segment and corrupt the data. Older chips (P5 and older) do not automatically disable RO when EOP is enabled. On some ARM systems, data corruption was reported on 57508 (P5) chips with RO enabled. Always disable EOP on all chips on the AGG rings when TPA is enabled to fix the data corruption. Fixes: bfcd8d791ec1 ("bnxt_en: Add fast path logic for TPA on 57500 chips.") Reviewed-by: Pavan Chebbi Reviewed-by: Kalesh AP Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-5-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 6deb8f348d07..25099077fe4f 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -4611,11 +4611,14 @@ static void bnxt_init_one_rx_agg_ring_rxbd(struct bnxt *bp, type = ((u32)rxr->rx_page_size << RX_BD_LEN_SHIFT) | RX_BD_TYPE_RX_AGG_BD; - /* On P7, setting EOP will cause the chip to disable - * Relaxed Ordering (RO) for TPA data. Disable EOP for - * potentially higher performance with RO. + /* Disable EOP if TPA is enabled to prevent overlapping zero + * padding with the next segment's data. On P7_PLUS, EOP will + * automatically disable Relaxed Ordering (RO) to prevent + * potential data corruption (and may degrade performance). On + * older chips, RO will not be automatically disabled and may + * cause corruption. */ - if (BNXT_CHIP_P5_AND_MINUS(bp) || !(bp->flags & BNXT_FLAG_TPA)) + if (!(bp->flags & BNXT_FLAG_TPA)) type |= RX_BD_FLAGS_AGG_EOP; bnxt_init_rxbd_pages(ring, type); From 80eaf88efec33ac77ed7726d066c4f2f932cc329 Mon Sep 17 00:00:00 2001 From: Keegan Freyhof Date: Fri, 31 Jul 2026 12:09:37 -0700 Subject: [PATCH 456/501] bnxt_en: Fix PTP PPS setting bug The existing driver logic is always turning on PTP_CLK_REQ_PPS regardless of the "on" parameter passed to bnxt_ptp_enable(). During shutdown, PTP_CLK_REQ_PPS may be turned off and this bug will do the opposite and may trigger a PCIe PTM request TLP. On some systems this can trigger a PCIe AER. Fix it by properly configuring PTP_CLK_REQ_PPS based on the "on" parameter. Fixes: 9e518f25802c ("bnxt_en: 1PPS functions to configure TSIO pins") Reviewed-by: Pavan Chebbi Signed-off-by: Keegan Freyhof Signed-off-by: Michael Chan Link: https://patch.msgid.link/20260731190937.807270-6-michael.chan@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c index 5d41dc1bc782..b89293dfd2d1 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ptp.c @@ -495,12 +495,15 @@ static int bnxt_ptp_enable(struct ptp_clock_info *ptp_info, return rc; case PTP_CLK_REQ_PPS: /* Configure PHC PPS IN */ - rc = bnxt_ptp_cfg_pin(bp, 0, BNXT_PPS_PIN_PPS_IN); + pin_id = 0; + if (!on) + break; + rc = bnxt_ptp_cfg_pin(bp, pin_id, BNXT_PPS_PIN_PPS_IN); if (rc) return rc; rc = bnxt_ptp_cfg_event(bp, BNXT_PPS_EVENT_INTERNAL); if (!rc) - ptp->pps_info.pins[0].event = BNXT_PPS_EVENT_INTERNAL; + ptp->pps_info.pins[pin_id].event = BNXT_PPS_EVENT_INTERNAL; return rc; default: netdev_err(ptp->bp->dev, "Unrecognized PIN function\n"); From aa2e13ae8d3cbe2c15ef4f7e971b2de0832794aa Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Tue, 4 Aug 2026 10:55:14 +0800 Subject: [PATCH 457/501] sctp: fix addip_serial increment on ASCONF_ACK allocation failure In sctp_process_asconf(), when sctp_make_asconf_ack() fails to allocate the ASCONF_ACK chunk due to memory pressure, the code jumps to the done label where asoc->peer.addip_serial is unconditionally incremented. This leaves the peer's ASCONF (serial N) unacknowledged while the local endpoint now expects serial N+1. When the peer retransmits serial N, it falls into the serial < addip_serial + 1 branch , which attempts to look up a cached ACK for serial N. No cached ACK exists since the allocation failed, so the retransmission is silently discarded. The peer eventually times out and ABORTs the association. Move the addip_serial increment inside the if (asconf_ack) block so that the serial number is only advanced when the ASCONF_ACK is successfully created and cached. This way, on allocation failure, the serial number is unchanged and the peer's retransmitted ASCONF will be correctly re-processed. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Qing Luo Acked-by: Xin Long Link: https://patch.msgid.link/20260804025514.241767-1-l1138897701@163.com Signed-off-by: Jakub Kicinski --- net/sctp/sm_make_chunk.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0ae30c3c8913..e25612e9d082 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3336,12 +3336,11 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, goto done; } done: - asoc->peer.addip_serial++; - /* If we are sending a new ASCONF_ACK hold a reference to it in assoc * after freeing the reference to old asconf ack if any. */ if (asconf_ack) { + asoc->peer.addip_serial++; sctp_chunk_hold(asconf_ack); list_add_tail(&asconf_ack->transmitted_list, &asoc->asconf_ack_list); From 478a1c3abebfc717db0d1281a9cdd7befafee542 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Mon, 3 Aug 2026 17:37:08 -0700 Subject: [PATCH 458/501] mm: fix incorrect flush address in direct page table reclaim When zap_pte_range reclaims a page table, it does: pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); and this is unconditionally wrong: if this code executes, addr *always* points one past the end of the range covered by the table. The addr parameter is used to flush the TLB (really the paging-structure-cache) to drop references to the to-be-freed table, and any architecture that cares about the parameter will flush the wrong address. (But they'll still free the correct page). I think it's worth contemplating why the kernel works at all. If we hit the offending line of code, we will first clear the PMD entry (line 1954, zap_empty_pte_table), then we will issue pending flushes if force_flush is set (tlb_flush_mmu_tlbonly(tlb)), then we will skip the retry on line 1979 (phew!), and then we will do the offending pte_free_tlb call. *Or* we will clear the PMD entry immediately before pte_free_tlb (line 1983, zap_pte_table_if_empty). If we have any pending flushes (i.e. we actually zapped any last-level entries) at the time we clear the PMD entry, then the flush really ought to flush all references to the table (Linus certainly seems to think it will on all architectures [0]). The condition under which we have no accumulated flushes at the time of the clear is very complex (the whole zap_pte_range function has absurdly complex control flow). If we do hit the bad case, then we will end up clearing the PMD entry after the last time the range is flushed, and any CPU is free to cache a reference to the (empty) page table. If this happens due to an ordinary read or write, it would segfault, so it would be rare. But the cache could be speculatively filled as well. Then we'll flush the wrong address and then free and possibly reuse the table. On x86, even flushing the wrong address works on non-KPTI Intel systems because INVLPG flushes *all* paging-structure-caches, not just the ones for the target address. But INVPCID does not, and flush_tlb_one_user will use INVPCID if it's available. And then we're toast. AMD systems are more susceptible: we set the EFER.TCE bit, which makes even INVLPG only flush the target address. I think this might fix an issue in ripgrep reported here: https://github.com/BurntSushi/ripgrep/issues/3494 [0] https://lore.kernel.org/all/CA+55aFzBggoXtNXQeng5d_mRoDnaMBE5Y+URs+PHR67nUpMtaw@mail.gmail.com/T/#u Signed-off-by: Andy Lutomirski Fixes: 4c640eb4181c ("mm: move pte table reclaim code to memory.c") Cc: Liam Howlett Cc: Liam R. Howlett Cc: Lorenzo Stoakes Cc: Mike Rapoport Cc: Suren Baghdasaryan Cc: stable@vger.kernel.org Acked-by: Vlastimil Babka (SUSE) Acked-by: David Hildenbrand (Arm) Acked-by: Michal Hocko Acked-by: Qi Zheng Signed-off-by: Linus Torvalds --- mm/memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/memory.c b/mm/memory.c index ff338c2abe92..6b8280cfc1db 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -1981,7 +1981,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb, if (can_reclaim_pt) { if (direct_reclaim || zap_pte_table_if_empty(mm, pmd, start, &pmdval)) { - pte_free_tlb(tlb, pmd_pgtable(pmdval), addr); + pte_free_tlb(tlb, pmd_pgtable(pmdval), start); mm_dec_nr_ptes(mm); } } From a0ab2ba83e35159d81cec830a92e885ecf8139be Mon Sep 17 00:00:00 2001 From: Jiayuan Chen Date: Mon, 3 Aug 2026 14:17:38 +0800 Subject: [PATCH 459/501] tcp: fix TFO max_qlen accounting across reuseport migration A listener's TCP_FASTOPEN max_qlen stops being accurate and lets through far more pending Fast Open requests than it was configured for. This only shows up with SO_REUSEPORT listener migration, where closing a listener hands its still-pending TFO children over to a surviving one. fastopenq.qlen is charged in tcp_fastopen_create_child() when the child is created and uncharged in reqsk_fastopen_remove() when the handshake completes. The uncharge follows rsk_listener of the request the child points at, and inet_reqsk_clone() has repointed the child at a new request owned by the new listener, so the ++ and the -- land on two different sockets. The new listener's qlen drifts negative and its limit no longer binds. Charge the new listener during migration, like reqsk_queue_migrated() already does for queue->young and queue->qlen. Fixes: 54b92e841937 ("tcp: Migrate TCP_ESTABLISHED/TCP_SYN_RECV sockets in accept queues.") Signed-off-by: Jiayuan Chen Reviewed-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Link: https://patch.msgid.link/20260803061739.134737-1-jiayuan.chen@linux.dev Signed-off-by: Jakub Kicinski --- net/ipv4/inet_connection_sock.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 56902bba5483..6257459bcee2 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -943,11 +943,23 @@ static struct request_sock *inet_reqsk_clone(struct request_sock *req, nreq->rsk_listener = sk; - /* We need not acquire fastopenq->lock - * because the child socket is locked in inet_csk_listen_stop(). - */ - if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) + if (sk->sk_protocol == IPPROTO_TCP && tcp_rsk(nreq)->tfo_listener) { + struct fastopen_queue *fastopenq; + + /* reqsk_fastopen_remove() will uncharge nreq->rsk_listener, + * that is @sk, so charge it here. Unlike the listener + * being closed, @sk is live and needs its lock. + */ + fastopenq = &inet_csk(sk)->icsk_accept_queue.fastopenq; + spin_lock_bh(&fastopenq->lock); + fastopenq->qlen++; + spin_unlock_bh(&fastopenq->lock); + + /* We need not acquire fastopenq->lock + * because the child socket is locked in inet_csk_listen_stop(). + */ rcu_assign_pointer(tcp_sk(nreq->sk)->fastopen_rsk, nreq); + } return nreq; } From 8aecf0bbcc72605592134c917c222207d8f63ab0 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Aug 2026 09:33:28 +0000 Subject: [PATCH 460/501] netfilter: nf_flow_table: drop existing skb dst before skb_dst_set_noref() Incoming skbs passing through netfilter flowtable offload hooks (or XFRM offload path) might already carry a ref-counted dst_entry assigned during earlier RX or routing steps. Calling skb_dst_set_noref() when skb already holds a ref-counted dst overwrites skb->_skb_refdst, leaking the previous dst_entry reference count and triggering a DEBUG_NET_WARN_ON_ONCE assertion in skb_dst_check_unset(): WARNING: at skb_dst_check_unset include/linux/skbuff.h:1170 WARNING: at skb_dst_set_noref include/linux/skbuff.h:1234 WARNING: at nf_flow_offload_ip_hook+0xf6c/0x2b60 net/netfilter/nf_flow_table_ip.c:864 Drop any existing dst_entry reference with skb_dst_drop(skb) before setting the non-referenced flowtable destination. Fixes: 2a79fd3908ac ("netfilter: nf_flow_table: attach dst to skbs") Reported-by: syzbot+76d4e3a055aec3b007ec@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a71b141.9511d2ce.1fc5b9.033b.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Reviewed-by: Pablo Neira Ayuso Link: https://patch.msgid.link/20260804093328.1831847-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/netfilter/nf_flow_table_ip.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c index 0b78decce8a9..c9e332fafcb5 100644 --- a/net/netfilter/nf_flow_table_ip.c +++ b/net/netfilter/nf_flow_table_ip.c @@ -310,6 +310,7 @@ static unsigned int nf_flow_xmit_xfrm(struct sk_buff *skb, struct dst_entry *dst) { skb_orphan(skb); + skb_dst_drop(skb); skb_dst_set_noref(skb, dst); dst_output(state->net, state->sk, skb); return NF_STOLEN; @@ -861,6 +862,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb, return NF_DROP; } xmit.dest = neigh->ha; + skb_dst_drop(skb); skb_dst_set_noref(skb, &rt->dst); break; case FLOW_OFFLOAD_XMIT_DIRECT: @@ -1178,6 +1180,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb, return NF_DROP; } xmit.dest = neigh->ha; + skb_dst_drop(skb); skb_dst_set_noref(skb, &rt->dst); break; case FLOW_OFFLOAD_XMIT_DIRECT: From 5f26a690e8efa54315e4922368daf54e0b8f5515 Mon Sep 17 00:00:00 2001 From: Zihan Xi Date: Sun, 2 Aug 2026 09:23:34 +0000 Subject: [PATCH 461/501] mac802154: fix netdev use-after-free in beacon worker mac802154_beacon_worker() reads local->beacon_req under RCU and derives the sub-interface from the request, but then drops the RCU read lock and continues to use both sdata and the embedded wpan_dev. mac802154_stop_beacons_locked() cancels only pending beacon work, clears local->beacon_req and frees the request. A beacon worker that is already running can therefore continue after interface teardown and dereference the freed netdev private area. The scan worker already pins the netdev before leaving RCU. Apply the same lifetime rule to the beacon worker: take a netdev reference while the request is still protected by RCU, and release it on all paths that continue after the reference is acquired. Fixes: 3accf4762734 ("mac802154: Handle basic beaconing") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zihan Xi Reviewed-by: Miquel Raynal Link: https://patch.msgid.link/e9a3909c7a6281967961773ca841e860b8ecf40e.1785596603.git.zihanx@nebusec.ai Signed-off-by: Jakub Kicinski --- net/mac802154/scan.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac802154/scan.c b/net/mac802154/scan.c index 65089826ff59..005338f89b75 100644 --- a/net/mac802154/scan.c +++ b/net/mac802154/scan.c @@ -415,6 +415,7 @@ void mac802154_beacon_worker(struct work_struct *work) container_of(work, struct ieee802154_local, beacon_work.work); struct cfg802154_beacon_request *beacon_req; struct ieee802154_sub_if_data *sdata; + netdevice_tracker dev_tracker; struct wpan_dev *wpan_dev; u8 interval; int ret; @@ -427,12 +428,14 @@ void mac802154_beacon_worker(struct work_struct *work) } sdata = IEEE802154_WPAN_DEV_TO_SUB_IF(beacon_req->wpan_dev); + netdev_hold(sdata->dev, &dev_tracker, GFP_ATOMIC); /* Wait an arbitrary amount of time in case we cannot use the device */ if (local->suspended || !ieee802154_sdata_running(sdata)) { rcu_read_unlock(); queue_delayed_work(local->mac_wq, &local->beacon_work, msecs_to_jiffies(1000)); + netdev_put(sdata->dev, &dev_tracker); return; } @@ -450,6 +453,7 @@ void mac802154_beacon_worker(struct work_struct *work) if (interval < IEEE802154_ACTIVE_SCAN_DURATION) queue_delayed_work(local->mac_wq, &local->beacon_work, local->beacon_interval); + netdev_put(sdata->dev, &dev_tracker); } int mac802154_stop_beacons_locked(struct ieee802154_local *local, From afa58b7384913c8773d837acdb07b035690ec5d2 Mon Sep 17 00:00:00 2001 From: Henry Martin Date: Mon, 3 Aug 2026 12:36:18 +0800 Subject: [PATCH 462/501] net/ncsi: fix heap OOB read in NCSI_CMD_SEND_CMD payload length ncsi_send_cmd_nl() takes the number of bytes to copy from the attacker-controlled ncsi_pkt_hdr.length field of the in-band packet header, while the source buffer is the NCSI_ATTR_DATA netlink attribute whose readable size is nla_len() - sizeof(ncsi_pkt_hdr). The two length sources are never cross-checked: only nla_len() >= sizeof(struct ncsi_pkt_hdr) is enforced. With hdr->length set larger than the attribute payload (up to 65535 against at most 2032 readable bytes), ncsi_cmd_handler_oem() copies past the end of the netlink attribute buffer with unsafe_memcpy(), leaking up to ~64KB of kernel heap memory into the transmitted NCSI command packet. The destination skb is sized by the declared payload, so the write side does not overflow - this is a pure OOB read / information leak, reachable with CAP_NET_ADMIN on systems with a registered NCSI device (e.g. OpenBMC on Aspeed BMC SoCs, where NET_NCSI=y is standard). Reject commands whose declared payload extends past the end of the data attribute. The issue was found by the autokbug dynamic kernel fuzzer at Tencent Yunding Lab. Fixes: 9771b8ccdfa6 ("net/ncsi: Extend NC-SI Netlink interface to allow user space to send NC-SI command") Reported-by: Henry Martin Signed-off-by: Henry Martin Link: https://patch.msgid.link/20260803043618.3210301-1-bsdhenrymartin@gmail.com Signed-off-by: Jakub Kicinski --- net/ncsi/ncsi-netlink.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/ncsi/ncsi-netlink.c b/net/ncsi/ncsi-netlink.c index 2f872d064396..8cc538358f6a 100644 --- a/net/ncsi/ncsi-netlink.c +++ b/net/ncsi/ncsi-netlink.c @@ -461,6 +461,10 @@ static int ncsi_send_cmd_nl(struct sk_buff *msg, struct genl_info *info) nca.req_flags = NCSI_REQ_FLAG_NETLINK_DRIVEN; nca.info = info; nca.payload = ntohs(hdr->length); + if (nca.payload > len - sizeof(*hdr)) { + ret = -EINVAL; + goto out_netlink; + } nca.data = data + sizeof(*hdr); ret = ncsi_xmit_cmd(&nca); From f803c086399da277b5d0ff36a107d0f162751800 Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 14:12:33 +0800 Subject: [PATCH 463/501] ip6_tunnel: clear skb2->cb[] in ip6ip6_err() ip6ip6_err() clones an outer IPv6 ICMP error skb, pulls it to the quoted inner IPv6 packet, and then passes the clone to icmpv6_send(). The clone still carries the outer packet's inet6_skb_parm in skb->cb. If the outer packet had a Home Address Option, IP6CB(skb2)->dsthao remains non-zero after skb_pull(). icmpv6_send() later calls mip6_addr_swap(), which uses that stale dsthao offset against the quoted inner packet. A malformed inner destination-options header can then make the HAO lookup and address swap run past the end of the quoted packet and corrupt skb_shared_info. Clear skb2->cb[] before pulling the quoted inner IPv6 packet so the reply path does not reuse metadata left by the outer IPv6 stack. Fixes: e490d1d85cf5 ("[IPV6] IP6TUNNEL: Split out generic routine in ip6ip6_err().") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Reviewed-by: Ido Schimmel Link: https://patch.msgid.link/fe1a5e765fbca88d69391887f0ed26a19e3e4d39.1785736562.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/ipv6/ip6_tunnel.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index bf8e40af60b0..ebf83f090376 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -684,6 +684,9 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, if (!skb2) return 0; + /* Remove debris left by outer IPv6 stack. */ + memset(IP6CB(skb2), 0, sizeof(*IP6CB(skb2))); + skb_dst_drop(skb2); skb_pull(skb2, offset); skb_reset_network_header(skb2); From 3874892dd27d5387aa9a06f58d9060f18f351d24 Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Sun, 2 Aug 2026 15:46:12 -0700 Subject: [PATCH 464/501] net: tap: set skb->dev before parsing virtio net header in tap_get_user_xdp() The commit 4f61f133f354 ("net: tap: NULL pointer derefence in dev_parse_header_protocol when skb->dev is null") fixed a crash in tap_get_user() by assigning skb->dev before calling tun_vnet_hdr_to_skb(). This is required because virtio_net_hdr_to_skb() may invoke dev_parse_header_protocol(), which dereferences skb->dev. Without the assignment, a NULL pointer dereference can occur. However, tap_get_user_xdp() still parses the virtio-net header before assigning skb->dev. When the vhost TX path passes an XDP buffer containing a GSO virtio-net header but the protocol is set to zero on purpose, tun_vnet_hdr_to_skb() can reach dev_parse_header_protocol() while skb->dev is still NULL, resulting in a crash. Fix this by looking up the tap device and assigning skb->dev before calling tun_vnet_hdr_to_skb(), matching the ordering already used in tap_get_user(). Preserve the existing RCU read-side critical section across dev_queue_xmit(). Fixes: 924a9bc362a5 ("net: check if protocol extracted by virtio_net_hdr_set_proto is correct") Cc: stable@vger.kernel.org Signed-off-by: Dongli Zhang Reviewed-by: Willem de Bruijn Acked-by: Michael S. Tsirkin Link: https://patch.msgid.link/20260802224612.264563-1-dongli.zhang@oracle.com Signed-off-by: Jakub Kicinski --- drivers/net/tap.c | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/tap.c b/drivers/net/tap.c index fae115915c8e..5d2d34d24ce8 100644 --- a/drivers/net/tap.c +++ b/drivers/net/tap.c @@ -1074,10 +1074,21 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) skb_reset_mac_header(skb); skb->protocol = eth_hdr(skb)->h_proto; + rcu_read_lock(); + tap = rcu_dereference(q->tap); + if (!tap) { + kfree_skb(skb); + rcu_read_unlock(); + return 0; + } + skb->dev = tap->dev; + if (vnet_hdr_len) { err = tun_vnet_hdr_to_skb(q->flags, skb, gso); - if (err) + if (err) { + rcu_read_unlock(); goto err_kfree; + } } /* Move network header to the right position for VLAN tagged packets */ @@ -1085,15 +1096,8 @@ static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp) vlan_get_protocol_and_depth(skb, skb->protocol, &depth) != 0) skb_set_network_header(skb, depth); - rcu_read_lock(); - tap = rcu_dereference(q->tap); - if (tap) { - skb->dev = tap->dev; - skb_probe_transport_header(skb); - dev_queue_xmit(skb); - } else { - kfree_skb(skb); - } + skb_probe_transport_header(skb); + dev_queue_xmit(skb); rcu_read_unlock(); return 0; From 8ae344eb540af3f457179b52bc6061416752485c Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Fri, 31 Jul 2026 22:19:06 +0800 Subject: [PATCH 465/501] net: prestera: validate firmware header length prestera_fw_hdr_parse() reads the firmware header before checking that the firmware image contains that header. Reject images shorter than struct prestera_fw_header before decoding the magic and version fields. Fixes: 4c2703dfd7fabb ("net: marvell: prestera: Add PCI interface support") Signed-off-by: Pengpeng Hou Acked-by: Elad Nachman Link: https://patch.msgid.link/20260731141500.1-prestera-v2-pengpeng@iscas.ac.cn Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/marvell/prestera/prestera_pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/marvell/prestera/prestera_pci.c b/drivers/net/ethernet/marvell/prestera/prestera_pci.c index 2989a77e3b42..1ad0e62a8433 100644 --- a/drivers/net/ethernet/marvell/prestera/prestera_pci.c +++ b/drivers/net/ethernet/marvell/prestera/prestera_pci.c @@ -684,6 +684,9 @@ static int prestera_fw_hdr_parse(struct prestera_fw *fw) struct prestera_fw_header *hdr; u32 magic; + if (fw->bin->size < sizeof(*hdr)) + return -EINVAL; + hdr = (struct prestera_fw_header *)fw->bin->data; magic = be32_to_cpu(hdr->magic_number); From c27e360545373b7aee9862a5beef3b9fb3df0c25 Mon Sep 17 00:00:00 2001 From: Hidayath Khan Date: Thu, 30 Jul 2026 14:42:27 +0200 Subject: [PATCH 466/501] dibs: initialise dibs->lock in dibs_dev_alloc() dibs->lock is initialised by dibs_dev_add(), but a dibs device can already take interrupts before that call: ism_probe() runs ism_dev_init(), and hence request_irq(), before it calls dibs_dev_add(). No client can have registered a dmb at that point, so no dmb interrupt can occur, but a GID event interrupt can, and ism_handle_irq() takes dibs->lock unconditionally on entry, before it inspects anything else. Initialise the lock in dibs_dev_alloc() instead, so that it is valid as soon as a driver can publish the device to its interrupt handler. Fixes: cc21191b584c ("dibs: Move data path to dibs layer") Cc: stable@vger.kernel.org Reviewed-by: Alexandra Winter Signed-off-by: Hidayath Khan Link: https://patch.msgid.link/20260730124227.167829-1-hidayath@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/dibs/dibs_main.c | 2 +- include/linux/dibs.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dibs/dibs_main.c b/drivers/dibs/dibs_main.c index 14c3e2d84902..4c26fd06973f 100644 --- a/drivers/dibs/dibs_main.c +++ b/drivers/dibs/dibs_main.c @@ -138,6 +138,7 @@ struct dibs_dev *dibs_dev_alloc(void) dibs = kzalloc_obj(*dibs); if (!dibs) return dibs; + spin_lock_init(&dibs->lock); dibs->dev.release = dibs_dev_release; dibs->dev.class = &dibs_class; device_initialize(&dibs->dev); @@ -186,7 +187,6 @@ int dibs_dev_add(struct dibs_dev *dibs) int i, ret; max_dmbs = dibs->ops->max_dmbs(); - spin_lock_init(&dibs->lock); dibs->dmb_clientid_arr = kzalloc(max_dmbs, GFP_KERNEL); if (!dibs->dmb_clientid_arr) return -ENOMEM; diff --git a/include/linux/dibs.h b/include/linux/dibs.h index c75607f8a5cf..d3e0777f25ae 100644 --- a/include/linux/dibs.h +++ b/include/linux/dibs.h @@ -439,7 +439,7 @@ static inline void *dibs_get_priv(struct dibs_dev *dev, /** * dibs_dev_alloc() - allocate and reference device structure * - * The following fields will be valid upon successful return: dev + * The following fields will be valid upon successful return: dev, lock * NOTE: Use put_device(dibs_get_dev(@dibs)) to give up your reference instead * of freeing @dibs @dev directly once you have successfully called this * function. From 44871eadd07a7f004aa00cb87399461eea08c630 Mon Sep 17 00:00:00 2001 From: Jijie Shao Date: Tue, 4 Aug 2026 21:05:54 +0800 Subject: [PATCH 467/501] MAINTAINERS: add myself as a maintainer for Hisilicon Network Subsystem I am already listed as a maintainer for the HNS3 and HIBMCGE drivers, but not for the broader Hisilicon Network Subsystem entry, whose file pattern covers drivers/net/ethernet/hisilicon/ (e.g. the legacy hns driver). As a result, patches to those files are not CC'd to me. Add myself alongside Jian Shen to help maintain these legacy Hisilicon ethernet drivers and ensure patches in this tree are routed to me. Signed-off-by: Jijie Shao Acked-by: Jian Shen Reviewed-by: Simon Horman Link: https://patch.msgid.link/20260804130554.871716-1-shaojijie@huawei.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0d7987278c07..a77cf599bac2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11743,6 +11743,7 @@ F: drivers/net/ethernet/hisilicon/hibmcge/ HISILICON NETWORK SUBSYSTEM DRIVER M: Jian Shen +M: Jijie Shao L: netdev@vger.kernel.org S: Maintained W: http://www.hisilicon.com From b8a39a09ae4eaae04309e1e38ed6a1101d967496 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 4 Aug 2026 15:20:48 +0000 Subject: [PATCH 468/501] net: remove WARN_ON_ONCE() from sk_mc_loop() sk_mc_loop() can be called for sockets that are neither AF_INET nor AF_INET6 (e.g. AF_PACKET sockets when sending packets via raw/packet socket over virtual devices such as VRF or ipvlan). In such cases, sk_family is not AF_INET/AF_INET6 and sk_mc_loop() falls through the switch statement and triggers WARN_ON_ONCE(1). Non-INET sockets do not support IP_MULTICAST_LOOP or IPV6_MULTICAST_LOOP options, so loopback should default to true without generating a warning. Fixes: f60e5990d9c1 ("ipv6: protect skb->sk accesses from recursive dereference inside the stack") Reported-by: syzbot+22c3218a6fa219e47321@syzkaller.appspotmail.com Closes: https://lore.kernel.org/netdev/6a72024c.13623e66.bdc14.0019.GAE@google.com/T/#u Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260804152048.2134341-1-edumazet@google.com Signed-off-by: Paolo Abeni --- net/core/sock.c | 1 - 1 file changed, 1 deletion(-) diff --git a/net/core/sock.c b/net/core/sock.c index ffa73594c13c..1ad41904db25 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -779,7 +779,6 @@ bool sk_mc_loop(const struct sock *sk) return inet6_test_bit(MC6_LOOP, sk); #endif } - WARN_ON_ONCE(1); return true; } EXPORT_SYMBOL(sk_mc_loop); From 185a4caeecabc150106deda1da170b09f2ad803f Mon Sep 17 00:00:00 2001 From: Sidraya Jayagond Date: Mon, 3 Aug 2026 09:07:01 +0200 Subject: [PATCH 469/501] net/smc: fix TOCTOU race between smc_listen_out() and listener close smc_listen_out() reads lsmc->sk.sk_state without the listener lock, then acquires lock_sock_nested() only after the check passes. This opens a window where smc_close_active() can transition the listener to SMC_CLOSED, call smc_close_cleanup_listen() to drain the accept queue, and release the lock, all between the lockless read and the delayed lock acquisition: smc_listen_work (smc_hs_wq) smc_close_active() ------------------------------- ------------------------- release_sock(child) if (sk_state == SMC_LISTEN) TRUE lock_sock(listener) sk_state = SMC_CLOSED smc_close_cleanup_listen() release_sock(listener) flush_work(tcp_listen_work) lock_sock_nested(listener) smc_accept_enqueue(listener, child) /* child enqueued on dead listener */ smc_close_active() flushes only tcp_listen_work. Work items already dispatched onto smc_hs_wq for the CLC handshake continue running unguarded. smc_accept_enqueue() takes a sock_hold() on the child that is never released, so the child smc_sock, its clcsock, and the reference all leak. A remote peer that opens TCP connections while the server calls close() can exhaust kernel memory. Move lock_sock_nested() to before the sk_state check so that the test and the enqueue are atomic under the listener lock. Fixes: fd57770dd198 ("net/smc: wait for pending work before clcsock release_sock") Reviewed-by: Mahanta Jambigi Signed-off-by: Sidraya Jayagond Reviewed-by: Breno Leitao Reviewed-by: Dust Li Link: https://patch.msgid.link/20260803070701.126339-1-sidraya@linux.ibm.com Signed-off-by: Paolo Abeni --- net/smc/af_smc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index b5db69073e20..00403175b740 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -1931,11 +1931,12 @@ static void smc_listen_out(struct smc_sock *new_smc) atomic_dec(&lsmc->queued_smc_hs); release_sock(newsmcsk); /* lock in smc_listen_work() */ + lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); if (lsmc->sk.sk_state == SMC_LISTEN) { - lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); smc_accept_enqueue(&lsmc->sk, newsmcsk); release_sock(&lsmc->sk); } else { /* no longer listening */ + release_sock(&lsmc->sk); smc_close_non_accepted(newsmcsk); } From f684c514f7965385dae21f2535f99938e73ec1af Mon Sep 17 00:00:00 2001 From: Ahmed Naseef Date: Tue, 4 Aug 2026 15:35:11 +0400 Subject: [PATCH 470/501] net: phy: mediatek: fix TX blink masks using the RX bits MTK_GPHY_LED_TX_BLINK_SET and MTK_2P5GPHY_LED_TX_BLINK_SET are built from the RX blink bits instead of the TX ones, so both TX masks are identical to their RX counterparts. The TX bits they should be using, MTK_PHY_LED_BLINK_{10,100,1000,2500}TX, are otherwise only referenced by the per-speed branch of mtk_phy_led_hw_ctrl_set(). A TX trigger selected without a link trigger therefore programs the RX blink bits, and the LED blinks on received traffic. The masks are also used to decode the blink register in mtk_phy_led_hw_ctrl_get(), which as a result cannot tell the two triggers apart: an RX-only configuration reads back as RX and TX, and a TX-only configuration reads back as neither. Fixes: 7f9c320c98db ("net: phy: mediatek: Move LED helper functions into mtk phy lib") Cc: stable@vger.kernel.org Signed-off-by: Ahmed Naseef Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/20260804113511.3371248-1-naseefkm@gmail.com Signed-off-by: Paolo Abeni --- drivers/net/phy/mediatek/mtk.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/mediatek/mtk.h b/drivers/net/phy/mediatek/mtk.h index 320f76ffa81f..79e09727b81b 100644 --- a/drivers/net/phy/mediatek/mtk.h +++ b/drivers/net/phy/mediatek/mtk.h @@ -53,15 +53,15 @@ #define MTK_GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ MTK_PHY_LED_BLINK_100RX | \ MTK_PHY_LED_BLINK_10RX) -#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000RX | \ - MTK_PHY_LED_BLINK_100RX | \ - MTK_PHY_LED_BLINK_10RX) +#define MTK_GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_1000TX | \ + MTK_PHY_LED_BLINK_100TX | \ + MTK_PHY_LED_BLINK_10TX) #define MTK_2P5GPHY_LED_ON_SET (MTK_PHY_LED_ON_LINK2500 | \ MTK_GPHY_LED_ON_SET) #define MTK_2P5GPHY_LED_RX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ MTK_GPHY_LED_RX_BLINK_SET) -#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500RX | \ +#define MTK_2P5GPHY_LED_TX_BLINK_SET (MTK_PHY_LED_BLINK_2500TX | \ MTK_GPHY_LED_TX_BLINK_SET) #define MTK_PHY_LED_STATE_FORCE_ON 0 From 7e2d693af0d4c05bddccb3541a0aabd69f4cb244 Mon Sep 17 00:00:00 2001 From: "Sergey V. Frolov" Date: Tue, 4 Aug 2026 15:04:48 +0300 Subject: [PATCH 471/501] net: octeontx2-pf: Fix UB in shift operation In function otx2_get_egress_burst_cfg, when the parameter `burst` is 255 and the max mantissa is 255 (0xFFULL), `burst_exp` is set to `ilog2(255) - 1`, which equals 6. This results in an unsigned wrap-around when calculating `(1ULL << (*burst_exp - 7))`, since `*burst_exp - 7` becomes -1, which makes the shift operand 0xFFFFFFFF. This value is greater than the width of the left operand. According to standard 6.5.7 p.3: "The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined." Fix the off-by-one boundary condition. Add a WARN_ON(*burst_exp < 7) before the else branch as an explicit safeguard. This ensures that if max_mantissa ever changes in a way that reintroduces this condition, it will be immediately caught at runtime rather than silently triggering UB. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: e638a83f167e ("octeontx2-pf: TC_MATCHALL egress ratelimiting offload") Signed-off-by: Sergey V. Frolov Cc: stable@vger.kernel.org Reviewed-by: Ratheesh Kannoth Reviewed-by: Sunil Goutham Link: https://patch.msgid.link/20260804120446.1955448-1-Sergey.V.Frolov@kaspersky.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 0b46ec29e64e..039fd47ebf52 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -54,10 +54,12 @@ static void otx2_get_egress_burst_cfg(struct otx2_nic *nic, u32 burst, if (burst) { *burst_exp = ilog2(burst) ? ilog2(burst) - 1 : 0; tmp = burst - rounddown_pow_of_two(burst); - if (burst < max_mantissa) + if (burst <= max_mantissa) { *burst_mantissa = tmp * 2; - else + } else { + WARN_ON(*burst_exp < 7); *burst_mantissa = tmp / (1ULL << (*burst_exp - 7)); + } } else { *burst_exp = MAX_BURST_EXPONENT; *burst_mantissa = max_mantissa; From dedd34b0f2310e28c5f6d4875cfbf4b7ed821c01 Mon Sep 17 00:00:00 2001 From: Zijie Huang Date: Sat, 1 Aug 2026 21:42:33 +0800 Subject: [PATCH 472/501] net/sched: reject overly deep qdisc hierarchies Deep qdisc hierarchies can lead to excessive recursion in qdisc tree walkers and exhaust the kernel stack. The existing loop check does not cover the create-and-graft path, so a hierarchy can still be extended by creating a new child qdisc below an already deep parent. Store the hierarchy depth in struct Qdisc and update it when qdiscs are grafted. Reject new child qdiscs once the parent is already at the maximum allowed depth. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Suggested-by: Jamal Hadi Salim Reported-by: Vega Assisted-by: Codex:gpt-5.4 Signed-off-by: Zijie Huang Signed-off-by: Ren Wei Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/1e9ab39597423fd5d13cfaaf52279b8ee3d9fc3c.1785434373.git.milkory@outlook.com Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni --- include/net/sch_generic.h | 1 + net/sched/sch_api.c | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 45a1e8c78222..cbc248776511 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -99,6 +99,7 @@ struct Qdisc { struct hlist_node hash; u32 handle; u32 parent; + int depth; struct netdev_queue *dev_queue; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 668bcd60d183..65b35528d125 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1114,6 +1114,9 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, unsigned int i, num_q, ingress; struct netdev_queue *dev_queue; + if (new) + new->depth = 0; + ingress = 0; num_q = dev->num_tx_queues; if ((q && q->flags & TCQ_F_INGRESS) || @@ -1211,9 +1214,15 @@ static int qdisc_graft(struct net_device *dev, struct Qdisc *parent, NL_SET_ERR_MSG(extack, "STAB not supported on a non root"); return -EINVAL; } + if (new && parent->depth >= 7) { + NL_SET_ERR_MSG(extack, "Qdisc hierarchy is too deep"); + return -E2BIG; + } err = cops->graft(parent, cl, new, &old, extack); if (err) return err; + if (new) + new->depth = parent->depth + 1; notify_and_destroy(net, skb, n, classid, old, new, extack); } return 0; From d8eb9a06b922418598eac38fb9772ea4e9ef4273 Mon Sep 17 00:00:00 2001 From: Zijie Huang Date: Sat, 1 Aug 2026 21:42:34 +0800 Subject: [PATCH 473/501] selftests/tc-testing: add qdisc hierarchy depth tests Add tdc coverage for the qdisc hierarchy depth limit. The tests verify that the deepest allowed hierarchy can still be created and that adding another child qdisc below it is rejected. Signed-off-by: Zijie Huang Assisted-by: Codex:gpt-5.4 Signed-off-by: Ren Wei Reviewed-by: Victor Nogueira Link: https://patch.msgid.link/5cc2d26a7c8e553759cdd29a3116f843fabc25ba.1785434373.git.milkory@outlook.com Acked-by: Jamal Hadi Salim Signed-off-by: Paolo Abeni --- .../tc-testing/tc-tests/qdiscs/depth.json | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json new file mode 100644 index 000000000000..48f8683f0d1a --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/depth.json @@ -0,0 +1,74 @@ +[ + { + "id": "ad43", + "name": "Create qdisc hierarchy at maximum depth", + "category": [ + "qdisc", + "depth" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: htb default 1", + "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: htb default 1", + "$TC class add dev $DUMMY parent 2: classid 2:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: htb default 1", + "$TC class add dev $DUMMY parent 3: classid 3:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 3:1 handle 4: htb default 1", + "$TC class add dev $DUMMY parent 4: classid 4:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 4:1 handle 5: htb default 1", + "$TC class add dev $DUMMY parent 5: classid 5:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 5:1 handle 6: htb default 1", + "$TC class add dev $DUMMY parent 6: classid 6:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 6:1 handle 7: htb default 1", + "$TC class add dev $DUMMY parent 7: classid 7:1 htb rate 10mbit" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 7:1 handle 8: htb default 1", + "expExitCode": "0", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc htb 8: parent 7:1", + "matchCount": "1", + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + }, + { + "id": "c519", + "name": "Reject qdisc hierarchy above maximum depth", + "category": [ + "qdisc", + "depth" + ], + "plugins": { + "requires": "nsPlugin" + }, + "setup": [ + "$TC qdisc add dev $DUMMY root handle 1: htb default 1", + "$TC class add dev $DUMMY parent 1: classid 1:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 1:1 handle 2: htb default 1", + "$TC class add dev $DUMMY parent 2: classid 2:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 2:1 handle 3: htb default 1", + "$TC class add dev $DUMMY parent 3: classid 3:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 3:1 handle 4: htb default 1", + "$TC class add dev $DUMMY parent 4: classid 4:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 4:1 handle 5: htb default 1", + "$TC class add dev $DUMMY parent 5: classid 5:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 5:1 handle 6: htb default 1", + "$TC class add dev $DUMMY parent 6: classid 6:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 6:1 handle 7: htb default 1", + "$TC class add dev $DUMMY parent 7: classid 7:1 htb rate 10mbit", + "$TC qdisc add dev $DUMMY parent 7:1 handle 8: htb default 1", + "$TC class add dev $DUMMY parent 8: classid 8:1 htb rate 10mbit" + ], + "cmdUnderTest": "$TC qdisc add dev $DUMMY parent 8:1 handle 9: htb default 1", + "expExitCode": "2", + "verifyCmd": "$TC qdisc show dev $DUMMY", + "matchPattern": "qdisc htb 9:", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DUMMY root" + ] + } +] From 7fc1c937b6b37c77df4ba374c37435ab06a2e945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Patron?= Date: Tue, 4 Aug 2026 22:18:30 +0200 Subject: [PATCH 474/501] net: qrtr: ns: Raise lookup limit to 128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current limit of 64 is not enough for Sony Xperia 10 VII (SM6475). After merging v6.6.142 into a downstream AOSP device, it's stuck on boot animation and following log spam can be observed in dmesg: E qrtr : ctrl_cmd_new_lookup(): QRTR client node exceeds max lookup limit! E qrtr : qrtr_ns_worker(): failed while handling packet from 1:16600 No idea why it needs more than 64 client lookups, but it appears to work fine with 128 as it did when there were no limits. I don't really have a good way to investigate what it needs all these lookups for as most of the userspace is closed source. Fixes: 5640227d9a21 ("net: qrtr: ns: Limit the maximum number of lookups") Signed-off-by: Łukasz Patron Link: https://patch.msgid.link/20260804201919.1148015-1-priv.luk@gmail.com Signed-off-by: Jakub Kicinski --- net/qrtr/ns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/qrtr/ns.c b/net/qrtr/ns.c index e5b2adb161d9..c5e7e01db249 100644 --- a/net/qrtr/ns.c +++ b/net/qrtr/ns.c @@ -78,7 +78,7 @@ struct qrtr_node { */ #define QRTR_NS_MAX_NODES 512 #define QRTR_NS_MAX_SERVERS 256 -#define QRTR_NS_MAX_LOOKUPS 64 +#define QRTR_NS_MAX_LOOKUPS 128 static u16 node_count; From 68bf02b6b4ad3f748c6db71fd77b6c0402d252f4 Mon Sep 17 00:00:00 2001 From: Fan XinRan Date: Mon, 3 Aug 2026 14:38:50 +0000 Subject: [PATCH 475/501] net: thunderbolt: Tear down DMA paths before stopping the rings tbnet_tear_down() stops both rings and frees their frame buffers before calling tb_xdomain_disable_paths(). tb_ring_stop() zeroes the ring's descriptor base and tbnet_free_buffers() unmaps and frees the pages the frames sit in, so by the time __tb_path_deactivate_hop() polls the hop's 'pending' bit, anything still in flight has nowhere to drain to. The teardown sequence has been in this order since the driver was added. The setup path has not: commit ff7cd07f3064 ("net: thunderbolt: Enable DMA paths only after rings are enabled") moved the path enable to the end of tbnet_connected_work() and documented why: /* Both logins successful so enable the rings, high-speed DMA * paths and start the network device queue. * * Note we enable the DMA paths last to make sure we have primed * the Rx ring before any incoming packets are allowed to * arrive. */ Teardown was never updated to match, so the rings and the paths now come down in the same order they go up instead of in reverse. On an ASMedia ASM4242 host router the 'pending' bit then never clears: every teardown burns the full 500 ms timeout and __tb_path_deactivate_hop() returns -ETIMEDOUT. Raising the timeout to 5 s does not help, so the hop is not slow to drain, it never drains at all. The failure is invisible above the thunderbolt core. __tb_path_deactivate_hops() is void and only calls tb_port_warn(); tb_path_deactivate(), tb_tunnel_deactivate() and __tb_disconnect_xdomain_paths() are void as well, and tb_disconnect_xdomain_paths() ends in an unconditional "return 0". So tb_xdomain_disable_paths() reports success and the netdev_warn() below it never fires. Repeated teardowns eventually take the XDomain control channel down, after which the peer node is gone and only a power cycle brings the controller back. Deactivating the paths first fixes it. Measured with kretprobes on a stock v6.17 tree with no other patches applied, on a link that was up and had just carried traffic: before: __tb_path_deactivate_hop() returns 0 for the first hop, then -ETIMEDOUT for the second 500335 us later after: 0 for both, 525 us apart Alternating the two orderings ABBA over three load levels, four teardowns per arm: every teardown failed before the change (21 of 21 that ran), none failed after (0 of 24). The before arms ran short because the link died partway through. The same split shows up when the interface is enslaved to a bond instead of just brought down, which is how I ran into this in the first place. Throughput and latency after the change are unchanged. Hosts whose routers drain the hop despite the stale descriptor base see no functional difference, since the paths end up deactivated either way. Fixes: e69b6c02b4c3 ("net: Add support for networking over Thunderbolt cable") Signed-off-by: Fan XinRan Acked-by: Mika Westerberg Link: https://patch.msgid.link/20260803-b4-tbnet-teardown-v2-1-27de6a13ca2d@gmail.com Signed-off-by: Jakub Kicinski --- drivers/net/thunderbolt/main.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/net/thunderbolt/main.c b/drivers/net/thunderbolt/main.c index c5670d61820c..98893732bc6e 100644 --- a/drivers/net/thunderbolt/main.c +++ b/drivers/net/thunderbolt/main.c @@ -386,11 +386,16 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) break; } - tb_ring_stop(net->rx_ring.ring); - tb_ring_stop(net->tx_ring.ring); - tbnet_free_buffers(&net->rx_ring); - tbnet_free_buffers(&net->tx_ring); - + /* Tear the paths down before stopping the rings. This mirrors + * tbnet_connected_work(), which enables the paths last so the + * Rx ring is primed before packets can arrive. Stopping a + * ring zeroes its descriptor base and tbnet_free_buffers() + * unmaps and frees the frame buffers, leaving anything still + * in flight with nowhere to drain to; + * __tb_path_deactivate_hop() then waits for the hop's + * 'pending' bit, which on some host routers never clears in + * that state. + */ ret = tb_xdomain_disable_paths(net->xd, net->local_transmit_path, net->tx_ring.ring->hop, @@ -399,6 +404,11 @@ static void tbnet_tear_down(struct tbnet *net, bool send_logout) if (ret) netdev_warn(net->dev, "failed to disable DMA paths\n"); + tb_ring_stop(net->rx_ring.ring); + tb_ring_stop(net->tx_ring.ring); + tbnet_free_buffers(&net->rx_ring); + tbnet_free_buffers(&net->tx_ring); + tb_xdomain_release_in_hopid(net->xd, net->remote_transmit_path); net->remote_transmit_path = 0; } From fde39b8a521780391fb4e5bda2c0aa4928947f12 Mon Sep 17 00:00:00 2001 From: Doruk Tan Ozturk Date: Sun, 2 Aug 2026 14:06:02 +0200 Subject: [PATCH 476/501] net: usb: ipheth: fix carrier_work UAF on disconnect ipheth_sndbulk_callback() re-arms the carrier-check work on any non-zero URB status: else schedule_delayed_work(&dev->carrier_work, 0); Nothing ties that to the interface being up, so the work can be armed again after ipheth_close() has already drained it, and stay armed until the netdev whose private area embeds it is freed. On unplug with a TX URB in flight, ipheth_disconnect() drains the work through unregister_netdev() -> ipheth_close() -> cancel_delayed_work_sync() and only then calls ipheth_kill_urbs(). usb_kill_urb() completes the in-flight TX URB with -ENOENT, so ipheth_sndbulk_callback() runs after the drain and re-arms carrier_work. The same completion also re-arms the work if the interface is only brought down while a TX URB is in flight, and ipheth_carrier_check_work() then keeps re-queueing itself once a second. unregister_netdev() does not call ipheth_close() for an already-down interface, so nothing drains it on the later unplug either. In both cases free_netdev() frees the netdev while carrier_work is still pending, and ipheth_carrier_check_work() dereferences freed memory. Tie the work to the interface state instead of chasing the completion: disable it in ipheth_close() and enable it in ipheth_open(), so a schedule_delayed_work() from the URB completion is a no-op whenever the interface is not up. disable_delayed_work_sync() also waits for a running instance, so it fully replaces the cancel_delayed_work_sync() it takes the place of. The work starts out disabled in ipheth_probe() so the enable/disable counts balance from the first open. Reproduced under KASAN on linux-next (next-20260731) with dummy_hcd and raw-gadget standing in for the device, driving the second path above (the interface is already down, so unregister_netdev() does not call ipheth_close()): 15 of 15 unpatched boots report a slab-use-after-free in __run_timers(), freed by ipheth_disconnect() and re-armed from ipheth_sndbulk_callback() via queue_delayed_work_on(). The same trigger on a kernel differing only by this patch reports 0 of 15, and the carrier check still functions across open/close cycles. The reproducer needs an attached USB device that stops draining bulk OUT, plus a link down and unplug, driven as root. It is not a privilege boundary crossing and no exploit primitive was developed. Found by 0sec (https://0sec.ai). Fixes: bb1b40c7cb86 ("usbnet: ipheth: prevent TX queue timeouts when device not ready") Cc: stable@vger.kernel.org Signed-off-by: Doruk Tan Ozturk Link: https://patch.msgid.link/20260802120602.42595-1-doruk@0sec.ai Signed-off-by: Jakub Kicinski --- drivers/net/usb/ipheth.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c index bb1364f85bd1..2b490114d232 100644 --- a/drivers/net/usb/ipheth.c +++ b/drivers/net/usb/ipheth.c @@ -490,6 +490,7 @@ static int ipheth_open(struct net_device *net) if (retval) return retval; + enable_delayed_work(&dev->carrier_work); schedule_delayed_work(&dev->carrier_work, IPHETH_CARRIER_CHECK_TIMEOUT); return retval; } @@ -499,7 +500,11 @@ static int ipheth_close(struct net_device *net) struct ipheth_device *dev = netdev_priv(net); netif_stop_queue(net); - cancel_delayed_work_sync(&dev->carrier_work); + /* A TX URB can still complete with an error after this point and + * try to re-arm the carrier work. Disable it instead of cancelling + * it, so that such a schedule_delayed_work() is a no-op. + */ + disable_delayed_work_sync(&dev->carrier_work); return 0; } @@ -629,6 +634,10 @@ static int ipheth_probe(struct usb_interface *intf, } INIT_DELAYED_WORK(&dev->carrier_work, ipheth_carrier_check_work); + /* Armed only between ipheth_open() and ipheth_close(). Start out + * disabled so the enable/disable counts balance from the first open. + */ + disable_delayed_work(&dev->carrier_work); retval = ipheth_alloc_urbs(dev); if (retval) { From 35772b4981f38ba8059372cde8753e8e477e98ec Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:33 +0200 Subject: [PATCH 477/501] mptcp: options: reset DSS fields in case of unexpected size A remote peer could send a malformed DSS with a wrong size, followed by another DSS or MPC + Data. In this case, the first suboption will be ignored, but leaving some fields written, which could lead to inconsistency or access uninitialized data. Explicitly reset the fields that could have been modified in case of unexpected size. Link: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260728-net-mptcp-misc-fixes-7-2-rc6-v1-0-f7e2d229159d%40kernel.org?part=1 Fixes: 648ef4b88673 ("mptcp: Implement MPTCP receive path") Cc: stable@vger.kernel.org Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-1-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index c664023d37ba..038eca33c6b1 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -188,8 +188,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, * RFC 8684 Section 3.3.0 checks later in subflow_data_ready */ if (opsize != expected_opsize && - opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) + opsize != expected_opsize + TCPOLEN_MPTCP_DSS_CHECKSUM) { + mp_opt->dsn64 = 0; + mp_opt->use_map = 0; + mp_opt->ack64 = 0; + mp_opt->use_ack = 0; + mp_opt->data_fin = 0; break; + } mp_opt->suboptions |= OPTION_MPTCP_DSS; if (mp_opt->use_ack) { From b6ee361524641f57b2e2363f7737f20e17f67827 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:34 +0200 Subject: [PATCH 478/501] mptcp: avoid combining some incoming suboptions Some MPTCP suboptions are mutually exclusive according to the RFC8684, but also because in different places, the code doesn't expect some combinations to be present. That's specially true for suboptions that would be present twice, but with different attributes. The new restrictions are the same as the ones applied on the output side, with mptcp_write_options. The same rules can be reused with a small fix: an MP_FASTCLOSE can be used with a DSS when the sender picks this option [1], which is not the case on Linux. Here are the rules: Which options can be used together? X: mutually exclusive O: often used together C: can be used together in some cases P: could be used together but we prefer not to (optimisations) | Opt: | MPC | MPJ | DSS | ADD | RM | PRIO | FAIL | FC | |------|------|------|------|------|------|------|------|------| | MPC |------|------|------|------|------|------|------|------| | MPJ | X |------|------|------|------|------|------|------| | DSS | X | X |------|------|------|------|------|------| | ADD | X | X | P |------|------|------|------|------| | RM | C | C | C | P |------|------|------|------| | PRIO | X | C | C | C | C |------|------|------| | FAIL | X | X | C | X | X | X |------|------| | FC | X | X | P | X | X | X | X |------| | RST | X | X | X | X | X | X | O | O | |------|------|------|------|------|------|------|------|------| The only difference is with the 'P': another stack could send and ADD_ADDR with other suboptions (DSS, RM_ADDR), and this should be allowed. A few points of attention: - In theory, an MP_CAPABLE could be used with a RM_ADDR, but there is no reason to add it with a SYN. Note that even with a 4th ACK, it doesn't seem to be useful, except when IDs are known in advance via another channel. Better not to break that. - Now, combining both an MP_CAPABLE and an MP_JOIN will no longer result to a reject of the two options, but only the second suboption is ignored. That seems OK to do that for this unexpected error. At least now all inconsistent combinations are handled the same way. This could change later in next. This also means the explicit checks for having both MPC + MPJ in subflow.c will now be unreachable. That's fine, they will be removed in a follow-up patch. - In case of conflicting combinations, the extra suboption(s) is/are ignored: having such combinations either means the remote peer is buggy, or is evil. The simplest action is then taken in this case: stop processing the current suboption. - In mp_opt->suboptions, there is also a bit reserved to the checksum, which can be used in an MP_CAPABLE and a DSS. Each time a DSS option can be used in parallel with another option, the checksum can be set, so the verification is combined into a new OPTIONS_MPTCP_DSS macro. - An MP_CAPABLE ACK can carry a Data-Level Length, and an optional Checksum: they are the same as the ones found in a DSS, because a DSS cannot be used in parallel to an MP_CAPABLE. Similarly, even if there is room, a DSS cannot be used with an MP_JOIN. Fixes: eda7acddf808 ("mptcp: Handle MPTCP TCP options") Cc: stable@vger.kernel.org Link: https://www.rfc-editor.org/rfc/rfc8684.html#section-3.5-5.1 [1] Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-2-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/options.c | 59 +++++++++++++++++++++++++++++++++++++++++++- net/mptcp/protocol.h | 1 + 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/net/mptcp/options.c b/net/mptcp/options.c index 038eca33c6b1..1057d500577b 100644 --- a/net/mptcp/options.c +++ b/net/mptcp/options.c @@ -50,6 +50,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, } } + /* Only the MPC + ACK can be used with a RM_ADDR */ + if (subopt == OPTION_MPTCP_MPC_ACK) { + if ((mp_opt->suboptions & ~OPTION_MPTCP_RM_ADDR) != 0) + break; + } else if (mp_opt->suboptions != 0) { + break; + } + /* Cfr RFC 8684 Section 3.3.0: * If a checksum is present but its use had * not been negotiated in the MP_CAPABLE handshake, the receiver MUST @@ -122,6 +130,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_JOIN: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize == TCPOLEN_MPTCP_MPJ_SYN) { mp_opt->suboptions |= OPTION_MPTCP_MPJ_SYN; mp_opt->backup = *ptr++ & MPTCPOPT_BACKUP; @@ -153,6 +166,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_DSS: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO | + OPTION_MPTCP_FASTCLOSE | + OPTION_MPTCP_FAIL)) != 0) + break; + pr_debug("DSS\n"); ptr++; @@ -240,6 +261,12 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_ADD_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RM_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + mp_opt->echo = (*ptr++) & MPTCP_ADDR_ECHO; if (!mp_opt->echo) { if (opsize == TCPOLEN_MPTCP_ADD_ADDR || @@ -299,6 +326,14 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RM_ADDR: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_MPC_ACK | + OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_PRIO)) != 0) + break; + if (opsize < TCPOLEN_MPTCP_RM_ADDR_BASE + 1 || opsize > TCPOLEN_MPTCP_RM_ADDR_BASE + MPTCP_RM_IDS_MAX) break; @@ -313,6 +348,13 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_PRIO: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_MPJ | + OPTIONS_MPTCP_DSS | + OPTION_MPTCP_ADD_ADDR | + OPTION_MPTCP_RM_ADDR)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_PRIO) break; @@ -322,6 +364,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FASTCLOSE: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FASTCLOSE) break; @@ -333,6 +380,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_RST: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTION_MPTCP_FAIL | + OPTION_MPTCP_FASTCLOSE)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_RST) break; @@ -348,6 +400,11 @@ static void mptcp_parse_option(const struct sk_buff *skb, break; case MPTCPOPT_MP_FAIL: + /* Can be used with a restricted number of other options */ + if ((mp_opt->suboptions & ~(OPTIONS_MPTCP_DSS | + OPTION_MPTCP_RST)) != 0) + break; + if (opsize != TCPOLEN_MPTCP_FAIL) break; @@ -1406,7 +1463,7 @@ void mptcp_write_options(struct tcphdr *th, __be32 *ptr, struct tcp_sock *tp, * RM | C | C | C | P |------|------|------|------| * PRIO | X | C | C | C | C |------|------|------| * FAIL | X | X | C | X | X | X |------|------| - * FC | X | X | X | X | X | X | X |------| + * FC | X | X | P | X | X | X | X |------| * RST | X | X | X | X | X | X | O | O | * ------|------|------|------|------|------|------|------|------| * diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index 4a2d40cd7b13..c13680d18994 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -37,6 +37,7 @@ OPTION_MPTCP_MPC_ACK) #define OPTIONS_MPTCP_MPJ (OPTION_MPTCP_MPJ_SYN | OPTION_MPTCP_MPJ_SYNACK | \ OPTION_MPTCP_MPJ_ACK) +#define OPTIONS_MPTCP_DSS (OPTION_MPTCP_DSS | OPTION_MPTCP_CSUMREQD) /* MPTCP option subtypes */ #define MPTCPOPT_MP_CAPABLE 0 From 900e6d80548e69be7ffd358122790fbaa458a9b4 Mon Sep 17 00:00:00 2001 From: "Matthieu Baerts (NGI0)" Date: Mon, 3 Aug 2026 18:16:35 +0200 Subject: [PATCH 479/501] mptcp: remove MPC && MPJ check After the parent commit ("mptcp: avoid combining some incoming suboptions"), the parsing step no longer allow to have both the MP_CAPABLE and MP_JOIN suboptions set together. These chunks are now unreachable, these checks can then be removed. Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-3-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/subflow.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c index 8e386899ceb9..e1f20ff8fdb4 100644 --- a/net/mptcp/subflow.c +++ b/net/mptcp/subflow.c @@ -174,8 +174,6 @@ static int subflow_check_req(struct request_sock *req, if (unlikely(listener->pm_listener)) return subflow_reset_req_endp(req, skb); - if (opt_mp_join) - return 0; } else if (opt_mp_join) { SUBFLOW_REQ_INC_STATS(req, MPTCP_MIB_JOINSYNRX); @@ -277,9 +275,6 @@ int mptcp_subflow_init_cookie_req(struct request_sock *req, opt_mp_capable = !!(mp_opt.suboptions & OPTION_MPTCP_MPC_ACK); opt_mp_join = !!(mp_opt.suboptions & OPTION_MPTCP_MPJ_ACK); - if (opt_mp_capable && opt_mp_join) - return -EINVAL; - if (opt_mp_capable && listener->request_mptcp) { if (mp_opt.sndr_key == 0) return -EINVAL; From a7aad5b69d3bdaec20a3ed9284e184502450c0cd Mon Sep 17 00:00:00 2001 From: Qing Luo Date: Mon, 3 Aug 2026 18:16:36 +0200 Subject: [PATCH 480/501] mptcp: pm: fix data race in add_addr timer callback The timer callback reads entry->retrans_times outside pm.lock to decide whether to call mptcp_pm_subflow_established(). Since mptcp_pm_announced_del_timer() can concurrently set retrans_times = ADD_ADDR_RETRANS_MAX under pm.lock, a race condition exists. I discovered this issue while studying the code. AI tools helped me to verify the issue can potentially happen under race conditions. Use a local 'retransmit' flag set inside pm.lock to capture whether retransmission is still possible when the lock is taken. This allows to call mptcp_pm_subflow_established() accordingly, and not depending on the situation that can be different when checked outside the pm.lock. Fixes: 348d5c1dec60 ("mptcp: move to next addr when timeout") Cc: stable@vger.kernel.org Signed-off-by: Qing Luo Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-4-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index 6afd39aea110..c71dcf887683 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -380,6 +380,7 @@ static void mptcp_pm_add_addr_timer(struct timer_list *timer) struct mptcp_sock *msk = entry->sock; struct sock *sk = (struct sock *)msk; unsigned int timeout = 0; + bool retransmit; pr_debug("msk=%p\n", msk); @@ -412,14 +413,15 @@ static void mptcp_pm_add_addr_timer(struct timer_list *timer) entry->retrans_times++; } - if (entry->retrans_times < ADD_ADDR_RETRANS_MAX) + retransmit = entry->retrans_times < ADD_ADDR_RETRANS_MAX; + if (retransmit) timeout <<= entry->retrans_times; else timeout = 0; spin_unlock_bh(&msk->pm.lock); - if (entry->retrans_times == ADD_ADDR_RETRANS_MAX) + if (!retransmit) mptcp_pm_subflow_established(msk); out: From ca318e7bbb7723f57bcd9e69a2873b5884435552 Mon Sep 17 00:00:00 2001 From: Gang Yan Date: Mon, 3 Aug 2026 18:16:37 +0200 Subject: [PATCH 481/501] selftests: mptcp: join: mark tests with data corruption as failed check_transfer() compares the input and output files byte-by-byte using `cmp -l "$in" "$out" | while read ...`. Because the while-loop body runs in a subshell (the script sets neither lastpipe nor pipefail), the fail_test call inside it -- which sets the global ret/last_test_failed -- and the `return 1` both act on the subshell, not on check_transfer(). check_transfer() thus always falls through to `return 0`, and any data corruption affecting only the payload (leaving the subflow/PM counters untouched) is silently reported as PASS. Fixes: 8117dac3e7c3 ("selftests: mptcp: add invert check in check_transfer") Cc: stable@vger.kernel.org Signed-off-by: Gang Yan Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-5-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/mptcp/mptcp_join.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh index c0aeffd5cb71..7dc91fac4917 100755 --- a/tools/testing/selftests/net/mptcp/mptcp_join.sh +++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh @@ -584,7 +584,7 @@ check_transfer() mv "$tmpfile" "$out" tmpfile="" fi - cmp -l "$in" "$out" | while read -r i a b; do + while read -r i a b; do local sum=$((0${a} + 0${b})) if [ $check_invert -eq 0 ] || [ $sum -ne $((0xff)) ]; then fail_test "$what does not match (in, out):" @@ -595,7 +595,7 @@ check_transfer() else print_info "$what has inverted byte at ${i}" fi - done + done < <(cmp -l "$in" "$out") return 0 } From efc33b5102ff859bacd390a5f30112d8e0c084c0 Mon Sep 17 00:00:00 2001 From: Shardul Bankar Date: Mon, 3 Aug 2026 18:16:38 +0200 Subject: [PATCH 482/501] mptcp: pm: fix memory leak from alloc-during-teardown race mptcp_pm_destroy() empties msk->pm.anno_list and msk->pm.userspace_pm_local_addr_list under msk->pm.lock during socket teardown, dropping the lock between the two. A concurrent userspace PM genl ANNOUNCE on the same msk holds a sock reference via mptcp_token_get_sock() and, in mptcp_pm_nl_announce_doit(), calls mptcp_userspace_pm_append_new_local_addr() and mptcp_pm_announced_alloc(). Both take msk->pm.lock briefly to add to their respective lists. Because the genl handler holds a sock reference, mptcp_pm_destroy() may run on the same msk via mptcp_disconnect(), which invokes mptcp_destroy_common() without dropping the sock refcount, before the handler completes. If the lock acquisitions interleave such that mptcp_pm_destroy() empties a list first, the later alloc adds its entry to a list head that nothing else iterates for this msk, and the entry leaks. kmemleak reports both mptcp_pm_add_addr objects (from mptcp_pm_announced_alloc()) and mptcp_pm_addr_entry objects (from mptcp_userspace_pm_append_new_local_addr()) under sustained concurrent ANNOUNCE + close load against the userspace PM. Add an MPTCP_PM_DESTROYING bit in msk->pm.status, set by mptcp_pm_destroy() under pm.lock before the lists are emptied and checked under pm.lock by the alloc paths. Either the alloc takes pm.lock first, in which case its entry is on the list when mptcp_pm_destroy() frees it; or mptcp_pm_destroy() takes pm.lock first, in which case the later alloc observes the bit and refuses. Found by an MPTCP protocol-flow harness extending BRF (arXiv:2305.08782). Fixes: 9ab4807c84a4 ("mptcp: netlink: Add MPTCP_PM_CMD_ANNOUNCE") Cc: stable@vger.kernel.org Signed-off-by: Shardul Bankar Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-6-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/pm.c | 13 +++++++++++-- net/mptcp/pm_userspace.c | 4 ++++ net/mptcp/protocol.h | 7 ++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/net/mptcp/pm.c b/net/mptcp/pm.c index c71dcf887683..64a1236aabee 100644 --- a/net/mptcp/pm.c +++ b/net/mptcp/pm.c @@ -443,6 +443,9 @@ bool mptcp_pm_announced_alloc(struct mptcp_sock *msk, lockdep_assert_held(&msk->pm.lock); + if (msk->pm.status & BIT(MPTCP_PM_DESTROYING)) + return false; + add_entry = mptcp_pm_announced_lookup(msk, addr); if (add_entry) { if (WARN_ON_ONCE(mptcp_pm_is_kernel(msk))) @@ -1145,10 +1148,16 @@ void mptcp_pm_worker(struct mptcp_sock *msk) void mptcp_pm_destroy(struct mptcp_sock *msk) { + spin_lock_bh(&msk->pm.lock); + msk->pm.status |= BIT(MPTCP_PM_DESTROYING); + spin_unlock_bh(&msk->pm.lock); + mptcp_pm_free_announced_list(msk); - if (mptcp_pm_is_userspace(msk)) - mptcp_userspace_pm_free_local_addr_list(msk); + /* Free the userspace local address list unconditionally: the socket + * can be reused (mptcp_disconnect()) and re-selected to a different PM + */ + mptcp_userspace_pm_free_local_addr_list(msk); } void mptcp_pm_data_reset(struct mptcp_sock *msk) diff --git a/net/mptcp/pm_userspace.c b/net/mptcp/pm_userspace.c index 945aa5afc2dd..2203cc2d2748 100644 --- a/net/mptcp/pm_userspace.c +++ b/net/mptcp/pm_userspace.c @@ -54,6 +54,10 @@ static int mptcp_userspace_pm_append_new_local_addr(struct mptcp_sock *msk, bitmap_zero(id_bitmap, MPTCP_PM_MAX_ADDR_ID + 1); spin_lock_bh(&msk->pm.lock); + if (msk->pm.status & BIT(MPTCP_PM_DESTROYING)) { + ret = -EINVAL; + goto append_err; + } mptcp_for_each_userspace_pm_addr(msk, e) { addr_match = mptcp_addresses_equal(&e->addr, &entry->addr, true); if (addr_match && entry->addr.id == 0 && needs_id) diff --git a/net/mptcp/protocol.h b/net/mptcp/protocol.h index c13680d18994..1b80f2d6ec5a 100644 --- a/net/mptcp/protocol.h +++ b/net/mptcp/protocol.h @@ -190,9 +190,10 @@ enum mptcp_pm_status { MPTCP_PM_ESTABLISHED, MPTCP_PM_SUBFLOW_ESTABLISHED, MPTCP_PM_ALREADY_ESTABLISHED, /* persistent status, set after ESTABLISHED event */ - MPTCP_PM_MPC_ENDPOINT_ACCOUNTED /* persistent status, set after MPC local address is - * accounted int id_avail_bitmap - */ + MPTCP_PM_MPC_ENDPOINT_ACCOUNTED, /* persistent status, set after MPC local address is + * accounted int id_avail_bitmap + */ + MPTCP_PM_DESTROYING, /* To fence out PM list allocs */ }; enum mptcp_pm_type { From e00b63056fb4f261455b3e5df5268a1f8ce47a87 Mon Sep 17 00:00:00 2001 From: Wyatt Feng Date: Mon, 3 Aug 2026 18:16:39 +0200 Subject: [PATCH 483/501] mptcp: fastopen: only mark MPTFO subflows with SYN data Passive TCP Fast Open accepts a valid-cookie SYN even when it carries no data. In that case the child socket's receive queue is intentionally left empty. mptcp_fastopen_subflow_synack_set_params() set is_mptfo before checking for queued SYN data. That made data-less TFO SYNs hit a WARN and, if the warning was non-fatal, left stale MPTFO state behind. The stale flag could later trigger a state-confusion bug in check_fully_established(). Only mark the subflow as MPTFO after confirming that an SKB was queued. Return quietly when the receive queue is empty. Note that mptcp_subflow_context's is_mptfo field is now not just about subflows where the TFO was present, but about MPTFO subflow that consumed SYN data. Only having a valid cookie but not carrying data is not really "doing TFO". Fixes: 36b122baf6a8 ("mptcp: add subflow_v(4,6)_send_synack()") Cc: stable@vger.kernel.org Reported-by: Yuan Tan Reported-by: Yifan Wu Reported-by: Juefei Pu Reported-by: Zhengchuan Liang Reported-by: Xin Liu Signed-off-by: Wyatt Feng Signed-off-by: Ren Wei Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-7-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/fastopen.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/net/mptcp/fastopen.c b/net/mptcp/fastopen.c index 082c46c0f50e..f717750906ff 100644 --- a/net/mptcp/fastopen.c +++ b/net/mptcp/fastopen.c @@ -24,12 +24,13 @@ void mptcp_fastopen_subflow_synack_set_params(struct mptcp_subflow_context *subf sk = subflow->conn; tp = tcp_sk(ssk); - subflow->is_mptfo = 1; - + /* A valid TFO cookie does not guarantee SYN data. */ skb = skb_peek(&ssk->sk_receive_queue); - if (WARN_ON_ONCE(!skb)) + if (!skb) return; + subflow->is_mptfo = 1; + /* dequeue the skb from sk receive queue */ __skb_unlink(skb, &ssk->sk_receive_queue); skb_ext_reset(skb); From 41b49a8b914ec7dcb03eae93fb27f3c464078644 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Mon, 3 Aug 2026 18:16:40 +0200 Subject: [PATCH 484/501] mptcp: reclaim forward-allocated memory on RX path errors After commit 9db5b3cec4ec ("mptcp: borrow forward memory from subflow"), errors in the receive path prior to queueing skbs into the receive queue do not trigger forward-allocated memory reclaiming. Prevent forward memory from growing unboundedly in pathological drop scenarios by explicitly reclaiming memory when skbs are dropped. Fixes: 9db5b3cec4ec ("mptcp: borrow forward memory from subflow") Cc: stable@vger.kernel.org Signed-off-by: Paolo Abeni Reviewed-by: Matthieu Baerts (NGI0) Signed-off-by: Matthieu Baerts (NGI0) Link: https://patch.msgid.link/20260803-net-mptcp-misc-fixes-7-2-rc6-v2-8-b8f496d71664@kernel.org Signed-off-by: Jakub Kicinski --- net/mptcp/protocol.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/mptcp/protocol.c b/net/mptcp/protocol.c index ca644ec53eed..7c8180d8d5ef 100644 --- a/net/mptcp/protocol.c +++ b/net/mptcp/protocol.c @@ -149,6 +149,12 @@ struct sock *__mptcp_nmpc_sk(struct mptcp_sock *msk) static void mptcp_drop(struct sock *sk, struct sk_buff *skb) { + /* The skb forward memory was already transferred to sk by + * mptcp_borrow_fwdmem(), even before setting the destructor. + */ + if (!skb->destructor) + sk_mem_reclaim(sk); + sk_drops_skbadd(sk, skb); __kfree_skb(skb); } From e48e8edbef2eb824201495daa5234560f632b23c Mon Sep 17 00:00:00 2001 From: Zhiling Zou Date: Mon, 3 Aug 2026 20:15:32 +0800 Subject: [PATCH 485/501] xdp: reject clones that overrun skb_shared_info tailroom xdpf_clone() clones broadcast copies into a single page and sets frame_sz to PAGE_SIZE. __xdp_build_skb_from_frame() later treats that page like a normal XDP frame and expects the usual skb_shared_info tailroom at the end of the buffer. The current check only rejects frames whose linear xdp_frame header, headroom, and packet data exceed PAGE_SIZE. A source frame backed by a larger allocation can still satisfy that check while extending into the clone's required shared-info area. When such a clone is converted back into an skb, build_skb_around() places skb_shared_info over live packet bytes and later writes can corrupt XDP return metadata. Reject clones unless their linear area fits inside SKB_WITH_OVERHEAD(PAGE_SIZE), matching the tailroom requirement already enforced by the XDP-to-skb conversion path. Fixes: e624d4ed4aa8 ("xdp: Extend xdp_redirect_map with broadcast support") Cc: stable@vger.kernel.org Reported-by: Vega Signed-off-by: Zhiling Zou Link: https://patch.msgid.link/6b2afef5d1738763c6965e8e466eb16e43e4f956.1785757386.git.zhilinz@nebusec.ai Signed-off-by: Jakub Kicinski --- net/core/xdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/core/xdp.c b/net/core/xdp.c index 9890a30584ba..0194e69da339 100644 --- a/net/core/xdp.c +++ b/net/core/xdp.c @@ -871,7 +871,7 @@ struct xdp_frame *xdpf_clone(struct xdp_frame *xdpf) headroom = xdpf->headroom + sizeof(*xdpf); totalsize = headroom + xdpf->len; - if (unlikely(totalsize > PAGE_SIZE)) + if (unlikely(totalsize > SKB_WITH_OVERHEAD(PAGE_SIZE))) return NULL; page = dev_alloc_page(); if (!page) From 7bca91d63341274e857f4aeaad54d229405e93dc Mon Sep 17 00:00:00 2001 From: chanyoung Date: Tue, 4 Aug 2026 14:28:35 +0900 Subject: [PATCH 486/501] tls: don't leave a full plaintext sk_msg ring unpushed When the copy path in tls_sw_sendmsg_locked() adds the fragment that fills the plaintext sk_msg ring, it does not set full_record, so the record is left full and unpushed. A later splice() then adds to an already full ring: sk_msg_page_add() has no fullness check of its own, so sg.end wraps onto sg.start and the ring appears empty. Fragments added after that overwrite live entries, and sg.size no longer matches what is reachable between sg.start and sg.end, so pushing the record runs the scatterwalk off the end of the scatterlist. An unprivileged user can trigger this on a loopback TCP socket with the "tls" ULP attached: BUG: kernel NULL pointer dereference, address: 0000000000000008 RIP: 0010:memcpy_from_scatterwalk+0x32/0xc0 Call Trace: skcipher_walk_next+0x1d1/0x2c0 gcm_encrypt_aesni_avx+0x1e9/0x220 bpf_exec_tx_verdict+0x3bb/0x860 tls_sw_sendmsg+0xa1a/0xca0 __sys_sendto+0x1da/0x1f0 Set full_record in the copy path when the ring becomes full, and push a record that is already full on entry to the sendmsg loop. Suggested-by: Sabrina Dubroca Fixes: fe1e81d4f73b ("tls/sw: Support MSG_SPLICE_PAGES") Cc: stable@vger.kernel.org Signed-off-by: chanyoung Link: https://patch.msgid.link/20260804052837.49015-2-ppoo1220@gmail.com Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index d4afc90fd796..d2e399be8ef6 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -832,6 +832,14 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, if (!sk_stream_memory_free(sk)) goto wait_for_sndbuf; + /* open record may be full if we couldn't push it in the last sendmsg call */ + if (sk_msg_full(msg_pl)) { + full_record = true; + sk_msg_trim(sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + goto copied; + } + alloc_encrypted: ret = tls_alloc_encrypted_msg(sk, required_size); if (ret) { @@ -921,6 +929,12 @@ static int tls_sw_sendmsg_locked(struct sock *sk, struct msghdr *msg, msg_pl, try_to_copy); if (ret < 0) goto trim_sgl; + + if (sk_msg_full(msg_pl)) { + full_record = true; + sk_msg_trim(sk, msg_en, + msg_pl->sg.size + prot->overhead_size); + } } /* Open records defined only if successfully copied, otherwise From 3834e079d67feda5fd57d1ea8d4185d316997335 Mon Sep 17 00:00:00 2001 From: chanyoung Date: Tue, 4 Aug 2026 14:28:36 +0900 Subject: [PATCH 487/501] selftests: tls: add a test for splicing onto a full plaintext record Splicing onto a plaintext sk_msg ring that is already full used to wrap the ring and make the kernel oops in the scatterwalk once the record was pushed. Only the copy path leaves the ring full without pushing it, so splice until the ring is one fragment short, add the last fragment with a one-byte MSG_MORE send, and splice once more before pushing the record. CONFIG_MAX_SKB_FRAGS is 17..45, so that last fragment follows between 16 and 44 splices; sweep that range to trigger the bug on any build. Signed-off-by: chanyoung Link: https://patch.msgid.link/20260804052837.49015-3-ppoo1220@gmail.com Signed-off-by: Jakub Kicinski --- tools/testing/selftests/net/tls.c | 37 +++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index cbdd3ea28b99..3d6f553eaf97 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -835,6 +835,43 @@ TEST_F(tls, send_and_splice) EXPECT_EQ(memcmp(mem_send, mem_recv, send_len), 0); } +TEST_F(tls, splice_onto_full_record) +{ + char mem_send[4608]; + char mem_recv[4608]; + int frag_len = 100; + int nfrags, i, off; + int p[2]; + + memrnd(mem_send, sizeof(mem_send)); + ASSERT_GE(pipe(p), 0); + + for (nfrags = 16; nfrags <= 44; nfrags++) { + for (i = 0, off = 0; i < nfrags; i++, off += frag_len) { + EXPECT_EQ(write(p[1], mem_send + off, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + } + + EXPECT_EQ(send(self->fd, mem_send + off, 1, MSG_MORE), 1); + off++; + + EXPECT_EQ(write(p[1], mem_send + off, frag_len), frag_len); + EXPECT_EQ(splice(p[0], NULL, self->fd, NULL, frag_len, + SPLICE_F_MORE), frag_len); + off += frag_len; + + EXPECT_EQ(send(self->fd, mem_send + off, 1, 0), 1); + off++; + + EXPECT_EQ(recv(self->cfd, mem_recv, off, MSG_WAITALL), off); + EXPECT_EQ(memcmp(mem_send, mem_recv, off), 0); + } + + close(p[0]); + close(p[1]); +} + TEST_F(tls, splice_to_pipe) { int send_len = TLS_PAYLOAD_MAX_LEN; From 1c8629651cb54f7b51db8fc0b1a9944e4a4b0f5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?= Date: Tue, 4 Aug 2026 12:55:28 +0000 Subject: [PATCH 488/501] tls: rx: restore msg_iter before TLS 1.3 optimistic retry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tls_decrypt_sg() advances msg->msg_iter when it maps user pages for the optimistic TLS 1.3 zero-copy path. If the decrypted record turns out not to be unpadded application data, tls_decrypt_sw() retries into a kernel skb, but leaves the iterator advanced. The subsequent copy from the skb then writes decrypted bytes again at a later point in the caller iovecs while recvmsg() reports only the post-retry length. A TLS peer can trigger this after the receiver enables TLS_RX_EXPECT_NO_PAD. Revert the iterator by the number of bytes consumed by the optimistic mapping before retrying without zero-copy. Add a selftest which sends a TLS 1.3 control record with TLS_RX_EXPECT_NO_PAD enabled and verifies that recvmsg() does not overwrite later iovecs beyond the returned length. Fixes: ce61327ce989 ("tls: rx: support optimistic decrypt to user buffer with TLS 1.3") Cc: stable@vger.kernel.org Signed-off-by: Jérémy Jean Link: https://patch.msgid.link/20260804125528.2139928-1-Jeremy.Jean@oss.cyber.gouv.fr Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 2 ++ tools/testing/selftests/net/tls.c | 57 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index d2e399be8ef6..133ed7c89402 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -1456,6 +1456,8 @@ tls_decrypt_sw(struct sock *sk, struct tls_context *tls_ctx, /* If opportunistic TLS 1.3 ZC failed retry without ZC */ if (unlikely(darg->zc && prot->version == TLS_1_3_VERSION && darg->tail != TLS_RECORD_TYPE_DATA)) { + iov_iter_revert(&msg->msg_iter, strp_msg(darg->skb)->full_len - + prot->overhead_size); darg->zc = false; if (!darg->tail) TLS_INC_STATS(sock_net(sk), LINUX_MIB_TLSRXNOPADVIOL); diff --git a/tools/testing/selftests/net/tls.c b/tools/testing/selftests/net/tls.c index 3d6f553eaf97..a0e72e402748 100644 --- a/tools/testing/selftests/net/tls.c +++ b/tools/testing/selftests/net/tls.c @@ -1839,6 +1839,63 @@ TEST_F(tls, recv_efault) } #define TLS_RECORD_TYPE_HANDSHAKE 0x16 + +TEST_F(tls_basic, recvmsg_nopad_retry_iov) +{ + char payload[32]; + char first_iov[sizeof(payload)]; + char later_iov[sizeof(payload) * 2]; + char expected_later_iov[sizeof(later_iov)]; + char cbuf[CMSG_SPACE(sizeof(char))]; + struct tls_crypto_info_keys tls13; + struct iovec iov[] = { + { .iov_base = first_iov, .iov_len = sizeof(first_iov) }, + { .iov_base = later_iov, .iov_len = sizeof(later_iov) }, + }; + struct msghdr msg = { + .msg_iov = iov, + .msg_iovlen = ARRAY_SIZE(iov), + .msg_control = cbuf, + .msg_controllen = sizeof(cbuf), + }; + int one = 1; + int ret; + int i; + + if (self->notls) + SKIP(return, "no TLS support"); + + tls_crypto_info_init(TLS_1_3_VERSION, TLS_CIPHER_AES_GCM_128, + &tls13, 0); + + ret = setsockopt(self->fd, SOL_TLS, TLS_TX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX, &tls13, tls13.len); + ASSERT_EQ(ret, 0); + + ret = setsockopt(self->cfd, SOL_TLS, TLS_RX_EXPECT_NO_PAD, + &one, sizeof(one)); + ASSERT_EQ(ret, 0); + + for (i = 0; i < sizeof(payload); i++) + payload[i] = 0x40 + i; + memset(first_iov, 0xa5, sizeof(first_iov)); + memset(later_iov, 0x5a, sizeof(later_iov)); + memset(expected_later_iov, 0x5a, sizeof(expected_later_iov)); + + /* A control record forces optimistic TLS 1.3 RX to retry. */ + ret = tls_send_cmsg(self->fd, TLS_RECORD_TYPE_HANDSHAKE, + payload, sizeof(payload), 0); + ASSERT_EQ(ret, sizeof(payload)); + + ret = recvmsg(self->cfd, &msg, 0); + ASSERT_EQ(ret, sizeof(payload)); + EXPECT_EQ(memcmp(first_iov, payload, sizeof(payload)), 0); + EXPECT_EQ(memcmp(later_iov, expected_later_iov, + sizeof(later_iov)), 0); +} + /* key_update, length 1, update_not_requested */ static const char key_update_msg[] = "\x18\x00\x00\x01\x00"; static void tls_send_keyupdate(struct __test_metadata *_metadata, int fd) From 6b69f2ef10cdb018c0b127a7cab88e590bbddba4 Mon Sep 17 00:00:00 2001 From: Ahmad Byagowi Date: Tue, 4 Aug 2026 14:07:51 -0700 Subject: [PATCH 489/501] ptp: ocp: Fix board ID over-read The EEPROM board ID is a fixed 13-byte field and is not guaranteed to contain a NUL terminator. Passing it directly to devlink_info_version_fixed_put() treats it as a C string and may read beyond the field. Format at most OCP_BOARD_ID_LEN bytes into the existing local buffer before reporting the ID. Use a precision limit because the snprintf() output size alone does not bound the source string scan. Fixes: 0cfcdd1ebcfe ("ptp: ocp: add nvmem interface for accessing eeprom") Cc: stable@vger.kernel.org Signed-off-by: Ahmad Byagowi Reviewed-by: Vadim Fedorenko Link: https://patch.msgid.link/20260804210751.48248-1-ahmadexp@gmail.com Signed-off-by: Jakub Kicinski --- drivers/ptp/ptp_ocp.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 35e911f1ad78..3d26ec1f7b9e 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -2177,9 +2177,11 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, if (err) return err; + snprintf(buf, sizeof(buf), "%.*s", OCP_BOARD_ID_LEN, + (const char *)bp->board_id); err = devlink_info_version_fixed_put(req, DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, - bp->board_id); + buf); if (err) return err; From bfec39ff1484b4e9f7d93bc4580fdb634bbc7d19 Mon Sep 17 00:00:00 2001 From: Bhargava Marreddy Date: Wed, 5 Aug 2026 15:10:22 +0530 Subject: [PATCH 490/501] bnge: Fix resource leak in bnge_init_nic() error path If bnge_init_chip() fails, bnge_init_nic() jumps to err_free_ring_grps and returns immediately, skipping cleanup for RX ring pair buffers. Remove the early return so execution falls through to err_free_rx_ring_pair_bufs to properly free resources on error. Fixes: 23df6aebf803 ("bng_en: Allocate stat contexts") Signed-off-by: Bhargava Marreddy Reviewed-by: Dharmender Garg Reviewed-by: Rajashekar Hudumula Link: https://patch.msgid.link/20260805094022.15487-1-bhargava.marreddy@broadcom.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/broadcom/bnge/bnge_netdev.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c index 6f7ef506d4e1..ac4c93e5b634 100644 --- a/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c +++ b/drivers/net/ethernet/broadcom/bnge/bnge_netdev.c @@ -2768,8 +2768,6 @@ static int bnge_init_nic(struct bnge_net *bn) err_free_ring_grps: bnge_free_ring_grps(bn); - return rc; - err_free_rx_ring_pair_bufs: bnge_free_rx_ring_pair_bufs(bn); return rc; From 3b9a324e646d3657a8d9806dfbfe4f3e4066e882 Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:27 +0800 Subject: [PATCH 491/501] net: remove CAP_SYS_RAWIO zero-padding in dev_validate_header dev_validate_header() reads dev->hard_header_len directly when zero-padding short link layer headers for CAP_SYS_RAWIO holders: if (capable(CAP_SYS_RAWIO)) { memset(ll_header + len, 0, dev->hard_header_len - len); return true; } Packet send paths call dev_validate_header() on skbs whose headroom was allocated from an earlier hard_header_len read. If the device is reconfigured so that dev->hard_header_len increases before validation, the memset writes past the reserved buffer, an out-of-bounds write. This out-of-bounds write is masked in some SOCK_RAW paths today because the same concurrent increase can first make skb_push() exceed the reserved headroom and trigger skb_under_panic(). Remove the zero-padding branch before making those hard_header_len reads consistent, so the snapshot fixes do not turn a loud panic into a silent overwrite. This path is only reached for variable length L2 protocols, where len < hard_header_len but len >= min_header_len. No remaining in-tree variable length L2 protocol implements header_ops->validate, and the CAP_SYS_RAWIO bypass that zero-pads and accepts short headers has no real value beyond allowing testing of intentionally malformed input. Drop the CAP_SYS_RAWIO branch. The remaining reads of dev->hard_header_len in dev_validate_header() are comparisons only and have no memory safety impact. Suggested-by: Willem de Bruijn Fixes: 2793a23aacbd ("net: validate variable length ll headers") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-2-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9981d637f8b5..9a770eb823ce 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3531,11 +3531,6 @@ static inline bool dev_validate_header(const struct net_device *dev, if (len < dev->min_header_len) return false; - if (capable(CAP_SYS_RAWIO)) { - memset(ll_header + len, 0, dev->hard_header_len - len); - return true; - } - if (dev->header_ops && dev->header_ops->validate) return dev->header_ops->validate(ll_header, len); From 03390aa32e669cc4ecd7d34108e2e1afc13d689d Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:28 +0800 Subject: [PATCH 492/501] packet: use consistent hard_header_len in non-ring send paths packet_snd() reads dev->hard_header_len multiple times while allocating and constructing an skb. Device reconfiguration can change this value concurrently, for example through bonding device type changes. For SOCK_RAW, packet_snd() can save a larger value in reserve and later allocate headroom using a smaller value. Moving skb->data back by reserve then places it before skb->head, and the following copy from userspace can attempt an out-of-bounds write. packet_sendmsg_spkt() has the same issue because it calculates its reservation and header offset from separate reads before dropping the RCU read lock to allocate the skb. Add LL_RESERVED_SPACE_EX() for callers that already saved a header length. Read hard_header_len once in packet_snd() and use it for allocation and construction. In packet_sendmsg_spkt(), preserve the allocation-time value through the device lookup retry. The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here. Fixes: b84bbaf7a6c8 ("packet: in packet_snd start writing at link layer allocation") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-3-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- include/linux/netdevice.h | 6 ++++-- net/packet/af_packet.c | 26 ++++++++++++++++---------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 9a770eb823ce..8840b126979f 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -300,9 +300,11 @@ struct hh_cache { * We could use other alignment values, but we must maintain the * relationship HH alignment <= LL alignment. */ -#define LL_RESERVED_SPACE(dev) \ - ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom)) \ +#define LL_RESERVED_SPACE_EX(dev, hlen) \ + ((((hlen) + READ_ONCE((dev)->needed_headroom)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) +#define LL_RESERVED_SPACE(dev) \ + LL_RESERVED_SPACE_EX(dev, (dev)->hard_header_len) #define LL_RESERVED_SPACE_EXTRA(dev,extra) \ ((((dev)->hard_header_len + READ_ONCE((dev)->needed_headroom) + (extra)) \ & ~(HH_DATA_MOD - 1)) + HH_DATA_MOD) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index 0e1355be89f6..b7af45c809e4 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -1966,8 +1966,9 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, struct net_device *dev; struct sockcm_cookie sockc; __be16 proto = 0; - int err; + int hard_header_len; int extra_len = 0; + int err; /* * Get and verify the address. @@ -2010,14 +2011,18 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, extra_len = 4; /* We're doing our own CRC */ } + /* Keep the allocation-time header length across retry. */ + if (!skb) + hard_header_len = READ_ONCE(dev->hard_header_len); + err = -EMSGSIZE; - if (len > dev->mtu + dev->hard_header_len + VLAN_HLEN + extra_len) + if (len > dev->mtu + hard_header_len + VLAN_HLEN + extra_len) goto out_unlock; if (!skb) { - size_t reserved = LL_RESERVED_SPACE(dev); + size_t reserved = LL_RESERVED_SPACE_EX(dev, hard_header_len); int tlen = dev->needed_tailroom; - unsigned int hhlen = dev->header_ops ? dev->hard_header_len : 0; + unsigned int hhlen = dev->header_ops ? hard_header_len : 0; rcu_read_unlock(); skb = sock_wmalloc(sk, len + reserved + tlen, 0, GFP_KERNEL); @@ -2047,7 +2052,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, err = -EINVAL; goto out_unlock; } - if (len > (dev->mtu + dev->hard_header_len + extra_len) && + if (len > (dev->mtu + hard_header_len + extra_len) && !packet_extra_vlan_len_allowed(dev, skb)) { err = -EMSGSIZE; goto out_unlock; @@ -2969,7 +2974,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) int offset = 0; struct packet_sock *po = pkt_sk(sk); int vnet_hdr_sz = READ_ONCE(po->vnet_hdr_sz); - int hlen, tlen, linear; + int hard_header_len, hlen, tlen, linear; int extra_len = 0; /* @@ -3009,8 +3014,9 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_unlock; } + hard_header_len = READ_ONCE(dev->hard_header_len); if (sock->type == SOCK_RAW) - reserve = dev->hard_header_len; + reserve = hard_header_len; if (vnet_hdr_sz) { err = packet_snd_vnet_parse(msg, &len, &vnet_hdr, vnet_hdr_sz); if (err) @@ -3031,10 +3037,10 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) goto out_unlock; err = -ENOBUFS; - hlen = LL_RESERVED_SPACE(dev); + hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len); tlen = dev->needed_tailroom; linear = __virtio16_to_cpu(vio_le(), vnet_hdr.hdr_len); - linear = max(linear, min_t(int, len, dev->hard_header_len)); + linear = max(linear, min_t(int, len, hard_header_len)); skb = packet_alloc_skb(sk, hlen + tlen, hlen, len, linear, msg->msg_flags & MSG_DONTWAIT, &err); if (skb == NULL) @@ -3050,7 +3056,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) } else if (reserve) { skb_reserve(skb, -reserve); if (len < reserve + sizeof(struct ipv6hdr) && - dev->min_header_len != dev->hard_header_len) + dev->min_header_len != hard_header_len) skb_reset_network_header(skb); } From 21b5953e7494c16a42e6cd8cf110e18d13ae4a6b Mon Sep 17 00:00:00 2001 From: Qihang Tang Date: Wed, 5 Aug 2026 20:57:29 +0800 Subject: [PATCH 493/501] packet: use consistent hard_header_len in TX_RING send path tpacket_snd() reads dev->hard_header_len independently for skb allocation and header construction in tpacket_fill_skb(). Concurrent netdevice reconfiguration can therefore make the reserved headroom smaller than the amount later pushed, or make copylen - hard_header_len negative. Snapshot hard_header_len once before processing ring frames and use it for the frame limit, headroom allocation, copy length, and skb construction. Pass the snapshot to tpacket_fill_skb(). The separate SOCK_DGRAM consistency problem between hard_header_len and header_ops->create is not addressed here. Fixes: 69e3c75f4d54 ("net: TX_RING and packet mmap") Cc: stable@vger.kernel.org Signed-off-by: Qihang Tang Reviewed-by: Willem de Bruijn Link: https://patch.msgid.link/20260805125729.19220-4-q.h.hack.winter@gmail.com Signed-off-by: Jakub Kicinski --- net/packet/af_packet.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index b7af45c809e4..435756877aba 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -2587,6 +2587,7 @@ static int packet_snd_vnet_parse(struct msghdr *msg, size_t *len, static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, void *frame, struct net_device *dev, void *data, int tp_len, __be16 proto, unsigned char *addr, int hlen, int copylen, + int hard_header_len, const struct sockcm_cookie *sockc) { union tpacket_uhdr ph; @@ -2618,8 +2619,8 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, } else if (copylen) { int hdrlen = min_t(int, copylen, tp_len); - skb_push(skb, dev->hard_header_len); - skb_put(skb, copylen - dev->hard_header_len); + skb_push(skb, hard_header_len); + skb_put(skb, copylen - hard_header_len); err = skb_store_bits(skb, 0, data, hdrlen); if (unlikely(err)) return err; @@ -2750,7 +2751,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) void *data; int len_sum = 0; int status = TP_STATUS_AVAILABLE; - int hlen, tlen, copylen = 0; + int hard_header_len, hlen, tlen, copylen = 0; long timeo; mutex_lock(&po->pg_vec_lock); @@ -2797,8 +2798,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto out_put; } + hard_header_len = READ_ONCE(dev->hard_header_len); if (po->sk.sk_socket->type == SOCK_RAW) - reserve = dev->hard_header_len; + reserve = hard_header_len; size_max = po->tx_ring.frame_size - (po->tp_hdrlen - sizeof(struct sockaddr_ll)); @@ -2835,7 +2837,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto tpacket_error; status = TP_STATUS_SEND_REQUEST; - hlen = LL_RESERVED_SPACE(dev); + hlen = LL_RESERVED_SPACE_EX(dev, hard_header_len); tlen = dev->needed_tailroom; if (vnet_hdr_sz) { data += vnet_hdr_sz; @@ -2853,10 +2855,10 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) vnet_hdr.hdr_len); has_vnet_hdr = true; } - copylen = max_t(int, copylen, dev->hard_header_len); + copylen = max_t(int, copylen, hard_header_len); skb = sock_alloc_send_skb(&po->sk, hlen + tlen + sizeof(struct sockaddr_ll) + - (copylen - dev->hard_header_len), + (copylen - hard_header_len), !need_wait, &err); if (unlikely(skb == NULL)) { @@ -2866,7 +2868,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) goto out_status; } tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto, - addr, hlen, copylen, &sockc); + addr, hlen, copylen, hard_header_len, + &sockc); if (likely(tp_len >= 0) && tp_len > dev->mtu + reserve && !vnet_hdr_sz && From b1896543ce59c4258625a35cf41e23a9a1f80ea2 Mon Sep 17 00:00:00 2001 From: Alexandra Winter Date: Wed, 5 Aug 2026 15:10:43 +0200 Subject: [PATCH 494/501] s390/ism: Fix UAF of sba and ieq during ism_dev_exit() A ism interrupt handler can be active in parallel with ism_dev_exit(), accessing freed data structures. No new interrupts will be generated after unregister_ieq(). Drain ongoing interrupt handlers by free_irq(), before freeing ism data structures. Fixes: 684b89bc39ce ("s390/ism: add device driver for internal shared memory") Signed-off-by: Alexandra Winter Link: https://patch.msgid.link/20260805131043.954639-1-wintera@linux.ibm.com Signed-off-by: Jakub Kicinski --- drivers/s390/net/ism_drv.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index d99c588d3e00..242da20f27e0 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -148,13 +148,16 @@ static int unregister_sba(struct ism_dev *ism) if (ret && ret != ISM_ERROR) return -EIO; + return 0; +} + +static void ism_free_sba(struct ism_dev *ism) +{ dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ism->sba, ism->sba_dma_addr); ism->sba = NULL; ism->sba_dma_addr = 0; - - return 0; } static int unregister_ieq(struct ism_dev *ism) @@ -168,13 +171,16 @@ static int unregister_ieq(struct ism_dev *ism) if (ret && ret != ISM_ERROR) return -EIO; + return 0; +} + +static void ism_free_ieq(struct ism_dev *ism) +{ dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ism->ieq, ism->ieq_dma_addr); ism->ieq = NULL; ism->ieq_dma_addr = 0; - - return 0; } static int ism_read_local_gid(struct dibs_dev *dibs) @@ -573,6 +579,7 @@ static int ism_dev_init(struct ism_dev *ism) unreg_sba: unregister_sba(ism); + ism_free_sba(ism); free_irq: free_irq(pci_irq_vector(pdev, 0), ism); free_vectors: @@ -585,9 +592,13 @@ static void ism_dev_exit(struct ism_dev *ism) { struct pci_dev *pdev = ism->pdev; + /* ism will only generate new IRQs while ieq & sba are registered */ unregister_ieq(ism); unregister_sba(ism); + /* drain ongoing irpt handlers */ free_irq(pci_irq_vector(pdev, 0), ism); + ism_free_ieq(ism); + ism_free_sba(ism); pci_free_irq_vectors(pdev); } From d0c80dbb970439bd2eeb0e5effff8c16a5f4e1e3 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Wed, 5 Aug 2026 13:15:08 +0000 Subject: [PATCH 495/501] net/atm: fix slab-out-of-bounds read in vcc_setsockopt() vcc_setsockopt() contained an ineffective optlen check: if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) return -EINVAL; If __SO_LEVEL_MATCH(optname, level) evaluated to false (e.g. if the caller passed a mismatched level), the length check optlen != __SO_SIZE(optname) was short-circuited and bypassed. Execution then fell through to switch(optname), calling copy_from_sockptr() assuming optval contained sufficient space. Furthermore, even if level matched, a cgroup BPF setsockopt filter could shrink optlen after entry. Because copy_from_sockptr() on kernel pointers uses memcpy(), this leads to a KASAN slab-out-of-bounds read when optlen is smaller than the expected structure size. Fix this by using copy_safe_from_sockptr(), which unconditionally validates that optlen is at least the expected size before copying. Also change the local 'value' variable type from 'unsigned long' to 'int' so that SO_SETCLP matches its sizeof(int) ABI encoding on 64-bit systems. Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Reported-by: syzbot+53ecc09fb81df10ef4de@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=53ecc09fb81df10ef4de Signed-off-by: Eric Dumazet Link: https://patch.msgid.link/20260805131508.3227331-1-edumazet@google.com Signed-off-by: Jakub Kicinski --- net/atm/common.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/atm/common.c b/net/atm/common.c index c7f92405daf0..81195727fa18 100644 --- a/net/atm/common.c +++ b/net/atm/common.c @@ -710,7 +710,7 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, sockptr_t optval, unsigned int optlen) { struct atm_vcc *vcc; - unsigned long value; + int value; int error; if (__SO_LEVEL_MATCH(optname, level) && optlen != __SO_SIZE(optname)) @@ -722,8 +722,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, { struct atm_qos qos; - if (copy_from_sockptr(&qos, optval, sizeof(qos))) - return -EFAULT; + error = copy_safe_from_sockptr(&qos, sizeof(qos), optval, + optlen); + if (error) + return error; error = check_qos(&qos); if (error) return error; @@ -737,8 +739,10 @@ int vcc_setsockopt(struct socket *sock, int level, int optname, return 0; } case SO_SETCLP: - if (copy_from_sockptr(&value, optval, sizeof(value))) - return -EFAULT; + error = copy_safe_from_sockptr(&value, sizeof(value), optval, + optlen); + if (error) + return error; if (value) vcc->atm_options |= ATM_ATMOPT_CLP; else From c9158ceaf27780ef64534ad72f44ffde3f8ccc49 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 5 Aug 2026 11:18:40 -0400 Subject: [PATCH 496/501] sctp: clear control chunk transport if it is being removed sctp_make_heartbeat_ack() caches the destination transport in chunk->transport without taking a reference. When src_out_of_asoc_ok is enabled, the HEARTBEAT ACK may remain queued on control_chunk_list instead of being transmitted immediately. If the peer transport is removed while the chunk is still queued, sctp_assoc_rm_peer() drops the transport and schedules it for RCU freeing, but only clears cached transport pointers in out_chunk_list. The queued control chunk therefore retains a dangling transport pointer. Once an ASCONF_ACK clears the suppression and the queued control chunk is transmitted, SCTP dereferences the stale transport pointer, leading to a use-after-free. Fix this by also clearing chunk->transport for queued control chunks in control_chunk_list when removing the transport. Fixes: 8a07eb0a50ae ("sctp: Add ASCONF operation on the single-homed host") Reported-by: Daniele Linguaglossa Signed-off-by: Xin Long Link: https://patch.msgid.link/7e1168cb722132152a29d47e5eafaeac4a3bf6f3.1785943120.git.lucien.xin@gmail.com Signed-off-by: Jakub Kicinski --- net/sctp/associola.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/sctp/associola.c b/net/sctp/associola.c index b6ac0966420a..5b0ae616e1ff 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -573,6 +573,10 @@ void sctp_assoc_rm_peer(struct sctp_association *asoc, if (ch->transport == peer) ch->transport = NULL; + list_for_each_entry(ch, &asoc->outqueue.control_chunk_list, list) + if (ch->transport == peer) + ch->transport = NULL; + asoc->peer.transport_count--; sctp_ulpevent_notify_peer_addr_change(peer, SCTP_ADDR_REMOVED, 0); From 03a736fde464cefa0a6e10d29914c27635965ce2 Mon Sep 17 00:00:00 2001 From: Ivan Vecera Date: Wed, 5 Aug 2026 17:54:25 +0200 Subject: [PATCH 497/501] MAINTAINERS: dpll: zl3073x: replace Prathosh Satish with Min Li Replace Prathosh Satish by Min Li as the Microchip co-maintainer of the ZL3073X DPLL driver. Signed-off-by: Ivan Vecera Link: https://patch.msgid.link/20260805155425.38808-1-ivecera@redhat.com Signed-off-by: Jakub Kicinski --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a77cf599bac2..9de29cdcd8c6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -17847,7 +17847,7 @@ F: drivers/net/wireless/microchip/ MICROCHIP ZL3073X DRIVER M: Ivan Vecera -M: Prathosh Satish +M: Min Li L: netdev@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/dpll/microchip,zl30731.yaml From 8e63c9e6179ace3033dcd19991f95386e9d3130f Mon Sep 17 00:00:00 2001 From: Dragos Tatulea Date: Thu, 6 Aug 2026 11:07:58 +0300 Subject: [PATCH 498/501] net: Defer netdev KOBJ_ADD uevent until the device is published netdev_register_kobject() calls device_add(), which emits KOBJ_ADD and wakes udev, but register_netdevice() only makes the device findable by name later, in list_netdevice(). A udev worker that reacts to the uevent can therefore run against a device that no lookup can find yet. This used to be harmless because the ethtool ioctl took the rtnl_lock when looking the device up, and register_netdevice() runs under rtnl, so the worker simply blocked until registration finished. The commit in the fixes tag moved the lookup out from under rtnl for ops-locked drivers. Now there is a short window in register_netdevice() between netdev_register_kobject() until list_netdevice() when the device is not findable by name. This was reproduced with the mlx5 driver on a kernel with KASAN enabled during devlink reload: systemd-udevd's net_driver builtin gets -ENODEV from ETHTOOL_GDRVINFO, which was preventing interface renaming. Suppress the uevent in netdev_register_kobject() and emit it from register_netdevice() next to rtmsg_ifinfo(). This is the last point in register_netdevice() where no error can happen, so only fully registered devices are announced: the registration error paths never reach it, and the device_del() that unwinds them stays silent as well, leaving userspace with neither an add nor a remove. Fixes: f994752b1127 ("net: ethtool: optionally skip rtnl_lock on IOCTL path") Signed-off-by: Dragos Tatulea Reviewed-by: Shahar Shitrit Link: https://patch.msgid.link/20260806080758.2039586-2-dtatulea@nvidia.com Signed-off-by: Jakub Kicinski --- net/core/dev.c | 1 + net/core/net-sysfs.c | 14 ++++++++++++++ net/core/net-sysfs.h | 1 + 3 files changed, 16 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index 5933c5dab09e..c49d2ce51285 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -11494,6 +11494,7 @@ int register_netdevice(struct net_device *dev) * Prevent userspace races by waiting until the network * device is fully setup before sending notifications. */ + netdev_uevent_add(dev); if (!(dev->rtnl_link_ops && dev->rtnl_link_initializing)) rtmsg_ifinfo(RTM_NEWLINK, dev, ~0U, GFP_KERNEL, 0, NULL); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 0e71c9ed41e8..25546deacec8 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -2334,6 +2334,9 @@ int netdev_register_kobject(struct net_device *ndev) *groups++ = &wireless_group; #endif /* CONFIG_SYSFS */ + /* Hold back the KOBJ_ADD uevent until the device is listed. */ + dev_set_uevent_suppress(dev, 1); + error = device_add(dev); if (error) return error; @@ -2349,6 +2352,17 @@ int netdev_register_kobject(struct net_device *ndev) return error; } +/* Announce a fully registered device to userspace. This pairs with the uevent + * suppression from netdev_register_kobject(). + */ +void netdev_uevent_add(struct net_device *ndev) +{ + struct device *dev = &ndev->dev; + + dev_set_uevent_suppress(dev, 0); + kobject_uevent(&dev->kobj, KOBJ_ADD); +} + /* Change owner for sysfs entries when moving network devices across network * namespaces owned by different user namespaces. */ diff --git a/net/core/net-sysfs.h b/net/core/net-sysfs.h index 38e2e3ffd0bd..2f41a4dee866 100644 --- a/net/core/net-sysfs.h +++ b/net/core/net-sysfs.h @@ -4,6 +4,7 @@ int __init netdev_kobject_init(void); int netdev_register_kobject(struct net_device *); +void netdev_uevent_add(struct net_device *dev); void netdev_unregister_kobject(struct net_device *); int net_rx_queue_update_kobjects(struct net_device *, int old_num, int new_num); int netdev_queue_update_kobjects(struct net_device *net, From d31c14e56a4f01f0689c29c0bcbcd45f65b4a473 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Wed, 5 Aug 2026 19:28:21 -0700 Subject: [PATCH 499/501] net: avoid theoretical races with ref drain Technically, it's illegal to take a ref on a netdev just because we have a pointer on which we already hold a ref, with no other protection. This is because our simple per-cpu refcount implementation cannot atomically read the count. Let's make sure we cancel outstanding work and never queue more work for a device we know is dead. This way taking a ref on a dev we know is on the netdev_work_list is always going to be safe. Jiangshan Yi reports that the issues is caught by ref tracker infra leading to a warning: WARNING: lib/ref_tracker.c:322 at ref_tracker_free WARNING: lib/ref_tracker.c:246 at ref_tracker_dir_exit Reported-by: Jiangshan Yi Link: https://lore.kernel.org/20260731035135.3917308-2-yijiangshan@kylinos.cn Fixes: 12c765be84d2 ("net: turn the rx_mode work into a generic netdev_work facility") Link: https://patch.msgid.link/20260806022821.2079945-1-kuba@kernel.org Signed-off-by: Jakub Kicinski --- net/core/dev.c | 1 + net/core/dev.h | 1 + net/core/netdev_work.c | 16 ++++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/net/core/dev.c b/net/core/dev.c index c49d2ce51285..af260ff5462a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -12436,6 +12436,7 @@ void unregister_netdevice_many_notify(struct list_head *head, dev_tcx_uninstall(dev); dev_xdp_uninstall(dev); dev_memory_provider_uninstall(dev); + netdev_work_cancel_all(dev); netdev_unlock_ops(dev); bpf_dev_bound_netdev_unregister(dev); diff --git a/net/core/dev.h b/net/core/dev.h index 5d0b0305d3ba..b757faead4d1 100644 --- a/net/core/dev.h +++ b/net/core/dev.h @@ -179,6 +179,7 @@ enum netdev_work_core { void __netdev_work_core_sched(struct net_device *dev, unsigned long event); unsigned long __netdev_work_core_cancel(struct net_device *dev, unsigned long mask); +void netdev_work_cancel_all(struct net_device *dev); void __dev_notify_flags(struct net_device *dev, unsigned int old_flags, unsigned int gchanges, u32 portid, diff --git a/net/core/netdev_work.c b/net/core/netdev_work.c index 3109fae132ad..e721a06d58df 100644 --- a/net/core/netdev_work.c +++ b/net/core/netdev_work.c @@ -31,6 +31,10 @@ static void netdev_work_enqueue(struct net_device *dev, unsigned long events, return; spin_lock_bh(&netdev_work_lock); + if (!dev_isalive(dev)) { + spin_unlock_bh(&netdev_work_lock); + return; + } if (list_empty(&dev->work_node)) { list_add_tail(&dev->work_node, &netdev_work_list); netdev_hold(dev, &dev->work_tracker, GFP_ATOMIC); @@ -61,6 +65,18 @@ netdev_work_dequeue(struct net_device *dev, unsigned long *pending, return events; } +void netdev_work_cancel_all(struct net_device *dev) +{ + spin_lock_bh(&netdev_work_lock); + dev->work_pending = 0; + dev->work_core_pending = 0; + if (!list_empty(&dev->work_node)) { + list_del_init(&dev->work_node); + netdev_put(dev, &dev->work_tracker); + } + spin_unlock_bh(&netdev_work_lock); +} + void netdev_work_sched(struct net_device *dev, unsigned long events) { netdev_work_enqueue(dev, events, 0); From af0e5cdd031f4f4a8f6d4160bfbda4f36872b0ed Mon Sep 17 00:00:00 2001 From: Maximilian Immanuel Brandtner Date: Wed, 5 Aug 2026 08:22:48 +0200 Subject: [PATCH 500/501] tls: don't abort the connection on signal-interrupted sends When a signal interrupts a blocking send, tls_tx_records() treats the resulting -ERESTARTSYS as a transmission failure and marks the socket errored via tls_err_abort() with the raw error code. Later syscalls return the kernel-internal errno 512 (ERESTARTSYS) to userspace, as the signal it stems from is no longer pending during syscall exit and thus never translated. An interrupted send is not a connection error: the partially sent record stays queued and is resent later. Interrupt error codes are therefore excluded from the abort in the same way as -EAGAIN. Fixes: b341ca51d267 ("tls: Fix tls_sw_sendmsg error handling") Signed-off-by: Maximilian Immanuel Brandtner Link: https://patch.msgid.link/20260805063109.1772314-1-maxbr@linux.ibm.com Signed-off-by: Jakub Kicinski --- net/tls/tls_sw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 133ed7c89402..62d46736e24b 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -458,7 +458,7 @@ int tls_tx_records(struct sock *sk, int flags) } tx_err: - if (rc < 0 && rc != -EAGAIN) + if (rc < 0 && rc != -EAGAIN && rc != -EINTR && rc != -ERESTARTSYS) tls_err_abort(sk, rc); return rc; From b0ce5fd9fabe7c79463cf4602217d4dfeff5b1fd Mon Sep 17 00:00:00 2001 From: Philipp David Date: Tue, 4 Aug 2026 15:22:03 -0700 Subject: [PATCH 501/501] igc: fix netdev not re-attached after resume if interface is down __igc_resume() calls netif_device_attach() only inside the netif_running() branch, so an interface that was down during suspend is never re-attached on resume. It then stays in the not-present state that __igc_shutdown() set via netif_device_detach(): ethtool reports ENODEV and every attempt to bring the interface up fails the netif_device_present() check in __dev_open() with -ENODEV, silently, since __igc_resume() returns 0. Only reloading the driver recovers the device. This is easy to hit in practice because NetworkManager brings managed interfaces down before sleep unless Wake-on-LAN is configured, making the adapter unusable after every suspend/resume cycle with WoL disabled. Re-attach the netdev on every successful resume, as igb and e1000e do. Fixes: 6f31d6b643a3 ("igc: Refactor runtime power management flow") Cc: stable@vger.kernel.org Signed-off-by: Philipp David Reviewed-by: Aleksandr Loktionov Reviewed-by: Dima Ruinskiy Signed-off-by: Tony Nguyen Link: https://patch.msgid.link/20260804222205.1580328-11-anthony.l.nguyen@intel.com Signed-off-by: Jakub Kicinski --- drivers/net/ethernet/intel/igc/igc_main.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 16fcc8cf6191..39043d8ca1bf 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -7585,11 +7585,13 @@ static int __igc_resume(struct device *dev, bool rpm) err = __igc_open(netdev, true); if (!rpm) rtnl_unlock(); - if (!err) - netif_device_attach(netdev); + if (err) + return err; } - return err; + netif_device_attach(netdev); + + return 0; } static int igc_resume(struct device *dev)