6.12.110-rc1 review
Subsystems (7)
Greg Kroah-Hartman
maintainerPATCH 1050/1376 integrity: Make arch_ima_get_secureboot integrity-wide
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coiby Xu <[email protected]>
[ Upstream commit 31a6a07eefeb4c84bd6730fbe9e95fd9221712cf ]
EVM and other LSMs need the ability to query the secure boot status of the system, without directly calling the IMA arch_ima_get_secureboot function. Refactor the secure boot status check into a general function named arch_get_secureboot.
Stable-dep-of: 3f7c9f9c36b4 ("s390/irqflags: Add out-of-line definitions of arch_local_irq_*() for KMSAN")
--- MAINTAINERS | 1 + arch/powerpc/kernel/ima_arch.c | 5 -- arch/powerpc/kernel/secure_boot.c | 6 ++ arch/s390/kernel/ima_arch.c | 6 -- arch/s390/kernel/ipl.c | 5 ++ arch/x86/include/asm/efi.h | 4 +- arch/x86/platform/efi/efi.c | 2 +- include/linux/ima.h | 7 +-- include/linux/secure_boot.h | 19 +++++++ security/integrity/Makefile | 3 +- security/integrity/efi_secureboot.c | 56 +++++++++++++++++++ security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_efi.c | 47 +--------------- security/integrity/ima/ima_main.c | 3 +- security/integrity/integrity.h | 1 + security/integrity/platform_certs/load_uefi.c | 2 +- security/integrity/secure_boot.c | 16 ++++++ 17 files changed, 115 insertions(+), 70 deletions(-) create mode 100644 include/linux/secure_boot.h create mode 100644 security/integrity/efi_secureboot.c create mode 100644 security/integrity/secure_boot.c
MAINTAINERS +1 -0
@@ -11257,6 +11257,7 @@ R: Eric Snowberg <[email protected]> L: [email protected] S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git +F: include/linux/secure_boot.h F: security/integrity/ F: security/integrity/ima/
arch/powerpc/kernel/ima_arch.c +0 -5
@@ -7,11 +7,6 @@#include <linux/ima.h> #include <asm/secure_boot.h> -bool arch_ima_get_secureboot (void ) -{ -return is_ppc_secureboot_enabled (); -} -/* * The "secure_rules" are enabled only on "secureboot" enabled systems. * These rules verify the file signatures against known good values.
arch/powerpc/kernel/secure_boot.c +6 -0
@@ -5,6 +5,7 @@*/ #include <linux/types.h> #include <linux/of.h> +#include <linux/secure_boot.h> #include <linux/string_choices.h> #include <asm/secure_boot.h>
@@ -44,6 +45,11 @@ bool is_ppc_secureboot_enabled(void)return enabled; } +bool arch_get_secureboot (void ) +{ +return is_ppc_secureboot_enabled (); +} +bool is_ppc_trustedboot_enabled (void ) {struct device_node *node;
arch/s390/kernel/ima_arch.c +0 -6
@@ -1,12 +1,6 @@// SPDX-License-Identifier: GPL-2.0 #include <linux/ima.h> -#include <asm/boot_data.h> - -bool arch_ima_get_secureboot (void ) -{ -return ipl_secure_flag; -}const char *const *arch_get_ima_policy (void ) {
arch/s390/kernel/ipl.c +5 -0
@@ -2486,6 +2486,11 @@ void *ipl_report_finish(struct ipl_report *report)return buf; } +bool arch_get_secureboot (void ) +{ +return ipl_secure_flag; +} +int ipl_report_free (struct ipl_report *report) {struct ipl_report_component *comp, *ncomp;
arch/x86/include/asm/efi.h +2 -2
@@ -398,9 +398,9 @@ extern int __init efi_memmap_split_count(efi_memory_desc_t *md,extern void __init efi_memmap_insert (struct efi_memory_map *old_memmap,void *buf,struct efi_mem_range *mem); -extern enum efi_secureboot_mode __x86_ima_efi_boot_mode (void ); +enum efi_secureboot_mode __x86_efi_boot_mode (void ); -#define arch_ima_efi_boot_mode __x86_ima_efi_boot_mode() +#define arch_efi_boot_mode __x86_efi_boot_mode()#ifdef CONFIG_EFI_RUNTIME_MAP int efi_get_runtime_map_size (void );
arch/x86/platform/efi/efi.c +1 -1
@@ -949,7 +949,7 @@ umode_t efi_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n)return attr->mode; } -enum efi_secureboot_mode __x86_ima_efi_boot_mode (void ) +enum efi_secureboot_mode __x86_efi_boot_mode (void ) {return boot_params.secure_boot; }
include/linux/ima.h +1 -6
@@ -11,6 +11,7 @@#include <linux/fs.h> #include <linux/security.h> #include <linux/kexec.h> +#include <linux/secure_boot.h> #include <crypto/hash_info.h> struct linux_binprm ;
@@ -73,14 +74,8 @@ int ima_validate_range(phys_addr_t phys, size_t size);#endif #ifdef CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT -extern bool arch_ima_get_secureboot (void );extern const char *const *arch_get_ima_policy (void );#else -static inline bool arch_ima_get_secureboot (void ) -{ -return false; -} -static inline const char *const *arch_get_ima_policy (void ) {return NULL ;
include/linux/secure_boot.h new file +19 -0
@@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* +* Copyright (C) 2026 Red Hat, Inc. All Rights Reserved. +* +* Author: Coiby Xu <[email protected]> +*/ + +#ifndef _LINUX_SECURE_BOOT_H +#define _LINUX_SECURE_BOOT_H + +#include <linux/types.h> + +/* +* Returns true if the platform secure boot is enabled. +* Returns false if disabled or not supported. +*/ +bool arch_get_secureboot (void ); + +#endif /* _LINUX_SECURE_BOOT_H */
security/integrity/Makefile +2 -1
@@ -5,7 +5,7 @@ obj-$(CONFIG_INTEGRITY) += integrity.o -integrity-y := iint.o +integrity-y := iint.o secure_boot.o integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
@@ -18,6 +18,7 @@ integrity-$(CONFIG_LOAD_IPL_KEYS) += platform_certs/load_ipl_s390.o integrity-$(CONFIG_LOAD_PPC_KEYS) += platform_certs/efi_parser.o \ platform_certs/load_powerpc.o \ platform_certs/keyring_handler.o +integrity-$(CONFIG_EFI) += efi_secureboot.o # The relative order of the 'ima' and 'evm' LSMs depends on the order below. obj-$(CONFIG_IMA) += ima/ obj-$(CONFIG_EVM) += evm/
security/integrity/efi_secureboot.c new file +56 -0
56 lines not on this page · raw message
security/integrity/ima/ima_appraise.c +1 -1
@@ -26,7 +26,7 @@ core_param(ima_appraise, ima_appraise_cmdline_default, charp, 0);void __init ima_appraise_parse_cmdline (void ) {const char *str = ima_appraise_cmdline_default; -bool sb_state =arch_ima_get_secureboot (); +bool sb_state =arch_get_secureboot ();int appraisal_state = ima_appraise;if (!str)
security/integrity/ima/ima_efi.c +2 -45
61 lines not on this page · raw message
security/integrity/ima/ima_main.c +1 -2
@@ -907,8 +907,7 @@ static int ima_load_data(enum kernel_load_data_id id, bool contents)switch (id) {case LOADING_KEXEC_IMAGE : -if (IS_ENABLED (CONFIG_KEXEC_SIG ) - &&arch_ima_get_secureboot ()) { +if (IS_ENABLED (CONFIG_KEXEC_SIG ) &&arch_get_secureboot ()) {pr_err ("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n" );return -EACCES ; }
security/integrity/integrity.h +1 -0
@@ -14,6 +14,7 @@#include <linux/types.h> #include <linux/integrity.h> +#include <linux/secure_boot.h> #include <crypto/sha1.h> #include <crypto/hash.h> #include <linux/key.h>
security/integrity/platform_certs/load_uefi.c +1 -1
@@ -212,7 +212,7 @@ static int __init load_uefi_certs(void) }/* the MOK/MOKx can not be trusted when secure boot is disabled */ -if (!arch_ima_get_secureboot ()) +if (!arch_get_secureboot ())return 0 ; mokx =get_cert_list (L"MokListXRT" , &mok_var, &mokxsize, &status);
security/integrity/secure_boot.c new file +16 -0
@@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* +* Copyright (C) 2026 Red Hat, Inc. All Rights Reserved. +* +* Author: Coiby Xu <[email protected]> +*/ +#include <linux/secure_boot.h> + +/* +* Default weak implementation. +* Architectures that support secure boot must override this. +*/ +__weak bool arch_get_secureboot (void ) +{ +return false; +}
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1051/1376 s390: Drop unnecessary CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Coiby Xu <[email protected]>
[ Upstream commit a2e507afd9a25e333b7a58082f5db8c4de2bd12d ]
Commit b5ca117365d9 ("ima: prevent kexec_load syscall based on runtime secureboot flag") and commit 268a78404973 ("s390/kexec_file: Disable kexec_load when IPLed secure") disabled the kexec_load syscall based on the secureboot mode. Commit 9e2b4be377f0 ("ima: add a new CONFIG for loading arch-specific policies") needed to detect the secure boot mode, not to load an IMA architecture specific policy. Since there is the new CONFIG_INTEGRITY_SECURE_BOOT, drop CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT for s390.
Stable-dep-of: 3f7c9f9c36b4 ("s390/irqflags: Add out-of-line definitions of arch_local_irq_*() for KMSAN")
--- arch/s390/Kconfig | 1 - arch/s390/kernel/Makefile | 1 - arch/s390/kernel/ima_arch.c | 8 -------- arch/s390/kernel/ipl.c | 1 + 4 files changed, 1 insertion(+), 10 deletions(-) delete mode 100644 arch/s390/kernel/ima_arch.c
arch/s390/Kconfig +0 -1
@@ -57,7 +57,6 @@ config S390 # # Note: keep this list sorted alphabetically # - imply IMA_SECURE_AND_OR_TRUSTED_BOOT select ALTERNATE_USER_ADDRESS_SPACE select ARCH_32BIT_USTAT_F_TINODE select ARCH_BINFMT_ELF_STATE
arch/s390/kernel/Makefile +0 -1
@@ -73,7 +73,6 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o obj-$(CONFIG_CERT_STORE) += cert_store.o -obj-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_arch.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
arch/s390/kernel/ima_arch.c deleted +0 -8
@@ -1,8 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <linux/ima.h> - -const char *const *arch_get_ima_policy (void ) -{ -return NULL ; -}
arch/s390/kernel/ipl.c +1 -0
@@ -21,6 +21,7 @@#include <linux/crash_dump.h> #include <linux/debug_locks.h> #include <linux/vmalloc.h> +#include <linux/secure_boot.h> #include <asm/asm-extable.h> #include <asm/diag.h> #include <asm/ipl.h>
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1052/1376 s390/irqflags: Add out-of-line definitions of arch_local_irq_*() for KMSAN
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ilya Leoshkevich <[email protected]>
[ Upstream commit 3f7c9f9c36b47a2a5cdbc56597eddf32662ef7fa ]
Inline KMSAN arch_local_irq_*() definitions run afoul of -Wstatic-in-inline. Move them out-of-line. Make sure decompressor and non-GPL modules see the out-of-line definitions.
--- arch/s390/include/asm/irqflags.h | 20 +++++++++++++------- arch/s390/kernel/Makefile | 1 + arch/s390/kernel/irqflags.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 arch/s390/kernel/irqflags.c
arch/s390/include/asm/irqflags.h +13 -7
@@ -37,18 +37,24 @@ static __always_inline void __arch_local_irq_ssm(unsigned long flags) asmvolatile ("ssm %0" : :"Q" (flags) :"memory" ); } -#ifdef CONFIG_KMSAN -#define arch_local_irq_attributes noinline notrace __no_sanitize_memory __maybe_unused +#if defined(CONFIG_KMSAN ) && !defined(__DECOMPRESSOR) +unsigned long arch_local_save_flags (void ); +unsigned long arch_local_irq_save (void ); +void arch_local_irq_enable_external (void ); +void arch_local_irq_enable (void );#else -#define arch_local_irq_attributes __always_inline +#define arch_local_save_flags __arch_local_save_flags +#define arch_local_irq_save __arch_local_irq_save +#define arch_local_irq_enable_external __arch_local_irq_enable_external +#define arch_local_irq_enable __arch_local_irq_enable#endif -static arch_local_irq_attributes unsigned long arch_local_save_flags (void ) +static __always_inline unsigned long __arch_local_save_flags (void ) {return __arch_local_irq_stnsm (0xff ); } -static arch_local_irq_attributes unsigned long arch_local_irq_save (void ) +static __always_inline unsigned long __arch_local_irq_save (void ) {return __arch_local_irq_stnsm (0xfc ); }
@@ -58,12 +64,12 @@ static __always_inline void arch_local_irq_disable(void)arch_local_irq_save (); } -static arch_local_irq_attributes void arch_local_irq_enable_external(void) +static __always_inline void __arch_local_irq_enable_external(void) {__arch_local_irq_stosm (0x01 ); } -static arch_local_irq_attributes void arch_local_irq_enable(void) +static __always_inline void __arch_local_irq_enable(void) {__arch_local_irq_stosm (0x03 ); }
arch/s390/kernel/Makefile +1 -0
@@ -73,6 +73,7 @@ obj-$(CONFIG_JUMP_LABEL) += jump_label.o obj-$(CONFIG_KEXEC_FILE) += machine_kexec_file.o kexec_image.o obj-$(CONFIG_KEXEC_FILE) += kexec_elf.o obj-$(CONFIG_CERT_STORE) += cert_store.o +obj-$(CONFIG_KMSAN) += irqflags.o obj-$(CONFIG_PERF_EVENTS) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_cpum_cf.o perf_cpum_sf.o
arch/s390/kernel/irqflags.c new file +28 -0
@@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/export.h> +#include <asm/irqflags.h> + +noinstr unsigned long arch_local_save_flags (void ) +{ +return __arch_local_save_flags (); +} +EXPORT_SYMBOL (arch_local_save_flags); + +noinstr unsigned long arch_local_irq_save (void ) +{ +return __arch_local_irq_save (); +} +EXPORT_SYMBOL (arch_local_irq_save); + +noinstr void arch_local_irq_enable_external(void) +{ +__arch_local_irq_enable_external (); +} +EXPORT_SYMBOL (arch_local_irq_enable_external); + +noinstr void arch_local_irq_enable(void) +{ +__arch_local_irq_enable (); +} +EXPORT_SYMBOL (arch_local_irq_enable);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1053/1376 nvme: add reservation commands defines
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guixin Liu <[email protected]>
[ Upstream commit 1900e1a4495b7376cb9b4e58f1d846660f4c9c4b ]
This is a preparation patch for NVMeOF target reservation commands implantation.
Add the defines of reservation command, such as reservation log and sub operations.
Stable-dep-of: 5bb96cc21883 ("nvmet: fix heap out-of-bounds read in nvmet_auth_negotiate()")
--- include/linux/nvme.h | 68 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+)
include/linux/nvme.h +68 -0
@@ -2038,4 +2038,72 @@ struct nvme_completion {#define NVME_MINOR (ver) (((ver) >> 8) & 0xff)#define NVME_TERTIARY (ver) ((ver) & 0xff) +enum { +NVME_AEN_RESV_LOG_PAGE_AVALIABLE =0x00 , +}; + +enum { +NVME_PR_LOG_EMPTY_LOG_PAGE =0x00 , +NVME_PR_LOG_REGISTRATION_PREEMPTED =0x01 , +NVME_PR_LOG_RESERVATION_RELEASED =0x02 , +NVME_PR_LOG_RESERVATOPM_PREEMPTED =0x03 , +}; + +enum { +NVME_PR_NOTIFY_BIT_REG_PREEMPTED =1 , +NVME_PR_NOTIFY_BIT_RESV_RELEASED =2 , +NVME_PR_NOTIFY_BIT_RESV_PREEMPTED =3 , +}; + +struct nvme_pr_log { +__le64 count; +__u8 type; +__u8 nr_pages; +__u8 rsvd1[2 ]; +__le32 nsid; +__u8 rsvd2[48 ]; +}; + +struct nvmet_pr_register_data { +__le64 crkey; +__le64 nrkey; +}; + +struct nvmet_pr_acquire_data { +__le64 crkey; +__le64 prkey; +}; + +struct nvmet_pr_release_data { +__le64 crkey; +}; + +enum nvme_pr_capabilities { +NVME_PR_SUPPORT_PTPL =1 , +NVME_PR_SUPPORT_WRITE_EXCLUSIVE =1 <<1 , +NVME_PR_SUPPORT_EXCLUSIVE_ACCESS =1 <<2 , +NVME_PR_SUPPORT_WRITE_EXCLUSIVE_REG_ONLY =1 <<3 , +NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_REG_ONLY =1 <<4 , +NVME_PR_SUPPORT_WRITE_EXCLUSIVE_ALL_REGS =1 <<5 , +NVME_PR_SUPPORT_EXCLUSIVE_ACCESS_ALL_REGS =1 <<6 , +NVME_PR_SUPPORT_IEKEY_VER_1_3_DEF =1 <<7 , +}; + +enum nvme_pr_register_action { +NVME_PR_REGISTER_ACT_REG =0 , +NVME_PR_REGISTER_ACT_UNREG =1 , +NVME_PR_REGISTER_ACT_REPLACE =1 <<1 , +}; + +enum nvme_pr_acquire_action { +NVME_PR_ACQUIRE_ACT_ACQUIRE =0 , +NVME_PR_ACQUIRE_ACT_PREEMPT =1 , +NVME_PR_ACQUIRE_ACT_PREEMPT_AND_ABORT =1 <<1 , +}; + +enum nvme_pr_release_action { +NVME_PR_RELEASE_ACT_RELEASE =0 , +NVME_PR_RELEASE_ACT_CLEAR =1 , +}; +#endif /* _LINUX_NVME_H */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1054/1376 nvme: introduce change ptpl and iekey definition
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guixin Liu <[email protected]>
[ Upstream commit 7d2f9f870f26798699585f96fa7a2a2cab38dc02 ]
This is for the next tuning pr code more readble patch, make linux/nvme.h's changes separately.
Stable-dep-of: 5bb96cc21883 ("nvmet: fix heap out-of-bounds read in nvmet_auth_negotiate()")
--- include/linux/nvme.h | 9 +++++++++ 1 file changed, 9 insertions(+)
include/linux/nvme.h +9 -0
@@ -2106,4 +2106,13 @@ enum nvme_pr_release_action { NVME_PR_RELEASE_ACT_CLEAR = 1, }; +enum nvme_pr_change_ptpl { +NVME_PR_CPTPL_NO_CHANGE =0 , +NVME_PR_CPTPL_RESV =1 <<30 , +NVME_PR_CPTPL_CLEARED =2 <<30 , +NVME_PR_CPTPL_PERSIST =3 <<30 , +}; + +#define NVME_PR_IGNORE_KEY (1 << 3) +#endif /* _LINUX_NVME_H */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1055/1376 nvme: Add the DHCHAP maximum HD IDs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Alistair Francis <[email protected]>
[ Upstream commit 09e8f0f93491c6be867f32d4edc0b16fb5da785e ]
In preperation for using DHCHAP length in upcoming host and target patches let's add the hash and diffie-hellman ID length macros.
Stable-dep-of: 5bb96cc21883 ("nvmet: fix heap out-of-bounds read in nvmet_auth_negotiate()")
--- include/linux/nvme.h | 4 ++++ 1 file changed, 4 insertions(+)
include/linux/nvme.h +4 -0
@@ -2115,4 +2115,8 @@ enum nvme_pr_change_ptpl {#define NVME_PR_IGNORE_KEY (1 << 3) +/* Section 8.3.4.5.2 of the NVMe 2.1 */ +#define NVME_AUTH_DHCHAP_MAX_HASH_IDS 30 +#define NVME_AUTH_DHCHAP_MAX_DH_IDS 30 +#endif /* _LINUX_NVME_H */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1056/1376 nvmet: fix heap out-of-bounds read in nvmet_auth_negotiate()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guixin Liu <[email protected]>
[ Upstream commit 5bb96cc218835769ab74ec7f3ea2bf81fbffe955 ]
nvmet_execute_auth_send() allocates the DH-HMAC-CHAP message buffer with the host-supplied transfer length (tl) and hands it to nvmet_auth_negotiate() without passing tl along. nvmet_auth_negotiate() then reads the negotiate header and, for each of the halen hash identifiers and dhlen DH group identifiers, indexes into the fixed idlist[60] array (hashes at idlist[0..halen), groups at idlist[30..]).
Neither the transfer length nor halen/dhlen is validated. A malicious or non-conformant host can report a tl smaller than the negotiate structure, or a halen/dhlen larger than the array (both are u8, up to 255), making the loops read past the end of the allocated buffer (heap out-of-bounds read). The sibling nvmet_auth_reply() already validates tl against the structure size; the negotiate path did not.
Pass tl into nvmet_auth_negotiate(), reject a tl that does not cover the negotiate data plus one full protocol descriptor, and reject halen/dhlen larger than NVME_AUTH_DHCHAP_MAX_DH_IDS.
--- drivers/nvme/target/fabrics-cmd-auth.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
drivers/nvme/target/fabrics-cmd-auth.c +10 -2
@@ -31,12 +31,16 @@ void nvmet_auth_sq_init(struct nvmet_sq *sq) sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE ; } -static u8 nvmet_auth_negotiate (struct nvmet_req *req,void *d) +static u8 nvmet_auth_negotiate (struct nvmet_req *req,void *d,u32 tl) {struct nvmet_ctrl *ctrl = req->sq->ctrl;struct nvmf_auth_dhchap_negotiate_data *data = d;int i, hash_id =0 , fallback_hash_id =0 , dhgid, fallback_dhgid; +if (tl <sizeof (*data) + +sizeof (struct nvmf_auth_dhchap_protocol_descriptor )) +return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n", __func__, ctrl->cntlid, req->sq->qid, data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid,
@@ -53,6 +57,10 @@ static u8 nvmet_auth_negotiate(struct nvmet_req *req, void *d) NVME_AUTH_DHCHAP_AUTH_ID)return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +if (data->auth_protocol[0 ].dhchap.dhlen >NVME_AUTH_DHCHAP_MAX_DH_IDS || + data->auth_protocol[0 ].dhchap.halen >NVME_AUTH_DHCHAP_MAX_HASH_IDS ) +return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD ; +for (i =0 ; i < data->auth_protocol[0 ].dhchap.halen; i++) {u8 host_hmac_id = data->auth_protocol[0 ].dhchap.idlist[i];
@@ -270,7 +278,7 @@ void nvmet_execute_auth_send(struct nvmet_req *req) }else if (data->auth_id != req->sq->dhchap_step) goto done_failure1;/* Validate negotiation parameters */ - dhchap_status =nvmet_auth_negotiate (req, d); + dhchap_status =nvmet_auth_negotiate (req, d, tl);if (dhchap_status ==0 ) req->sq->dhchap_step =NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1057/1376 nvme-apple: Destroy the admin queue on removal
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit 87d5b9864c8118d26f54de4b66d2bddf2c659272 ]
The admin queue is allocated with blk_mq_alloc_queue() but never destroyed. nvme_free_ctrl() only drops the last reference and blk_mq_exit_queue() and blk_sync_queue() never run: the hctx is never moved to q->unused_hctx_list and the timeout timer and work stay armed on a queue that is about to be freed which will eventually oops inside blk_mq_timeout_work().
This can only be triggered when the controller fails to come up and is then immediately torn down again which is why no one ever ran into this before.
Let's just copy what the pcie driver does: unquiesce and destroy the admin queue before nvme_uninit_ctrl().
With this the following WARN followed by a panic no longer happens:
WARNING: block/blk-mq.c:4390 at blk_mq_release+0x194/0x238, CPU#4: kworker/u34:4/119
CPU: 4 UID: 0 PID: 119 Comm: kworker/u34:4 Not tainted 7.2.0-rc1-dirty #248 PREEMPT
Hardware name: Apple Mac mini (M1, 2020) (DT)
Workqueue: nvme-wq apple_nvme_remove_dead_ctrl_work
pstate: 61400005 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
pc : blk_mq_release+0x194/0x238
lr : blk_mq_release+0x58/0x238
sp : ffffc000833a3b50
x29: ffffc000833a3b50 x28: ffff80001d0450f8 x27: ffff800020c95200
x26: 0000000000000088 x25: 0000000000000000 x24: ffff800020f36805
x23: 0000000000000000 x22: ffffc00081a86878 x21: ffff800020be9c60
x20: 0000000000000000 x19: ffff800022501698 x18: 000000000000000a
x17: 7365757165722066 x16: 666f7265776f7020 x15: 0000000000000000
x14: 0000000000000028 x13: 0000000000004def x12: 0000000000000003
x11: 0000000000000000 x10: 0000000000000000 x9 : ffffc000805b4fc8
x8 : ffffc00081915820 x7 : ffffc00081c4f3c8 x6 : 0000000000000001
x5 : 0000000000000004 x4 : ffff800022498d80 x3 : ffffc000833a3b14
x2 : 0000000000000000 x1 : 0000000000000000 x0 : ffff800022501698
Call trace:
blk_mq_release+0x194/0x238 (P)
blk_put_queue+0x8c/0xf0
nvme_free_ctrl+0x4c/0x260
device_release+0x44/0x128
kobject_put+0xa0/0x120
put_device+0x1c/0x40
nvme_uninit_ctrl+0x48/0x60
apple_nvme_remove+0x54/0xb0
platform_remove+0x28/0x40
device_remove+0x54/0x98
device_release_driver_internal+
device_release_driver+0x20/0x38
apple_nvme_remove_dead_ctrl_wor
process_one_work+0x1f4/0x770
worker_thread+0x1b8/0x360
kthread+0x140/0x160
ret_from_fork+0x10/0x20
irq event stamp: 448
hardirqs last enabled at (447):in_unlock_irqrestore+0x74/0x80
hardirqs last disabled at (448): [<ffffc000811cf5c0>] el1_brk64+0x20/0x60
softirqs last enabled at (0): [ess+0xb28/0x2698
softirqs last disabled at (0): [<0000000000000000>] 0x0
---[ end trace 0000000000000000
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000
Mem abort info:
ESR = 0x0000000096000005
EC = 0x25: DABT (current EL),
SET = 0, FnV = 0
EA = 0, S1PTW = 0
FSC = 0x05: level 1 translation fault
Data abort info:
ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000
CM = 0, WnR = 0, TnD = 0, TagA
GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0
[0000000000000000] user address
Internal error: Oops: 0000000096000005 [#1] SMP
CPU: 7 UID: 0 PID: 54 Comm: kwor 7.2.0-rc1-dirty #248PREEMPT
Tainted: [W]=WARN
Hardware name: Apple Mac mini (M1, 2020) (DT)
Workqueue: kblockd blk_mq_timeou
pstate: 01400005 (nzcv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--)
pc : percpu_ref_tryget_many.cons
lr : percpu_ref_tryget_many.constprop.0+0xc0/0x168
sp : ffffc000829cbce0
x29: ffffc000829cbce0 x28: ffff800020be9f48 x27: ffff800013e503c0
x26: 0000000000000108 x25: 000009c05
x23: 0000000000000000 x22: ffffc000819f5000 x21: ffff800020be9f48
x20: ffff8001deda4808 x19: ffff8000a
x17: 00000000580e1fac x16: ffffc00082bbbb7c x15: 0000000000000000
x14: 0000000000000028 x13: 000000001
x11: 0000000000000000 x10: 0000000000000000 x9 : ffffc000829cbc20
x8 : ffffc00081915820 x7 : ffffc0001
x5 : ffff80001ca77d08 x4 : 0000000000000000 x3 : ffff80001ca77cb8
x2 : 0000000000000000 x1 : 000000007
Call trace:
percpu_ref_tryget_many.constpro
blk_mq_timeout_work+0x48/0x298
process_one_work+0x1f4/0x770
worker_thread+0x1b8/0x360
kthread+0x140/0x160
ret_from_fork+0x10/0x20
Code: 91282000 97ed44b2 17ffffd2
---[ end trace 0000000000000000 ]------ drivers/nvme/host/apple.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/nvme/host/apple.c +9 -0
@@ -1578,6 +1578,15 @@ static void apple_nvme_remove(struct platform_device *pdev)nvme_stop_ctrl (&anv->ctrl);nvme_remove_namespaces (&anv->ctrl);apple_nvme_disable (anv, true); +if (anv->ctrl.admin_q && !blk_queue_dying (anv->ctrl.admin_q)) { +/* +* If the controller was reset during removal, it's possible +* user requests may be waiting on a stopped queue. Start the +* queue to flush these to completion. +*/ +nvme_unquiesce_admin_queue (&anv->ctrl); +blk_mq_destroy_queue (anv->ctrl.admin_q); + }nvme_uninit_ctrl (&anv->ctrl);if (apple_rtkit_is_running (anv->rtk))
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1058/1376 nvme-apple: Dont set a DMA direction for commands without a data transfer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit 94dd5804938d6681dbf26f023b1356d511f4fc48 ]
Setting the DMA direction for commands that don't do any transfer likely triggered the PRP NULL check for which we needed a chicken bit. That bit has disappeared starting with macOS 15 so let's just do this correctly instead.
--- drivers/nvme/host/apple.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
drivers/nvme/host/apple.c +3 -1
@@ -310,7 +310,9 @@ static void apple_nvme_submit_cmd(struct apple_nvme_queue *q, tcb->length = cmd->rw.length; tcb->command_id = tag; -if (nvme_is_write (cmd)) +if (!cmd->common.dptr.prp1) + tcb->dma_flags =0 ; +else if (nvme_is_write (cmd)) tcb->dma_flags =APPLE_ANS_TCB_DMA_TO_DEVICE ;else tcb->dma_flags =APPLE_ANS_TCB_DMA_FROM_DEVICE ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1059/1376 nvme-apple: Never set the opcode in the NVMMU TCB
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit cc0fec9b42cfbc69d70cb4c4b616408a7037b445 ]
macOS always sets this to zero and the firmware starting with macOS 15 has started to complain about what we're doing here.
--- drivers/nvme/host/apple.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/host/apple.c +1 -1
@@ -304,7 +304,7 @@ static void apple_nvme_submit_cmd(struct apple_nvme_queue *q,u32 tag =nvme_tag_from_cid (cmd->common.command_id);struct apple_nvmmu_tcb *tcb = &q->tcbs[tag]; - tcb->opcode = cmd->common.opcode; + tcb->opcode =0 ; tcb->prp1 = cmd->common.dptr.prp1; tcb->prp2 = cmd->common.dptr.prp2; tcb->length = cmd->rw.length;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1060/1376 nvme: apple: Add Apple A11 support
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Nick Chan <[email protected]>
[ Upstream commit 04d8ecf37b5e06d16228a4d37d8548c17cf70461 ]
Add support for ANS2 NVMe on Apple A11 SoC.
This version of ANS2 is less quirky than the one in M1, and does not have NVMMU or Linear SQ. However, it still requires a non-standard 128-byte SQE.
Stable-dep-of: 8ce883fd068b ("nvme-apple: Drop the PRP null check chicken bit")
--- drivers/nvme/host/apple.c | 197 ++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 60 deletions(-)
drivers/nvme/host/apple.c +137 -60
@@ -35,7 +35,6 @@#include "nvme.h" #define APPLE_ANS_BOOT_TIMEOUT USEC_PER_SEC -#define APPLE_ANS_MAX_QUEUE_DEPTH 64#define APPLE_ANS_COPROC_CPU_CONTROL 0x44#define APPLE_ANS_COPROC_CPU_CONTROL_RUN BIT(4)
@@ -75,6 +74,8 @@#define APPLE_NVME_AQ_DEPTH 2#define APPLE_NVME_AQ_MQ_TAG_DEPTH (APPLE_NVME_AQ_DEPTH - 1) +#define APPLE_NVME_IOSQES 7 +/* * These can be higher, but we need to ensure that any command doesn't * require an sg allocation that needs more than a page of data.
@@ -142,6 +143,7 @@ struct apple_nvme_queue {u32 __iomem *sq_db;u32 __iomem *cq_db; +u16 sq_tail;u16 cq_head;u8 cq_phase;
@@ -183,11 +185,17 @@ struct apple_nvme_iod {struct scatterlist *sg; }; +struct apple_nvme_hw { +bool has_lsq_nvmmu; +u32 max_queue_depth; +}; +struct apple_nvme {struct device *dev;void __iomem *mmio_coproc;void __iomem *mmio_nvme; +const struct apple_nvme_hw *hw;struct device **pd_dev;struct device_link **pd_link;
@@ -232,10 +240,12 @@ static inline struct apple_nvme *queue_to_apple_nvme(struct apple_nvme_queue *q)static unsigned int apple_nvme_queue_depth (struct apple_nvme_queue *q) { -if (q->is_adminq) +struct apple_nvme *anv =queue_to_apple_nvme (q); + +if (q->is_adminq && anv->hw->has_lsq_nvmmu)return APPLE_NVME_AQ_DEPTH ; -return APPLE_ANS_MAX_QUEUE_DEPTH ; +return anv->hw->max_queue_depth; }static void apple_nvme_rtkit_crashed (void *cookie)
@@ -297,7 +307,28 @@ static void apple_nvmmu_inval(struct apple_nvme_queue *q, unsigned int tag) "NVMMU TCB invalidation failed\n"); } -static void apple_nvme_submit_cmd (struct apple_nvme_queue *q, +static void apple_nvme_submit_cmd_t8015 (struct apple_nvme_queue *q, +struct nvme_command *cmd) +{ +struct apple_nvme *anv =queue_to_apple_nvme (q); + +spin_lock_irq (&anv->lock); + +if (q->is_adminq) +memcpy (&q->sqes[q->sq_tail], cmd,sizeof (*cmd)); +else +memcpy ((void *)q->sqes + (q->sq_tail <<APPLE_NVME_IOSQES ), + cmd,sizeof (*cmd)); + +if (++q->sq_tail == anv->hw->max_queue_depth) + q->sq_tail =0 ; + +writel (q->sq_tail, q->sq_db); +spin_unlock_irq (&anv->lock); +} + + +static void apple_nvme_submit_cmd_t8103 (struct apple_nvme_queue *q,struct nvme_command *cmd) {struct apple_nvme *anv =queue_to_apple_nvme (q);
@@ -609,7 +640,8 @@ static inline void apple_nvme_handle_cqe(struct apple_nvme_queue *q,__u16 command_id =READ_ONCE (cqe->command_id);struct request *req; -apple_nvmmu_inval (q, command_id); +if (anv->hw->has_lsq_nvmmu) +apple_nvmmu_inval (q, command_id); req =nvme_find_rq (apple_nvme_queue_tagset (anv, q), command_id);if (unlikely (!req)) {
@@ -704,7 +736,7 @@ static int apple_nvme_create_cq(struct apple_nvme *anv) c.create_cq.opcode = nvme_admin_create_cq; c.create_cq.prp1 =cpu_to_le64 (anv->ioq.cq_dma_addr); c.create_cq.cqid =cpu_to_le16 (1 ); - c.create_cq.qsize =cpu_to_le16 (APPLE_ANS_MAX_QUEUE_DEPTH -1 ); + c.create_cq.qsize =cpu_to_le16 (anv->hw->max_queue_depth -1 ); c.create_cq.cq_flags =cpu_to_le16 (NVME_QUEUE_PHYS_CONTIG |NVME_CQ_IRQ_ENABLED ); c.create_cq.irq_vector =cpu_to_le16 (0 );
@@ -732,7 +764,7 @@ static int apple_nvme_create_sq(struct apple_nvme *anv) c.create_sq.opcode = nvme_admin_create_sq; c.create_sq.prp1 =cpu_to_le64 (anv->ioq.sq_dma_addr); c.create_sq.sqid =cpu_to_le16 (1 ); - c.create_sq.qsize =cpu_to_le16 (APPLE_ANS_MAX_QUEUE_DEPTH -1 ); + c.create_sq.qsize =cpu_to_le16 (anv->hw->max_queue_depth -1 ); c.create_sq.sq_flags =cpu_to_le16 (NVME_QUEUE_PHYS_CONTIG ); c.create_sq.cqid =cpu_to_le16 (1 );
@@ -784,7 +816,12 @@ static blk_status_t apple_nvme_queue_rq(struct blk_mq_hw_ctx *hctx, }nvme_start_request (req); -apple_nvme_submit_cmd (q, cmnd); + +if (anv->hw->has_lsq_nvmmu) +apple_nvme_submit_cmd_t8103 (q, cmnd); +else +apple_nvme_submit_cmd_t8015 (q, cmnd); +return BLK_STS_OK ; out_free_cmd:
@@ -989,11 +1026,13 @@ static const struct blk_mq_ops apple_nvme_mq_ops = {static void apple_nvme_init_queue (struct apple_nvme_queue *q) {unsigned int depth =apple_nvme_queue_depth (q); +struct apple_nvme *anv =queue_to_apple_nvme (q); q->cq_head =0 ; q->cq_phase =1 ; -memset (q->tcbs,0 , -APPLE_ANS_MAX_QUEUE_DEPTH *sizeof (struct apple_nvmmu_tcb )); +if (anv->hw->has_lsq_nvmmu) +memset (q->tcbs,0 , anv->hw->max_queue_depth + *sizeof (struct apple_nvmmu_tcb ));memset (q->cqes,0 , depth *sizeof (struct nvme_completion ));apple_nvme_enable_queue (q); }
@@ -1075,49 +1114,55 @@ static void apple_nvme_reset_work(struct work_struct *work)dma_set_max_seg_size (anv->dev,0xffffffff ); -/* -* Enable NVMMU and linear submission queues. -* While we could keep those disabled and pretend this is slightly -* more common NVMe controller we'd still need some quirks (e.g. -* sq entries will be 128 bytes) and Apple might drop support for -* that mode in the future. -*/ -writel (APPLE_ANS_LINEAR_SQ_EN , - anv->mmio_nvme +APPLE_ANS_LINEAR_SQ_CTRL ); +if (anv->hw->has_lsq_nvmmu) { +/* +* Enable NVMMU and linear submission queues which is required +* since T6000. +*/ +writel (APPLE_ANS_LINEAR_SQ_EN , + anv->mmio_nvme +APPLE_ANS_LINEAR_SQ_CTRL ); -/* Allow as many pending command as possible for both queues */ -writel (APPLE_ANS_MAX_QUEUE_DEPTH | (APPLE_ANS_MAX_QUEUE_DEPTH <<16 ), - anv->mmio_nvme +APPLE_ANS_MAX_PEND_CMDS_CTRL ); +/* Allow as many pending command as possible for both queues */ +writel (anv->hw->max_queue_depth + | (anv->hw->max_queue_depth <<16 ), anv->mmio_nvme + +APPLE_ANS_MAX_PEND_CMDS_CTRL ); -/* Setup the NVMMU for the maximum admin and IO queue depth */ -writel (APPLE_ANS_MAX_QUEUE_DEPTH -1 , - anv->mmio_nvme +APPLE_NVMMU_NUM_TCBS ); +/* Setup the NVMMU for the maximum admin and IO queue depth */ +writel (anv->hw->max_queue_depth -1 , + anv->mmio_nvme +APPLE_NVMMU_NUM_TCBS ); -/* -* This is probably a chicken bit: without it all commands where any PRP -* is set to zero (including those that don't use that field) fail and -* the co-processor complains about "completed with err BAD_CMD-" or -* a "NULL_PRP_PTR_ERR" in the syslog -*/ -writel (readl (anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ) & - ~APPLE_ANS_PRP_NULL_CHECK , - anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ); +/* +* This is probably a chicken bit: without it all commands +* where any PRP is set to zero (including those that don't use +* that field) fail and the co-processor complains about +* "completed with err BAD_CMD-" or a "NULL_PRP_PTR_ERR" in the +* syslog +*/ +writel (readl (anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ) & + ~APPLE_ANS_PRP_NULL_CHECK , + anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ); + }/* Setup the admin queue */ - aqa =APPLE_NVME_AQ_DEPTH -1 ; +if (anv->hw->has_lsq_nvmmu) + aqa =APPLE_NVME_AQ_DEPTH -1 ; +else + aqa = anv->hw->max_queue_depth -1 ; aqa |= aqa <<16 ;writel (aqa, anv->mmio_nvme +NVME_REG_AQA );writeq (anv->adminq.sq_dma_addr, anv->mmio_nvme +NVME_REG_ASQ );writeq (anv->adminq.cq_dma_addr, anv->mmio_nvme +NVME_REG_ACQ ); -/* Setup NVMMU for both queues */ -writeq (anv->adminq.tcb_dma_addr, - anv->mmio_nvme +APPLE_NVMMU_ASQ_TCB_BASE ); -writeq (anv->ioq.tcb_dma_addr, - anv->mmio_nvme +APPLE_NVMMU_IOSQ_TCB_BASE ); +if (anv->hw->has_lsq_nvmmu) { +/* Setup NVMMU for both queues */ +writeq (anv->adminq.tcb_dma_addr, + anv->mmio_nvme +APPLE_NVMMU_ASQ_TCB_BASE ); +writeq (anv->ioq.tcb_dma_addr, + anv->mmio_nvme +APPLE_NVMMU_IOSQ_TCB_BASE ); + } anv->ctrl.sqsize = -APPLE_ANS_MAX_QUEUE_DEPTH -1 ;/* 0's based queue depth */ + anv->hw->max_queue_depth -1 ;/* 0's based queue depth */ anv->ctrl.cap =readq (anv->mmio_nvme +NVME_REG_CAP );dev_dbg (anv->dev,"Enabling controller now" );
@@ -1285,8 +1330,9 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv)* both queues. The admin queue gets the first APPLE_NVME_AQ_DEPTH which * must be marked as reserved in the IO queue. */ - anv->tagset.reserved_tags =APPLE_NVME_AQ_DEPTH ; - anv->tagset.queue_depth =APPLE_ANS_MAX_QUEUE_DEPTH -1 ; +if (anv->hw->has_lsq_nvmmu) + anv->tagset.reserved_tags =APPLE_NVME_AQ_DEPTH ; + anv->tagset.queue_depth = anv->hw->max_queue_depth -1 ; anv->tagset.timeout =NVME_IO_TIMEOUT ; anv->tagset.numa_node =NUMA_NO_NODE ; anv->tagset.cmd_size =sizeof (struct apple_nvme_iod );
@@ -1311,6 +1357,7 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv, struct apple_nvme_queue *q) {unsigned int depth =apple_nvme_queue_depth (q); +size_t iosq_size; q->cqes = dmam_alloc_coherent(anv->dev, depth * sizeof(struct nvme_completion),
@@ -1318,22 +1365,28 @@ static int apple_nvme_queue_alloc(struct apple_nvme *anv,if (!q->cqes)return -ENOMEM ; - q->sqes =dmam_alloc_coherent (anv->dev, - depth *sizeof (struct nvme_command ), +if (anv->hw->has_lsq_nvmmu) + iosq_size = depth *sizeof (struct nvme_command ); +else + iosq_size = depth <<APPLE_NVME_IOSQES ; + + q->sqes =dmam_alloc_coherent (anv->dev, iosq_size, &q->sq_dma_addr,GFP_KERNEL );if (!q->sqes)return -ENOMEM ; -/* -* We need the maximum queue depth here because the NVMMU only has a -* single depth configuration shared between both queues. -*/ - q->tcbs =dmam_alloc_coherent (anv->dev, -APPLE_ANS_MAX_QUEUE_DEPTH * -sizeof (struct apple_nvmmu_tcb ), - &q->tcb_dma_addr,GFP_KERNEL ); -if (!q->tcbs) -return -ENOMEM ; +if (anv->hw->has_lsq_nvmmu) { +/* +* We need the maximum queue depth here because the NVMMU only +* has a single depth configuration shared between both queues. +*/ + q->tcbs =dmam_alloc_coherent (anv->dev, + anv->hw->max_queue_depth * +sizeof (struct apple_nvmmu_tcb ), + &q->tcb_dma_addr,GFP_KERNEL ); +if (!q->tcbs) +return -ENOMEM ; + }/* * initialize phase to make sure the allocated and empty memory
@@ -1417,6 +1470,12 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev) anv->adminq.is_adminq = true;platform_set_drvdata (pdev, anv); + anv->hw =of_device_get_match_data (&pdev->dev); +if (!anv->hw) { + ret = -ENODEV ; + goto put_dev; + } + ret =apple_nvme_attach_genpd (anv);if (ret <0 ) {dev_err_probe (dev, ret,"Failed to attach power domains" );
@@ -1448,10 +1507,17 @@ static struct apple_nvme *apple_nvme_alloc(struct platform_device *pdev) goto put_dev; } - anv->adminq.sq_db = anv->mmio_nvme +APPLE_ANS_LINEAR_ASQ_DB ; - anv->adminq.cq_db = anv->mmio_nvme +APPLE_ANS_ACQ_DB ; - anv->ioq.sq_db = anv->mmio_nvme +APPLE_ANS_LINEAR_IOSQ_DB ; - anv->ioq.cq_db = anv->mmio_nvme +APPLE_ANS_IOCQ_DB ; +if (anv->hw->has_lsq_nvmmu) { + anv->adminq.sq_db = anv->mmio_nvme +APPLE_ANS_LINEAR_ASQ_DB ; + anv->adminq.cq_db = anv->mmio_nvme +APPLE_ANS_ACQ_DB ; + anv->ioq.sq_db = anv->mmio_nvme +APPLE_ANS_LINEAR_IOSQ_DB ; + anv->ioq.cq_db = anv->mmio_nvme +APPLE_ANS_IOCQ_DB ; + }else { + anv->adminq.sq_db = anv->mmio_nvme +NVME_REG_DBS ; + anv->adminq.cq_db = anv->mmio_nvme +APPLE_ANS_ACQ_DB ; + anv->ioq.sq_db = anv->mmio_nvme +NVME_REG_DBS +8 ; + anv->ioq.cq_db = anv->mmio_nvme +APPLE_ANS_IOCQ_DB ; + } anv->sart =devm_apple_sart_get (dev);if (IS_ERR (anv->sart)) {
@@ -1631,8 +1697,19 @@ static int apple_nvme_suspend(struct device *dev) static DEFINE_SIMPLE_DEV_PM_OPS(apple_nvme_pm_ops, apple_nvme_suspend, apple_nvme_resume); +static const struct apple_nvme_hw apple_nvme_t8015_hw = { + .has_lsq_nvmmu = false, + .max_queue_depth =16 , +}; + +static const struct apple_nvme_hw apple_nvme_t8103_hw = { + .has_lsq_nvmmu = true, + .max_queue_depth =64 , +}; +static const struct of_device_id apple_nvme_of_match[] = { - { .compatible ="apple,nvme-ans2" }, + { .compatible ="apple,t8015-nvme-ans2" , .data = &apple_nvme_t8015_hw }, + { .compatible ="apple,nvme-ans2" , .data = &apple_nvme_t8103_hw }, {}, };MODULE_DEVICE_TABLE (of, apple_nvme_of_match);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1061/1376 nvme-apple: Drop the PRP null check chicken bit
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit 8ce883fd068b7ba9ab493cd3ecca3a7ea868c375 ]
Now that we program the DMA direction correctly the NULL check that used to make commands fail passes. Another side effect of this bit was that non-align buffers on the admin queue were silently allowed and that's been fixed now as well and we this don't need this chicken bit anymore. More importantly, starting with the firmware installed with macOS 15, which is required for M4 but can also be installed on the previous SoCs, the controller no longer exposes this control register and any access SErrors instead. Just drop the write entirely.
--- drivers/nvme/host/apple.c | 14 -------------- 1 file changed, 14 deletions(-)
drivers/nvme/host/apple.c +0 -14
@@ -47,9 +47,6 @@#define APPLE_ANS_BOOT_STATUS 0x1300#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55 -#define APPLE_ANS_UNKNOWN_CTRL 0x24008 -#define APPLE_ANS_PRP_NULL_CHECK BIT(11) -#define APPLE_ANS_LINEAR_SQ_CTRL 0x24908#define APPLE_ANS_LINEAR_SQ_EN BIT(0)
@@ -1130,17 +1127,6 @@ static void apple_nvme_reset_work(struct work_struct *work)/* Setup the NVMMU for the maximum admin and IO queue depth */ writel (anv->hw->max_queue_depth -1 , anv->mmio_nvme +APPLE_NVMMU_NUM_TCBS ); - -/* -* This is probably a chicken bit: without it all commands -* where any PRP is set to zero (including those that don't use -* that field) fail and the co-processor complains about -* "completed with err BAD_CMD-" or a "NULL_PRP_PTR_ERR" in the -* syslog -*/ -writel (readl (anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ) & - ~APPLE_ANS_PRP_NULL_CHECK , - anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ); }/* Setup the admin queue */
Signature · 1 line
2.53.0
Re: [PATCH 6.12 1061/1376] nvme-apple: Drop the PRP null check chicken bit
Quoted 47 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit
8ce883fd068b7ba9ab493cd3ecca3a7ea868c375]Now that we program the DMA direction correctly the NULL check that used to make commands fail passes. Another side effect of this bit was that non-align buffers on the admin queue were silently allowed and that's been fixed now as well and we this don't need this chicken bit anymore. More importantly, starting with the firmware installed with macOS 15, which is required for M4 but can also be installed on the previous SoCs, the controller no longer exposes this control register and any access SErrors instead. Just drop the write entirely.
Fixes 5bd2927aceba ("nvme-apple: Add initial Apple SoC NVMe driver")Tested-by Joshua Peisach <[email protected]>Tested-by Janne Grunau <[email protected]>Tested-by Nick Chan <[email protected]>Signed-off-by Sven Peter <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- drivers/nvme/host/apple.c | 14 -------------- 1 file changed, 14 deletions(-)drivers/nvme/host/apple.c +0
-5@@ -47,9 +47,6 @@#define APPLE_ANS_BOOT_STATUS 0x1300#define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55 -#define APPLE_ANS_UNKNOWN_CTRL 0x24008 -#define APPLE_ANS_PRP_NULL_CHECK BIT(11) -#define APPLE_ANS_LINEAR_SQ_CTRL 0x24908#define APPLE_ANS_LINEAR_SQ_EN BIT(0)@@ -1130,17 +1127,6 @@ static void apple_nvme_reset_work(struct work_struct *work)/* Setup the NVMMU for the maximum admin and IO queue depth */ writel (anv->hw->max_queue_depth -1 , anv->mmio_nvme +APPLE_NVMMU_NUM_TCBS ); - -/*
drivers/nvme/host/apple.c static void apple_nvme_reset_work(struct work_struct *work) · line 1130
Quoted 10 lines
drivers/nvme/host/apple.c +0
-9-* This is probably a chicken bit: without it all commands -* where any PRP is set to zero (including those that don't use -* that field) fail and the co-processor complains about -* "completed with err BAD_CMD-" or a "NULL_PRP_PTR_ERR" in the -* syslog -*/ -writel (readl (anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ) & - ~APPLE_ANS_PRP_NULL_CHECK , - anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL ); }
Hi Greg/Sasha,
I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.
Upstream removes the Apple PRP workaround only after69d22a6b2f698 adds a page-alignment quirk and ea2160c7b7818 enables it for nvme-apple.
Upstream code with both alignment prerequisites:
if (is_admin &&
(ctrl->quirks & NVME_QUIRK_ADMIN_PAGE_ALIGN))
lim->dma_alignment = NVME_CTRL_PAGE_SIZE - 1;Downstream 6.12.y code:
lim->dma_alignment = 3; NVME_QUIRK_SKIP_CID_GEN | NVME_QUIRK_IDENTIFY_CNS
Neither is present in 6.12, whose admin queue still permits four-byte buffer alignment.
I suggest porting those two prerequisites to the older queue-limit API and applying them before this deletion. Thoughts?
thanks,
Harshit
Quoted 2 lines
drivers/nvme/host/apple.c +0
-0/* Setup the admin queue */
Re: [PATCH 6.12 1061/1376] nvme-apple: Drop the PRP null check chicken bit
Harshit Mogalapalli 於 2026/9/16 下午4:32 寫道:
Quoted 79 lines · Greg Kroah-Hartman
Quoted 47 lines
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sven Peter <[email protected]>
[ Upstream commit
8ce883fd068b7ba9ab493cd3ecca3a7ea868c375]Now that we program the DMA direction correctly the NULL check that used to make commands fail passes. Another side effect of this bit was that non-align buffers on the admin queue were silently allowed and that's been fixed now as well and we this don't need this chicken bit anymore. More importantly, starting with the firmware installed with macOS 15, which is required for M4 but can also be installed on the previous SoCs, the controller no longer exposes this control register and any access SErrors instead. Just drop the write entirely.
Fixes 5bd2927aceba ("nvme-apple: Add initial Apple SoC NVMe driver")Tested-by Joshua Peisach <[email protected]>Tested-by Janne Grunau <[email protected]>Tested-by Nick Chan <[email protected]>Signed-off-by Sven Peter <[email protected]>Signed-off-by Sasha Levin <[email protected]>---
drivers/nvme/host/apple.c| 14 --------------1 file changed, 14 deletions(-)drivers/nvme/host/apple.c +0
-0@@ -47,9 +47,6 @@ #define APPLE_ANS_BOOT_STATUS 0x1300 #define APPLE_ANS_BOOT_STATUS_OK 0xde71ce55-#define APPLE_ANS_UNKNOWN_CTRL
0x24008drivers/nvme/host/apple.c +0
-2-#define APPLE_ANS_PRP_NULL_CHECK BIT(11) -#define
APPLE_ANS_LINEAR_SQ_CTRL0x24908
#define APPLE_ANS_LINEAR_SQ_EN BIT(0)
@@ -1130,17 +1127,6 @@ static voidapple_nvme_reset_work(struct work_struct *work)/* Setup the NVMMU for the maximum admin and IO queue depth */ writel(anv->hw->max_queue_depth - 1,
anv->mmio_nvme+APPLE_NVMMU_NUM_TCBS);
-
- /*
- * This is probably a chicken bit: without it all commands
- * where any PRP is set to zero (including those that don't useQuoted 8 lines
- * that field) fail and the co-processor complains about - * "completed with err BAD_CMD-" or a "
NULL_PRP_PTR_ERR" in the - * syslog
- */
- writel(readl(anv->mmio_nvme +APPLE_ANS_UNKNOWN_CTRL) & - ~APPLE_ANS_PRP_NULL_CHECK,
-anv->mmio_nvme+APPLE_ANS_UNKNOWN_CTRL);
}Hi Greg/Sasha,
I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.
Upstream removes the Apple PRP workaround only after
69d22a6b2f698adds a page-alignment quirk andea2160c7b7818enables it for nvme-apple.Upstream code with both alignment prerequisites:
if (
is_admin&&
(ctrl->quirks&NVME_QUIRK_ADMIN_PAGE_ALIGN))lim->dma_alignment=NVME_CTRL_PAGE_SIZE- 1;Downstream 6.12.y code:
lim->dma_alignment= 3;NVME_QUIRK_SKIP_CID_GEN|NVME_QUIRK_IDENTIFY_CNS
Quoted 5 lines
Neither is present in 6.12, whose admin queue still permits four-byte buffer alignment.
I suggest porting those two prerequisites to the older queue-limit API and applying them before this deletion. Thoughts?
This appears to be correct. In addition, the way this deletion is currently structured depends on commit
04d8ecf37b5e06d1 ("nvme: apple: Add Apple A11 support")
solely because adding A11 support wrapped the existing code in an if statement. The correct patch would be to remove the corresponding code directly in 6.1, 6.6 and 6.12.
I suggest this patch and all of its dependencies to be dropped from 6.1, 6.6 and 6.12 so this patch can be backported properly alongside the page size quirk later.
Patches that should be dropped:
04d8ecf37b5e06d1 ("nvme: apple: Add Apple A11 support") 6fe0687245e8406b ("nvme-apple: Prevent shared tags across queues on Apple A11") a6ab75639e23169a ("nvme-apple: Reset q->sq_tail during queue init") 8ce883fd068b7ba9 ("nvme-apple: Drop the PRP null check chicken bit")
Best Regards,
Nick Chan
Quoted 5 lines
thanks,
HarshitQuoted 1 line
/* Setup the admin queue */
Sasha Levin
maintainerRe: [PATCH 6.12 1061/1376] nvme-apple: Drop the PRP null check chicken bit
Quoted 3 lines
I suggest this patch and all of its dependencies to be dropped from 6.1, 6.6 and 6.12 so this patch can be backported properly alongside the page size quirk later.
All four shipped already, in 6.12.110, 6.6.157 and 6.1.188, so they come out as reverts on top rather than as queue drops. The four reverts are queued for 6.12, 6.6 and 6.1.
Please send the proper backport alongside the page size quirk when you have it ready.
Signature · 2 lines
Thanks, Sasha
Greg Kroah-Hartman
maintainerPATCH 1062/1376 nvmet: fix NULL pointer dereference in nvmet_execute_identify_ns_zns()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guixin Liu <[email protected]>
[ Upstream commit f594863967d87b7fcbff6e724d51135fd701a13d ]
When a host issues an Identify command with CNS 05h (I/O Command Set specific Identify Namespace) and CSI 02h (ZNS) targeting a file-backed namespace, nvmet_execute_identify_ns_zns() calls bdev_is_zoned() on req->ns->bdev. A file-backed namespace has no block device, so req->ns->bdev is NULL and bdev_is_zoned() dereferences it, oopsing.
The I/O command set is selected by the host-supplied CSI field and the command is routed here whenever CONFIG_BLK_DEV_ZONED is enabled, independent of the namespace backing type, so any file-backed namespace is exposed.
Reject the command with Invalid Field when the namespace is not backed by a block device.
--- drivers/nvme/target/zns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/nvme/target/zns.c +1 -1
@@ -120,7 +120,7 @@ void nvmet_execute_identify_ns_zns(struct nvmet_req *req)mutex_unlock (&req->ns->subsys->lock); } -if (!bdev_is_zoned (req->ns->bdev)) { +if (!req->ns->bdev || !bdev_is_zoned (req->ns->bdev)) { status =NVME_SC_INVALID_FIELD |NVME_STATUS_DNR ; req->error_loc = offsetof(struct nvme_identify , nsid); goto out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1063/1376 nvme/ioctl: check SUBMIT_IO with nvme_cmd_allowed()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yang Xiuwei <[email protected]>
[ Upstream commit b53d495c7f0db46b6748b5ade48371a10dd5d3bc ]
Unlike IO_CMD / IO64_CMD, NVME_IOCTL_SUBMIT_IO never calls nvme_cmd_allowed(). Unprivileged callers can thus issue I/O on a partition device or write through a read-only file descriptor.
Pass flags and open_for_write through and reject disallowed commands with -EACCES.
Stable-dep-of: 1161be71d1ec ("nvme: reject passthrough of driver-managed Set Features")
--- drivers/nvme/host/ioctl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
drivers/nvme/host/ioctl.c +6 -2
@@ -220,7 +220,8 @@ static int nvme_submit_user_cmd(struct request_queue *q,return ret; } -static int nvme_submit_io (struct nvme_ns *ns,struct nvme_user_io __user *uio) +static int nvme_submit_io (struct nvme_ns *ns,struct nvme_user_io __user *uio, +unsigned int flags,bool open_for_write) {struct nvme_user_io io;struct nvme_command c;
@@ -278,6 +279,9 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.lbat =cpu_to_le16 (io.apptag); c.rw.lbatm =cpu_to_le16 (io.appmask); +if (!nvme_cmd_allowed (ns, &c, flags, open_for_write)) +return -EACCES ; +return nvme_submit_user_cmd (ns->queue, &c, io.addr, length, metadata, meta_len,NULL ,0 ,0 ); }
@@ -582,7 +586,7 @@ static int nvme_ns_ioctl(struct nvme_ns *ns, unsigned int cmd,case NVME_IOCTL_SUBMIT_IO32 :#endif case NVME_IOCTL_SUBMIT_IO : -return nvme_submit_io (ns, argp); +return nvme_submit_io (ns, argp, flags, open_for_write);case NVME_IOCTL_IO64_CMD_VEC : flags |=NVME_IOCTL_VEC ; fallthrough;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1064/1376 nvme: reject passthrough of driver-managed Set Features
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chao Shi <[email protected]>
[ Upstream commit 1161be71d1ecf7dc785382114c71afed0349531e ]
Since commit b58da2d270db ("nvme: update keep alive interval when kato is modified"), a Set Features (KATO) passthrough command lets userspace start keep-alive on any transport. nvme_keep_alive_work() allocates with BLK_MQ_REQ_RESERVED, but nvme_alloc_admin_tag_set() reserves admin tags only for fabrics, so on other transports the allocation trips WARN_ON_ONCE() in blk_mq_get_tag() and fails:
nvme nvme0: keep-alive failed: -11
Several Set Features change controller state the driver manages itself and cannot react to when set behind its back. Reject these in nvme_admin_cmd_allowed():
- KATO on non-fabrics (keep-alive is only armed for fabrics; on PCIe it has no reserved tag and harms idle power states)
- Host Behavior Support, Host Memory Buffer, Number of Queues, and Autonomous Power State Transition (all driver-managed)
Keep Alive on fabrics is unchanged; I/O commands are unaffected as the check is confined to the admin path (ns == NULL).
Found by FuzzNvme.
--- drivers/nvme/host/ioctl.c | 111 ++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 39 deletions(-)
drivers/nvme/host/ioctl.c +72 -39
@@ -14,45 +14,54 @@ enum { NVME_IOCTL_PARTITION = (1 << 1), }; -static bool nvme_cmd_allowed (struct nvme_ns *ns,struct nvme_command *c, -unsigned int flags,bool open_for_write) +static bool nvme_admin_cmd_allowed (struct nvme_ctrl *ctrl, +struct nvme_command *c) { -u32 effects; - -/* -* Do not allow unprivileged passthrough on partitions, as that allows an -* escape from the containment of the partition. -*/ -if (flags &NVME_IOCTL_PARTITION ) - goto admin; - -/* -* Do not allow unprivileged processes to send vendor specific or fabrics -* commands as we can't be sure about their effects. -*/ -if (c->common.opcode >= nvme_cmd_vendor_start || - c->common.opcode == nvme_fabrics_command) - goto admin; -/* * Do not allow unprivileged passthrough of admin commands except * for a subset of identify commands that contain information required * to form proper I/O commands in userspace and do not expose any * potentially sensitive information. */ -if (!ns) { -if (c->common.opcode == nvme_admin_identify) { -switch (c->identify.cns) { -case NVME_ID_CNS_NS : -case NVME_ID_CNS_CS_NS : -case NVME_ID_CNS_NS_CS_INDEP : -case NVME_ID_CNS_CS_CTRL : -case NVME_ID_CNS_CTRL : -return true; - } +switch (c->common.opcode) { +case nvme_admin_identify: +switch (c->identify.cns) { +case NVME_ID_CNS_NS : +case NVME_ID_CNS_CS_NS : +case NVME_ID_CNS_NS_CS_INDEP : +case NVME_ID_CNS_CS_CTRL : +case NVME_ID_CNS_CTRL : +return true; } - goto admin; +break ; +case nvme_admin_set_features: +/* +* Reject Set Features that change controller state the driver +* manages itself; setting them behind the driver's back from +* userspace leaves it unable to react correctly. Keep Alive is +* only armed for fabrics - on other transports it has no +* reserved tag and harms idle power states. +*/ +switch (le32_to_cpu (c->features.fid) &0xff ) { +case NVME_FEAT_KATO : +if (ctrl->ops->flags &NVME_F_FABRICS ) +break ; + fallthrough; +case NVME_FEAT_HOST_BEHAVIOR : +case NVME_FEAT_HOST_MEM_BUF : +case NVME_FEAT_NUM_QUEUES : +case NVME_FEAT_AUTO_PST : +return false; + } +break ; } +return capable (CAP_SYS_ADMIN ); +} + +static bool nvme_ns_cmd_allowed (struct nvme_ns *ns,struct nvme_command *c, +bool open_for_write) +{ +u32 effects;/* * Check if the controller provides a Commands Supported and Effects log
@@ -61,7 +70,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,*/ effects =nvme_command_effects (ns->ctrl, ns, c->common.opcode);if (!(effects &NVME_CMD_EFFECTS_CSUPP )) - goto admin; +return capable (CAP_SYS_ADMIN );/* * Don't allow passthrough for command that have intrusive (or unknown)
@@ -70,7 +79,7 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,if (effects & ~(NVME_CMD_EFFECTS_CSUPP |NVME_CMD_EFFECTS_LBCC |NVME_CMD_EFFECTS_UUID_SEL |NVME_CMD_EFFECTS_SCOPE_MASK )) - goto admin; +return capable (CAP_SYS_ADMIN );/* * Only allow I/O commands that transfer data to the controller or that
@@ -79,11 +88,34 @@ static bool nvme_cmd_allowed(struct nvme_ns *ns, struct nvme_command *c,*/ if ((nvme_is_write (c) || (effects &NVME_CMD_EFFECTS_LBCC )) && !open_for_write) - goto admin; +return capable (CAP_SYS_ADMIN );return true; -admin: -return capable (CAP_SYS_ADMIN ); +} + +static bool nvme_cmd_allowed (struct nvme_ctrl *ctrl,struct nvme_ns *ns, +struct nvme_command *c,unsigned int flags, +bool open_for_write) +{ +/* +* Do not allow unprivileged passthrough on partitions, as that +* allows an escape from the containment of the partition. +*/ +if (flags &NVME_IOCTL_PARTITION ) +return capable (CAP_SYS_ADMIN ); + +/* +* Do not allow unprivileged processes to send vendor specific or +* fabrics commands as we can't be sure about their effects. +*/ +if (c->common.opcode >= nvme_cmd_vendor_start || + c->common.opcode == nvme_fabrics_command) +return capable (CAP_SYS_ADMIN ); + +if (!ns) +return nvme_admin_cmd_allowed (ctrl, c); + +return nvme_ns_cmd_allowed (ns, c, open_for_write); }/*
@@ -279,7 +311,7 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio, c.rw.lbat =cpu_to_le16 (io.apptag); c.rw.lbatm =cpu_to_le16 (io.appmask); -if (!nvme_cmd_allowed (ns, &c, flags, open_for_write)) +if (!nvme_cmd_allowed (ns->ctrl, ns, &c, flags, open_for_write))return -EACCES ; return nvme_submit_user_cmd(ns->queue, &c, io.addr, length, metadata,
@@ -329,7 +361,7 @@ static int nvme_user_cmd(struct nvme_ctrl *ctrl, struct nvme_ns *ns, c.common.cdw14 =cpu_to_le32 (cmd.cdw14); c.common.cdw15 =cpu_to_le32 (cmd.cdw15); -if (!nvme_cmd_allowed (ns, &c,0 , open_for_write)) +if (!nvme_cmd_allowed (ctrl, ns, &c,0 , open_for_write))return -EACCES ;if (cmd.timeout_ms)
@@ -376,7 +408,7 @@ static int nvme_user_cmd64(struct nvme_ctrl *ctrl, struct nvme_ns *ns, c.common.cdw14 =cpu_to_le32 (cmd.cdw14); c.common.cdw15 =cpu_to_le32 (cmd.cdw15); -if (!nvme_cmd_allowed (ns, &c, flags, open_for_write)) +if (!nvme_cmd_allowed (ctrl, ns, &c, flags, open_for_write))return -EACCES ;if (cmd.timeout_ms)
@@ -462,6 +494,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns,struct nvme_uring_cmd_pdu *pdu =nvme_uring_cmd_pdu (ioucmd);const struct nvme_uring_cmd *cmd =io_uring_sqe_cmd (ioucmd->sqe);struct request_queue *q = ns ? ns->queue : ctrl->admin_q; +bool open_for_write = ioucmd->file->f_mode &FMODE_WRITE ;struct nvme_uring_data d;struct nvme_command c;struct request *req;
@@ -490,7 +523,7 @@ static int nvme_uring_cmd_io(struct nvme_ctrl *ctrl, struct nvme_ns *ns, c.common.cdw14 =cpu_to_le32 (READ_ONCE (cmd->cdw14)); c.common.cdw15 =cpu_to_le32 (READ_ONCE (cmd->cdw15)); -if (!nvme_cmd_allowed (ns, &c,0 , ioucmd->file->f_mode &FMODE_WRITE )) +if (!nvme_cmd_allowed (ctrl, ns, &c,0 , open_for_write))return -EACCES ; d.metadata =READ_ONCE (cmd->metadata);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1065/1376 nfc: llcp: avoid userspace overflow on invalid optlen
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 99985bfa8336fadcc69190ba2dcbd5386af3d661 ]
nfc_llcp_getsockopt() casts optval to (u32 __user *) for put_user(), so the kernel always stores 4 bytes regardless of the caller-supplied optlen. The existing min_t(u32, len, sizeof(u32)) only clamps the length reported back to userspace; it does not constrain the store. A call with optlen < 4 therefore writes past the user buffer, violating the getsockopt(2) contract for all five supported optnames.
Reject any call with optlen < sizeof(u32) up front. 'len' is int, so a plain size comparison would promote a negative optlen to size_t and slip past the check; an explicit 'len < 0' test is added first to catch negative values before the size compare.
--- net/nfc/llcp_sock.c | 6 ++++++ 1 file changed, 6 insertions(+)
net/nfc/llcp_sock.c +6 -0
@@ -319,6 +319,12 @@ static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,if (get_user (len, optlen))return -EFAULT ; +if (len <0 ) +return -EINVAL ; + +if (len <sizeof (u32)) +return -EINVAL ; + local = llcp_sock->local;if (!local)return -ENODEV ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1066/1376 nfc: llcp: read llcp_sock->local under the socket lock in getsockopt
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Breno Leitao <[email protected]>
[ Upstream commit 36812527052c5bfb1ec6c1e292d67a5bf76b750f ]
nfc_llcp_getsockopt() read llcp_sock->local before lock_sock(sk) and then dereferenced the cached pointer inside the locked region. llcp_sock_bind() assigns and clears llcp_sock->local under the same socket lock, dropping the last reference on its error path. A getsockopt() racing an in-flight bind() can observe the pointer, block on lock_sock(), and then dereference a freed nfc_llcp_local once bind() has unwound.
Move the llcp_sock->local read and the NULL check inside the lock_sock(sk) region so bind() cannot mutate or free the pointer between the load and the use.
--- net/nfc/llcp_sock.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
net/nfc/llcp_sock.c +6 -4
@@ -325,14 +325,16 @@ static int nfc_llcp_getsockopt(struct socket *sock, int level, int optname,if (len <sizeof (u32))return -EINVAL ; - local = llcp_sock->local; -if (!local) -return -ENODEV ; - len =min_t (u32, len,sizeof (u32));lock_sock (sk); + local = llcp_sock->local; +if (!local) { +release_sock (sk); +return -ENODEV ; + } +switch (optname) {case NFC_LLCP_RW : rw = llcp_sock->rw >LLCP_MAX_RW ? local->rw : llcp_sock->rw;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1067/1376 nfc: nci: fix double completion race in nci_data_exchange_complete
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhenghang Xiao <[email protected]>
[ Upstream commit 8265a626cc14a48e46e6dc8c47667e72b4232ac2 ]
nci_close_device() and nci_rx_work can both call nci_data_exchange_complete() concurrently. After commit 4527025d440ce8 ("nfc: nci: fix circular locking dependency in nci_close_device") moved flush_workqueue(ndev->rx_wq) after mutex_unlock(&ndev->req_lock), rx_work is no longer serialized with the explicit completion call in the close path. Both callers read the non-NULL callback pointer and invoke rawsock_data_exchange_complete(), which calls sock_put() -- but only one sock_hold() was taken, so the second sock_put() underflows the refcount and frees the socket while it is still in use.
Replace the bare clear_bit(NCI_DATA_EXCHANGE) with test_and_clear_bit() so that only the first caller proceeds to invoke the callback.
--- net/nfc/nci/data.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
net/nfc/nci/data.c +5 -5
@@ -46,11 +46,11 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb,del_timer_sync (&ndev->data_timer);clear_bit (NCI_DATA_EXCHANGE_TO , &ndev->flags); -/* Mark the exchange as done before calling the callback. -* The callback (e.g. rawsock_data_exchange_complete) may -* want to immediately queue another data exchange. -*/ -clear_bit (NCI_DATA_EXCHANGE , &ndev->flags); +/* Claim completion atomically -- both close and rx_work may race here */ +if (!test_and_clear_bit (NCI_DATA_EXCHANGE , &ndev->flags)) { +kfree_skb (skb); +return ; + }if (cb) {/* forward skb to nfc core */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1068/1376 nfc: llcp: bound SNL TLV parsing to the skb and add length checks
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Doruk Tan Ozturk <[email protected]>
[ Upstream commit f4c7f37f0ab990952539dc68d931d65c3657600a ]
nfc_llcp_recv_snl() walked the SNL TLV list using a u16 offset/length pair derived from skb->len, without bounding reads to the actual skb data. Three problems followed:
- For a short frame (skb->len < LLCP_HEADER_SIZE), tlv_len underflowed.
- The per-TLV header (type, length) was read without checking that two bytes remained.
- A declared TLV length could run past the end of the buffer, and an SDREQ with length == 0 made "service_name_len = length - 1" underflow (size_t), driving an out-of-bounds read in the following strncmp() / nfc_llcp_sock_from_sn(). The SDRES case likewise read tlv[2]/tlv[3] without a length check.
A nearby NFC device can reach this without authentication; LLCP link activation happens automatically after NFC-DEP.
Walk the TLV list by pointer, bounded by skb_tail_pointer() over the linear skb data, and validate each TLV declared length before use. Add explicit length checks for SDREQ (>= 1) and SDRES (exactly 2).
Found by 0sec automated security-research tooling (https://0sec.ai).
--- net/nfc/llcp_core.c | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-)
net/nfc/llcp_core.c +21 -8
@@ -1286,10 +1286,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, {struct nfc_llcp_sock *llcp_sock;u8 dsap, ssap, type, length, tid, sap; -const u8 *tlv; -u16 tlv_len, offset; +const u8 *tlv, *tlv_end;const char *service_name; -size_t service_name_len; +int service_name_len;struct nfc_llcp_sdp_tlv *sdp;HLIST_HEAD (llc_sdres_list);size_t sdres_tlvs_len;
@@ -1305,22 +1304,34 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local, return; } + /* + * Walk the SNL TLV list in the linear part of the skb only, + * bounded by skb_tail_pointer(). Each TLV needs a two-byte + * header (type, length) and its declared length must fit before + * the end; this also keeps the walk safe for very short frames. + */ tlv = &skb->data[LLCP_HEADER_SIZE]; - tlv_len = skb->len - LLCP_HEADER_SIZE; - offset = 0; + tlv_end = skb_tail_pointer(skb); sdres_tlvs_len = 0; - while (offset < tlv_len) { + while (tlv + 2 < tlv_end) { type = tlv[0]; length = tlv[1]; + if (tlv + 2 + length > tlv_end) + break; + switch (type) { case LLCP_TLV_SDREQ: + if (length < 1) + break; + tid = tlv[2]; service_name = (char *) &tlv[3]; service_name_len = length - 1; - pr_debug("Looking for %.16s\n", service_name); + pr_debug("Looking for %.*s\n", service_name_len, + service_name); if (service_name_len == strlen("urn:nfc:sn:sdp") && !strncmp(service_name, "urn:nfc:sn:sdp",
@@ -1380,6 +1391,9 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,break ;case LLCP_TLV_SDRES : +if (length !=2 ) +break ; +mutex_lock (&local->sdreq_lock);pr_debug ("LLCP_TLV_SDRES: searching tid %d\n" , tlv[2 ]);
@@ -1408,7 +1422,6 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,break ; } - offset += length +2 ; tlv += length +2 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1069/1376 nfc: pn533: hold a reference to the request skb during send_frame
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yinhao Hu <[email protected]>
[ Upstream commit 47792358a624ea066455ef86b744159928cd7716 ]
__pn533_send_async() publishes the command and then calls dev->phy_ops->send_frame(). Once dev->cmd is set, an incoming frame can be matched to this command: the I2C threaded IRQ runs pn533_recv_frame(), which queues cmd_complete_work, and pn533_send_async_complete() frees cmd->req with consume_skb().
On the I2C transport, pn533_i2c_send_frame() still dereferences the same skb after i2c_master_send() returns, so a completion that races the send can free the skb while the transport is still using it.
The request skb is owned by the command object and may be freed by command completion at any time after dev->cmd is published, so the transport send path must not assume it stays alive. Hold a temporary reference to the request skb across the send_frame() call so the transport always sees a live skb even if completion races the send. Add a pn533_send_cmd_frame() helper and use it from all three send paths.
--- drivers/nfc/pn533/pn533.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-)
drivers/nfc/pn533/pn533.c +15 -6
@@ -434,6 +434,18 @@ static int pn533_send_async_complete(struct pn533 *dev)return rc; } +static int pn533_send_cmd_frame (struct pn533 *dev,struct pn533_cmd *cmd) +{ +struct sk_buff *req = cmd->req; +int rc; + +skb_get (req); + dev->cmd = cmd; + rc = dev->phy_ops->send_frame (dev, req); +dev_kfree_skb (req); +return rc; +} + static int __pn533_send_async(struct pn533 *dev, u8 cmd_code, struct sk_buff *req, pn533_send_async_complete_t complete_cb,
@@ -458,8 +470,7 @@ static int __pn533_send_async(struct pn533 *dev, u8 cmd_code,mutex_lock (&dev->cmd_lock);if (!dev->cmd_pending) { - dev->cmd = cmd; - rc = dev->phy_ops->send_frame (dev, req); + rc =pn533_send_cmd_frame (dev, cmd);if (rc) { dev->cmd =NULL ; goto error;
@@ -529,8 +540,7 @@ static int pn533_send_cmd_direct_async(struct pn533 *dev, u8 cmd_code,pn533_build_cmd_frame (dev, cmd_code, req); - dev->cmd = cmd; - rc = dev->phy_ops->send_frame (dev, req); + rc =pn533_send_cmd_frame (dev, cmd);if (rc <0 ) { dev->cmd =NULL ;kfree (cmd);
@@ -569,8 +579,7 @@ static void pn533_wq_cmd(struct work_struct *work)mutex_unlock (&dev->cmd_lock); - dev->cmd = cmd; - rc = dev->phy_ops->send_frame (dev, cmd->req); + rc =pn533_send_cmd_frame (dev, cmd);if (rc <0 ) { dev->cmd =NULL ;dev_kfree_skb (cmd->req);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1070/1376 nfc: digital: Do not dump a NULL response in command completion
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit 1c7dd70c0adfa58fd66b5cbd03efb747ad6d8d8d ]
digital_wq_cmd_complete() dumps the response data whenever cmd->resp is not an error pointer. However, a driver can legitimately complete a command with no response skb at all.
digital_tg_send_psl_res() is the only caller that passes timeout=0, meaning no response is expected once the command has been transmitted. On that path trf7970a completes the command with
trf->rx_skb = ERR_PTR(0);
which evaluates to NULL. IS_ERR(NULL) is false, so the NULL response passes the !IS_ERR() check and cmd->resp->data and cmd->resp->len are dereferenced whenever the debug print site is enabled. The driver guards its own dump with "trf->rx_skb && !IS_ERR(trf->rx_skb)"; the digital layer is missing the NULL half of that test.
Use IS_ERR_OR_NULL() so that NULL responses are skipped as well. The callback on that path, digital_tg_send_psl_res_complete(), never dereferences resp and dev_kfree_skb() accepts NULL, so only the debug dump needs fixing.
--- net/nfc/digital_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
net/nfc/digital_core.c +1 -1
@@ -127,7 +127,7 @@ static void digital_wq_cmd_complete(struct work_struct *work)mutex_unlock (&ddev->cmd_lock); -if (!IS_ERR (cmd->resp)) +if (!IS_ERR_OR_NULL (cmd->resp))print_hex_dump_debug ("DIGITAL RX: " ,DUMP_PREFIX_NONE ,16 ,1 , cmd->resp->data, cmd->resp->len, false);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1071/1376 nfc: nci: fix use of uninitialized memory in CORE_INIT_RSP parsing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yun Zhou <[email protected]>
[ Upstream commit d56575a2595ee1f597f39e8a1cfb67ed3501678d ]
nci_core_init_rsp_packet_v1() and nci_core_init_rsp_packet_v2() parse the CORE_INIT_RSP packet without validating that the skb contains enough data. A malformed response (e.g. injected via virtual_ncidev) can declare a large num_supported_rf_interfaces while providing insufficient data, causing reads of uninitialized slab memory. This is later used in nci_init_complete_req(), triggering a KMSAN uninit-value warning.
Add skb length checks before accessing packet fields:
- Validate the skb has at least 1 byte for the status field.
- Validate the skb can hold the fixed-size header before parsing.
- In v2, bounds-check each variable-length rf_interface entry and its extension parameters within the parsing loop.
- In v1, verify the skb is large enough for both the variable-length rf_interfaces array and the trailing rsp_2 structure.
--- net/nfc/nci/rsp.c | 41 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-)
net/nfc/nci/rsp.c +38 -3
@@ -50,11 +50,27 @@ static u8 nci_core_init_rsp_packet_v1(struct nci_dev *ndev,const struct nci_core_init_rsp_1 *rsp_1 = (void *)skb->data;const struct nci_core_init_rsp_2 *rsp_2; +/* Ensure that the status field can be accessed. */ +if (skb_headlen (skb) <1 ) +return NCI_STATUS_SYNTAX_ERROR ; +pr_debug ("status 0x%x\n" , rsp_1->status);if (rsp_1->status !=NCI_STATUS_OK )return rsp_1->status; +/* Success response must contain the full fixed-size header */ +if (skb_headlen (skb) <sizeof (*rsp_1)) +return NCI_STATUS_SYNTAX_ERROR ; + +/* Ensure the variable-length rf_interfaces array and trailing +* rsp_2 structure are fully contained within the skb. +*/ +if (skb_headlen (skb) <sizeof (*rsp_1) + + rsp_1->num_supported_rf_interfaces + +sizeof (*rsp_2)) +return NCI_STATUS_SYNTAX_ERROR ; + ndev->nfcc_features =__le32_to_cpu (rsp_1->nfcc_features); ndev->num_supported_rf_interfaces = rsp_1->num_supported_rf_interfaces;
@@ -87,15 +103,25 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev, const struct sk_buff *skb) {const struct nci_core_init_rsp_nci_ver2 *rsp = (void *)skb->data; -const u8 *supported_rf_interface = rsp->supported_rf_interfaces; +const u8 *supported_rf_interface;u8 rf_interface_idx =0 ;u8 rf_extension_cnt =0 ; +/* Ensure that the status field can be accessed. */ +if (skb_headlen (skb) <1 ) +return NCI_STATUS_SYNTAX_ERROR ; +pr_debug ("status %x\n" , rsp->status);if (rsp->status !=NCI_STATUS_OK )return rsp->status; +/* Success response must contain the full fixed-size header */ +if (skb_headlen (skb) <sizeof (*rsp)) +return NCI_STATUS_SYNTAX_ERROR ; + + supported_rf_interface = rsp->supported_rf_interfaces; + ndev->nfcc_features =__le32_to_cpu (rsp->nfcc_features); ndev->num_supported_rf_interfaces = rsp->num_supported_rf_interfaces;
@@ -104,13 +130,22 @@ static u8 nci_core_init_rsp_packet_v2(struct nci_dev *ndev, NCI_MAX_SUPPORTED_RF_INTERFACES);while (rf_interface_idx < ndev->num_supported_rf_interfaces) { - ndev->supported_rf_interfaces[rf_interface_idx++] = *supported_rf_interface++; +/* Each entry: [rf_interface_type (1B)] [ext_count (1B)] [ext...] */ +if (supported_rf_interface +2 >skb_tail_pointer (skb)) +break ; + ndev->supported_rf_interfaces[rf_interface_idx] = *supported_rf_interface++; -/* skip rf extension parameters */ rf_extension_cnt = *supported_rf_interface++; +if (supported_rf_interface + rf_extension_cnt >skb_tail_pointer (skb)) +break ; + +/* Only count the entry after full validation */ + rf_interface_idx++; supported_rf_interface += rf_extension_cnt; } + ndev->num_supported_rf_interfaces = rf_interface_idx; + ndev->max_logical_connections = rsp->max_logical_connections; ndev->max_routing_table_size =__le16_to_cpu (rsp->max_routing_table_size);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1072/1376 ALSA: seq: Dont leak the extension cell pointer in the bounce payload
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: HyeongJun An <[email protected]>
[ Upstream commit 59e1592d3c270ff4642d5d6dc55c545306eb0693 ]
The bounce_error_event() embeds the failed event in the bounce payload by pointing data.ext.ptr at it. When that event is a queued variable-length event, its own data.ext.ptr holds the address of its first extension cell, put there by snd_seq_event_dup(). The payload goes out verbatim through snd_seq_expand_var_event(), so the address reaches userspace.
That is the same address commit 705dd6dcbc0e ("ALSA: seq: Clear variable event pointer on read") removed from the event header. The read path still clears it there, just above the call that expands the payload.
Embed a sanitised copy instead, treated exactly as snd_seq_read() treats the header. A stack copy is enough because delivery is synchronous and snd_seq_event_dup() copies before returning.
An unprivileged client reaches this by setting SNDRV_SEQ_FILTER_BOUNCE, queueing a variable-length event to a port that does not exist and reading the bounce back. Eight bytes on 64-bit, from its own pool.
--- sound/core/seq/seq_clientmgr.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
sound/core/seq/seq_clientmgr.c +10 -6
@@ -579,7 +579,7 @@ static int bounce_error_event(struct snd_seq_client *client,struct snd_seq_event *event, int err, int atomic, int hop) { - struct snd_seq_event bounce_ev; + struct snd_seq_event bounce_ev, quoted;int result; if (client == NULL ||
@@ -595,15 +595,19 @@ static int bounce_error_event(struct snd_seq_client *client,* For user clients, send SNDRV_SEQ_EVENT_BOUNCE with the * original event embedded as variable-length data. This * avoids exposing data.quote.event (a kernel pointer) to -* userspace. The variable-length path in snd_seq_event_dup() -* copies the event data from data.ext.ptr into chained cells, -* and snd_seq_expand_var_event() copies only the data content -* -- never the pointer -- to userspace. +* userspace. Sanitise the embedded copy too - a queued +* variable-length event carries the address of its own +* extension cell, and the payload goes out verbatim. */ + quoted = *event; +if (snd_seq_ev_is_variable ("ed)) { + quoted.data.ext.len &= ~SNDRV_SEQ_EXT_MASK ; + quoted.data.ext.ptr =NULL ; + } bounce_ev.type =SNDRV_SEQ_EVENT_BOUNCE ; bounce_ev.flags =SNDRV_SEQ_EVENT_LENGTH_VARIABLE ; bounce_ev.data.ext.len =sizeof (struct snd_seq_event ); - bounce_ev.data.ext.ptr = (char *)event; + bounce_ev.data.ext.ptr = (char *)"ed; }else {/* * For kernel clients, quote the event pointer directly.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1073/1376 dmaengine: qcom-bam-dma: fix autosuspend cleanup during removal
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit a50184171235045d96104811414b25cf5338cf86 ]
bam_dma_probe() calls pm_runtime_use_autosuspend(), but bam_dma_remove() does not call the matching pm_runtime_dont_use_autosuspend() when removing the device.
If the autosuspend delay is set to a negative value while autosuspend is enabled, the runtime PM core increments usage_count to prevent runtime suspend. Without calling pm_runtime_dont_use_autosuspend() during teardown, this reference is not dropped and usage_count remains unbalanced.
Add the missing pm_runtime_dont_use_autosuspend() call before forcing the device into runtime suspend during removal.
This issue was found by manual code inspection.
--- drivers/dma/qcom/bam_dma.c | 1 + 1 file changed, 1 insertion(+)
drivers/dma/qcom/bam_dma.c +1 -0
@@ -1391,6 +1391,7 @@ static void bam_dma_remove(struct platform_device *pdev)struct bam_device *bdev =platform_get_drvdata (pdev);u32 i; +pm_runtime_dont_use_autosuspend (&pdev->dev);pm_runtime_force_suspend (&pdev->dev);of_dma_controller_free (pdev->dev.of_node);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1074/1376 RDMA/cxgb4: Free debugfs on registration failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Fan Wu <[email protected]>
[ Upstream commit fe5c16bb6252dea6025b748257ddc3b2665495b0 ]
c4iw_alloc() creates the per-device debugfs tree (dev->debugfs_root via setup_debugfs()), but it is removed only in c4iw_remove(), not in c4iw_dealloc(). When RDMA device registration fails, the registration worker's err_dealloc_ctx path calls c4iw_dealloc() directly, bypassing c4iw_remove(), so the debugfs dentries leak and outlive the freed c4iw_dev.
Move debugfs_remove_recursive() into c4iw_dealloc() so every path that frees ctx->dev also removes its debugfs tree.
--- drivers/infiniband/hw/cxgb4/device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/infiniband/hw/cxgb4/device.c +1 -1
@@ -935,6 +935,7 @@ static void c4iw_rdev_close(struct c4iw_rdev *rdev)void c4iw_dealloc (struct uld_ctx *ctx) { +debugfs_remove_recursive (ctx->dev->debugfs_root);c4iw_rdev_close (&ctx->dev->rdev);WARN_ON (!xa_empty (&ctx->dev->cqs));WARN_ON (!xa_empty (&ctx->dev->qps));
@@ -959,7 +960,6 @@ static void c4iw_remove(struct uld_ctx *ctx)if (!ctx->dev)return ; -debugfs_remove_recursive (ctx->dev->debugfs_root);c4iw_unregister_device (ctx->dev);c4iw_dealloc (ctx); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1075/1376 RDMA/cma: Fix WARNING in res_to_rt
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhu Yanjun <[email protected]>
[ Upstream commit c6d1ec4fbe56492bb88987d577f04a5fb6955f26 ]
syzbot reported a WARN_ON(!res->dev) in res_to_rt() triggered via addr_handler() during asynchronous address resolution:
"
WARNING: drivers/infiniband/core/restrack.c:138 at res_to_rt+0x1c4/0x230 CPU#1: kworker/u8:4/59 Modules linked in: CPU: 1 UID: 0 PID: 59 Comm: kworker/u8:4 Not tainted syzkaller #0 PREEMPT(full) Hardware name: Google Compute Engine, BIOS Google 07/24/2026 Workqueue: ib_addr process_one_req RIP: 0010:res_to_rt+0x1c4/0x230 drivers/infiniband/core/restrack.c:138 RSP: 0018:ffffc9000201f850 EFLAGS: 00010293 RAX: ffffffff88d00ce5 RBX: ffff88807f0fd4f8 RCX: ffff88801e6e0000 RDX: 0000000000000000 RSI: ffffffff8fd996f0 RDI: 0000000000000003 RBP: 0000000000000000 R08: ffff88801e6e0000 R09: 000000000000000a R10: 0000000000000009 R11: 0000000000000000 R12: dffffc0000000000 R13: 1ffff1100fe1fa9f R14: 0000000000000000 R15: 0000000000000003 FS: 0000000000000000(0000) GS:ffff888125012000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 00001d559c3d2000 CR3: 0000000077c4c000 CR4: 00000000003526f0 Call Trace: <TASK> rdma_restrack_add+0x5a/0x8a0 drivers/infiniband/core/restrack.c:236 addr_handler+0x41a/0x5a0 drivers/infiniband/core/cma.c:3534 process_one_req+0x2eb/0x540 drivers/infiniband/core/addr.c:624 process_one_work kernel/workqueue.c:3375 [inline] process_scheduled_works+0xc4e/0x1630 kernel/workqueue.c:3458 worker_thread+0xa47/0xfb0 kernel/workqueue.c:3539 kthread+0x388/0x470 kernel/kthread.c:436 ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 </TASK>
"
In addr_handler(), cma_acquire_dev_by_src_ip() is called to populate id_priv->cma_dev and bind the associated ib_device to id_priv->id.device. If cma_acquire_dev_by_src_ip() returns an error (non-zero status), the ID remains unassociated with any RDMA device.
Previously, rdma_restrack_add(&id_priv->res) was invoked unconditionally even when cma_acquire_dev_by_src_ip() failed, passing a resource with a NULL dev pointer and triggering the WARN_ON assertion in res_to_rt().
Fix this by only adding the resource to restrack when acquiring the device succeeds.
--- drivers/infiniband/core/cma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/infiniband/core/cma.c +4 -2
@@ -3517,10 +3517,12 @@ static void addr_handler(int status, struct sockaddr *src_addr,memcpy (addr, src_addr,rdma_addr_size (src_addr));if (!status && !id_priv->cma_dev) { status =cma_acquire_dev_by_src_ip (id_priv); -if (status) +if (status) {pr_debug_ratelimited ("RDMA CM: ADDR_ERROR: failed to acquire device. status %d\n" , status); -rdma_restrack_add (&id_priv->res); + }else { +rdma_restrack_add (&id_priv->res); + } }else if (status) {pr_debug_ratelimited ("RDMA CM: ADDR_ERROR: failed to resolve IP. status %d\n" , status); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1076/1376 ice: clear the default forwarding VSI rule when releasing a VSI
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Petr Oros <[email protected]>
[ Upstream commit d04287e27bf1c0b879a10d929c163f2da69715b6 ]
When a VSI is configured as the switch's default forwarding VSI (ICE_SW_LKUP_DFLT) and is then torn down, the rule is left behind in the switch. ice_vsi_release() no longer removes it, and the SR-IOV VF free path (ice_free_vfs() -> ice_free_vf_res() -> ice_vf_vsi_release() -> ice_vsi_release()) does not disable promiscuous mode either, which only happens on VF reset in ice_vf_clear_all_promisc_modes().
A trusted VF that enters unicast promiscuous mode becomes the default forwarding VSI (this is the default mode, when the PF does not have VF true-promiscuous mode enabled). If the VFs are then destroyed without the VF first leaving promiscuous mode, the ICE_SW_LKUP_DFLT rule for the now-freed VSI is leaked. When VFs are recreated, a VSI reuses the freed hw_vsi_id. If it is assigned a different VSI handle than the leaked rule holds, ice_set_dflt_vsi() does not recognize it as already-default, and ice_add_update_vsi_list() folds the dangling (freed) handle into a VSI list, which the firmware rejects. The VSI handle assigned on re-creation varies, so the failure is intermittent rather than every cycle.
Reproduce by repeatedly running the cycle below on the two ports of the same card, where $VF0 and $VF1 are the netdevs of vf 15 once they appear. The VF must be brought up so iavf actually pushes the unicast promiscuous request, and the rule must settle before the VFs are torn down again:
echo 16 > /sys/class/net/$PF0/device/sriov_numvfs echo 16 > /sys/class/net/$PF1/device/sriov_numvfs ip link set $PF0 vf 15 trust on
ip link set $PF1 vf 15 trust on
ip link set $VF0 up
ip link set $VF1 up
ip link set $VF0 promisc on
ip link set $VF1 promisc on
sleep 1
echo 0 > /sys/class/net/$PF0/device/sriov_numvfs echo 0 > /sys/class/net/$PF1/device/sriov_numvfs
Within a few cycles the ice PF and iavf VF log:
Failed to set VSI 25 as the default forwarding VSI, error -22 Turning on/off promiscuous mode for VF 63 failed, error: -22 PF returned error -53 (IAVF_ERR_ADMIN_QUEUE_ERROR) to our request 14
This cleanup used to live in ice_vsi_release() but was dropped by the referenced refactor. Restore it. Clear the default forwarding VSI rule in ice_vsi_release() when this VSI owns it, which covers every teardown path.
--- drivers/net/ethernet/intel/ice/ice_lib.c | 3 +++ 1 file changed, 3 insertions(+)
drivers/net/ethernet/intel/ice/ice_lib.c +3 -0
@@ -2803,6 +2803,9 @@ int ice_vsi_release(struct ice_vsi *vsi)return -ENODEV ; pf = vsi->back; +if (ice_is_vsi_dflt_vsi (vsi)) +ice_clear_dflt_vsi (vsi); +if (test_bit (ICE_FLAG_RSS_ENA , pf->flags))ice_rss_clean (vsi);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1077/1376 ASoC: pxa: Use devm_clk_get_optional() for extclk clock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: bui duc phuc <[email protected]>
[ Upstream commit 6ec64d757af9b75a3c64f9f7dad76bdc1efc06ca ]
The Device Tree binding defines the extclk clock as an optional property, but the driver currently uses devm_clk_get() and manually handles the absence of the clock.
Use devm_clk_get_optional() to match the binding and simplify the optional clock handling. This also propagates errors other than the absence of the optional clock, including -EPROBE_DEFER.
This changes the existing behavior for errors other than -EPROBE_DEFER. RFC to discuss whether these errors should cause probe to fail rather than being treated as an unavailable optional clock.
--- sound/soc/pxa/pxa-ssp.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
sound/soc/pxa/pxa-ssp.c +2 -5
@@ -769,13 +769,10 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai) goto err_priv; } - priv->extclk =devm_clk_get (dev,"extclk" ); + priv->extclk =devm_clk_get_optional (dev,"extclk" );if (IS_ERR (priv->extclk)) { ret =PTR_ERR (priv->extclk); -if (ret == -EPROBE_DEFER ) - goto err_priv; - - priv->extclk =NULL ; + goto err_priv; } }else { priv->ssp =pxa_ssp_request (dai->id +1 ,"SoC audio" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1078/1376 ASoC: fsl-asoc-card: defer probe when the CPU DAI device is not ready
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: LiangCheng Wang <[email protected]>
[ Upstream commit 0286324da660875dc504fd65b3be87e9c8b9a547 ]
fsl_asoc_card_probe() hard-fails with -EINVAL when the CPU DAI (SAI) platform device is not found. Like the codec, the CPU DAI may just be probed later than the machine driver; the order is not guaranteed and varies across kernel versions, so a permanent -EINVAL leaves the card unregistered with no analog playback or capture.
Defer probe instead, mirroring commit e396dec46c56 ("ASoC:
fsl-asoc-card: Defer probe when fail to find codec device").
Tested on i.MX8MP with an ALC5672 on SAI3: the card that failed to register on v6.18 now comes up during boot.
--- sound/soc/fsl/fsl-asoc-card.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
sound/soc/fsl/fsl-asoc-card.c +2 -2
@@ -648,8 +648,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) cpu_pdev =of_find_device_by_node (cpu_np);if (!cpu_pdev) { -dev_err (&pdev->dev,"failed to find CPU DAI device\n" ); - ret = -EINVAL ; + ret =dev_err_probe (&pdev->dev, -EPROBE_DEFER , +"failed to find CPU DAI device\n" ); goto fail; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1079/1376 UBI: Preserve torture flag when rescheduling failed erasures
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhihao Cheng <[email protected]>
[ Upstream commit 420477a84f1e52babfe60a43dbdd0988ed956845 ]
In __erase_worker(), when ubi_sync_erase() returns -EINTR, -ENOMEM, -EAGAIN, or -EBUSY, the physical eraseblock is rescheduled for erasure via schedule_erase() with the torture flag hardcoded to 0. This unconditionally drops the torture request.
If the error occurred before torture_peb() completed (e.g., -ENOMEM from the ec_hdr allocation in ubi_sync_erase(), errors from self_check_not_bad()/nor_erase_prepare() in ubi_io_sync_erase(), or torture_peb() failing mid-test because its internal do_sync_erase()/ ubi_io_read()/ubi_io_write() returned one of the above error codes), the torture test was never finished. Such a PEB may reach the free pool without being tortured, defeating the purpose of the torture test for detecting marginally-bad eraseblocks.
If the error occurred after torture_peb() succeeded (the final do_sync_erase() in ubi_io_sync_erase() or ubi_io_write_ec_hdr() in ubi_sync_erase() failed), the torture has already been done and need not be repeated.
Pass the torture flag by pointer to ubi_sync_erase() and ubi_io_sync_erase(), and clear it to zero once torture_peb() has completed successfully. __erase_worker() then forwards the updated wl_wrk->torture to schedule_erase(): torture already done is dropped, torture not completed is preserved across the reschedule.
--- drivers/mtd/ubi/attach.c | 4 ++-- drivers/mtd/ubi/fastmap.c | 6 +++--- drivers/mtd/ubi/io.c | 11 +++++++---- drivers/mtd/ubi/ubi.h | 4 ++-- drivers/mtd/ubi/wl.c | 15 +++++++++------ 5 files changed, 23 insertions(+), 17 deletions(-)
drivers/mtd/ubi/attach.c +2 -2
@@ -771,7 +771,7 @@ void ubi_remove_av(struct ubi_attach_info *ai, struct ubi_ainf_volume *av)static int early_erase_peb (struct ubi_device *ubi,const struct ubi_attach_info *ai,int pnum,int ec) { -int err; +int err, torture =0 ;struct ubi_ec_hdr *ec_hdr;if ((long long )ec >=UBI_MAX_ERASECOUNTER ) {
@@ -790,7 +790,7 @@ static int early_erase_peb(struct ubi_device *ubi, ec_hdr->ec =cpu_to_be64 (ec); - err =ubi_io_sync_erase (ubi, pnum,0 ); + err =ubi_io_sync_erase (ubi, pnum, &torture);if (err <0 ) goto out_free;
drivers/mtd/ubi/fastmap.c +3 -3
@@ -1488,7 +1488,7 @@ static void return_fm_pebs(struct ubi_device *ubi,*/ int ubi_update_fastmap (struct ubi_device *ubi) { -int ret, i, j; +int ret, i, j, torture =0 ;struct ubi_fastmap_layout *new_fm, *old_fm;struct ubi_wl_entry *tmp_e;
@@ -1526,7 +1526,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)if (!tmp_e) {if (old_fm && old_fm->e[i]) { - ret =ubi_sync_erase (ubi, old_fm->e[i],0 ); + ret =ubi_sync_erase (ubi, old_fm->e[i], &torture);if (ret <0 ) {ubi_err (ubi,"could not erase old fastmap PEB" );
@@ -1578,7 +1578,7 @@ int ubi_update_fastmap(struct ubi_device *ubi)if (old_fm) {/* no fresh anchor PEB was found, reuse the old one */ if (!tmp_e) { - ret =ubi_sync_erase (ubi, old_fm->e[0 ],0 ); + ret =ubi_sync_erase (ubi, old_fm->e[0 ], &torture);if (ret <0 ) {ubi_err (ubi,"could not erase old anchor PEB" );
drivers/mtd/ubi/io.c +7 -4
@@ -519,8 +519,10 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)/** * ubi_io_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object -* @pnum: physical eraseblock number to erase -* @torture: if this physical eraseblock has to be tortured +* @pnum: the physical eraseblock number to erase +* @torture: if this physical eraseblock has to be tortured; cleared to zero +* once the torture test has completed successfully so that a retry +* of the erase does not torture the physical eraseblock again * * This function synchronously erases physical eraseblock @pnum. If @torture * flag is not zero, the physical eraseblock is checked by means of writing
@@ -532,7 +534,7 @@ static int nor_erase_prepare(struct ubi_device *ubi, int pnum)* codes in case of other errors. Note, %-EIO means that the physical * eraseblock is bad. */ -int ubi_io_sync_erase (struct ubi_device *ubi,int pnum,int torture) +int ubi_io_sync_erase (struct ubi_device *ubi,int pnum,int *torture) {int err, ret =0 ;
@@ -560,10 +562,11 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture)return err; } -if (torture) { +if (*torture) { ret =torture_peb (ubi, pnum);if (ret <0 )return ret; + *torture =0 ; } err =do_sync_erase (ubi, pnum);
drivers/mtd/ubi/ubi.h +2 -2
@@ -902,7 +902,7 @@ int self_check_eba(struct ubi_device *ubi, struct ubi_attach_info *ai_fastmap, struct ubi_attach_info *ai_scan);/* wl.c */ -int ubi_sync_erase (struct ubi_device *ubi,struct ubi_wl_entry *e,int torture); +int ubi_sync_erase (struct ubi_device *ubi,struct ubi_wl_entry *e,int *torture);int ubi_wl_get_peb (struct ubi_device *ubi);int ubi_wl_put_peb (struct ubi_device *ubi,int vol_id,int lnum,int pnum,int torture);
@@ -924,7 +924,7 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, int len);int ubi_io_write (struct ubi_device *ubi,const void *buf,int pnum,int offset,int len); -int ubi_io_sync_erase (struct ubi_device *ubi,int pnum,int torture); +int ubi_io_sync_erase (struct ubi_device *ubi,int pnum,int *torture);int ubi_io_is_bad (const struct ubi_device *ubi,int pnum);int ubi_io_mark_bad (const struct ubi_device *ubi,int pnum); int ubi_io_read_ec_hdr(struct ubi_device *ubi, int pnum,
drivers/mtd/ubi/wl.c +9 -6
@@ -443,12 +443,14 @@ static int prot_queue_del(struct ubi_device *ubi, int pnum)* ubi_sync_erase - synchronously erase a physical eraseblock. * @ubi: UBI device description object * @e: the physical eraseblock to erase -* @torture: if the physical eraseblock has to be tortured +* @torture: if the physical eraseblock has to be tortured; cleared to zero +* once the torture test has completed successfully so that a retry +* of the erase does not torture the physical eraseblock again * * This function returns zero in case of success and a negative error code in * case of failure. */ -int ubi_sync_erase (struct ubi_device *ubi,struct ubi_wl_entry *e,int torture) +int ubi_sync_erase (struct ubi_device *ubi,struct ubi_wl_entry *e,int *torture) {int err;struct ubi_ec_hdr *ec_hdr;
@@ -1113,7 +1115,7 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)dbg_wl ("erase PEB %d EC %d LEB %d:%d" , pnum, e->ec, wl_wrk->vol_id, wl_wrk->lnum); - err =ubi_sync_erase (ubi, e, wl_wrk->torture); + err =ubi_sync_erase (ubi, e, &wl_wrk->torture);if (!err) {spin_lock (&ubi->wl_lock);
@@ -1150,7 +1152,8 @@ static int __erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk)int err1;/* Re-schedule the LEB for erasure */ - err1 =schedule_erase (ubi, e, vol_id, lnum,0 , true); + err1 =schedule_erase (ubi, e, vol_id, lnum, wl_wrk->torture, + true);if (err1) {spin_lock (&ubi->wl_lock);wl_entry_destroy (ubi, e);
@@ -1757,7 +1760,7 @@ static void shutdown_work(struct ubi_device *ubi)static int erase_aeb (struct ubi_device *ubi,struct ubi_ainf_peb *aeb,bool sync) {struct ubi_wl_entry *e; -int err; +int err, torture =0 ; e =kmem_cache_alloc (ubi_wl_entry_slab,GFP_KERNEL );if (!e)
@@ -1768,7 +1771,7 @@ static int erase_aeb(struct ubi_device *ubi, struct ubi_ainf_peb *aeb, bool sync ubi->lookuptbl[e->pnum] = e;if (sync) { - err =ubi_sync_erase (ubi, e, false); + err =ubi_sync_erase (ubi, e, &torture);if (err) goto out_free;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1080/1376 UBI: fastmap: Pass to_be_tortured when reusing old fastmap PEBs
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhihao Cheng <[email protected]>
[ Upstream commit be04fce9945aac2a843f5d16725391eb80d948ef ]
In ubi_update_fastmap(), when no fresh PEB is available to replace an old fastmap PEB, the old PEB is reused by erasing it synchronously via ubi_sync_erase() with the torture flag hardcoded to zero.
However, old_fm->to_be_tortured[] may be non-zero: it is set during fastmap scanning when ubi_io_read_data()/ubi_io_read_ec_hdr() reports UBI_IO_BITFLIPS, meaning the PEB showed signs of bit decay and should be tortured to verify it is still reliable.
When the old PEB is instead returned to the WL sub-system via ubi_wl_put_fm_peb(), the to_be_tortured flag is honored. The synchronous reuse path is inconsistent: it silently skips the torture test for PEBs that exhibited bit-flips.
Fix it by passing &old_fm->to_be_tortured[i] to ubi_sync_erase().
--- drivers/mtd/ubi/fastmap.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
drivers/mtd/ubi/fastmap.c +5 -3
@@ -1488,7 +1488,7 @@ static void return_fm_pebs(struct ubi_device *ubi,*/ int ubi_update_fastmap (struct ubi_device *ubi) { -int ret, i, j, torture =0 ; +int ret, i, j;struct ubi_fastmap_layout *new_fm, *old_fm;struct ubi_wl_entry *tmp_e;
@@ -1526,7 +1526,8 @@ int ubi_update_fastmap(struct ubi_device *ubi)if (!tmp_e) {if (old_fm && old_fm->e[i]) { - ret =ubi_sync_erase (ubi, old_fm->e[i], &torture); + ret =ubi_sync_erase (ubi, old_fm->e[i], + &old_fm->to_be_tortured[i]);if (ret <0 ) {ubi_err (ubi,"could not erase old fastmap PEB" );
@@ -1578,7 +1579,8 @@ int ubi_update_fastmap(struct ubi_device *ubi)if (old_fm) {/* no fresh anchor PEB was found, reuse the old one */ if (!tmp_e) { - ret =ubi_sync_erase (ubi, old_fm->e[0 ], &torture); + ret =ubi_sync_erase (ubi, old_fm->e[0 ], + &old_fm->to_be_tortured[0 ]);if (ret <0 ) {ubi_err (ubi,"could not erase old anchor PEB" );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1081/1376 ubi: Fix rollback for explicit UBI device numbers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit 5b0a6b554e12a97f9771a9a9f4ea1f5457373c73 ]
ubi_init_attach() rolls back module initialization failures by scanning ubi_devices[0..i-1], where i is the mtd= parameter index. That assumes the parameter index matches the UBI device number.
That assumption is not true when mtd= specifies an explicit ubi_num. A successfully attached device can be stored at a higher ubi_devices[] slot, and a later failure can miss it during rollback.
Scan the full ubi_devices[] array and detach by the actual array index, matching the way UBI devices are stored.
--- drivers/mtd/ubi/build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/mtd/ubi/build.c +2 -2
@@ -1317,10 +1317,10 @@ static int __init ubi_init_attach(void)return 0 ; out_detach: -for (k =0 ; k < i; k++) +for (k =0 ; k <UBI_MAX_DEVICES ; k++)if (ubi_devices[k]) {mutex_lock (&ubi_devices_mutex); -ubi_detach_mtd_dev (ubi_devices[k]->ubi_num,1 ); +ubi_detach_mtd_dev (k,1 );mutex_unlock (&ubi_devices_mutex); }return err;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1082/1376 mtd: ubi: Release device reference on busy detach
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit 31dd710cd84d5dd63c49f640d3a9f36c9699ca95 ]
ubi_detach_mtd_dev() obtains a device reference through ubi_get_device() before checking whether the UBI device is busy. The busy return path drops ubi->ref_count but leaves the device reference held, so the device object cannot be released after a later detach.
Drop the device reference before returning -EBUSY.
--- drivers/mtd/ubi/build.c | 1 + 1 file changed, 1 insertion(+)
drivers/mtd/ubi/build.c +1 -0
@@ -1105,6 +1105,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) ubi->ref_count -=1 ;if (ubi->ref_count) {if (!anyway) { +put_device (&ubi->dev);spin_unlock (&ubi_devices_lock);return -EBUSY ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1083/1376 ASoC: xilinx: formatter_pcm: fix stream_data leak on open error
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rosen Penev <[email protected]>
[ Upstream commit b992511180e126150c6ad3580a6fd568c385f4c6 ]
In xlnx_formatter_pcm_open(), stream_data is allocated and adata->play_stream or adata->capture_stream is assigned early. If a later step, such as snd_pcm_hw_constraint_step() or snd_pcm_hw_constraint_integer(), fails, the function returns the error immediately. ALSA does not call the close callback when open fails, so stream_data is leaked and the stream pointer is left dangling, pointing to a substream that ALSA frees. A later interrupt would then call snd_pcm_period_elapsed() on the freed substream.
Free stream_data and clear the stream pointer on the error paths.
--- sound/soc/xilinx/xlnx_formatter_pcm.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
sound/soc/xilinx/xlnx_formatter_pcm.c +11 -3
@@ -384,7 +384,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,if (err) {dev_err (component->dev,"Unable to set constraint on period bytes\n" ); -return err; + goto error; }/* Resize the buffer bytes as divisible by 64 */
@@ -394,7 +394,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,if (err) {dev_err (component->dev,"Unable to set constraint on buffer bytes\n" ); -return err; + goto error; }/* Set periods as integer multiple */
@@ -403,7 +403,7 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,if (err <0 ) {dev_err (component->dev,"Unable to set constraint on periods to be integer\n" ); -return err; + goto error; }/* enable DMA IOC irq */
@@ -412,6 +412,14 @@ static int xlnx_formatter_pcm_open(struct snd_soc_component *component,writel (val, stream_data->mmio +XLNX_AUD_CTRL );return 0 ; + +error: +if (substream->stream ==SNDRV_PCM_STREAM_PLAYBACK ) + adata->play_stream =NULL ; +else + adata->capture_stream =NULL ; +kfree (stream_data); +return err; } static int xlnx_formatter_pcm_close(struct snd_soc_component *component,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1084/1376 UBI: fix two issues in the ubi.mtd MODULE_PARM_DESC
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Ran Hongyun <[email protected]>
[ Upstream commit ff7937d14f5333ec7e6fb50d00a152d7974f4105 ]
Fix two issues introduced before:
- The parameter format string was missing the enable_fm and need_resv_pool tokens introduced in an earlier commit.
- The bad-block reservation note was misplaced after Example 5 instead of after Example 3. It was misplaced due to an earlier patch.
--- drivers/mtd/ubi/build.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/mtd/ubi/build.c +3 -3
@@ -1575,7 +1575,7 @@ static int ubi_mtd_param_parse(const char *val, const struct kernel_param *kp) }module_param_call (mtd, ubi_mtd_param_parse,NULL ,NULL ,0400 ); -MODULE_PARM_DESC (mtd,"MTD devices to attach. Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num]]].\n" +MODULE_PARM_DESC (mtd,"MTD devices to attach. Parameter format: mtd=<name|num|path>[,<vid_hdr_offs>[,max_beb_per1024[,ubi_num[,enable_fm[,need_resv_pool]]]]].\n" "Multiple \"mtd\" parameters may be specified.\n" "MTD devices may be specified by their number, name, or path to the MTD character device node.\n" "Optional \"vid_hdr_offs\" parameter specifies UBI VID header position to be used by UBI. (default value if 0)\n"
@@ -1588,9 +1588,9 @@ MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: mtd=<name|num|pa"Example 1: mtd=/dev/mtd0 - attach MTD device /dev/mtd0.\n" "Example 2: mtd=content,1984 mtd=4 - attach MTD device with name \"content\" using VID header offset 1984, and MTD device number 4 with default VID header offset.\n" "Example 3: mtd=/dev/mtd1,0,25 - attach MTD device /dev/mtd1 using default VID header offset and reserve 25*nand_size_in_blocks/1024 erase blocks for bad block handling.\n" +"\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device).\n" "Example 4: mtd=/dev/mtd1,0,0,5 - attach MTD device /dev/mtd1 to UBI 5 and using default values for the other fields.\n" -"example 5: mtd=1,0,0,5 mtd=2,0,0,6,1 - attach MTD device /dev/mtd1 to UBI 5 and disable fastmap; attach MTD device /dev/mtd2 to UBI 6 and enable fastmap.(only works when fastmap is enabled and fm_autoconvert=Y).\n" - "\t(e.g. if the NAND *chipset* has 4096 PEB, 100 will be reserved for this UBI device)."); + "example 5: mtd=1,0,0,5 mtd=2,0,0,6,1 - attach MTD device /dev/mtd1 to UBI 5 and disable fastmap; attach MTD device /dev/mtd2 to UBI 6 and enable fastmap.(only works when fastmap is enabled and fm_autoconvert=Y).\n");#ifdef CONFIG_MTD_UBI_FASTMAP module_param (fm_autoconvert, bool,0644 );MODULE_PARM_DESC (fm_autoconvert,"Set this parameter to enable fastmap automatically on images without a fastmap." );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1085/1376 firewire: core: add KUnit test skeleton for node tree
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit 1f9b65cf57081035547b4d3ca1805df20892e9db ]
Some issues have been reported in node tree management. Refactoring the topology-related code in the core is required. Adding unit tests would help ensure that the refactoring does not introduce regressions.
This commit adds a KUnit test skeleton for this purpose.
Stable-dep-of: 05bfb1327dc5 ("firewire: core: fix memory leak in error path of build_tree()")
--- drivers/firewire/.kunitconfig | 1 + drivers/firewire/Kconfig | 15 +++++++++++++++ drivers/firewire/core-topology.c | 4 ++++ drivers/firewire/node-tree-test.c | 19 +++++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 drivers/firewire/node-tree-test.c
drivers/firewire/.kunitconfig +1 -0
@@ -6,3 +6,4 @@ CONFIG_FIREWIRE_KUNIT_DEVICE_ATTRIBUTE_TEST=y CONFIG_FIREWIRE_KUNIT_PACKET_SERDES_TEST=y CONFIG_FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST=y CONFIG_FIREWIRE_KUNIT_OHCI_SERDES_TEST=y +CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST=y
drivers/firewire/Kconfig +15 -0
@@ -81,6 +81,21 @@ config FIREWIRE_KUNIT_SELF_ID_SEQUENCE_HELPER_TEST For more information on KUnit and unit tests in general, refer to the KUnit documentation in Documentation/dev-tools/kunit/. +config FIREWIRE_KUNIT_NODE_TREE_TEST + tristate "KUnit tests for node tree" if !KUNIT_ALL_TESTS + depends on FIREWIRE && KUNIT + default KUNIT_ALL_TESTS + help + This builds the KUnit tests for node tree. + + KUnit tests run during boot and output the results to the debug + log in TAP format (https://testanything.org/). Only useful for + kernel devs running KUnit test harness and are not for inclusion + into a production build. + + For more information on KUnit and unit tests in general, refer + to the KUnit documentation in Documentation/dev-tools/kunit/. + config FIREWIRE_OHCI tristate "OHCI-1394 controllers" depends on PCI && FIREWIRE && MMU
drivers/firewire/core-topology.c +4 -0
@@ -501,3 +501,7 @@ void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation, } }EXPORT_SYMBOL (fw_core_handle_bus_reset); + +#ifdef CONFIG_FIREWIRE_KUNIT_NODE_TREE_TEST +#include "node-tree-test.c" +#endif
drivers/firewire/node-tree-test.c new file +19 -0
@@ -0,0 +1,19 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// node-tree-test.c - An application of Kunit to test node tree. +// +// Copyright (c) 2026 Takashi Sakamoto +// +// This file can not be built independently since it is intentionally included in core-topology.c. + +#include <kunit/test.h> + +static struct kunit_case node_tree_test_cases[] = { + {} +}; + +static struct kunit_suite node_tree_test_suite = { + .name ="firewire-node-tree" , + .test_cases = node_tree_test_cases, +}; +kunit_test_suite (node_tree_test_suite);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1086/1376 firewire: core: add KUnit tests for successful tree building
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit 7b763ea17152cf61ede5289c3c66bf133c8aafa7 ]
After a bus reset, self ID sequence is captured by 1394 OHCI hardware and passed to software through SelfID DMA context. The core parses the sequence to build an internal cache of the node tree for the current generation of the bus. This is the first step in managing resources on the bus.
The tree is build by the build_tree() function. This commit adds KUnit tests for the function, covering several successful scenarios.
Stable-dep-of: 05bfb1327dc5 ("firewire: core: fix memory leak in error path of build_tree()")
--- drivers/firewire/node-tree-test.c | 508 ++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+)
drivers/firewire/node-tree-test.c +508 -0
@@ -7,13 +7,521 @@// This file can not be built independently since it is intentionally included in core-topology.c. #include <kunit/test.h> +#include <kunit/test-bug.h> +#include <kunit/device.h> + +struct private_data { +struct fw_card *card; +unsigned int release_count; +}; + +static int node_tree_test_init (struct kunit *test) +{ +struct private_data *data; + + data =kunit_kzalloc (test,sizeof (*data),GFP_KERNEL ); +KUNIT_ASSERT_NOT_NULL (test, data); + + data->card =kunit_kzalloc (test,sizeof (struct fw_card ),GFP_KERNEL ); +KUNIT_ASSERT_NOT_NULL (test, data->card); + + data->card->device =kunit_device_register (test,"dummy-device" ); +KUNIT_ASSERT_NOT_ERR_OR_NULL (test, data->card->device); + + test->priv = data; + +return 0 ; +} + +static void node_tree_test_exit (struct kunit *test) +{ +struct private_data *data = test->priv; + +kunit_device_unregister (test, data->card->device); +kunit_kfree (test, data->card); +kunit_kfree (test, data); +} + +static void release_fw_node (struct fw_card *card,struct fw_node *node,struct fw_node *parent) +{ +struct private_data *data =kunit_get_current_test ()->priv; + +fw_node_put (node); + ++data->release_count; +} + +static void node_tree_test_two_nodes (struct kunit *test) +{ +// root +// ++============++ +// || phy 1 || +// || P0 P1 P2 || +// ++===|==|==|==++ +// | +// +-----+ +// | +// ++===|==x==x==++ +// || P0 P1 P2 || +// || phy 0 || +// ++============++ +// +static const u32 self_id_sequence[] = { +0x80000080 , +0x8100005e , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x01 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + +struct fw_node *parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,1 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,2 ); +} + +static void node_tree_test_two_nodes_1394a (struct kunit *test) +{ +// root +// ++===============++ +// || phy 0 || +// || P0 P1 P2 P3 || +// ++===|==|==|==|==++ +// | +// +--+ +// | +// ++===|==|==|==|==|==++ +// || P0 P1 P2 P3 P4 || +// || phy 1 || +// ++==================++ +// +// NOTE: Just for Self-ID Packets Zero and One. +static const u32 self_id_sequence[] = { +0x80000065 ,0x80814000 , +0x8100005d ,0x81810000 , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x01 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,4 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); +KUNIT_EXPECT_NULL (test, node->ports[3 ]); + +struct fw_node *parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,5 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[1 ], parent); +KUNIT_EXPECT_NULL (test, node->ports[2 ]); +KUNIT_EXPECT_NULL (test, node->ports[3 ]); +KUNIT_EXPECT_NULL (test, node->ports[4 ]); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,2 ); +} + +static void node_tree_test_three_nodes_case0 (struct kunit *test) +{ +// root +// ++============++ +// || phy 2 || +// || P0 P1 P2 || +// ++===|==|==|==++ +// | | +// +--+ +-----------------+ +// | | +// ++===|==|==x==++ ++===|==|==|==++ +// || P0 P1 P2 || || P0 P1 P2 || +// || phy 0 || || phy 1 || +// ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000060 , +0x81000058 , +0x820000dc , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x02 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NOT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + +struct fw_node *parent = node; + node = parent->ports[0 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[1 ], parent); + + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[2 ], parent); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,3 ); +} + +static void node_tree_test_three_nodes_case1 (struct kunit *test) +{ +// root +// ++============++ +// || phy 2 || +// || P0 P1 P2 || +// ++===|==|==x==++ +// | +// | +-----------+ +// | | | +// ++===|==|==|==++ ++===|==x==x==++ +// || P0 P1 P2 || || P0 P1 P2 || +// || phy 1 || || phy 0 || +// ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000080 , +0x8100006c , +0x82000070 , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x02 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); + +struct fw_node *parent = node; + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[1 ], parent); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + + parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,1 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,3 ); +} + +static void node_tree_test_four_nodes_case0 (struct kunit *test) +{ +// root +// ++============++ +// || phy 3 || +// || P0 P1 P2 || +// ++===|==|==|==++ +// | +// | +-----------+ +--------------+ +// | | | | | +// ++===|==|==|==++ ++===|==|==x==++ ++===|==x==x==++ +// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 || +// || phy 2 || || phy 1 || || phy 0 || +// ++============++ ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000080 , +0x810000b0 , +0x8200006c , +0x83000074 , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x03 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NULL (test, node->ports[2 ]); + +struct fw_node *parent = node; + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[1 ], parent); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + + parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); + + parent = node; + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,1 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,4 ); +} + +static void node_tree_test_four_nodes_case1 (struct kunit *test) +{ +// root +// ++============++ +// || phy 3 || +// || P0 P1 P2 || +// ++===|==|==x==++ +// | +// | +--------------------------------+ +// | | +-----------+ | +// ++===|==|==|==++ ++===|==x==x==++ ++===|==|==|==++ +// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 || +// || phy 2 || || phy 1 || || phy 0 || +// ++============++ ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000094 , +0x81000080 , +0x820000bc , +0x830000d0 , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x03 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_NOT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); + +struct fw_node *parent = node; + node = parent->ports[0 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + + parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,1 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); + + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NULL (test, node->ports[2 ]); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,4 ); +} + +static void node_tree_test_four_nodes_case2 (struct kunit *test) +{ +// root +// ++============++ +// || phy 3 || +// || P0 P1 P2 || +// ++===|==|==|==++ +// | | +// | +-----------------------------+ +// | +--------------+ | +// ++===|==|==x==++ ++===|==|==|==++ ++===|==x==x==++ +// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 || +// || phy 1 || || phy 0 || || phy 2 || +// ++============++ ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000094 , +0x810000b0 , +0x82000080 , +0x830000dc , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x03 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NOT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + +struct fw_node *parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,1 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); + + node = parent->ports[0 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); + + parent = node; + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NULL (test, node->ports[2 ]); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,4 ); +} + +static void node_tree_test_four_nodes_case3 (struct kunit *test) +{ +// root +// ++============++ +// || phy 3 || +// || P0 P1 P2 || +// ++===|==|==|==++ +// | | +--------------------------------+ +// | +--------------------+ | +// | | | +// ++===|==|==x==++ ++===|==|==|==++ ++===|==|==x==++ +// || P0 P1 P2 || || P0 P1 P2 || || P0 P1 P2 || +// || phy 0 || || phy 1 || || phy 2 || +// ++============++ ++============++ ++============++ +// +static const u32 self_id_sequence[] = { +0x80000090 , +0x81000058 , +0x82000060 , +0x830000fc , + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NOT_NULL (test, card->local_node); +KUNIT_EXPECT_PTR_EQ (test, card->local_node, card->root_node); + +struct fw_node *node = card->root_node; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x03 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NOT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_NOT_NULL (test, node->ports[2 ]); + +struct fw_node *parent = node; + node = parent->ports[2 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x02 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[1 ], parent); + + node = parent->ports[1 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x01 ); +KUNIT_EXPECT_EQ (test, node->port_count,3 ); +KUNIT_EXPECT_NULL (test, node->ports[0 ]); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); +KUNIT_EXPECT_PTR_EQ (test, node->ports[2 ], parent); + + node = parent->ports[0 ]; +KUNIT_EXPECT_EQ (test, node->node_id,LOCAL_BUS |0x00 ); +KUNIT_EXPECT_EQ (test, node->port_count,2 ); +KUNIT_EXPECT_PTR_EQ (test, node->ports[0 ], parent); +KUNIT_EXPECT_NULL (test, node->ports[1 ]); + + ++card->color; +for_each_fw_node (card, card->root_node, release_fw_node); +KUNIT_EXPECT_EQ (test, data->release_count,4 ); +}static struct kunit_case node_tree_test_cases[] = { +KUNIT_CASE (node_tree_test_two_nodes), +KUNIT_CASE (node_tree_test_two_nodes_1394a), +KUNIT_CASE (node_tree_test_three_nodes_case0), +KUNIT_CASE (node_tree_test_three_nodes_case1), +KUNIT_CASE (node_tree_test_four_nodes_case0), +KUNIT_CASE (node_tree_test_four_nodes_case1), +KUNIT_CASE (node_tree_test_four_nodes_case2), +KUNIT_CASE (node_tree_test_four_nodes_case3), {} };static struct kunit_suite node_tree_test_suite = { .name ="firewire-node-tree" , + .init = node_tree_test_init, + .exit = node_tree_test_exit, .test_cases = node_tree_test_cases, };kunit_test_suite (node_tree_test_suite);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1087/1376 firewire: core: add KUnit tests for failure of tree building
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit f744022705b7eb479a1931ddd95aae4e9a4be221 ]
Abdun Nihaal has reported a memory leak when tree building fails in the middle of self ID sequence enumeration. This is caused by an invalid self ID sequence and is not a common occurrence.
This commit is intended to assist in fixing the issue by adding KUnit tests to show the cases in which the memory leak is triggered. The leak occurs internally in the build_tree() function, therefore it cannot be detected directly by the tests.
Stable-dep-of: 05bfb1327dc5 ("firewire: core: fix memory leak in error path of build_tree()")
--- drivers/firewire/node-tree-test.c | 84 +++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+)
drivers/firewire/node-tree-test.c +84 -0
@@ -506,6 +506,86 @@ static void node_tree_test_four_nodes_case3(struct kunit *test)KUNIT_EXPECT_EQ (test, data->release_count,4 ); } +static void node_tree_test_invalid_extended_self_id_sequence (struct kunit *test) +{ +// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid +// content of self ID packet for the phy 3. +static const u32 self_id_sequence[] = { +0x80000094 , +0x81000080 , +0x820000bc , +0x830000d1 ,// Invalid. + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + +// TODO: Memory leak. + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NULL (test, card->local_node); +} + +static void node_tree_test_invalid_phy_id (struct kunit *test) +{ +// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid +// phy ID for phy 3. +static const u32 self_id_sequence[] = { +0x80000094 , +0x81000080 , +0x820000bc , +0x8f0000d0 ,// Invalid. + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + +// TODO: Memory leak. + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NULL (test, card->local_node); +} + +static void node_tree_test_invalid_child_port_count (struct kunit *test) +{ +// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid +// count of child ports for phy 3. +static const u32 self_id_sequence[] = { +0x80000094 , +0x81000080 , +0x820000bc , +0x830000fc ,// Invalid. + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + +// TODO: Memory leak. + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NULL (test, card->local_node); +} + +static void node_tree_test_invalid_parent_port_count (struct kunit *test) +{ +// Use the same node tree as node_tree_test_four_nodes_case1, except for the invalid +// count of parent ports for phy 3. +static const u32 self_id_sequence[] = { +0x80000094 , +0x81000080 , +0x820000bc , +0x830000e8 ,// Invalid. + }; +struct private_data *data = test->priv; +struct fw_card *card = data->card; + + card->node_id =LOCAL_BUS |0x03 ; + +// TODO: Memory leak. + card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 ); +KUNIT_EXPECT_NULL (test, card->local_node); +} +static struct kunit_case node_tree_test_cases[] = {KUNIT_CASE (node_tree_test_two_nodes),KUNIT_CASE (node_tree_test_two_nodes_1394a),
@@ -515,6 +595,10 @@ static struct kunit_case node_tree_test_cases[] = {KUNIT_CASE (node_tree_test_four_nodes_case1),KUNIT_CASE (node_tree_test_four_nodes_case2),KUNIT_CASE (node_tree_test_four_nodes_case3), +KUNIT_CASE (node_tree_test_invalid_extended_self_id_sequence), +KUNIT_CASE (node_tree_test_invalid_phy_id), +KUNIT_CASE (node_tree_test_invalid_child_port_count), + KUNIT_CASE(node_tree_test_invalid_parent_port_count), {} };
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1088/1376 firewire: core: consolidate port counting in build_tree()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit 87c48e54f28d410af0c12b5dd339307bb43abeea ]
The self ID sequence describes the state of each port for each PHY. Currently, build_tree() counts the ports in two separate places.
Consolidate the port counting in one place.
Stable-dep-of: 05bfb1327dc5 ("firewire: core: fix memory leak in error path of build_tree()")
--- drivers/firewire/core-topology.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-)
drivers/firewire/core-topology.c +10 -8
@@ -119,8 +119,8 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int selfwhile (enumerator.quadlet_count >0 ) {unsigned int child_port_count =0 ; +unsigned int parent_port_count =0 ;unsigned int total_port_count =0 ; -unsigned int parent_count =0 ;unsigned int quadlet_count;const u32 *self_id_sequence;unsigned int port_capacity;
@@ -148,16 +148,19 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int selfswitch (port_status) {case PHY_PACKET_SELF_ID_PORT_STATUS_CHILD : ++child_port_count; - fallthrough; +break ;case PHY_PACKET_SELF_ID_PORT_STATUS_PARENT : + ++parent_port_count; +break ;case PHY_PACKET_SELF_ID_PORT_STATUS_NCONN : ++total_port_count; - fallthrough; +break ;case PHY_PACKET_SELF_ID_PORT_STATUS_NONE :default :break ; } } + total_port_count += child_port_count + parent_port_count;if (phy_id !=phy_packet_self_id_get_phy_id (self_id_sequence[0 ])) { fw_err(card, "PHY ID mismatch in self ID: %d != %d\n",
@@ -203,7 +206,6 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self// we temporarily abuse node->color for remembering the entry in // the node->ports array where the parent node should be. Later, // when we handle the parent node, we fix up the reference. - ++parent_count; node->color = port_index;break ;
@@ -223,10 +225,10 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self// Check that the node reports exactly one parent port, except for the root, which // of course should have no parents. -if ((enumerator.quadlet_count ==0 && parent_count !=0 ) || - (enumerator.quadlet_count >0 && parent_count !=1 )) { +if ((enumerator.quadlet_count ==0 && parent_port_count !=0 ) || + (enumerator.quadlet_count >0 && parent_port_count !=1 )) {fw_err (card,"parent port inconsistency for node %d: " -"parent_count=%d\n" , phy_id, parent_count); +"parent_count=%d\n" , phy_id, parent_port_count);return NULL ; }
@@ -235,7 +237,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int selflist_add_tail (&node->link, &stack); stack_depth +=1 - child_port_count; -if (node->phy_speed ==SCODE_BETA && parent_count + child_port_count >1 ) +if (node->phy_speed ==SCODE_BETA && parent_port_count + child_port_count >1 ) beta_repeaters_present = true;// If PHYs report different gap counts, set an invalid count which will force a gap
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1089/1376 firewire: core: validate parent port count before allocating nodes in build_tree()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit a563a7cb645a79a3a7e31bed8cbf95980ef5feb1 ]
The node tree requires each child node to have exactly one port connected to a parent node, while the root node must have no such port. This can be validated by comparing the parent port count for a PHY with the rest of the self ID sequence.
Currently, this validation is done after the node has been allocated. Move it before the allocation so that an invalid self ID sequence can cause an error without having to clean up the newly allocated node.
Stable-dep-of: 05bfb1327dc5 ("firewire: core: fix memory leak in error path of build_tree()")
--- drivers/firewire/core-topology.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
drivers/firewire/core-topology.c +9 -9
@@ -162,6 +162,15 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self } total_port_count += child_port_count + parent_port_count; +// Check that the node reports exactly one parent port, except for the root, which +// of course should have no parents. +if ((enumerator.quadlet_count ==0 && parent_port_count !=0 ) || + (enumerator.quadlet_count >0 && parent_port_count !=1 )) { +fw_err (card,"parent port inconsistency for node %d: parent_count=%d\n" , + phy_id, parent_port_count); +return NULL ; + } +if (phy_id !=phy_packet_self_id_get_phy_id (self_id_sequence[0 ])) {fw_err (card,"PHY ID mismatch in self ID: %d != %d\n" , phy_id,phy_packet_self_id_get_phy_id (self_id_sequence[0 ]));
@@ -223,15 +232,6 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self } } -// Check that the node reports exactly one parent port, except for the root, which -// of course should have no parents. -if ((enumerator.quadlet_count ==0 && parent_port_count !=0 ) || - (enumerator.quadlet_count >0 && parent_port_count !=1 )) { -fw_err (card,"parent port inconsistency for node %d: " -"parent_count=%d\n" , phy_id, parent_port_count); -return NULL ; - } -/* Pop the child nodes off the stack and push the new node. */ __list_del (h->prev, &stack);list_add_tail (&node->link, &stack);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1090/1376 firewire: core: fix memory leak in error path of build_tree()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Sakamoto <[email protected]>
[ Upstream commit 05bfb1327dc5fb61528bab31cd8f0c1e4bddec23 ]
In the error path of build_tree(), node instances can remain in the local linked list when the function returns.
Whenever an invalid value is detected in the self ID sequence, each allocated node instance is either an entry in the linked list or an entry in the ports array of its parent node. Therefore, the allocate node instances can be safely released by traversing the linked list from its head.
Release the remaining node instances with for_each_fw_node() before returning to the caller.
--- drivers/firewire/core-topology.c | 30 +++++++++++++++++++++--------- drivers/firewire/node-tree-test.c | 4 ---- 2 files changed, 21 insertions(+), 13 deletions(-)
drivers/firewire/core-topology.c +21 -9
@@ -88,6 +88,17 @@ static inline struct fw_node *fw_node(struct list_head *l) return list_entry(l, struct fw_node, link); } +typedef void (*fw_node_callback_t )(struct fw_card *card,struct fw_node *node, +struct fw_node *parent); + +static void for_each_fw_node (struct fw_card *card,struct fw_node *root, +fw_node_callback_t callback); + +static void free_fw_node (struct fw_card *card,struct fw_node *node,struct fw_node *parent) +{ +kfree (node); +} +/* * This function builds the tree representation of the topology given * by the self IDs from the latest bus reset. During the construction
@@ -134,7 +145,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int selfif (PTR_ERR (self_id_sequence) != -ENODATA ) {fw_err (card,"inconsistent extended self IDs: %ld\n" ,PTR_ERR (self_id_sequence)); -return NULL ; + goto error; }break ; }
@@ -168,18 +179,18 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self (enumerator.quadlet_count > 0 && parent_port_count != 1)) {fw_err (card,"parent port inconsistency for node %d: parent_count=%d\n" , phy_id, parent_port_count); -return NULL ; + goto error; }if (phy_id !=phy_packet_self_id_get_phy_id (self_id_sequence[0 ])) {fw_err (card,"PHY ID mismatch in self ID: %d != %d\n" , phy_id,phy_packet_self_id_get_phy_id (self_id_sequence[0 ])); -return NULL ; + goto error; }if (child_port_count > stack_depth) {fw_err (card,"topology stack underflow\n" ); -return NULL ; + goto error; }/*
@@ -197,7 +208,7 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self node =fw_node_create (self_id_sequence[0 ], total_port_count, card->color);if (node ==NULL ) {fw_err (card,"out of memory while building topology\n" ); -return NULL ; + goto error; }if (phy_id == (card->node_id &0x3f ))
@@ -256,12 +267,13 @@ static struct fw_node *build_tree(struct fw_card *card, const u32 *sid, int self card->beta_repeaters_present = beta_repeaters_present;return local_node; +error: + ++card->color; +list_for_each_entry_safe (node, child, &stack, link) +for_each_fw_node (card, node, free_fw_node); +return NULL ; } -typedef void (*fw_node_callback_t )(struct fw_card * card, -struct fw_node * node, -struct fw_node * parent); -static void for_each_fw_node (struct fw_card *card,struct fw_node *root,fw_node_callback_t callback) {
drivers/firewire/node-tree-test.c +0 -4
@@ -521,7 +521,6 @@ static void node_tree_test_invalid_extended_self_id_sequence(struct kunit *test) card->node_id =LOCAL_BUS |0x03 ; -// TODO: Memory leak. card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 );KUNIT_EXPECT_NULL (test, card->local_node); }
@@ -541,7 +540,6 @@ static void node_tree_test_invalid_phy_id(struct kunit *test) card->node_id =LOCAL_BUS |0x03 ; -// TODO: Memory leak. card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 );KUNIT_EXPECT_NULL (test, card->local_node); }
@@ -561,7 +559,6 @@ static void node_tree_test_invalid_child_port_count(struct kunit *test) card->node_id =LOCAL_BUS |0x03 ; -// TODO: Memory leak. card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 );KUNIT_EXPECT_NULL (test, card->local_node); }
@@ -581,7 +578,6 @@ static void node_tree_test_invalid_parent_port_count(struct kunit *test) card->node_id =LOCAL_BUS |0x03 ; -// TODO: Memory leak. card->local_node =build_tree (card, self_id_sequence,ARRAY_SIZE (self_id_sequence),123 );KUNIT_EXPECT_NULL (test, card->local_node); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1091/1376 PCI/ASPM: Use pcie_capability_clear_and_set_word() for ASPM disable/restore
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krishna Chaitanya Chundru <[email protected]>
[ Upstream commit 75a3b50ad9dc99ce9693a0086b968c6d3501db21 ]
pcie_aspm_cap_init() disables ASPM L0s/L1 on both ends of the Link before touching L1SS config, then later restores the LNKCTL state that was in effect beforehand. Both steps use raw pcie_capability_write_word() calls: the disable step computes the new value by hand from a snapshot taken earlier in the function, and the restore step writes that same snapshot straight back.
Switch both steps to pcie_capability_clear_and_set_word(), masked to PCI_EXP_LNKCTL_ASPMC, matching the accessor pcie_config_aspm_dev() already uses elsewhere in this file for the exact same register. This does a live read-modify-write of just the ASPM Control bits instead of relying on a stale snapshot for the rest of the word, and is consistent with how the rest of the file already touches this register. No functional change.
--- drivers/pci/pcie/aspm.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-)
drivers/pci/pcie/aspm.c +10 -6
@@ -835,10 +835,10 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)/* Disable L0s/L1 before updating L1SS config */ if (FIELD_GET (PCI_EXP_LNKCTL_ASPMC , child_lnkctl) ||FIELD_GET (PCI_EXP_LNKCTL_ASPMC , parent_lnkctl)) { -pcie_capability_write_word (child,PCI_EXP_LNKCTL , - child_lnkctl & ~PCI_EXP_LNKCTL_ASPMC ); -pcie_capability_write_word (parent,PCI_EXP_LNKCTL , - parent_lnkctl & ~PCI_EXP_LNKCTL_ASPMC ); +pcie_capability_clear_and_set_word (child,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_ASPMC ,0 ); +pcie_capability_clear_and_set_word (parent,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_ASPMC ,0 ); }/*
@@ -868,8 +868,12 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)/* Restore L0s/L1 if they were enabled */ if (FIELD_GET (PCI_EXP_LNKCTL_ASPMC , child_lnkctl) ||FIELD_GET (PCI_EXP_LNKCTL_ASPMC , parent_lnkctl)) { -pcie_capability_write_word (parent,PCI_EXP_LNKCTL , parent_lnkctl); -pcie_capability_write_word (child,PCI_EXP_LNKCTL , child_lnkctl); +pcie_capability_clear_and_set_word (parent,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_ASPMC , + parent_lnkctl &PCI_EXP_LNKCTL_ASPMC ); +pcie_capability_clear_and_set_word (child,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_ASPMC , + child_lnkctl &PCI_EXP_LNKCTL_ASPMC ); }/* Save default state */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1092/1376 super: fix dying superblock warning messages
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 9ac8fd831252e52aa78399eaccc11a72f6c2af1a ]
WARN_ON_ONCE() takes a condition, not a message. The string literals are always true, so the warnings still trigger but the messages are never printed.
Use WARN_ONCE(1, ...) instead to print the messages and keep the once-only behavior.
Found with a Coccinelle script. Clang's -Wstring-conversion also flags such calls but is not enabled in kernel builds.
--- fs/super.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/super.c +2 -2
@@ -2065,7 +2065,7 @@ int freeze_super(struct super_block *sb, enum freeze_holder who)int ret;if (!super_lock_excl (sb)) { -WARN_ON_ONCE ("Dying superblock while freezing!" ); +WARN_ONCE (1 ,"Dying superblock while freezing!" );return -EINVAL ; }atomic_inc (&sb->s_active);
@@ -2219,7 +2219,7 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who)int thaw_super (struct super_block *sb,enum freeze_holder who) {if (!super_lock_excl (sb)) { -WARN_ON_ONCE ("Dying superblock while thawing!" ); +WARN_ONCE (1 ,"Dying superblock while thawing!" );return -EINVAL ; }return thaw_super_locked (sb, who);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1093/1376 kunit: tool: fix _list_tests filtering wrong variable when list has TAP prefix
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Mohammad Abu-Khader <[email protected]>
[ Upstream commit dea754ded9518b51740c417d2c1e02ff540784c6 ]
_list_tests() runs the kernel to list tests, strips printk timestamp lines via extract_tap_lines(), then drops the dummy TAP header from the cleaned lines. However the subsequent regex filter mistakenly operates on the original output instead of the cleaned lines. When the kernel output includes timestamp prefixes (common with UML or slower setups), e.g.:
[ 0.100000] suite.test1 [ 0.100000] suite.test2
the anchored regex ^[^\s.]+\.[^\s.]+$ rejects them and --list_tests returns an empty list.
Filter lines instead of output, matching the behavior of the adjacent _list_tests_attr() which already returns the cleaned list.
Add a regression test with timestamp-prefixed input to verify the fix.
--- tools/testing/kunit/kunit.py | 2 +- tools/testing/kunit/kunit_tool_test.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-)
tools/testing/kunit/kunit.py +1 -1
@@ -121,7 +121,7 @@ def _list_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) lines.pop() # Filter out any extraneous non-test output that might have gotten mixed in. - return [l for l in output if re.match(r'^[^\s.]+\.[^\s.]+$', l)] + return [l for l in lines if re.match(r'^[^\s.]+\.[^\s.]+$', l)] def _list_tests_attr(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -> Iterable[str]: args = ['kunit.action=list_attr']
tools/testing/kunit/kunit_tool_test.py +12 -0
@@ -884,6 +884,18 @@ class KUnitMainTest(unittest.TestCase): self.linux_source_mock.run_kernel.assert_called_once_with( args=['kunit.action=list'], build_dir='.kunit', filter_glob='suite*', filter='', filter_action=None, timeout=300) + def test_list_tests_with_prefix(self): + want = ['suite.test1', 'suite.test2', 'suite2.test1'] + self.linux_source_mock.run_kernel.return_value = [ + '[ 0.100000] TAP version 14', + '[ 0.200000] suite.test1', + '[ 0.200000] suite.test2', + '[ 0.300000] suite2.test1'] + + got = kunit._list_tests(self.linux_source_mock, + kunit.KunitExecRequest(None, None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False, False)) + self.assertEqual(got, want) + @mock.patch.object(kunit, '_list_tests') def test_run_isolated_by_suite(self, mock_tests): mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1']
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1094/1376 bpf, s390: Clear fetch destination on faulting arena atomic
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Borkmann <[email protected]>
[ Upstream commit cc3e12330599f097f0e1f792435686ddc276f26d ]
Same missing register clear as on riscv64. A RMW atomic on an arena pointer is converted to BPF_PROBE_ATOMIC and gets an exception table entry, but bpf_jit_probe_atomic_pre() only fills in the arena base and the probe offset, leaving probe->reg at the -1 that bpf_jit_probe_init() set, which bpf_jit_probe_post() writes into the entry and ex_handler_bpf() then reads back as "there is nothing to clear".
That is right for a plain BPF_{ADD,AND,OR,XOR}, which only writes memory, but an RMW carrying BPF_FETCH also reads the old value into a register:src_reg for BPF_{ADD,AND,OR,XOR} | BPF_FETCH and BPF_XCHG, and r0 for BPF_CMPXCHG. So on a fault over an unmapped arena page the program resumes at the landing pad with whatever that register held before the atomic instead of the 0 that every other BPF_PROBE_* access delivers.
Fill probe->reg in from bpf_atomic_load_reg(). Unlike x86-64 and arm64, s390x does not report arena violations from its exception handler, so there is no access direction to correct here, only the missing register clear.
--- arch/s390/net/bpf_jit_comp.c | 11 +++++++++++ 1 file changed, 11 insertions(+)
arch/s390/net/bpf_jit_comp.c +11 -0
@@ -782,6 +782,8 @@ static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit,struct bpf_insn *insn, struct bpf_jit_probe *probe) { + int load_reg; +if (BPF_MODE (insn->code) !=BPF_PROBE_ATOMIC )return ;
@@ -791,6 +793,14 @@ static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit,EMIT4 (0xb9080000 ,REG_W1 , insn->dst_reg); probe->arena_reg =REG_W1 ; probe->prg = jit->prg; +/* +* A read-modify-write carrying BPF_FETCH reads the old value into +* src_reg, or into r0 for a BPF_CMPXCHG. Clear that register on +* fault, the remaining atomics only write memory. +*/ + load_reg =bpf_atomic_load_reg (insn); +if (load_reg >=0 ) + probe->reg = reg2hex[load_reg]; } static int bpf_jit_probe_post(struct bpf_jit *jit, struct bpf_prog *fp,
@@ -1653,6 +1663,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,if (load_probe.prg !=-1 ) { probe.prg = jit->prg; probe.arena_reg = load_probe.arena_reg; + probe.reg = load_probe.reg; } loop_start = jit->prg;/* 0: {csy|csg} %w0,%src,off(%arena) */
Signature · 1 line
2.53.0
Re: [PATCH 6.12 1094/1376] bpf, s390: Clear fetch destination on faulting arena atomic
Quoted 51 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Daniel Borkmann <[email protected]>
[ Upstream commit
cc3e12330599f097f0e1f792435686ddc276f26d]Same missing register clear as on riscv64. A RMW atomic on an arena pointer is converted to
BPF_PROBE_ATOMICand gets an exception table entry, butbpf_jit_probe_atomic_pre()only fills in the arena base and the probe offset, leavingprobe->regat the -1 thatbpf_jit_probe_init()set, whichbpf_jit_probe_post()writes into the entry andex_handler_bpf()then reads back as "there is nothing to clear".That is right for a plain BPF_{ADD,AND,OR,XOR}, which only writes memory, but an RMW carrying
BPF_FETCHalso reads the old value into a register:src_regfor BPF_{ADD,AND,OR,XOR} |BPF_FETCHandBPF_XCHG, and r0 forBPF_CMPXCHG. So on a fault over an unmapped arena page the program resumes at the landing pad with whatever that register held before the atomic instead of the 0 that every other BPF_PROBE_* access delivers.Fill
probe->regin frombpf_atomic_load_reg(). Unlike x86-64 and arm64, s390x does not report arena violations from its exception handler, so there is no access direction to correct here, only the missing register clear.Fixes 2f9469484a3b ("s390/bpf: Support arena atomics")Signed-off-by Daniel Borkmann <[email protected]>Reviewed-by Ilya Leoshkevich <[email protected]>Signed-off-by Eduard Zingerman <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- arch/s390/net/bpf_jit_comp.c | 11 +++++++++++ 1 file changed, 11 insertions(+)arch/s390/net/bpf_jit_comp.c +2
-0@@ -782,6 +782,8 @@ static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit,struct bpf_insn *insn, struct bpf_jit_probe *probe) { + int load_reg; +if (BPF_MODE (insn->code) !=BPF_PROBE_ATOMIC )return ;@@ -791,6 +793,14 @@ static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit,EMIT4 (0xb9080000 ,REG_W1 , insn->dst_reg);
arch/s390/net/bpf_jit_comp.c static void bpf_jit_probe_atomic_pre(struct bpf_jit *jit, · line 803
Quoted 10 lines
arch/s390/net/bpf_jit_comp.c +8
-0probe->arena_reg =REG_W1 ; probe->prg = jit->prg; +/* +* A read-modify-write carrying BPF_FETCH reads the old value into +* src_reg, or into r0 for a BPF_CMPXCHG. Clear that register on +* fault, the remaining atomics only write memory. +*/ + load_reg =bpf_atomic_load_reg (insn); +if (load_reg >=0 ) + probe->reg = reg2hex[load_reg];
Hi Greg/Sasha,
I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.
The new s390 call to bpf_atomic_load_reg() has no declaration or definition in the destination, and the normal s390 build treats that implicit call as fatal. Upstream obtains the helper from 41c5dbb4be3c, but that version also assumes BPF_LOAD_ACQ and BPF_STORE_REL support which 6.12 lacks.
Upstream helper code:
switch (insn->imm) {
case BPF_CMPXCHG:
return BPF_REG_0;
default:
return (insn->imm & BPF_FETCH) ?
insn->src_reg : -1;
}Downstream 6.12.y code:
load_reg = bpf_atomic_load_reg(insn);
The downstream call has no declaration or definition.
Maybe we should be adding a minimal 6.12 helper covering only its existing atomic encodings, using the verifier's current result-register mapping, before retaining this patch. Thoughts?
thanks,
Harshit
Quoted 11 lines
arch/s390/net/bpf_jit_comp.c +1
-0} static int bpf_jit_probe_post(struct bpf_jit *jit, struct bpf_prog *fp,@@ -1653,6 +1663,7 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,if (load_probe.prg !=-1 ) { probe.prg = jit->prg; probe.arena_reg = load_probe.arena_reg; + probe.reg = load_probe.reg; } loop_start = jit->prg;/* 0: {csy|csg} %w0,%src,off(%arena) */
Sasha Levin
maintainerRe: [PATCH 6.12 1094/1376] bpf, s390: Clear fetch destination on faulting arena atomic
Quoted 3 lines
The new s390 call to
bpf_atomic_load_reg()has no declaration or definition in the destination, and the normal s390 build treats that implicit call as fatal.
Already handled: the revert of cc3e12330599 ("bpf, s390: Clear fetch destination on faulting arena atomic") is queued for 6.12 and goes out in 6.12.111. 6.12.110 did ship with the break.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 6.12 1094/1376] bpf, s390: Clear fetch destination on faulting arena atomic
Quoted 3 lines · Sasha Levin
Already handled: the revert of
cc3e12330599("bpf, s390: Clear fetch destination on faulting arena atomic") is queued for 6.12 and goes out in 6.12.111. 6.12.110 did ship with the break.
thanks for that.
regards,
Harshit
Greg Kroah-Hartman
maintainerPATCH 1095/1376 selftests: harness: Restore order of test functions
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Thomas Weißschuh <[email protected]>
[ Upstream commit 465b05bae5ac553c13315681c1490dc565337771 ]
The recent addition of explicit constructor orders for fixture tests broke the ordering of those relative to non-fixture tests and the reverse-constructor-order detection.
Restore the ordering of the test functions relative to each other by using the same explicit test order for all test registrations and __constructor_order_first().
Rename the constant, as it is not specific to TEST_F() anymore.
Stable-dep-of: 37ffa24c9d07 ("selftests: harness: Mark test fixture objects __maybe_unused")
--- tools/testing/selftests/kselftest_harness.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
tools/testing/selftests/kselftest_harness.h +6 -6
@@ -75,7 +75,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n)memset (s, c, n); } -#define KSELFTEST_PRIO_TEST_F 20000 +#define KSELFTEST_PRIO_TEST 20000#define KSELFTEST_PRIO_XFAIL 20001#define TEST_TIMEOUT_DEFAULT 30
@@ -196,7 +196,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) .fixture = &_fixture_global, \ .termsig = _signal, \ .timeout = TEST_TIMEOUT_DEFAULT, }; \ - static void __attribute__((constructor)) _register_##test_name(void) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) _register_##test_name(void) \ { \ __register_test(&_##test_name##_object); \ } \
@@ -240,7 +240,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n)FIXTURE_VARIANT (fixture_name); \ static struct __fixture_metadata _##fixture_name##_fixture_object = \ { .name = #fixture_name, }; \ - static void __attribute__((constructor)) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_data(void) \ { \ __register_fixture(&_##fixture_name##_fixture_object); \
@@ -366,7 +366,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) _##fixture_name##_##variant_name##_object = \ { .name = #variant_name, \ .data = &_##fixture_name##_##variant_name##_variant}; \ - static void __attribute__((constructor)) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_##variant_name(void) \ { \ __register_fixture_variant(&_##fixture_name##_fixture_object, \
@@ -468,7 +468,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n)__test_check_assert (_metadata); \ } \ static struct __test_metadata *_##fixture_name##_##test_name##_object; \ - static void __attribute__((constructor(KSELFTEST_PRIO_TEST_F))) \ + static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_##test_name(void) \ { \ struct __test_metadata *object = mmap(NULL, sizeof(*object), \
@@ -1335,7 +1335,7 @@ static int test_harness_run(int argc, char **argv)return KSFT_FAIL ; } -static void __attribute__((constructor))__constructor_order_first (void ) +static void __attribute__((constructor (KSELFTEST_PRIO_TEST )))__constructor_order_first (void ) { __constructor_order_forward = true; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1096/1376 selftests: harness: Mark test fixture objects __maybe_unused
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Matlack <[email protected]>
[ Upstream commit 37ffa24c9d07edcd414d34283e02af3f3866cf12 ]
Mark _##fixture_name##_##test_name##_object __maybe_unused since it may not ever be read. This pointer is only read in XFAIL_ADD(), which tests are not required to use.
clang made a change to -Wunused-but-set-variable (split out into its own subwarning, -Wunused-but-set-global) that causes this warning to be emitted for various selftests and can be upgraded to an error in selftest that set -Werror.
VFIO selftests have been broken since commit ff556bd98348 ("vfio:
selftests: Add -Wall and -Werror to the Makefile"), and the net selftests builds have been noisy due to -Wall.
--- tools/testing/selftests/kselftest_harness.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/testing/selftests/kselftest_harness.h +1 -1
@@ -467,7 +467,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) } \__test_check_assert (_metadata); \ } \ - static struct __test_metadata *_##fixture_name##_##test_name##_object; \ + static struct __test_metadata *_##fixture_name##_##test_name##_object __maybe_unused; \ static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) \ _register_##fixture_name##_##test_name(void) \ { \
Signature · 1 line
2.53.0
Re: [PATCH 6.12 1096/1376] selftests: harness: Mark test fixture objects __maybe_unused
Quoted 40 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: David Matlack <[email protected]>
[ Upstream commit
37ffa24c9d07edcd414d34283e02af3f3866cf12]Mark _##fixture_name##_##test_name##_object
__maybe_unusedsince it may not ever be read. This pointer is only read inXFAIL_ADD(), which tests are not required to use.clang made a change to -Wunused-but-set-variable (split out into its own subwarning, -Wunused-but-set-global) that causes this warning to be emitted for various selftests and can be upgraded to an error in selftest that set -Werror.
VFIO selftests have been broken since commit
ff556bd98348("vfio:
selftests: Add -Wall and -Werror to theMakefile"), and the net selftests builds have been noisy due to -Wall.Fixes 24cf65a62266 ("selftests/harness: Share _metadata between forked processes")Reported-by Kuniyuki Iwashima <[email protected]>Reported-by Aaron Lewis <[email protected]>Reviewed-by Alex Williamson <[email protected]>Reviewed-by Kuniyuki Iwashima <[email protected]>Signed-off-by David Matlack <[email protected]>Reviewed-by Nathan Chancellor <[email protected]>Signed-off-by Alex Williamson <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- tools/testing/selftests/kselftest_harness.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)tools/testing/selftests/kselftest_harness.h +0
-0
tools/testing/selftests/kselftest_harness.h static inline void __kselftest_memset_safe(void *s, int c, size_t n) · line 470
Quoted 5 lines
tools/testing/selftests/kselftest_harness.h +1
-1@@ -467,7 +467,7 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n) } \__test_check_assert (_metadata); \ } \ - static struct __test_metadata *_##fixture_name##_##test_name##_object; \ + static struct __test_metadata *_##fixture_name##_##test_name##_object __maybe_unused; \
Hi Greg/Sasha,
I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.
The harness now annotates fixture declarations with __maybe_unused, but the 6.12 kselftest.h does not define that macro. A real TEST_F() consumer, tools/testing/selftests/exec/non-regular.c, consequently fails to compile at the declaration. Supplying the guarded unused-attribute definition makes the same source compile.
Upstream prerequisite code:
#ifndef __maybe_unused
#define __maybe_unused __attribute__((__unused__))
#endifDownstream 6.12.y harness code:
static struct __test_metadata * _##fixture_name##_##test_name##_object __maybe_unused;
The downstream header uses a macro its included kselftest.h lacks.
I suggest taking the kselftest.h definition from a7498388b099f08219bad5f19d00e436da27fbf9 before this backport. Thoughts?
Quoted 3 lines
tools/testing/selftests/kselftest_harness.h +0
-0static void __attribute__((constructor (KSELFTEST_PRIO_TEST ))) \ _register_##fixture_name##_##test_name(void) \ { \
Sasha Levin
maintainerRe: [PATCH 6.12 1096/1376] selftests: harness: Mark test fixture objects __maybe_unused
Quoted 2 lines
I suggest taking the
kselftest.hdefinition froma7498388b099f08219bad5f19d00e436da27fbf9before this backport. Thoughts?
37ffa24c9d07 ("selftests: harness: Mark test fixture objects __maybe_unused") is a cosmetic cleanup with no Fixes: tag and no Cc: stable, so rather than pull a second selftests header commit in behind it, I reverted it on 6.12.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 6.12 1096/1376] selftests: harness: Mark test fixture objects __maybe_unused
Hi Sasha,
Quoted 3 lines · Sasha Levin
37ffa24c9d07("selftests: harness: Mark test fixture objects__maybe_unused") is a cosmetic cleanup with no Fixes: tag and no Cc: stable, so rather than pull a second selftests header commit in behind it, I reverted it on 6.12.
Agreed, best thing!
thanks,
Harshit
Greg Kroah-Hartman
maintainerPATCH 1097/1376 selftests/bpf: vmtest.sh: Preserve command quoting when running in the VM
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vineet Gupta <[email protected]>
[ Upstream commit 3a59f11e0f989bdd637c87151992605a6559a7cb ]
vmtest.sh captures the trailing command with command="$@", which flattens the arguments into a single space-separated string, and then pastes it into the generated guest init script:
cd /root/bpf
echo ${command}
stdbuf -oL -eL ${command}That here-doc is unquoted, so the host expands ${command} and the flattened text lands in the script verbatim. The guest bash then parses those lines as shell source, re-splitting the text on whitespace and glob-expanding it against /root/bpf. As a result any command with a glob or an argument containing spaces is corrupted before it reaches the test binary. For example:
vmtest.sh -- ./test_progs -a 'verifier_*'
has 'verifier_*' expanded in the guest into the matching object/skeleton files (verifier_align.bpf.o verifier_align.skel.h ...), so test_progs is handed a list of filenames instead of the intended name filter and runs no matching tests.
Quote each argument with printf '%q ' so the command is reproduced verbatim inside the VM: the escaped text goes through exactly one round of quote removal when the guest parses the init script, yielding the original argv with globs and special characters intact. The common case (e.g. -t <name>) is unaffected.
Only do this when there is a command to quote. printf '%q ' with no arguments still applies the format once and emits '', which the -s (debug shell) path would take for a real command and try to run.
Note this makes the trailing command strictly an argv rather than a shell
snippet: passing it pre-quoted as one word, e.g.
vmtest.sh -- "./test_progs -t foo"
no longer works, and neither does embedding guest-side shell syntax such as ';' or a redirection. 'sh -c ...' still works.
The RV64 recipe in README.rst does depend on the old double parse: it wraps the denylist in \" so the literal quotes reach the guest, whose second parse of the init script removes them. Under %q those quotes now survive into argv, and parse_test_list() strtok_r()s on ',' turns them into junk filters:
-d ",exceptions," -> ["] [exceptions] ["]
That is harmless for DENYLIST.riscv64 only because its first line is a comment, so the leading field is empty. A denylist starting with a real entry would silently lose it - ["*arena*] never matches - so drop the backslashes and let the host consume the quotes instead.
--- tools/testing/selftests/bpf/README.rst | 4 ++-- tools/testing/selftests/bpf/vmtest.sh | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-)
tools/testing/selftests/bpf/README.rst +2 -2
@@ -107,12 +107,12 @@ Docker container and local rootfs image. The overall steps are as follows: tools/testing/selftests/bpf/vmtest.sh \ -l <path of local rootfs image> -- \ ./test_progs -d \ - \"$(cat tools/testing/selftests/bpf/DENYLIST.riscv64 \ + "$(cat tools/testing/selftests/bpf/DENYLIST.riscv64 \ | cut -d'#' -f1 \ | sed -e 's/^[[:space:]]*//' \ -e 's/[[:space:]]*$//' \ | tr -s '\n' ',' \ - )\" + )" Link: https://github.com/pulehui/riscv-bpf-vmtest.git [0] Link: https://github.com/libbpf/ci/blob/main/rootfs/mkrootfs_debian.sh [1]
tools/testing/selftests/bpf/vmtest.sh +11 -2
@@ -419,8 +419,17 @@ main() if [[ $# -eq 0 && "${debug_shell}" == "no" ]]; then echo "No command specified, will run ${DEFAULT_COMMAND} in the vm" - else - command="$@" + elif [[ $# -gt 0 ]]; then + # Quote each argument so the command survives into the guest: the + # host expands ${command} into the generated init script, which + # the guest bash then parses as shell source. Without the %q + # escapes an argument with a space or a glob (e.g. -a 'verifier_*') + # is re-split and expanded against /root/bpf there. + # + # Skip this when there is no command: printf '%q ' would still + # apply the format once and emit '', which is not the empty + # command that -s (debug shell) expects. + command=$(printf '%q ' "$@") fi local kconfig_file="${OUTPUT_DIR}/latest.config"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1098/1376 spi: img-spfi: dont disable runtime PM on DMA deferred probe
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Felix Gu <[email protected]>
[ Upstream commit 923e41ed59511cffe98357c7d58d0294a1c157ee ]
When dma_request_chan() returns -EPROBE_DEFER, the error path jumps to disable_pm and calls pm_runtime_disable() even though pm_runtime_enable() was never called, leaving disable_depth unbalanced and the device permanently PM-disabled.
Route the defer path through free_dma to skip pm_runtime_disable().
--- drivers/spi/spi-img-spfi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
drivers/spi/spi-img-spfi.c +3 -2
@@ -616,7 +616,7 @@ static int img_spfi_probe(struct platform_device *pdev) ret =PTR_ERR (spfi->tx_ch); spfi->tx_ch =NULL ;if (ret == -EPROBE_DEFER ) - goto disable_pm; + goto free_dma; } spfi->rx_ch =dma_request_chan (spfi->dev,"rx" );
@@ -624,7 +624,7 @@ static int img_spfi_probe(struct platform_device *pdev) ret =PTR_ERR (spfi->rx_ch); spfi->rx_ch =NULL ;if (ret == -EPROBE_DEFER ) - goto disable_pm; + goto free_dma; }if (!spfi->tx_ch || !spfi->rx_ch) {
@@ -652,6 +652,7 @@ static int img_spfi_probe(struct platform_device *pdev) disable_pm:pm_runtime_disable (spfi->dev); +free_dma:if (spfi->rx_ch)dma_release_channel (spfi->rx_ch);if (spfi->tx_ch)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1099/1376 PCI/ASPM: Cache L0s/L1 Supported so advertised link states can be overridden
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bjorn Helgaas <[email protected]>
[ Upstream commit 4495bffd86ba0fdabfaef0c41d12f68ec2a1e05b ]
Defective devices sometimes advertise support for ASPM L0s or L1 states even if they don't work correctly.
Cache the L0s Supported and L1 Supported bits early in enumeration so HEADER quirks can override the ASPM states advertised in Link Capabilities before pcie_aspm_cap_init() enables ASPM.
Stable-dep-of: 733cd811b3ac ("PCI/ASPM: Disable/restore ASPM on every function for multi-function devices")
--- drivers/pci/pcie/aspm.c | 12 ++++-------- drivers/pci/probe.c | 7 +++++++ include/linux/pci.h | 2 ++ 3 files changed, 13 insertions(+), 8 deletions(-)
drivers/pci/pcie/aspm.c +4 -8
@@ -798,7 +798,6 @@ static void aspm_l1ss_init(struct pcie_link_state *link)static void pcie_aspm_cap_init (struct pcie_link_state *link,int blacklist) {struct pci_dev *child = link->downstream, *parent = link->pdev; -u32 parent_lnkcap, child_lnkcap;u16 parent_lnkctl, child_lnkctl;struct pci_bus *linkbus = parent->subordinate;
@@ -813,9 +812,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)* If ASPM not supported, don't mess with the clocks and link, * bail out now. */ -pcie_capability_read_dword (parent,PCI_EXP_LNKCAP , &parent_lnkcap); -pcie_capability_read_dword (child,PCI_EXP_LNKCAP , &child_lnkcap); -if (!(parent_lnkcap & child_lnkcap &PCI_EXP_LNKCAP_ASPMS )) +if (!(parent->aspm_l0s_support && child->aspm_l0s_support) && + !(parent->aspm_l1_support && child->aspm_l1_support))return ;/* Configure common clock before checking latencies */
@@ -827,8 +825,6 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)* read-only Link Capabilities may change depending on common clock * configuration (PCIe r5.0, sec 7.5.3.6). */ -pcie_capability_read_dword (parent,PCI_EXP_LNKCAP , &parent_lnkcap); -pcie_capability_read_dword (child,PCI_EXP_LNKCAP , &child_lnkcap);pcie_capability_read_word (parent,PCI_EXP_LNKCTL , &parent_lnkctl);pcie_capability_read_word (child,PCI_EXP_LNKCTL , &child_lnkctl);
@@ -848,7 +844,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)* given link unless components on both sides of the link each * support L0s. */ -if (parent_lnkcap & child_lnkcap &PCI_EXP_LNKCAP_ASPM_L0S ) +if (parent->aspm_l0s_support && child->aspm_l0s_support) link->aspm_support |=PCIE_LINK_STATE_L0S ;if (child_lnkctl &PCI_EXP_LNKCTL_ASPM_L0S )
@@ -857,7 +853,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) link->aspm_enabled |=PCIE_LINK_STATE_L0S_DW ;/* Setup L1 state */ -if (parent_lnkcap & child_lnkcap &PCI_EXP_LNKCAP_ASPM_L1 ) +if (parent->aspm_l1_support && child->aspm_l1_support) link->aspm_support |=PCIE_LINK_STATE_L1 ;if (parent_lnkctl & child_lnkctl &PCI_EXP_LNKCTL_ASPM_L1 )
drivers/pci/probe.c +7 -0
@@ -1589,6 +1589,13 @@ void set_pcie_port_type(struct pci_dev *pdev)if (reg32 &PCI_EXP_LNKCAP_DLLLARC ) pdev->link_active_reporting =1 ; +#ifdef CONFIG_PCIEASPM +if (reg32 &PCI_EXP_LNKCAP_ASPM_L0S ) + pdev->aspm_l0s_support =1 ; +if (reg32 &PCI_EXP_LNKCAP_ASPM_L1 ) + pdev->aspm_l1_support =1 ; +#endif + parent =pci_upstream_bridge (pdev);if (!parent)return ;
include/linux/pci.h +2 -0
@@ -409,6 +409,8 @@ struct pci_dev {u16 l1ss;/* L1SS Capability pointer */ #ifdef CONFIG_PCIEASPM struct pcie_link_state *link_state;/* ASPM link state */ + unsigned int aspm_l0s_support:1; /* ASPM L0s support */ + unsigned int aspm_l1_support:1; /* ASPM L1 support */ unsigned int ltr_path:1; /* Latency Tolerance Reportingsupported from root to here */ #endif
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1100/1376 PCI/ASPM: Disable/restore ASPM on every function for multi-function devices
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krishna Chaitanya Chundru <[email protected]>
[ Upstream commit 733cd811b3ac50586164a0864c4351fe23e21890 ]
pcie_aspm_cap_init() disables ASPM L0s/L1 before touching L1SS config, then restores the pre-existing state afterward. Both steps only ever touched link->downstream, i.e. function 0 of the downstream component, leaving sibling functions (>0) on a multi-function device untouched.
This means the "disable" step does not actually disable ASPM link-wide on a multi-function device: a sibling function can still have L1 enabled even after this step runs. PCIe r7.0, sec 7.5.3.7, recommends programming the same ASPM Control value for all functions of a multi-function device, and pcie_config_aspm_link() already loops over every function on the bus for exactly this reason.
Loop over every function on linkbus->devices for both the disable and restore steps, keeping the existing sec 7.5.3.7 ordering (disable downstream functions before upstream, restore upstream before downstream functions). The masked pcie_capability_clear_and_set_word() accessor from the previous commit makes this safe: it only ever touches the ASPM Control bits, so function-specific bits elsewhere in LNKCTL (e.g. Read Completion Boundary, CLKREQ Enable) on sibling functions are left untouched.
--- drivers/pci/pcie/aspm.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
drivers/pci/pcie/aspm.c +6 -3
@@ -798,6 +798,7 @@ static void aspm_l1ss_init(struct pcie_link_state *link)static void pcie_aspm_cap_init (struct pcie_link_state *link,int blacklist) {struct pci_dev *child = link->downstream, *parent = link->pdev; +struct pci_dev *fn;u16 parent_lnkctl, child_lnkctl;struct pci_bus *linkbus = parent->subordinate;
@@ -831,8 +832,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)/* Disable L0s/L1 before updating L1SS config */ if (FIELD_GET (PCI_EXP_LNKCTL_ASPMC , child_lnkctl) ||FIELD_GET (PCI_EXP_LNKCTL_ASPMC , parent_lnkctl)) { -pcie_capability_clear_and_set_word (child,PCI_EXP_LNKCTL , -PCI_EXP_LNKCTL_ASPMC ,0 ); +list_for_each_entry (fn, &linkbus->devices, bus_list) +pcie_capability_clear_and_set_word (fn,PCI_EXP_LNKCTL , +PCI_EXP_LNKCTL_ASPMC ,0 );pcie_capability_clear_and_set_word (parent,PCI_EXP_LNKCTL ,PCI_EXP_LNKCTL_ASPMC ,0 ); }
@@ -867,7 +869,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)pcie_capability_clear_and_set_word (parent,PCI_EXP_LNKCTL ,PCI_EXP_LNKCTL_ASPMC , parent_lnkctl &PCI_EXP_LNKCTL_ASPMC ); -pcie_capability_clear_and_set_word (child,PCI_EXP_LNKCTL , +list_for_each_entry (fn, &linkbus->devices, bus_list) +pcie_capability_clear_and_set_word (fn,PCI_EXP_LNKCTL ,PCI_EXP_LNKCTL_ASPMC , child_lnkctl &PCI_EXP_LNKCTL_ASPMC ); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1101/1376 power: supply: bd99954: Drop bad register fields
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Matti Vaittinen <[email protected]>
[ Upstream commit 177299384a224377b4152242d875e752e5958c02 ]
The bd99954 driver creates regmap fields for all the registers. All the fields for VBUS_UCD_FCTRL_SET (extended command 0x33) are accidentally created for VCC_UCD_FCTRL_SET (extended command 0x2b), causing all reads/writes for the fields to access wrong register. Luckily the fields for VBUS_UCD_FCTRL_SET are unused.
Taking a look at the data-sheet all the fields in the VBUS_UCD_FCTRL_SET are marked as: "In normal operation, please don’t set these registers" with not much of additional documentation. As the fields are wrong and because there seems to be no users of the fields - it is better to just drop them. This will also decrease the memory footprint.
Drop incorrect VBUS_UCD_FCTRL_SET fields.
Datasheet: https://fscdn.rohm.com/en/products/databook/datasheet/ic/power/battery_management/bd99954xxx-e.pdf
--- drivers/power/supply/bd99954-charger.h | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-)
drivers/power/supply/bd99954-charger.h +1 -22
@@ -279,17 +279,7 @@ enum bd9995x_fields {F_VBUS_EXTID ,F_VBUS_IDRDET ,F_VBUS_INDO , -F_VBUS_UCDSWEN , -F_VBUS_RREF_EN , -F_VBUS_DPPU_EN , -F_VBUS_DPREF_EN , -F_VBUS_DMREF_EN , -F_VBUS_DPDET_EN , -F_VBUS_DMDET_EN , -F_VBUS_DPSINK_EN , -F_VBUS_DMSINK_EN , -F_VBUS_DP_BUFF_EN , -F_VBUS_DM_BUFF_EN , +F_VBUS_EXTCLKENBL ,F_VBUS_PLSTESTEN , F_VBUS_UCDSWEN_TSTENB,
@@ -626,17 +616,6 @@ static const struct reg_field bd9995x_reg_fields[] = { [F_VBUS_EXTID] = REG_FIELD(VBUS_IDD_STATUS, 5, 5), [F_VBUS_IDRDET] = REG_FIELD(VBUS_IDD_STATUS, 4, 4), [F_VBUS_INDO] = REG_FIELD(VBUS_IDD_STATUS, 0, 3), - [F_VBUS_UCDSWEN] = REG_FIELD(VCC_UCD_FCTRL_SET, 10, 10), - [F_VBUS_RREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 9, 9), - [F_VBUS_DPPU_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 8, 8), - [F_VBUS_DPREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 7, 7), - [F_VBUS_DMREF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 6, 6), - [F_VBUS_DPDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 5, 5), - [F_VBUS_DMDET_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 4, 4), - [F_VBUS_DPSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 3, 3), - [F_VBUS_DMSINK_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 2, 2), - [F_VBUS_DP_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 1, 1), - [F_VBUS_DM_BUFF_EN] = REG_FIELD(VCC_UCD_FCTRL_SET, 0, 0), [F_VBUS_EXTCLKENBL] = REG_FIELD(VBUS_UCD_FCTRL_EN, 15, 15), [F_VBUS_PLSTESTEN] = REG_FIELD(VBUS_UCD_FCTRL_EN, 14, 14),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1102/1376 power: supply: bq27xxx: bq27520g4: fix REG_TTES address
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrik Grimler <[email protected]>
[ Upstream commit c774a623c821a173c950e1297f8e8c85a89455be ]
According to the technical reference manual the StandbyTimeToEmpty (TTES) register is at 0x1a, not 0x1c. At 0x1c StateOfHealth instead resides.
--- drivers/power/supply/bq27xxx_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq27xxx_battery.c +1 -1
@@ -324,7 +324,7 @@ static u8 [BQ27XXX_REG_FLAGS] = 0x0a, [BQ27XXX_REG_TTE] = 0x16, [BQ27XXX_REG_TTF] = INVALID_REG_ADDR, - [BQ27XXX_REG_TTES] = 0x1c, + [BQ27XXX_REG_TTES] = 0x1a, [BQ27XXX_REG_TTECP] = INVALID_REG_ADDR, [BQ27XXX_REG_NAC] = 0x0c, [BQ27XXX_REG_RC] = 0x10,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1103/1376 power: supply: bq27xxx: bq28z610: fix invalid AverageEnergy address
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrik Grimler <[email protected]>
[ Upstream commit e9387e9ab5121f330d313afb90430c02200f9782 ]
bq28z610 currently uses the AveragePower reg, 0x22, for reading both AveragePower and AvailableEnergy. The technical reference manual does not mention AvailableEnergy, and it does not make sense to read a cumulative, always positive, property like AvailableEnergy from a rate property like AveragePower. Set REG_AE as invalid to fix it.
--- drivers/power/supply/bq27xxx_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq27xxx_battery.c +1 -1
@@ -510,7 +510,7 @@ static u8 [BQ27XXX_REG_RC] = 0x10, [BQ27XXX_REG_FCC] = 0x12, [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, [BQ27XXX_REG_SOC] = 0x2c, [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x22,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1104/1376 power: supply: bq27xxx: bq27z561: fix invalid AverageEnergy address
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Henrik Grimler <[email protected]>
[ Upstream commit 2da28b059e0ddcd2e1956eeae383246207965573 ]
bq27z561 currently uses the AveragePower reg, 0x22, for reading both AveragePower and AvailableEnergy. The technical reference manual does not mention AvailableEnergy, and it does not make sense to read a cumulative, always positive, property like AvailableEnergy from a rate property like AveragePower. Set REG_AE as invalid to fix it.
--- drivers/power/supply/bq27xxx_battery.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/power/supply/bq27xxx_battery.c +1 -1
@@ -489,7 +489,7 @@ static u8 [BQ27XXX_REG_RC] = 0x10, [BQ27XXX_REG_FCC] = 0x12, [BQ27XXX_REG_CYCT] = 0x2a, - [BQ27XXX_REG_AE] = 0x22, + [BQ27XXX_REG_AE] = INVALID_REG_ADDR, [BQ27XXX_REG_SOC] = 0x2c, [BQ27XXX_REG_DCAP] = 0x3c, [BQ27XXX_REG_AP] = 0x22,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1105/1376 xenbus: Unregister reboot notifier on init failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit d330fb86a7170f845123ae82d95df440fad9b707 ]
xs_init() registers xs_reboot_nb before initializing XenStore communications and starting xenwatch. If either operation fails, the notifier remains registered and a later initialization attempt can hit a duplicate registration.
Check the notifier registration result and unregister it on every subsequent failure path.
Message-ID: <[email protected]>
--- drivers/xen/xenbus/xenbus_xs.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)
drivers/xen/xenbus/xenbus_xs.c +12 -4
@@ -952,19 +952,27 @@ int xs_init(void)int err;struct task_struct *task; -register_reboot_notifier (&xs_reboot_nb); + err =register_reboot_notifier (&xs_reboot_nb); +if (err) +return err;/* Initialize the shared memory rings to talk to xenstored */ err =xb_init_comms ();if (err) -return err; + goto err_unregister_reboot_notifier; task =kthread_run (xenwatch_thread,NULL ,"xenwatch" ); -if (IS_ERR (task)) -return PTR_ERR (task); +if (IS_ERR (task)) { + err =PTR_ERR (task); + goto err_unregister_reboot_notifier; + }/* shutdown watches for kexec boot */ xs_reset_watches ();return 0 ; + +err_unregister_reboot_notifier: +unregister_reboot_notifier (&xs_reboot_nb); +return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1106/1376 s390/debug: Fix deadlock during unregister
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Peter Oberparleiter <[email protected]>
[ Upstream commit 445c31ac638fd1af203d79bdf25fc0cb3149fbbc ]
Unregistering an s390dbf debug area while one of the associated debugfs files is being written to can cause a deadlock:
$ echo >.../vmur/level $ rmmod vmur
===================================================
debugfs write
debugfs_file_get()
debug_unregister()
mutex_lock(debug_mutex)
debugfs_remove()
wait for debugfs_file_put()
debug_file_ops.write()
debug_input()
mutex_lock(debug_mutex) ==> DEADLOCKFix this by splitting debug_unregister() into an s390dbf and debugfs part, and running only the s390dbf part with debug_mutex locked.
--- arch/s390/kernel/debug.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
arch/s390/kernel/debug.c +8 -2
@@ -746,8 +746,8 @@ void debug_register_static(debug_info_t *id, int pages_per_area, int nr_areas)mutex_unlock (&debug_mutex); } -/* Remove debugfs entries and remove from internal list. */ -static void _debug_unregister (debug_info_t *id) +/* Remove debugfs entries. */ +static void _debug_unregister_debugfs (debug_info_t *id) {int i;
@@ -757,6 +757,11 @@ static void _debug_unregister(debug_info_t *id)debugfs_remove (id->debugfs_entries[i]); }debugfs_remove (id->debugfs_root_entry); +} + +/* Remove from internal list. */ +static void _debug_unregister (debug_info_t *id) +{if (id == debug_area_first) debug_area_first = id->next;if (id == debug_area_last)
@@ -782,6 +787,7 @@ void debug_unregister(debug_info_t *id)mutex_lock (&debug_mutex);_debug_unregister (id);mutex_unlock (&debug_mutex); +_debug_unregister_debugfs (id);debug_info_put (id); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1107/1376 clocksource/drivers/clps711x: Do not unmap clocksource MMIO
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Guangshuo Li <[email protected]>
[ Upstream commit e998c6300ef4e062a704ee17b5a812c0b595cf42 ]
clps711x_clksrc_init() stores the timer base address in the static tcd pointer and registers it as both the clocksource MMIO address and the sched_clock read address.
The clocksource init path must therefore keep the mapping alive after clps711x_timer_init() returns. However, the shared unmap_io exit path is also reached after successful clocksource registration, so the MMIO mapping is torn down while the clocksource and sched_clock readers may still access it.
Return directly after successful clocksource registration and leave the mapping alive for the registered readers. Keep the unmap_io path for the error paths and for the clockevent init path.
--- drivers/clocksource/clps711x-timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/clocksource/clps711x-timer.c +1 -1
@@ -94,7 +94,7 @@ static int __init clps711x_timer_init(struct device_node *np)switch (of_alias_get_id (np,"timer" )) {case CLPS711X_CLKSRC_CLOCKSOURCE :clps711x_clksrc_init (clock, base); -break ; +return 0 ;case CLPS711X_CLKSRC_CLOCKEVENT : ret =_clps711x_clkevt_init (clock, base, irq);break ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1108/1376 clocksource/drivers/armada: Unwind timer clock on init failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuho Choi <[email protected]>
[ Upstream commit 8b4127f6db40381229f3564d34ac35f36311c201 ]
The Armada timer init paths enable their clock before calling the common initialization routine. If that routine returns an error, the clock is left enabled even though the timer was not initialized successfully.
--- drivers/clocksource/timer-armada-370-xp.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
drivers/clocksource/timer-armada-370-xp.c +15 -3
@@ -346,7 +346,11 @@ static int __init armada_xp_timer_init(struct device_node *np) timer_clk =clk_get_rate (clk); -return armada_370_xp_timer_common_init (np); + ret =armada_370_xp_timer_common_init (np); +if (ret) +clk_disable_unprepare (clk); + +return ret; }TIMER_OF_DECLARE (armada_xp,"marvell,armada-xp-timer" , armada_xp_timer_init);
@@ -384,7 +388,11 @@ static int __init armada_375_timer_init(struct device_node *np) timer25Mhz = false; } -return armada_370_xp_timer_common_init (np); + ret =armada_370_xp_timer_common_init (np); +if (ret) +clk_disable_unprepare (clk); + +return ret; }TIMER_OF_DECLARE (armada_375,"marvell,armada-375-timer" , armada_375_timer_init);
@@ -407,7 +415,11 @@ static int __init armada_370_timer_init(struct device_node *np) timer_clk =clk_get_rate (clk) /TIMER_DIVIDER ; timer25Mhz = false; -return armada_370_xp_timer_common_init (np); + ret =armada_370_xp_timer_common_init (np); +if (ret) +clk_disable_unprepare (clk); + +return ret; }TIMER_OF_DECLARE (armada_370,"marvell,armada-370-timer" , armada_370_timer_init);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1109/1376 ALSA: seq: midi: Optimize event_input locking with RCU
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Takashi Iwai <[email protected]>
[ Upstream commit 4cc25cdd3cffa475edb8dec8199b3227038ebcfb ]
The recent fix for serializing the output teardown introduced a spinlock invocation at every MIDI output event via event_process_midi. Since this is a hot path, let's do performance optimization with RCU.
The new output_substream __rcu pointer is published via rcu_assign_pointer() in midisynth_use() after output_rfile is set, and cleared in midisynth_unuse() before the resource teardown. event_process_midi() reads it under rcu_read_lock() and bumps output_use_lock inside that section, which is necessary to close the window between the pointer dereference and the refcount increment.
midisynth_unuse() calls synchronize_rcu() before snd_use_lock_sync():
this guarantees that any reader who obtained a non-NULL pointer has already called atomic_inc (output_use_lock), so the subsequent snd_use_lock_sync() sees the correct in-flight count.
--- sound/core/seq/seq_midi.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-)
sound/core/seq/seq_midi.c +11 -13
@@ -43,8 +43,8 @@ struct seq_midisynth {int device;int subdevice;struct snd_rawmidi_file input_rfile; -spinlock_t output_lock;/* protects output_rfile publication */ snd_use_lock_t output_use_lock;/* in-flight event_input users */ +struct snd_rawmidi_substream __rcu *output_substream;struct snd_rawmidi_file output_rfile;int seq_client;int seq_port;
@@ -134,8 +134,8 @@ static int event_process_midi(struct snd_seq_event *ev, int direct,if (snd_BUG_ON (!msynth))return -EINVAL ; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - substream = msynth->output_rfile.output; +scoped_guard (rcu) { + substream =rcu_dereference (msynth->output_substream);if (!substream)return -ENODEV ;snd_use_lock_use (&msynth->output_use_lock);
@@ -177,7 +177,6 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; -spin_lock_init (&msynth->output_lock);snd_use_lock_init (&msynth->output_use_lock);return 0 ; }
@@ -252,8 +251,8 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *inforeturn err; }snd_midi_event_reset_decode (msynth->parser); -scoped_guard (spinlock_irqsave, &msynth->output_lock) - msynth->output_rfile = rfile; + msynth->output_rfile = rfile; +rcu_assign_pointer (msynth->output_substream, rfile.output);return 0 ; }
@@ -261,17 +260,16 @@ static int midisynth_use(void *private_data, struct snd_seq_port_subscribe *infostatic int midisynth_unuse (void *private_data,struct snd_seq_port_subscribe *info) {struct seq_midisynth *msynth = private_data; -struct snd_rawmidi_file rfile = {}; +struct snd_rawmidi_file rfile; - scoped_guard(spinlock_irqsave, &msynth->output_lock) { - rfile = msynth->output_rfile; - msynth->output_rfile = (struct snd_rawmidi_file ){}; - } +rcu_assign_pointer (msynth->output_substream,NULL ); +synchronize_rcu (); +snd_use_lock_sync (&msynth->output_use_lock); + rfile = msynth->output_rfile; + msynth->output_rfile = (struct snd_rawmidi_file ){};if (snd_BUG_ON (!rfile.output))return -EINVAL ; - -snd_use_lock_sync (&msynth->output_use_lock);snd_rawmidi_drain_output (rfile.output);return snd_rawmidi_kernel_release (&rfile); }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1110/1376 ALSA: seq: midi: Serialize input teardown with event_input
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: John Keeping <[email protected]>
[ Upstream commit 403f7f3ad3808a0096d84cf228fab68dc253fd9d ]
snd_midi_input_event() must not be running while a rawmidi substream is closing, since this can lead to the trigger state becoming out-of-step through this sequence in snd_rawmidi_input_trigger():
snd_rawmidi_input_trigger(up=0) snd_midi_input_event() -> snd_rawmidi_kernel_read() -> snd_rawmidi_input_trigger(up=1) -> cancel_work_sync()
which ends with the underlying device being active unexpectedly.
When this is called from close_substream(), further input can re-trigger the input event leaving it running after rawmidi_release_priv() has set rfile->rmidi to NULL which leads to:
Unable to handle kernel NULL pointer dereference at virtual address 00000000000000b0 Call trace: snd_midi_input_event+0x3c/0x134 [snd_seq_midi] (P) snd_rawmidi_input_event_work+0x1c/0x2c process_one_work+0x150/0x3a4 worker_thread+0x190/0x318
Apply a similar approach to commit ef7607ab1c8ad ("ALSA: seq: midi:
Serialize output teardown with event_input") which fixed the same issue in the output direction, but updated to use RCU following Takashi Iwai's proposed follow-on patch [1].
With this change in place, midisynth_unsubscribe() clears the input file so snd_midi_input_event() will not re-trigger the stream and will be quiesced by the cancel_work_sync() in snd_rawmidi_input_trigger().
[1] https://lore.kernel.org/linux-sound/[email protected]/
--- sound/core/seq/seq_midi.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-)
sound/core/seq/seq_midi.c +31 -6
@@ -42,6 +42,8 @@ struct seq_midisynth {struct snd_rawmidi *rmidi;int device;int subdevice; +struct snd_rawmidi_substream __rcu *input_substream; +snd_use_lock_t input_use_lock;/* in-flight event_input users */ struct snd_rawmidi_file input_rfile;snd_use_lock_t output_use_lock;/* in-flight event_input users */ struct snd_rawmidi_substream __rcu *output_substream;
@@ -76,6 +78,14 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream) msynth = runtime->private_data;if (msynth ==NULL )return ; + +scoped_guard (rcu) { +if (rcu_dereference (msynth->input_substream) != substream) +return ; + +snd_use_lock_use (&msynth->input_use_lock); + } +memset (&ev,0 ,sizeof (ev));while (runtime->avail >0 ) { res =snd_rawmidi_kernel_read (substream, buf,sizeof (buf));
@@ -95,6 +105,8 @@ static void snd_midi_input_event(struct snd_rawmidi_substream *substream)memset (&ev,0 ,sizeof (ev)); } } + +snd_use_lock_free (&msynth->input_use_lock); }static int dump_midi (struct snd_rawmidi_substream *substream,const char *buf,int count)
@@ -177,6 +189,7 @@ static int snd_seq_midisynth_new(struct seq_midisynth *msynth, msynth->card = card; msynth->device = device; msynth->subdevice = subdevice; +snd_use_lock_init (&msynth->input_use_lock);snd_use_lock_init (&msynth->output_use_lock);return 0 ; }
@@ -187,28 +200,31 @@ static int midisynth_subscribe(void *private_data, struct snd_seq_port_subscribeint err;struct seq_midisynth *msynth = private_data;struct snd_rawmidi_runtime *runtime; +struct snd_rawmidi_file rfile = {};struct snd_rawmidi_params params;/* open midi port */ err =snd_rawmidi_kernel_open (msynth->rmidi, msynth->subdevice,SNDRV_RAWMIDI_LFLG_INPUT , - &msynth->input_rfile); + &rfile);if (err <0 ) {pr_debug ("ALSA: seq_midi: midi input open failed!!!\n" );return err; } - runtime = msynth->input_rfile.input->runtime; + runtime = rfile.input->runtime;memset (¶ms,0 ,sizeof (params)); params.avail_min =1 ; params.buffer_size = input_buffer_size; - err =snd_rawmidi_input_params (msynth->input_rfile.input, ¶ms); + err =snd_rawmidi_input_params (rfile.input, ¶ms);if (err <0 ) { -snd_rawmidi_kernel_release (&msynth->input_rfile); +snd_rawmidi_kernel_release (&rfile);return err; }snd_midi_event_reset_encode (msynth->parser); runtime->event = snd_midi_input_event; runtime->private_data = msynth; + msynth->input_rfile = rfile; +rcu_assign_pointer (msynth->input_substream, rfile.input);snd_rawmidi_kernel_read (msynth->input_rfile.input,NULL ,0 );return 0 ; }
@@ -218,10 +234,19 @@ static int midisynth_unsubscribe(void *private_data, struct snd_seq_port_subscri {int err;struct seq_midisynth *msynth = private_data; +struct snd_rawmidi_file rfile; -if (snd_BUG_ON (!msynth->input_rfile.input)) +rcu_assign_pointer (msynth->input_substream,NULL ); +synchronize_rcu (); +snd_use_lock_sync (&msynth->input_use_lock); + + rfile = msynth->input_rfile; + msynth->input_rfile = (struct snd_rawmidi_file ){}; + +if (snd_BUG_ON (!rfile.input))return -EINVAL ; - err =snd_rawmidi_kernel_release (&msynth->input_rfile); + + err =snd_rawmidi_kernel_release (&rfile);return err; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1111/1376 cgroup/cpuset: Fix spelling errors in file kernel/cgroup/cpuset.c
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: everestkc <[email protected]>
[ Upstream commit 95a616d89ccd2d2af0bd26c13c50143b301d82e8 ]
Corrected the spelling errors repoted by codespell as follows:
temparary ==> temporary
Proprogate ==> Propagate
constrainted ==> constrainedStable-dep-of: ddabc5dbd262 ("selftests/cgroup: Preserve CPU hotplug write errors")
--- kernel/cgroup/cpuset.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
kernel/cgroup/cpuset.c +8 -8
@@ -1386,7 +1386,7 @@ static inline bool is_local_partition(struct cpuset *cs)* remote_partition_enable - Enable current cpuset as a remote partition root * @cs: the cpuset to update * @new_prs: new partition_root_state -* @tmp: temparary masks +* @tmp: temporary masks * Return: 0 if successful, errcode if error * * Enable the current cpuset to become a remote partition root taking CPUs
@@ -1425,7 +1425,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs, cs->prs_err =0 ;/* -* Proprogate changes in top_cpuset's effective_cpus down the hierarchy. +* Propagate changes in top_cpuset's effective_cpus down the hierarchy. */ cpuset_update_tasks_cpumask (&top_cpuset, tmp->new_cpus);update_sibling_cpumasks (&top_cpuset,NULL , tmp);
@@ -1435,7 +1435,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,/* * remote_partition_disable - Remove current cpuset from remote partition list * @cs: the cpuset to update -* @tmp: temparary masks +* @tmp: temporary masks * * The effective_cpus is also updated. *
@@ -1463,7 +1463,7 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)update_unbound_workqueue_cpumask (isolcpus_updated);/* -* Proprogate changes in top_cpuset's effective_cpus down the hierarchy. +* Propagate changes in top_cpuset's effective_cpus down the hierarchy. */ cpuset_update_tasks_cpumask (&top_cpuset, tmp->new_cpus);update_sibling_cpumasks (&top_cpuset,NULL , tmp);
@@ -1473,7 +1473,7 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)* remote_cpus_update - cpus_exclusive change of remote partition * @cs: the cpuset to be updated * @newmask: the new effective_xcpus mask -* @tmp: temparary masks +* @tmp: temporary masks * * top_cpuset and subpartitions_cpus will be updated or partition can be * invalidated.
@@ -1522,7 +1522,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *newmask,update_unbound_workqueue_cpumask (isolcpus_updated);/* -* Proprogate changes in top_cpuset's effective_cpus down the hierarchy. +* Propagate changes in top_cpuset's effective_cpus down the hierarchy. */ cpuset_update_tasks_cpumask (&top_cpuset, tmp->new_cpus);update_sibling_cpumasks (&top_cpuset,NULL , tmp);
@@ -1537,7 +1537,7 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *newmask,* @cs: the cpuset to be updated * @newmask: the new effective_xcpus mask * @delmask: temporary mask for deletion (not in tmp) -* @tmp: temparary masks +* @tmp: temporary masks * * This should be called before the given cs has updated its cpus_allowed * and/or effective_xcpus.
@@ -2248,7 +2248,7 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,return -EINVAL ;/* -* When exclusive_cpus isn't explicitly set, it is constrainted +* When exclusive_cpus isn't explicitly set, it is constrained * by cpus_allowed and parent's effective_xcpus. Otherwise, * trialcs->effective_xcpus is used as a temporary cpumask * for checking validity of the partition root.
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1112/1376 cgroup/cpuset: Remove remote_partition_check() & make update_cpumasks_hier() handle remote partition
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
[ Upstream commit f62a5d39368e34a966c8df63e1f05eed7fe9c5de ]
Currently, changes in exclusive CPUs are being handled in remote_partition_check() by disabling conflicting remote partitions. However, that may lead to results unexpected by the users. Fix this problem by removing remote_partition_check() and making update_cpumasks_hier() handle changes in descendant remote partitions properly.
The compute_effective_exclusive_cpumask() function is enhanced to check the exclusive_cpus and effective_xcpus from siblings and excluded them in its effective exclusive CPUs computation and return a value to show if there is any sibling conflicts. This is somewhat like the cpu_exclusive flag check in validate_change(). This is the initial step to enable us to retire the use of cpu_exclusive flag in cgroup v2 in the future.
One of the tests in the TEST_MATRIX of the test_cpuset_prs.sh script has to be updated due to changes in the way a child remote partition root is being handled (updated instead of invalidation) in update_cpumasks_hier().
Stable-dep-of: ddabc5dbd262 ("selftests/cgroup: Preserve CPU hotplug write errors")
--- kernel/cgroup/cpuset.c | 258 ++++++++++-------- .../selftests/cgroup/test_cpuset_prs.sh | 4 +- 2 files changed, 143 insertions(+), 119 deletions(-)
kernel/cgroup/cpuset.c +141 -117
@@ -88,7 +88,6 @@ static struct list_head remote_children;* A flag to force sched domain rebuild at the end of an operation. * It can be set in * - update_partition_sd_lb() -* - remote_partition_check() * - update_cpumasks_hier() * - cpuset_update_flag() * - cpuset_hotplug_update_tasks()
@@ -1356,20 +1355,57 @@ EXPORT_SYMBOL_GPL(cpuset_cpu_is_isolated);* compute_effective_exclusive_cpumask - compute effective exclusive CPUs * @cs: cpuset * @xcpus: effective exclusive CPUs value to be set -* Return: true if xcpus is not empty, false otherwise. +* @real_cs: the real cpuset (can be NULL) +* Return: 0 if there is no sibling conflict, > 0 otherwise * -* Starting with exclusive_cpus (cpus_allowed if exclusive_cpus is not set), -* it must be a subset of parent's effective_xcpus. +* If exclusive_cpus isn't explicitly set or a real_cs is provided, we have to +* scan the sibling cpusets and exclude their exclusive_cpus or effective_xcpus +* as well. The provision of real_cs means that a cpumask is being changed and +* the given cs is a trial one. */ -static bool compute_effective_exclusive_cpumask (struct cpuset *cs, -struct cpumask *xcpus) +static int compute_effective_exclusive_cpumask (struct cpuset *cs, +struct cpumask *xcpus, +struct cpuset *real_cs) { +struct cgroup_subsys_state *css;struct cpuset *parent =parent_cs (cs); +struct cpuset *sibling; +int retval =0 ;if (!xcpus) xcpus = cs->effective_xcpus; -return cpumask_and (xcpus,user_xcpus (cs), parent->effective_xcpus); +cpumask_and (xcpus,user_xcpus (cs), parent->effective_xcpus); + +if (!real_cs) { +if (!cpumask_empty (cs->exclusive_cpus)) +return 0 ; + }else { + cs = real_cs; + } + +/* +* Exclude exclusive CPUs from siblings +*/ +rcu_read_lock (); +cpuset_for_each_child (sibling, css, parent) { +if (sibling == cs) +continue ; + +if (!cpumask_empty (sibling->exclusive_cpus) && +cpumask_intersects (xcpus, sibling->exclusive_cpus)) { +cpumask_andnot (xcpus, xcpus, sibling->exclusive_cpus); + retval++; +continue ; + } +if (!cpumask_empty (sibling->effective_xcpus) && +cpumask_intersects (xcpus, sibling->effective_xcpus)) { +cpumask_andnot (xcpus, xcpus, sibling->effective_xcpus); + retval++; + } + } +rcu_read_unlock (); +return retval; }static inline bool is_remote_partition (struct cpuset *cs)
@@ -1411,7 +1447,7 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,* remote partition root underneath it, its exclusive_cpus must * have overlapped with subpartitions_cpus. */ -compute_effective_exclusive_cpumask (cs, tmp->new_cpus); +compute_effective_exclusive_cpumask (cs, tmp->new_cpus,NULL );if (cpumask_empty (tmp->new_cpus) ||cpumask_intersects (tmp->new_cpus, subpartitions_cpus) ||cpumask_subset (top_cpuset.effective_cpus, tmp->new_cpus))
@@ -1420,8 +1456,10 @@ static int remote_partition_enable(struct cpuset *cs, int new_prs,spin_lock_irq (&callback_lock); isolcpus_updated =partition_xcpus_add (new_prs,NULL , tmp->new_cpus);list_add (&cs->remote_sibling, &remote_children); +cpumask_copy (cs->effective_xcpus, tmp->new_cpus);spin_unlock_irq (&callback_lock);update_unbound_workqueue_cpumask (isolcpus_updated); +cpuset_force_rebuild (); cs->prs_err =0 ;/*
@@ -1445,22 +1483,24 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp) {bool isolcpus_updated; -compute_effective_exclusive_cpumask (cs, tmp->new_cpus);WARN_ON_ONCE (!is_remote_partition (cs)); -WARN_ON_ONCE (!cpumask_subset (tmp->new_cpus, subpartitions_cpus)); +WARN_ON_ONCE (!cpumask_subset (cs->effective_xcpus, subpartitions_cpus));spin_lock_irq (&callback_lock);list_del_init (&cs->remote_sibling); isolcpus_updated =partition_xcpus_del (cs->partition_root_state, -NULL , tmp->new_cpus); +NULL , cs->effective_xcpus);if (cs->prs_err) cs->partition_root_state = -cs->partition_root_state;else cs->partition_root_state =PRS_MEMBER ; +/* effective_xcpus may need to be changed */ +compute_effective_exclusive_cpumask (cs,NULL ,NULL );reset_partition_data (cs);spin_unlock_irq (&callback_lock);update_unbound_workqueue_cpumask (isolcpus_updated); +cpuset_force_rebuild ();/* * Propagate changes in top_cpuset's effective_cpus down the hierarchy.
@@ -1472,14 +1512,15 @@ static void remote_partition_disable(struct cpuset *cs, struct tmpmasks *tmp)/* * remote_cpus_update - cpus_exclusive change of remote partition * @cs: the cpuset to be updated -* @newmask: the new effective_xcpus mask +* @xcpus: the new exclusive_cpus mask, if non-NULL +* @excpus: the new effective_xcpus mask * @tmp: temporary masks * * top_cpuset and subpartitions_cpus will be updated or partition can be * invalidated. */ -static void remote_cpus_update (struct cpuset *cs,struct cpumask *newmask, -struct tmpmasks *tmp) +static void remote_cpus_update (struct cpuset *cs,struct cpumask *xcpus, +struct cpumask *excpus,struct tmpmasks *tmp) {bool adding, deleting;int prs = cs->partition_root_state;
@@ -1490,13 +1531,13 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *newmask,WARN_ON_ONCE (!cpumask_subset (cs->effective_xcpus, subpartitions_cpus)); -if (cpumask_empty (newmask)) { +if (cpumask_empty (excpus)) { cs->prs_err =PERR_CPUSEMPTY ; goto invalidate; } - adding =cpumask_andnot (tmp->addmask, newmask, cs->effective_xcpus); - deleting =cpumask_andnot (tmp->delmask, cs->effective_xcpus, newmask); + adding =cpumask_andnot (tmp->addmask, excpus, cs->effective_xcpus); + deleting =cpumask_andnot (tmp->delmask, cs->effective_xcpus, excpus);/* * Additions of remote CPUs is only allowed if those CPUs are
@@ -1518,8 +1559,17 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *newmask, isolcpus_updated +=partition_xcpus_add (prs,NULL , tmp->addmask);if (deleting) isolcpus_updated +=partition_xcpus_del (prs,NULL , tmp->delmask); +/* +* Need to update effective_xcpus and exclusive_cpus now as +* update_sibling_cpumasks() below may iterate back to the same cs. +*/ +cpumask_copy (cs->effective_xcpus, excpus); +if (xcpus) +cpumask_copy (cs->exclusive_cpus, xcpus);spin_unlock_irq (&callback_lock);update_unbound_workqueue_cpumask (isolcpus_updated); +if (adding || deleting) +cpuset_force_rebuild ();/* * Propagate changes in top_cpuset's effective_cpus down the hierarchy.
@@ -1532,47 +1582,6 @@ static void remote_cpus_update(struct cpuset *cs, struct cpumask *newmask,remote_partition_disable (cs, tmp); } -/* -* remote_partition_check - check if a child remote partition needs update -* @cs: the cpuset to be updated -* @newmask: the new effective_xcpus mask -* @delmask: temporary mask for deletion (not in tmp) -* @tmp: temporary masks -* -* This should be called before the given cs has updated its cpus_allowed -* and/or effective_xcpus. -*/ -static void remote_partition_check (struct cpuset *cs,struct cpumask *newmask, -struct cpumask *delmask,struct tmpmasks *tmp) -{ -struct cpuset *child, *next; -int disable_cnt =0 ; - -/* -* Compute the effective exclusive CPUs that will be deleted. -*/ -if (!cpumask_andnot (delmask, cs->effective_xcpus, newmask) || - !cpumask_intersects (delmask, subpartitions_cpus)) -return ;/* No deletion of exclusive CPUs in partitions */ - -/* -* Searching the remote children list to look for those that will -* be impacted by the deletion of exclusive CPUs. -* -* Since a cpuset must be removed from the remote children list -* before it can go offline and holding cpuset_mutex will prevent -* any change in cpuset status. RCU read lock isn't needed. -*/ -lockdep_assert_held (&cpuset_mutex); -list_for_each_entry_safe (child, next, &remote_children, remote_sibling) -if (cpumask_intersects (child->effective_cpus, delmask)) { -remote_partition_disable (child, tmp); - disable_cnt++; - } -if (disable_cnt) -cpuset_force_rebuild (); -} -/* * prstate_housekeeping_conflict - check for partition & housekeeping conflicts * @prstate: partition root state to be checked
@@ -1645,6 +1654,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd,bool nocpu;lockdep_assert_held (&cpuset_mutex); +WARN_ON_ONCE (is_remote_partition (cs));/* * new_prs will only be changed for the partcmd_update and
@@ -1686,13 +1696,20 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, nocpu =tasks_nocpu_error (parent, cs, xcpus);if ((cmd == partcmd_enable) || (cmd == partcmd_enablei)) { +/* +* Need to call compute_effective_exclusive_cpumask() in case +* exclusive_cpus not set. Sibling conflict should only happen +* if exclusive_cpus isn't set. +*/ + xcpus = tmp->new_cpus; +if (compute_effective_exclusive_cpumask (cs, xcpus,NULL )) +WARN_ON_ONCE (!cpumask_empty (cs->exclusive_cpus)); +/* * Enabling partition root is not allowed if its -* effective_xcpus is empty or doesn't overlap with -* parent's effective_xcpus. +* effective_xcpus is empty. */ -if (cpumask_empty (xcpus) || - !cpumask_intersects (xcpus, parent->effective_xcpus)) +if (cpumask_empty (xcpus))return PERR_INVCPUS ;if (prstate_housekeeping_conflict (new_prs, xcpus))
@@ -1707,13 +1724,16 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, new_prs = (cmd == partcmd_enable) ?PRS_ROOT :PRS_ISOLATED ; }else if (cmd == partcmd_disable) {/* -* May need to add cpus to parent's effective_cpus for -* valid partition root. +* May need to add cpus back to parent's effective_cpus +* (and maybe removed from subpartitions_cpus/isolated_cpus) +* for valid partition root. xcpus may contain CPUs that +* shouldn't be removed from the two global cpumasks. */ - adding = !is_prs_invalid (old_prs) && -cpumask_and (tmp->addmask, xcpus, parent->effective_xcpus); -if (adding) +if (is_partition_valid (cs)) { +cpumask_copy (tmp->addmask, cs->effective_xcpus); + adding = true; subparts_delta--; + } new_prs =PRS_MEMBER ; }else if (newmask) {/*
@@ -1723,6 +1743,7 @@ static int update_parent_effective_cpumask(struct cpuset *cs, int cmd, part_error =PERR_CPUSEMPTY ; goto write_error; } +/* Check newmask again, whether cpus are available for parent/cs */ nocpu |=tasks_nocpu_error (parent, cs, newmask);
@@ -1939,7 +1960,7 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,* 2) All the effective_cpus will be used up and cp * has tasks */ -compute_effective_exclusive_cpumask (cs, new_ecpus); +compute_effective_exclusive_cpumask (cs, new_ecpus,NULL );cpumask_and (new_ecpus, new_ecpus, cpu_active_mask);rcu_read_lock ();
@@ -1947,6 +1968,11 @@ static void compute_partition_effective_cpumask(struct cpuset *cs,if (!is_partition_valid (child))continue ; +/* +* There shouldn't be a remote partition underneath another +* partition root. +*/ +WARN_ON_ONCE (is_remote_partition (child)); child->prs_err =0 ;if (!cpumask_subset (child->effective_xcpus, cs->effective_xcpus))
@@ -2002,32 +2028,39 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,bool remote =is_remote_partition (cp);bool update_parent = false; + old_prs = new_prs = cp->partition_root_state; +/* -* Skip descendent remote partition that acquires CPUs -* directly from top cpuset unless it is cs. +* For child remote partition root (!= cs), we need to call +* remote_cpus_update() if effective_xcpus will be changed. +* Otherwise, we can skip the whole subtree. +* +* remote_cpus_update() will reuse tmp->new_cpus only after +* its value is being processed. */ if (remote && (cp != cs)) { - pos_css =css_rightmost_descendant (pos_css); -continue ; - } +compute_effective_exclusive_cpumask (cp, tmp->new_cpus,NULL ); +if (cpumask_equal (cp->effective_xcpus, tmp->new_cpus)) { + pos_css =css_rightmost_descendant (pos_css); +continue ; + } +rcu_read_unlock (); +remote_cpus_update (cp,NULL , tmp->new_cpus, tmp); +rcu_read_lock (); -/* -* Update effective_xcpus if exclusive_cpus set. -* The case when exclusive_cpus isn't set is handled later. -*/ -if (!cpumask_empty (cp->exclusive_cpus) && (cp != cs)) { -spin_lock_irq (&callback_lock); -compute_effective_exclusive_cpumask (cp,NULL ); -spin_unlock_irq (&callback_lock); +/* Remote partition may be invalidated */ + new_prs = cp->partition_root_state; + remote = (new_prs == old_prs); } - old_prs = new_prs = cp->partition_root_state; -if (remote || (is_partition_valid (parent) && -is_partition_valid (cp))) +if (remote || (is_partition_valid (parent) &&is_partition_valid (cp)))compute_partition_effective_cpumask (cp, tmp->new_cpus);else compute_effective_cpumask (tmp->new_cpus, cp, parent); +if (remote) + goto get_css;/* Ready to update cpuset data */ +/* * A partition with no effective_cpus is allowed as long as * there is no task associated with it. Call
@@ -2047,9 +2080,6 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,if (is_in_v2_mode () && !remote &&cpumask_empty (tmp->new_cpus))cpumask_copy (tmp->new_cpus, parent->effective_cpus); -if (remote) - goto get_css; -/* * Skip the whole subtree if * 1) the cpumask remains the same,
@@ -2110,6 +2140,9 @@ static void update_cpumasks_hier(struct cpuset *cs, struct tmpmasks *tmp,spin_lock_irq (&callback_lock);cpumask_copy (cp->effective_cpus, tmp->new_cpus); cp->partition_root_state = new_prs; +if (!cpumask_empty (cp->exclusive_cpus) && (cp != cs)) +compute_effective_exclusive_cpumask (cp,NULL ,NULL ); +/* * Make sure effective_xcpus is properly set for a valid * partition root.
@@ -2196,7 +2229,14 @@ static void update_sibling_cpumasks(struct cpuset *parent, struct cpuset *cs, parent);if (cpumask_equal (tmp->new_cpus, sibling->effective_cpus))continue ; + }else if (is_remote_partition (sibling)) { +/* +* Change in a sibling cpuset won't affect a remote +* partition root. +*/ +continue ; } +if (!css_tryget_online (&sibling->css))continue ;
@@ -2253,8 +2293,9 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,* trialcs->effective_xcpus is used as a temporary cpumask * for checking validity of the partition root. */ + trialcs->partition_root_state =PRS_MEMBER ;if (!cpumask_empty (trialcs->exclusive_cpus) ||is_partition_valid (cs)) -compute_effective_exclusive_cpumask (trialcs,NULL ); +compute_effective_exclusive_cpumask (trialcs,NULL , cs); }/* Nothing to do if the cpus didn't change */
@@ -2327,19 +2368,13 @@ static int update_cpumask(struct cpuset *cs, struct cpuset *trialcs,* Call remote_cpus_update() to handle valid remote partition */ if (is_remote_partition (cs)) -remote_cpus_update (cs, xcpus, &tmp); +remote_cpus_update (cs,NULL , xcpus, &tmp);else if (invalidate)update_parent_effective_cpumask (cs, partcmd_invalidate,NULL , &tmp);else update_parent_effective_cpumask (cs, partcmd_update, xcpus, &tmp); - }else if (!cpumask_empty (cs->exclusive_cpus)) { -/* -* Use trialcs->effective_cpus as a temp cpumask -*/ -remote_partition_check (cs, trialcs->effective_xcpus, - trialcs->effective_cpus, &tmp); }spin_lock_irq (&callback_lock);
@@ -2391,8 +2426,15 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,if (cpumask_equal (cs->exclusive_cpus, trialcs->exclusive_cpus))return 0 ; -if (*buf) -compute_effective_exclusive_cpumask (trialcs,NULL ); +if (*buf) { + trialcs->partition_root_state =PRS_MEMBER ; +/* +* Reject the change if there is exclusive CPUs conflict with +* the siblings. +*/ +if (compute_effective_exclusive_cpumask (trialcs,NULL , cs)) +return -EINVAL ; + }/* * Check all the descendants in update_cpumasks_hier() if
@@ -2423,8 +2465,8 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,if (invalidate)remote_partition_disable (cs, &tmp);else -remote_cpus_update (cs, trialcs->effective_xcpus, - &tmp); +remote_cpus_update (cs, trialcs->exclusive_cpus, + trialcs->effective_xcpus, &tmp); }else if (invalidate) {update_parent_effective_cpumask (cs, partcmd_invalidate,NULL , &tmp);
@@ -2432,12 +2474,6 @@ static int update_exclusive_cpumask(struct cpuset *cs, struct cpuset *trialcs,update_parent_effective_cpumask (cs, partcmd_update, trialcs->effective_xcpus, &tmp); } - }else if (!cpumask_empty (trialcs->exclusive_cpus)) { -/* -* Use trialcs->effective_cpus as a temp cpumask -*/ -remote_partition_check (cs, trialcs->effective_xcpus, - trialcs->effective_cpus, &tmp); }spin_lock_irq (&callback_lock);cpumask_copy (cs->exclusive_cpus, trialcs->exclusive_cpus);
@@ -2823,17 +2859,6 @@ static int update_prstate(struct cpuset *cs, int new_prs)if (alloc_cpumasks (NULL , &tmpmask))return -ENOMEM ; -/* -* Setup effective_xcpus if not properly set yet, it will be cleared -* later if partition becomes invalid. -*/ -if ((new_prs >0 ) &&cpumask_empty (cs->exclusive_cpus)) { -spin_lock_irq (&callback_lock); -cpumask_and (cs->effective_xcpus, - cs->cpus_allowed, parent->effective_xcpus); -spin_unlock_irq (&callback_lock); - } - err =update_partition_exclusive (cs, new_prs);if (err) goto out;
@@ -3797,7 +3822,6 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)remote_partition_disable (cs, tmp);compute_effective_cpumask (&new_cpus, cs, parent); remote = false; -cpuset_force_rebuild (); }/*
tools/testing/selftests/cgroup/test_cpuset_prs.sh +2 -2
@@ -326,8 +326,8 @@ TEST_MATRIX=( . . X3 P2 . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3 \ A1:P0,A3:P2 3" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ - . . X3 . . 0 A1:0-3,A2:1-3,XA2:3,XA3:3,A3:2-3 \ - A1:P0,A3:P-2" + . . X3 . . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3,XA3:3 \ + A1:P0,A3:P2 3" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ . X4 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:4,XA2:,XA3 \ A1:P0,A3:P-2"
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1113/1376 selftest/cgroup: Update test_cpuset_prs.sh to use | as effective CPUs and state separator
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
[ Upstream commit 65046b5e0ad71990b5a0256710cf050d2d2ab3dd ]
Currently, ',' is used as the cgroup separator of the expected effective CPUs and partition root states in the test matrix. However, ',' can be part of the output of the cpuset.cpus*.effective and cpuset.cpus.isolated files. Change the separator to '|' so that ',' can appear as part of the expected values.
Stable-dep-of: ddabc5dbd262 ("selftests/cgroup: Preserve CPU hotplug write errors")
--- .../selftests/cgroup/test_cpuset_prs.sh | 236 +++++++++--------- 1 file changed, 118 insertions(+), 118 deletions(-)
tools/testing/selftests/cgroup/test_cpuset_prs.sh +118 -118
@@ -207,130 +207,130 @@ TEST_MATRIX=( " C0-1:P1 . . C2-3 S+:C4-5 . . . 0 A1:4-5" " C0-1 . . C2-3:P1 . . . C2 0 " " C0-1 . . C2-3:P1 . . . C4-5 0 B1:4-5" - "C0-3:P1:S+ C2-3:P1 . . . . . . 0 A1:0-1,A2:2-3" - "C0-3:P1:S+ C2-3:P1 . . C1-3 . . . 0 A1:1,A2:2-3" - "C2-3:P1:S+ C3:P1 . . C3 . . . 0 A1:,A2:3 A1:P1,A2:P1" - "C2-3:P1:S+ C3:P1 . . C3 P0 . . 0 A1:3,A2:3 A1:P1,A2:P0" - "C2-3:P1:S+ C2:P1 . . C2-4 . . . 0 A1:3-4,A2:2" - "C2-3:P1:S+ C3:P1 . . C3 . . C0-2 0 A1:,B1:0-2 A1:P1,A2:P1" - "$SETUP_A123_PARTITIONS . C2-3 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1" + "C0-3:P1:S+ C2-3:P1 . . . . . . 0 A1:0-1|A2:2-3" + "C0-3:P1:S+ C2-3:P1 . . C1-3 . . . 0 A1:1|A2:2-3" + "C2-3:P1:S+ C3:P1 . . C3 . . . 0 A1:|A2:3 A1:P1|A2:P1" + "C2-3:P1:S+ C3:P1 . . C3 P0 . . 0 A1:3|A2:3 A1:P1|A2:P0" + "C2-3:P1:S+ C2:P1 . . C2-4 . . . 0 A1:3-4|A2:2" + "C2-3:P1:S+ C3:P1 . . C3 . . C0-2 0 A1:|B1:0-2 A1:P1|A2:P1" + "$SETUP_A123_PARTITIONS . C2-3 . . . 0 A1:|A2:2|A3:3 A1:P1|A2:P1|A3:P1" # CPU offlining cases: - " C0-1 . . C2-3 S+ C4-5 . O2=0 0 A1:0-1,B1:3" - "C0-3:P1:S+ C2-3:P1 . . O2=0 . . . 0 A1:0-1,A2:3" - "C0-3:P1:S+ C2-3:P1 . . O2=0 O2=1 . . 0 A1:0-1,A2:2-3" - "C0-3:P1:S+ C2-3:P1 . . O1=0 . . . 0 A1:0,A2:2-3" - "C0-3:P1:S+ C2-3:P1 . . O1=0 O1=1 . . 0 A1:0-1,A2:2-3" - "C2-3:P1:S+ C3:P1 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P1" - "C2-3:P1:S+ C3:P2 . . O3=0 O3=1 . . 0 A1:2,A2:3 A1:P1,A2:P2" - "C2-3:P1:S+ C3:P1 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P1" - "C2-3:P1:S+ C3:P2 . . O2=0 O2=1 . . 0 A1:2,A2:3 A1:P1,A2:P2" - "C2-3:P1:S+ C3:P1 . . O2=0 . . . 0 A1:,A2:3 A1:P1,A2:P1" - "C2-3:P1:S+ C3:P1 . . O3=0 . . . 0 A1:2,A2: A1:P1,A2:P1" - "C2-3:P1:S+ C3:P1 . . T:O2=0 . . . 0 A1:3,A2:3 A1:P1,A2:P-1" - "C2-3:P1:S+ C3:P1 . . . T:O3=0 . . 0 A1:2,A2:2 A1:P1,A2:P-1" - "$SETUP_A123_PARTITIONS . O1=0 . . . 0 A1:,A2:2,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . O2=0 . . . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . O3=0 . . . 0 A1:1,A2:2,A3: A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . T:O1=0 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1" - "$SETUP_A123_PARTITIONS . . T:O2=0 . . 0 A1:1,A2:3,A3:3 A1:P1,A2:P1,A3:P-1" - "$SETUP_A123_PARTITIONS . . . T:O3=0 . 0 A1:1,A2:2,A3:2 A1:P1,A2:P1,A3:P-1" - "$SETUP_A123_PARTITIONS . T:O1=0 O1=1 . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . . T:O2=0 O2=1 . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . . . T:O3=0 O3=1 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O1=1 . 0 A1:1,A2:,A3:3 A1:P1,A2:P1,A3:P1" - "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O2=1 . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1" + " C0-1 . . C2-3 S+ C4-5 . O2=0 0 A1:0-1|B1:3" + "C0-3:P1:S+ C2-3:P1 . . O2=0 . . . 0 A1:0-1|A2:3" + "C0-3:P1:S+ C2-3:P1 . . O2=0 O2=1 . . 0 A1:0-1|A2:2-3" + "C0-3:P1:S+ C2-3:P1 . . O1=0 . . . 0 A1:0|A2:2-3" + "C0-3:P1:S+ C2-3:P1 . . O1=0 O1=1 . . 0 A1:0-1|A2:2-3" + "C2-3:P1:S+ C3:P1 . . O3=0 O3=1 . . 0 A1:2|A2:3 A1:P1|A2:P1" + "C2-3:P1:S+ C3:P2 . . O3=0 O3=1 . . 0 A1:2|A2:3 A1:P1|A2:P2" + "C2-3:P1:S+ C3:P1 . . O2=0 O2=1 . . 0 A1:2|A2:3 A1:P1|A2:P1" + "C2-3:P1:S+ C3:P2 . . O2=0 O2=1 . . 0 A1:2|A2:3 A1:P1|A2:P2" + "C2-3:P1:S+ C3:P1 . . O2=0 . . . 0 A1:|A2:3 A1:P1|A2:P1" + "C2-3:P1:S+ C3:P1 . . O3=0 . . . 0 A1:2|A2: A1:P1|A2:P1" + "C2-3:P1:S+ C3:P1 . . T:O2=0 . . . 0 A1:3|A2:3 A1:P1|A2:P-1" + "C2-3:P1:S+ C3:P1 . . . T:O3=0 . . 0 A1:2|A2:2 A1:P1|A2:P-1" + "$SETUP_A123_PARTITIONS . O1=0 . . . 0 A1:|A2:2|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . O2=0 . . . 0 A1:1|A2:|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . O3=0 . . . 0 A1:1|A2:2|A3: A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . T:O1=0 . . . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1" + "$SETUP_A123_PARTITIONS . . T:O2=0 . . 0 A1:1|A2:3|A3:3 A1:P1|A2:P1|A3:P-1" + "$SETUP_A123_PARTITIONS . . . T:O3=0 . 0 A1:1|A2:2|A3:2 A1:P1|A2:P1|A3:P-1" + "$SETUP_A123_PARTITIONS . T:O1=0 O1=1 . . 0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . . T:O2=0 O2=1 . 0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . . . T:O3=0 O3=1 0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O1=1 . 0 A1:1|A2:|A3:3 A1:P1|A2:P1|A3:P1" + "$SETUP_A123_PARTITIONS . T:O1=0 O2=0 O2=1 . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1" # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- # # Remote partition and cpuset.cpus.exclusive tests # - " C0-3:S+ C1-3:S+ C2-3 . X2-3 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:2-3" - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3:P2 . . 0 A1:0-1,A2:2-3,A3:2-3 A1:P0,A2:P2 2-3" - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X3:P2 . . 0 A1:0-2,A2:3,A3:3 A1:P0,A2:P2 3" - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2 . 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3" - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:C3 . 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3" - " C0-3:S+ C1-3:S+ C2-3 C2-3 . . . P2 0 A1:0-3,A2:1-3,A3:2-3,B1:2-3 A1:P0,A3:P0,B1:P-2" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 . . . 0 A1:0-3|A2:1-3|A3:2-3|XA1:2-3" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3:P2 . . 0 A1:0-1|A2:2-3|A3:2-3 A1:P0|A2:P2 2-3" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X3:P2 . . 0 A1:0-2|A2:3|A3:3 A1:P0|A2:P2 3" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2 . 0 A1:0-1|A2:1|A3:2-3 A1:P0|A3:P2 2-3" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:C3 . 0 A1:0-1|A2:1|A3:2-3 A1:P0|A3:P2 2-3" + " C0-3:S+ C1-3:S+ C2-3 C2-3 . . . P2 0 A1:0-3|A2:1-3|A3:2-3|B1:2-3 A1:P0|A3:P0|B1:P-2" " C0-3:S+ C1-3:S+ C2-3 C4-5 . . . P2 0 B1:4-5 B1:P2 4-5" - " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2 0 A3:2-3,B1:4 A3:P2,B1:P2 2-4" - " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2:C1-3 P2 0 A3:2-3,B1:4 A3:P2,B1:P2 2-4" - " C0-3:S+ C1-3:S+ C2-3 C4 X1-3 X1-3:P2 P2 . 0 A2:1,A3:2-3 A2:P2,A3:P2 1-3" - " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3,B1:4-5 A3:P2,B1:P2 2-5" - " C4:X0-3:S+ X1-3:S+ X2-3 . . P2 . . 0 A1:4,A2:1-3,A3:1-3 A2:P2 1-3" - " C4:X0-3:S+ X1-3:S+ X2-3 . . . P2 . 0 A1:4,A2:4,A3:2-3 A3:P2 2-3" + " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2 0 A3:2-3|B1:4 A3:P2|B1:P2 2-4" + " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2:C1-3 P2 0 A3:2-3|B1:4 A3:P2|B1:P2 2-4" + " C0-3:S+ C1-3:S+ C2-3 C4 X1-3 X1-3:P2 P2 . 0 A2:1|A3:2-3 A2:P2|A3:P2 1-3" + " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3 X2-3:P2 P2:C4-5 0 A3:2-3|B1:4-5 A3:P2|B1:P2 2-5" + " C4:X0-3:S+ X1-3:S+ X2-3 . . P2 . . 0 A1:4|A2:1-3|A3:1-3 A2:P2 1-3" + " C4:X0-3:S+ X1-3:S+ X2-3 . . . P2 . 0 A1:4|A2:4|A3:2-3 A3:P2 2-3" # Nested remote/local partition tests - " C0-3:S+ C1-3:S+ C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:,A3:2-3,B1:4-5 \ - A1:P0,A2:P1,A3:P2,B1:P1 2-3" - " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:,A3:2-3,B1:4 \ - A1:P0,A2:P1,A3:P2,B1:P1 2-4,2-3" - " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 . P1 0 A1:0-1,A2:2-3,A3:2-3,B1:4 \ - A1:P0,A2:P1,A3:P0,B1:P1" - " C0-3:S+ C1-3:S+ C3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1,A2:2,A3:3,B1:4 \ - A1:P0,A2:P1,A3:P2,B1:P1 2-4,3" - " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X4:P1 . 0 A1:0-1,A2:2-3,A3:4 \ - A1:P0,A2:P2,A3:P1 2-4,2-3" - " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X3-4:P1 . 0 A1:0-1,A2:2,A3:3-4 \ - A1:P0,A2:P2,A3:P1 2" + " C0-3:S+ C1-3:S+ C2-3 C4-5 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:|A3:2-3|B1:4-5 \ + A1:P0|A2:P1|A3:P2|B1:P1 2-3" + " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:|A3:2-3|B1:4 \ + A1:P0|A2:P1|A3:P2|B1:P1 2-4|2-3" + " C0-3:S+ C1-3:S+ C2-3 C4 X2-3 X2-3:P1 . P1 0 A1:0-1|A2:2-3|A3:2-3|B1:4 \ + A1:P0|A2:P1|A3:P0|B1:P1" + " C0-3:S+ C1-3:S+ C3 C4 X2-3 X2-3:P1 P2 P1 0 A1:0-1|A2:2|A3:3|B1:4 \ + A1:P0|A2:P1|A3:P2|B1:P1 2-4|3" + " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X4:P1 . 0 A1:0-1|A2:2-3|A3:4 \ + A1:P0|A2:P2|A3:P1 2-4|2-3" + " C0-4:S+ C1-4:S+ C2-4 . X2-4 X2-4:P2 X3-4:P1 . 0 A1:0-1|A2:2|A3:3-4 \ + A1:P0|A2:P2|A3:P1 2" " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \ - . . X5 . . 0 A1:0-4,A2:1-4,A3:2-4 \ - A1:P0,A2:P-2,A3:P-1" + . . X5 . . 0 A1:0-4|A2:1-4|A3:2-4 \ + A1:P0|A2:P-2|A3:P-1" " C0-4:X2-4:S+ C1-4:X2-4:S+:P2 C2-4:X4:P1 \ - . . . X1 . 0 A1:0-1,A2:2-4,A3:2-4 \ - A1:P0,A2:P2,A3:P-1 2-4" + . . . X1 . 0 A1:0-1|A2:2-4|A3:2-4 \ + A1:P0|A2:P2|A3:P-1 2-4" # Remote partition offline tests - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 . 0 A1:0-1,A2:1,A3:3 A1:P0,A3:P2 2-3" - " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 O2=1 0 A1:0-1,A2:1,A3:2-3 A1:P0,A3:P2 2-3" - " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 P2:O3=0 . 0 A1:0-2,A2:1-2,A3: A1:P0,A3:P2 3" - " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 T:P2:O3=0 . 0 A1:0-2,A2:1-2,A3:1-2 A1:P0,A3:P-2 3," + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 . 0 A1:0-1|A2:1|A3:3 A1:P0|A3:P2 2-3" + " C0-3:S+ C1-3:S+ C2-3 . X2-3 X2-3 X2-3:P2:O2=0 O2=1 0 A1:0-1|A2:1|A3:2-3 A1:P0|A3:P2 2-3" + " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 P2:O3=0 . 0 A1:0-2|A2:1-2|A3: A1:P0|A3:P2 3" + " C0-3:S+ C1-3:S+ C3 . X2-3 X2-3 T:P2:O3=0 . 0 A1:0-2|A2:1-2|A3:1-2 A1:P0|A3:P-2 3|" # An invalidated remote partition cannot self-recover from hotplug - " C0-3:S+ C1-3:S+ C2 . X2-3 X2-3 T:P2:O2=0 O2=1 0 A1:0-3,A2:1-3,A3:2 A1:P0,A3:P-2" + " C0-3:S+ C1-3:S+ C2 . X2-3 X2-3 T:P2:O2=0 O2=1 0 A1:0-3|A2:1-3|A3:2 A1:P0|A3:P-2" # cpus.exclusive.effective clearing test - " C0-3:S+ C1-3:S+ C2 . X2-3:X . . . 0 A1:0-3,A2:1-3,A3:2,XA1:" + " C0-3:S+ C1-3:S+ C2 . X2-3:X . . . 0 A1:0-3|A2:1-3|A3:2|XA1:" # Invalid to valid remote partition transition test - " C0-3:S+ C1-3 . . . X3:P2 . . 0 A1:0-3,A2:1-3,XA2: A2:P-2" + " C0-3:S+ C1-3 . . . X3:P2 . . 0 A1:0-3|A2:1-3|XA2: A2:P-2" " C0-3:S+ C1-3:X3:P2 - . . X2-3 P2 . . 0 A1:0-2,A2:3,XA2:3 A2:P2 3" + . . X2-3 P2 . . 0 A1:0-2|A2:3|XA2:3 A2:P2 3" # Invalid to valid local partition direct transition tests - " C1-3:S+:P2 X4:P2 . . . . . . 0 A1:1-3,XA1:1-3,A2:1-3:XA2: A1:P2,A2:P-2 1-3" - " C1-3:S+:P2 X4:P2 . . . X3:P2 . . 0 A1:1-2,XA1:1-3,A2:3:XA2:3 A1:P2,A2:P2 1-3" - " C0-3:P2 . . C4-6 C0-4 . . . 0 A1:0-4,B1:4-6 A1:P-2,B1:P0" - " C0-3:P2 . . C4-6 C0-4:C0-3 . . . 0 A1:0-3,B1:4-6 A1:P2,B1:P0 0-3" - " C0-3:P2 . . C3-5:C4-5 . . . . 0 A1:0-3,B1:4-5 A1:P2,B1:P0 0-3" + " C1-3:S+:P2 X4:P2 . . . . . . 0 A1:1-3|XA1:1-3|A2:1-3:XA2: A1:P2|A2:P-2 1-3" + " C1-3:S+:P2 X4:P2 . . . X3:P2 . . 0 A1:1-2|XA1:1-3|A2:3:XA2:3 A1:P2|A2:P2 1-3" + " C0-3:P2 . . C4-6 C0-4 . . . 0 A1:0-4|B1:4-6 A1:P-2|B1:P0" + " C0-3:P2 . . C4-6 C0-4:C0-3 . . . 0 A1:0-3|B1:4-6 A1:P2|B1:P0 0-3" + " C0-3:P2 . . C3-5:C4-5 . . . . 0 A1:0-3|B1:4-5 A1:P2|B1:P0 0-3" # Local partition invalidation tests " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ - . . . . . 0 A1:1,A2:2,A3:3 A1:P2,A2:P2,A3:P2 1-3" + . . . . . 0 A1:1|A2:2|A3:3 A1:P2|A2:P2|A3:P2 1-3" " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ - . . X4 . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3" + . . X4 . . 0 A1:1-3|A2:1-3|A3:2-3|XA2:|XA3: A1:P2|A2:P-2|A3:P-2 1-3" " C0-3:X1-3:S+:P2 C1-3:X2-3:S+:P2 C2-3:X3:P2 \ - . . C4:X . . 0 A1:1-3,A2:1-3,A3:2-3,XA2:,XA3: A1:P2,A2:P-2,A3:P-2 1-3" + . . C4:X . . 0 A1:1-3|A2:1-3|A3:2-3|XA2:|XA3: A1:P2|A2:P-2|A3:P-2 1-3" # Local partition CPU change tests - " C0-5:S+:P2 C4-5:S+:P1 . . . C3-5 . . 0 A1:0-2,A2:3-5 A1:P2,A2:P1 0-2" - " C0-5:S+:P2 C4-5:S+:P1 . . C1-5 . . . 0 A1:1-3,A2:4-5 A1:P2,A2:P1 1-3" + " C0-5:S+:P2 C4-5:S+:P1 . . . C3-5 . . 0 A1:0-2|A2:3-5 A1:P2|A2:P1 0-2" + " C0-5:S+:P2 C4-5:S+:P1 . . C1-5 . . . 0 A1:1-3|A2:4-5 A1:P2|A2:P1 1-3" # cpus_allowed/exclusive_cpus update tests " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ - . X:C4 . P2 . 0 A1:4,A2:4,XA2:,XA3:,A3:4 \ - A1:P0,A3:P-2" + . X:C4 . P2 . 0 A1:4|A2:4|XA2:|XA3:|A3:4 \ + A1:P0|A3:P-2" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ - . X1 . P2 . 0 A1:0-3,A2:1-3,XA1:1,XA2:,XA3:,A3:2-3 \ - A1:P0,A3:P-2" + . X1 . P2 . 0 A1:0-3|A2:1-3|XA1:1|XA2:|XA3:|A3:2-3 \ + A1:P0|A3:P-2" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3 \ - . . X3 P2 . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3 \ - A1:P0,A3:P2 3" + . . X3 P2 . 0 A1:0-2|A2:1-2|XA2:3|XA3:3|A3:3 \ + A1:P0|A3:P2 3" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ - . . X3 . . 0 A1:0-2,A2:1-2,XA2:3,XA3:3,A3:3,XA3:3 \ - A1:P0,A3:P2 3" + . . X3 . . 0 A1:0-2|A2:1-2|XA2:3|XA3:3|A3:3|XA3:3 \ + A1:P0|A3:P2 3" " C0-3:X2-3:S+ C1-3:X2-3:S+ C2-3:X2-3:P2 \ - . X4 . . . 0 A1:0-3,A2:1-3,A3:2-3,XA1:4,XA2:,XA3 \ - A1:P0,A3:P-2" + . X4 . . . 0 A1:0-3|A2:1-3|A3:2-3|XA1:4|XA2:|XA3 \ + A1:P0|A3:P-2" # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ --------
@@ -339,68 +339,68 @@ TEST_MATRIX=( # # Adding CPUs to partition root that are not in parent's # cpuset.cpus is allowed, but those extra CPUs are ignored. - "C2-3:P1:S+ C3:P1 . . . C2-4 . . 0 A1:,A2:2-3 A1:P1,A2:P1" + "C2-3:P1:S+ C3:P1 . . . C2-4 . . 0 A1:|A2:2-3 A1:P1|A2:P1" # Taking away all CPUs from parent or itself if there are tasks # will make the partition invalid. - "C2-3:P1:S+ C3:P1 . . T C2-3 . . 0 A1:2-3,A2:2-3 A1:P1,A2:P-1" - " C3:P1:S+ C3 . . T P1 . . 0 A1:3,A2:3 A1:P1,A2:P-1" - "$SETUP_A123_PARTITIONS . T:C2-3 . . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P-1,A3:P-1" - "$SETUP_A123_PARTITIONS . T:C2-3:C1-3 . . . 0 A1:1,A2:2,A3:3 A1:P1,A2:P1,A3:P1" + "C2-3:P1:S+ C3:P1 . . T C2-3 . . 0 A1:2-3|A2:2-3 A1:P1|A2:P-1" + " C3:P1:S+ C3 . . T P1 . . 0 A1:3|A2:3 A1:P1|A2:P-1" + "$SETUP_A123_PARTITIONS . T:C2-3 . . . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P-1|A3:P-1" + "$SETUP_A123_PARTITIONS . T:C2-3:C1-3 . . . 0 A1:1|A2:2|A3:3 A1:P1|A2:P1|A3:P1" # Changing a partition root to member makes child partitions invalid - "C2-3:P1:S+ C3:P1 . . P0 . . . 0 A1:2-3,A2:3 A1:P0,A2:P-1" - "$SETUP_A123_PARTITIONS . C2-3 P0 . . 0 A1:2-3,A2:2-3,A3:3 A1:P1,A2:P0,A3:P-1" + "C2-3:P1:S+ C3:P1 . . P0 . . . 0 A1:2-3|A2:3 A1:P0|A2:P-1" + "$SETUP_A123_PARTITIONS . C2-3 P0 . . 0 A1:2-3|A2:2-3|A3:3 A1:P1|A2:P0|A3:P-1" # cpuset.cpus can contains cpus not in parent's cpuset.cpus as long # as they overlap. - "C2-3:P1:S+ . . . . C3-4:P1 . . 0 A1:2,A2:3 A1:P1,A2:P1" + "C2-3:P1:S+ . . . . C3-4:P1 . . 0 A1:2|A2:3 A1:P1|A2:P1" # Deletion of CPUs distributed to child cgroup is allowed. - "C0-1:P1:S+ C1 . C2-3 C4-5 . . . 0 A1:4-5,A2:4-5" + "C0-1:P1:S+ C1 . C2-3 C4-5 . . . 0 A1:4-5|A2:4-5" # To become a valid partition root, cpuset.cpus must overlap parent's # cpuset.cpus. - " C0-1:P1 . . C2-3 S+ C4-5:P1 . . 0 A1:0-1,A2:0-1 A1:P1,A2:P-1" + " C0-1:P1 . . C2-3 S+ C4-5:P1 . . 0 A1:0-1|A2:0-1 A1:P1|A2:P-1" # Enabling partition with child cpusets is allowed - " C0-1:S+ C1 . C2-3 P1 . . . 0 A1:0-1,A2:1 A1:P1" + " C0-1:S+ C1 . C2-3 P1 . . . 0 A1:0-1|A2:1 A1:P1" - # A partition root with non-partition root parent is invalid, but it + # A partition root with non-partition root parent is invalid| but it # can be made valid if its parent becomes a partition root too. - " C0-1:S+ C1 . C2-3 . P2 . . 0 A1:0-1,A2:1 A1:P0,A2:P-2" - " C0-1:S+ C1:P2 . C2-3 P1 . . . 0 A1:0,A2:1 A1:P1,A2:P2" + " C0-1:S+ C1 . C2-3 . P2 . . 0 A1:0-1|A2:1 A1:P0|A2:P-2" + " C0-1:S+ C1:P2 . C2-3 P1 . . . 0 A1:0|A2:1 A1:P1|A2:P2" # A non-exclusive cpuset.cpus change will invalidate partition and its siblings - " C0-1:P1 . . C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P0" - " C0-1:P1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P-1,B1:P-1" - " C0-1 . . P1:C2-3 C0-2 . . . 0 A1:0-2,B1:2-3 A1:P0,B1:P-1" + " C0-1:P1 . . C2-3 C0-2 . . . 0 A1:0-2|B1:2-3 A1:P-1|B1:P0" + " C0-1:P1 . . P1:C2-3 C0-2 . . . 0 A1:0-2|B1:2-3 A1:P-1|B1:P-1" + " C0-1 . . P1:C2-3 C0-2 . . . 0 A1:0-2|B1:2-3 A1:P0|B1:P-1" # cpuset.cpus can overlap with sibling cpuset.cpus.exclusive but not subsumed by it - " C0-3 . . C4-5 X5 . . . 0 A1:0-3,B1:4-5" + " C0-3 . . C4-5 X5 . . . 0 A1:0-3|B1:4-5" # Child partition root that try to take all CPUs from parent partition # with tasks will remain invalid. - " C1-4:P1:S+ P1 . . . . . . 0 A1:1-4,A2:1-4 A1:P1,A2:P-1" - " C1-4:P1:S+ P1 . . . C1-4 . . 0 A1,A2:1-4 A1:P1,A2:P1" - " C1-4:P1:S+ P1 . . T C1-4 . . 0 A1:1-4,A2:1-4 A1:P1,A2:P-1" + " C1-4:P1:S+ P1 . . . . . . 0 A1:1-4|A2:1-4 A1:P1|A2:P-1" + " C1-4:P1:S+ P1 . . . C1-4 . . 0 A1|A2:1-4 A1:P1|A2:P1" + " C1-4:P1:S+ P1 . . T C1-4 . . 0 A1:1-4|A2:1-4 A1:P1|A2:P-1" # Clearing of cpuset.cpus with a preset cpuset.cpus.exclusive shouldn't # affect cpuset.cpus.exclusive.effective. - " C1-4:X3:S+ C1:X3 . . . C . . 0 A2:1-4,XA2:3" + " C1-4:X3:S+ C1:X3 . . . C . . 0 A2:1-4|XA2:3" # old-A1 old-A2 old-A3 old-B1 new-A1 new-A2 new-A3 new-B1 fail ECPUs Pstate ISOLCPUS # ------ ------ ------ ------ ------ ------ ------ ------ ---- ----- ------ -------- # Failure cases: # A task cannot be added to a partition with no cpu - "C2-3:P1:S+ C3:P1 . . O2=0:T . . . 1 A1:,A2:3 A1:P1,A2:P1" + "C2-3:P1:S+ C3:P1 . . O2=0:T . . . 1 A1:|A2:3 A1:P1|A2:P1" # Changes to cpuset.cpus.exclusive that violate exclusivity rule is rejected - " C0-3 . . C4-5 X0-3 . . X3-5 1 A1:0-3,B1:4-5" + " C0-3 . . C4-5 X0-3 . . X3-5 1 A1:0-3|B1:4-5" # cpuset.cpus cannot be a subset of sibling cpuset.cpus.exclusive - " C0-3 . . C4-5 X3-5 . . . 1 A1:0-3,B1:4-5" + " C0-3 . . C4-5 X3-5 . . . 1 A1:0-3|B1:4-5" ) #
@@ -567,12 +567,12 @@ dump_states() # # Check effective cpus -# $1 - check string, format: <cgroup>:<cpu-list>[,<cgroup>:<cpu-list>]* +# $1 - check string, format: <cgroup>:<cpu-list>[|<cgroup>:<cpu-list>]* # check_effective_cpus() { CHK_STR=$1 - for CHK in $(echo $CHK_STR | sed -e "s/,/ /g") + for CHK in $(echo $CHK_STR | sed -e "s/|/ /g") do set -- $(echo $CHK | sed -e "s/:/ /g") CGRP=$1
@@ -593,12 +593,12 @@ check_effective_cpus() # # Check cgroup states -# $1 - check string, format: <cgroup>:<state>[,<cgroup>:<state>]* +# $1 - check string, format: <cgroup>:<state>[|<cgroup>:<state>]* # check_cgroup_states() { CHK_STR=$1 - for CHK in $(echo $CHK_STR | sed -e "s/,/ /g") + for CHK in $(echo $CHK_STR | sed -e "s/|/ /g") do set -- $(echo $CHK | sed -e "s/:/ /g") CGRP=$1
@@ -674,9 +674,9 @@ check_isolcpus() then EXPECT_VAL= EXPECT_VAL2= - elif [[ $(expr $EXPECT_VAL : ".*,.*") > 0 ]] + elif [[ $(expr $EXPECT_VAL : ".*|.*") > 0 ]] then - set -- $(echo $EXPECT_VAL | sed -e "s/,/ /g") + set -- $(echo $EXPECT_VAL | sed -e "s/|/ /g") EXPECT_VAL=$1 EXPECT_VAL2=$2 else
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1114/1376 selftest/cgroup: Clean up and restructure test_cpuset_prs.sh
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Waiman Long <[email protected]>
[ Upstream commit b2b2b4d058b776be0168b4ea46ed84cfb0f884e9 ]
Cleaning up the test_cpuset_prs.sh script and restructure some of the functions so that a new test matrix with a different cgroup directory structure can be added in the next patch.
Stable-dep-of: ddabc5dbd262 ("selftests/cgroup: Preserve CPU hotplug write errors")
--- .../selftests/cgroup/test_cpuset_prs.sh | 257 +++++++++++------- 1 file changed, 156 insertions(+), 101 deletions(-)
tools/testing/selftests/cgroup/test_cpuset_prs.sh +156 -101
@@ -88,22 +88,30 @@ echo "" > test/cpuset.cpus # If isolated CPUs have been reserved at boot time (as shown in # cpuset.cpus.isolated), these isolated CPUs should be outside of CPUs 0-8 # that will be used by this script for testing purpose. If not, some of -# the tests may fail incorrectly. These pre-isolated CPUs should stay in -# an isolated state throughout the testing process for now. +# the tests may fail incorrectly. Wait a bit and retry again just in case +# these isolated CPUs are leftover from previous run and have just been +# cleaned up earlier in this script. +# +# These pre-isolated CPUs should stay in an isolated state throughout the +# testing process for now. # BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated) +[[ -n "$BOOT_ISOLCPUS" ]] && { + sleep 0.5 + BOOT_ISOLCPUS=$(cat $CGROUP2/cpuset.cpus.isolated) +} if [[ -n "$BOOT_ISOLCPUS" ]] then [[ $(echo $BOOT_ISOLCPUS | sed -e "s/[,-].*//") -le 8 ]] && skip_test "Pre-isolated CPUs ($BOOT_ISOLCPUS) overlap CPUs to be tested" echo "Pre-isolated CPUs: $BOOT_ISOLCPUS" fi + cleanup() { online_cpus cd $CGROUP2 - rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1 - rmdir test > /dev/null 2>&1 + rmdir A1/A2/A3 A1/A2 A1 B1 test/A1 test/B1 test > /dev/null 2>&1 [[ -n "$SCHED_DEBUG" ]] && echo "$SCHED_DEBUG" > /sys/kernel/debug/sched/verbose }
@@ -173,14 +181,22 @@ test_add_proc() # # Cgroup test hierarchy # -# root -- A1 -- A2 -- A3 -# +- B1 +# root +# | +# +------+------+ +# | | +# A1 B1 +# | +# A2 +# | +# A3 # # P<v> = set cpus.partition (0:member, 1:root, 2:isolated) # C<l> = add cpu-list to cpuset.cpus # X<l> = add cpu-list to cpuset.cpus.exclusive # S<p> = use prefix in subtree_control # T = put a task into cgroup +# CX<l> = add cpu-list to both cpuset.cpus and cpuset.cpus.exclusive # O<c>=<v> = Write <v> to CPU online file of <c> # # ECPUs - effective CPUs of cpusets
@@ -453,25 +469,26 @@ set_ctrl_state() PFILE=$CGRP/cpuset.cpus.partition CFILE=$CGRP/cpuset.cpus XFILE=$CGRP/cpuset.cpus.exclusive - S=$(expr substr $CMD 1 1) - if [[ $S = S ]] - then - PREFIX=${CMD#?} + case $CMD in + S*) PREFIX=${CMD#?} COMM="echo ${PREFIX}${CTRL} > $SFILE" eval $COMM $REDIRECT - elif [[ $S = X ]] - then + ;; + X*) CPUS=${CMD#?} COMM="echo $CPUS > $XFILE" eval $COMM $REDIRECT - elif [[ $S = C ]] - then - CPUS=${CMD#?} + ;; + CX*) + CPUS=${CMD#??} + COMM="echo $CPUS > $CFILE; echo $CPUS > $XFILE" + eval $COMM $REDIRECT + ;; + C*) CPUS=${CMD#?} COMM="echo $CPUS > $CFILE" eval $COMM $REDIRECT - elif [[ $S = P ]] - then - VAL=${CMD#?} + ;; + P*) VAL=${CMD#?} case $VAL in 0) VAL=member ;;
@@ -486,15 +503,17 @@ set_ctrl_state() esac COMM="echo $VAL > $PFILE" eval $COMM $REDIRECT - elif [[ $S = O ]] - then - VAL=${CMD#?} + ;; + O*) VAL=${CMD#?} write_cpu_online $VAL - elif [[ $S = T ]] - then - COMM="echo 0 > $TFILE" + ;; + T*) COMM="echo 0 > $TFILE" eval $COMM $REDIRECT - fi + ;; + *) echo "Unknown command: $CMD" + exit 1 + ;; + esac RET=$? [[ $RET -ne 0 ]] && { [[ -n "$SHOWERR" ]] && {
@@ -532,21 +551,18 @@ online_cpus() } # -# Return 1 if the list of effective cpus isn't the same as the initial list. +# Remove all the test cgroup directories # reset_cgroup_states() { echo 0 > $CGROUP2/cgroup.procs online_cpus - rmdir A1/A2/A3 A1/A2 A1 B1 > /dev/null 2>&1 - pause 0.02 - set_ctrl_state . R- - pause 0.01 + rmdir $RESET_LIST > /dev/null 2>&1 } dump_states() { - for DIR in . A1 A1/A2 A1/A2/A3 B1 + for DIR in $CGROUP_LIST do CPUS=$DIR/cpuset.cpus ECPUS=$DIR/cpuset.cpus.effective
@@ -565,6 +581,21 @@ dump_states() done } +# +# Set the actual cgroup directory into $CGRP_DIR +# $1 - cgroup name +# +set_cgroup_dir() +{ + CGRP_DIR=$1 + [[ $CGRP_DIR = A2 ]] && CGRP_DIR=A1/A2 + [[ $CGRP_DIR = A3 ]] && CGRP_DIR=A1/A2/A3 + [[ $CGRP_DIR = c11 ]] && CGRP_DIR=p1/c11 + [[ $CGRP_DIR = c12 ]] && CGRP_DIR=p1/c12 + [[ $CGRP_DIR = c21 ]] && CGRP_DIR=p2/c21 + [[ $CGRP_DIR = c22 ]] && CGRP_DIR=p2/c22 +} + # # Check effective cpus # $1 - check string, format: <cgroup>:<cpu-list>[|<cgroup>:<cpu-list>]*
@@ -576,7 +607,8 @@ check_effective_cpus() do set -- $(echo $CHK | sed -e "s/:/ /g") CGRP=$1 - CPUS=$2 + EXPECTED_CPUS=$2 + ACTUAL_CPUS= if [[ $CGRP = X* ]] then CGRP=${CGRP#X}
@@ -584,10 +616,10 @@ check_effective_cpus() else FILE=cpuset.cpus.effective fi - [[ $CGRP = A2 ]] && CGRP=A1/A2 - [[ $CGRP = A3 ]] && CGRP=A1/A2/A3 - [[ -e $CGRP/$FILE ]] || return 1 - [[ $CPUS = $(cat $CGRP/$FILE) ]] || return 1 + set_cgroup_dir $CGRP + [[ -e $CGRP_DIR/$FILE ]] || return 1 + ACTUAL_CPUS=$(cat $CGRP_DIR/$FILE) + [[ $EXPECTED_CPUS = $ACTUAL_CPUS ]] || return 1 done }
@@ -602,23 +634,21 @@ check_cgroup_states() do set -- $(echo $CHK | sed -e "s/:/ /g") CGRP=$1 - CGRP_DIR=$CGRP - STATE=$2 + EXPECTED_STATE=$2 FILE= - EVAL=$(expr substr $STATE 2 2) - [[ $CGRP = A2 ]] && CGRP_DIR=A1/A2 - [[ $CGRP = A3 ]] && CGRP_DIR=A1/A2/A3 + EVAL=$(expr substr $EXPECTED_STATE 2 2) - case $STATE in + set_cgroup_dir $CGRP + case $EXPECTED_STATE in P*) FILE=$CGRP_DIR/cpuset.cpus.partition ;; - *) echo "Unknown state: $STATE!" + *) echo "Unknown state: $EXPECTED_STATE!" exit 1 ;; esac - VAL=$(cat $FILE) + ACTUAL_STATE=$(cat $FILE) - case "$VAL" in + case "$ACTUAL_STATE" in member) VAL=0 ;; root) VAL=1
@@ -642,7 +672,7 @@ check_cgroup_states() [[ $VAL -eq 1 && $VERBOSE -gt 0 ]] && { DOMS=$(cat $CGRP_DIR/cpuset.cpus.effective) [[ -n "$DOMS" ]] && - echo " [$CGRP] sched-domain: $DOMS" > $CONSOLE + echo " [$CGRP_DIR] sched-domain: $DOMS" > $CONSOLE } done return 0
@@ -665,22 +695,22 @@ check_cgroup_states() # check_isolcpus() { - EXPECT_VAL=$1 - ISOLCPUS= + EXPECTED_ISOLCPUS=$1 + ISCPUS=${CGROUP2}/cpuset.cpus.isolated + ISOLCPUS=$(cat $ISCPUS) LASTISOLCPU= SCHED_DOMAINS=/sys/kernel/debug/sched/domains - ISCPUS=${CGROUP2}/cpuset.cpus.isolated - if [[ $EXPECT_VAL = . ]] + if [[ $EXPECTED_ISOLCPUS = . ]] then - EXPECT_VAL= - EXPECT_VAL2= - elif [[ $(expr $EXPECT_VAL : ".*|.*") > 0 ]] + EXPECTED_ISOLCPUS= + EXPECTED_SDOMAIN= + elif [[ $(expr $EXPECTED_ISOLCPUS : ".*|.*") > 0 ]] then - set -- $(echo $EXPECT_VAL | sed -e "s/|/ /g") - EXPECT_VAL=$1 - EXPECT_VAL2=$2 + set -- $(echo $EXPECTED_ISOLCPUS | sed -e "s/|/ /g") + EXPECTED_ISOLCPUS=$2 + EXPECTED_SDOMAIN=$1 else - EXPECT_VAL2=$EXPECT_VAL + EXPECTED_SDOMAIN=$EXPECTED_ISOLCPUS fi #
@@ -689,20 +719,21 @@ check_isolcpus() # to make appending those CPUs easier. # [[ -n "$BOOT_ISOLCPUS" ]] && { - EXPECT_VAL=${EXPECT_VAL:+${EXPECT_VAL},}${BOOT_ISOLCPUS} - EXPECT_VAL2=${EXPECT_VAL2:+${EXPECT_VAL2},}${BOOT_ISOLCPUS} + EXPECTED_ISOLCPUS=${EXPECTED_ISOLCPUS:+${EXPECTED_ISOLCPUS},}${BOOT_ISOLCPUS} + EXPECTED_SDOMAIN=${EXPECTED_SDOMAIN:+${EXPECTED_SDOMAIN},}${BOOT_ISOLCPUS} } # # Check cpuset.cpus.isolated cpumask # - [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && { + [[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && { # Take a 50ms pause and try again pause 0.05 ISOLCPUS=$(cat $ISCPUS) } - [[ "$EXPECT_VAL2" != "$ISOLCPUS" ]] && return 1 + [[ "$EXPECTED_ISOLCPUS" != "$ISOLCPUS" ]] && return 1 ISOLCPUS= + EXPECTED_ISOLCPUS=$EXPECTED_SDOMAIN # # Use the sched domain in debugfs to check isolated CPUs, if available
@@ -736,7 +767,7 @@ check_isolcpus() done [[ "$ISOLCPUS" = *- ]] && ISOLCPUS=${ISOLCPUS}$LASTISOLCPU - [[ "$EXPECT_VAL" = "$ISOLCPUS" ]] + [[ "$EXPECTED_SDOMAIN" = "$ISOLCPUS" ]] } test_fail()
@@ -773,6 +804,63 @@ null_isolcpus_check() exit 1 } +# +# Check state transition test result +# $1 - Test number +# $2 - Expected effective CPU values +# $3 - Expected partition states +# $4 - Expected isolated CPUs +# +check_test_results() +{ + _NR=$1 + _ECPUS="$2" + _PSTATES="$3" + _ISOLCPUS="$4" + + [[ -n "$_ECPUS" && "$_ECPUS" != . ]] && { + check_effective_cpus $_ECPUS + [[ $? -ne 0 ]] && test_fail $_NR "effective CPU" \ + "Cgroup $CGRP: expected $EXPECTED_CPUS, got $ACTUAL_CPUS" + } + + [[ -n "$_PSTATES" && "$_PSTATES" != . ]] && { + check_cgroup_states $_PSTATES + [[ $? -ne 0 ]] && test_fail $_NR states \ + "Cgroup $CGRP: expected $EXPECTED_STATE, got $ACTUAL_STATE" + } + + # Compare the expected isolated CPUs with the actual ones, + # if available + [[ -n "$_ISOLCPUS" ]] && { + check_isolcpus $_ISOLCPUS + [[ $? -ne 0 ]] && { + [[ -n "$BOOT_ISOLCPUS" ]] && _ISOLCPUS=${_ISOLCPUS},${BOOT_ISOLCPUS} + test_fail $_NR "isolated CPU" \ + "Expect $_ISOLCPUS, get $ISOLCPUS instead" + } + } + reset_cgroup_states + # + # Check to see if effective cpu list changes + # + _NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective) + RETRY=0 + while [[ $_NEWLIST != $CPULIST && $RETRY -lt 8 ]] + do + # Wait a bit longer & recheck a few times + pause 0.02 + ((RETRY++)) + _NEWLIST=$(cat $CGROUP2/cpuset.cpus.effective) + done + [[ $_NEWLIST != $CPULIST ]] && { + echo "Effective cpus changed to $_NEWLIST after test $_NR!" + exit 1 + } + null_isolcpus_check + [[ $VERBOSE -gt 0 ]] && echo "Test $I done." +} + # # Run cpuset state transition test # $1 - test matrix name
@@ -785,6 +873,8 @@ run_state_test() { TEST=$1 CONTROLLER=cpuset + CGROUP_LIST=". A1 A1/A2 A1/A2/A3 B1" + RESET_LIST="A1/A2/A3 A1/A2 A1 B1" I=0 eval CNT="\${#$TEST[@]}"
@@ -824,45 +914,7 @@ run_state_test() [[ $RETVAL -ne $RESULT ]] && test_fail $I result - [[ -n "$ECPUS" && "$ECPUS" != . ]] && { - check_effective_cpus $ECPUS - [[ $? -ne 0 ]] && test_fail $I "effective CPU" - } - - [[ -n "$STATES" && "$STATES" != . ]] && { - check_cgroup_states $STATES - [[ $? -ne 0 ]] && test_fail $I states - } - - # Compare the expected isolated CPUs with the actual ones, - # if available - [[ -n "$ICPUS" ]] && { - check_isolcpus $ICPUS - [[ $? -ne 0 ]] && { - [[ -n "$BOOT_ISOLCPUS" ]] && ICPUS=${ICPUS},${BOOT_ISOLCPUS} - test_fail $I "isolated CPU" \ - "Expect $ICPUS, get $ISOLCPUS instead" - } - } - reset_cgroup_states - # - # Check to see if effective cpu list changes - # - NEWLIST=$(cat cpuset.cpus.effective) - RETRY=0 - while [[ $NEWLIST != $CPULIST && $RETRY -lt 8 ]] - do - # Wait a bit longer & recheck a few times - pause 0.02 - ((RETRY++)) - NEWLIST=$(cat cpuset.cpus.effective) - done - [[ $NEWLIST != $CPULIST ]] && { - echo "Effective cpus changed to $NEWLIST after test $I!" - exit 1 - } - null_isolcpus_check - [[ $VERBOSE -gt 0 ]] && echo "Test $I done." + check_test_results $I "$ECPUS" "$STATES" "$ICPUS" ((I++)) done echo "All $I tests of $TEST PASSED."
@@ -932,6 +984,7 @@ test_isolated() echo $$ > $CGROUP2/cgroup.procs [[ -d A1 ]] && rmdir A1 null_isolcpus_check + pause 0.05 } #
@@ -997,6 +1050,8 @@ test_inotify() else echo "Inotify test PASSED" fi + echo member > cpuset.cpus.partition + echo "" > cpuset.cpus } trap cleanup 0 2 3 6
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1115/1376 selftests/cgroup: Preserve CPU hotplug write errors
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Rui Qi <[email protected]>
[ Upstream commit ddabc5dbd262f3ca981e679ddae99ed7a9adb279 ]
The cpuset partition root state selftest checks several CPU hotplug transitions. If writing to a CPU online file fails, the helper still runs pause afterwards and returns the status of pause instead of the failed write.
This hides the real hotplug failure and can make later checks run against expectations for a transition that never happened. Move the write before the bookkeeping and return when it fails, so callers can observe the hotplug error and the test does not record a CPU as offline unless the offline operation actually succeeded.
Also change the O* command handler in set_ctrl_state() to use "eval $COMM $REDIRECT" like all other handlers. The previous version set COMM but still called write_cpu_online directly, bypassing the redirect that captures stderr for error reporting.
Changes since v1:
- Use eval $COMM $REDIRECT in the O* handler instead of calling write_cpu_online directly (Waiman Long)
--- tools/testing/selftests/cgroup/test_cpuset_prs.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
tools/testing/selftests/cgroup/test_cpuset_prs.sh +3 -2
@@ -428,6 +428,7 @@ write_cpu_online() CPU=${1%=*} VAL=${1#*=} CPUFILE=//sys/devices/system/cpu/cpu${CPU}/online + echo $VAL > $CPUFILE || return 1 if [[ $VAL -eq 0 ]] then OFFLINE_CPUS="$OFFLINE_CPUS $CPU"
@@ -437,7 +438,6 @@ write_cpu_online() sort | uniq -u) } fi - echo $VAL > $CPUFILE pause 0.05 }
@@ -505,7 +505,8 @@ set_ctrl_state() eval $COMM $REDIRECT ;; O*) VAL=${CMD#?} - write_cpu_online $VAL + COMM="write_cpu_online $VAL" + eval $COMM $REDIRECT ;; T*) COMM="echo 0 > $TFILE" eval $COMM $REDIRECT
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1116/1376 x86/pkeys: Fix pkey_alloc() return value when pkeys are not supported
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bijan Tabatabai <[email protected]>
[ Upstream commit dee87e09b0dd63da9b1e1876167ccae37842dfd0 ]
The man page for pkey_alloc(2) specifies that it should return -1 with the errno set to ENOSPC when pkeys are not supported [1]. However, on x86 pkey_alloc() sets errno to EINVAL when called for the first time on a CPU that does not support pkeys.
The root cause of this is the x86 implementation of mm_pkey_alloc() not directly checking if pkeys are supported. It only checks if all the pkeys have been allocated by comparing the allocation map against all_pkeys_mask. When OSPKE is not enabled, init_new_context() skips the initialization of the allocation map, leaving it as 0, while all_pkeys_mask is 1. mm_pkey_alloc() interprets this as there being a pkey available and it returns pkey 0. Then, pkey_alloc() fails with -EINVAL from arch_set_user_pkey_access() instead of returning -ENOSPC. Subsequent calls to pkey_alloc() do return -ENOSPC because pkey 0 is left marked as allocated.
Change mm_pkey_alloc() to directly check if OSPKE is enabled, and return -1 if it is not, which causes pkey_alloc() to return -ENOSPC. The arm64 and powerpc implementations of mm_pkey_alloc() already do this check.
[1] https://man7.org/linux/man-pages/man2/pkey_alloc.2.html
[ dhansen: use arch_pkeys_enabled() to follow arm ]
--- arch/x86/include/asm/pkeys.h | 3 +++ 1 file changed, 3 insertions(+)
arch/x86/include/asm/pkeys.h +3 -0
@@ -89,6 +89,9 @@ int mm_pkey_alloc(struct mm_struct *mm)u16 all_pkeys_mask = ((1U <<arch_max_pkey ()) -1 );int ret; +if (!arch_pkeys_enabled ()) +return -1 ; +/* * Are we out of pkeys? We must handle this specially * because ffz() behavior is undefined if there are no
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1117/1376 bpftool: Fix double close in map dump
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yuan Chen <[email protected]>
[ Upstream commit 259d60f5bfa41056fe01cbf2ba3f6f0331865a16 ]
map_dump() closes the map fd in its error path, and do_dump() then closes the same fd again after a successful dump. Closing an already closed fd leaves errno set to EBADF, which poisons later errno checks such as the batch file read check in do_batch(). Let do_dump() own the fd and remove the close from map_dump().
The same double-close pattern exists in do_show_subset(): both show_map_close_json() and show_map_close_plain() already close the fd, so drop the extra close() there as well.
Also propagate the error when bpf_map_get_info_by_fd() fails on a subsequent map in do_dump(): set err = -1 before breaking out of the loop, so a later failure is not silently hidden after an earlier iteration succeeded.
--- tools/bpf/bpftool/map.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
tools/bpf/bpftool/map.c +1 -3
@@ -659,8 +659,6 @@ static int do_show_subset(int argc, char **argv)show_map_close_json (fds[i], &info); else show_map_close_plain(fds[i], &info); - -close (fds[i]); }if (json_output && nb_fds >1 )jsonw_end_array (json_wtr);/* root array */
@@ -892,7 +890,6 @@ map_dump(int fd, struct bpf_map_info *info, json_writer_t *wtr, exit_free:free (key);free (value); -close (fd);free_map_kv_btf (btf);return err;
@@ -941,6 +938,7 @@ static int do_dump(int argc, char **argv)for (i =0 ; i < nb_fds; i++) {if (bpf_map_get_info_by_fd (fds[i], &info, &len)) {p_err ("can't get map info: %s" ,strerror (errno)); + err =-1 ;break ; } err =map_dump (fds[i], &info, wtr, nb_fds >1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1118/1376 ocfs2: fix circular locking dependency in ocfs2_init_acl()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Krystian Kaniewski <[email protected]>
[ Upstream commit bd7c05fb4a4776dff5a87b19008d28458647d15d ]
A lockdep warning indicates a circular locking dependency between &oi->ip_xattr_sem and &journal->j_trans_barrier:
WARNING: possible circular locking dependency detected is trying to acquire lock:
(&oi->ip_xattr_sem){++++}-{4:4}, at: ocfs2_init_acl+0x2fd/0x7e0 fs/ocfs2/acl.c:367
but task is already holding lock:
(&journal->j_trans_barrier){.+.+}-{4:4}, at: ocfs2_start_trans+0x3ab/0x700 fs/ocfs2/journal.c:369
The deadlock involves two code paths: Path 1 (setxattr) where ocfs2_xattr_set() acquires ip_xattr_sem (write) and then starts a transaction, which acquires j_trans_barrier (read); and Path 2 (mkdir/mknod) where ocfs2_mknod() starts a transaction (j_trans_barrier read) and then calls ocfs2_init_acl(), which attempts to acquire ip_xattr_sem (read) on the parent directory to retrieve the default ACL.
Because rw_semaphores are subject to writer priority, a pending writer on j_trans_barrier (e.g., the journal commit thread) can cause Path 1 to block, while Path 2 is blocked waiting for Path 1 to release ip_xattr_sem.
The patch fixes the lock ordering by precomputing the ACL state before starting the OCFS2 transaction, while preserving POSIX ACL storage semantics and the existing inode/security initialization order. By reading the parent directory's default ACL and preparing the new inode's ACLs outside the transaction, ip_xattr_sem is always acquired before j_trans_barrier.
struct ocfs2_acl_state encapsulates the prepared ACL state, while ocfs2_acl_init_prepare() and ocfs2_acl_init_release() avoid code duplication between ocfs2_mknod() and ocfs2_init_security_and_acl(). ocfs2_calc_xattr_init() and ocfs2_init_acl() use this precomputed state, removing internal ip_xattr_sem acquisition and redundant disk reads.
Additionally, remove the ip_xattr_sem acquisition from ocfs2_xattr_set_handle(). This function is only used while initializing a new inode that has not yet been inserted into the inode hash or attached to a dentry, meaning there is no risk of concurrent access and the lock is unnecessary.
--- fs/ocfs2/acl.c | 135 ++++++++++++++++++++++++++++++----------------- fs/ocfs2/acl.h | 19 +++++-- fs/ocfs2/namei.c | 17 ++++-- fs/ocfs2/xattr.c | 86 ++++++++++++++++++------------ fs/ocfs2/xattr.h | 9 ++-- 5 files changed, 173 insertions(+), 93 deletions(-)
fs/ocfs2/acl.c +88 -47
@@ -109,8 +109,7 @@ static void *ocfs2_acl_to_xattr(const struct posix_acl *acl, size_t *size)return ocfs2_acl; } -static struct posix_acl *ocfs2_get_acl_nolock (struct inode *inode, -int type, +static struct posix_acl *ocfs2_get_acl_nolock (struct inode *inode,int type,struct buffer_head *di_bh) {int name_index;
@@ -348,63 +347,105 @@ int ocfs2_acl_chmod(struct inode *inode, struct buffer_head *bh)* Initialize the ACLs of a new inode. If parent directory has default ACL, * then clone to new inode. Called from ocfs2_mknod. */ -int ocfs2_init_acl (handle_t *handle, -struct inode *inode, -struct inode *dir, -struct buffer_head *di_bh, -struct buffer_head *dir_bh, -struct ocfs2_alloc_context *meta_ac, -struct ocfs2_alloc_context *data_ac) +void ocfs2_acl_init_release (struct ocfs2_acl_state *state) +{ +posix_acl_release (state->default_acl); +posix_acl_release (state->acl); + state->default_acl =NULL ; + state->acl =NULL ; +} + +int ocfs2_acl_init_prepare (struct inode *inode,struct inode *dir, +struct buffer_head *dir_bh, +struct ocfs2_acl_state *state) {struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); -struct posix_acl *acl =NULL ; -int ret =0 , ret2; -umode_t mode; - -if (!S_ISLNK (inode->i_mode)) { -if (osb->s_mount_opt &OCFS2_MOUNT_POSIX_ACL ) { -down_read (&OCFS2_I (dir)->ip_xattr_sem); - acl =ocfs2_get_acl_nolock (dir,ACL_TYPE_DEFAULT , - dir_bh); -up_read (&OCFS2_I (dir)->ip_xattr_sem); -if (IS_ERR (acl)) -return PTR_ERR (acl); +int ret =0 ; + + state->default_acl =NULL ; + state->acl =NULL ; + state->mode = inode->i_mode; + +if (S_ISLNK (inode->i_mode)) +return 0 ; + +if (osb->s_mount_opt &OCFS2_MOUNT_POSIX_ACL ) { +down_read (&OCFS2_I (dir)->ip_xattr_sem); + state->default_acl = +ocfs2_get_acl_nolock (dir,ACL_TYPE_DEFAULT , dir_bh); +up_read (&OCFS2_I (dir)->ip_xattr_sem); +if (IS_ERR (state->default_acl)) { + ret =PTR_ERR (state->default_acl); + state->default_acl =NULL ; +return ret; } -if (!acl) { - mode = inode->i_mode & ~current_umask (); - ret =ocfs2_acl_set_mode (inode, di_bh, handle, mode); -if (ret) { -mlog_errno (ret); +if (state->default_acl) { + state->acl =posix_acl_dup (state->default_acl); +if (!state->acl) { + ret = -ENOMEM ; goto cleanup; } + ret =__posix_acl_create (&state->acl,GFP_NOFS , + &state->mode); +if (ret <0 ) + goto cleanup; +if (ret ==0 ) { +posix_acl_release (state->acl); + state->acl =NULL ; + } +if (!S_ISDIR (inode->i_mode)) { +posix_acl_release (state->default_acl); + state->default_acl =NULL ; + } + }else { + state->mode &= ~current_umask (); } + }else { + state->mode &= ~current_umask (); } -if ((osb->s_mount_opt &OCFS2_MOUNT_POSIX_ACL ) && acl) { -if (S_ISDIR (inode->i_mode)) { + +return 0 ; +cleanup: +ocfs2_acl_init_release (state); +return ret; +} + +int ocfs2_init_acl (handle_t *handle,struct inode *inode, +struct buffer_head *di_bh, +struct ocfs2_alloc_context *meta_ac, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_acl_state *state) +{ +struct ocfs2_super *osb =OCFS2_SB (inode->i_sb); +int ret =0 ; + +if (S_ISLNK (inode->i_mode)) +return 0 ; + +if (osb->s_mount_opt &OCFS2_MOUNT_POSIX_ACL ) { +if (S_ISDIR (inode->i_mode) && state->default_acl) { ret =ocfs2_set_acl (handle, inode, di_bh, -ACL_TYPE_DEFAULT , acl, - meta_ac, data_ac); +ACL_TYPE_DEFAULT , + state->default_acl, meta_ac, + data_ac);if (ret) - goto cleanup; +return ret; } - mode = inode->i_mode; - ret =__posix_acl_create (&acl,GFP_NOFS , &mode); -if (ret <0 ) -return ret; + } - ret2 =ocfs2_acl_set_mode (inode, di_bh, handle, mode); -if (ret2) { -mlog_errno (ret2); - ret = ret2; - goto cleanup; - } -if (ret >0 ) { - ret =ocfs2_set_acl (handle, inode, - di_bh,ACL_TYPE_ACCESS , - acl, meta_ac, data_ac); + ret =ocfs2_acl_set_mode (inode, di_bh, handle, state->mode); +if (ret) { +mlog_errno (ret); +return ret; + } + +if (osb->s_mount_opt &OCFS2_MOUNT_POSIX_ACL ) { +if (state->acl) { + ret =ocfs2_set_acl (handle, inode, di_bh, +ACL_TYPE_ACCESS , state->acl, + meta_ac, data_ac); } } -cleanup: -posix_acl_release (acl); +return ret; }
fs/ocfs2/acl.h +15 -4
@@ -20,9 +20,20 @@ struct posix_acl *ocfs2_iop_get_acl(struct inode *inode, int type, bool rcu);int ocfs2_iop_set_acl (struct mnt_idmap *idmap,struct dentry *dentry,struct posix_acl *acl,int type);extern int ocfs2_acl_chmod (struct inode *,struct buffer_head *); -extern int ocfs2_init_acl (handle_t *,struct inode *,struct inode *, -struct buffer_head *,struct buffer_head *, -struct ocfs2_alloc_context *, -struct ocfs2_alloc_context *); +struct ocfs2_acl_state { +struct posix_acl *default_acl; +struct posix_acl *acl; +umode_t mode; +}; + +int ocfs2_acl_init_prepare (struct inode *inode,struct inode *dir, +struct buffer_head *dir_bh, +struct ocfs2_acl_state *state); +void ocfs2_acl_init_release (struct ocfs2_acl_state *state); +int ocfs2_init_acl (handle_t *handle,struct inode *inode, +struct buffer_head *di_bh, +struct ocfs2_alloc_context *meta_ac, +struct ocfs2_alloc_context *data_ac, +struct ocfs2_acl_state *state);#endif /* OCFS2_ACL_H */
fs/ocfs2/namei.c +12 -5
@@ -252,6 +252,7 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,sigset_t oldset;int did_block_signals =0 ;struct ocfs2_dentry_lock *dl =NULL ; +struct ocfs2_acl_state acl_state = {0 }; trace_ocfs2_mknod(dir, dentry, dentry->d_name.len, dentry->d_name.name, (unsigned long long)OCFS2_I(dir)->ip_blkno,
@@ -326,10 +327,14 @@ static int ocfs2_mknod(struct mnt_idmap *idmap, } } + status =ocfs2_acl_init_prepare (inode, dir, parent_fe_bh, &acl_state); +if (status <0 ) + goto leave; +/* calculate meta data/clusters for setting security and acl xattr */ - status =ocfs2_calc_xattr_init (dir, parent_fe_bh, mode, - &si, &want_clusters, - &xattr_credits, &want_meta); + status =ocfs2_calc_xattr_init (dir, mode, &si, &want_clusters, + &xattr_credits, &want_meta, + &acl_state);if (status <0 ) {mlog_errno (status); goto leave;
@@ -407,8 +412,8 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,inc_nlink (dir); } - status =ocfs2_init_acl (handle, inode, dir, new_fe_bh, parent_fe_bh, - meta_ac, data_ac); + status =ocfs2_init_acl (handle, inode, new_fe_bh, meta_ac, data_ac, + &acl_state);if (status <0 ) {mlog_errno (status);
@@ -473,6 +478,8 @@ static int ocfs2_mknod(struct mnt_idmap *idmap,brelse (parent_fe_bh);kfree (si.value); +ocfs2_acl_init_release (&acl_state); +ocfs2_free_dir_lookup_result (&lookup);if (inode_ac)
fs/ocfs2/xattr.c +52 -34
138 lines not on this page · raw message
fs/ocfs2/xattr.h +6 -3
@@ -55,9 +55,12 @@ int ocfs2_init_security_set(handle_t *, struct inode *,int ocfs2_calc_security_init (struct inode *,struct ocfs2_security_xattr_info *,int *,int *,struct ocfs2_alloc_context **); -int ocfs2_calc_xattr_init (struct inode *,struct buffer_head *, -umode_t ,struct ocfs2_security_xattr_info *, -int *,int *,int *); + +struct ocfs2_acl_state ; +int ocfs2_calc_xattr_init (struct inode *dir,umode_t mode, +struct ocfs2_security_xattr_info *si, +int *want_clusters,int *xattr_credits, +int *want_meta,struct ocfs2_acl_state *acl_state);/* * xattrs can live inside an inode, as part of an external xattr block,
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1119/1376 Squashfs: check block offset is not negative
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Phillip Lougher <[email protected]>
[ Upstream commit e300eb5002925b29be803d2661af07266cfa267e ]
If a negative offset is read off disk (for example the offset into the decompressed fragment block), this will cause squashfs_copy_data() to perform an out of bounds access.
Fix by checking if offset is negative, and returning 0. This matches existing behaviour where an offset beyond the block returns 0 bytes copied.
To trigger this out of bounds access requires a crafted Squashfs filesystem and CAP_SYS_ADMIN to mount it. Unprivileged users will not be able to mount such a filesystem, but once mounted, an unprivileged user can trigger the out of bounds access by reading the crafted file with the negative offset.
--- fs/squashfs/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/squashfs/cache.c +1 -1
@@ -295,7 +295,7 @@ int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry, {int remaining = length; -if (length ==0 ) +if (length ==0 || offset <0 )return 0 ;else if (buffer ==NULL )return min (length, entry->length - offset);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1120/1376 selftests/bpf: Fix for veristat file/prog filters processing
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Eduard Zingerman <[email protected]>
[ Upstream commit 98d309ec8189fd91698d1a72946c3d888270c57e ]
At the moment veristat filtering behaves unexpectedly for the following filter expression:
-f !file/prog
The expression rejects all programs with name 'prog', and all programs in a file with name 'file'. This commit fixes the expression to exclude only a program 'prog' from a file 'file'. Additionally, the commit makes empty filters like '-f ""' or '-f "/"' and error. Here is the filtering behaviour compared old versus new:
| filter | file | prog | old verdict | new verdict | |----------+------+------+-------------+-------------| | !foo | foo | bar | skipped | skipped | | !foo | bar | foo | skipped | skipped | | !foo | bar | bar | processed | processed | | !foo/bar | foo | bar | skipped | skipped | | !foo/bar | foo | buz | skipped | processed | (!) | !foo/bar | bar | bar | skipped | processed | (!) | !foo/ | foo | bar | skipped | skipped | | !foo/ | bar | bar | processed | processed | | !/bar | foo | bar | skipped | skipped | | !/bar | foo | foo | processed | processed | | !/ | foo | bar | processed | error | (!) | ! | foo | bar | processed | error | (!) |----------+------+------+-------------+-------------| | foo | foo | bar | processed | processed | | foo | bar | foo | processed | processed | | foo | bar | bar | skipped | skipped | | foo/bar | foo | bar | processed | processed | | foo/bar | foo | buz | skipped | skipped | | foo/bar | bar | bar | skipped | skipped | | foo/ | foo | bar | processed | processed | | foo/ | bar | bar | skipped | skipped | | /bar | foo | bar | processed | processed | | /bar | foo | foo | skipped | skipped | | / | foo | bar | processed | error | (!) | | foo | bar | skipped | error | (!)
--- tools/testing/selftests/bpf/veristat.c | 76 +++++++++++++++++--------- 1 file changed, 49 insertions(+), 27 deletions(-)
tools/testing/selftests/bpf/veristat.c +49 -27
@@ -405,6 +405,40 @@ static bool is_bpf_obj_file(const char *path) {return err ==0 ; } +/* Exact filter match */ +static bool name_filter_matches (struct filter *f,const char *filename,const char *prog_name) +{ +if (f->any_glob) +return glob_matches (filename, f->any_glob) || + (prog_name &&glob_matches (prog_name, f->any_glob)); +if (f->file_glob && f->prog_glob) +return prog_name && +glob_matches (filename, f->file_glob) && +glob_matches (prog_name, f->prog_glob); +if (f->file_glob) +return glob_matches (filename, f->file_glob); +if (f->prog_glob) +return prog_name &&glob_matches (prog_name, f->prog_glob); +return false; +} + +/* Check if the filter does not outright reject the file name */ +static bool name_filter_may_match (struct filter *f,const char *filename) +{ +if (f->file_glob) +return glob_matches (filename, f->file_glob); +/* +* If we don't know program name yet, any_glob filter +* has to assume that current BPF object file might be +* relevant; we'll check again later on after opening +* BPF object file, at which point program name will +* be known finally. +*/ +if (f->any_glob || f->prog_glob) +return true; +return false; +} +static bool should_process_file_prog (const char *filename,const char *prog_name) {struct filter *f;
@@ -412,16 +446,7 @@ static bool should_process_file_prog(const char *filename, const char *prog_namefor (i =0 ; i < env.deny_filter_cnt; i++) { f = &env.deny_filters[i]; -if (f->kind !=FILTER_NAME ) -continue ; - -if (f->any_glob &&glob_matches (filename, f->any_glob)) -return false; -if (f->any_glob && prog_name &&glob_matches (prog_name, f->any_glob)) -return false; -if (f->file_glob &&glob_matches (filename, f->file_glob)) -return false; -if (f->prog_glob && prog_name &&glob_matches (prog_name, f->prog_glob)) +if (f->kind ==FILTER_NAME &&name_filter_matches (f, filename, prog_name))return false; }
@@ -431,24 +456,15 @@ static bool should_process_file_prog(const char *filename, const char *prog_namecontinue ; allow_cnt++; -if (f->any_glob) { -if (glob_matches (filename, f->any_glob)) -return true; -/* If we don't know program name yet, any_glob filter -* has to assume that current BPF object file might be -* relevant; we'll check again later on after opening -* BPF object file, at which point program name will -* be known finally. -*/ -if (!prog_name ||glob_matches (prog_name, f->any_glob)) -return true; - }else { -if (f->file_glob && !glob_matches (filename, f->file_glob)) -continue ; -if (f->prog_glob && prog_name && !glob_matches (prog_name, f->prog_glob)) -continue ; +if (prog_name &&name_filter_matches (f, filename, prog_name)) +return true; +/* +* If there is no prog_name and the file name is not blocked by +* the filter, allow to open the file. Afterwards there would be +* a second refining query with prog_name set. +*/ +if (!prog_name &&name_filter_may_match (f, filename))return true; - } }/* if there are no file/prog name allow filters, allow all progs,
@@ -594,6 +610,12 @@ static int append_filter(struct filter **filters, int *cnt, const char *str) } } +if ((!f->any_glob && !f->file_glob && !f->prog_glob) || + (f->any_glob &&strcmp (f->any_glob,"" ) ==0 )) { +fprintf (stderr,"Invalid filter: '%s'\n" , str); +return -EINVAL ; + } + *cnt +=1 ;return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1121/1376 scsi: mpt3sas: Avoid freeing unallocated PCIe SGL buffers
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Chandrakanth Patil <[email protected]>
[ Upstream commit b9f679dfe629004b593f018df33b330d799bcee4 ]
_base_release_memory_pools() unconditionally frees every ioc->pcie_sg_lookup[] entry, including ones the setup loop never allocated after a partial failure, causing a "bad dma" warning on debug kernels or a NULL pointer dereference otherwise.
--- drivers/scsi/mpt3sas/mpt3sas_base.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/scsi/mpt3sas/mpt3sas_base.c +2 -0
@@ -5854,6 +5854,8 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)if (ioc->pcie_sgl_dma_pool) {for (i =0 ; i < ioc->scsiio_depth; i++) { +if (!ioc->pcie_sg_lookup[i].pcie_sgl) +continue ;dma_pool_free (ioc->pcie_sgl_dma_pool, ioc->pcie_sg_lookup[i].pcie_sgl, ioc->pcie_sg_lookup[i].pcie_sgl_dma);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1122/1376 scsi: ufs: core: Remove redundant host_lock calls around UTMRLDBR
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Avri Altman <[email protected]>
[ Upstream commit 2b314e182caabd32f656ddba22432a7749572013 ]
There is no need to serialize single read/write calls to the host controller registers. Remove the redundant host_lock calls that protect access to the task management doorbell register: UTMRLDBR.
Stable-dep-of: 720d8b2f2457 ("scsi: ufs: core: Set task state before io_schedule_timeout()")
--- drivers/ufs/core/ufshcd.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
drivers/ufs/core/ufshcd.c +6 -8
@@ -1264,11 +1264,13 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba,static u32 ufshcd_pending_cmds (struct ufs_hba *hba) {const struct scsi_device *sdev; +unsigned long flags;u32 pending =0 ; -lockdep_assert_held (hba->host->host_lock); +spin_lock_irqsave (hba->host->host_lock, flags);__shost_for_each_device (sdev, hba->host) pending +=sbitmap_weight (&sdev->budget_map); +spin_unlock_irqrestore (hba->host->host_lock, flags);return pending; }
@@ -1282,7 +1284,6 @@ static u32 ufshcd_pending_cmds(struct ufs_hba *hba)static int ufshcd_wait_for_doorbell_clr (struct ufs_hba *hba,u64 wait_timeout_us) { -unsigned long flags;int ret =0 ;u32 tm_doorbell;u32 tr_pending;
@@ -1290,7 +1291,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,ktime_t start;ufshcd_hold (hba); -spin_lock_irqsave (hba->host->host_lock, flags);/* * Wait for all the outstanding tasks/transfer requests. * Verify by checking the doorbell registers are clear.
@@ -1311,7 +1311,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,break ; } -spin_unlock_irqrestore (hba->host->host_lock, flags);io_schedule_timeout (msecs_to_jiffies (20 ));if (ktime_to_us (ktime_sub (ktime_get (), start)) > wait_timeout_us) {
@@ -1323,7 +1322,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,*/ do_last_check = true; } -spin_lock_irqsave (hba->host->host_lock, flags); }while (tm_doorbell || tr_pending);if (timeout) {
@@ -1333,7 +1331,6 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba, ret = -EBUSY ; } out: -spin_unlock_irqrestore (hba->host->host_lock, flags);ufshcd_release (hba);return ret; }
@@ -7127,12 +7124,13 @@ static int __ufshcd_issue_tm_cmd(struct ufs_hba *hba,memcpy (hba->utmrdl_base_addr + task_tag, treq,sizeof (*treq));ufshcd_vops_setup_task_mgmt (hba, task_tag, tm_function); -/* send command to the controller */ __set_bit (task_tag, &hba->outstanding_tasks); -ufshcd_writel (hba,1 << task_tag,REG_UTP_TASK_REQ_DOOR_BELL );spin_unlock_irqrestore (host->host_lock, flags); +/* send command to the controller */ +ufshcd_writel (hba,1 << task_tag,REG_UTP_TASK_REQ_DOOR_BELL ); +ufshcd_add_tm_upiu_trace (hba, task_tag,UFS_TM_SEND );/* wait until the task management command is completed */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1123/1376 scsi: ufs: core: Set task state before io_schedule_timeout()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Bart Van Assche <[email protected]>
[ Upstream commit 720d8b2f2457f4962ca353dd90395e2660d9a10d ]
Set the task state to TASK_UNINTERRUPTIBLE before calling io_schedule_timeout() in ufshcd_wait_for_pending_cmds(). Without setting the task state, io_schedule_timeout() returns immediately because the task state remains TASK_RUNNING. This results in a busy loop that wastes CPU cycles.
--- drivers/ufs/core/ufshcd.c | 1 + 1 file changed, 1 insertion(+)
drivers/ufs/core/ufshcd.c +1 -0
@@ -1311,6 +1311,7 @@ static int ufshcd_wait_for_doorbell_clr(struct ufs_hba *hba,break ; } +__set_current_state (TASK_UNINTERRUPTIBLE );io_schedule_timeout (msecs_to_jiffies (20 ));if (ktime_to_us (ktime_sub (ktime_get (), start)) > wait_timeout_us) {
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1124/1376 page_pool: rename __page_pool_release_page_dma() to __page_pool_release_netmem_dma()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Byungchul Park <[email protected]>
[ Upstream commit 4ad125ae380bf0bd55d6e1efb492d2ad4f867030 ]
Now that __page_pool_release_page_dma() is for releasing netmem, not struct page, rename it to __page_pool_release_netmem_dma() to reflect what it does.
Stable-dep-of: 24ef02f934ee ("net: page_pool: fix UAF in __page_pool_release_netmem_dma on xa_cmpxchg race")
--- net/core/page_pool.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
net/core/page_pool.c +4 -4
@@ -726,8 +726,8 @@ void page_pool_clear_pp_info(netmem_ref netmem)netmem_set_pp (netmem,NULL ); } -static __always_inline void __page_pool_release_page_dma(struct page_pool *pool, -netmem_ref netmem) +static __always_inline void __page_pool_release_netmem_dma(struct page_pool *pool, +netmem_ref netmem) {dma_addr_t dma;
@@ -763,7 +763,7 @@ void page_pool_return_page(struct page_pool *pool, netmem_ref netmem)if (static_branch_unlikely (&page_pool_mem_providers) && pool->mp_ops) put = pool->mp_ops->release_netmem (pool, netmem);else -__page_pool_release_page_dma (pool, netmem); +__page_pool_release_netmem_dma (pool, netmem);/* This may be the last page returned, releasing the pool, so * it is not safe to reference pool afterwards.
@@ -1138,7 +1138,7 @@ static void page_pool_scrub(struct page_pool *pool) }xa_for_each (&pool->dma_mapped, id, ptr) -__page_pool_release_page_dma (pool,page_to_netmem (ptr)); +__page_pool_release_netmem_dma (pool,page_to_netmem ((struct page *)ptr)); }/* No more consumers should exist, but producers could still
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1125/1376 net: page_pool: fix UAF in __page_pool_release_netmem_dma on xa_cmpxchg race
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jijie Shao <[email protected]>
[ Upstream commit 24ef02f934eeb48830cff6b739abc3c62b1d107b ]
This bug was discovered while testing the hns3 driver under channel reconfiguration (ethtool -L / ethtool -G) with iperf3 traffic on arm64. The race is intermittently triggered when page_pool_destroy() runs page_pool_scrub() concurrently with page return via page_pool_put_netmem() on a different CPU. A WARN in page_pool_clear_pp_info() surfaced the dangling DMA index bits left by the cmpxchg loser, which led to the investigation.
page_pool_scrub() iterates pool->dma_mapped via xa_for_each() with no page ref held. __page_pool_release_netmem_dma() currently reads and writes netmem fields (dma_addr, DMA index bits in pp_magic) after xa_cmpxchg() returns. The unref path calls put_page() unconditionally regardless of the cmpxchg outcome; when it loses the cmpxchg, it still frees the page before the scrub winner finishes these netmem accesses, so scrub touches a freed page -- a Use-After-Free.
Fix this by splitting the DMA release into two functions:
1. __page_pool_unmap_netmem_dma() caches dma_addr before xa_cmpxchg(), does the cmpxchg to remove the DMA mapping, and calls dma_unmap on the cached address. It never touches netmem fields after the cmpxchg, making it safe for the scrub path which holds no page ref.
2. __page_pool_release_netmem_dma() wraps the above and additionally clears dma_addr and DMA index bits in netmem fields. This is safe only when the caller holds a page ref, so it is used by the return path (page_pool_return_netmem).
The scrub path calls __page_pool_unmap_netmem_dma() directly; the return path calls __page_pool_release_netmem_dma().
--- net/core/page_pool.c | 66 +++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 31 deletions(-)
net/core/page_pool.c +35 -31
@@ -500,29 +500,40 @@ static int page_pool_register_dma_index(struct page_pool *pool,return err; } -static int page_pool_release_dma_index (struct page_pool *pool, -netmem_ref netmem) +static void __page_pool_unmap_netmem_dma (struct page_pool *pool, +netmem_ref netmem) {struct page *old, *page =netmem_to_page (netmem);unsigned long id; +dma_addr_t dma; -if (unlikely (!PP_DMA_INDEX_BITS )) -return 0 ; - - id =netmem_get_dma_index (netmem); -if (!id) -return -1 ; +if (!pool->dma_map) +return ; -if (in_softirq ()) - old =xa_cmpxchg (&pool->dma_mapped, id, page,NULL ,0 ); -else - old =xa_cmpxchg_bh (&pool->dma_mapped, id, page,NULL ,0 ); -if (old != page) -return -1 ; +/* Cache dma_addr before xa_cmpxchg. The scrub path holds no page ref; +* the unref path calls put_page() regardless of cmpxchg outcome, so +* after the cmpxchg we cannot safely touch netmem fields. +*/ + dma =page_pool_get_dma_addr_netmem (netmem); -netmem_set_dma_index (netmem,0 ); +if (likely (PP_DMA_INDEX_BITS )) { + id =netmem_get_dma_index (netmem); +if (!id) +return ; + +if (in_softirq ()) + old =xa_cmpxchg (&pool->dma_mapped, + id, page,NULL ,0 ); +else + old =xa_cmpxchg_bh (&pool->dma_mapped, + id, page,NULL ,0 ); +if (old != page) +return ; + } -return 0 ; +dma_unmap_page_attrs (pool->p.dev, dma, +PAGE_SIZE << pool->p.order, pool->p.dma_dir, +DMA_ATTR_SKIP_CPU_SYNC |DMA_ATTR_WEAK_ORDERING ); }static bool page_pool_dma_map (struct page_pool *pool,netmem_ref netmem,gfp_t gfp)
@@ -729,24 +740,16 @@ void page_pool_clear_pp_info(netmem_ref netmem) static __always_inline void __page_pool_release_netmem_dma(struct page_pool *pool,netmem_ref netmem) { -dma_addr_t dma; - +/* Caller must hold a page ref: __page_pool_unmap_netmem_dma() is +* safe without a ref, but the field clears below require it. +*/ if (!pool->dma_map) -/* Always account for inflight pages, even if we didn't -* map them -*/ return ; -if (page_pool_release_dma_index (pool, netmem)) -return ; - - dma =page_pool_get_dma_addr_netmem (netmem); - -/* When page is unmapped, it cannot be returned to our pool */ -dma_unmap_page_attrs (pool->p.dev, dma, -PAGE_SIZE << pool->p.order, pool->p.dma_dir, -DMA_ATTR_SKIP_CPU_SYNC |DMA_ATTR_WEAK_ORDERING ); +__page_pool_unmap_netmem_dma (pool, netmem);page_pool_set_dma_addr_netmem (netmem,0 ); +if (likely (PP_DMA_INDEX_BITS )) +netmem_set_dma_index (netmem,0 ); }/* Disconnects a page (from a page_pool). API users can have a need
@@ -1137,8 +1140,9 @@ static void page_pool_scrub(struct page_pool *pool)synchronize_net (); } +/* No page ref, dma-unmap only. */ xa_for_each (&pool->dma_mapped, id, ptr) -__page_pool_release_netmem_dma (pool,page_to_netmem ((struct page *)ptr)); +__page_pool_unmap_netmem_dma (pool,page_to_netmem ((struct page *)ptr)); }/* No more consumers should exist, but producers could still
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1126/1376 fs/ntfs3: fix integer overflow in MFT cluster validation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
[ Upstream commit c510c63873103a5da6a498fe537bdb5d6f8d03a2 ]
In ntfs_init_from_boot(), the boot sector's MFT cluster numbers are validated against the volume size with:
if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) goto out;
mlcn and mlcn2 are u64 fields read directly from the boot sector. sct_per_clst is bounded above by 4096 (true_sectors_per_clst() plus the is_power_of_2() check below it), but the multiplication is done in u64 and wraps when mlcn (or mlcn2) is large enough -- e.g. mlcn near 2^62 with sct_per_clst == 4 wraps to 0, which compares below any non-zero 'sectors', so the check is bypassed and the malformed record is accepted.
The accepted mlcn is then used unchanged in
sbi->mft.lbo = mlcn << cluster_bits;
In practice the resulting reads fail at the block layer (sb_bread() returns NULL via grow_buffers()'s check_mul_overflow() guard), so today this manifests as mount failing in odd places rather than as something more dangerous, but the validation step is still wrong and there is no reason for callers to rely on the block layer to catch a value that should never have been accepted in the first place.
Use check_mul_overflow() to compute the two sector positions and fail the mount if either multiplication wraps; this preserves the existing semantics (mlcn * sct_per_clst >= sectors) instead of switching to division (mlcn >= sectors / sct_per_clst), which would tighten the check at edge cases where 'sectors' is not a multiple of sct_per_clst. The check_*_overflow() style is the one ntfs3 already uses for similar on-disk arithmetic in fs/ntfs3/run.c.
--- fs/ntfs3/super.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-)
fs/ntfs3/super.c +11 -2
@@ -57,6 +57,7 @@#include <linux/minmax.h> #include <linux/module.h> #include <linux/nls.h> +#include <linux/overflow.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/statfs.h>
@@ -877,7 +878,7 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size,struct ntfs_sb_info *sbi = sb->s_fs_info;int err;u32 mb, gb, boot_sector_size, sct_per_clst, record_size; -u64 sectors, clusters, mlcn, mlcn2, dev_size0; +u64 sectors, clusters, mlcn, mlcn2, mft_pos, mft2_pos, dev_size0;struct NTFS_BOOT *boot;struct buffer_head *bh;struct MFT_REC *rec;
@@ -946,7 +947,15 @@ static int ntfs_init_from_boot(struct super_block *sb, u32 sector_size, mlcn2 =le64_to_cpu (boot->mft2_clst); sectors =le64_to_cpu (boot->sectors_per_volume); -if (mlcn * sct_per_clst >= sectors || mlcn2 * sct_per_clst >= sectors) { +/* +* Convert mlcn/mlcn2 to sector positions before comparing with +* 'sectors'. All three are u64 values that come from the boot +* sector, so use check_mul_overflow() to keep a wraparound from +* silently bypassing the comparison. +*/ +if (check_mul_overflow (mlcn, (u64 )sct_per_clst, &mft_pos) || +check_mul_overflow (mlcn2, (u64 )sct_per_clst, &mft2_pos) || + mft_pos >= sectors || mft2_pos >= sectors) { ntfs_err( sb, "%s: start of MFT 0x%llx (0x%llx) is out of volume 0x%llx.",
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1127/1376 fs/ntfs3: reject out-of-range evcn in mi_enum_attr()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhan Xusheng <[email protected]>
[ Upstream commit 20fd9f64c0050658f2031e6bd5d552c6f0c8f7e3 ]
In mi_enum_attr(), the start/end VCN validation for non-resident attributes is:
if (svcn > evcn + 1) goto out;
When evcn is U64_MAX the "evcn + 1" expression wraps to 0 and any svcn passes the check. For evcn values close to U64_MAX (but not equal to it) the right-hand side is still a meaningless near-wrap upper bound, so a malformed on-disk attribute with svcn == 0 and evcn near U64_MAX can pass mi_enum_attr() unrejected.
VCN (virtual cluster number) is a cluster index, so any valid evcn is bounded by the volume's total cluster count, which ntfs3 holds in sbi->used.bitmap.nbits (set up in ntfs_init_from_boot() before any caller of mi_enum_attr() runs). Reject evcn values that fall outside this range.
However, an empty non-resident attribute (no allocated clusters) is legitimately encoded with svcn == 0 and evcn == -1 (U64_MAX), e.g. via attr->nres.evcn = cpu_to_le64((u64)vcn - 1) with vcn == 0. That sentinel must keep passing, so exclude evcn == U64_MAX from the range check. The existing "svcn > evcn + 1" test still tolerates the sentinel ("0 > 0" is false) and continues to require svcn == 0 for it, while the range check rejects every other out-of-range evcn and thereby also defuses the "evcn + 1" wraparound.
svcn does not need its own bound: once evcn < nbits, "svcn > evcn + 1" implies svcn <= nbits.
--- fs/ntfs3/record.c | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-)
fs/ntfs3/record.c +31 -3
@@ -202,7 +202,7 @@ struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi,u32 used =le32_to_cpu (rec->used);u32 t32, off, asize, prev_type;u16 t16; -u64 data_size, alloc_size, tot_size; +u64 svcn, evcn, data_size, alloc_size, tot_size;if (!attr) {u32 total =le32_to_cpu (rec->total);
@@ -310,10 +310,38 @@ struct ATTRIB *mi_enum_attr(struct ntfs_inode *ni, struct mft_inode *mi,if (t32 &&le16_to_cpu (attr->name_off) + t32 > t16) goto out; -/* Check start/end vcn. */ -if (le64_to_cpu (attr->nres.svcn) >le64_to_cpu (attr->nres.evcn) +1 ) +/* +* Check start/end vcn. svcn == 0 with evcn == -1 (U64_MAX) is the +* sentinel for an empty non-resident attribute (no allocated +* clusters) and must be accepted: "svcn > evcn + 1" tolerates it, +* since "(u64)-1 + 1" is 0 and "0 > 0" is false. +* +* For a non-empty attribute evcn is a cluster index and must lie +* within the volume (sbi->used.bitmap.nbits, set up in +* ntfs_init_from_boot() before any caller of mi_enum_attr() runs). +* Bounding evcn also prevents a malformed value close to U64_MAX +* from slipping through the near-wrap "evcn + 1" upper bound. +*/ + svcn =le64_to_cpu (attr->nres.svcn); + evcn =le64_to_cpu (attr->nres.evcn); +if (svcn > evcn +1 ) goto out; +if (is_attr_ext (attr)) { +/* sparsed/compressed attribute. */ +#ifdef CONFIG_NTFS3_64BIT_CLUSTER +/* No limits. */ +#else +/* Check evcn fits into 32 bits. */ +if (evcn !=U64_MAX && evcn >= (1ull <<32 )) + goto out; +#endif + }else { +/* Check out of volume for normal attribute. */ +if (evcn !=U64_MAX && evcn >= mi->sbi->used.bitmap.nbits) + goto out; + } + data_size =le64_to_cpu (attr->nres.data_size);if (le64_to_cpu (attr->nres.valid_size) > data_size) goto out;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1128/1376 ALSA: core: Fix use-after-free in snd_card_do_free()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Aleksandr Nogikh <[email protected]>
[ Upstream commit 5ae1a690c522fea2900ff56c8c2ace7b059f5e04 ]
A use-after-free was detected in snd_card_do_free() when a sound card managed by devres is unbound while a user-space application still holds an open file descriptor.
For managed cards, the memory is allocated using devres_alloc(), and its release function is set to __snd_card_release(), which calls snd_card_free(). When the device is unbound, the unbind thread calls snd_card_free(), which drops a reference to the card's device. If the user thread still has an open file descriptor, the reference count does not reach zero, and the unbind thread blocks on wait_for_completion(&released).
When the user thread closes the file descriptor, it drops the final reference, invoking the device release callback release_card_device(), which calls snd_card_do_free(). snd_card_do_free() performs cleanup and calls complete(card->release_completion). This wakes up the unbind thread, which returns from snd_card_free() and __snd_card_release(). The devres core then immediately frees the memory block containing the snd_card structure.
Meanwhile, the user thread continues execution in snd_card_do_free() and evaluates if (!card->managed). It reads the managed boolean from the snd_card structure that was just freed by the unbind thread, triggering a KASAN use-after-free.
Fix this by caching the value of card->managed in a local variable before calling complete(). This ensures that the card pointer is not dereferenced after the unbind thread has been woken up and potentially freed the card.
BUG: KASAN: use-after-free in snd_card_do_free sound/core/init.c:604 [inline] BUG: KASAN: use-after-free in release_card_device+0x1ab/0x1b0 sound/core/init.c:153 Read of size 1 at addr ffff8881912ec909 by task syz-executor130/5857 Call Trace: <TASK> dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120 print_address_description+0x55/0x1e0 mm/kasan/report.c:378 print_report+0x58/0x70 mm/kasan/report.c:482 kasan_report+0x117/0x150 mm/kasan/report.c:595 snd_card_do_free sound/core/init.c:604 [inline] release_card_device+0x1ab/0x1b0 sound/core/init.c:153 device_release+0xc4/0x1f0 drivers/base/core.c:-1 kobject_cleanup lib/kobject.c:689 [inline] kobject_release lib/kobject.c:720 [inline] kref_put include/linux/kref.h:65 [inline] kobject_put+0x222/0x550 lib/kobject.c:737 snd_card_file_remove+0x331/0x390 sound/core/init.c:1125 snd_pcm_release+0x12c/0x160 sound/core/pcm_native.c:2986 __fput+0x418/0xa50 fs/file_table.c:512 fput_close_sync+0x11f/0x240 fs/file_table.c:617 __do_sys_close fs/open.c:1511 [inline] __se_sys_close fs/open.c:1496 [inline] __x64_sys_close+0x7e/0x110 fs/open.c:1496 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x77/0x7f </TASK>
--- sound/core/init.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
sound/core/init.c +3 -1
@@ -579,6 +579,8 @@ EXPORT_SYMBOL_GPL(snd_card_disconnect_sync);static int snd_card_do_free (struct snd_card *card) { +bool managed = card->managed; + card->releasing = true;#if IS_ENABLED (CONFIG_SND_MIXER_OSS )if (snd_mixer_oss_notify_callback)
@@ -593,7 +595,7 @@ static int snd_card_do_free(struct snd_card *card) }if (card->release_completion)complete (card->release_completion); -if (!card->managed) +if (!managed)kfree (card);return 0 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1129/1376 tracing: Remove "__attribute__()" from the type field of event format
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Masami Hiramatsu (Google) <[email protected]>
[ Upstream commit 1a967e92bf47cf5170336b88d748117c700edc47 ]
With CONFIG_DEBUG_INFO_BTF=y and PAHOLE_HAS_BTF_TAG=y, __user is converted to __attribute__((btf_type_tag("user"))). In this case, some syscall events have it for __user data, like below;
/sys/kernel/tracing # cat events/syscalls/sys_enter_openat/format
name: sys_enter_openat
ID: 720
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:int __syscall_nr; offset:8; size:4; signed:1;
field:int dfd; offset:16; size:8; signed:0;
field:const char __attribute__((btf_type_tag("user"))) * filename; offset:24; size:8; signed:0;
field:int flags; offset:32; size:8; signed:0;
field:umode_t mode; offset:40; size:8; signed:0;Then the trace event filter fails to set the string acceptable flag (FILTER_PTR_STRING) to the field and rejects setting string filter;
# echo 'filename.ustring ~ "*ftracetest-dir.wbx24v*"' \
Quoted 1 line
Quoted 1 line
events/syscalls/sys_enter_openat/filter
sh: write error: Invalid argument
# cat error_log [ 723.743637] event filter parse error: error: Expecting numeric field Command: filename.ustring ~ "*ftracetest-dir.wbx24v*"
Since this __attribute__ makes format parsing complicated and not needed, remove the __attribute__(.*) from the type string.
Stable-dep-of: ae70b04ab9c7 ("tracing: Have trace_event_update_all() only handle module that is loading")
--- kernel/trace/trace.c | 28 +++++--- kernel/trace/trace.h | 4 +- kernel/trace/trace_events.c | 128 ++++++++++++++++++++++++++++++------ 3 files changed, 127 insertions(+), 33 deletions(-)
kernel/trace/trace.c +18 -10
@@ -5823,17 +5823,27 @@ static inline void trace_insert_eval_map_file(struct module *mod, struct trace_eval_map **start, int len) { }#endif /* !CONFIG_TRACE_EVAL_MAP_FILE */ -static void trace_insert_eval_map (struct module *mod, -struct trace_eval_map **start,int len) +static void +trace_event_update_with_eval_map (struct module *mod, +struct trace_eval_map **start, +int len) {struct trace_eval_map **map; -if (len <=0 ) -return ; +/* Always run sanitizer only if btf_type_tag attr exists. */ +if (len <=0 ) { +if (!(IS_ENABLED (CONFIG_DEBUG_INFO_BTF ) && +IS_ENABLED (CONFIG_PAHOLE_HAS_BTF_TAG ) && +__has_attribute (btf_type_tag))) +return ; + } map = start; -trace_event_eval_update (map, len); +trace_event_update_all (map, len); + +if (len <=0 ) +return ;trace_insert_eval_map_file (mod, start, len); }
@@ -9805,7 +9815,7 @@ static void __init eval_map_work_func(struct work_struct *work)int len; len = __stop_ftrace_eval_maps - __start_ftrace_eval_maps; -trace_insert_eval_map (NULL , __start_ftrace_eval_maps, len); +trace_event_update_with_eval_map (NULL , __start_ftrace_eval_maps, len); }static int __init trace_eval_init (void )
@@ -9840,9 +9850,6 @@ late_initcall_sync(trace_eval_sync);#ifdef CONFIG_MODULES static void trace_module_add_evals (struct module *mod) { -if (!mod->num_trace_evals) -return ; -/* * Modules with bad taint do not have events created, do * not bother with enums either.
@@ -9850,7 +9857,8 @@ static void trace_module_add_evals(struct module *mod)if (trace_module_has_bad_taint (mod))return ; -trace_insert_eval_map (mod, mod->trace_evals, mod->num_trace_evals); +/* Even if no trace_evals, this need to sanitize field types. */ +trace_event_update_with_eval_map (mod, mod->trace_evals, mod->num_trace_evals); }#ifdef CONFIG_TRACE_EVAL_MAP_FILE
kernel/trace/trace.h +2 -2
@@ -2071,13 +2071,13 @@ static inline const char *get_syscall_name(int syscall)#ifdef CONFIG_EVENT_TRACING void trace_event_init (void ); -void trace_event_eval_update (struct trace_eval_map **map,int len); +void trace_event_update_all (struct trace_eval_map **map,int len);/* Used from boot time tracer */ extern int ftrace_set_clr_event (struct trace_array *tr,char *buf,int set);extern int trigger_process_regex (struct trace_event_file *file,char *buff);#else static inline void __init trace_event_init (void ) { } -static inline void trace_event_eval_update (struct trace_eval_map **map,int len) { } +static inline void trace_event_update_all (struct trace_eval_map **map,int len) { }#endif #ifdef CONFIG_TRACER_SNAPSHOT
kernel/trace/trace_events.c +107 -21
178 lines not on this page · raw message
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1130/1376 tracing: Have trace_event_update_all() only handle module that is loading
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Steven Rostedt <[email protected]>
[ Upstream commit ae70b04ab9c7f6162a8c0fdd18a62a945c133142 ]
The function trace_event_update_all() does a scan of events looking to replace enums with their values in the strings that get exported to the event format files. It's run at boot up on all events and again when a module loads.
The issue is that when a module loads, it still runs on *all* events. There's no reason to process every event when a module loads as the previous events have already been processed. Only execute on the events that are loaded with the module.
--- kernel/trace/trace.c | 2 +- kernel/trace/trace.h | 4 ++-- kernel/trace/trace_events.c | 6 +++++- 3 files changed, 8 insertions(+), 4 deletions(-)
kernel/trace/trace.c +1 -1
@@ -5840,7 +5840,7 @@ trace_event_update_with_eval_map(struct module *mod, map = start; -trace_event_update_all (map, len); +trace_event_update_all (map, len, mod);if (len <=0 )return ;
kernel/trace/trace.h +2 -2
@@ -2071,13 +2071,13 @@ static inline const char *get_syscall_name(int syscall)#ifdef CONFIG_EVENT_TRACING void trace_event_init (void ); -void trace_event_update_all (struct trace_eval_map **map,int len); +void trace_event_update_all (struct trace_eval_map **map,int len,struct module *mod);/* Used from boot time tracer */ extern int ftrace_set_clr_event (struct trace_array *tr,char *buf,int set);extern int trigger_process_regex (struct trace_event_file *file,char *buff);#else static inline void __init trace_event_init (void ) { } -static inline void trace_event_update_all (struct trace_eval_map **map,int len) { } +static inline void trace_event_update_all (struct trace_eval_map **map,int len,struct module *mod) { }#endif #ifdef CONFIG_TRACER_SNAPSHOT
kernel/trace/trace_events.c +5 -1
@@ -3144,7 +3144,7 @@ static void update_event_fields(struct trace_event_call *call, }/* Update all events for replacing eval and sanitizing */ -void trace_event_update_all (struct trace_eval_map **map,int len) +void trace_event_update_all (struct trace_eval_map **map,int len,struct module *mod) {struct trace_event_call *call, *p;const char *last_system =NULL ;
@@ -3156,6 +3156,10 @@ void trace_event_update_all(struct trace_eval_map **map, int len)mutex_lock (&event_mutex);down_write (&trace_event_sem);list_for_each_entry_safe (call, p, &ftrace_events, list) { + +if (mod && call->module != mod) +continue ; +/* events are usually grouped together with systems */ if (!last_system || call->class->system != last_system) { first = true;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1131/1376 ASoC: SOF: validate topology volume range before allocation
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pengpeng Hou <[email protected]>
[ Upstream commit a698e4a60fa54268a38f4e66378851a196cb139b ]
SOF treats the topology mixer min and max values as non-negative indices into its volume table. It stores them in signed fields, allocates max + 1 entries through an int argument, and later indexes the table with the stored range.
An inverted range is invalid, while a maximum at or above INT_MAX cannot be represented safely after the increment or in the signed fields. Validate the complete range before storing it or allocating the table.
--- sound/soc/sof/topology.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
sound/soc/sof/topology.c +12 -6
@@ -827,6 +827,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,struct snd_soc_tplg_mixer_control *mc = container_of(hdr, struct snd_soc_tplg_mixer_control, hdr);int tlv[SOF_TLV_ITEMS ]; +u32 min, max;unsigned int mask;int ret;
@@ -834,6 +835,11 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,if (le32_to_cpu (mc->num_channels) >SND_SOC_TPLG_MAX_CHAN )return -EINVAL ; + min =le32_to_cpu (mc->min); + max =le32_to_cpu (mc->max); +if (min > max || max >=INT_MAX ) +return -EINVAL ; +/* * If control has more than 2 channels we need to override the info. This is because even if * ASoC layer has defined topology's max channel count to SND_SOC_TPLG_MAX_CHAN = 8, the
@@ -844,12 +850,12 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, kc->info = snd_sof_volume_info; scontrol->comp_id = sdev->next_comp_id; - scontrol->min_volume_step =le32_to_cpu (mc->min); - scontrol->max_volume_step =le32_to_cpu (mc->max); + scontrol->min_volume_step = min; + scontrol->max_volume_step = max; scontrol->num_channels =le32_to_cpu (mc->num_channels); - scontrol->max =le32_to_cpu (mc->max); -if (le32_to_cpu (mc->max) ==1 ) + scontrol->max = max; +if (max ==1 ) goto skip;/* extract tlv data */
@@ -859,7 +865,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, }/* set up volume table */ - ret =set_up_volume_table (scontrol, tlv,le32_to_cpu (mc->max) +1 ); + ret =set_up_volume_table (scontrol, tlv, max +1 );if (ret <0 ) {dev_err (scomp->dev,"error: setting up volume table\n" );return ret;
@@ -892,7 +898,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp,return 0 ; err: -if (le32_to_cpu (mc->max) >1 ) +if (max >1 )kfree (scontrol->volume_table);return ret;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1132/1376 HID: multitouch: reclassify HTIX5288 to WIN_8_FORCE_MULTI_INPUT_NSMU
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xianglin Lin <[email protected]>
[ Upstream commit 69226cd6fad7a07370ec9a009af1c174de7635c5 ]
Commit b5e65ae557da ("HID: multitouch: Add quirk for Hantick 5288 touchpad") assigned MT_CLS_NSMU to the HTIX5288 (0911:5288). This was necessary because the device sometimes fails to send touch release signals when transitioning from >=2 fingers to <2 fingers, and MT_QUIRK_NOT_SEEN_MEANS_UP fixes stuck touches by treating missing contacts as released.
However, MT_CLS_NSMU only carries MT_QUIRK_NOT_SEEN_MEANS_UP. It lacks MT_QUIRK_CONTACT_CNT_ACCURATE and MT_QUIRK_IGNORE_DUPLICATES. As a result, after a two-finger scroll finger lift, the device still reports stale coordinates from the released contact in subsequent frames, and the driver overwrites the remaining active slot with those frozen coordinates. The remaining finger appears stuck at the lift position until all fingers are lifted.
This was confirmed via evtest on Arch Linux 7.1.3: after TRACKING_ID=-1 for the released slot, every subsequent frame contained duplicate position pairs -- the real moving finger's coordinates followed by the lifted finger's frozen position, both attributed to the active slot.
Reclassify the device to MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU (0x0018), which preserves the original MT_QUIRK_NOT_SEEN_MEANS_UP fix while adding the necessary Win8 quirks (CONTACT_CNT_ACCURATE, IGNORE_DUPLICATES), preventing stale coordinate contamination.
The additional FORCE_MULTI_INPUT flag is harmless here: it separates the mouse and touchpad collections into distinct input devices, which is the standard behavior libinput already expects.
--- drivers/hid/hid-multitouch.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/hid/hid-multitouch.c +1 -1
@@ -2426,7 +2426,7 @@ static const struct hid_device_id mt_devices[] = { HID_ANY_ID) },/* Hantick */ - { .driver_data = MT_CLS_NSMU, + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU,HID_DEVICE (BUS_I2C ,HID_GROUP_MULTITOUCH_WIN_8 , I2C_VENDOR_ID_HANTICK, I2C_PRODUCT_ID_HANTICK_5288) },
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1133/1376 riscv, bpf: Fix missing sign-ext for signed 1-byte and 2-byte kfunc args
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Pu Lehui <[email protected]>
[ Upstream commit f2aaa621591093cfe8224a25ef2f04a3b1e304b0 ]
On RV64, the ABI requires sign-extension for signed 1-byte and 2-byte kfunc args. However, the RV64 JIT currently does not perform sign-extension for such kfunc args.
Before commit 7ce090afbf72 ("bpf: Infer zext_dst based on static register liveness analysis"), state pruning could potentially omit zero-extension of 32-bit subregisters, which inadvertently masked the above issue by making the args appear as if they had been properly sign-extended. After that commit, the problem is exposed, causing the kfunc_call/kfunc_call_test4 selftest to fail.
Fix this by extending the existing sign-extension logic to handle signed 1-byte and 2-byte kfunc args as well.
--- arch/riscv/net/bpf_jit_comp64.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
arch/riscv/net/bpf_jit_comp64.c +3 -2
@@ -1617,9 +1617,10 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,for (idx =0 ; idx < fm->nr_args; idx++) {u8 reg =bpf_to_rv_reg (BPF_REG_1 + idx, ctx); +bool sign = fm->arg_flags[idx] &BTF_FMODEL_SIGNED_ARG ; -if (fm->arg_size[idx] ==sizeof (int )) -emit_sextw (reg, reg, ctx); +if (sign_extend (reg, reg, fm->arg_size[idx], sign, ctx)) +return -EINVAL ; } }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1134/1376 ring-buffer: Remove trace_buffer::cpus
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Vincent Donnefort <[email protected]>
[ Upstream commit d9b5e22bf24d5c82522475306cd332b287ac43bc ]
The 'cpus' field in struct trace_buffer became useless in commit 8e7b58c27b3c ("ring-buffer: Just update the subbuffers when changing their allocation order"). Remove it
--- kernel/trace/ring_buffer.c | 2 -- 1 file changed, 2 deletions(-)
kernel/trace/ring_buffer.c +0 -2
@@ -544,7 +544,6 @@ struct ring_buffer_per_cpu {struct trace_buffer {unsigned flags; -int cpus;atomic_t record_disabled;atomic_t resizing;cpumask_var_t cpumask;
@@ -2407,7 +2406,6 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags,init_irq_work (&buffer->irq_work.work, rb_wake_up_waiters);init_waitqueue_head (&buffer->irq_work.waiters); - buffer->cpus = nr_cpu_ids; bsize =sizeof (void *) * nr_cpu_ids; buffer->buffers = kzalloc(ALIGN(bsize, cache_line_size()),
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1135/1376 ACPI: scan: fix bus ID cleanup on device_add() failures
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
[ Upstream commit a414485ebc2aa50907d0ce97cde2b1a353696897 ]
When device_add() fails after acpi_device_set_name() has allocated an instance ID and a new acpi_device_bus_id has been linked into acpi_bus_id_list, the rollback path only removes wakeup_list and detaches the ACPI handle data.
That leaves the bus-ID bookkeeping behind and keeps the allocated instance number consumed.
Move the bus-ID cleanup and wakeup-list removal into a single helper.
Use it from both the normal device teardown path and the device_add() rollback path. The wakeup list node is initialized before registration, so it can be deleted without checking whether the device is wakeup- capable like in the original teardown path.
--- drivers/acpi/scan.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-)
drivers/acpi/scan.c +9 -4
@@ -521,12 +521,10 @@ static void acpi_device_release(struct device *dev)kfree (acpi_dev); } -static void acpi_device_del (struct acpi_device *device) +static void acpi_device_cleanup (struct acpi_device *device) {struct acpi_device_bus_id *acpi_device_bus_id; -mutex_lock (&acpi_device_lock); -list_for_each_entry (acpi_device_bus_id, &acpi_bus_id_list, node)if (!strcmp (acpi_device_bus_id->bus_id,acpi_device_hid (device))) {
@@ -541,6 +539,13 @@ static void acpi_device_del(struct acpi_device *device) }list_del (&device->wakeup_list); +} + +static void acpi_device_del (struct acpi_device *device) +{ +mutex_lock (&acpi_device_lock); + +acpi_device_cleanup (device);mutex_unlock (&acpi_device_lock);
@@ -803,7 +808,7 @@ int acpi_device_add(struct acpi_device *device) err:mutex_lock (&acpi_device_lock); -list_del (&device->wakeup_list); +acpi_device_cleanup (device); err_unlock:mutex_unlock (&acpi_device_lock);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1136/1376 bpf: Fix pending_pos walk on 32-bit ring position wrap
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Israel Téllez García <[email protected]>
[ Upstream commit 6ff5b56a50c5351aeeb180e34327736576c038fa ]
The reservation path caches the position of the oldest not-yet-committed record in rb->pending_pos and advances it past already committed records on every reservation:
while (pend_pos < prod_pos) {consumer_pos, producer_pos and pending_pos are unsigned long, i.e. 32-bit on 32-bit architectures, and Documentation/bpf/ringbuf.rst states that these counters may wrap around there. Every other comparison in the file is written as a difference, so modular arithmetic keeps them correct across the wrap. This one is an ordering comparison, and it is not wrap-safe.
Once producer_pos wraps past 2^32, prod_pos is small while pend_pos still holds its pre-wrap value, so the loop condition is false and pending_pos is never advanced again. Reservations keep succeeding for a while, because bpf_ringbuf_has_space() uses differences, but new_prod_pos - pend_pos grows as the producer advances, and once it exceeds rb->mask every subsequent __bpf_ringbuf_reserve() call fails:
the kernel believes a pending record spans the whole buffer. The ring never recovers, bpf_ringbuf_output() drops every event from then on, and nothing is logged.
Observed on four armv7 devices (i.MX7 Dual, 6.6.52) running a tracepoint-based collector with a 512 KiB ring and 160-byte records. Every one of them stopped delivering after exactly 26846821 records and 4295491360 bytes had passed through the ring, at event rates between 441 and 862 records/s, that is after 8 h to 17 h of uptime: the trigger is the byte count, not time or load. That figure is 2^32 plus 524064 bytes, and the excess is one ring's worth of grace period, as expected while new_prod_pos - pend_pos is still below rb->mask. The last reservation that fits is the largest record boundary X with X + 160 <= 524287, and since 2^32 mod 160 = 96 the boundaries after the wrap sit at X = 64 (mod 160), giving X = 524064. Userspace kept consuming normally until the producer stopped, then read zero records for good. With this patch applied, one of the four devices took 10 GiB through the same ring with no stall, while the three unpatched ones kept wedging at the same byte count.
64-bit hosts are unaffected in practice: their counters would need 16 EiB to wrap.
Compare the two positions as a difference instead. pending_pos never runs ahead of producer_pos, so the unsigned difference is the real distance between them and stays correct across the wrap.
--- kernel/bpf/ringbuf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/bpf/ringbuf.c +1 -1
@@ -429,7 +429,7 @@ static void *__bpf_ringbuf_reserve(struct bpf_ringbuf *rb, u64 size) prod_pos = rb->producer_pos; new_prod_pos = prod_pos + len; -while (pend_pos < prod_pos) { +while (prod_pos - pend_pos >0 ) { hdr = (void *)rb->data + (pend_pos & rb->mask); hdr_len =READ_ONCE (hdr->len);if (hdr_len &BPF_RINGBUF_BUSY_BIT )
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1137/1376 crypto: hisilicon/sec2 - fix CCM algorithm long packet failure
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zhushuai Yin <[email protected]>
[ Upstream commit b82f60be50c87b3d75e207852c5ca74fa18f66cf ]
In the CCM B0 block the message-length field Q spans L bytes, where L (cl in the driver) is derived from the cipher IV flags byte as c_ivin[0] + 1. set_aead_auth_iv() hardcoded writing only the last 2 bytes of a_ivin with cryptlen, implicitly assuming cl = 2.
When cl = 3 (a shorter nonce yielding a 3-byte length field) and the packet is longer than 65535 bytes, cryptlen no longer fits in 2 bytes. The dropped high byte made the auth IV built by the driver differ from the one consumed by the hardware, so the software/hardware comparison failed and the CCM request errored out.
Write the last cl bytes of a_ivin in a loop driven by the IV's CL value, so the length-field width always matches the algorithm configuration instead of assuming a fixed 2-byte field.
--- drivers/crypto/hisilicon/sec2/sec_crypto.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
drivers/crypto/hisilicon/sec2/sec_crypto.c +6 -6
@@ -98,7 +98,6 @@#define IV_FLAGS_OFFSET 0x6#define IV_CM_OFFSET 0x3#define IV_LAST_BYTE1 1 -#define IV_LAST_BYTE2 2#define IV_LAST_BYTE_MASK 0xFF#define IV_CTR_INIT 0x1#define IV_BYTE_OFFSET 0x8
@@ -1465,7 +1464,7 @@ static void set_aead_auth_iv(struct sec_ctx *ctx, struct sec_req *req)struct sec_cipher_req *c_req = &req->c_req;u32 data_size = aead_req->cryptlen;u8 flage =0 ; -u8 cm, cl; +u8 cm, cl, i;/* the specification has been checked in aead_iv_demension_check() */ cl = c_req->c_ivin[0 ] +1 ;
@@ -1489,15 +1488,16 @@ static void set_aead_auth_iv(struct sec_ctx *ctx, struct sec_req *req)* the last 32bit is counter's initial number, * but the nonce uses the first 16bit * the tail 16bit fill with the cipher length +* When CL is 3, the tail 24bit fill with the cipher length. */ if (!c_req->encrypt) data_size = aead_req->cryptlen - authsize; - a_req->a_ivin[ctx->c_ctx.ivsize -IV_LAST_BYTE1 ] = - data_size &IV_LAST_BYTE_MASK ; - data_size >>=IV_BYTE_OFFSET ; - a_req->a_ivin[ctx->c_ctx.ivsize -IV_LAST_BYTE2 ] = +for (i =1 ; i <= cl; i++) { + a_req->a_ivin[ctx->c_ctx.ivsize - i] = data_size &IV_LAST_BYTE_MASK ; + data_size >>=IV_BYTE_OFFSET ; + } }static void sec_aead_set_iv (struct sec_ctx *ctx,struct sec_req *req)
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1138/1376 crypto: lskcipher - propagate errors from unaligned crypt
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Karl Mehltretter <[email protected]>
[ Upstream commit 7537036a2e6fe96f8ed82034f755c54714a0e417 ]
The while loop declares a second err variable that shadows the outer one. When the crypt callback fails, the goto out path returns the outer err, which still holds the -ENOMEM value assigned before the successful allocation check. The real error from the cipher is discarded and the caller sees -ENOMEM instead.
Drop the inner declaration so the callback error reaches the caller.
Verified with a test module that registers an lskcipher whose encrypt callback fails with -EIO and calls it through a misaligned buffer. An unpatched kernel returns -ENOMEM, a patched kernel returns -EIO.
Found with Clang's -Wshadow.
--- crypto/lskcipher.c | 1 - 1 file changed, 1 deletion(-)
crypto/lskcipher.c +0 -1
@@ -95,7 +95,6 @@ static int crypto_lskcipher_crypt_unaligned(while (len >= bs) {unsigned chunk =min ((unsigned )PAGE_SIZE , len); -int err;if (chunk > cs) chunk &= ~(cs -1 );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1139/1376 sched_ext/scx_flatcg: Fix cvtime true-up on slice expiry
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Tao Cui <[email protected]>
[ Upstream commit 9b4243d30469e7733730e786792d1560442e229e ]
fcg_dispatch() true-ups the current cgroup's cvtime when its slice expires or its DSQ runs empty while the slice is still active:
__sync_fetch_and_add(&cgc->cvtime_delta, (cpuc->cur_at + cgrp_slice_ns - now) * FCG_HWEIGHT_ONE / (cgc->hweight ?: 1));
The true-up should be actual minus charged: on CNS_EXPIRE, the overrun (now - cur_at - cgrp_slice_ns) should be added; on CNS_EMPTY, the unused portion of the slice should be subtracted. The expression above has the sign inverted, and in the CNS_EXPIRE case now is already past cur_at + cgrp_slice_ns, so the u64 subtraction wraps. The multiplication preserves the two's complement encoding but the unsigned division by hweight destroys it, adding roughly 2^64/hweight per expiry instead of a small correction.
Under saturation the hweight budget clamp in cgrp_cap_budget() masks most of the garbage, so the weight distribution barely moves, but the accounting is broken all the same. Compute the delta as a signed value and use fetch_and_add()/fetch_and_sub() so that the dividends stay positive, as BPF division is unsigned.
Instrumented the true-up and ran a saturated three-leaf cgroup tree on a 4-CPU VM: without the fix, each expiry added ~5e15 (2^64/hweight territory) to cvtime_delta; with it, the corrections are back to slice scale, with the overrun added and the unused portion subtracted.
--- tools/sched_ext/scx_flatcg.bpf.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
tools/sched_ext/scx_flatcg.bpf.c +11 -3
@@ -770,10 +770,18 @@ void BPF_STRUCT_OPS(fcg_dispatch, s32 cpu, struct task_struct *prev)* cgroup to execute but the latter needs to be done in a loop * and we can't keep the lock held. Oh well... */ +s64 delta = now - cpuc->cur_at - cgrp_slice_ns; +bpf_spin_lock (&cgv_tree_lock); -__sync_fetch_and_add (&cgc->cvtime_delta, - (cpuc->cur_at + cgrp_slice_ns - now) * -FCG_HWEIGHT_ONE / (cgc->hweight ?:1 )); +/* keep the dividends positive, BPF division is unsigned */ +if (delta >=0 ) +__sync_fetch_and_add (&cgc->cvtime_delta, + (u64 )delta *FCG_HWEIGHT_ONE / + (cgc->hweight ?:1 )); +else +__sync_fetch_and_sub (&cgc->cvtime_delta, + (u64 )-delta *FCG_HWEIGHT_ONE / + (cgc->hweight ?:1 ));bpf_spin_unlock (&cgv_tree_lock); }else {stat_inc (FCG_STAT_CNS_GONE );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1140/1376 perf dso: Guard close() against invalid fd in dso__decompress_kmodule_path()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnaldo Carvalho de Melo <[email protected]>
[ Upstream commit 10f452dc2de401be76ae8e7395c9663313df8b53 ]
dso__decompress_kmodule_path() unconditionally calls close(fd) on the return value of decompress_kmodule(). When decompression fails or the DSO is not compressed, decompress_kmodule() returns -1. close(-1) fails with EBADF and clobbers errno, which callers up the chain (dso__get_filename → __open_dso) depend on for error propagation.
Guard the close() call with fd >= 0 so only valid file descriptors are closed.
--- tools/perf/util/dso.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
tools/perf/util/dso.c +3 -1
@@ -385,7 +385,9 @@ int dso__decompress_kmodule_path(struct dso *dso, const char *name, {int fd =decompress_kmodule (dso, name, pathname, len); -close (fd); +/* decompress_kmodule() returns -1 on failure, don't close(-1) */ +if (fd >=0 ) +close (fd);return fd >=0 ?0 :-1 ; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1141/1376 perf dso: Use stored fd error instead of stale errno in file_read() and file_size()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnaldo Carvalho de Melo <[email protected]>
[ Upstream commit 075d2c32353ded0fe5f3b62f4aa4e98dccb3fb29 ]
file_read() and file_size() use ret = -errno when dso__data(dso)->fd is negative after try_to_open_dso() fails. By this point errno has been through mutex_lock(), nsinfo__mountns_enter(), and multiple open() attempts inside try_to_open_dso() — it no longer reflects the actual open failure. If errno happens to be 0, ret = 0 looks like EOF rather than an error, and file_size() callers like dso__data_size() would then report a zero-sized file instead of failing.
dso__data(dso)->fd is always negative on failure — -errno from __open_dso() when no filename could be built (e.g. -EINVAL, -ENOENT), or -1 when do_open() itself failed — and never 0, so use it directly instead of reading the stale global errno.
No assert() or comment is needed after the assignment: the enclosing if (dso__data(dso)->fd < 0) already guarantees ret < 0 [Namhyung Kim review].
--- tools/perf/util/dso.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/dso.c +2 -2
@@ -963,7 +963,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,if (dso__data (dso)->fd <0 ) {dso__data (dso)->status =DSO_DATA_STATUS_ERROR ; - ret = -errno; + ret =dso__data (dso)->fd; goto out; }
@@ -1086,8 +1086,8 @@ static int file_size(struct dso *dso, struct machine *machine)try_to_open_dso (dso, machine);if (dso__data (dso)->fd <0 ) { - ret = -errno;dso__data (dso)->status =DSO_DATA_STATUS_ERROR ; + ret =dso__data (dso)->fd; goto out; }
Signature · 1 line
2.53.0
Re: [PATCH 6.12 1141/1376] perf dso: Use stored fd error instead of stale errno in file_read() and file_size()
Quoted 42 lines · Greg Kroah-Hartman
6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnaldo Carvalho de Melo <[email protected]>
[ Upstream commit
075d2c32353ded0fe5f3b62f4aa4e98dccb3fb29]
file_read()andfile_size()use ret = -errno when dso__data(dso)->fd is negative aftertry_to_open_dso()fails. By this point errno has been throughmutex_lock(),nsinfo__mountns_enter(), and multipleopen()attempts insidetry_to_open_dso()— it no longer reflects the actual open failure. If errno happens to be 0, ret = 0 looks like EOF rather than an error, andfile_size()callers likedso__data_size()would then report a zero-sized file instead of failing.dso__data(dso)->fd is always negative on failure — -errno from
__open_dso()when no filename could be built (e.g.-EINVAL,-ENOENT), or -1 whendo_open()itself failed — and never 0, so use it directly instead of reading the stale global errno.No
assert()or comment is needed after the assignment: the enclosing if (dso__data(dso)->fd < 0) already guarantees ret < 0 [Namhyung Kim review].Fixes 33bdedcea2d7 ("perf tools: Protect dso cache fd with a mutex")Reported-by sashiko-bot <[email protected]>Reviewed-by Ian Rogers <[email protected]>Assisted-by Claude:claude-opus-4.6Signed-off-by Arnaldo Carvalho de Melo <[email protected]>Signed-off-by Namhyung Kim <[email protected]>Signed-off-by Sasha Levin <[email protected]>--- tools/perf/util/dso.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)tools/perf/util/dso.c +0
-0
tools/perf/util/dso.c static ssize_t file_read(struct dso *dso, struct machine *machine, · line 967
Quoted 6 lines
tools/perf/util/dso.c +1
-1@@ -963,7 +963,7 @@ static ssize_t file_read(struct dso *dso, struct machine *machine,if (dso__data (dso)->fd <0 ) {dso__data (dso)->status =DSO_DATA_STATUS_ERROR ; - ret = -errno; + ret =dso__data (dso)->fd; goto out;
Hi Greg/Sasha,
I ran an AI-assisted backport review, and I have verified the report. It looks valid to me.
Upstream prerequisite code:
if (dso__decompress_kmodule_path(dso, name,
newpath, len) < 0) {
errno = EIO;
goto out;
}Downstream 6.12.y code:
if (dso__decompress_kmodule_path(dso, name,
newpath, len) < 0) {
fd = -(*dso__load_errno(dso));
goto out;
}Downstream negates the custom negative value into a positive pseudo-fd.
The new callers return dso_data->fd when it is negative, but a 6.12 compressed module decompression failure negates internal error -9996 and stores positive fd 9996. The open path treats that value as success, so neither corrected caller error arm runs and an invalid pseudo-descriptor is retained.
Upstream prerequisite 1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27 normalizes this failure to a standard negative errno. Maybe we should be
adding the 6.12-equivalent fd = -EIO adaptation before retaining this
patch.
Thoughts? Also, should we skip patching perf completely on stable trees ?
thanks,
Harshit
Quoted 12 lines
tools/perf/util/dso.c +1
-1}@@ -1086,8 +1086,8 @@ static int file_size(struct dso *dso, struct machine *machine)try_to_open_dso (dso, machine);if (dso__data (dso)->fd <0 ) { - ret = -errno;dso__data (dso)->status =DSO_DATA_STATUS_ERROR ; + ret =dso__data (dso)->fd; goto out; }
Sasha Levin
maintainerRe: [PATCH 6.12 1141/1376] perf dso: Use stored fd error instead of stale errno in file_read() and file_size()
Quoted 4 lines
Upstream prerequisite
1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27normalizes this failure to a standard negative errno. Maybe we should be
adding the 6.12-equivalent fd =-EIOadaptation before retaining this
patch.
1a5f9334a45a ("perf dso: Set standard errno on decompression failure") patches dso__get_filename(), which does not exist below v7.0. It arrives with 069e603d8248 ("perf tools: Get debug info of DSO properly"), so there is nothing to backport on 6.12, 6.6, 6.1, 5.15 or 5.10.
What is left is a hand written fd = -EIO per tree, which is not a backport of anything, so I am leaving the queued patch alone.
Signature · 2 lines
Thanks, Sasha
Re: [PATCH 6.12 1141/1376] perf dso: Use stored fd error instead of stale errno in file_read() and file_size()
Quoted 5 lines · Sasha Levin
Quoted 4 lines
Upstream prerequisite
1a5f9334a45a6b0c1cd7341cc72a3b87adad1d27normalizes this failure to a standard negative errno. Maybe we should be
adding the 6.12-equivalent fd =-EIOadaptation before retaining this
patch.
Quoted 7 lines
1a5f9334a45a("perf dso: Set standard errno on decompression failure") patchesdso__get_filename(), which does not exist below v7.0. It arrives with069e603d8248("perf tools: Get debug info of DSO properly"), so there is nothing to backport on 6.12, 6.6, 6.1, 5.15 or 5.10.What is left is a hand written fd =
-EIOper tree, which is not a backport of anything, so I am leaving the queued patch alone.
Thanks!
Any thoughts on skipping perf patches, I see perf build failures once in a while, but I was assuming perf from upstream could be used.
thanks,
Harshit
Quoted 1 line
Greg Kroah-Hartman
maintainerPATCH 1142/1376 perf dso: Guard against cache underflow on short reads in dso_cache__memcpy()
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Arnaldo Carvalho de Melo <[email protected]>
[ Upstream commit 390a9461cd73bdd13acc0f6d763618ae1ff8fa17 ]
dso_cache__memcpy() computes cache_offset = offset - cache->offset, then cache_size = min(cache->size - cache_offset, size). The RB tree lookup in __dso_cache__find() matches using the full DSO__DATA_CACHE_SIZE window, but cache->size reflects the actual pread return value from dso_cache__populate().
A short pread (e.g. near end-of-file) makes cache->size smaller than DSO__DATA_CACHE_SIZE. If a subsequent access targets an offset past cache->offset + cache->size but within the DSO__DATA_CACHE_SIZE window, the cache entry is found but cache_offset exceeds cache->size. Since both are u64, the subtraction cache->size - cache_offset wraps to a large value, min() selects the caller's size, and memcpy reads out of bounds.
Return 0 for an offset past the valid cached data. For a regular file a short pread only happens at end-of-file, so 0 is what a direct pread() at that offset would return: cached_io() stops its read loop as on EOF. Re-reading from the backing file would not help — a second pread at the same offset returns the same short count.
--- tools/perf/util/dso.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-)
tools/perf/util/dso.c +14 -1
@@ -939,7 +939,20 @@ static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data, u64 size, bool out) {u64 cache_offset = offset - cache->offset; -u64 cache_size =min (cache->size - cache_offset, size); +u64 cache_size; + +/* +* The RB tree matches using DSO__DATA_CACHE_SIZE, but a short +* pread may leave cache->size smaller. For a regular file a +* short pread only happens at end-of-file, so an offset past +* the valid data is EOF: return 0, matching what a direct +* pread() at that offset would return, and cached_io() then +* stops its read loop. +*/ +if (cache_offset >= cache->size) +return 0 ; + + cache_size =min (cache->size - cache_offset, size);if (out)memcpy (data, cache->data + cache_offset, cache_size);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1143/1376 mailbox: qcom-cpucp: fix PREEMPT_RT self-deadlock in IRQ handler
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Yang <[email protected]>
[ Upstream commit 3690aaa6d18f6775c3e7932fb8af8c5bf6a6b69c ]
qcom_cpucp_mbox_irq_fn() calls mbox_chan_received_data() while holding chan->lock. Under PREEMPT_RT, spin_lock_irqsave() is converted to an rt_spinlock (rtmutex-based), which tracks ownership and can sleep.
The callback chain triggered by mbox_chan_received_data() eventually reaches mailbox_clear_channel() -> mbox_send_message() -> add_to_rbuf(), which attempts to re-acquire the same chan->lock. Since rtmutex detects the re-entrant lock attempt by the same owner, the thread blocks waiting for a lock it already holds, causing a permanent deadlock.
This deadlock manifests as 'irq/N-apss_cpucp_mbox' stuck in D state with the following call trace:
rt_spin_lock -> mbox_send_message -> mailbox_clear_channel -> scmi_rx_callback -> mbox_chan_received_data [<- held chan->lock here]
Fix by saving chan->cl locally and clearing the HW interrupt register inside the lock, then invoking mbox_chan_received_data() after releasing the lock. This preserves the mutual exclusion for chan->cl access while avoiding the lock re-entrancy that causes the PREEMPT_RT deadlock.
--- drivers/mailbox/qcom-cpucp-mbox.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-)
drivers/mailbox/qcom-cpucp-mbox.c +14 -3
@@ -56,14 +56,25 @@ static irqreturn_t qcom_cpucp_mbox_irq_fn(int irq, void *data)for_each_set_bit (i, (unsigned long *)&status,APSS_CPUCP_IPC_CHAN_SUPPORTED ) {u32 val =readl (cpucp->rx_base +APSS_CPUCP_RX_MBOX_CMD (i) +APSS_CPUCP_MBOX_CMD_OFF );struct mbox_chan *chan = &cpucp->chans[i]; +struct mbox_client *cl;unsigned long flags; -/* Provide mutual exclusion with changes to chan->cl */ +/* +* Provide mutual exclusion with changes to chan->cl. +* Save cl locally and clear the HW interrupt inside the lock, +* then invoke mbox_chan_received_data() outside the lock to +* avoid a PREEMPT_RT self-deadlock: mbox_chan_received_data() +* can call back into mbox_send_message() via scmi_rx_callback() +* -> mailbox_clear_channel(), which re-acquires chan->lock +* (converted to an rt_spinlock under PREEMPT_RT). +*/ spin_lock_irqsave (&chan->lock, flags); -if (chan->cl) -mbox_chan_received_data (chan, &val); + cl = chan->cl;writeq (BIT (i), cpucp->rx_base +APSS_CPUCP_RX_MBOX_CLEAR );spin_unlock_irqrestore (&chan->lock, flags); + +if (cl) +mbox_chan_received_data (chan, &val); }return IRQ_HANDLED ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1144/1376 mailbox: qcom-cpucp: handle NULL data in send_data callback
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Jia Yang <[email protected]>
[ Upstream commit fc4f2f99530298a1739226947aa76525142d421d ]
mailbox_clear_channel() calls mbox_send_message() with NULL data to notify the remote side that the RX channel has been cleared. qcom_cpucp_mbox_send_data() blindly dereferenced the data pointer, causing a NULL pointer dereference kernel panic when invoked from this path under PREEMPT_RT.
Add an explicit NULL check and return early without writing to the TX register, which is the correct behaviour for a channel-clear notification.
--- drivers/mailbox/qcom-cpucp-mbox.c | 8 ++++++++ 1 file changed, 8 insertions(+)
drivers/mailbox/qcom-cpucp-mbox.c +8 -0
@@ -110,6 +110,14 @@ static int qcom_cpucp_mbox_send_data(struct mbox_chan *chan, void *data)unsigned long chan_id =channel_number (chan);u32 *val = data; +/* +* mailbox_clear_channel() calls mbox_send_message() with NULL data to +* signal the remote side that the channel has been cleared. Nothing +* needs to be written to the TX register in that case, so just return. +*/ +if (!val) +return 0 ; +writel (*val, cpucp->tx_base +APSS_CPUCP_TX_MBOX_CMD (chan_id) +APSS_CPUCP_MBOX_CMD_OFF );return 0 ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1145/1376 mailbox: rockchip: disable pclk on probe failure and unbind
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Linmao Li <[email protected]>
[ Upstream commit ded48fcbdc1e3ed1dc8e1974b7fe9639fcea6dd6 ]
rockchip_mbox_probe() enables the peripheral clock and then keeps going. None of the later failure paths - platform_get_irq(), devm_request_threaded_irq() and devm_mbox_controller_register() - disables it again. The driver has no remove callback either, so the clock also stays prepared and enabled once the device is unbound, and its enable count keeps growing over bind/unbind cycles.
Use devm_clk_get_enabled() to tie disabling and unpreparing the clock to the device lifetime. It is registered before the interrupts and the mailbox controller, so devres releases it after both are gone.
While rewriting the error path, switch it to dev_err_probe() so that a deferred probe is not reported as an error.
--- drivers/mailbox/rockchip-mailbox.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-)
drivers/mailbox/rockchip-mailbox.c +4 -13
@@ -201,19 +201,10 @@ static int rockchip_mbox_probe(struct platform_device *pdev)/* Each channel has two buffers for A2B and B2A */ mb->buf_size = (size_t )resource_size (res) / (drv_data->num_chans *2 ); - mb->pclk =devm_clk_get (&pdev->dev,"pclk_mailbox" ); -if (IS_ERR (mb->pclk)) { - ret =PTR_ERR (mb->pclk); -dev_err (&pdev->dev,"failed to get pclk_mailbox clock: %d\n" , - ret); -return ret; - } - - ret =clk_prepare_enable (mb->pclk); -if (ret) { -dev_err (&pdev->dev,"failed to enable pclk: %d\n" , ret); -return ret; - } + mb->pclk =devm_clk_get_enabled (&pdev->dev,"pclk_mailbox" ); +if (IS_ERR (mb->pclk)) +return dev_err_probe (&pdev->dev,PTR_ERR (mb->pclk), +"failed to get and enable pclk_mailbox clock\n" );for (i =0 ; i < mb->mbox.num_chans; i++) { irq =platform_get_irq (pdev, i);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1146/1376 mailbox: pcc: Always map the shared memory communication address
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit fa362ffafa51b08cf8e2fcca38e056332f6b9b05 ]
Currently the shared memory communication address was mapped by the mailbox client drivers leading to all sorts of inconsistencies.
It also has resulted in the inconsistent attributes used while mapping the shared memory regions.
In order to remove/eliminate any issues, let us ensures the shared memory address is always mapped and unmapped when the PCC channels are requested and release.
We need to map them as the ACPI PCCT associates these shared memory with each channel subspace and may need use the status or the flags in the headers of those shared memory communication address regions to manage the transport/channel.
Note, until all the drivers using PCC start using this mapped shmem, there might be double mapping of the shared memory address. This shouldn't have any impact on existing mbox client drivers.
Since there are no users of pcc_chan_ioremap() and also it is mapped by default, we can stop exporting it and merge the functionality into pcc_mbox_request_channel().
Stable-dep-of: 3360b088175d ("mailbox: pcc: Fix command timeout due to missed interrupt")
--- drivers/mailbox/pcc.c | 29 +++++++++-------------------- include/acpi/pcc.h | 5 ----- 2 files changed, 9 insertions(+), 25 deletions(-)
drivers/mailbox/pcc.c +9 -20
@@ -372,6 +372,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)struct pcc_mbox_chan *pcc_mbox_request_channel (struct mbox_client *cl,int subspace_id) { +struct pcc_mbox_chan *pcc_mchan;struct pcc_chan_info *pchan;struct mbox_chan *chan;int rc;
@@ -390,7 +391,14 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)if (rc)return ERR_PTR (rc); -return &pchan->chan; + pcc_mchan = &pchan->chan; + pcc_mchan->shmem =acpi_os_ioremap (pcc_mchan->shmem_base_addr, + pcc_mchan->shmem_size); +if (pcc_mchan->shmem) +return pcc_mchan; + +mbox_free_channel (chan); +return ERR_PTR (-ENXIO ); }EXPORT_SYMBOL_GPL (pcc_mbox_request_channel);
@@ -419,25 +427,6 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan) }EXPORT_SYMBOL_GPL (pcc_mbox_free_channel); -int pcc_mbox_ioremap (struct mbox_chan *chan) -{ -struct pcc_chan_info *pchan_info; -struct pcc_mbox_chan *pcc_mbox_chan; - -if (!chan || !chan->cl) -return -1 ; - pchan_info = chan->con_priv; - pcc_mbox_chan = &pchan_info->chan; - - pcc_mbox_chan->shmem =acpi_os_ioremap (pcc_mbox_chan->shmem_base_addr, - pcc_mbox_chan->shmem_size); -if (!pcc_mbox_chan->shmem) -return -ENXIO ; - -return 0 ; -} -EXPORT_SYMBOL_GPL (pcc_mbox_ioremap); -/** * pcc_send_data - Called from Mailbox Controller code. Used * here only to ring the channel doorbell. The PCC client
include/acpi/pcc.h +0 -5
@@ -38,7 +38,6 @@ struct pcc_mbox_chan {extern struct pcc_mbox_chan *pcc_mbox_request_channel (struct mbox_client *cl,int subspace_id);extern void pcc_mbox_free_channel (struct pcc_mbox_chan *chan); -extern int pcc_mbox_ioremap (struct mbox_chan *chan);#else static inline struct pcc_mbox_chan *pcc_mbox_request_channel (struct mbox_client *cl,int subspace_id)
@@ -46,10 +45,6 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id)return ERR_PTR (-ENODEV ); }static inline void pcc_mbox_free_channel (struct pcc_mbox_chan *chan) { } -static inline int pcc_mbox_ioremap (struct mbox_chan *chan) -{ -return 0 ; -};#endif #endif /* _PCC_H */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1147/1376 mailbox/pcc: support mailbox management of the shared buffer
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Adam Young <[email protected]>
[ Upstream commit 5378bdf6a611a32500fccf13d14156f219bb0c85 ]
Define a new, optional, callback that allows the driver to specify how the return data buffer is allocated. If that callback is set, mailbox/pcc.c is now responsible for reading from and writing to the PCC shared buffer.
This also allows for proper checks of the Commnand complete flag between the PCC sender and receiver.
For Type 4 channels, initialize the command complete flag prior to accepting messages.
Since the mailbox does not know what memory allocation scheme to use for response messages, the client now has an optional callback that allows it to allocate the buffer for a response message.
When an outbound message is written to the buffer, the mailbox checks for the flag indicating the client wants an tx complete notification via IRQ. Upon receipt of the interrupt It will pair it with the outgoing message. The expected use is to free the kernel memory buffer for the previous outgoing message.
Stable-dep-of: 3360b088175d ("mailbox: pcc: Fix command timeout due to missed interrupt")
--- drivers/mailbox/pcc.c | 102 ++++++++++++++++++++++++++++++++++++++++-- include/acpi/pcc.h | 29 ++++++++++++ 2 files changed, 127 insertions(+), 4 deletions(-)
drivers/mailbox/pcc.c +98 -4
@@ -316,6 +316,22 @@ static void check_and_ack(struct pcc_chan_info *pchan, struct mbox_chan *chan)pcc_chan_reg_read_modify_write (&pchan->cmd_update); } +static void *write_response (struct pcc_chan_info *pchan) +{ +struct pcc_header pcc_header; +void *buffer; +int data_len; + +memcpy_fromio (&pcc_header, pchan->chan.shmem, +sizeof (pcc_header)); + data_len = pcc_header.length -sizeof (u32) +sizeof (struct pcc_header ); + + buffer = pchan->chan.rx_alloc (pchan->chan.mchan->cl, data_len); +if (buffer !=NULL ) +memcpy_fromio (buffer, pchan->chan.shmem, data_len); +return buffer; +} +/** * pcc_mbox_irq - PCC mailbox interrupt handler * @irq: interrupt number
@@ -327,6 +343,8 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) {struct pcc_chan_info *pchan;struct mbox_chan *chan = p; +struct pcc_header *pcc_header = chan->active_req; +void *handle =NULL ; pchan = chan->con_priv;
@@ -350,7 +368,17 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)* required to avoid any possible race in updatation of this flag. */ pchan->chan_in_use = false; -mbox_chan_received_data (chan,NULL ); + +if (pchan->chan.rx_alloc) + handle =write_response (pchan); + +if (chan->active_req) { + pcc_header = chan->active_req; +if (pcc_header->flags &PCC_CMD_COMPLETION_NOTIFY ) +mbox_chan_txdone (chan,0 ); + } + +mbox_chan_received_data (chan, handle);check_and_ack (pchan, chan);
@@ -394,9 +422,24 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id) pcc_mchan = &pchan->chan; pcc_mchan->shmem =acpi_os_ioremap (pcc_mchan->shmem_base_addr, pcc_mchan->shmem_size); -if (pcc_mchan->shmem) -return pcc_mchan; +if (!pcc_mchan->shmem) + goto err; + + pcc_mchan->manage_writes = false; +/* This indicates that the channel is ready to accept messages. +* This needs to happen after the channel has registered +* its callback. There is no access point to do that in +* the mailbox API. That implies that the mailbox client must +* have set the allocate callback function prior to +* sending any messages. +*/ +if (pchan->type ==ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE ) +pcc_chan_reg_read_modify_write (&pchan->cmd_update); + +return pcc_mchan; + +err:mbox_free_channel (chan);return ERR_PTR (-ENXIO ); }
@@ -427,8 +470,38 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan) }EXPORT_SYMBOL_GPL (pcc_mbox_free_channel); +static int pcc_write_to_buffer (struct mbox_chan *chan,void *data) +{ +struct pcc_chan_info *pchan = chan->con_priv; +struct pcc_mbox_chan *pcc_mbox_chan = &pchan->chan; +struct pcc_header *pcc_header = data; + +if (!pchan->chan.manage_writes) +return 0 ; + +/* The PCC header length includes the command field +* but not the other values from the header. +*/ +int len = pcc_header->length -sizeof (u32) +sizeof (struct pcc_header ); +u64 val; + +pcc_chan_reg_read (&pchan->cmd_complete, &val); +if (!val) { +pr_info ("%s pchan->cmd_complete not set" , __func__); +return -1 ; + } +memcpy_toio (pcc_mbox_chan->shmem, data, len); +return 0 ; +} + +/** -* pcc_send_data - Called from Mailbox Controller code. Used +* pcc_send_data - Called from Mailbox Controller code. If +* pchan->chan.rx_alloc is set, then the command complete +* flag is checked and the data is written to the shared +* buffer io memory. +* +* If pchan->chan.rx_alloc is not set, then it is used * here only to ring the channel doorbell. The PCC client * specific read/write is done in the client driver in * order to maintain atomicity over PCC channel once
@@ -444,17 +517,37 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)int ret;struct pcc_chan_info *pchan = chan->con_priv; + ret =pcc_write_to_buffer (chan, data); +if (ret) +return ret; + ret =pcc_chan_reg_read_modify_write (&pchan->cmd_update);if (ret)return ret; ret =pcc_chan_reg_read_modify_write (&pchan->db); +if (!ret && pchan->plat_irq >0 ) pchan->chan_in_use = true;return ret; } + +static bool pcc_last_tx_done (struct mbox_chan *chan) +{ +struct pcc_chan_info *pchan = chan->con_priv; +u64 val; + +pcc_chan_reg_read (&pchan->cmd_complete, &val); +if (!val) +return false; +else +return true; +} + + +/** * pcc_startup - Called from Mailbox Controller code. Used here * to request the interrupt.
@@ -500,6 +593,7 @@ static const struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, .startup = pcc_startup, .shutdown = pcc_shutdown, + .last_tx_done = pcc_last_tx_done, };/**
include/acpi/pcc.h +29 -0
@@ -17,6 +17,35 @@ struct pcc_mbox_chan {u32 latency;u32 max_access_rate;u16 min_turnaround_time; + +/* Set to true to indicate that the mailbox should manage +* writing the dat to the shared buffer. This differs from +* the case where the drivesr are writing to the buffer and +* using send_data only to ring the doorbell. If this flag +* is set, then the void * data parameter of send_data must +* point to a kernel-memory buffer formatted in accordance with +* the PCC specification. +* +* The active buffer management will include reading the +* notify_on_completion flag, and will then +* call mbox_chan_txdone when the acknowledgment interrupt is +* received. +*/ +bool manage_writes; + +/* Optional callback that allows the driver +* to allocate the memory used for receiving +* messages. The return value is the location +* inside the buffer where the mailbox should write the data. +*/ +void *(*rx_alloc)(struct mbox_client *cl,int size); +}; + +struct pcc_header { +u32 signature; +u32 flags; +u32 length; +u32 command; };/* Generic Communications Channel Shared Memory Region */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1148/1376 Revert "mailbox/pcc: support mailbox management of the shared buffer"
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Sudeep Holla <[email protected]>
[ Upstream commit f82c3e62b6b8c31d8c56415bf38658f306fda4cb ]
This reverts commit 5378bdf6a611a32500fccf13d14156f219bb0c85.
Commit 5378bdf6a611 ("mailbox/pcc: support mailbox management of the shared buffer") attempted to introduce generic helpers for managing the PCC shared memory, but it largely duplicates functionality already provided by the mailbox core and leaves gaps:
1. TX preparation: The mailbox framework already supports this via ->tx_prepare callback for mailbox clients. The patch adds pcc_write_to_buffer() and expects clients to toggle pchan->chan.manage_writes, but no drivers set manage_writes, so pcc_write_to_buffer() has no users.
2. RX handling: Data reception is already delivered through mbox_chan_received_data() and client ->rx_callback. The patch adds an optional pchan->chan.rx_alloc, which again has no users and duplicates the existing path.
3. Completion handling: While adding last_tx_done is directionally useful, the implementation only covers Type 3/4 and fails to handle the absence of a command_complete register, so it is incomplete for other types.
Given the duplication and incomplete coverage, revert this change. Any new requirements should be addressed in focused follow-ups rather than bundling multiple behavioral changes together.
Stable-dep-of: 3360b088175d ("mailbox: pcc: Fix command timeout due to missed interrupt")
--- drivers/mailbox/pcc.c | 102 ++---------------------------------------- include/acpi/pcc.h | 29 ------------ 2 files changed, 4 insertions(+), 127 deletions(-)
drivers/mailbox/pcc.c +4 -98
@@ -316,22 +316,6 @@ static void check_and_ack(struct pcc_chan_info *pchan, struct mbox_chan *chan)pcc_chan_reg_read_modify_write (&pchan->cmd_update); } -static void *write_response (struct pcc_chan_info *pchan) -{ -struct pcc_header pcc_header; -void *buffer; -int data_len; - -memcpy_fromio (&pcc_header, pchan->chan.shmem, -sizeof (pcc_header)); - data_len = pcc_header.length -sizeof (u32) +sizeof (struct pcc_header ); - - buffer = pchan->chan.rx_alloc (pchan->chan.mchan->cl, data_len); -if (buffer !=NULL ) -memcpy_fromio (buffer, pchan->chan.shmem, data_len); -return buffer; -} -/** * pcc_mbox_irq - PCC mailbox interrupt handler * @irq: interrupt number
@@ -343,8 +327,6 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p) {struct pcc_chan_info *pchan;struct mbox_chan *chan = p; -struct pcc_header *pcc_header = chan->active_req; -void *handle =NULL ; pchan = chan->con_priv;
@@ -368,17 +350,7 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)* required to avoid any possible race in updatation of this flag. */ pchan->chan_in_use = false; - -if (pchan->chan.rx_alloc) - handle =write_response (pchan); - -if (chan->active_req) { - pcc_header = chan->active_req; -if (pcc_header->flags &PCC_CMD_COMPLETION_NOTIFY ) -mbox_chan_txdone (chan,0 ); - } - -mbox_chan_received_data (chan, handle); +mbox_chan_received_data (chan,NULL );check_and_ack (pchan, chan);
@@ -422,24 +394,9 @@ pcc_mbox_request_channel(struct mbox_client *cl, int subspace_id) pcc_mchan = &pchan->chan; pcc_mchan->shmem =acpi_os_ioremap (pcc_mchan->shmem_base_addr, pcc_mchan->shmem_size); -if (!pcc_mchan->shmem) - goto err; - - pcc_mchan->manage_writes = false; +if (pcc_mchan->shmem) +return pcc_mchan; -/* This indicates that the channel is ready to accept messages. -* This needs to happen after the channel has registered -* its callback. There is no access point to do that in -* the mailbox API. That implies that the mailbox client must -* have set the allocate callback function prior to -* sending any messages. -*/ -if (pchan->type ==ACPI_PCCT_TYPE_EXT_PCC_SLAVE_SUBSPACE ) -pcc_chan_reg_read_modify_write (&pchan->cmd_update); - -return pcc_mchan; - -err:mbox_free_channel (chan);return ERR_PTR (-ENXIO ); }
@@ -470,38 +427,8 @@ void pcc_mbox_free_channel(struct pcc_mbox_chan *pchan) }EXPORT_SYMBOL_GPL (pcc_mbox_free_channel); -static int pcc_write_to_buffer (struct mbox_chan *chan,void *data) -{ -struct pcc_chan_info *pchan = chan->con_priv; -struct pcc_mbox_chan *pcc_mbox_chan = &pchan->chan; -struct pcc_header *pcc_header = data; - -if (!pchan->chan.manage_writes) -return 0 ; - -/* The PCC header length includes the command field -* but not the other values from the header. -*/ -int len = pcc_header->length -sizeof (u32) +sizeof (struct pcc_header ); -u64 val; - -pcc_chan_reg_read (&pchan->cmd_complete, &val); -if (!val) { -pr_info ("%s pchan->cmd_complete not set" , __func__); -return -1 ; - } -memcpy_toio (pcc_mbox_chan->shmem, data, len); -return 0 ; -} - -/** -* pcc_send_data - Called from Mailbox Controller code. If -* pchan->chan.rx_alloc is set, then the command complete -* flag is checked and the data is written to the shared -* buffer io memory. -* -* If pchan->chan.rx_alloc is not set, then it is used +* pcc_send_data - Called from Mailbox Controller code. Used * here only to ring the channel doorbell. The PCC client * specific read/write is done in the client driver in * order to maintain atomicity over PCC channel once
@@ -517,37 +444,17 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)int ret;struct pcc_chan_info *pchan = chan->con_priv; - ret =pcc_write_to_buffer (chan, data); -if (ret) -return ret; - ret =pcc_chan_reg_read_modify_write (&pchan->cmd_update);if (ret)return ret; ret =pcc_chan_reg_read_modify_write (&pchan->db); -if (!ret && pchan->plat_irq >0 ) pchan->chan_in_use = true;return ret; } - -static bool pcc_last_tx_done (struct mbox_chan *chan) -{ -struct pcc_chan_info *pchan = chan->con_priv; -u64 val; - -pcc_chan_reg_read (&pchan->cmd_complete, &val); -if (!val) -return false; -else -return true; -} - - -/** * pcc_startup - Called from Mailbox Controller code. Used here * to request the interrupt.
@@ -593,7 +500,6 @@ static const struct mbox_chan_ops pcc_chan_ops = { .send_data = pcc_send_data, .startup = pcc_startup, .shutdown = pcc_shutdown, - .last_tx_done = pcc_last_tx_done, };/**
include/acpi/pcc.h +0 -29
@@ -17,35 +17,6 @@ struct pcc_mbox_chan {u32 latency;u32 max_access_rate;u16 min_turnaround_time; - -/* Set to true to indicate that the mailbox should manage -* writing the dat to the shared buffer. This differs from -* the case where the drivesr are writing to the buffer and -* using send_data only to ring the doorbell. If this flag -* is set, then the void * data parameter of send_data must -* point to a kernel-memory buffer formatted in accordance with -* the PCC specification. -* -* The active buffer management will include reading the -* notify_on_completion flag, and will then -* call mbox_chan_txdone when the acknowledgment interrupt is -* received. -*/ -bool manage_writes; - -/* Optional callback that allows the driver -* to allocate the memory used for receiving -* messages. The return value is the location -* inside the buffer where the mailbox should write the data. -*/ -void *(*rx_alloc)(struct mbox_client *cl,int size); -}; - -struct pcc_header { -u32 signature; -u32 flags; -u32 length; -u32 command; };/* Generic Communications Channel Shared Memory Region */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1149/1376 mailbox: pcc: Fix command timeout due to missed interrupt
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Huisong Li <[email protected]>
[ Upstream commit 3360b088175dc5d5c9166685836158470d00571e ]
PCC command execution can time out when a fast platform completes a transaction and signals the platform interrupt before pcc_send_data() marks the channel as in use. For shared platform interrupts, the type 3 handler uses chan_in_use to decide whether the interrupt belongs to the channel. If it observes false, it ignores the completion and the caller waits until timeout.
Publish chan_in_use before ringing the doorbell. Use WRITE_ONCE() for the lockless flag updates and READ_ONCE() in the interrupt handler. The following ordered I/O accessor orders the flag store before the platform is notified.
Clear chan_in_use if ringing the doorbell fails. Otherwise, leave it set until the interrupt handler completes the transaction, clearing it before the mailbox core can submit another transfer.
--- drivers/mailbox/pcc.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-)
drivers/mailbox/pcc.c +27 -14
@@ -93,12 +93,11 @@ struct pcc_chan_reg {* @plat_irq: platform interrupt * @type: PCC subspace type * @plat_irq_flags: platform interrupt flags -* @chan_in_use: this flag is used just to check if the interrupt needs -* handling when it is shared. Since only one transfer can occur -* at a time and mailbox takes care of locking, this flag can be -* accessed without a lock. Note: the type only support the -* communication from OSPM to Platform, like type3, use it, and -* other types completely ignore it. +* @chan_in_use: lockless flag used by type 3 initiator subspaces to filter +* platform interrupts. Only one transfer can occur at a time, but +* the interrupt handler may sample the flag on another CPU, so all +* accesses must use READ_ONCE() or WRITE_ONCE(). Other subspace +* types do not test it. */ struct pcc_chan_info {struct pcc_mbox_chan chan;
@@ -333,8 +332,13 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)if (pcc_chan_reg_read_modify_write (&pchan->plat_irq_ack))return IRQ_NONE ; +/* +* Initiator subspaces use this flag to filter shared interrupts. Use +* READ_ONCE() to sample the lockless flag written by pcc_send_data() +* on another CPU. +*/ if (pchan->type ==ACPI_PCCT_TYPE_EXT_PCC_MASTER_SUBSPACE && - !pchan->chan_in_use) + !READ_ONCE (pchan->chan_in_use))return IRQ_NONE ;if (!pcc_mbox_cmd_complete_check (pchan))
@@ -344,12 +348,12 @@ static irqreturn_t pcc_mbox_irq(int irq, void *p)return IRQ_NONE ;/* -* Clear this flag after updating interrupt ack register and just -* before mbox_chan_received_data() which might call pcc_send_data() -* where the flag is set again to start new transfer. This is -* required to avoid any possible race in updatation of this flag. +* Clear this flag after updating the interrupt ack register and before +* notifying the client and mailbox core. mbox_chan_txdone() may submit +* the next queued transfer and set the flag again. Use WRITE_ONCE() for +* the lockless update observed by the send and interrupt paths. */ - pchan->chan_in_use = false; +WRITE_ONCE (pchan->chan_in_use, false);mbox_chan_received_data (chan,NULL );check_and_ack (pchan, chan);
@@ -448,9 +452,18 @@ static int pcc_send_data(struct mbox_chan *chan, void *data)if (ret)return ret; +/* +* Set chan_in_use before ringing the doorbell so a fast completion +* interrupt is not mistaken for a shared interrupt from another +* subspace. Use WRITE_ONCE() for the lockless flag update. The +* ordered I/O accessor used to ring the doorbell orders this store +* before the platform is notified. +*/ +if (pchan->plat_irq >0 ) +WRITE_ONCE (pchan->chan_in_use, true); ret =pcc_chan_reg_read_modify_write (&pchan->db); -if (!ret && pchan->plat_irq >0 ) - pchan->chan_in_use = true; +if (ret && pchan->plat_irq >0 ) +WRITE_ONCE (pchan->chan_in_use, false);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1150/1376 null_blk: use DEFINE_MUTEX for the file-scope mutex
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 017dac7670909eaea3eb36e6b3b5a8be9ce0a14d ]
In null_init(), mutex_init(&lock) currently happens after configfs_register_subsystem(), which exposes the nullb subsystem to userspace. A racing mkdir() into /sys/kernel/config/nullb/ can reach null_find_dev_by_name() -> mutex_lock(&lock) before the mutex is initialized, trigger warning:
[ 123.137788] DEBUG_LOCKS_WARN_ON(lock->magic != lock) [ 123.137796] WARNING: kernel/locking/mutex.c:159 at mutex_lock+0x171/0x1c0, CPU#13: mkdir/1301 [ 123.140090] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4 ...... [ 123.154926] Call Trace: [ 123.155172] <TASK> [ 123.155419] ? __pfx_mutex_lock+0x10/0x10 [ 123.156181] ? __pfx__raw_spin_lock+0x10/0x10 [ 123.156571] nullb_group_make_group+0x20/0x100 [null_blk] [ 123.157011] configfs_mkdir+0x47b/0xc70 [ 123.157337] ? __pfx_configfs_mkdir+0x10/0x10 [ 123.157719] ? may_create_dentry+0x242/0x2e0 [ 123.158061] vfs_mkdir+0x2a9/0x6c0 [ 123.158352] filename_mkdirat+0x3dc/0x500 [ 123.158710] ? __pfx_filename_mkdirat+0x10/0x10 [ 123.159070] ? strncpy_from_user+0x3a/0x1d0 [ 123.159413] __x64_sys_mkdir+0x6b/0x90 [ 123.159760] do_syscall_64+0xea/0x600
Replace the runtime mutex_init(&lock) with a static DEFINE_MUTEX(lock) declaration to fix this issue.
--- drivers/block/null_blk/main.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
drivers/block/null_blk/main.c +1 -3
@@ -66,7 +66,7 @@ struct nullb_page {#define NULLB_PAGE_FREE (MAP_SZ - 2)static LIST_HEAD (nullb_list); -static struct mutex lock; +static DEFINE_MUTEX (lock);static int null_major;static DEFINE_IDA (nullb_indexes);static struct blk_mq_tag_set tag_set;
@@ -2109,8 +2109,6 @@ static int __init null_init(void)if (ret)return ret; -mutex_init (&lock); - null_major =register_blkdev (0 ,"nullb" );if (null_major <0 ) { ret = null_major;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1151/1376 null_blk: register configfs subsystem after creating default devices
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit c9d293d6bb0575fcb1f3408129453187e2a28a4e ]
In null_init(), configfs_register_subsystem() currently runs before register_blkdev(), so when null_blk is built as a module, a racing mkdir()
+ poweron from userspace can reach null_add_dev() while null_major is still
0. __add_disk() then hits WARN_ON(disk->minors) (major=0 with minors!=0) and fails:
[root@fedora ~]# [ 2366.521436] WARNING: block/genhd.c:476 at __add_disk+0x8a7/0xde0,
[ 2366.523552] Modules linked in: null_blk(+) nft_fib_inet nft_fib_ipv4 nft_fib_ipv6 nft_fib [ 2366.529081] CPU: 26 UID: 0 PID: 1600 Comm: sh Not tainted 7.2.0-rc1+ #66 PREEMPT(full) ...... [ 2366.547251] Call Trace: [ 2366.547575] <TASK> [ 2366.547831] ? _raw_spin_lock+0x84/0xe0 [ 2366.548260] add_disk_fwnode+0x114/0x560 [ 2366.548739] null_add_dev+0x102d/0x1b80 [null_blk] [ 2366.549310] ? __pfx_null_add_dev+0x10/0x10 [null_blk] [ 2366.549906] ? mutex_lock+0xde/0x1c0 [ 2366.550361] ? __pfx_mutex_lock+0x10/0x10 [ 2366.550827] nullb_device_power_store+0x1e7/0x280 [null_blk] [ 2366.551499] ? __pfx_nullb_device_power_store+0x10/0x10 [null_blk] [ 2366.552177] ? __kmalloc_cache_noprof+0x1f5/0x470 [ 2366.552748] ? configfs_write_iter+0x35c/0x4e0 [ 2366.553242] configfs_write_iter+0x286/0x4e0 [ 2366.553787] vfs_write+0x52d/0xd00 [ 2366.554169] ? __pfx_vfs_write+0x10/0x10 [ 2366.554679] ? __pfx___css_rstat_updated+0x10/0x10 [ 2366.555196] ? fdget_pos+0x1cf/0x4c0 [ 2366.555649] ksys_write+0xfc/0x1d0
......
Additionally, the err_dev path destroys all devices on nullb_list while configfs is still registered. If a racing mkdir() + poweron puts a user device on the list, null_destroy_dev()->null_free_dev() kfrees the user device's nullb_device but /sys/kernel/config/nullb/<name> is still reachable. Any userspace access to the item will trigger a UAF.
For simplicity, move configfs_register_subsystem() to the end to solve the problems above.
--- drivers/block/null_blk/main.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-)
drivers/block/null_blk/main.c +6 -10
@@ -2105,15 +2105,9 @@ static int __init null_init(void)config_group_init (&nullb_subsys.su_group);mutex_init (&nullb_subsys.su_mutex); - ret =configfs_register_subsystem (&nullb_subsys); -if (ret) -return ret; - null_major =register_blkdev (0 ,"nullb" ); -if (null_major <0 ) { - ret = null_major; - goto err_conf; - } +if (null_major <0 ) +return null_major;for (i =0 ; i < nr_devices; i++) { ret =null_create_dev ();
@@ -2121,6 +2115,10 @@ static int __init null_init(void) goto err_dev; } + ret =configfs_register_subsystem (&nullb_subsys); +if (ret) + goto err_dev; +pr_info ("module loaded\n" );return 0 ;
@@ -2130,8 +2128,6 @@ static int __init null_init(void)null_destroy_dev (nullb); }unregister_blkdev (null_major,"nullb" ); -err_conf: -configfs_unregister_subsystem (&nullb_subsys);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1152/1376 null_blk: free global tag_set on init error path
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 5a1c5ff3a49ba93a1fd0b70537e7a0164071760d ]
If shared_tags is enabled, null_setup_tagset() allocates the global tag_set via null_init_global_tag_set(). If device creation later fails, err_dev destroys the default devices and calls unregister_blkdev(), but never frees the global tag_set. Since module init failed, null_exit() is never invoked, so the global tag_set's tags and maps are permanently leaked.
Free the global tag_set in err_dev, matching null_exit() which does if (tag_set.ops) blk_mq_free_tag_set(&tag_set).
--- drivers/block/null_blk/main.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/block/null_blk/main.c +2 -0
@@ -2128,6 +2128,8 @@ static int __init null_init(void)null_destroy_dev (nullb); }unregister_blkdev (null_major,"nullb" ); +if (tag_set.ops) +blk_mq_free_tag_set (&tag_set);return ret; }
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1153/1376 null_blk: free zones array on device power-off
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 2a6357a9b935a34f5508618fee8a7fffbf7722a8 ]
null_init_zoned_dev() allocates dev->zones when a zoned device is powered on, but null_del_dev() never frees it on power-off; dev->zones is only freed later in null_free_dev(), when the configfs directory is removed. If the device is powered off and then on again, null_init_zoned_dev() allocates a new array and overwrites the dev->zones pointer, leaking the previous allocation each power cycle.
Free dev->zones in null_del_dev() via null_free_zoned_dev() to solve it. And calling null_free_zoned_dev() in null_free_dev() is no longer necessary because every caller already invokes null_del_dev() first: via nullb_group_drop_item() before nullb_device_release(), in the null_add_dev() error path of null_create_dev(), and in null_destroy_dev(). Remove the redundant call.
And take &lock around zone_cond_store() in the two store wrappers to serialize dev->zones check-and-deref against its alloc/free, which already run under &lock. The reason there was no problem before is that only nullb_device_release() or null_exit() frees the dev->zones, which guarantees that subsequent users won't access the configfs interface.
--- drivers/block/null_blk/main.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/block/null_blk/main.c +13 -3
@@ -572,8 +572,13 @@ static ssize_t nullb_device_zone_readonly_store(struct config_item *item, const char *page, size_t count) {struct nullb_device *dev =to_nullb_device (item); +ssize_t ret; + +mutex_lock (&lock); + ret =zone_cond_store (dev, page, count,BLK_ZONE_COND_READONLY ); +mutex_unlock (&lock); -return zone_cond_store (dev, page, count,BLK_ZONE_COND_READONLY ); +return ret; }CONFIGFS_ATTR_WO (nullb_device_, zone_readonly);
@@ -581,8 +586,13 @@ static ssize_t nullb_device_zone_offline_store(struct config_item *item, const char *page, size_t count) {struct nullb_device *dev =to_nullb_device (item); +ssize_t ret; -return zone_cond_store (dev, page, count,BLK_ZONE_COND_OFFLINE ); +mutex_lock (&lock); + ret =zone_cond_store (dev, page, count,BLK_ZONE_COND_OFFLINE ); +mutex_unlock (&lock); + +return ret; }CONFIGFS_ATTR_WO (nullb_device_, zone_offline);
@@ -812,7 +822,6 @@ static void null_free_dev(struct nullb_device *dev)if (!dev)return ; -null_free_zoned_dev (dev);badblocks_exit (&dev->badblocks);kfree (dev); }
@@ -1721,6 +1730,7 @@ static void null_del_dev(struct nullb *nullb) }put_disk (nullb->disk); +null_free_zoned_dev (dev);if (nullb->tag_set == &nullb->__tag_set)blk_mq_free_tag_set (nullb->tag_set);kfree (nullb->queues);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1154/1376 null_blk: reject per-device queue resize for shared tag set
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 1cdfe2fa62b48728a9b436fbbd3dbe4c11593e24 ]
When shared_tags is enabled, null_setup_tagset() makes the device use the global tag_set, whose driver_data stays NULL. null_map_queues() therefore falls back to the module-wide g_submit_queues/g_poll_queues instead of any per-device value.
Resizing submit_queues or poll_queues via configfs on such a device calls blk_mq_update_nr_hw_queues() on the shared set, shrinking set->nr_hw_queues. __blk_mq_realloc_hw_ctxs() only grows the q->queue_hw_ctx[] allocation, so on shrink it merely exits and NULLs the now-excess hctx slots. null_map_queues(), however, keeps mapping CPUs with the unchanged g_submit_queues/g_poll_queues, so mq_map[] ends up pointing at those NULLed hctx slots. blk_mq_map_swqueue() then dereferences the NULL hctx (hctx->cpumask), crashing the kernel:
[ 460.218374] KASAN: null-ptr-deref in range [0x0000000000000098-0x000000000000009f] [ 460.219003] CPU: 24 UID: 0 PID: 1492 Comm: sh Not tainted 7.2.0-rc2+ #67 PREEMPT(full) [ 460.219792] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-4.fc41 04/01/2014 [ 460.220452] RIP: 0010:blk_mq_map_swqueue+0x4db/0x1430 ...... [ 460.228977] Call Trace: [ 460.229175] <TASK> [ 460.229354] blk_mq_update_nr_hw_queues+0xd49/0x11c0 [ 460.229779] ? __pfx_blk_mq_update_nr_hw_queues+0x10/0x10 [ 460.230200] nullb_update_nr_hw_queues+0x1a9/0x370 [null_blk] [ 460.230694] nullb_device_submit_queues_store+0xd9/0x170 [null_blk] [ 460.231190] ? __pfx_nullb_device_submit_queues_store+0x10/0x10 [null_blk] [ 460.231776] ? configfs_write_iter+0x35c/0x4e0 [ 460.232122] configfs_write_iter+0x286/0x4e0 [ 460.232460] vfs_write+0x52d/0xd00 [ 460.232779] ? __x64_sys_openat+0x108/0x1d0 [ 460.233106] ? __pfx_vfs_write+0x10/0x10 [ 460.233413] ? fdget_pos+0x1cf/0x4c0 [ 460.233745] ? fput_close+0x133/0x190 [ 460.234038] ? __pfx_expand_files+0x10/0x10 [ 460.234368] ksys_write+0xfc/0x1d0
Reproducer:
modprobe null_blk shared_tags=1 submit_queues=64 poll_queues=1 mkdir /sys/kernel/config/nullb/dev
echo 1 > /sys/kernel/config/nullb/dev/power
echo 1 > /sys/kernel/config/nullb/dev/submit_queues
A per-device resize of a shared tag set is meaningless anyway, so reject it with -EINVAL in nullb_update_nr_hw_queues() when the device is bound to the global tag_set.
--- drivers/block/null_blk/main.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/block/null_blk/main.c +9 -0
@@ -378,6 +378,15 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,if (!dev->nullb)return 0 ; +/* +* A shared tag_set is mapped via the module-wide queue counts, so a +* per-device resize is meaningless. On shrink it would also leave +* mq_map[] pointing at NULLed hctx slots, causing a NULL deref in +* blk_mq_map_swqueue(). Reject it. +*/ +if (dev->shared_tags) +return -EINVAL ; +/* * Make sure at least one submit queue exists. */
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1155/1376 null_blk: serialize configfs attribute stores with the lock
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Zizhi Wo <[email protected]>
[ Upstream commit 7e7fff51808237703a3a1df6dd5cae1dfd1db86d ]
The NULLB_DEVICE_ATTR _store takes no lock: apply_fn attributes (submit_queues, poll_queues) get dev->NAME written again after apply_fn returns, outside its lock; APPLY=NULL attributes are entirely lockless. configfs only serializes stores per-open-file, so concurrent stores on separate fds race.
For apply_fn attributes, once one store's apply_fn has reconfigured the hardware, a second (losing) store can still overwrite dev->NAME afterwards. This leaves dev->submit_queues out of sync with the live queue count, which is later caught by the WARN_ON_ONCE() in null_map_queues().
For !apply_fn attributes, power_store()'s null_add_dev() validates and builds the device under "lock" but only sets CONFIGURED afterwards. A store slipping in during this window can change a field mid-setup -- for example, zone_nr_conv can be pushed above nr_zones after it has already been clamped, leading to an out-of-bounds dev->zones[] access.
Take "lock" in the macro around the apply_fn call, the CONFIGURED test and the field write, and move it out of nullb_apply_submit_queues()/ nullb_apply_poll_queues() so both paths are covered once. This serializes stores with power_store's setup and with each other.
--- drivers/block/null_blk/main.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-)
drivers/block/null_blk/main.c +3 -14
@@ -356,6 +356,7 @@ nullb_device_##NAME##_store(struct config_item *item, const char *page, \ ret = nullb_device_##TYPE##_attr_store(&new_value, page, count);\ if (ret < 0) \ return ret; \ + guard(mutex)(&lock); \ if (apply_fn) \ ret = apply_fn(dev, new_value); \ else if (test_bit(NULLB_DEV_FL_CONFIGURED, &dev->flags)) \
@@ -426,25 +427,13 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,static int nullb_apply_submit_queues (struct nullb_device *dev,unsigned int submit_queues) { -int ret; - -mutex_lock (&lock); - ret =nullb_update_nr_hw_queues (dev, submit_queues, dev->poll_queues); -mutex_unlock (&lock); - -return ret; +return nullb_update_nr_hw_queues (dev, submit_queues, dev->poll_queues); }static int nullb_apply_poll_queues (struct nullb_device *dev,unsigned int poll_queues) { -int ret; - -mutex_lock (&lock); - ret =nullb_update_nr_hw_queues (dev, dev->submit_queues, poll_queues); -mutex_unlock (&lock); - -return ret; +return nullb_update_nr_hw_queues (dev, dev->submit_queues, poll_queues); }NULLB_DEVICE_ATTR (size, ulong,NULL );
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1156/1376 null_blk: serialize configfs attribute updates with device setup
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Niklas Cassel <[email protected]>
[ Upstream commit 4e1f23f9c33c156be7e313b40695af5a3a834739 ]
The attribute store methods generated with NULLB_DEVICE_ATTR() refuse to change the configuration of a live device by testing NULLB_DEV_FL_CONFIGURED, but that flag is only set by nullb_device_power_store() after null_add_dev() has returned, and the store methods take no lock at all. configfs only serializes writes to the same open file (buffer->mutex), so a write to any attribute can run concurrently with null_add_dev() and change the device configuration while it is being used.
null_add_dev() reads the configuration several times, e.g. dev->zoned is read once to set up the queue limits and once to initialize the zone resources:
CPU0: echo 1 > nullb0/power CPU1: echo 1 > nullb0/zoned
nullb_device_power_store()
mutex_lock(&lock)
null_add_dev()
if (dev->zoned) -> false
/* no BLK_FEAT_ZONED */ nullb_device_zoned_store()
test_bit(FL_CONFIGURED) -> 0
dev->zoned = true
blk_mq_alloc_disk()
/* queue is not zoned */
if (nullb->dev->zoned) -> true
null_register_zoned_dev()
blk_revalidate_disk_zones()blk_revalidate_disk_zones() is then called for a queue that does not have BLK_FEAT_ZONED set, which triggers its WARN_ON_ONCE() and fails the device setup with -EIO:
WARNING: CPU: 2 PID: 322 at block/blk-zoned.c:2357 blk_revalidate_disk_zones+0x4c/0x560
Clearing dev->zoned in the same window is worse: the queue is created with BLK_FEAT_ZONED but the zone resources are never initialized, so add_disk() succeeds for a zoned disk that has no zones. And a store that lands after the last dev->zoned test leaves dev->zoned set while dev->zones is still NULL, which null_process_zoned_cmd() dereferences on the first write.
Fix this by taking the global lock, which nullb_device_power_store() already holds across null_add_dev() and null_del_dev(), around both the NULLB_DEV_FL_CONFIGURED test and the update of the device configuration. The submit_queues and poll_queues apply callbacks are now called with that lock held, so remove the locking they did themselves.
Since the store methods can run as soon as configfs_register_subsystem() returns, that is, before null_init() gets to mutex_init(&lock), also initialize the lock statically with DEFINE_MUTEX().
--- drivers/block/null_blk/main.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
drivers/block/null_blk/main.c +11 -3
@@ -336,7 +336,15 @@ static ssize_t nullb_device_bool_attr_store(bool *val, const char *page,return count; } -/* The following macro should only be used with TYPE = {uint, ulong, bool}. */ +/* +* The following macro should only be used with TYPE = {uint, ulong, bool}. +* +* The device configuration is modified under the global lock to serialize +* attribute changes against null_add_dev() and null_del_dev(): without this, +* an attribute could be changed while null_add_dev() is running, that is, +* before NULLB_DEV_FL_CONFIGURED is set, which would let null_add_dev() +* observe inconsistent values for the device configuration. +*/ #define NULLB_DEVICE_ATTR (NAME ,TYPE ,APPLY ) \ static ssize_t \ nullb_device_##NAME##_show(struct config_item *item, char *page) \
@@ -376,6 +384,8 @@ static int nullb_update_nr_hw_queues(struct nullb_device *dev,struct blk_mq_tag_set *set;int ret, nr_hw_queues; +lockdep_assert_held (&lock); +if (!dev->nullb)return 0 ;
@@ -2158,8 +2168,6 @@ static void __exit null_exit(void)if (tag_set.ops)blk_mq_free_tag_set (&tag_set); - -mutex_destroy (&lock); }module_init (null_init);
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1157/1376 ublk: reject non-power-of-2 zone sizes in SET_PARAMS
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Yao Sang <[email protected]>
[ Upstream commit 2707acf1856da266139986c9398ad722fd4f48c0 ]
UBLK_F_ZONED uses params.basic.chunk_sectors as zone size. ublk uses ilog2(chunk_sectors) to get number of zones, so the value must be power of 2.
If chunk_sectors is 96 and dev_sectors is 96 * 16, userspace asks for 16 zones. But the shift calculation gets 24 zones.
Block layer rejects such zone size when the disk is started. But SET_PARAMS has already returned success, which is confusing for userspace. Reject it in SET_PARAMS with other zoned parameter checks.
--- drivers/block/ublk_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
drivers/block/ublk_drv.c +1 -1
@@ -543,7 +543,7 @@ static int ublk_validate_params(const struct ublk_device *ub)if (p->max_sectors <PAGE_SECTORS )return -EINVAL ; -if (ublk_dev_is_zoned (ub) && !p->chunk_sectors) +if (ublk_dev_is_zoned (ub) && !is_power_of_2 (p->chunk_sectors))return -EINVAL ; }else return -EINVAL ;
Signature · 1 line
2.53.0
Greg Kroah-Hartman
maintainerPATCH 1158/1376 block: mtip32xx: synchronize ioctls with device removal
In reply to Greg Kroah-Hartman6.12-stable review patch. If anyone has any objections, please let me know.
------------------
From: Hongyan Xu <[email protected]>
[ Upstream commit 68940f841d013192086a0f6d7cfbac2cd079e228 ]
The ioctl handlers only test REMOVE_PENDING before entering mtip_hw_ioctl(). Removal can set that bit immediately afterwards and free dd->port in mtip_hw_exit() while an ioctl still dereferences it. An already open block device can reach the handlers while del_gendisk() is in progress.
Serialize both native and compat ioctls with removal. Set REMOVE_PENDING before taking the mutex so new callers fail after an in-flight ioctl has drained, and hold the mutex until the port has been torn down.
--- drivers/block/mtip32xx/mtip32xx.c | 7 +++++++ drivers/block/mtip32xx/mtip32xx.h | 2 ++ 2 files changed, 9 insertions(+)
drivers/block/mtip32xx/mtip32xx.c +7 -0
@@ -3038,6 +3038,8 @@ static int mtip_block_ioctl(struct block_device *dev,if (!dd)return -ENOTTY ; +guard (mutex)(&dd->ioctl_mutex); +if (unlikely (test_bit (MTIP_DDF_REMOVE_PENDING_BIT , &dd->dd_flag)))return -ENOTTY ;
@@ -3076,6 +3078,8 @@ static int mtip_block_compat_ioctl(struct block_device *dev,if (!dd)return -ENOTTY ; +guard (mutex)(&dd->ioctl_mutex); +if (unlikely (test_bit (MTIP_DDF_REMOVE_PENDING_BIT , &dd->dd_flag)))return -ENOTTY ;
@@ -3700,6 +3704,7 @@ static int mtip_pci_probe(struct pci_dev *pdev, dd =kzalloc_node (sizeof (struct driver_data ),GFP_KERNEL , my_node);if (!dd)return -ENOMEM ; +mutex_init (&dd->ioctl_mutex);/* Attach the private data to this PCI device. */ pci_set_drvdata (pdev, dd);
@@ -3872,6 +3877,7 @@ static void mtip_pci_remove(struct pci_dev *pdev) }set_bit (MTIP_DDF_REMOVE_PENDING_BIT , &dd->dd_flag); +mutex_lock (&dd->ioctl_mutex);if (test_bit (MTIP_DDF_INIT_DONE_BIT , &dd->dd_flag))del_gendisk (dd->disk);
@@ -3900,6 +3906,7 @@ static void mtip_pci_remove(struct pci_dev *pdev)/* De-initialize the protocol layer. */ mtip_hw_exit (dd); +mutex_unlock (&dd->ioctl_mutex);if (dd->isr_workq) {destroy_workqueue (dd->isr_workq);
drivers/block/mtip32xx/mtip32xx.h +2 -0
@@ -12,6 +12,7 @@#define __MTIP32XX_H__#include <linux/spinlock.h> +#include <linux/mutex.h> #include <linux/rwsem.h> #include <linux/ata.h> #include <linux/interrupt.h>
@@ -432,6 +433,7 @@ struct driver_data {struct request_queue *queue;/* Our request queue. */ struct blk_mq_tag_set tags;/* blk_mq tags */ +struct mutex ioctl_mutex;struct mtip_port *port;/* Pointer to the port data structure. */
Signature · 1 line
2.53.0