From: Sasha Levin Date: Mon, 29 Jan 2024 00:17:58 +0000 (-0500) Subject: Fixes for 5.15 X-Git-Tag: v6.1.76~24 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a9189c7a7e90d2934a62f8fa5dfa86481c4ebe47;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- diff --git a/queue-5.15/arm-dts-qcom-sdx55-fix-pdc-interrupt-cells.patch b/queue-5.15/arm-dts-qcom-sdx55-fix-pdc-interrupt-cells.patch new file mode 100644 index 00000000000..e6085625845 --- /dev/null +++ b/queue-5.15/arm-dts-qcom-sdx55-fix-pdc-interrupt-cells.patch @@ -0,0 +1,41 @@ +From 5ac28110ac4e6591b9a3ecbb3d2bdae6cff04bf1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 Dec 2023 18:31:29 +0100 +Subject: ARM: dts: qcom: sdx55: fix pdc '#interrupt-cells' + +From: Johan Hovold + +[ Upstream commit cc25bd06c16aa582596a058d375b2e3133f79b93 ] + +The Qualcomm PDC interrupt controller binding expects two cells in +interrupt specifiers. + +Fixes: 9d038b2e62de ("ARM: dts: qcom: Add SDX55 platform and MTP board support") +Cc: stable@vger.kernel.org # 5.12 +Cc: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Reviewed-by: Konrad Dybcio +Reviewed-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20231213173131.29436-2-johan+linaro@kernel.org +Signed-off-by: Bjorn Andersson +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/qcom-sdx55.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/qcom-sdx55.dtsi b/arch/arm/boot/dts/qcom-sdx55.dtsi +index 4e7381d0e205..e8b4cbd39413 100644 +--- a/arch/arm/boot/dts/qcom-sdx55.dtsi ++++ b/arch/arm/boot/dts/qcom-sdx55.dtsi +@@ -447,7 +447,7 @@ pdc: interrupt-controller@b210000 { + compatible = "qcom,sdx55-pdc", "qcom,pdc"; + reg = <0x0b210000 0x30000>; + qcom,pdc-ranges = <0 179 52>; +- #interrupt-cells = <3>; ++ #interrupt-cells = <2>; + interrupt-parent = <&intc>; + interrupt-controller; + }; +-- +2.43.0 + diff --git a/queue-5.15/arm-dts-qcom-sdx55-fix-usb-dp-dm-hs-phy-interrupts.patch b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-dp-dm-hs-phy-interrupts.patch new file mode 100644 index 00000000000..fbe456cf8e9 --- /dev/null +++ b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-dp-dm-hs-phy-interrupts.patch @@ -0,0 +1,55 @@ +From 3a22928cb52748862acefd72fc12c1938ea91a7f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 Dec 2023 18:31:30 +0100 +Subject: ARM: dts: qcom: sdx55: fix USB DP/DM HS PHY interrupts + +From: Johan Hovold + +[ Upstream commit de95f139394a5ed82270f005bc441d2e7c1e51b7 ] + +The USB DP/DM HS PHY interrupts need to be provided by the PDC interrupt +controller in order to be able to wake the system up from low-power +states and to be able to detect disconnect events, which requires +triggering on falling edges. + +A recent commit updated the trigger type but failed to change the +interrupt provider as required. This leads to the current Linux driver +failing to probe instead of printing an error during suspend and USB +wakeup not working as intended. + +Fixes: d0ec3c4c11c3 ("ARM: dts: qcom: sdx55: fix USB wakeup interrupt types") +Fixes: fea4b41022f3 ("ARM: dts: qcom: sdx55: Add USB3 and PHY support") +Cc: stable@vger.kernel.org # 5.12 +Cc: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Reviewed-by: Konrad Dybcio +Reviewed-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20231213173131.29436-3-johan+linaro@kernel.org +Signed-off-by: Bjorn Andersson +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/qcom-sdx55.dtsi | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-sdx55.dtsi b/arch/arm/boot/dts/qcom-sdx55.dtsi +index e8b4cbd39413..9c0d2432f105 100644 +--- a/arch/arm/boot/dts/qcom-sdx55.dtsi ++++ b/arch/arm/boot/dts/qcom-sdx55.dtsi +@@ -420,10 +420,10 @@ usb: usb@a6f8800 { + <&gcc GCC_USB30_MASTER_CLK>; + assigned-clock-rates = <19200000>, <200000000>; + +- interrupts = , +- , +- , +- ; ++ interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, ++ <&intc GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>, ++ <&pdc 11 IRQ_TYPE_EDGE_BOTH>, ++ <&pdc 10 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "hs_phy_irq", "ss_phy_irq", + "dm_hs_phy_irq", "dp_hs_phy_irq"; + +-- +2.43.0 + diff --git a/queue-5.15/arm-dts-qcom-sdx55-fix-usb-ss-wakeup.patch b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-ss-wakeup.patch new file mode 100644 index 00000000000..22ccc8466ba --- /dev/null +++ b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-ss-wakeup.patch @@ -0,0 +1,42 @@ +From d1538f20b12a7550139abe78d64ab65a7624b39d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 13 Dec 2023 18:31:31 +0100 +Subject: ARM: dts: qcom: sdx55: fix USB SS wakeup + +From: Johan Hovold + +[ Upstream commit 710dd03464e4ab5b3d329768388b165d61958577 ] + +The USB SS PHY interrupt needs to be provided by the PDC interrupt +controller in order to be able to wake the system up from low-power +states. + +Fixes: fea4b41022f3 ("ARM: dts: qcom: sdx55: Add USB3 and PHY support") +Cc: stable@vger.kernel.org # 5.12 +Cc: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Reviewed-by: Konrad Dybcio +Reviewed-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20231213173131.29436-4-johan+linaro@kernel.org +Signed-off-by: Bjorn Andersson +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/qcom-sdx55.dtsi | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/arm/boot/dts/qcom-sdx55.dtsi b/arch/arm/boot/dts/qcom-sdx55.dtsi +index 9c0d2432f105..67159e8608ed 100644 +--- a/arch/arm/boot/dts/qcom-sdx55.dtsi ++++ b/arch/arm/boot/dts/qcom-sdx55.dtsi +@@ -421,7 +421,7 @@ usb: usb@a6f8800 { + assigned-clock-rates = <19200000>, <200000000>; + + interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>, +- <&intc GIC_SPI 198 IRQ_TYPE_LEVEL_HIGH>, ++ <&pdc 51 IRQ_TYPE_LEVEL_HIGH>, + <&pdc 11 IRQ_TYPE_EDGE_BOTH>, + <&pdc 10 IRQ_TYPE_EDGE_BOTH>; + interrupt-names = "hs_phy_irq", "ss_phy_irq", +-- +2.43.0 + diff --git a/queue-5.15/arm-dts-qcom-sdx55-fix-usb-wakeup-interrupt-types.patch b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-wakeup-interrupt-types.patch new file mode 100644 index 00000000000..5dbb7df0443 --- /dev/null +++ b/queue-5.15/arm-dts-qcom-sdx55-fix-usb-wakeup-interrupt-types.patch @@ -0,0 +1,42 @@ +From 81ab42f89ee8b35f438d2e21b318d12459f2b32e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Nov 2023 17:43:21 +0100 +Subject: ARM: dts: qcom: sdx55: fix USB wakeup interrupt types + +From: Johan Hovold + +[ Upstream commit d0ec3c4c11c3b30e1f2d344973b2a7bf0f986734 ] + +The DP/DM wakeup interrupts are edge triggered and which edge to trigger +on depends on use-case and whether a Low speed or Full/High speed device +is connected. + +Fixes: fea4b41022f3 ("ARM: dts: qcom: sdx55: Add USB3 and PHY support") +Cc: stable@vger.kernel.org # 5.12 +Cc: Manivannan Sadhasivam +Signed-off-by: Johan Hovold +Link: https://lore.kernel.org/r/20231120164331.8116-2-johan+linaro@kernel.org +Signed-off-by: Bjorn Andersson +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/qcom-sdx55.dtsi | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/boot/dts/qcom-sdx55.dtsi b/arch/arm/boot/dts/qcom-sdx55.dtsi +index 73fa0ef6b69e..4e7381d0e205 100644 +--- a/arch/arm/boot/dts/qcom-sdx55.dtsi ++++ b/arch/arm/boot/dts/qcom-sdx55.dtsi +@@ -422,8 +422,8 @@ usb: usb@a6f8800 { + + interrupts = , + , +- , +- ; ++ , ++ ; + interrupt-names = "hs_phy_irq", "ss_phy_irq", + "dm_hs_phy_irq", "dp_hs_phy_irq"; + +-- +2.43.0 + diff --git a/queue-5.15/arm-dts-samsung-exynos4210-i9100-unconditionally-ena.patch b/queue-5.15/arm-dts-samsung-exynos4210-i9100-unconditionally-ena.patch new file mode 100644 index 00000000000..2384f995fa3 --- /dev/null +++ b/queue-5.15/arm-dts-samsung-exynos4210-i9100-unconditionally-ena.patch @@ -0,0 +1,47 @@ +From d4e1dc126dce97720f2aed7d74115256f8db1475 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 6 Dec 2023 23:15:54 +0100 +Subject: ARM: dts: samsung: exynos4210-i9100: Unconditionally enable LDO12 + +From: Paul Cercueil + +[ Upstream commit 84228d5e29dbc7a6be51e221000e1d122125826c ] + +The kernel hangs for a good 12 seconds without any info being printed to +dmesg, very early in the boot process, if this regulator is not enabled. + +Force-enable it to work around this issue, until we know more about the +underlying problem. + +Signed-off-by: Paul Cercueil +Fixes: 8620cc2f99b7 ("ARM: dts: exynos: Add devicetree file for the Galaxy S2") +Cc: stable@vger.kernel.org # v5.8+ +Link: https://lore.kernel.org/r/20231206221556.15348-2-paul@crapouillou.net +Signed-off-by: Krzysztof Kozlowski +Signed-off-by: Sasha Levin +--- + arch/arm/boot/dts/exynos4210-i9100.dts | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/arch/arm/boot/dts/exynos4210-i9100.dts b/arch/arm/boot/dts/exynos4210-i9100.dts +index 93880bdbcad9..8232d843bfe0 100644 +--- a/arch/arm/boot/dts/exynos4210-i9100.dts ++++ b/arch/arm/boot/dts/exynos4210-i9100.dts +@@ -520,6 +520,14 @@ vtcam_reg: LDO12 { + regulator-name = "VT_CAM_1.8V"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; ++ ++ /* ++ * Force-enable this regulator; otherwise the ++ * kernel hangs very early in the boot process ++ * for about 12 seconds, without apparent ++ * reason. ++ */ ++ regulator-always-on; + }; + + vcclcd_reg: LDO13 { +-- +2.43.0 + diff --git a/queue-5.15/btrfs-add-definition-for-extent_tree_v2.patch b/queue-5.15/btrfs-add-definition-for-extent_tree_v2.patch new file mode 100644 index 00000000000..11771d5ef11 --- /dev/null +++ b/queue-5.15/btrfs-add-definition-for-extent_tree_v2.patch @@ -0,0 +1,107 @@ +From 2b5a8427f3f452bd6c02e781473eea4d4dd3f65d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 15 Dec 2021 15:39:58 -0500 +Subject: btrfs: add definition for EXTENT_TREE_V2 + +From: Josef Bacik + +[ Upstream commit 2c7d2a230237e7c43fa067d695937b7e484bb92a ] + +This adds the initial definition of the EXTENT_TREE_V2 incompat feature +flag. This also hides the support behind CONFIG_BTRFS_DEBUG. + +THIS IS A IN DEVELOPMENT FORMAT CHANGE, DO NOT USE UNLESS YOU ARE A +DEVELOPER OR A TESTER. + +The format is in flux and will be added in stages, any fs will need to +be re-made between updates to the format. + +Signed-off-by: Josef Bacik +Signed-off-by: David Sterba +Stable-dep-of: 7081929ab257 ("btrfs: don't abort filesystem when attempting to snapshot deleted subvolume") +Signed-off-by: Sasha Levin +--- + fs/btrfs/ctree.h | 21 +++++++++++++++++++++ + fs/btrfs/sysfs.c | 5 ++++- + include/uapi/linux/btrfs.h | 1 + + 3 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h +index 7905f178efa3..17ebcf19b444 100644 +--- a/fs/btrfs/ctree.h ++++ b/fs/btrfs/ctree.h +@@ -282,6 +282,26 @@ struct btrfs_super_block { + #define BTRFS_FEATURE_COMPAT_RO_SAFE_SET 0ULL + #define BTRFS_FEATURE_COMPAT_RO_SAFE_CLEAR 0ULL + ++#ifdef CONFIG_BTRFS_DEBUG ++/* ++ * Extent tree v2 supported only with CONFIG_BTRFS_DEBUG ++ */ ++#define BTRFS_FEATURE_INCOMPAT_SUPP \ ++ (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ ++ BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ ++ BTRFS_FEATURE_INCOMPAT_MIXED_GROUPS | \ ++ BTRFS_FEATURE_INCOMPAT_BIG_METADATA | \ ++ BTRFS_FEATURE_INCOMPAT_COMPRESS_LZO | \ ++ BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD | \ ++ BTRFS_FEATURE_INCOMPAT_RAID56 | \ ++ BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF | \ ++ BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA | \ ++ BTRFS_FEATURE_INCOMPAT_NO_HOLES | \ ++ BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \ ++ BTRFS_FEATURE_INCOMPAT_RAID1C34 | \ ++ BTRFS_FEATURE_INCOMPAT_ZONED | \ ++ BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2) ++#else + #define BTRFS_FEATURE_INCOMPAT_SUPP \ + (BTRFS_FEATURE_INCOMPAT_MIXED_BACKREF | \ + BTRFS_FEATURE_INCOMPAT_DEFAULT_SUBVOL | \ +@@ -296,6 +316,7 @@ struct btrfs_super_block { + BTRFS_FEATURE_INCOMPAT_METADATA_UUID | \ + BTRFS_FEATURE_INCOMPAT_RAID1C34 | \ + BTRFS_FEATURE_INCOMPAT_ZONED) ++#endif + + #define BTRFS_FEATURE_INCOMPAT_SAFE_SET \ + (BTRFS_FEATURE_INCOMPAT_EXTENDED_IREF) +diff --git a/fs/btrfs/sysfs.c b/fs/btrfs/sysfs.c +index 899dda6eb835..93a9dfbc8d13 100644 +--- a/fs/btrfs/sysfs.c ++++ b/fs/btrfs/sysfs.c +@@ -283,9 +283,11 @@ BTRFS_FEAT_ATTR_INCOMPAT(no_holes, NO_HOLES); + BTRFS_FEAT_ATTR_INCOMPAT(metadata_uuid, METADATA_UUID); + BTRFS_FEAT_ATTR_COMPAT_RO(free_space_tree, FREE_SPACE_TREE); + BTRFS_FEAT_ATTR_INCOMPAT(raid1c34, RAID1C34); +-/* Remove once support for zoned allocation is feature complete */ + #ifdef CONFIG_BTRFS_DEBUG ++/* Remove once support for zoned allocation is feature complete */ + BTRFS_FEAT_ATTR_INCOMPAT(zoned, ZONED); ++/* Remove once support for extent tree v2 is feature complete */ ++BTRFS_FEAT_ATTR_INCOMPAT(extent_tree_v2, EXTENT_TREE_V2); + #endif + #ifdef CONFIG_FS_VERITY + BTRFS_FEAT_ATTR_COMPAT_RO(verity, VERITY); +@@ -314,6 +316,7 @@ static struct attribute *btrfs_supported_feature_attrs[] = { + BTRFS_FEAT_ATTR_PTR(raid1c34), + #ifdef CONFIG_BTRFS_DEBUG + BTRFS_FEAT_ATTR_PTR(zoned), ++ BTRFS_FEAT_ATTR_PTR(extent_tree_v2), + #endif + #ifdef CONFIG_FS_VERITY + BTRFS_FEAT_ATTR_PTR(verity), +diff --git a/include/uapi/linux/btrfs.h b/include/uapi/linux/btrfs.h +index 4693b6ba30a1..493b10b9fedd 100644 +--- a/include/uapi/linux/btrfs.h ++++ b/include/uapi/linux/btrfs.h +@@ -310,6 +310,7 @@ struct btrfs_ioctl_fs_info_args { + #define BTRFS_FEATURE_INCOMPAT_METADATA_UUID (1ULL << 10) + #define BTRFS_FEATURE_INCOMPAT_RAID1C34 (1ULL << 11) + #define BTRFS_FEATURE_INCOMPAT_ZONED (1ULL << 12) ++#define BTRFS_FEATURE_INCOMPAT_EXTENT_TREE_V2 (1ULL << 13) + + struct btrfs_ioctl_feature_flags { + __u64 compat_flags; +-- +2.43.0 + diff --git a/queue-5.15/btrfs-don-t-abort-filesystem-when-attempting-to-snap.patch b/queue-5.15/btrfs-don-t-abort-filesystem-when-attempting-to-snap.patch new file mode 100644 index 00000000000..9028d8807e5 --- /dev/null +++ b/queue-5.15/btrfs-don-t-abort-filesystem-when-attempting-to-snap.patch @@ -0,0 +1,103 @@ +From 89c9aa2ee17347ff34f87961e2c1287bd351d3cf Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 4 Jan 2024 11:48:46 -0800 +Subject: btrfs: don't abort filesystem when attempting to snapshot deleted + subvolume + +From: Omar Sandoval + +[ Upstream commit 7081929ab2572920e94d70be3d332e5c9f97095a ] + +If the source file descriptor to the snapshot ioctl refers to a deleted +subvolume, we get the following abort: + + BTRFS: Transaction aborted (error -2) + WARNING: CPU: 0 PID: 833 at fs/btrfs/transaction.c:1875 create_pending_snapshot+0x1040/0x1190 [btrfs] + Modules linked in: pata_acpi btrfs ata_piix libata scsi_mod virtio_net blake2b_generic xor net_failover virtio_rng failover scsi_common rng_core raid6_pq libcrc32c + CPU: 0 PID: 833 Comm: t_snapshot_dele Not tainted 6.7.0-rc6 #2 + Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-1.fc39 04/01/2014 + RIP: 0010:create_pending_snapshot+0x1040/0x1190 [btrfs] + RSP: 0018:ffffa09c01337af8 EFLAGS: 00010282 + RAX: 0000000000000000 RBX: ffff9982053e7c78 RCX: 0000000000000027 + RDX: ffff99827dc20848 RSI: 0000000000000001 RDI: ffff99827dc20840 + RBP: ffffa09c01337c00 R08: 0000000000000000 R09: ffffa09c01337998 + R10: 0000000000000003 R11: ffffffffb96da248 R12: fffffffffffffffe + R13: ffff99820535bb28 R14: ffff99820b7bd000 R15: ffff99820381ea80 + FS: 00007fe20aadabc0(0000) GS:ffff99827dc00000(0000) knlGS:0000000000000000 + CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 + CR2: 0000559a120b502f CR3: 00000000055b6000 CR4: 00000000000006f0 + Call Trace: + + ? create_pending_snapshot+0x1040/0x1190 [btrfs] + ? __warn+0x81/0x130 + ? create_pending_snapshot+0x1040/0x1190 [btrfs] + ? report_bug+0x171/0x1a0 + ? handle_bug+0x3a/0x70 + ? exc_invalid_op+0x17/0x70 + ? asm_exc_invalid_op+0x1a/0x20 + ? create_pending_snapshot+0x1040/0x1190 [btrfs] + ? create_pending_snapshot+0x1040/0x1190 [btrfs] + create_pending_snapshots+0x92/0xc0 [btrfs] + btrfs_commit_transaction+0x66b/0xf40 [btrfs] + btrfs_mksubvol+0x301/0x4d0 [btrfs] + btrfs_mksnapshot+0x80/0xb0 [btrfs] + __btrfs_ioctl_snap_create+0x1c2/0x1d0 [btrfs] + btrfs_ioctl_snap_create_v2+0xc4/0x150 [btrfs] + btrfs_ioctl+0x8a6/0x2650 [btrfs] + ? kmem_cache_free+0x22/0x340 + ? do_sys_openat2+0x97/0xe0 + __x64_sys_ioctl+0x97/0xd0 + do_syscall_64+0x46/0xf0 + entry_SYSCALL_64_after_hwframe+0x6e/0x76 + RIP: 0033:0x7fe20abe83af + RSP: 002b:00007ffe6eff1360 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 + RAX: ffffffffffffffda RBX: 0000000000000004 RCX: 00007fe20abe83af + RDX: 00007ffe6eff23c0 RSI: 0000000050009417 RDI: 0000000000000003 + RBP: 0000000000000003 R08: 0000000000000000 R09: 00007fe20ad16cd0 + R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 + R13: 00007ffe6eff13c0 R14: 00007fe20ad45000 R15: 0000559a120b6d58 + + ---[ end trace 0000000000000000 ]--- + BTRFS: error (device vdc: state A) in create_pending_snapshot:1875: errno=-2 No such entry + BTRFS info (device vdc: state EA): forced readonly + BTRFS warning (device vdc: state EA): Skipping commit of aborted transaction. + BTRFS: error (device vdc: state EA) in cleanup_transaction:2055: errno=-2 No such entry + +This happens because create_pending_snapshot() initializes the new root +item as a copy of the source root item. This includes the refs field, +which is 0 for a deleted subvolume. The call to btrfs_insert_root() +therefore inserts a root with refs == 0. btrfs_get_new_fs_root() then +finds the root and returns -ENOENT if refs == 0, which causes +create_pending_snapshot() to abort. + +Fix it by checking the source root's refs before attempting the +snapshot, but after locking subvol_sem to avoid racing with deletion. + +CC: stable@vger.kernel.org # 4.14+ +Reviewed-by: Sweet Tea Dorminy +Reviewed-by: Anand Jain +Signed-off-by: Omar Sandoval +Reviewed-by: David Sterba +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/ioctl.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c +index 37f4370a779d..768f58a53a94 100644 +--- a/fs/btrfs/ioctl.c ++++ b/fs/btrfs/ioctl.c +@@ -725,6 +725,9 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir, + struct btrfs_trans_handle *trans; + int ret; + ++ if (btrfs_root_refs(&root->root_item) == 0) ++ return -ENOENT; ++ + if (btrfs_root_refs(&root->root_item) == 0) + return -ENOENT; + +-- +2.43.0 + diff --git a/queue-5.15/bus-mhi-host-add-alignment-check-for-event-ring-read.patch b/queue-5.15/bus-mhi-host-add-alignment-check-for-event-ring-read.patch new file mode 100644 index 00000000000..ba237f282b9 --- /dev/null +++ b/queue-5.15/bus-mhi-host-add-alignment-check-for-event-ring-read.patch @@ -0,0 +1,46 @@ +From 1dd0380d4d6f0ebd7690cfcda6bc9fe0ce3f179a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 31 Oct 2023 15:21:05 +0530 +Subject: bus: mhi: host: Add alignment check for event ring read pointer + +From: Krishna chaitanya chundru + +[ Upstream commit eff9704f5332a13b08fbdbe0f84059c9e7051d5f ] + +Though we do check the event ring read pointer by "is_valid_ring_ptr" +to make sure it is in the buffer range, but there is another risk the +pointer may be not aligned. Since we are expecting event ring elements +are 128 bits(struct mhi_ring_element) aligned, an unaligned read pointer +could lead to multiple issues like DoS or ring buffer memory corruption. + +So add a alignment check for event ring read pointer. + +Fixes: ec32332df764 ("bus: mhi: core: Sanity check values from remote device before use") +cc: stable@vger.kernel.org +Signed-off-by: Krishna chaitanya chundru +Reviewed-by: Jeffrey Hugo +Reviewed-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20231031-alignment_check-v2-1-1441db7c5efd@quicinc.com +Signed-off-by: Manivannan Sadhasivam +Signed-off-by: Sasha Levin +--- + drivers/bus/mhi/host/main.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c +index 23bd1db94558..1cb7c60594f1 100644 +--- a/drivers/bus/mhi/host/main.c ++++ b/drivers/bus/mhi/host/main.c +@@ -267,7 +267,8 @@ static void mhi_del_ring_element(struct mhi_controller *mhi_cntrl, + + static bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr) + { +- return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len; ++ return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len && ++ !(addr & (sizeof(struct mhi_ring_element) - 1)); + } + + int mhi_destroy_device(struct device *dev, void *data) +-- +2.43.0 + diff --git a/queue-5.15/bus-mhi-host-rename-struct-mhi_tre-to-struct-mhi_rin.patch b/queue-5.15/bus-mhi-host-rename-struct-mhi_tre-to-struct-mhi_rin.patch new file mode 100644 index 00000000000..1476dc532fd --- /dev/null +++ b/queue-5.15/bus-mhi-host-rename-struct-mhi_tre-to-struct-mhi_rin.patch @@ -0,0 +1,162 @@ +From 09068651c6304d67704c3e713d38c722e2c0f1e4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 1 Mar 2022 21:33:06 +0530 +Subject: bus: mhi: host: Rename "struct mhi_tre" to "struct mhi_ring_element" + +From: Manivannan Sadhasivam + +[ Upstream commit 84f5f31f110e5e8cd5581e8efdbf8c369e962eb9 ] + +Structure "struct mhi_tre" is representing a generic MHI ring element and +not specifically a Transfer Ring Element (TRE). Fix the naming. + +Reviewed-by: Alex Elder +Signed-off-by: Manivannan Sadhasivam +Link: https://lore.kernel.org/r/20220301160308.107452-9-manivannan.sadhasivam@linaro.org +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: eff9704f5332 ("bus: mhi: host: Add alignment check for event ring read pointer") +Signed-off-by: Sasha Levin +--- + drivers/bus/mhi/host/init.c | 6 +++--- + drivers/bus/mhi/host/internal.h | 2 +- + drivers/bus/mhi/host/main.c | 20 ++++++++++---------- + 3 files changed, 14 insertions(+), 14 deletions(-) + +diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c +index 829d4fca7ddc..7ccc5cd27fd0 100644 +--- a/drivers/bus/mhi/host/init.c ++++ b/drivers/bus/mhi/host/init.c +@@ -338,7 +338,7 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) + er_ctxt->msivec = cpu_to_le32(mhi_event->irq); + mhi_event->db_cfg.db_mode = true; + +- ring->el_size = sizeof(struct mhi_tre); ++ ring->el_size = sizeof(struct mhi_ring_element); + ring->len = ring->el_size * ring->elements; + ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); + if (ret) +@@ -370,7 +370,7 @@ int mhi_init_dev_ctxt(struct mhi_controller *mhi_cntrl) + for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { + struct mhi_ring *ring = &mhi_cmd->ring; + +- ring->el_size = sizeof(struct mhi_tre); ++ ring->el_size = sizeof(struct mhi_ring_element); + ring->elements = CMD_EL_PER_RING; + ring->len = ring->el_size * ring->elements; + ret = mhi_alloc_aligned_ring(mhi_cntrl, ring, ring->len); +@@ -613,7 +613,7 @@ int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, + + buf_ring = &mhi_chan->buf_ring; + tre_ring = &mhi_chan->tre_ring; +- tre_ring->el_size = sizeof(struct mhi_tre); ++ tre_ring->el_size = sizeof(struct mhi_ring_element); + tre_ring->len = tre_ring->el_size * tre_ring->elements; + chan_ctxt = &mhi_cntrl->mhi_ctxt->chan_ctxt[mhi_chan->chan]; + ret = mhi_alloc_aligned_ring(mhi_cntrl, tre_ring, tre_ring->len); +diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h +index 71f181402be9..df65bb17fdba 100644 +--- a/drivers/bus/mhi/host/internal.h ++++ b/drivers/bus/mhi/host/internal.h +@@ -257,7 +257,7 @@ struct mhi_ctxt { + dma_addr_t cmd_ctxt_addr; + }; + +-struct mhi_tre { ++struct mhi_ring_element { + __le64 ptr; + __le32 dword[2]; + }; +diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c +index 0d35acb2f7c0..23bd1db94558 100644 +--- a/drivers/bus/mhi/host/main.c ++++ b/drivers/bus/mhi/host/main.c +@@ -556,7 +556,7 @@ static void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, + } + + static int parse_xfer_event(struct mhi_controller *mhi_cntrl, +- struct mhi_tre *event, ++ struct mhi_ring_element *event, + struct mhi_chan *mhi_chan) + { + struct mhi_ring *buf_ring, *tre_ring; +@@ -592,7 +592,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, + case MHI_EV_CC_EOT: + { + dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); +- struct mhi_tre *local_rp, *ev_tre; ++ struct mhi_ring_element *local_rp, *ev_tre; + void *dev_rp; + struct mhi_buf_info *buf_info; + u16 xfer_len; +@@ -695,7 +695,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, + } + + static int parse_rsc_event(struct mhi_controller *mhi_cntrl, +- struct mhi_tre *event, ++ struct mhi_ring_element *event, + struct mhi_chan *mhi_chan) + { + struct mhi_ring *buf_ring, *tre_ring; +@@ -759,12 +759,12 @@ static int parse_rsc_event(struct mhi_controller *mhi_cntrl, + } + + static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, +- struct mhi_tre *tre) ++ struct mhi_ring_element *tre) + { + dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre); + struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; + struct mhi_ring *mhi_ring = &cmd_ring->ring; +- struct mhi_tre *cmd_pkt; ++ struct mhi_ring_element *cmd_pkt; + struct mhi_chan *mhi_chan; + u32 chan; + +@@ -797,7 +797,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, + struct mhi_event *mhi_event, + u32 event_quota) + { +- struct mhi_tre *dev_rp, *local_rp; ++ struct mhi_ring_element *dev_rp, *local_rp; + struct mhi_ring *ev_ring = &mhi_event->ring; + struct mhi_event_ctxt *er_ctxt = + &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; +@@ -967,7 +967,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, + struct mhi_event *mhi_event, + u32 event_quota) + { +- struct mhi_tre *dev_rp, *local_rp; ++ struct mhi_ring_element *dev_rp, *local_rp; + struct mhi_ring *ev_ring = &mhi_event->ring; + struct mhi_event_ctxt *er_ctxt = + &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; +@@ -1188,7 +1188,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, + struct mhi_buf_info *info, enum mhi_flags flags) + { + struct mhi_ring *buf_ring, *tre_ring; +- struct mhi_tre *mhi_tre; ++ struct mhi_ring_element *mhi_tre; + struct mhi_buf_info *buf_info; + int eot, eob, chain, bei; + int ret; +@@ -1266,7 +1266,7 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl, + struct mhi_chan *mhi_chan, + enum mhi_cmd_type cmd) + { +- struct mhi_tre *cmd_tre = NULL; ++ struct mhi_ring_element *cmd_tre = NULL; + struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; + struct mhi_ring *ring = &mhi_cmd->ring; + struct device *dev = &mhi_cntrl->mhi_dev->dev; +@@ -1524,7 +1524,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, + int chan) + + { +- struct mhi_tre *dev_rp, *local_rp; ++ struct mhi_ring_element *dev_rp, *local_rp; + struct mhi_ring *ev_ring; + struct device *dev = &mhi_cntrl->mhi_dev->dev; + unsigned long flags; +-- +2.43.0 + diff --git a/queue-5.15/cpufreq-intel_pstate-drop-redundant-intel_pstate_get.patch b/queue-5.15/cpufreq-intel_pstate-drop-redundant-intel_pstate_get.patch new file mode 100644 index 00000000000..39ff4a141cf --- /dev/null +++ b/queue-5.15/cpufreq-intel_pstate-drop-redundant-intel_pstate_get.patch @@ -0,0 +1,64 @@ +From 2af81d33442247fee488492a78d15042d9e78bb1 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 10 Dec 2021 17:12:18 +0100 +Subject: cpufreq: intel_pstate: Drop redundant intel_pstate_get_hwp_cap() call + +From: Rafael J. Wysocki + +[ Upstream commit 458b03f81afbb27143c45d47c2d8f418b2ba2407 ] + +It is not necessary to call intel_pstate_get_hwp_cap() from +intel_pstate_update_perf_limits(), because it gets called from +intel_pstate_verify_cpu_policy() which is either invoked directly +right before intel_pstate_update_perf_limits(), in +intel_cpufreq_verify_policy() in the passive mode, or called +from driver callbacks in a sequence that causes it to be followed +by an immediate intel_pstate_update_perf_limits(). + +Namely, in the active mode intel_cpufreq_verify_policy() is called +by intel_pstate_verify_policy() which is the ->verify() callback +routine of intel_pstate and gets called by the cpufreq core right +before intel_pstate_set_policy(), which is the driver's ->setoplicy() +callback routine, where intel_pstate_update_perf_limits() is called. + +Signed-off-by: Rafael J. Wysocki +Stable-dep-of: 192cdb1c907f ("cpufreq: intel_pstate: Refine computation of P-state for given frequency") +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 20 ++++++++------------ + 1 file changed, 8 insertions(+), 12 deletions(-) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index 736cb2cfcbb0..f2a94afb6eec 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -2332,18 +2332,14 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, + * HWP needs some special consideration, because HWP_REQUEST uses + * abstract values to represent performance rather than pure ratios. + */ +- if (hwp_active) { +- intel_pstate_get_hwp_cap(cpu); +- +- if (cpu->pstate.scaling != perf_ctl_scaling) { +- int scaling = cpu->pstate.scaling; +- int freq; +- +- freq = max_policy_perf * perf_ctl_scaling; +- max_policy_perf = DIV_ROUND_UP(freq, scaling); +- freq = min_policy_perf * perf_ctl_scaling; +- min_policy_perf = DIV_ROUND_UP(freq, scaling); +- } ++ if (hwp_active && cpu->pstate.scaling != perf_ctl_scaling) { ++ int scaling = cpu->pstate.scaling; ++ int freq; ++ ++ freq = max_policy_perf * perf_ctl_scaling; ++ max_policy_perf = DIV_ROUND_UP(freq, scaling); ++ freq = min_policy_perf * perf_ctl_scaling; ++ min_policy_perf = DIV_ROUND_UP(freq, scaling); + } + + pr_debug("cpu:%d min_policy_perf:%d max_policy_perf:%d\n", +-- +2.43.0 + diff --git a/queue-5.15/cpufreq-intel_pstate-refine-computation-of-p-state-f.patch b/queue-5.15/cpufreq-intel_pstate-refine-computation-of-p-state-f.patch new file mode 100644 index 00000000000..fbc53ba9d98 --- /dev/null +++ b/queue-5.15/cpufreq-intel_pstate-refine-computation-of-p-state-f.patch @@ -0,0 +1,144 @@ +From 1eeaa96a9ad602510eb2271ad6cb7f9255c8ab7d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Jan 2024 15:18:11 +0100 +Subject: cpufreq: intel_pstate: Refine computation of P-state for given + frequency + +From: Rafael J. Wysocki + +[ Upstream commit 192cdb1c907fd8df2d764c5bb17496e415e59391 ] + +On systems using HWP, if a given frequency is equal to the maximum turbo +frequency or the maximum non-turbo frequency, the HWP performance level +corresponding to it is already known and can be used directly without +any computation. + +Accordingly, adjust the code to use the known HWP performance levels in +the cases mentioned above. + +This also helps to avoid limiting CPU capacity artificially in some +cases when the BIOS produces the HWP_CAP numbers using a different +E-core-to-P-core performance scaling factor than expected by the kernel. + +Fixes: f5c8cf2a4992 ("cpufreq: intel_pstate: hybrid: Use known scaling factor for P-cores") +Cc: 6.1+ # 6.1+ +Tested-by: Srinivas Pandruvada +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Sasha Levin +--- + drivers/cpufreq/intel_pstate.c | 55 +++++++++++++++++++++------------- + 1 file changed, 34 insertions(+), 21 deletions(-) + +diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c +index f2a94afb6eec..dd5f4eee9ffb 100644 +--- a/drivers/cpufreq/intel_pstate.c ++++ b/drivers/cpufreq/intel_pstate.c +@@ -490,6 +490,30 @@ static inline int intel_pstate_get_cppc_guaranteed(int cpu) + } + #endif /* CONFIG_ACPI_CPPC_LIB */ + ++static int intel_pstate_freq_to_hwp_rel(struct cpudata *cpu, int freq, ++ unsigned int relation) ++{ ++ if (freq == cpu->pstate.turbo_freq) ++ return cpu->pstate.turbo_pstate; ++ ++ if (freq == cpu->pstate.max_freq) ++ return cpu->pstate.max_pstate; ++ ++ switch (relation) { ++ case CPUFREQ_RELATION_H: ++ return freq / cpu->pstate.scaling; ++ case CPUFREQ_RELATION_C: ++ return DIV_ROUND_CLOSEST(freq, cpu->pstate.scaling); ++ } ++ ++ return DIV_ROUND_UP(freq, cpu->pstate.scaling); ++} ++ ++static int intel_pstate_freq_to_hwp(struct cpudata *cpu, int freq) ++{ ++ return intel_pstate_freq_to_hwp_rel(cpu, freq, CPUFREQ_RELATION_L); ++} ++ + /** + * intel_pstate_hybrid_hwp_adjust - Calibrate HWP performance levels. + * @cpu: Target CPU. +@@ -507,6 +531,7 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; + int perf_ctl_turbo = pstate_funcs.get_turbo(cpu->cpu); + int scaling = cpu->pstate.scaling; ++ int freq; + + pr_debug("CPU%d: perf_ctl_max_phys = %d\n", cpu->cpu, perf_ctl_max_phys); + pr_debug("CPU%d: perf_ctl_turbo = %d\n", cpu->cpu, perf_ctl_turbo); +@@ -520,16 +545,16 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) + cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, + perf_ctl_scaling); + +- cpu->pstate.max_pstate_physical = +- DIV_ROUND_UP(perf_ctl_max_phys * perf_ctl_scaling, +- scaling); ++ freq = perf_ctl_max_phys * perf_ctl_scaling; ++ cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); + +- cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; ++ freq = cpu->pstate.min_pstate * perf_ctl_scaling; ++ cpu->pstate.min_freq = freq; + /* + * Cast the min P-state value retrieved via pstate_funcs.get_min() to + * the effective range of HWP performance levels. + */ +- cpu->pstate.min_pstate = DIV_ROUND_UP(cpu->pstate.min_freq, scaling); ++ cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); + } + + static inline void update_turbo_state(void) +@@ -2333,13 +2358,12 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, + * abstract values to represent performance rather than pure ratios. + */ + if (hwp_active && cpu->pstate.scaling != perf_ctl_scaling) { +- int scaling = cpu->pstate.scaling; + int freq; + + freq = max_policy_perf * perf_ctl_scaling; +- max_policy_perf = DIV_ROUND_UP(freq, scaling); ++ max_policy_perf = intel_pstate_freq_to_hwp(cpu, freq); + freq = min_policy_perf * perf_ctl_scaling; +- min_policy_perf = DIV_ROUND_UP(freq, scaling); ++ min_policy_perf = intel_pstate_freq_to_hwp(cpu, freq); + } + + pr_debug("cpu:%d min_policy_perf:%d max_policy_perf:%d\n", +@@ -2708,18 +2732,7 @@ static int intel_cpufreq_target(struct cpufreq_policy *policy, + + cpufreq_freq_transition_begin(policy, &freqs); + +- switch (relation) { +- case CPUFREQ_RELATION_L: +- target_pstate = DIV_ROUND_UP(freqs.new, cpu->pstate.scaling); +- break; +- case CPUFREQ_RELATION_H: +- target_pstate = freqs.new / cpu->pstate.scaling; +- break; +- default: +- target_pstate = DIV_ROUND_CLOSEST(freqs.new, cpu->pstate.scaling); +- break; +- } +- ++ target_pstate = intel_pstate_freq_to_hwp_rel(cpu, freqs.new, relation); + target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, false); + + freqs.new = target_pstate * cpu->pstate.scaling; +@@ -2737,7 +2750,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, + + update_turbo_state(); + +- target_pstate = DIV_ROUND_UP(target_freq, cpu->pstate.scaling); ++ target_pstate = intel_pstate_freq_to_hwp(cpu, target_freq); + + target_pstate = intel_cpufreq_update_pstate(policy, target_pstate, true); + +-- +2.43.0 + diff --git a/queue-5.15/fs-pipe-move-check-to-pipe_has_watch_queue.patch b/queue-5.15/fs-pipe-move-check-to-pipe_has_watch_queue.patch new file mode 100644 index 00000000000..6cb7dfbce9c --- /dev/null +++ b/queue-5.15/fs-pipe-move-check-to-pipe_has_watch_queue.patch @@ -0,0 +1,96 @@ +From f96406b56380e050b931160af004c27c740bcaa9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 21 Sep 2023 09:57:53 +0200 +Subject: fs/pipe: move check to pipe_has_watch_queue() + +From: Max Kellermann + +[ Upstream commit b4bd6b4bac8edd61eb8f7b836969d12c0c6af165 ] + +This declutters the code by reducing the number of #ifdefs and makes +the watch_queue checks simpler. This has no runtime effect; the +machine code is identical. + +Signed-off-by: Max Kellermann +Message-Id: <20230921075755.1378787-2-max.kellermann@ionos.com> +Reviewed-by: David Howells +Signed-off-by: Christian Brauner +Stable-dep-of: e95aada4cb93 ("pipe: wakeup wr_wait after setting max_usage") +Signed-off-by: Sasha Levin +--- + fs/pipe.c | 12 +++--------- + include/linux/pipe_fs_i.h | 16 ++++++++++++++++ + 2 files changed, 19 insertions(+), 9 deletions(-) + +diff --git a/fs/pipe.c b/fs/pipe.c +index e08f0fe55584..1a43948c0c36 100644 +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -435,12 +435,10 @@ pipe_write(struct kiocb *iocb, struct iov_iter *from) + goto out; + } + +-#ifdef CONFIG_WATCH_QUEUE +- if (pipe->watch_queue) { ++ if (pipe_has_watch_queue(pipe)) { + ret = -EXDEV; + goto out; + } +-#endif + + /* + * If it wasn't empty we try to merge new data into +@@ -1319,10 +1317,8 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) + unsigned int nr_slots, size; + long ret = 0; + +-#ifdef CONFIG_WATCH_QUEUE +- if (pipe->watch_queue) ++ if (pipe_has_watch_queue(pipe)) + return -EBUSY; +-#endif + + size = round_pipe_size(arg); + nr_slots = size >> PAGE_SHIFT; +@@ -1374,10 +1370,8 @@ struct pipe_inode_info *get_pipe_info(struct file *file, bool for_splice) + + if (file->f_op != &pipefifo_fops || !pipe) + return NULL; +-#ifdef CONFIG_WATCH_QUEUE +- if (for_splice && pipe->watch_queue) ++ if (for_splice && pipe_has_watch_queue(pipe)) + return NULL; +-#endif + return pipe; + } + +diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h +index d15190b3e032..0613daf8997a 100644 +--- a/include/linux/pipe_fs_i.h ++++ b/include/linux/pipe_fs_i.h +@@ -124,6 +124,22 @@ struct pipe_buf_operations { + bool (*get)(struct pipe_inode_info *, struct pipe_buffer *); + }; + ++/** ++ * pipe_has_watch_queue - Check whether the pipe is a watch_queue, ++ * i.e. it was created with O_NOTIFICATION_PIPE ++ * @pipe: The pipe to check ++ * ++ * Return: true if pipe is a watch queue, false otherwise. ++ */ ++static inline bool pipe_has_watch_queue(const struct pipe_inode_info *pipe) ++{ ++#ifdef CONFIG_WATCH_QUEUE ++ return pipe->watch_queue != NULL; ++#else ++ return false; ++#endif ++} ++ + /** + * pipe_empty - Return true if the pipe is empty + * @head: The pipe ring head pointer +-- +2.43.0 + diff --git a/queue-5.15/ksmbd-fix-global-oob-in-ksmbd_nl_policy.patch b/queue-5.15/ksmbd-fix-global-oob-in-ksmbd_nl_policy.patch new file mode 100644 index 00000000000..f878d345b95 --- /dev/null +++ b/queue-5.15/ksmbd-fix-global-oob-in-ksmbd_nl_policy.patch @@ -0,0 +1,133 @@ +From 4e84346099553d37dc99bd557b1d26292cd0e0d3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 21 Jan 2024 15:35:06 +0800 +Subject: ksmbd: fix global oob in ksmbd_nl_policy + +From: Lin Ma + +[ Upstream commit ebeae8adf89d9a82359f6659b1663d09beec2faa ] + +Similar to a reported issue (check the commit b33fb5b801c6 ("net: +qualcomm: rmnet: fix global oob in rmnet_policy"), my local fuzzer finds +another global out-of-bounds read for policy ksmbd_nl_policy. See bug +trace below: + +================================================================== +BUG: KASAN: global-out-of-bounds in validate_nla lib/nlattr.c:386 [inline] +BUG: KASAN: global-out-of-bounds in __nla_validate_parse+0x24af/0x2750 lib/nlattr.c:600 +Read of size 1 at addr ffffffff8f24b100 by task syz-executor.1/62810 + +CPU: 0 PID: 62810 Comm: syz-executor.1 Tainted: G N 6.1.0 #3 +Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014 +Call Trace: + + __dump_stack lib/dump_stack.c:88 [inline] + dump_stack_lvl+0x8b/0xb3 lib/dump_stack.c:106 + print_address_description mm/kasan/report.c:284 [inline] + print_report+0x172/0x475 mm/kasan/report.c:395 + kasan_report+0xbb/0x1c0 mm/kasan/report.c:495 + validate_nla lib/nlattr.c:386 [inline] + __nla_validate_parse+0x24af/0x2750 lib/nlattr.c:600 + __nla_parse+0x3e/0x50 lib/nlattr.c:697 + __nlmsg_parse include/net/netlink.h:748 [inline] + genl_family_rcv_msg_attrs_parse.constprop.0+0x1b0/0x290 net/netlink/genetlink.c:565 + genl_family_rcv_msg_doit+0xda/0x330 net/netlink/genetlink.c:734 + genl_family_rcv_msg net/netlink/genetlink.c:833 [inline] + genl_rcv_msg+0x441/0x780 net/netlink/genetlink.c:850 + netlink_rcv_skb+0x14f/0x410 net/netlink/af_netlink.c:2540 + genl_rcv+0x24/0x40 net/netlink/genetlink.c:861 + netlink_unicast_kernel net/netlink/af_netlink.c:1319 [inline] + netlink_unicast+0x54e/0x800 net/netlink/af_netlink.c:1345 + netlink_sendmsg+0x930/0xe50 net/netlink/af_netlink.c:1921 + sock_sendmsg_nosec net/socket.c:714 [inline] + sock_sendmsg+0x154/0x190 net/socket.c:734 + ____sys_sendmsg+0x6df/0x840 net/socket.c:2482 + ___sys_sendmsg+0x110/0x1b0 net/socket.c:2536 + __sys_sendmsg+0xf3/0x1c0 net/socket.c:2565 + do_syscall_x64 arch/x86/entry/common.c:50 [inline] + do_syscall_64+0x3b/0x90 arch/x86/entry/common.c:80 + entry_SYSCALL_64_after_hwframe+0x63/0xcd +RIP: 0033:0x7fdd66a8f359 +Code: 28 00 00 00 75 05 48 83 c4 28 c3 e8 f1 19 00 00 90 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 b8 ff ff ff f7 d8 64 89 01 48 +RSP: 002b:00007fdd65e00168 EFLAGS: 00000246 ORIG_RAX: 000000000000002e +RAX: ffffffffffffffda RBX: 00007fdd66bbcf80 RCX: 00007fdd66a8f359 +RDX: 0000000000000000 RSI: 0000000020000500 RDI: 0000000000000003 +RBP: 00007fdd66ada493 R08: 0000000000000000 R09: 0000000000000000 +R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 +R13: 00007ffc84b81aff R14: 00007fdd65e00300 R15: 0000000000022000 + + +The buggy address belongs to the variable: + ksmbd_nl_policy+0x100/0xa80 + +The buggy address belongs to the physical page: +page:0000000034f47940 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1ccc4b +flags: 0x200000000001000(reserved|node=0|zone=2) +raw: 0200000000001000 ffffea00073312c8 ffffea00073312c8 0000000000000000 +raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 +page dumped because: kasan: bad access detected + +Memory state around the buggy address: + ffffffff8f24b000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + ffffffff8f24b080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +>ffffffff8f24b100: f9 f9 f9 f9 00 00 f9 f9 f9 f9 f9 f9 00 00 07 f9 + ^ + ffffffff8f24b180: f9 f9 f9 f9 00 05 f9 f9 f9 f9 f9 f9 00 00 00 05 + ffffffff8f24b200: f9 f9 f9 f9 00 00 03 f9 f9 f9 f9 f9 00 00 04 f9 +================================================================== + +To fix it, add a placeholder named __KSMBD_EVENT_MAX and let +KSMBD_EVENT_MAX to be its original value - 1 according to what other +netlink families do. Also change two sites that refer the +KSMBD_EVENT_MAX to correct value. + +Cc: stable@vger.kernel.org +Fixes: 0626e6641f6b ("cifsd: add server handler for central processing and tranport layers") +Signed-off-by: Lin Ma +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/ksmbd/ksmbd_netlink.h | 3 ++- + fs/ksmbd/transport_ipc.c | 4 ++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/fs/ksmbd/ksmbd_netlink.h b/fs/ksmbd/ksmbd_netlink.h +index 821ed8e3cbee..ecffcb8a1557 100644 +--- a/fs/ksmbd/ksmbd_netlink.h ++++ b/fs/ksmbd/ksmbd_netlink.h +@@ -304,7 +304,8 @@ enum ksmbd_event { + KSMBD_EVENT_SPNEGO_AUTHEN_REQUEST, + KSMBD_EVENT_SPNEGO_AUTHEN_RESPONSE = 15, + +- KSMBD_EVENT_MAX ++ __KSMBD_EVENT_MAX, ++ KSMBD_EVENT_MAX = __KSMBD_EVENT_MAX - 1 + }; + + /* +diff --git a/fs/ksmbd/transport_ipc.c b/fs/ksmbd/transport_ipc.c +index 9560c704033e..2c9662e32799 100644 +--- a/fs/ksmbd/transport_ipc.c ++++ b/fs/ksmbd/transport_ipc.c +@@ -74,7 +74,7 @@ static int handle_unsupported_event(struct sk_buff *skb, struct genl_info *info) + static int handle_generic_event(struct sk_buff *skb, struct genl_info *info); + static int ksmbd_ipc_heartbeat_request(void); + +-static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX] = { ++static const struct nla_policy ksmbd_nl_policy[KSMBD_EVENT_MAX + 1] = { + [KSMBD_EVENT_UNSPEC] = { + .len = 0, + }, +@@ -402,7 +402,7 @@ static int handle_generic_event(struct sk_buff *skb, struct genl_info *info) + return -EPERM; + #endif + +- if (type >= KSMBD_EVENT_MAX) { ++ if (type > KSMBD_EVENT_MAX) { + WARN_ON(1); + return -EINVAL; + } +-- +2.43.0 + diff --git a/queue-5.15/media-mtk-jpeg-fix-use-after-free-bug-due-to-error-p.patch b/queue-5.15/media-mtk-jpeg-fix-use-after-free-bug-due-to-error-p.patch new file mode 100644 index 00000000000..e94e260de74 --- /dev/null +++ b/queue-5.15/media-mtk-jpeg-fix-use-after-free-bug-due-to-error-p.patch @@ -0,0 +1,75 @@ +From 67cfccae23323c9254f098a15decfd5c9012e929 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 6 Nov 2023 15:48:10 +0100 +Subject: media: mtk-jpeg: Fix use after free bug due to error path handling in + mtk_jpeg_dec_device_run + +From: Zheng Wang + +[ Upstream commit 206c857dd17d4d026de85866f1b5f0969f2a109e ] + +In mtk_jpeg_probe, &jpeg->job_timeout_work is bound with +mtk_jpeg_job_timeout_work. + +In mtk_jpeg_dec_device_run, if error happens in +mtk_jpeg_set_dec_dst, it will finally start the worker while +mark the job as finished by invoking v4l2_m2m_job_finish. + +There are two methods to trigger the bug. If we remove the +module, it which will call mtk_jpeg_remove to make cleanup. +The possible sequence is as follows, which will cause a +use-after-free bug. + +CPU0 CPU1 +mtk_jpeg_dec_... | + start worker | + |mtk_jpeg_job_timeout_work +mtk_jpeg_remove | + v4l2_m2m_release | + kfree(m2m_dev); | + | + | v4l2_m2m_get_curr_priv + | m2m_dev->curr_ctx //use + +If we close the file descriptor, which will call mtk_jpeg_release, +it will have a similar sequence. + +Fix this bug by starting timeout worker only if started jpegdec worker +successfully. Then v4l2_m2m_job_finish will only be called in +either mtk_jpeg_job_timeout_work or mtk_jpeg_dec_device_run. + +Fixes: b2f0d2724ba4 ("[media] vcodec: mediatek: Add Mediatek JPEG Decoder Driver") +Signed-off-by: Zheng Wang +Signed-off-by: Dmitry Osipenko +Cc: stable@vger.kernel.org +Signed-off-by: Hans Verkuil +Signed-off-by: Mauro Carvalho Chehab +Signed-off-by: Sasha Levin +--- + drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +index bc84274ba87a..ba490b06ff70 100644 +--- a/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c ++++ b/drivers/media/platform/mtk-jpeg/mtk_jpeg_core.c +@@ -977,13 +977,13 @@ static void mtk_jpeg_dec_device_run(void *priv) + if (ret < 0) + goto dec_end; + +- schedule_delayed_work(&jpeg->job_timeout_work, +- msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); +- + mtk_jpeg_set_dec_src(ctx, &src_buf->vb2_buf, &bs); + if (mtk_jpeg_set_dec_dst(ctx, &jpeg_src_buf->dec_param, &dst_buf->vb2_buf, &fb)) + goto dec_end; + ++ schedule_delayed_work(&jpeg->job_timeout_work, ++ msecs_to_jiffies(MTK_JPEG_HW_TIMEOUT_MSEC)); ++ + spin_lock_irqsave(&jpeg->hw_lock, flags); + mtk_jpeg_dec_reset(jpeg->reg_base); + mtk_jpeg_dec_set_config(jpeg->reg_base, +-- +2.43.0 + diff --git a/queue-5.15/mm-sparsemem-fix-race-in-accessing-memory_section-us.patch b/queue-5.15/mm-sparsemem-fix-race-in-accessing-memory_section-us.patch new file mode 100644 index 00000000000..feecd81cfb9 --- /dev/null +++ b/queue-5.15/mm-sparsemem-fix-race-in-accessing-memory_section-us.patch @@ -0,0 +1,213 @@ +From 251410828c1f87e2c887ef1a1337183170a2b484 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 13 Oct 2023 18:34:27 +0530 +Subject: mm/sparsemem: fix race in accessing memory_section->usage + +From: Charan Teja Kalla + +[ Upstream commit 5ec8e8ea8b7783fab150cf86404fc38cb4db8800 ] + +The below race is observed on a PFN which falls into the device memory +region with the system memory configuration where PFN's are such that +[ZONE_NORMAL ZONE_DEVICE ZONE_NORMAL]. Since normal zone start and end +pfn contains the device memory PFN's as well, the compaction triggered +will try on the device memory PFN's too though they end up in NOP(because +pfn_to_online_page() returns NULL for ZONE_DEVICE memory sections). When +from other core, the section mappings are being removed for the +ZONE_DEVICE region, that the PFN in question belongs to, on which +compaction is currently being operated is resulting into the kernel crash +with CONFIG_SPASEMEM_VMEMAP enabled. The crash logs can be seen at [1]. + +compact_zone() memunmap_pages +------------- --------------- +__pageblock_pfn_to_page + ...... + (a)pfn_valid(): + valid_section()//return true + (b)__remove_pages()-> + sparse_remove_section()-> + section_deactivate(): + [Free the array ms->usage and set + ms->usage = NULL] + pfn_section_valid() + [Access ms->usage which + is NULL] + +NOTE: From the above it can be said that the race is reduced to between +the pfn_valid()/pfn_section_valid() and the section deactivate with +SPASEMEM_VMEMAP enabled. + +The commit b943f045a9af("mm/sparse: fix kernel crash with +pfn_section_valid check") tried to address the same problem by clearing +the SECTION_HAS_MEM_MAP with the expectation of valid_section() returns +false thus ms->usage is not accessed. + +Fix this issue by the below steps: + +a) Clear SECTION_HAS_MEM_MAP before freeing the ->usage. + +b) RCU protected read side critical section will either return NULL + when SECTION_HAS_MEM_MAP is cleared or can successfully access ->usage. + +c) Free the ->usage with kfree_rcu() and set ms->usage = NULL. No + attempt will be made to access ->usage after this as the + SECTION_HAS_MEM_MAP is cleared thus valid_section() return false. + +Thanks to David/Pavan for their inputs on this patch. + +[1] https://lore.kernel.org/linux-mm/994410bb-89aa-d987-1f50-f514903c55aa@quicinc.com/ + +On Snapdragon SoC, with the mentioned memory configuration of PFN's as +[ZONE_NORMAL ZONE_DEVICE ZONE_NORMAL], we are able to see bunch of +issues daily while testing on a device farm. + +For this particular issue below is the log. Though the below log is +not directly pointing to the pfn_section_valid(){ ms->usage;}, when we +loaded this dump on T32 lauterbach tool, it is pointing. + +[ 540.578056] Unable to handle kernel NULL pointer dereference at +virtual address 0000000000000000 +[ 540.578068] Mem abort info: +[ 540.578070] ESR = 0x0000000096000005 +[ 540.578073] EC = 0x25: DABT (current EL), IL = 32 bits +[ 540.578077] SET = 0, FnV = 0 +[ 540.578080] EA = 0, S1PTW = 0 +[ 540.578082] FSC = 0x05: level 1 translation fault +[ 540.578085] Data abort info: +[ 540.578086] ISV = 0, ISS = 0x00000005 +[ 540.578088] CM = 0, WnR = 0 +[ 540.579431] pstate: 82400005 (Nzcv daif +PAN -UAO +TCO -DIT -SSBSBTYPE=--) +[ 540.579436] pc : __pageblock_pfn_to_page+0x6c/0x14c +[ 540.579454] lr : compact_zone+0x994/0x1058 +[ 540.579460] sp : ffffffc03579b510 +[ 540.579463] x29: ffffffc03579b510 x28: 0000000000235800 x27:000000000000000c +[ 540.579470] x26: 0000000000235c00 x25: 0000000000000068 x24:ffffffc03579b640 +[ 540.579477] x23: 0000000000000001 x22: ffffffc03579b660 x21:0000000000000000 +[ 540.579483] x20: 0000000000235bff x19: ffffffdebf7e3940 x18:ffffffdebf66d140 +[ 540.579489] x17: 00000000739ba063 x16: 00000000739ba063 x15:00000000009f4bff +[ 540.579495] x14: 0000008000000000 x13: 0000000000000000 x12:0000000000000001 +[ 540.579501] x11: 0000000000000000 x10: 0000000000000000 x9 :ffffff897d2cd440 +[ 540.579507] x8 : 0000000000000000 x7 : 0000000000000000 x6 :ffffffc03579b5b4 +[ 540.579512] x5 : 0000000000027f25 x4 : ffffffc03579b5b8 x3 :0000000000000001 +[ 540.579518] x2 : ffffffdebf7e3940 x1 : 0000000000235c00 x0 :0000000000235800 +[ 540.579524] Call trace: +[ 540.579527] __pageblock_pfn_to_page+0x6c/0x14c +[ 540.579533] compact_zone+0x994/0x1058 +[ 540.579536] try_to_compact_pages+0x128/0x378 +[ 540.579540] __alloc_pages_direct_compact+0x80/0x2b0 +[ 540.579544] __alloc_pages_slowpath+0x5c0/0xe10 +[ 540.579547] __alloc_pages+0x250/0x2d0 +[ 540.579550] __iommu_dma_alloc_noncontiguous+0x13c/0x3fc +[ 540.579561] iommu_dma_alloc+0xa0/0x320 +[ 540.579565] dma_alloc_attrs+0xd4/0x108 + +[quic_charante@quicinc.com: use kfree_rcu() in place of synchronize_rcu(), per David] + Link: https://lkml.kernel.org/r/1698403778-20938-1-git-send-email-quic_charante@quicinc.com +Link: https://lkml.kernel.org/r/1697202267-23600-1-git-send-email-quic_charante@quicinc.com +Fixes: f46edbd1b151 ("mm/sparsemem: add helpers track active portions of a section at boot") +Signed-off-by: Charan Teja Kalla +Cc: Aneesh Kumar K.V +Cc: Dan Williams +Cc: David Hildenbrand +Cc: Mel Gorman +Cc: Oscar Salvador +Cc: Vlastimil Babka +Cc: +Signed-off-by: Andrew Morton +Signed-off-by: Sasha Levin +--- + include/linux/mmzone.h | 14 +++++++++++--- + mm/sparse.c | 17 +++++++++-------- + 2 files changed, 20 insertions(+), 11 deletions(-) + +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +index 9e1485083398..8b8349ffa1cd 100644 +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -1287,6 +1287,7 @@ static inline unsigned long section_nr_to_pfn(unsigned long sec) + #define SUBSECTION_ALIGN_DOWN(pfn) ((pfn) & PAGE_SUBSECTION_MASK) + + struct mem_section_usage { ++ struct rcu_head rcu; + #ifdef CONFIG_SPARSEMEM_VMEMMAP + DECLARE_BITMAP(subsection_map, SUBSECTIONS_PER_SECTION); + #endif +@@ -1457,7 +1458,7 @@ static inline int pfn_section_valid(struct mem_section *ms, unsigned long pfn) + { + int idx = subsection_map_index(pfn); + +- return test_bit(idx, ms->usage->subsection_map); ++ return test_bit(idx, READ_ONCE(ms->usage)->subsection_map); + } + #else + static inline int pfn_section_valid(struct mem_section *ms, unsigned long pfn) +@@ -1481,6 +1482,7 @@ static inline int pfn_section_valid(struct mem_section *ms, unsigned long pfn) + static inline int pfn_valid(unsigned long pfn) + { + struct mem_section *ms; ++ int ret; + + /* + * Ensure the upper PAGE_SHIFT bits are clear in the +@@ -1494,13 +1496,19 @@ static inline int pfn_valid(unsigned long pfn) + if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) + return 0; + ms = __pfn_to_section(pfn); +- if (!valid_section(ms)) ++ rcu_read_lock(); ++ if (!valid_section(ms)) { ++ rcu_read_unlock(); + return 0; ++ } + /* + * Traditionally early sections always returned pfn_valid() for + * the entire section-sized span. + */ +- return early_section(ms) || pfn_section_valid(ms, pfn); ++ ret = early_section(ms) || pfn_section_valid(ms, pfn); ++ rcu_read_unlock(); ++ ++ return ret; + } + #endif + +diff --git a/mm/sparse.c b/mm/sparse.c +index 120bc8ea5293..27092badd15b 100644 +--- a/mm/sparse.c ++++ b/mm/sparse.c +@@ -789,6 +789,13 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, + if (empty) { + unsigned long section_nr = pfn_to_section_nr(pfn); + ++ /* ++ * Mark the section invalid so that valid_section() ++ * return false. This prevents code from dereferencing ++ * ms->usage array. ++ */ ++ ms->section_mem_map &= ~SECTION_HAS_MEM_MAP; ++ + /* + * When removing an early section, the usage map is kept (as the + * usage maps of other sections fall into the same page). It +@@ -797,16 +804,10 @@ static void section_deactivate(unsigned long pfn, unsigned long nr_pages, + * was allocated during boot. + */ + if (!PageReserved(virt_to_page(ms->usage))) { +- kfree(ms->usage); +- ms->usage = NULL; ++ kfree_rcu(ms->usage, rcu); ++ WRITE_ONCE(ms->usage, NULL); + } + memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr); +- /* +- * Mark the section invalid so that valid_section() +- * return false. This prevents code from dereferencing +- * ms->usage array. +- */ +- ms->section_mem_map &= ~SECTION_HAS_MEM_MAP; + } + + /* +-- +2.43.0 + diff --git a/queue-5.15/mm-use-__pfn_to_section-instead-of-open-coding-it.patch b/queue-5.15/mm-use-__pfn_to_section-instead-of-open-coding-it.patch new file mode 100644 index 00000000000..9d9b056f903 --- /dev/null +++ b/queue-5.15/mm-use-__pfn_to_section-instead-of-open-coding-it.patch @@ -0,0 +1,47 @@ +From dfd6261dddcc299b40b8d5cbe3e7023f2e295cee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 5 Nov 2021 13:38:15 -0700 +Subject: mm: use __pfn_to_section() instead of open coding it + +From: Rolf Eike Beer + +[ Upstream commit f1dc0db296bd25960273649fc6ef2ecbf5aaa0e0 ] + +It is defined in the same file just a few lines above. + +Link: https://lkml.kernel.org/r/4598487.Rc0NezkW7i@mobilepool36.emlix.com +Signed-off-by: Rolf Eike Beer +Reviewed-by: Andrew Morton +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Stable-dep-of: 5ec8e8ea8b77 ("mm/sparsemem: fix race in accessing memory_section->usage") +Signed-off-by: Sasha Levin +--- + include/linux/mmzone.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h +index 6ba100216530..9e1485083398 100644 +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -1493,7 +1493,7 @@ static inline int pfn_valid(unsigned long pfn) + + if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) + return 0; +- ms = __nr_to_section(pfn_to_section_nr(pfn)); ++ ms = __pfn_to_section(pfn); + if (!valid_section(ms)) + return 0; + /* +@@ -1508,7 +1508,7 @@ static inline int pfn_in_present_section(unsigned long pfn) + { + if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS) + return 0; +- return present_section(__nr_to_section(pfn_to_section_nr(pfn))); ++ return present_section(__pfn_to_section(pfn)); + } + + static inline unsigned long next_present_section_nr(unsigned long section_nr) +-- +2.43.0 + diff --git a/queue-5.15/nfsd-add-documenting-comment-for-nfsd4_release_locko.patch b/queue-5.15/nfsd-add-documenting-comment-for-nfsd4_release_locko.patch new file mode 100644 index 00000000000..ad2737c5c1d --- /dev/null +++ b/queue-5.15/nfsd-add-documenting-comment-for-nfsd4_release_locko.patch @@ -0,0 +1,73 @@ +From bfc7f89d24d6164689aed7061a97879ef05fd861 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 22 May 2022 12:34:38 -0400 +Subject: NFSD: Add documenting comment for nfsd4_release_lockowner() + +From: Chuck Lever + +[ Upstream commit 043862b09cc00273e35e6c3a6389957953a34207 ] + +And return explicit nfserr values that match what is documented in the +new comment / API contract. + +Signed-off-by: Chuck Lever +Stable-dep-of: edcf9725150e ("nfsd: fix RELEASE_LOCKOWNER") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 23 ++++++++++++++++++++--- + 1 file changed, 20 insertions(+), 3 deletions(-) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 798063b9b96f..f8533299db1c 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -7284,6 +7284,23 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) + return status; + } + ++/** ++ * nfsd4_release_lockowner - process NFSv4.0 RELEASE_LOCKOWNER operations ++ * @rqstp: RPC transaction ++ * @cstate: NFSv4 COMPOUND state ++ * @u: RELEASE_LOCKOWNER arguments ++ * ++ * The lockowner's so_count is bumped when a lock record is added ++ * or when copying a conflicting lock. The latter case is brief, ++ * but can lead to fleeting false positives when looking for ++ * locks-in-use. ++ * ++ * Return values: ++ * %nfs_ok: lockowner released or not found ++ * %nfserr_locks_held: lockowner still in use ++ * %nfserr_stale_clientid: clientid no longer active ++ * %nfserr_expired: clientid not recognized ++ */ + __be32 + nfsd4_release_lockowner(struct svc_rqst *rqstp, + struct nfsd4_compound_state *cstate, +@@ -7310,7 +7327,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, + lo = find_lockowner_str_locked(clp, &rlockowner->rl_owner); + if (!lo) { + spin_unlock(&clp->cl_lock); +- return status; ++ return nfs_ok; + } + if (atomic_read(&lo->lo_owner.so_count) != 2) { + spin_unlock(&clp->cl_lock); +@@ -7326,11 +7343,11 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, + put_ol_stateid_locked(stp, &reaplist); + } + spin_unlock(&clp->cl_lock); ++ + free_ol_stateid_reaplist(&reaplist); + remove_blocked_locks(lo); + nfs4_put_stateowner(&lo->lo_owner); +- +- return status; ++ return nfs_ok; + } + + static inline struct nfs4_client_reclaim * +-- +2.43.0 + diff --git a/queue-5.15/nfsd-fix-release_lockowner.patch b/queue-5.15/nfsd-fix-release_lockowner.patch new file mode 100644 index 00000000000..6938f300626 --- /dev/null +++ b/queue-5.15/nfsd-fix-release_lockowner.patch @@ -0,0 +1,149 @@ +From b99447c0e88afdb26e8907c0904190f6064f4f1b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 22 Jan 2024 14:58:16 +1100 +Subject: nfsd: fix RELEASE_LOCKOWNER + +From: NeilBrown + +[ Upstream commit edcf9725150e42beeca42d085149f4c88fa97afd ] + +The test on so_count in nfsd4_release_lockowner() is nonsense and +harmful. Revert to using check_for_locks(), changing that to not sleep. + +First: harmful. +As is documented in the kdoc comment for nfsd4_release_lockowner(), the +test on so_count can transiently return a false positive resulting in a +return of NFS4ERR_LOCKS_HELD when in fact no locks are held. This is +clearly a protocol violation and with the Linux NFS client it can cause +incorrect behaviour. + +If RELEASE_LOCKOWNER is sent while some other thread is still +processing a LOCK request which failed because, at the time that request +was received, the given owner held a conflicting lock, then the nfsd +thread processing that LOCK request can hold a reference (conflock) to +the lock owner that causes nfsd4_release_lockowner() to return an +incorrect error. + +The Linux NFS client ignores that NFS4ERR_LOCKS_HELD error because it +never sends NFS4_RELEASE_LOCKOWNER without first releasing any locks, so +it knows that the error is impossible. It assumes the lock owner was in +fact released so it feels free to use the same lock owner identifier in +some later locking request. + +When it does reuse a lock owner identifier for which a previous RELEASE +failed, it will naturally use a lock_seqid of zero. However the server, +which didn't release the lock owner, will expect a larger lock_seqid and +so will respond with NFS4ERR_BAD_SEQID. + +So clearly it is harmful to allow a false positive, which testing +so_count allows. + +The test is nonsense because ... well... it doesn't mean anything. + +so_count is the sum of three different counts. +1/ the set of states listed on so_stateids +2/ the set of active vfs locks owned by any of those states +3/ various transient counts such as for conflicting locks. + +When it is tested against '2' it is clear that one of these is the +transient reference obtained by find_lockowner_str_locked(). It is not +clear what the other one is expected to be. + +In practice, the count is often 2 because there is precisely one state +on so_stateids. If there were more, this would fail. + +In my testing I see two circumstances when RELEASE_LOCKOWNER is called. +In one case, CLOSE is called before RELEASE_LOCKOWNER. That results in +all the lock states being removed, and so the lockowner being discarded +(it is removed when there are no more references which usually happens +when the lock state is discarded). When nfsd4_release_lockowner() finds +that the lock owner doesn't exist, it returns success. + +The other case shows an so_count of '2' and precisely one state listed +in so_stateid. It appears that the Linux client uses a separate lock +owner for each file resulting in one lock state per lock owner, so this +test on '2' is safe. For another client it might not be safe. + +So this patch changes check_for_locks() to use the (newish) +find_any_file_locked() so that it doesn't take a reference on the +nfs4_file and so never calls nfsd_file_put(), and so never sleeps. With +this check is it safe to restore the use of check_for_locks() rather +than testing so_count against the mysterious '2'. + +Fixes: ce3c4ad7f4ce ("NFSD: Fix possible sleep during nfsd4_release_lockowner()") +Signed-off-by: NeilBrown +Reviewed-by: Jeff Layton +Cc: stable@vger.kernel.org # v6.2+ +Signed-off-by: Chuck Lever +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 26 +++++++++++++++----------- + 1 file changed, 15 insertions(+), 11 deletions(-) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index f8533299db1c..07ea35803629 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -7257,14 +7257,16 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) + { + struct file_lock *fl; + int status = false; +- struct nfsd_file *nf = find_any_file(fp); ++ struct nfsd_file *nf; + struct inode *inode; + struct file_lock_context *flctx; + ++ spin_lock(&fp->fi_lock); ++ nf = find_any_file_locked(fp); + if (!nf) { + /* Any valid lock stateid should have some sort of access */ + WARN_ON_ONCE(1); +- return status; ++ goto out; + } + + inode = locks_inode(nf->nf_file); +@@ -7280,7 +7282,8 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) + } + spin_unlock(&flctx->flc_lock); + } +- nfsd_file_put(nf); ++out: ++ spin_unlock(&fp->fi_lock); + return status; + } + +@@ -7290,10 +7293,8 @@ check_for_locks(struct nfs4_file *fp, struct nfs4_lockowner *lowner) + * @cstate: NFSv4 COMPOUND state + * @u: RELEASE_LOCKOWNER arguments + * +- * The lockowner's so_count is bumped when a lock record is added +- * or when copying a conflicting lock. The latter case is brief, +- * but can lead to fleeting false positives when looking for +- * locks-in-use. ++ * Check if theree are any locks still held and if not - free the lockowner ++ * and any lock state that is owned. + * + * Return values: + * %nfs_ok: lockowner released or not found +@@ -7329,10 +7330,13 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, + spin_unlock(&clp->cl_lock); + return nfs_ok; + } +- if (atomic_read(&lo->lo_owner.so_count) != 2) { +- spin_unlock(&clp->cl_lock); +- nfs4_put_stateowner(&lo->lo_owner); +- return nfserr_locks_held; ++ ++ list_for_each_entry(stp, &lo->lo_owner.so_stateids, st_perstateowner) { ++ if (check_for_locks(stp->st_stid.sc_file, lo)) { ++ spin_unlock(&clp->cl_lock); ++ nfs4_put_stateowner(&lo->lo_owner); ++ return nfserr_locks_held; ++ } + } + unhash_lockowner_locked(lo); + while (!list_empty(&lo->lo_owner.so_stateids)) { +-- +2.43.0 + diff --git a/queue-5.15/nfsd-modernize-nfsd4_release_lockowner.patch b/queue-5.15/nfsd-modernize-nfsd4_release_lockowner.patch new file mode 100644 index 00000000000..5c1ac7d883b --- /dev/null +++ b/queue-5.15/nfsd-modernize-nfsd4_release_lockowner.patch @@ -0,0 +1,86 @@ +From 05aaa4892314880beb9474adeb0dd95d3b8086ad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 22 May 2022 12:07:18 -0400 +Subject: NFSD: Modernize nfsd4_release_lockowner() + +From: Chuck Lever + +[ Upstream commit bd8fdb6e545f950f4654a9a10d7e819ad48146e5 ] + +Refactor: Use existing helpers that other lock operations use. This +change removes several automatic variables, so re-organize the +variable declarations for readability. + +Signed-off-by: Chuck Lever +Stable-dep-of: edcf9725150e ("nfsd: fix RELEASE_LOCKOWNER") +Signed-off-by: Sasha Levin +--- + fs/nfsd/nfs4state.c | 36 +++++++++++------------------------- + 1 file changed, 11 insertions(+), 25 deletions(-) + +diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c +index 9b660491f393..798063b9b96f 100644 +--- a/fs/nfsd/nfs4state.c ++++ b/fs/nfsd/nfs4state.c +@@ -7290,16 +7290,13 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, + union nfsd4_op_u *u) + { + struct nfsd4_release_lockowner *rlockowner = &u->release_lockowner; ++ struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + clientid_t *clid = &rlockowner->rl_clientid; +- struct nfs4_stateowner *sop; +- struct nfs4_lockowner *lo = NULL; + struct nfs4_ol_stateid *stp; +- struct xdr_netobj *owner = &rlockowner->rl_owner; +- unsigned int hashval = ownerstr_hashval(owner); +- __be32 status; +- struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); ++ struct nfs4_lockowner *lo; + struct nfs4_client *clp; +- LIST_HEAD (reaplist); ++ LIST_HEAD(reaplist); ++ __be32 status; + + dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", + clid->cl_boot, clid->cl_id); +@@ -7307,30 +7304,19 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, + status = set_client(clid, cstate, nn); + if (status) + return status; +- + clp = cstate->clp; +- /* Find the matching lock stateowner */ +- spin_lock(&clp->cl_lock); +- list_for_each_entry(sop, &clp->cl_ownerstr_hashtbl[hashval], +- so_strhash) { + +- if (sop->so_is_open_owner || !same_owner_str(sop, owner)) +- continue; +- +- if (atomic_read(&sop->so_count) != 1) { +- spin_unlock(&clp->cl_lock); +- return nfserr_locks_held; +- } +- +- lo = lockowner(sop); +- nfs4_get_stateowner(sop); +- break; +- } ++ spin_lock(&clp->cl_lock); ++ lo = find_lockowner_str_locked(clp, &rlockowner->rl_owner); + if (!lo) { + spin_unlock(&clp->cl_lock); + return status; + } +- ++ if (atomic_read(&lo->lo_owner.so_count) != 2) { ++ spin_unlock(&clp->cl_lock); ++ nfs4_put_stateowner(&lo->lo_owner); ++ return nfserr_locks_held; ++ } + unhash_lockowner_locked(lo); + while (!list_empty(&lo->lo_owner.so_stateids)) { + stp = list_first_entry(&lo->lo_owner.so_stateids, +-- +2.43.0 + diff --git a/queue-5.15/pipe-wakeup-wr_wait-after-setting-max_usage.patch b/queue-5.15/pipe-wakeup-wr_wait-after-setting-max_usage.patch new file mode 100644 index 00000000000..fddd5189b1d --- /dev/null +++ b/queue-5.15/pipe-wakeup-wr_wait-after-setting-max_usage.patch @@ -0,0 +1,62 @@ +From 5b7b64f163aaa80fab411ec73cbb0e94e184e90b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 1 Dec 2023 11:11:28 +0100 +Subject: pipe: wakeup wr_wait after setting max_usage + +From: Lukas Schauer + +[ Upstream commit e95aada4cb93d42e25c30a0ef9eb2923d9711d4a ] + +Commit c73be61cede5 ("pipe: Add general notification queue support") a +regression was introduced that would lock up resized pipes under certain +conditions. See the reproducer in [1]. + +The commit resizing the pipe ring size was moved to a different +function, doing that moved the wakeup for pipe->wr_wait before actually +raising pipe->max_usage. If a pipe was full before the resize occured it +would result in the wakeup never actually triggering pipe_write. + +Set @max_usage and @nr_accounted before waking writers if this isn't a +watch queue. + +Link: https://bugzilla.kernel.org/show_bug.cgi?id=212295 [1] +Link: https://lore.kernel.org/r/20231201-orchideen-modewelt-e009de4562c6@brauner +Fixes: c73be61cede5 ("pipe: Add general notification queue support") +Reviewed-by: David Howells +Cc: +Signed-off-by: Lukas Schauer +[Christian Brauner : rewrite to account for watch queues] +Signed-off-by: Christian Brauner +Signed-off-by: Sasha Levin +--- + fs/pipe.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/fs/pipe.c b/fs/pipe.c +index 1a43948c0c36..a8b8ef2dae7b 100644 +--- a/fs/pipe.c ++++ b/fs/pipe.c +@@ -1300,6 +1300,11 @@ int pipe_resize_ring(struct pipe_inode_info *pipe, unsigned int nr_slots) + pipe->tail = tail; + pipe->head = head; + ++ if (!pipe_has_watch_queue(pipe)) { ++ pipe->max_usage = nr_slots; ++ pipe->nr_accounted = nr_slots; ++ } ++ + spin_unlock_irq(&pipe->rd_wait.lock); + + /* This might have made more room for writers */ +@@ -1351,8 +1356,6 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg) + if (ret < 0) + goto out_revert_acct; + +- pipe->max_usage = nr_slots; +- pipe->nr_accounted = nr_slots; + return pipe->max_usage * PAGE_SIZE; + + out_revert_acct: +-- +2.43.0 + diff --git a/queue-5.15/pm-core-remove-unnecessary-void-conversions.patch b/queue-5.15/pm-core-remove-unnecessary-void-conversions.patch new file mode 100644 index 00000000000..3321308d579 --- /dev/null +++ b/queue-5.15/pm-core-remove-unnecessary-void-conversions.patch @@ -0,0 +1,83 @@ +From bf88a10594f21c21887f67a3a71aec20d58c99ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 26 Mar 2023 06:19:35 +0800 +Subject: PM: core: Remove unnecessary (void *) conversions + +From: Li zeming + +[ Upstream commit 73d73f5ee7fb0c42ff87091d105bee720a9565f1 ] + +Assignments from pointer variables of type (void *) do not require +explicit type casts, so remove such type cases from the code in +drivers/base/power/main.c where applicable. + +Signed-off-by: Li zeming +[ rjw: Subject and changelog edits ] +Signed-off-by: Rafael J. Wysocki +Stable-dep-of: 7839d0078e0d ("PM: sleep: Fix possible deadlocks in core system-wide PM code") +Signed-off-by: Sasha Levin +--- + drivers/base/power/main.c | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +index 8c4819fe73d4..b4cd003d3e39 100644 +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -680,7 +680,7 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) + + static void async_resume_noirq(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = device_resume_noirq(dev, pm_transition, true); +@@ -819,7 +819,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn + + static void async_resume_early(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = device_resume_early(dev, pm_transition, true); +@@ -983,7 +983,7 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) + + static void async_resume(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = device_resume(dev, pm_transition, true); +@@ -1272,7 +1272,7 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a + + static void async_suspend_noirq(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = __device_suspend_noirq(dev, pm_transition, true); +@@ -1455,7 +1455,7 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as + + static void async_suspend_late(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = __device_suspend_late(dev, pm_transition, true); +@@ -1731,7 +1731,7 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async) + + static void async_suspend(void *data, async_cookie_t cookie) + { +- struct device *dev = (struct device *)data; ++ struct device *dev = data; + int error; + + error = __device_suspend(dev, pm_transition, true); +-- +2.43.0 + diff --git a/queue-5.15/pm-devfreq-add-cpu-based-scaling-support-to-passive-.patch b/queue-5.15/pm-devfreq-add-cpu-based-scaling-support-to-passive-.patch new file mode 100644 index 00000000000..cd96a5ca539 --- /dev/null +++ b/queue-5.15/pm-devfreq-add-cpu-based-scaling-support-to-passive-.patch @@ -0,0 +1,475 @@ +From 2fb24f0d287f9cf2db2cd73f41dba8b5a6710453 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Mar 2021 15:58:21 +0900 +Subject: PM / devfreq: Add cpu based scaling support to passive governor + +From: Saravana Kannan + +[ Upstream commit a03dacb0316f74400846aaf144d6c73f4217ca08 ] + +Many CPU architectures have caches that can scale independent of the +CPUs. Frequency scaling of the caches is necessary to make sure that the +cache is not a performance bottleneck that leads to poor performance and +power. The same idea applies for RAM/DDR. + +To achieve this, this patch adds support for cpu based scaling to the +passive governor. This is accomplished by taking the current frequency +of each CPU frequency domain and then adjust the frequency of the cache +(or any devfreq device) based on the frequency of the CPUs. It listens +to CPU frequency transition notifiers to keep itself up to date on the +current CPU frequency. + +To decide the frequency of the device, the governor does one of the +following: +* Derives the optimal devfreq device opp from required-opps property of + the parent cpu opp_table. + +* Scales the device frequency in proportion to the CPU frequency. So, if + the CPUs are running at their max frequency, the device runs at its + max frequency. If the CPUs are running at their min frequency, the + device runs at its min frequency. It is interpolated for frequencies + in between. + +Tested-by: Chen-Yu Tsai +Tested-by: Johnson Wang +Signed-off-by: Saravana Kannan +[Sibi: Integrated cpu-freqmap governor into passive_governor] +Signed-off-by: Sibi Sankar +[Chanwoo: Fix conflict with latest code and cleanup code] +Signed-off-by: Chanwoo Choi +Stable-dep-of: 08e23d05fa6d ("PM / devfreq: Fix buffer overflow in trans_stat_show") +Signed-off-by: Sasha Levin +--- + drivers/devfreq/governor.h | 22 +++ + drivers/devfreq/governor_passive.c | 298 +++++++++++++++++++++++++++-- + include/linux/devfreq.h | 17 +- + 3 files changed, 323 insertions(+), 14 deletions(-) + +diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h +index 9a9495f94ac6..3c36c92c89a9 100644 +--- a/drivers/devfreq/governor.h ++++ b/drivers/devfreq/governor.h +@@ -47,6 +47,28 @@ + #define DEVFREQ_GOV_ATTR_POLLING_INTERVAL BIT(0) + #define DEVFREQ_GOV_ATTR_TIMER BIT(1) + ++/** ++ * struct devfreq_cpu_data - Hold the per-cpu data ++ * @dev: reference to cpu device. ++ * @first_cpu: the cpumask of the first cpu of a policy. ++ * @opp_table: reference to cpu opp table. ++ * @cur_freq: the current frequency of the cpu. ++ * @min_freq: the min frequency of the cpu. ++ * @max_freq: the max frequency of the cpu. ++ * ++ * This structure stores the required cpu_data of a cpu. ++ * This is auto-populated by the governor. ++ */ ++struct devfreq_cpu_data { ++ struct device *dev; ++ unsigned int first_cpu; ++ ++ struct opp_table *opp_table; ++ unsigned int cur_freq; ++ unsigned int min_freq; ++ unsigned int max_freq; ++}; ++ + /** + * struct devfreq_governor - Devfreq policy governor + * @node: list node - contains registered devfreq governors +diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c +index fc09324a03e0..7f30088b500b 100644 +--- a/drivers/devfreq/governor_passive.c ++++ b/drivers/devfreq/governor_passive.c +@@ -8,11 +8,85 @@ + */ + + #include ++#include ++#include ++#include ++#include + #include + #include + #include "governor.h" + +-static int devfreq_passive_get_target_freq(struct devfreq *devfreq, ++#define HZ_PER_KHZ 1000 ++ ++static unsigned long get_target_freq_by_required_opp(struct device *p_dev, ++ struct opp_table *p_opp_table, ++ struct opp_table *opp_table, ++ unsigned long *freq) ++{ ++ struct dev_pm_opp *opp = NULL, *p_opp = NULL; ++ unsigned long target_freq; ++ ++ if (!p_dev || !p_opp_table || !opp_table || !freq) ++ return 0; ++ ++ p_opp = devfreq_recommended_opp(p_dev, freq, 0); ++ if (IS_ERR(p_opp)) ++ return 0; ++ ++ opp = dev_pm_opp_xlate_required_opp(p_opp_table, opp_table, p_opp); ++ dev_pm_opp_put(p_opp); ++ ++ if (IS_ERR(opp)) ++ return 0; ++ ++ target_freq = dev_pm_opp_get_freq(opp); ++ dev_pm_opp_put(opp); ++ ++ return target_freq; ++} ++ ++static int get_target_freq_with_cpufreq(struct devfreq *devfreq, ++ unsigned long *target_freq) ++{ ++ struct devfreq_passive_data *p_data = ++ (struct devfreq_passive_data *)devfreq->data; ++ struct devfreq_cpu_data *parent_cpu_data; ++ unsigned long cpu, cpu_cur, cpu_min, cpu_max, cpu_percent; ++ unsigned long dev_min, dev_max; ++ unsigned long freq = 0; ++ ++ for_each_online_cpu(cpu) { ++ parent_cpu_data = p_data->parent_cpu_data[cpu]; ++ if (!parent_cpu_data || parent_cpu_data->first_cpu != cpu) ++ continue; ++ ++ /* Get target freq via required opps */ ++ cpu_cur = parent_cpu_data->cur_freq * HZ_PER_KHZ; ++ freq = get_target_freq_by_required_opp(parent_cpu_data->dev, ++ parent_cpu_data->opp_table, ++ devfreq->opp_table, &cpu_cur); ++ if (freq) { ++ *target_freq = max(freq, *target_freq); ++ continue; ++ } ++ ++ /* Use interpolation if required opps is not available */ ++ devfreq_get_freq_range(devfreq, &dev_min, &dev_max); ++ ++ cpu_min = parent_cpu_data->min_freq; ++ cpu_max = parent_cpu_data->max_freq; ++ cpu_cur = parent_cpu_data->cur_freq; ++ ++ cpu_percent = ((cpu_cur - cpu_min) * 100) / (cpu_max - cpu_min); ++ freq = dev_min + mult_frac(dev_max - dev_min, cpu_percent, 100); ++ ++ *target_freq = max(freq, *target_freq); ++ } ++ ++ return 0; ++} ++ ++static int get_target_freq_with_devfreq(struct devfreq *devfreq, + unsigned long *freq) + { + struct devfreq_passive_data *p_data +@@ -99,6 +173,181 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, + return 0; + } + ++static int devfreq_passive_get_target_freq(struct devfreq *devfreq, ++ unsigned long *freq) ++{ ++ struct devfreq_passive_data *p_data = ++ (struct devfreq_passive_data *)devfreq->data; ++ int ret; ++ ++ if (!p_data) ++ return -EINVAL; ++ ++ /* ++ * If the devfreq device with passive governor has the specific method ++ * to determine the next frequency, should use the get_target_freq() ++ * of struct devfreq_passive_data. ++ */ ++ if (p_data->get_target_freq) ++ return p_data->get_target_freq(devfreq, freq); ++ ++ switch (p_data->parent_type) { ++ case DEVFREQ_PARENT_DEV: ++ ret = get_target_freq_with_devfreq(devfreq, freq); ++ break; ++ case CPUFREQ_PARENT_DEV: ++ ret = get_target_freq_with_cpufreq(devfreq, freq); ++ break; ++ default: ++ ret = -EINVAL; ++ dev_err(&devfreq->dev, "Invalid parent type\n"); ++ break; ++ } ++ ++ return ret; ++} ++ ++static int cpufreq_passive_notifier_call(struct notifier_block *nb, ++ unsigned long event, void *ptr) ++{ ++ struct devfreq_passive_data *p_data = ++ container_of(nb, struct devfreq_passive_data, nb); ++ struct devfreq *devfreq = (struct devfreq *)p_data->this; ++ struct devfreq_cpu_data *parent_cpu_data; ++ struct cpufreq_freqs *freqs = ptr; ++ unsigned int cur_freq; ++ int ret; ++ ++ if (event != CPUFREQ_POSTCHANGE || !freqs || ++ !p_data->parent_cpu_data[freqs->policy->cpu]) ++ return 0; ++ ++ parent_cpu_data = p_data->parent_cpu_data[freqs->policy->cpu]; ++ if (parent_cpu_data->cur_freq == freqs->new) ++ return 0; ++ ++ cur_freq = parent_cpu_data->cur_freq; ++ parent_cpu_data->cur_freq = freqs->new; ++ ++ mutex_lock(&devfreq->lock); ++ ret = devfreq_update_target(devfreq, freqs->new); ++ mutex_unlock(&devfreq->lock); ++ if (ret) { ++ parent_cpu_data->cur_freq = cur_freq; ++ dev_err(&devfreq->dev, "failed to update the frequency.\n"); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int cpufreq_passive_unregister_notifier(struct devfreq *devfreq) ++{ ++ struct devfreq_passive_data *p_data ++ = (struct devfreq_passive_data *)devfreq->data; ++ struct devfreq_cpu_data *parent_cpu_data; ++ int cpu, ret; ++ ++ if (p_data->nb.notifier_call) { ++ ret = cpufreq_unregister_notifier(&p_data->nb, ++ CPUFREQ_TRANSITION_NOTIFIER); ++ if (ret < 0) ++ return ret; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ parent_cpu_data = p_data->parent_cpu_data[cpu]; ++ if (!parent_cpu_data) ++ continue; ++ ++ if (parent_cpu_data->opp_table) ++ dev_pm_opp_put_opp_table(parent_cpu_data->opp_table); ++ kfree(parent_cpu_data); ++ } ++ ++ return 0; ++} ++ ++static int cpufreq_passive_register_notifier(struct devfreq *devfreq) ++{ ++ struct devfreq_passive_data *p_data ++ = (struct devfreq_passive_data *)devfreq->data; ++ struct device *dev = devfreq->dev.parent; ++ struct opp_table *opp_table = NULL; ++ struct devfreq_cpu_data *parent_cpu_data; ++ struct cpufreq_policy *policy; ++ struct device *cpu_dev; ++ unsigned int cpu; ++ int ret; ++ ++ p_data->nb.notifier_call = cpufreq_passive_notifier_call; ++ ret = cpufreq_register_notifier(&p_data->nb, CPUFREQ_TRANSITION_NOTIFIER); ++ if (ret) { ++ dev_err(dev, "failed to register cpufreq notifier\n"); ++ p_data->nb.notifier_call = NULL; ++ goto err; ++ } ++ ++ for_each_possible_cpu(cpu) { ++ if (p_data->parent_cpu_data[cpu]) ++ continue; ++ ++ policy = cpufreq_cpu_get(cpu); ++ if (!policy) { ++ ret = -EPROBE_DEFER; ++ goto err; ++ } ++ ++ parent_cpu_data = kzalloc(sizeof(*parent_cpu_data), ++ GFP_KERNEL); ++ if (!parent_cpu_data) { ++ ret = -ENOMEM; ++ goto err_put_policy; ++ } ++ ++ cpu_dev = get_cpu_device(cpu); ++ if (!cpu_dev) { ++ dev_err(dev, "failed to get cpu device\n"); ++ ret = -ENODEV; ++ goto err_free_cpu_data; ++ } ++ ++ opp_table = dev_pm_opp_get_opp_table(cpu_dev); ++ if (IS_ERR(opp_table)) { ++ dev_err(dev, "failed to get opp_table of cpu%d\n", cpu); ++ ret = PTR_ERR(opp_table); ++ goto err_free_cpu_data; ++ } ++ ++ parent_cpu_data->dev = cpu_dev; ++ parent_cpu_data->opp_table = opp_table; ++ parent_cpu_data->first_cpu = cpumask_first(policy->related_cpus); ++ parent_cpu_data->cur_freq = policy->cur; ++ parent_cpu_data->min_freq = policy->cpuinfo.min_freq; ++ parent_cpu_data->max_freq = policy->cpuinfo.max_freq; ++ ++ p_data->parent_cpu_data[cpu] = parent_cpu_data; ++ cpufreq_cpu_put(policy); ++ } ++ ++ mutex_lock(&devfreq->lock); ++ ret = devfreq_update_target(devfreq, 0L); ++ mutex_unlock(&devfreq->lock); ++ if (ret) ++ dev_err(dev, "failed to update the frequency\n"); ++ ++ return ret; ++ ++err_free_cpu_data: ++ kfree(parent_cpu_data); ++err_put_policy: ++ cpufreq_cpu_put(policy); ++err: ++ WARN_ON(cpufreq_passive_unregister_notifier(devfreq)); ++ ++ return ret; ++} ++ + static int devfreq_passive_notifier_call(struct notifier_block *nb, + unsigned long event, void *ptr) + { +@@ -131,30 +380,55 @@ static int devfreq_passive_notifier_call(struct notifier_block *nb, + return NOTIFY_DONE; + } + +-static int devfreq_passive_event_handler(struct devfreq *devfreq, +- unsigned int event, void *data) ++static int devfreq_passive_unregister_notifier(struct devfreq *devfreq) ++{ ++ struct devfreq_passive_data *p_data ++ = (struct devfreq_passive_data *)devfreq->data; ++ struct devfreq *parent = (struct devfreq *)p_data->parent; ++ struct notifier_block *nb = &p_data->nb; ++ ++ return devfreq_unregister_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER); ++} ++ ++static int devfreq_passive_register_notifier(struct devfreq *devfreq) + { + struct devfreq_passive_data *p_data + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent = (struct devfreq *)p_data->parent; + struct notifier_block *nb = &p_data->nb; +- int ret = 0; + + if (!parent) + return -EPROBE_DEFER; + ++ nb->notifier_call = devfreq_passive_notifier_call; ++ return devfreq_register_notifier(parent, nb, DEVFREQ_TRANSITION_NOTIFIER); ++} ++ ++static int devfreq_passive_event_handler(struct devfreq *devfreq, ++ unsigned int event, void *data) ++{ ++ struct devfreq_passive_data *p_data ++ = (struct devfreq_passive_data *)devfreq->data; ++ int ret = -EINVAL; ++ ++ if (!p_data) ++ return -EINVAL; ++ ++ if (!p_data->this) ++ p_data->this = devfreq; ++ + switch (event) { + case DEVFREQ_GOV_START: +- if (!p_data->this) +- p_data->this = devfreq; +- +- nb->notifier_call = devfreq_passive_notifier_call; +- ret = devfreq_register_notifier(parent, nb, +- DEVFREQ_TRANSITION_NOTIFIER); ++ if (p_data->parent_type == DEVFREQ_PARENT_DEV) ++ ret = devfreq_passive_register_notifier(devfreq); ++ else if (p_data->parent_type == CPUFREQ_PARENT_DEV) ++ ret = cpufreq_passive_register_notifier(devfreq); + break; + case DEVFREQ_GOV_STOP: +- WARN_ON(devfreq_unregister_notifier(parent, nb, +- DEVFREQ_TRANSITION_NOTIFIER)); ++ if (p_data->parent_type == DEVFREQ_PARENT_DEV) ++ WARN_ON(devfreq_passive_unregister_notifier(devfreq)); ++ else if (p_data->parent_type == CPUFREQ_PARENT_DEV) ++ WARN_ON(cpufreq_passive_unregister_notifier(devfreq)); + break; + default: + break; +diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h +index d94b9ed9443e..86ef2885c04b 100644 +--- a/include/linux/devfreq.h ++++ b/include/linux/devfreq.h +@@ -38,6 +38,7 @@ enum devfreq_timer { + + struct devfreq; + struct devfreq_governor; ++struct devfreq_cpu_data; + struct thermal_cooling_device; + + /** +@@ -289,6 +290,11 @@ struct devfreq_simple_ondemand_data { + #endif + + #if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) ++enum devfreq_parent_dev_type { ++ DEVFREQ_PARENT_DEV, ++ CPUFREQ_PARENT_DEV, ++}; ++ + /** + * struct devfreq_passive_data - ``void *data`` fed to struct devfreq + * and devfreq_add_device +@@ -300,8 +306,11 @@ struct devfreq_simple_ondemand_data { + * using governors except for passive governor. + * If the devfreq device has the specific method to decide + * the next frequency, should use this callback. +- * @this: the devfreq instance of own device. +- * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER list ++ * @parent_type: the parent type of the device. ++ * @this: the devfreq instance of own device. ++ * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER or ++ * CPUFREQ_TRANSITION_NOTIFIER list. ++ * @parent_cpu_data: the state min/max/current frequency of all online cpu's. + * + * The devfreq_passive_data have to set the devfreq instance of parent + * device with governors except for the passive governor. But, don't need to +@@ -315,9 +324,13 @@ struct devfreq_passive_data { + /* Optional callback to decide the next frequency of passvice device */ + int (*get_target_freq)(struct devfreq *this, unsigned long *freq); + ++ /* Should set the type of parent device */ ++ enum devfreq_parent_dev_type parent_type; ++ + /* For passive governor's internal use. Don't need to set them */ + struct devfreq *this; + struct notifier_block nb; ++ struct devfreq_cpu_data *parent_cpu_data[NR_CPUS]; + }; + #endif + +-- +2.43.0 + diff --git a/queue-5.15/pm-devfreq-export-devfreq_get_freq_range-symbol-with.patch b/queue-5.15/pm-devfreq-export-devfreq_get_freq_range-symbol-with.patch new file mode 100644 index 00000000000..fb06604073f --- /dev/null +++ b/queue-5.15/pm-devfreq-export-devfreq_get_freq_range-symbol-with.patch @@ -0,0 +1,125 @@ +From 7cde1179118a6f4e0ea429fd4bb6659165ba0a96 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 1 Mar 2021 02:07:29 +0900 +Subject: PM / devfreq: Export devfreq_get_freq_range symbol within devfreq + +From: Chanwoo Choi + +[ Upstream commit 713472e53e6e53c985e283782b0fd76b8ecfd47e ] + +In order to get frequency range within devfreq governors, +export devfreq_get_freq_range symbol within devfreq. + +Reviewed-by: Matthias Kaehlcke +Tested-by: Chen-Yu Tsai +Tested-by: Johnson Wang +Signed-off-by: Chanwoo Choi +Stable-dep-of: 08e23d05fa6d ("PM / devfreq: Fix buffer overflow in trans_stat_show") +Signed-off-by: Sasha Levin +--- + drivers/devfreq/devfreq.c | 20 ++++++++++++-------- + drivers/devfreq/governor.h | 2 ++ + 2 files changed, 14 insertions(+), 8 deletions(-) + +diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c +index 29a14b0ffe33..c8cee796a5a0 100644 +--- a/drivers/devfreq/devfreq.c ++++ b/drivers/devfreq/devfreq.c +@@ -112,16 +112,16 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq) + } + + /** +- * get_freq_range() - Get the current freq range ++ * devfreq_get_freq_range() - Get the current freq range + * @devfreq: the devfreq instance + * @min_freq: the min frequency + * @max_freq: the max frequency + * + * This takes into consideration all constraints. + */ +-static void get_freq_range(struct devfreq *devfreq, +- unsigned long *min_freq, +- unsigned long *max_freq) ++void devfreq_get_freq_range(struct devfreq *devfreq, ++ unsigned long *min_freq, ++ unsigned long *max_freq) + { + unsigned long *freq_table = devfreq->profile->freq_table; + s32 qos_min_freq, qos_max_freq; +@@ -158,6 +158,7 @@ static void get_freq_range(struct devfreq *devfreq, + if (*min_freq > *max_freq) + *min_freq = *max_freq; + } ++EXPORT_SYMBOL(devfreq_get_freq_range); + + /** + * devfreq_get_freq_level() - Lookup freq_table for the frequency +@@ -418,7 +419,7 @@ int devfreq_update_target(struct devfreq *devfreq, unsigned long freq) + err = devfreq->governor->get_target_freq(devfreq, &freq); + if (err) + return err; +- get_freq_range(devfreq, &min_freq, &max_freq); ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); + + if (freq < min_freq) { + freq = min_freq; +@@ -785,6 +786,7 @@ struct devfreq *devfreq_add_device(struct device *dev, + { + struct devfreq *devfreq; + struct devfreq_governor *governor; ++ unsigned long min_freq, max_freq; + int err = 0; + + if (!dev || !profile || !governor_name) { +@@ -849,6 +851,8 @@ struct devfreq *devfreq_add_device(struct device *dev, + goto err_dev; + } + ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); ++ + devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); + devfreq->opp_table = dev_pm_opp_get_opp_table(dev); + if (IS_ERR(devfreq->opp_table)) +@@ -1560,7 +1564,7 @@ static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); +- get_freq_range(df, &min_freq, &max_freq); ++ devfreq_get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); + + return sprintf(buf, "%lu\n", min_freq); +@@ -1614,7 +1618,7 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); +- get_freq_range(df, &min_freq, &max_freq); ++ devfreq_get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); + + return sprintf(buf, "%lu\n", max_freq); +@@ -1928,7 +1932,7 @@ static int devfreq_summary_show(struct seq_file *s, void *data) + + mutex_lock(&devfreq->lock); + cur_freq = devfreq->previous_freq; +- get_freq_range(devfreq, &min_freq, &max_freq); ++ devfreq_get_freq_range(devfreq, &min_freq, &max_freq); + timer = devfreq->profile->timer; + + if (IS_SUPPORTED_ATTR(devfreq->governor->attrs, POLLING_INTERVAL)) +diff --git a/drivers/devfreq/governor.h b/drivers/devfreq/governor.h +index 2d69a0ce6291..9a9495f94ac6 100644 +--- a/drivers/devfreq/governor.h ++++ b/drivers/devfreq/governor.h +@@ -86,6 +86,8 @@ int devfreq_remove_governor(struct devfreq_governor *governor); + + int devfreq_update_status(struct devfreq *devfreq, unsigned long freq); + int devfreq_update_target(struct devfreq *devfreq, unsigned long freq); ++void devfreq_get_freq_range(struct devfreq *devfreq, unsigned long *min_freq, ++ unsigned long *max_freq); + + static inline int devfreq_update_stats(struct devfreq *df) + { +-- +2.43.0 + diff --git a/queue-5.15/pm-devfreq-fix-buffer-overflow-in-trans_stat_show.patch b/queue-5.15/pm-devfreq-fix-buffer-overflow-in-trans_stat_show.patch new file mode 100644 index 00000000000..47bd00f933c --- /dev/null +++ b/queue-5.15/pm-devfreq-fix-buffer-overflow-in-trans_stat_show.patch @@ -0,0 +1,143 @@ +From 0ad57c14361cffe2434a4de131b2a24f0898300a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 24 Oct 2023 20:30:15 +0200 +Subject: PM / devfreq: Fix buffer overflow in trans_stat_show + +From: Christian Marangi + +[ Upstream commit 08e23d05fa6dc4fc13da0ccf09defdd4bbc92ff4 ] + +Fix buffer overflow in trans_stat_show(). + +Convert simple snprintf to the more secure scnprintf with size of +PAGE_SIZE. + +Add condition checking if we are exceeding PAGE_SIZE and exit early from +loop. Also add at the end a warning that we exceeded PAGE_SIZE and that +stats is disabled. + +Return -EFBIG in the case where we don't have enough space to write the +full transition table. + +Also document in the ABI that this function can return -EFBIG error. + +Link: https://lore.kernel.org/all/20231024183016.14648-2-ansuelsmth@gmail.com/ +Cc: stable@vger.kernel.org +Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218041 +Fixes: e552bbaf5b98 ("PM / devfreq: Add sysfs node for representing frequency transition information.") +Signed-off-by: Christian Marangi +Signed-off-by: Chanwoo Choi +Signed-off-by: Sasha Levin +--- + Documentation/ABI/testing/sysfs-class-devfreq | 3 + + drivers/devfreq/devfreq.c | 57 +++++++++++++------ + 2 files changed, 42 insertions(+), 18 deletions(-) + +diff --git a/Documentation/ABI/testing/sysfs-class-devfreq b/Documentation/ABI/testing/sysfs-class-devfreq +index 5e6b74f30406..1e7e0bb4c14e 100644 +--- a/Documentation/ABI/testing/sysfs-class-devfreq ++++ b/Documentation/ABI/testing/sysfs-class-devfreq +@@ -52,6 +52,9 @@ Description: + + echo 0 > /sys/class/devfreq/.../trans_stat + ++ If the transition table is bigger than PAGE_SIZE, reading ++ this will return an -EFBIG error. ++ + What: /sys/class/devfreq/.../available_frequencies + Date: October 2012 + Contact: Nishanth Menon +diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c +index 14b83b8531d7..96dc49c207c7 100644 +--- a/drivers/devfreq/devfreq.c ++++ b/drivers/devfreq/devfreq.c +@@ -1656,7 +1656,7 @@ static ssize_t trans_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) + { + struct devfreq *df = to_devfreq(dev); +- ssize_t len; ++ ssize_t len = 0; + int i, j; + unsigned int max_state; + +@@ -1665,7 +1665,7 @@ static ssize_t trans_stat_show(struct device *dev, + max_state = df->max_state; + + if (max_state == 0) +- return sprintf(buf, "Not Supported.\n"); ++ return scnprintf(buf, PAGE_SIZE, "Not Supported.\n"); + + mutex_lock(&df->lock); + if (!df->stop_polling && +@@ -1675,31 +1675,52 @@ static ssize_t trans_stat_show(struct device *dev, + } + mutex_unlock(&df->lock); + +- len = sprintf(buf, " From : To\n"); +- len += sprintf(buf + len, " :"); +- for (i = 0; i < max_state; i++) +- len += sprintf(buf + len, "%10lu", +- df->freq_table[i]); ++ len += scnprintf(buf + len, PAGE_SIZE - len, " From : To\n"); ++ len += scnprintf(buf + len, PAGE_SIZE - len, " :"); ++ for (i = 0; i < max_state; i++) { ++ if (len >= PAGE_SIZE - 1) ++ break; ++ len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu", ++ df->freq_table[i]); ++ } ++ if (len >= PAGE_SIZE - 1) ++ return PAGE_SIZE - 1; + +- len += sprintf(buf + len, " time(ms)\n"); ++ len += scnprintf(buf + len, PAGE_SIZE - len, " time(ms)\n"); + + for (i = 0; i < max_state; i++) { ++ if (len >= PAGE_SIZE - 1) ++ break; + if (df->freq_table[i] == df->previous_freq) +- len += sprintf(buf + len, "*"); ++ len += scnprintf(buf + len, PAGE_SIZE - len, "*"); + else +- len += sprintf(buf + len, " "); ++ len += scnprintf(buf + len, PAGE_SIZE - len, " "); ++ if (len >= PAGE_SIZE - 1) ++ break; ++ ++ len += scnprintf(buf + len, PAGE_SIZE - len, "%10lu:", ++ df->freq_table[i]); ++ for (j = 0; j < max_state; j++) { ++ if (len >= PAGE_SIZE - 1) ++ break; ++ len += scnprintf(buf + len, PAGE_SIZE - len, "%10u", ++ df->stats.trans_table[(i * max_state) + j]); ++ } ++ if (len >= PAGE_SIZE - 1) ++ break; ++ len += scnprintf(buf + len, PAGE_SIZE - len, "%10llu\n", (u64) ++ jiffies64_to_msecs(df->stats.time_in_state[i])); ++ } + +- len += sprintf(buf + len, "%10lu:", df->freq_table[i]); +- for (j = 0; j < max_state; j++) +- len += sprintf(buf + len, "%10u", +- df->stats.trans_table[(i * max_state) + j]); ++ if (len < PAGE_SIZE - 1) ++ len += scnprintf(buf + len, PAGE_SIZE - len, "Total transition : %u\n", ++ df->stats.total_trans); + +- len += sprintf(buf + len, "%10llu\n", (u64) +- jiffies64_to_msecs(df->stats.time_in_state[i])); ++ if (len >= PAGE_SIZE - 1) { ++ pr_warn_once("devfreq transition table exceeds PAGE_SIZE. Disabling\n"); ++ return -EFBIG; + } + +- len += sprintf(buf + len, "Total transition : %u\n", +- df->stats.total_trans); + return len; + } + +-- +2.43.0 + diff --git a/queue-5.15/pm-devfreq-passive-reduce-duplicate-code-when-passiv.patch b/queue-5.15/pm-devfreq-passive-reduce-duplicate-code-when-passiv.patch new file mode 100644 index 00000000000..cc6e1e1f51c --- /dev/null +++ b/queue-5.15/pm-devfreq-passive-reduce-duplicate-code-when-passiv.patch @@ -0,0 +1,121 @@ +From 38a1b2738c0915ede2421ea5df780f4ccbc99da0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 2 Mar 2021 17:22:50 +0900 +Subject: PM / devfreq: passive: Reduce duplicate code when passive_devfreq + case + +From: Chanwoo Choi + +[ Upstream commit 05723e71234b60a1a47313ea1a889797ec648f1c ] + +In order to keep the consistent coding style between passive_devfreq +and passive_cpufreq, use common code for handling required opp property. +Also remove the unneed conditional statement and unify the comment +of both passive_devfreq and passive_cpufreq when getting the target frequency. + +Tested-by: Chen-Yu Tsai +Tested-by: Johnson Wang +Signed-off-by: Chanwoo Choi +Stable-dep-of: 08e23d05fa6d ("PM / devfreq: Fix buffer overflow in trans_stat_show") +Signed-off-by: Sasha Levin +--- + drivers/devfreq/governor_passive.c | 66 ++++-------------------------- + 1 file changed, 8 insertions(+), 58 deletions(-) + +diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c +index 7f30088b500b..ffcce613a48c 100644 +--- a/drivers/devfreq/governor_passive.c ++++ b/drivers/devfreq/governor_passive.c +@@ -93,65 +93,16 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq, + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; + unsigned long child_freq = ULONG_MAX; +- struct dev_pm_opp *opp, *p_opp; + int i, count; + +- /* +- * If the devfreq device with passive governor has the specific method +- * to determine the next frequency, should use the get_target_freq() +- * of struct devfreq_passive_data. +- */ +- if (p_data->get_target_freq) +- return p_data->get_target_freq(devfreq, freq); ++ /* Get target freq via required opps */ ++ child_freq = get_target_freq_by_required_opp(parent_devfreq->dev.parent, ++ parent_devfreq->opp_table, ++ devfreq->opp_table, freq); ++ if (child_freq) ++ goto out; + +- /* +- * If the parent and passive devfreq device uses the OPP table, +- * get the next frequency by using the OPP table. +- */ +- +- /* +- * - parent devfreq device uses the governors except for passive. +- * - passive devfreq device uses the passive governor. +- * +- * Each devfreq has the OPP table. After deciding the new frequency +- * from the governor of parent devfreq device, the passive governor +- * need to get the index of new frequency on OPP table of parent +- * device. And then the index is used for getting the suitable +- * new frequency for passive devfreq device. +- */ +- if (!devfreq->profile || !devfreq->profile->freq_table +- || devfreq->profile->max_state <= 0) +- return -EINVAL; +- +- /* +- * The passive governor have to get the correct frequency from OPP +- * list of parent device. Because in this case, *freq is temporary +- * value which is decided by ondemand governor. +- */ +- if (devfreq->opp_table && parent_devfreq->opp_table) { +- p_opp = devfreq_recommended_opp(parent_devfreq->dev.parent, +- freq, 0); +- if (IS_ERR(p_opp)) +- return PTR_ERR(p_opp); +- +- opp = dev_pm_opp_xlate_required_opp(parent_devfreq->opp_table, +- devfreq->opp_table, p_opp); +- dev_pm_opp_put(p_opp); +- +- if (IS_ERR(opp)) +- goto no_required_opp; +- +- *freq = dev_pm_opp_get_freq(opp); +- dev_pm_opp_put(opp); +- +- return 0; +- } +- +-no_required_opp: +- /* +- * Get the OPP table's index of decided frequency by governor +- * of parent device. +- */ ++ /* Use interpolation if required opps is not available */ + for (i = 0; i < parent_devfreq->profile->max_state; i++) + if (parent_devfreq->profile->freq_table[i] == *freq) + break; +@@ -159,7 +110,6 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq, + if (i == parent_devfreq->profile->max_state) + return -EINVAL; + +- /* Get the suitable frequency by using index of parent device. */ + if (i < devfreq->profile->max_state) { + child_freq = devfreq->profile->freq_table[i]; + } else { +@@ -167,7 +117,7 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq, + child_freq = devfreq->profile->freq_table[count - 1]; + } + +- /* Return the suitable frequency for passive device. */ ++out: + *freq = child_freq; + + return 0; +-- +2.43.0 + diff --git a/queue-5.15/pm-devfreq-rework-freq_table-to-be-local-to-devfreq-.patch b/queue-5.15/pm-devfreq-rework-freq_table-to-be-local-to-devfreq-.patch new file mode 100644 index 00000000000..e81ca3a3c9f --- /dev/null +++ b/queue-5.15/pm-devfreq-rework-freq_table-to-be-local-to-devfreq-.patch @@ -0,0 +1,287 @@ +From 648562db5d7b7e518c785b460472ccd2fbdb1ecc Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 20 Jun 2022 00:03:51 +0200 +Subject: PM / devfreq: Rework freq_table to be local to devfreq struct + +From: Christian Marangi + +[ Upstream commit b5d281f6c16dd432b618bdfd36ddba1a58d5b603 ] + +On a devfreq PROBE_DEFER, the freq_table in the driver profile struct, +is never reset and may be leaved in an undefined state. + +This comes from the fact that we store the freq_table in the driver +profile struct that is commonly defined as static and not reset on +PROBE_DEFER. +We currently skip the reinit of the freq_table if we found +it's already defined since a driver may declare his own freq_table. + +This logic is flawed in the case devfreq core generate a freq_table, set +it in the profile struct and then PROBE_DEFER, freeing the freq_table. +In this case devfreq will found a NOT NULL freq_table that has been +freed, skip the freq_table generation and probe the driver based on the +wrong table. + +To fix this and correctly handle PROBE_DEFER, use a local freq_table and +max_state in the devfreq struct and never modify the freq_table present +in the profile struct if it does provide it. + +Fixes: 0ec09ac2cebe ("PM / devfreq: Set the freq_table of devfreq device") +Cc: stable@vger.kernel.org +Signed-off-by: Christian Marangi +Signed-off-by: Chanwoo Choi +Stable-dep-of: 08e23d05fa6d ("PM / devfreq: Fix buffer overflow in trans_stat_show") +Signed-off-by: Sasha Levin +--- + drivers/devfreq/devfreq.c | 71 ++++++++++++++---------------- + drivers/devfreq/governor_passive.c | 14 +++--- + include/linux/devfreq.h | 5 +++ + 3 files changed, 46 insertions(+), 44 deletions(-) + +diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c +index c8cee796a5a0..14b83b8531d7 100644 +--- a/drivers/devfreq/devfreq.c ++++ b/drivers/devfreq/devfreq.c +@@ -123,7 +123,7 @@ void devfreq_get_freq_range(struct devfreq *devfreq, + unsigned long *min_freq, + unsigned long *max_freq) + { +- unsigned long *freq_table = devfreq->profile->freq_table; ++ unsigned long *freq_table = devfreq->freq_table; + s32 qos_min_freq, qos_max_freq; + + lockdep_assert_held(&devfreq->lock); +@@ -133,11 +133,11 @@ void devfreq_get_freq_range(struct devfreq *devfreq, + * The devfreq drivers can initialize this in either ascending or + * descending order and devfreq core supports both. + */ +- if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { ++ if (freq_table[0] < freq_table[devfreq->max_state - 1]) { + *min_freq = freq_table[0]; +- *max_freq = freq_table[devfreq->profile->max_state - 1]; ++ *max_freq = freq_table[devfreq->max_state - 1]; + } else { +- *min_freq = freq_table[devfreq->profile->max_state - 1]; ++ *min_freq = freq_table[devfreq->max_state - 1]; + *max_freq = freq_table[0]; + } + +@@ -169,8 +169,8 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) + { + int lev; + +- for (lev = 0; lev < devfreq->profile->max_state; lev++) +- if (freq == devfreq->profile->freq_table[lev]) ++ for (lev = 0; lev < devfreq->max_state; lev++) ++ if (freq == devfreq->freq_table[lev]) + return lev; + + return -EINVAL; +@@ -178,7 +178,6 @@ static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq) + + static int set_freq_table(struct devfreq *devfreq) + { +- struct devfreq_dev_profile *profile = devfreq->profile; + struct dev_pm_opp *opp; + unsigned long freq; + int i, count; +@@ -188,25 +187,22 @@ static int set_freq_table(struct devfreq *devfreq) + if (count <= 0) + return -EINVAL; + +- profile->max_state = count; +- profile->freq_table = devm_kcalloc(devfreq->dev.parent, +- profile->max_state, +- sizeof(*profile->freq_table), +- GFP_KERNEL); +- if (!profile->freq_table) { +- profile->max_state = 0; ++ devfreq->max_state = count; ++ devfreq->freq_table = devm_kcalloc(devfreq->dev.parent, ++ devfreq->max_state, ++ sizeof(*devfreq->freq_table), ++ GFP_KERNEL); ++ if (!devfreq->freq_table) + return -ENOMEM; +- } + +- for (i = 0, freq = 0; i < profile->max_state; i++, freq++) { ++ for (i = 0, freq = 0; i < devfreq->max_state; i++, freq++) { + opp = dev_pm_opp_find_freq_ceil(devfreq->dev.parent, &freq); + if (IS_ERR(opp)) { +- devm_kfree(devfreq->dev.parent, profile->freq_table); +- profile->max_state = 0; ++ devm_kfree(devfreq->dev.parent, devfreq->freq_table); + return PTR_ERR(opp); + } + dev_pm_opp_put(opp); +- profile->freq_table[i] = freq; ++ devfreq->freq_table[i] = freq; + } + + return 0; +@@ -246,7 +242,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) + + if (lev != prev_lev) { + devfreq->stats.trans_table[ +- (prev_lev * devfreq->profile->max_state) + lev]++; ++ (prev_lev * devfreq->max_state) + lev]++; + devfreq->stats.total_trans++; + } + +@@ -835,6 +831,9 @@ struct devfreq *devfreq_add_device(struct device *dev, + if (err < 0) + goto err_dev; + mutex_lock(&devfreq->lock); ++ } else { ++ devfreq->freq_table = devfreq->profile->freq_table; ++ devfreq->max_state = devfreq->profile->max_state; + } + + devfreq->scaling_min_freq = find_available_min_freq(devfreq); +@@ -870,8 +869,8 @@ struct devfreq *devfreq_add_device(struct device *dev, + + devfreq->stats.trans_table = devm_kzalloc(&devfreq->dev, + array3_size(sizeof(unsigned int), +- devfreq->profile->max_state, +- devfreq->profile->max_state), ++ devfreq->max_state, ++ devfreq->max_state), + GFP_KERNEL); + if (!devfreq->stats.trans_table) { + mutex_unlock(&devfreq->lock); +@@ -880,7 +879,7 @@ struct devfreq *devfreq_add_device(struct device *dev, + } + + devfreq->stats.time_in_state = devm_kcalloc(&devfreq->dev, +- devfreq->profile->max_state, ++ devfreq->max_state, + sizeof(*devfreq->stats.time_in_state), + GFP_KERNEL); + if (!devfreq->stats.time_in_state) { +@@ -1638,9 +1637,9 @@ static ssize_t available_frequencies_show(struct device *d, + + mutex_lock(&df->lock); + +- for (i = 0; i < df->profile->max_state; i++) ++ for (i = 0; i < df->max_state; i++) + count += scnprintf(&buf[count], (PAGE_SIZE - count - 2), +- "%lu ", df->profile->freq_table[i]); ++ "%lu ", df->freq_table[i]); + + mutex_unlock(&df->lock); + /* Truncate the trailing space */ +@@ -1663,7 +1662,7 @@ static ssize_t trans_stat_show(struct device *dev, + + if (!df->profile) + return -EINVAL; +- max_state = df->profile->max_state; ++ max_state = df->max_state; + + if (max_state == 0) + return sprintf(buf, "Not Supported.\n"); +@@ -1680,19 +1679,17 @@ static ssize_t trans_stat_show(struct device *dev, + len += sprintf(buf + len, " :"); + for (i = 0; i < max_state; i++) + len += sprintf(buf + len, "%10lu", +- df->profile->freq_table[i]); ++ df->freq_table[i]); + + len += sprintf(buf + len, " time(ms)\n"); + + for (i = 0; i < max_state; i++) { +- if (df->profile->freq_table[i] +- == df->previous_freq) { ++ if (df->freq_table[i] == df->previous_freq) + len += sprintf(buf + len, "*"); +- } else { ++ else + len += sprintf(buf + len, " "); +- } +- len += sprintf(buf + len, "%10lu:", +- df->profile->freq_table[i]); ++ ++ len += sprintf(buf + len, "%10lu:", df->freq_table[i]); + for (j = 0; j < max_state; j++) + len += sprintf(buf + len, "%10u", + df->stats.trans_table[(i * max_state) + j]); +@@ -1716,7 +1713,7 @@ static ssize_t trans_stat_store(struct device *dev, + if (!df->profile) + return -EINVAL; + +- if (df->profile->max_state == 0) ++ if (df->max_state == 0) + return count; + + err = kstrtoint(buf, 10, &value); +@@ -1724,11 +1721,11 @@ static ssize_t trans_stat_store(struct device *dev, + return -EINVAL; + + mutex_lock(&df->lock); +- memset(df->stats.time_in_state, 0, (df->profile->max_state * ++ memset(df->stats.time_in_state, 0, (df->max_state * + sizeof(*df->stats.time_in_state))); + memset(df->stats.trans_table, 0, array3_size(sizeof(unsigned int), +- df->profile->max_state, +- df->profile->max_state)); ++ df->max_state, ++ df->max_state)); + df->stats.total_trans = 0; + df->stats.last_update = get_jiffies_64(); + mutex_unlock(&df->lock); +diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c +index ffcce613a48c..9997332af3df 100644 +--- a/drivers/devfreq/governor_passive.c ++++ b/drivers/devfreq/governor_passive.c +@@ -103,18 +103,18 @@ static int get_target_freq_with_devfreq(struct devfreq *devfreq, + goto out; + + /* Use interpolation if required opps is not available */ +- for (i = 0; i < parent_devfreq->profile->max_state; i++) +- if (parent_devfreq->profile->freq_table[i] == *freq) ++ for (i = 0; i < parent_devfreq->max_state; i++) ++ if (parent_devfreq->freq_table[i] == *freq) + break; + +- if (i == parent_devfreq->profile->max_state) ++ if (i == parent_devfreq->max_state) + return -EINVAL; + +- if (i < devfreq->profile->max_state) { +- child_freq = devfreq->profile->freq_table[i]; ++ if (i < devfreq->max_state) { ++ child_freq = devfreq->freq_table[i]; + } else { +- count = devfreq->profile->max_state; +- child_freq = devfreq->profile->freq_table[count - 1]; ++ count = devfreq->max_state; ++ child_freq = devfreq->freq_table[count - 1]; + } + + out: +diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h +index 86ef2885c04b..6084003ea509 100644 +--- a/include/linux/devfreq.h ++++ b/include/linux/devfreq.h +@@ -148,6 +148,8 @@ struct devfreq_stats { + * reevaluate operable frequencies. Devfreq users may use + * devfreq.nb to the corresponding register notifier call chain. + * @work: delayed work for load monitoring. ++ * @freq_table: current frequency table used by the devfreq driver. ++ * @max_state: count of entry present in the frequency table. + * @previous_freq: previously configured frequency value. + * @last_status: devfreq user device info, performance statistics + * @data: devfreq driver pass to governors, governor should not change it. +@@ -185,6 +187,9 @@ struct devfreq { + struct notifier_block nb; + struct delayed_work work; + ++ unsigned long *freq_table; ++ unsigned int max_state; ++ + unsigned long previous_freq; + struct devfreq_dev_status last_status; + +-- +2.43.0 + diff --git a/queue-5.15/pm-sleep-fix-possible-deadlocks-in-core-system-wide-.patch b/queue-5.15/pm-sleep-fix-possible-deadlocks-in-core-system-wide-.patch new file mode 100644 index 00000000000..7a7159aab05 --- /dev/null +++ b/queue-5.15/pm-sleep-fix-possible-deadlocks-in-core-system-wide-.patch @@ -0,0 +1,329 @@ +From 952ee00ffbbc1090c33acf503238117f089595df Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 27 Dec 2023 21:41:06 +0100 +Subject: PM: sleep: Fix possible deadlocks in core system-wide PM code + +From: Rafael J. Wysocki + +[ Upstream commit 7839d0078e0d5e6cc2fa0b0dfbee71de74f1e557 ] + +It is reported that in low-memory situations the system-wide resume core +code deadlocks, because async_schedule_dev() executes its argument +function synchronously if it cannot allocate memory (and not only in +that case) and that function attempts to acquire a mutex that is already +held. Executing the argument function synchronously from within +dpm_async_fn() may also be problematic for ordering reasons (it may +cause a consumer device's resume callback to be invoked before a +requisite supplier device's one, for example). + +Address this by changing the code in question to use +async_schedule_dev_nocall() for scheduling the asynchronous +execution of device suspend and resume functions and to directly +run them synchronously if async_schedule_dev_nocall() returns false. + +Link: https://lore.kernel.org/linux-pm/ZYvjiqX6EsL15moe@perf/ +Reported-by: Youngmin Nam +Signed-off-by: Rafael J. Wysocki +Reviewed-by: Stanislaw Gruszka +Tested-by: Youngmin Nam +Reviewed-by: Ulf Hansson +Cc: 5.7+ # 5.7+: 6aa09a5bccd8 async: Split async_schedule_node_domain() +Cc: 5.7+ # 5.7+: 7d4b5d7a37bd async: Introduce async_schedule_dev_nocall() +Cc: 5.7+ # 5.7+ +Signed-off-by: Sasha Levin +--- + drivers/base/power/main.c | 148 ++++++++++++++++++-------------------- + 1 file changed, 68 insertions(+), 80 deletions(-) + +diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c +index b4cd003d3e39..185ea0d93a5e 100644 +--- a/drivers/base/power/main.c ++++ b/drivers/base/power/main.c +@@ -580,7 +580,7 @@ bool dev_pm_skip_resume(struct device *dev) + } + + /** +- * device_resume_noirq - Execute a "noirq resume" callback for given device. ++ * __device_resume_noirq - Execute a "noirq resume" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. + * @async: If true, the device is being resumed asynchronously. +@@ -588,7 +588,7 @@ bool dev_pm_skip_resume(struct device *dev) + * The driver of @dev will not receive interrupts while this function is being + * executed. + */ +-static int device_resume_noirq(struct device *dev, pm_message_t state, bool async) ++static void __device_resume_noirq(struct device *dev, pm_message_t state, bool async) + { + pm_callback_t callback = NULL; + const char *info = NULL; +@@ -656,7 +656,13 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn + Out: + complete_all(&dev->power.completion); + TRACE_RESUME(error); +- return error; ++ ++ if (error) { ++ suspend_stats.failed_resume_noirq++; ++ dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); ++ dpm_save_failed_dev(dev_name(dev)); ++ pm_dev_err(dev, state, async ? " async noirq" : " noirq", error); ++ } + } + + static bool is_async(struct device *dev) +@@ -669,11 +675,15 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) + { + reinit_completion(&dev->power.completion); + +- if (is_async(dev)) { +- get_device(dev); +- async_schedule_dev(func, dev); ++ if (!is_async(dev)) ++ return false; ++ ++ get_device(dev); ++ ++ if (async_schedule_dev_nocall(func, dev)) + return true; +- } ++ ++ put_device(dev); + + return false; + } +@@ -681,15 +691,19 @@ static bool dpm_async_fn(struct device *dev, async_func_t func) + static void async_resume_noirq(void *data, async_cookie_t cookie) + { + struct device *dev = data; +- int error; +- +- error = device_resume_noirq(dev, pm_transition, true); +- if (error) +- pm_dev_err(dev, pm_transition, " async", error); + ++ __device_resume_noirq(dev, pm_transition, true); + put_device(dev); + } + ++static void device_resume_noirq(struct device *dev) ++{ ++ if (dpm_async_fn(dev, async_resume_noirq)) ++ return; ++ ++ __device_resume_noirq(dev, pm_transition, false); ++} ++ + static void dpm_noirq_resume_devices(pm_message_t state) + { + struct device *dev; +@@ -699,14 +713,6 @@ static void dpm_noirq_resume_devices(pm_message_t state) + mutex_lock(&dpm_list_mtx); + pm_transition = state; + +- /* +- * Advanced the async threads upfront, +- * in case the starting of async threads is +- * delayed by non-async resuming devices. +- */ +- list_for_each_entry(dev, &dpm_noirq_list, power.entry) +- dpm_async_fn(dev, async_resume_noirq); +- + while (!list_empty(&dpm_noirq_list)) { + dev = to_device(dpm_noirq_list.next); + get_device(dev); +@@ -714,17 +720,7 @@ static void dpm_noirq_resume_devices(pm_message_t state) + + mutex_unlock(&dpm_list_mtx); + +- if (!is_async(dev)) { +- int error; +- +- error = device_resume_noirq(dev, state, false); +- if (error) { +- suspend_stats.failed_resume_noirq++; +- dpm_save_failed_step(SUSPEND_RESUME_NOIRQ); +- dpm_save_failed_dev(dev_name(dev)); +- pm_dev_err(dev, state, " noirq", error); +- } +- } ++ device_resume_noirq(dev); + + put_device(dev); + +@@ -754,14 +750,14 @@ void dpm_resume_noirq(pm_message_t state) + } + + /** +- * device_resume_early - Execute an "early resume" callback for given device. ++ * __device_resume_early - Execute an "early resume" callback for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. + * @async: If true, the device is being resumed asynchronously. + * + * Runtime PM is disabled for @dev while this function is being executed. + */ +-static int device_resume_early(struct device *dev, pm_message_t state, bool async) ++static void __device_resume_early(struct device *dev, pm_message_t state, bool async) + { + pm_callback_t callback = NULL; + const char *info = NULL; +@@ -814,21 +810,31 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn + + pm_runtime_enable(dev); + complete_all(&dev->power.completion); +- return error; ++ ++ if (error) { ++ suspend_stats.failed_resume_early++; ++ dpm_save_failed_step(SUSPEND_RESUME_EARLY); ++ dpm_save_failed_dev(dev_name(dev)); ++ pm_dev_err(dev, state, async ? " async early" : " early", error); ++ } + } + + static void async_resume_early(void *data, async_cookie_t cookie) + { + struct device *dev = data; +- int error; +- +- error = device_resume_early(dev, pm_transition, true); +- if (error) +- pm_dev_err(dev, pm_transition, " async", error); + ++ __device_resume_early(dev, pm_transition, true); + put_device(dev); + } + ++static void device_resume_early(struct device *dev) ++{ ++ if (dpm_async_fn(dev, async_resume_early)) ++ return; ++ ++ __device_resume_early(dev, pm_transition, false); ++} ++ + /** + * dpm_resume_early - Execute "early resume" callbacks for all devices. + * @state: PM transition of the system being carried out. +@@ -842,14 +848,6 @@ void dpm_resume_early(pm_message_t state) + mutex_lock(&dpm_list_mtx); + pm_transition = state; + +- /* +- * Advanced the async threads upfront, +- * in case the starting of async threads is +- * delayed by non-async resuming devices. +- */ +- list_for_each_entry(dev, &dpm_late_early_list, power.entry) +- dpm_async_fn(dev, async_resume_early); +- + while (!list_empty(&dpm_late_early_list)) { + dev = to_device(dpm_late_early_list.next); + get_device(dev); +@@ -857,17 +855,7 @@ void dpm_resume_early(pm_message_t state) + + mutex_unlock(&dpm_list_mtx); + +- if (!is_async(dev)) { +- int error; +- +- error = device_resume_early(dev, state, false); +- if (error) { +- suspend_stats.failed_resume_early++; +- dpm_save_failed_step(SUSPEND_RESUME_EARLY); +- dpm_save_failed_dev(dev_name(dev)); +- pm_dev_err(dev, state, " early", error); +- } +- } ++ device_resume_early(dev); + + put_device(dev); + +@@ -891,12 +879,12 @@ void dpm_resume_start(pm_message_t state) + EXPORT_SYMBOL_GPL(dpm_resume_start); + + /** +- * device_resume - Execute "resume" callbacks for given device. ++ * __device_resume - Execute "resume" callbacks for given device. + * @dev: Device to handle. + * @state: PM transition of the system being carried out. + * @async: If true, the device is being resumed asynchronously. + */ +-static int device_resume(struct device *dev, pm_message_t state, bool async) ++static void __device_resume(struct device *dev, pm_message_t state, bool async) + { + pm_callback_t callback = NULL; + const char *info = NULL; +@@ -978,20 +966,30 @@ static int device_resume(struct device *dev, pm_message_t state, bool async) + + TRACE_RESUME(error); + +- return error; ++ if (error) { ++ suspend_stats.failed_resume++; ++ dpm_save_failed_step(SUSPEND_RESUME); ++ dpm_save_failed_dev(dev_name(dev)); ++ pm_dev_err(dev, state, async ? " async" : "", error); ++ } + } + + static void async_resume(void *data, async_cookie_t cookie) + { + struct device *dev = data; +- int error; + +- error = device_resume(dev, pm_transition, true); +- if (error) +- pm_dev_err(dev, pm_transition, " async", error); ++ __device_resume(dev, pm_transition, true); + put_device(dev); + } + ++static void device_resume(struct device *dev) ++{ ++ if (dpm_async_fn(dev, async_resume)) ++ return; ++ ++ __device_resume(dev, pm_transition, false); ++} ++ + /** + * dpm_resume - Execute "resume" callbacks for non-sysdev devices. + * @state: PM transition of the system being carried out. +@@ -1011,27 +1009,17 @@ void dpm_resume(pm_message_t state) + pm_transition = state; + async_error = 0; + +- list_for_each_entry(dev, &dpm_suspended_list, power.entry) +- dpm_async_fn(dev, async_resume); +- + while (!list_empty(&dpm_suspended_list)) { + dev = to_device(dpm_suspended_list.next); ++ + get_device(dev); +- if (!is_async(dev)) { +- int error; + +- mutex_unlock(&dpm_list_mtx); ++ mutex_unlock(&dpm_list_mtx); ++ ++ device_resume(dev); + +- error = device_resume(dev, state, false); +- if (error) { +- suspend_stats.failed_resume++; +- dpm_save_failed_step(SUSPEND_RESUME); +- dpm_save_failed_dev(dev_name(dev)); +- pm_dev_err(dev, state, "", error); +- } ++ mutex_lock(&dpm_list_mtx); + +- mutex_lock(&dpm_list_mtx); +- } + if (!list_empty(&dev->power.entry)) + list_move_tail(&dev->power.entry, &dpm_prepared_list); + +-- +2.43.0 + diff --git a/queue-5.15/series b/queue-5.15/series index 7d09d68af2f..256c015788b 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -94,3 +94,30 @@ drm-bridge-nxp-ptn3460-fix-i2c_master_send-error-checking.patch drm-tidss-fix-atomic_flush-check.patch drm-bridge-nxp-ptn3460-simplify-some-error-checking.patch cifs-fix-off-by-one-in-smb2_query_info_init.patch +pm-core-remove-unnecessary-void-conversions.patch +pm-sleep-fix-possible-deadlocks-in-core-system-wide-.patch +bus-mhi-host-rename-struct-mhi_tre-to-struct-mhi_rin.patch +bus-mhi-host-add-alignment-check-for-event-ring-read.patch +fs-pipe-move-check-to-pipe_has_watch_queue.patch +pipe-wakeup-wr_wait-after-setting-max_usage.patch +arm-dts-qcom-sdx55-fix-usb-wakeup-interrupt-types.patch +arm-dts-samsung-exynos4210-i9100-unconditionally-ena.patch +arm-dts-qcom-sdx55-fix-pdc-interrupt-cells.patch +arm-dts-qcom-sdx55-fix-usb-dp-dm-hs-phy-interrupts.patch +arm-dts-qcom-sdx55-fix-usb-ss-wakeup.patch +media-mtk-jpeg-fix-use-after-free-bug-due-to-error-p.patch +mm-use-__pfn_to_section-instead-of-open-coding-it.patch +mm-sparsemem-fix-race-in-accessing-memory_section-us.patch +pm-devfreq-export-devfreq_get_freq_range-symbol-with.patch +pm-devfreq-add-cpu-based-scaling-support-to-passive-.patch +pm-devfreq-passive-reduce-duplicate-code-when-passiv.patch +pm-devfreq-rework-freq_table-to-be-local-to-devfreq-.patch +pm-devfreq-fix-buffer-overflow-in-trans_stat_show.patch +btrfs-add-definition-for-extent_tree_v2.patch +btrfs-don-t-abort-filesystem-when-attempting-to-snap.patch +nfsd-modernize-nfsd4_release_lockowner.patch +nfsd-add-documenting-comment-for-nfsd4_release_locko.patch +nfsd-fix-release_lockowner.patch +ksmbd-fix-global-oob-in-ksmbd_nl_policy.patch +cpufreq-intel_pstate-drop-redundant-intel_pstate_get.patch +cpufreq-intel_pstate-refine-computation-of-p-state-f.patch