[PATCH OLK-6.6 0/8] arm64: Add FEAT_TLBID support
TLBI broadcasting all PEs introduces performance noise. By combining hardware and software, TLBID (TLBI Domain) limit TLBI to an appropriate scope, avoiding the performance overhead caused by broadcasting. Arm Architecture defines the FEAT_TLBID extension since Armv9.3, when present, allows the OSPM to target TLB invalidations to a specific domain. A TLBI domain can be contained in multiple parent domains. The TLBI table lists all the PE and SMMU components and the TLBI Domains that each component belongs to. The feature is detected at runtime, and will remain disabled if the system does not implement the feature. Jinjiang Tu (1): arm64: tlbflush: Optimize flush_tlb_mm() by using TLBID Liao Chang (1): acpi: arm64: Add basic skeleton for TLBI Domains Marc Zyngier (4): arm64: cpufeature: Add ID_AA64MMFR4_EL1 handling arm64: sysreg: Add layout for ID_AA64MMFR4_EL1 arm64: cpufeatures: Add missing ID_AA64MMFR4_EL1 to __read_sysreg_by_encoding() arm64: sysreg: Update ID_AA64MMFR4_EL1 description Zeng Heng (2): arm64: cpufeature: Add TLBID (Domain-based TLB Invalidation) detection arm64: mm: Track CPUs that a task has run on for TLBID optimization arch/arm64/Kconfig | 20 ++ arch/arm64/include/asm/cpu.h | 2 + arch/arm64/include/asm/cpufeature.h | 13 ++ arch/arm64/include/asm/tlbflush.h | 66 +++++- arch/arm64/include/asm/tlbidomain.h | 34 +++ arch/arm64/kernel/cpufeature.c | 20 ++ arch/arm64/kernel/cpuinfo.c | 4 + arch/arm64/mm/Makefile | 1 + arch/arm64/mm/context.c | 9 +- arch/arm64/mm/tlbidomain.c | 339 ++++++++++++++++++++++++++++ arch/arm64/tools/cpucaps | 2 +- arch/arm64/tools/sysreg | 64 ++++++ drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/init.c | 2 + drivers/acpi/arm64/init.h | 1 + drivers/acpi/arm64/tlbid.c | 107 +++++++++ drivers/acpi/tables.c | 9 + include/acpi/actbl2.h | 29 +++ include/linux/acpi.h | 1 + 19 files changed, 718 insertions(+), 6 deletions(-) create mode 100644 arch/arm64/include/asm/tlbidomain.h create mode 100644 arch/arm64/mm/tlbidomain.c create mode 100644 drivers/acpi/arm64/tlbid.c -- 2.43.0
From: Marc Zyngier <maz@kernel.org> mainline inclusion from mainline-v6.9-rc1 commit 805bb61f827997c59b92669ae8cc3740ebcf1087 category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... ---------------------------------------- Add ID_AA64MMFR4_EL1 to the list of idregs the kernel knows about, and describe the E2H0 field. Reviewed-by: Oliver Upton <oliver.upton@linux.dev> Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Link: https://lore.kernel.org/r/20240122181344.258974-6-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Conflicts: arch/arm64/kernel/cpufeature.c [Fix context conflicts.] Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/include/asm/cpu.h | 1 + arch/arm64/kernel/cpufeature.c | 7 +++++++ arch/arm64/kernel/cpuinfo.c | 1 + 3 files changed, 9 insertions(+) diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 40113c406d7d..56eb090ae48d 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -58,6 +58,7 @@ struct cpuinfo_arm64 { u64 reg_id_aa64mmfr1; u64 reg_id_aa64mmfr2; u64 reg_id_aa64mmfr3; + u64 reg_id_aa64mmfr4; u64 reg_id_aa64pfr0; u64 reg_id_aa64pfr1; u64 reg_id_aa64pfr2; diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 86e5404fbfae..ff9dfdc93c01 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -446,6 +446,11 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr3[] = { ARM64_FTR_END, }; +static const struct arm64_ftr_bits ftr_id_aa64mmfr4[] = { + S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR4_EL1_E2H0_SHIFT, 4, 0), + ARM64_FTR_END, +}; + static const struct arm64_ftr_bits ftr_ctr[] = { ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RES1 */ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, CTR_EL0_DIC_SHIFT, 1, 1), @@ -792,6 +797,7 @@ static const struct __ftr_reg_entry { &id_aa64mmfr1_override), ARM64_FTR_REG(SYS_ID_AA64MMFR2_EL1, ftr_id_aa64mmfr2), ARM64_FTR_REG(SYS_ID_AA64MMFR3_EL1, ftr_id_aa64mmfr3), + ARM64_FTR_REG(SYS_ID_AA64MMFR4_EL1, ftr_id_aa64mmfr4), /* Op1 = 0, CRn = 1, CRm = 2 */ ARM64_FTR_REG(SYS_ZCR_EL1, ftr_zcr), @@ -1092,6 +1098,7 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info) init_cpu_ftr_reg(SYS_ID_AA64MMFR1_EL1, info->reg_id_aa64mmfr1); init_cpu_ftr_reg(SYS_ID_AA64MMFR2_EL1, info->reg_id_aa64mmfr2); init_cpu_ftr_reg(SYS_ID_AA64MMFR3_EL1, info->reg_id_aa64mmfr3); + init_cpu_ftr_reg(SYS_ID_AA64MMFR4_EL1, info->reg_id_aa64mmfr4); init_cpu_ftr_reg(SYS_ID_AA64PFR0_EL1, info->reg_id_aa64pfr0); init_cpu_ftr_reg(SYS_ID_AA64PFR1_EL1, info->reg_id_aa64pfr1); init_cpu_ftr_reg(SYS_ID_AA64PFR2_EL1, info->reg_id_aa64pfr2); diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index fe1b511464c2..56b90127e78a 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -326,6 +326,7 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) info->reg_id_aa64mmfr1 = read_cpuid(ID_AA64MMFR1_EL1); info->reg_id_aa64mmfr2 = read_cpuid(ID_AA64MMFR2_EL1); info->reg_id_aa64mmfr3 = read_cpuid(ID_AA64MMFR3_EL1); + info->reg_id_aa64mmfr4 = read_cpuid(ID_AA64MMFR4_EL1); info->reg_id_aa64pfr0 = read_cpuid(ID_AA64PFR0_EL1); info->reg_id_aa64pfr1 = read_cpuid(ID_AA64PFR1_EL1); info->reg_id_aa64pfr2 = read_cpuid(ID_AA64PFR2_EL1); -- 2.43.0
From: Marc Zyngier <maz@kernel.org> mainline inclusion from mainline-v6.9-rc1 commit cfc680bb04c54e61faa51a34d8383a0aa25b583f category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... ---------------------------------------- ARMv9.5 has infroduced ID_AA64MMFR4_EL1 with a bunch of new features. Add the corresponding layout. This is extracted from the public ARM SysReg_xml_A_profile-2023-09 delivery, timestamped d55f5af8e09052abe92a02adf820deea2eaed717. Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Reviewed-by: Miguel Luis <miguel.luis@oracle.com> Link: https://lore.kernel.org/r/20240122181344.258974-5-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/tools/sysreg | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index c4ff6cf83623..864f7d0b67ed 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -2240,6 +2240,43 @@ UnsignedEnum 3:0 TCRX EndEnum EndSysreg +Sysreg ID_AA64MMFR4_EL1 3 0 0 7 4 +Res0 63:40 +UnsignedEnum 39:36 E3DSE + 0b0000 NI + 0b0001 IMP +EndEnum +Res0 35:28 +SignedEnum 27:24 E2H0 + 0b0000 IMP + 0b1110 NI_NV1 + 0b1111 NI +EndEnum +UnsignedEnum 23:20 NV_frac + 0b0000 NV_NV2 + 0b0001 NV2_ONLY +EndEnum +UnsignedEnum 19:16 FGWTE3 + 0b0000 NI + 0b0001 IMP +EndEnum +UnsignedEnum 15:12 HACDBS + 0b0000 NI + 0b0001 IMP +EndEnum +UnsignedEnum 11:8 ASID2 + 0b0000 NI + 0b0001 IMP +EndEnum +SignedEnum 7:4 EIESB + 0b0000 NI + 0b0001 ToEL3 + 0b0010 ToELx + 0b1111 ANY +EndEnum +Res0 3:0 +EndSysreg + Sysreg SCTLR_EL1 3 0 1 0 0 Field 63 TIDCP Field 62 SPINTMASK -- 2.43.0
From: Marc Zyngier <maz@kernel.org> mainline inclusion from mainline-v6.9-rc1 commit 87b8cf2387c5ee79576988b2e72b84eeb92c57ec category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... ---------------------------------------- When triggering a CPU hotplug scenario, we reparse the CPU feature with SCOPE_LOCAL_CPU, for which we use __read_sysreg_by_encoding() to get the HW value for this CPU. As it turns out, we're missing the handling for ID_AA64MMFR4_EL1, and trigger a BUG(). Funnily enough, Marek isn't completely happy about that. Add the damn register to the list. Fixes: 805bb61f8279 ("arm64: cpufeature: Add ID_AA64MMFR4_EL1 handling") Reported-by: Marek Szyprowski <m.szyprowski@samsung.com> Tested-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20240212144736.1933112-2-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev> Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/kernel/cpufeature.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index ff9dfdc93c01..3dd327850d35 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -1499,6 +1499,7 @@ u64 __read_sysreg_by_encoding(u32 sys_id) read_sysreg_case(SYS_ID_AA64MMFR1_EL1); read_sysreg_case(SYS_ID_AA64MMFR2_EL1); read_sysreg_case(SYS_ID_AA64MMFR3_EL1); + read_sysreg_case(SYS_ID_AA64MMFR4_EL1); read_sysreg_case(SYS_ID_AA64ISAR0_EL1); read_sysreg_case(SYS_ID_AA64ISAR1_EL1); read_sysreg_case(SYS_ID_AA64ISAR2_EL1); -- 2.43.0
From: Marc Zyngier <maz@kernel.org> mainline inclusion from mainline-v6.16-rc1 commit eef33835bf6f297faa222f48bf941d57d2f8bda0 category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... ---------------------------------------- Resync the ID_AA64MMFR4_EL1 with the architectue description. This results in: - the new PoPS field - the new NV2P1 value for the NV_frac field - the new RMEGDI field - the new SRMASK field These fields have been generated from the reference JSON file. Reviewed-by: Joey Gouly <joey.gouly@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/tools/sysreg | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 864f7d0b67ed..4770bc4b7690 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -2241,12 +2241,21 @@ EndEnum EndSysreg Sysreg ID_AA64MMFR4_EL1 3 0 0 7 4 -Res0 63:40 +Res0 63:48 +UnsignedEnum 47:44 SRMASK + 0b0000 NI + 0b0001 IMP +EndEnum +Res0 43:40 UnsignedEnum 39:36 E3DSE 0b0000 NI 0b0001 IMP EndEnum -Res0 35:28 +Res0 35:32 +UnsignedEnum 31:28 RMEGDI + 0b0000 NI + 0b0001 IMP +EndEnum SignedEnum 27:24 E2H0 0b0000 IMP 0b1110 NI_NV1 @@ -2255,6 +2264,7 @@ EndEnum UnsignedEnum 23:20 NV_frac 0b0000 NV_NV2 0b0001 NV2_ONLY + 0b0010 NV2P1 EndEnum UnsignedEnum 19:16 FGWTE3 0b0000 NI @@ -2274,7 +2284,10 @@ SignedEnum 7:4 EIESB 0b0010 ToELx 0b1111 ANY EndEnum -Res0 3:0 +UnsignedEnum 3:0 PoPS + 0b0000 NI + 0b0001 IMP +EndEnum EndSysreg Sysreg SCTLR_EL1 3 0 1 0 0 -- 2.43.0
hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 ---------------------------------------- Add detection for the ARM64 Domain-based TLB Invalidation (TLBID) feature as defined in ARMv9.3-A architecture. TLBID allows TLB invalidation operations to be scoped to a specific domain, avoiding the need for global TLB invalidation broadcasts across the system. This reduces cache coherency traffic and improves performance in virtualization scenarios. Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/Kconfig | 20 ++++++++++++++++++++ arch/arm64/include/asm/cpufeature.h | 6 ++++++ arch/arm64/kernel/cpufeature.c | 10 ++++++++++ arch/arm64/tools/cpucaps | 2 +- arch/arm64/tools/sysreg | 5 ++++- 5 files changed, 41 insertions(+), 2 deletions(-) diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index ca1041ff7f8c..bdf9c643a8e6 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -2290,6 +2290,26 @@ config ARM64_TLB_RANGE The feature introduces new assembly instructions, and they were support when binutils >= 2.30. +config ARM64_TLBID + bool "Enable support for TLBID (TLBI Domains)" + default y + help + TLBI broadcasting all PEs introduces performance noise. By + combining hardware and software, TLBID (TLBI Domain) limit TLBI + to an appropriate scope, avoiding the performance overhead caused + by broadcasting. + + Arm Architecture defines the FEAT_TLBID extension since Armv9.3, + when present, allows the OSPM to target TLB invalidations to + a specific domain. A TLBI domain can be contained in multiple + parent domains. + + The TLBI table lists all the PE and SMMU components and the TLBI + Domains that each component belongs to. + + The feature is detected at runtime, and will remain disabled + if the system does not implement the feature. + config ARM64_MPAM bool "Enable support for MPAM" select ACPI_MPAM if ACPI diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index fed81fd3baf2..9ff5141518b4 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -892,6 +892,12 @@ static inline bool system_supports_tlb_range(void) cpus_have_const_cap(ARM64_HAS_TLB_RANGE); } +static inline bool system_supports_tlbid(void) +{ + return IS_ENABLED(CONFIG_ARM64_TLBID) && + cpus_have_const_cap(ARM64_HAS_TLBID); +} + static inline bool cpus_support_mpam(void) { return IS_ENABLED(CONFIG_ARM64_MPAM) && diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 3dd327850d35..63115dfa0236 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -447,6 +447,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr3[] = { }; static const struct arm64_ftr_bits ftr_id_aa64mmfr4[] = { + ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR4_EL1_TLBID_SHIFT, 4, 0), S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR4_EL1_E2H0_SHIFT, 4, 0), ARM64_FTR_END, }; @@ -3256,6 +3257,15 @@ static const struct arm64_cpu_capabilities arm64_features[] = { .type = ARM64_CPUCAP_SYSTEM_FEATURE, .matches = has_copy_opt, }, +#endif +#ifdef CONFIG_ARM64_TLBID + { + .desc = "Domain based ARM64 TLB invalidation", + .capability = ARM64_HAS_TLBID, + .type = ARM64_CPUCAP_SYSTEM_FEATURE, + .matches = has_cpuid_feature, + ARM64_CPUID_FIELDS(ID_AA64MMFR4_EL1, TLBID, IMP) + }, #endif {}, }; diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps index f6445266e886..7320731020ef 100644 --- a/arch/arm64/tools/cpucaps +++ b/arch/arm64/tools/cpucaps @@ -116,7 +116,7 @@ HAS_HW_XCALL_XINT WORKAROUND_PHYTIUM_FT3386 HAS_COPY_OPT HAS_LSUI -KABI_RESERVE_9 +HAS_TLBID KABI_RESERVE_10 KABI_RESERVE_11 KABI_RESERVE_12 diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 4770bc4b7690..470b07be5d2c 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -2246,7 +2246,10 @@ UnsignedEnum 47:44 SRMASK 0b0000 NI 0b0001 IMP EndEnum -Res0 43:40 +UnsignedEnum 43:40 TLBID + 0b0000 NI + 0b0001 IMP +EndEnum UnsignedEnum 39:36 E3DSE 0b0000 NI 0b0001 IMP -- 2.43.0
From: Liao Chang <liaochang1@huawei.com> hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 ---------------------------------------- Add the initial infrastructure to support the Armv9.3 FEAT_TLBID extension, which allows OSPM to target TLB invalidations to a specific domain. A TLBI domain can be contained in multiple parent domains. This patch provides: - Kconfig option CONFIG_ARM64_TLBID to enable the feature - TLBIDIDR_EL1 register definition and CPU feature detection - ACPI TLBI table parser to discover TLBI domains for processors and SMMU [1]. - Data structures to manage TLBI domain to CPU mapping. - Help functions to choose the best TLBI domain for a specific set of CPUs. The TLBI table lists all the PE and SMMU components and the TLBI domains that each component belongs to. This information is used to optimize TLB invalidation by targeting specific domains instead of broadcasting to all CPUs. [1] https://github.com/user-attachments/files/25049127/ACPI_TLBID_v3.docx [2] https://github.com/masoncl/review-prompts.git CC: Xia Qinxin <xiaqinxin@huawei.com> CC: Zheng Tian <zhengtian10@huawei.com> CC: Yang Jinqian <yangjinqian1@huawei.com> CC: Wang Kefeng <wangkefeng.wang@huawei.com> CC: Yu Jiacheng <yujiacheng3@huawei.com> Signed-off-by: Liao Chang <liaochang1@huawei.com> Signed-off-by: Xinyu Zheng <zhengxinyu6@huawei.com> Signed-off-by: Zeng Heng <zengheng4@huawei.com> ------- v2->v3: 1. Fix bug about the calculation of number of system-wide domains. 2. Fix bug about the TLBID sanitization for vCPU hotplug 3. Polish implementation based on the review feedback 4. Fix compilation error for including tlbidomain.h in tlbflush.h 5. Fix fatal bug in validate_ids() that causes TLBID does't take effect ------- v1->v2: The /kreview skill in [2] discover 4 bugs: 1. validate_ids() incorrectly validates tlbid_data.nvis and tlbid_data.nvos instead of the passed parameters. 2. pick_best_domain() out-of-bounds access: Accesses cpu_domain[nr_cpu] without checking if nr_cpu < nr_domain. 3. tlbid_chip_data_init() has NULL pointer dereference due to find_target() can return NULL. 4. tlbid_chip_data_init() accesses cpu_domain[nr_cpu] where nr_cpu can exceed nr_domain. --- arch/arm64/include/asm/cpu.h | 1 + arch/arm64/include/asm/cpufeature.h | 7 + arch/arm64/include/asm/tlbidomain.h | 34 +++ arch/arm64/kernel/cpufeature.c | 2 + arch/arm64/kernel/cpuinfo.c | 3 + arch/arm64/mm/Makefile | 1 + arch/arm64/mm/tlbidomain.c | 339 ++++++++++++++++++++++++++++ arch/arm64/tools/sysreg | 11 + drivers/acpi/arm64/Makefile | 1 + drivers/acpi/arm64/init.c | 2 + drivers/acpi/arm64/init.h | 1 + drivers/acpi/arm64/tlbid.c | 107 +++++++++ drivers/acpi/tables.c | 9 + include/acpi/actbl2.h | 29 +++ include/linux/acpi.h | 1 + 15 files changed, 548 insertions(+) create mode 100644 arch/arm64/include/asm/tlbidomain.h create mode 100644 arch/arm64/mm/tlbidomain.c create mode 100644 drivers/acpi/arm64/tlbid.c diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h index 56eb090ae48d..ede40c40d410 100644 --- a/arch/arm64/include/asm/cpu.h +++ b/arch/arm64/include/asm/cpu.h @@ -47,6 +47,7 @@ struct cpuinfo_arm64 { u64 reg_gmid; u64 reg_smidr; u64 reg_mpamidr; + u64 reg_tlbididr; u64 reg_id_aa64dfr0; u64 reg_id_aa64dfr1; diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h index 9ff5141518b4..8d46fff3c104 100644 --- a/arch/arm64/include/asm/cpufeature.h +++ b/arch/arm64/include/asm/cpufeature.h @@ -641,6 +641,13 @@ static inline bool id_aa64pfr1_mte(u64 pfr1) return val >= ID_AA64PFR1_EL1_MTE_MTE2; } +static inline bool id_aa64mmfr4_tlbid(u64 mmfr4) +{ + u32 val = cpuid_feature_extract_unsigned_field(mmfr4, ID_AA64MMFR4_EL1_TLBID_SHIFT); + + return val > 0; +} + void __init setup_cpu_features(void); void check_local_cpu_capabilities(void); diff --git a/arch/arm64/include/asm/tlbidomain.h b/arch/arm64/include/asm/tlbidomain.h new file mode 100644 index 000000000000..1dac3771ff57 --- /dev/null +++ b/arch/arm64/include/asm/tlbidomain.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2026 Huawei Ltd. + */ +#ifndef __ASM_TLBIDOMAIN_H +#define __ASM_TLBIDOMAIN_H + +#include <linux/types.h> + +struct tlbi_target { + u32 identifier; + s32 *domains; + s32 nr_domain; + struct list_head list; +}; + +#define TLBI_RT_DOMAIN_WIDTH (16) +#define TLBI_INV_DOMAIN (-1) + +#ifdef CONFIG_ARM64_TLBID +void add_target(int type, struct tlbi_target *target); +int pick_best_domain(const cpumask_t *active_cpus); +int __init parse_tlbid_topology(void); +int __init tlbid_data_init(void); +void __init tlbid_data_reset(void); +void __init tlbid_early_init(void); + +#else +static inline int pick_best_domain(const cpumask_t *active_cpus) { return 0; } +static inline void __init tlbid_early_init(void) { } + +#endif /* ! CONFIG_ARM64_TLBID */ +#endif + diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index 63115dfa0236..0ff73f95279b 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c @@ -95,6 +95,7 @@ #include <asm/traps.h> #include <asm/vectors.h> #include <asm/virt.h> +#include <asm/tlbidomain.h> /* Kernel representation of AT_HWCAP and AT_HWCAP2 */ static DECLARE_BITMAP(elf_hwcap, MAX_CPU_FEATURES) __read_mostly; @@ -3951,6 +3952,7 @@ void __init setup_cpu_features(void) sve_setup(); sme_setup(); + tlbid_early_init(); minsigstksz_setup(); mpam_extra_caps(); diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c index 56b90127e78a..b320e9211127 100644 --- a/arch/arm64/kernel/cpuinfo.c +++ b/arch/arm64/kernel/cpuinfo.c @@ -340,6 +340,9 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info) if (id_aa64pfr0_32bit_el0(info->reg_id_aa64pfr0)) __cpuinfo_store_cpu_32bit(&info->aarch32); + if (id_aa64mmfr4_tlbid(info->reg_id_aa64mmfr4)) + info->reg_tlbididr = read_cpuid(TLBIDIDR_EL1); + if (IS_ENABLED(CONFIG_ARM64_MPAM) && mpam_detect_is_enabled() && (id_aa64pfr0_mpam(info->reg_id_aa64pfr0) || diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile index c02aeb729717..4d92d42826bf 100644 --- a/arch/arm64/mm/Makefile +++ b/arch/arm64/mm/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_TRANS_TABLE) += trans_pgd.o obj-$(CONFIG_TRANS_TABLE) += trans_pgd-asm.o obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o obj-$(CONFIG_ARM64_MTE) += mteswap.o +obj-$(CONFIG_ARM64_TLBID) += tlbidomain.o KASAN_SANITIZE_physaddr.o += n obj-$(CONFIG_KASAN) += kasan_init.o diff --git a/arch/arm64/mm/tlbidomain.c b/arch/arm64/mm/tlbidomain.c new file mode 100644 index 000000000000..9133167943d2 --- /dev/null +++ b/arch/arm64/mm/tlbidomain.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file implements the kernel functions rely on FEAT_TLBID + * + * Copyright (C) 2026 Huawei Ltd. + */ + +#define pr_fmt(fmt) "tlbidomain: " fmt + +#include <linux/acpi.h> +#include <linux/cpumask.h> + +#include <asm/cpu.h> +#include <asm/sysreg.h> +#include <asm/tlbidomain.h> +#include <asm/cpufeature.h> + +struct tlbi_domain { + cpumask_t cpumask; + int id; +}; + +struct tlbid_data { + struct list_head target_list[ACPI_TLBI_TYPE_RESERVED]; + unsigned long *found_domains; + u32 nr_domain; + struct tlbi_domain *cpu_domain[NR_CPUS]; + u8 nis; /* TLBIDIDR_EL1.NIS */ + u8 nvis; /* TLBIDIDR_EL1.NVIS */ + u8 nos; /* TLBIDIDR_EL1.NOS */ + u8 nvos; /* TLBIDIDR_EL1.NVOS */ +}; + +/* + * Global TLBI domain data. + * Actual supported domain bits will be reduced based on hardware capability. + */ +static struct tlbid_data tlbid_data __read_mostly = { + .nr_domain = 1, + .nis = TLBI_RT_DOMAIN_WIDTH, + .nvis = TLBI_RT_DOMAIN_WIDTH, + .nos = TLBI_RT_DOMAIN_WIDTH, + .nvos = TLBI_RT_DOMAIN_WIDTH, +}; + +/* + * validate_ids - Validate TLBID domain bit configurations + * + * Validates that NIS/NVIS and NOS/NVOS configurations follow Arm architecture + * constraints: if outer domain bits are zero, inner domain bits must also be + * zero. Outputs error and zeroes out all domain counts if validation fails. + */ +static void validate_ids(u8 nis, u8 nvis, u8 nos, u8 nvos) +{ + bool inv = false; + + switch (nos) { + case 0: + inv = tlbid_data.nvos > 0; + break; + case 1 ... 8: + inv = tlbid_data.nvos > 6; + break; + case 9 ... 16: + inv = tlbid_data.nvos > 5; + break; + } + + if (inv) { + pr_err("Illegal value of NVOS(%u) for NOS(%u)\n", + tlbid_data.nvos, tlbid_data.nos); + goto on_error; + } + + switch (nis) { + case 0: + inv = tlbid_data.nvis > 0; + break; + case 1 ... 8: + inv = tlbid_data.nvis > 6; + break; + case 9 ... 16: + inv = tlbid_data.nvis > 5; + break; + } + + if (!inv) + return; + + pr_err("Illegal value of NVIS(%u) for NIS(%u)\n", + tlbid_data.nvis, tlbid_data.nis); +on_error: + tlbid_data.nis = 0; + tlbid_data.nvis = 0; + tlbid_data.nos = 0; + tlbid_data.nvos = 0; +} + +/* + * find_target - Find TLBI target by type and identifier + * + * Looks up the tlbi_target structure associated with the given type and cpu. + * For PROCESSOR type, converts CPU logical ID to ACPI Processor UID. + */ +static struct tlbi_target *find_target(int type, int cpu) +{ + struct tlbi_target *target = NULL; + struct list_head *head; + int uid; + + switch (type) { + case ACPI_TLBI_TYPE_PROCESSOR: + uid = get_acpi_id_for_cpu(cpu); + break; + default: + pr_err("Only support Processor as TLBI broadcast target\n"); + return NULL; + } + + head = &tlbid_data.target_list[type]; + list_for_each_entry(target, head, list) { + if (target->identifier == uid) + break; + } + + return target; +} + +void add_target(int type, struct tlbi_target *target) +{ + s32 i; + + for (i = 0; i < target->nr_domain; i++) + set_bit(target->domains[i], tlbid_data.found_domains); + + INIT_LIST_HEAD(&target->list); + list_add_tail(&target->list, &tlbid_data.target_list[type]); +} + +/* + * pick_best_domain - Select optimal TLBI domain for given CPUs + * + * Iterates through cpu_domain table to find a domain whose cpumask exactly + * matches the given active CPUs. This allows targeted TLBI instead of + * broadcasting to all CPUs, improving TLB invalidation efficiency. + * Returns domain ID on success, 0 on failure. + */ +int pick_best_domain(const cpumask_t *active_cpus) +{ + struct tlbi_domain *domain; + cpumask_t tmp; + int nr_cpu; + + /* Domain 0 is the only supported for Innern Shareable TLBI */ + if (unlikely(tlbid_data.nr_domain == 1)) + return 0; + + nr_cpu = cpumask_weight(active_cpus); + if (unlikely(nr_cpu == 0)) + return 0; + + do { + domain = &tlbid_data.cpu_domain[nr_cpu][0]; + while (domain->id != TLBI_INV_DOMAIN) { + if (cpumask_and(&tmp, active_cpus, &domain->cpumask) && + cpumask_equal(&tmp, active_cpus)) + return domain->id; + domain++; + } + } while (++nr_cpu < num_possible_cpus()); + + return 0; +} + +int __init tlbid_data_init(void) +{ + s32 max_domains = (1 << max(tlbid_data.nis, tlbid_data.nos)); + int type; + + if (!tlbid_data.nis || !tlbid_data.nos) + return -EINVAL; + + tlbid_data.found_domains = kcalloc(BITS_TO_LONGS(max_domains), + sizeof(long), GFP_KERNEL); + if (!tlbid_data.found_domains) + return -ENOMEM; + + type = ACPI_TLBI_TYPE_PROCESSOR; + while (type != ACPI_TLBI_TYPE_RESERVED) + INIT_LIST_HEAD(&tlbid_data.target_list[type++]); + + return 0; +} + +void __init tlbid_data_reset(void) +{ + kfree(tlbid_data.found_domains); + tlbid_data.found_domains = NULL; + tlbid_data.nr_domain = 1; + tlbid_data.nis = 0; + tlbid_data.nos = 0; + tlbid_data.nvis = 0; + tlbid_data.nvos = 0; +} + +/* + * parse_tlbid_topology - Initialize TLBI domain mapping tables + * + * Allocates and populates the cpu_domain table: + * 1. Build CPU-to-domain mapping from ACPI-parsed target data + * 2. Reorganize data for efficient lookup (domain x CPU matrix) + * + * Returns 0 on success, -ENOMEM on allocation failure, -EVINAL on topology + * parse failure + */ +int __init parse_tlbid_topology(void) +{ + struct tlbi_domain *src, *dst; + struct tlbi_target *target; + int cpu, nr_cpu, err; + s32 i, j; + + tlbid_data.nr_domain = bitmap_weight(tlbid_data.found_domains, + ((1<<TLBI_RT_DOMAIN_WIDTH)-1)); + if (tlbid_data.nr_domain >= (1 << max(tlbid_data.nis, tlbid_data.nos))) { + pr_err("TLBI Domains ACPI reports are more than CPU support\n"); + err = -EINVAL; + goto on_error; + } + pr_info("found %d TLBI Domain(s).\n", tlbid_data.nr_domain); + + /* + * Allocate a sparse table to map the CPU count to domain list sharing + * the same CPU count. + * + * NOTICE: Each domain is assumed to contain all system-wide SMMUs, + * Any broadcast in a specific domain must wait replies from all SMMUs + * before it complete. + */ + for (nr_cpu = 0; nr_cpu < num_possible_cpus(); nr_cpu++) { + tlbid_data.cpu_domain[nr_cpu] = + kcalloc(tlbid_data.nr_domain, + sizeof(struct tlbi_domain), GFP_KERNEL); + if (!tlbid_data.cpu_domain[nr_cpu]) { + err = -ENOMEM; + goto on_error; + } + } + + for (i = 0; i < tlbid_data.nr_domain; i++) { + cpumask_clear(&tlbid_data.cpu_domain[0][i].cpumask); + tlbid_data.cpu_domain[0][i].id = i; + } + + /* Initialize the cpumask associated with each tlbi domain */ + for_each_possible_cpu(cpu) { + target = find_target(ACPI_TLBI_TYPE_PROCESSOR, cpu); + if (!target) { + err = -EINVAL; + goto on_error; + } + i = 0; + while ((j = target->domains[i++]) != TLBI_INV_DOMAIN) { + src = &tlbid_data.cpu_domain[0][j]; + /* Record all CPUs belong to the same domain */ + cpumask_set_cpu(cpu, &(src->cpumask)); + } + } + + /* Move each domain data onto the right position in sparse table */ + for (i = 0; i < tlbid_data.nr_domain; i++) { + src = &tlbid_data.cpu_domain[0][i]; + nr_cpu = cpumask_weight(&(src->cpumask)); + j = 0; + do { + dst = &tlbid_data.cpu_domain[nr_cpu][j++]; + } while (dst->id != TLBI_INV_DOMAIN); + memcpy(dst, src, sizeof(*dst)); + cpumask_clear(&src->cpumask); + src->id = TLBI_INV_DOMAIN; + } + + return 0; + +on_error: + for (nr_cpu = 0; nr_cpu < num_possible_cpus(); nr_cpu++) + kfree(tlbid_data.cpu_domain[nr_cpu]); + return err; +} + +/* + * tlbid_early_init - Early initialization of TLBID hardware capability after + * all CPUs activate. + * + * Called early during boot from setup_system_features() before ACPI is parsed. + * Reads TLBIDIDR_EL1 register on each CPU to discover hardware capabilities + * (NIS, NVIS, NOS, NVOS bits), and takes the minimum across all CPUs. + * Validates the combined configuration via validate_ids(). + */ +void __init tlbid_early_init(void) +{ + u16 nis, nos, nvis, nvos; + u64 tlbididr; + int cpu; + + if (!system_supports_tlbid()) { + tlbid_data.nis = 0; + tlbid_data.nos = 0; + tlbid_data.nvis = 0; + tlbid_data.nvos = 0; + return; + } + + for_each_online_cpu(cpu) { + tlbididr = per_cpu_ptr(&cpu_data, cpu)->reg_tlbididr; + nis = FIELD_GET(TLBIDIDR_EL1_NIS_MASK, tlbididr); + nos = FIELD_GET(TLBIDIDR_EL1_NOS_MASK, tlbididr); + nvis = FIELD_GET(TLBIDIDR_EL1_NVIS_MASK, tlbididr); + nvos = FIELD_GET(TLBIDIDR_EL1_NVOS_MASK, tlbididr); + + if (tlbid_data.nis > nis) + tlbid_data.nis = nis; + + if (tlbid_data.nos > nos) + tlbid_data.nos = nos; + + if (tlbid_data.nvis > nvis) + tlbid_data.nvis = nvis; + + if (tlbid_data.nvos > nvos) + tlbid_data.nvos = nvos; + } + + validate_ids(tlbid_data.nis, tlbid_data.nvis, + tlbid_data.nos, tlbid_data.nvos); + + pr_info("TLBI Domain bits: nis %d nos %d nvis %d nvos %d\n", + tlbid_data.nis, tlbid_data.nos, tlbid_data.nvis, tlbid_data.nvos); +} diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg index 470b07be5d2c..5a76321691c5 100644 --- a/arch/arm64/tools/sysreg +++ b/arch/arm64/tools/sysreg @@ -3524,3 +3524,14 @@ Field 5 F Field 4 P Field 3:0 Align EndSysreg + +Sysreg TLBIDIDR_EL1 3 0 10 4 6 +Res0 63:45 +Field 44:40 NVOS +Res0 39:37 +Field 36:32 NOS +Res0 31:13 +Field 12:8 NVIS +Res0 7:5 +Field 4:0 NIS +EndSysreg diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile index 9497a7777729..2c068491f16e 100644 --- a/drivers/acpi/arm64/Makefile +++ b/drivers/acpi/arm64/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_ACPI_GTDT) += gtdt.o obj-$(CONFIG_ACPI_APMT) += apmt.o obj-$(CONFIG_ARM_AMBA) += amba.o obj-$(CONFIG_ACPI_MPAM) += mpam.o +obj-$(CONFIG_ARM64_TLBID) += tlbid.o obj-y += dma.o init.o diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c index d0c8aed90fd1..309f29b63f5d 100644 --- a/drivers/acpi/arm64/init.c +++ b/drivers/acpi/arm64/init.c @@ -12,4 +12,6 @@ void __init acpi_arm_init(void) acpi_iort_init(); if (IS_ENABLED(CONFIG_ARM_AMBA)) acpi_amba_init(); + if (IS_ENABLED(CONFIG_ARM64_TLBID)) + acpi_tlbi_init(); } diff --git a/drivers/acpi/arm64/init.h b/drivers/acpi/arm64/init.h index dcc277977194..c036e2622a2f 100644 --- a/drivers/acpi/arm64/init.h +++ b/drivers/acpi/arm64/init.h @@ -5,3 +5,4 @@ void __init acpi_agdi_init(void); void __init acpi_apmt_init(void); void __init acpi_iort_init(void); void __init acpi_amba_init(void); +void __init acpi_tlbi_init(void); diff --git a/drivers/acpi/arm64/tlbid.c b/drivers/acpi/arm64/tlbid.c new file mode 100644 index 000000000000..3268c61982ba --- /dev/null +++ b/drivers/acpi/arm64/tlbid.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * This file implements handling of + * Arm Translation Lookaside Buffer Invalidate Domain table (TLBI) + * + * Copyright (C) 2026, Huawei Ltd. + */ +#define pr_fmt(fmt) "ACPI: TLBID: " fmt + +#include <linux/acpi.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/slab.h> + +#include "init.h" + +#include <asm/tlbidomain.h> + +static struct acpi_table_header *acpi_tlbid_table; + +/* + * tlbid_parse_subtable - Parse ACPI TLBI subtable entry + * + * Called for each entry in the ACPI TLBI table. Allocates a tlbi_target + * structure and copies domain IDs from the ACPI table. Builds the + * found_domains bitmap for later domain count calculation. + */ +static int __init tlbid_parse_subtable(union acpi_subtable_headers *header, + unsigned long end) +{ + struct acpi_tlbi_subtable *hdr = (struct acpi_tlbi_subtable *)header; + struct tlbi_target *target; + + if (!hdr) + return -EINVAL; + + target = kzalloc(sizeof(*target), GFP_KERNEL); + if (!target) + return -ENOMEM; + + /* The ACPI Processor UID of the corresponding GICC MADT entry */ + target->nr_domain = hdr->nr_domain; + target->identifier = hdr->identifier; + target->domains = kcalloc(hdr->nr_domain + 1, sizeof(*target->domains), + GFP_KERNEL); + if (!target->domains) { + kfree(target); + return -ENOMEM; + } + + memcpy(target->domains, + (void *)acpi_tlbid_table + hdr->offset, + hdr->nr_domain * sizeof(*target->domains)); + target->domains[hdr->nr_domain] = TLBI_INV_DOMAIN; + + add_target(header->tlbi.type, target); + + return 0; +} + +/* + * acpi_tlbi_init - Initialize TLBID from ACPI table + * + * Main entry point for TLBI table parsing. Gets the ACPI TLBI table, + * parses Processor and SMMU subtable entries, calculates total domain + * count, validates against hardware capability, and calls + * tlbid_chip_data_init() to build domain mapping tables. + * Outputs appropriate error messages on failure. + */ +void __init acpi_tlbi_init(void) +{ + enum acpi_tlbi_target_type type; + acpi_status status; + int ret; + + if (tlbid_data_init() < 0) + goto on_error; + + status = acpi_get_table(ACPI_SIG_TLBI, 0, &acpi_tlbid_table); + if (ACPI_FAILURE(status)) + goto on_error; + + type = ACPI_TLBI_TYPE_PROCESSOR; + while (type != ACPI_TLBI_TYPE_RESERVED) { + ret = acpi_table_parse_entries(ACPI_SIG_TLBI, + sizeof(struct acpi_table_tlbi), + type, tlbid_parse_subtable, 0); + if (ret) { + pr_err("Failed to get TLBID subtable for %s\n", + type == ACPI_TLBI_TYPE_PROCESSOR ? "Processor":"SMMU"); + goto on_error; + } + type++; + } + + if (parse_tlbid_topology()) { + pr_warn("Failed to initialize TLBI Domain data\n"); + goto on_error; + } + + acpi_put_table(acpi_tlbid_table); + return; + +on_error: + tlbid_data_reset(); + acpi_put_table(acpi_tlbid_table); +} diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 4fca04c8dd2d..62d786110ebf 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -42,6 +42,7 @@ enum acpi_subtable_type { ACPI_SUBTABLE_HMAT, ACPI_SUBTABLE_PRMT, ACPI_SUBTABLE_CEDT, + ACPI_SUBTABLE_TLBI, }; struct acpi_subtable_entry { @@ -287,6 +288,8 @@ acpi_get_entry_type(struct acpi_subtable_entry *entry) return 0; case ACPI_SUBTABLE_CEDT: return entry->hdr->cedt.type; + case ACPI_SUBTABLE_TLBI: + return entry->hdr->tlbi.type; } return 0; } @@ -303,6 +306,8 @@ acpi_get_entry_length(struct acpi_subtable_entry *entry) return entry->hdr->prmt.length; case ACPI_SUBTABLE_CEDT: return entry->hdr->cedt.length; + case ACPI_SUBTABLE_TLBI: + return entry->hdr->tlbi.length; } return 0; } @@ -319,6 +324,8 @@ acpi_get_subtable_header_length(struct acpi_subtable_entry *entry) return sizeof(entry->hdr->prmt); case ACPI_SUBTABLE_CEDT: return sizeof(entry->hdr->cedt); + case ACPI_SUBTABLE_TLBI: + return sizeof(entry->hdr->tlbi); } return 0; } @@ -332,6 +339,8 @@ acpi_get_subtable_type(char *id) return ACPI_SUBTABLE_PRMT; if (strncmp(id, ACPI_SIG_CEDT, 4) == 0) return ACPI_SUBTABLE_CEDT; + if (strncmp(id, ACPI_SIG_TLBI, 4) == 0) + return ACPI_SUBTABLE_TLBI; return ACPI_SUBTABLE_COMMON; } diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h index 97b05bd334a5..29dda0318c0f 100644 --- a/include/acpi/actbl2.h +++ b/include/acpi/actbl2.h @@ -26,6 +26,7 @@ */ #define ACPI_SIG_AGDI "AGDI" /* Arm Generic Diagnostic Dump and Reset Device Interface */ #define ACPI_SIG_APMT "APMT" /* Arm Performance Monitoring Unit table */ +#define ACPI_SIG_TLBI "TLBI" /* Translation Lookaside Buffer Invalidate Domain Table */ #define ACPI_SIG_BDAT "BDAT" /* BIOS Data ACPI Table */ #define ACPI_SIG_CCEL "CCEL" /* CC Event Log Table */ #define ACPI_SIG_CDAT "CDAT" /* Coherent Device Attribute Table */ @@ -341,6 +342,34 @@ enum acpi_apmt_node_type { #define ACPI_APMT_OVFLW_IRQ_FLAGS_TYPE_WIRED (0<<1) +/******************************************************************************* + * TLBI - Arm Translation Lookaside Buffer Invalidation Domain Table (TLBI) + * + ******************************************************************************/ +struct acpi_table_tlbi { + struct acpi_table_header header; /* Common ACPI table header */ + u32 nr_subtable; +}; + +struct acpi_tlbi_header { + u16 type; + u16 reserved; + u32 length; +}; + +struct acpi_tlbi_subtable { + struct acpi_tlbi_header header; + u32 identifier; + u32 nr_domain; + u32 offset; +}; + +enum acpi_tlbi_target_type { + ACPI_TLBI_TYPE_PROCESSOR = 1, + ACPI_TLBI_TYPE_SMMU, + ACPI_TLBI_TYPE_RESERVED, +}; + /******************************************************************************* * * BDAT - BIOS Data ACPI Table diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e59a74273e6e..acd1a9241829 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -127,6 +127,7 @@ union acpi_subtable_headers { struct acpi_hmat_structure hmat; struct acpi_prmt_module_header prmt; struct acpi_cedt_header cedt; + struct acpi_tlbi_header tlbi; }; typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table); -- 2.43.0
hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 ---------------------------------------- To utilize TLBID feature effectively, we need to know which CPUs a given mm (address space) has been active on. This patch implements cumulative CPU tracking for each mm: - On new ASID allocation (new_context()): Clear the cpumask to start fresh. This handles both new processes and ASID generation wrap-around cases. - On context switch (check_and_switch_context()): Set the current CPU in mm_cpumask(mm) if TLBID is supported. The tracking is cumulative (CPUs are never cleared except on ASID re-allocation). While this may include CPUs where the task no longer runs, TLB invalidation to a superset remains functionally correct. This infrastructure enables subsequent flush_tlb_mm() can use domain-based invalidation instead of full broadcast when the mm's CPU footprint is limited. Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/mm/context.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c index b2ac06246327..004c08dec441 100644 --- a/arch/arm64/mm/context.c +++ b/arch/arm64/mm/context.c @@ -207,6 +207,9 @@ static u64 new_context(struct mm_struct *mm) asid = find_next_zero_bit(asid_map, NUM_USER_ASIDS, 1); set_asid: + if (system_supports_tlbid()) + cpumask_clear(mm_cpumask(mm)); + __set_bit(asid, asid_map); cur_idx = asid; return asid2ctxid(asid, generation); @@ -215,8 +218,8 @@ static u64 new_context(struct mm_struct *mm) void check_and_switch_context(struct mm_struct *mm) { unsigned long flags; - unsigned int cpu; u64 asid, old_active_asid; + unsigned int cpu = smp_processor_id(); if (system_supports_cnp()) cpu_set_reserved_ttbr0(); @@ -251,7 +254,6 @@ void check_and_switch_context(struct mm_struct *mm) atomic64_set(&mm->context.id, asid); } - cpu = smp_processor_id(); if (cpumask_test_and_clear_cpu(cpu, &tlb_flush_pending)) local_flush_tlb_all(); @@ -262,6 +264,9 @@ void check_and_switch_context(struct mm_struct *mm) arm64_apply_bp_hardening(); + if (system_supports_tlbid()) + cpumask_set_cpu(cpu, mm_cpumask(mm)); + /* * Defer TTBR0_EL1 setting for user threads to uaccess_enable() when * emulating PAN. -- 2.43.0
From: Jinjiang Tu <tujinjiang@huawei.com> hulk inclusion category: feature bugzilla: https://atomgit.com/openeuler/kernel/issues/9795 ---------------------------------------- This patch optimizes flush_tlb_mm() by using TLBID when available: - Local invalidation: When the mm is only active on the current CPU, use non-shareable TLB invalidation (aside1) for better performance. - Domain-based invalidation: When the mm is active on multiple CPUs but not all, use TLBID to target only the relevant CPUs, reducing cache coherency traffic compared to full broadcast. - Broadcast fallback: When TLBID is not supported or when targeting all CPUs, maintain the existing behavior (aside1is). The scope detection logic (flush_tlb_user_scope()) determines the optimal invalidation strategy based on mm_cpumask() and TLBID capabilities. This optimization is particularly beneficial for: - Workloads with many short-lived processes - Systems with high CPU counts where broadcast TLB shootdowns are costly - Scenarios where process memory is mostly local to a subset of CPUs Signed-off-by: Jinjiang Tu <tujinjiang@huawei.com> Signed-off-by: Zeng Heng <zengheng4@huawei.com> --- arch/arm64/include/asm/tlbflush.h | 66 +++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h index c082cace5683..a0555b81c1e5 100644 --- a/arch/arm64/include/asm/tlbflush.h +++ b/arch/arm64/include/asm/tlbflush.h @@ -16,6 +16,7 @@ #include <linux/mmu_notifier.h> #include <asm/cputype.h> #include <asm/mmu.h> +#include <asm/tlbidomain.h> /* * Raw TLBI operations. @@ -165,6 +166,51 @@ static inline unsigned long get_trans_granule(void) (__pages >> (5 * (scale) + 1)) - 1; \ }) +enum tlb_flush_scope { + TLB_FLUSH_SCOPE_LOCAL, + TLB_FLUSH_SCOPE_DOMAIN, + TLB_FLUSH_SCOPE_BROADCAST, +}; + +#ifdef CONFIG_ARM64_TLBID +/* This macro creates a properly formatted VA operand for the TLBID */ +#define __TLBI_DOMAIN(asid, domain) \ + ({ \ + unsigned long __ta = 0; \ + __ta |= FIELD_PREP(GENMASK_ULL(15, 0), domain); \ + __ta |= FIELD_PREP(GENMASK_ULL(63, 48), asid); \ + __ta; \ + }) + +/* + * Determines whether the user tlbi invalidation can be performed only on the + * local CPU or whether it needs to be multicast or broadcast. + */ +static inline enum tlb_flush_scope flush_tlb_user_scope(struct mm_struct *mm) +{ + if (!system_supports_tlbid()) + return TLB_FLUSH_SCOPE_BROADCAST; + + /* check if the tlbflush needs to be sent to other CPUs */ + if (cpumask_any_but(mm_cpumask(mm), smp_processor_id()) >= + nr_cpu_ids) + return TLB_FLUSH_SCOPE_LOCAL; + + return TLB_FLUSH_SCOPE_DOMAIN; +} +#else +#define __TLBI_DOMAIN(asid, domain) \ + ( \ + (void)(domain), \ + __TLBI_VADDR(0, (asid)) \ + ) + +static inline enum tlb_flush_scope flush_tlb_user_scope(struct mm_struct *mm) +{ + return TLB_FLUSH_SCOPE_BROADCAST; +} +#endif + /* * TLB Invalidation * ================ @@ -252,12 +298,26 @@ static inline void flush_tlb_all(void) static inline void flush_tlb_mm(struct mm_struct *mm) { + enum tlb_flush_scope scope; unsigned long asid; + int domain; dsb(ishst); - asid = __TLBI_VADDR(0, ASID(mm)); - __tlbi(aside1is, asid); - __tlbi_user(aside1is, asid); + scope = flush_tlb_user_scope(mm); + if (scope == TLB_FLUSH_SCOPE_LOCAL) { + asid = __TLBI_VADDR(0, ASID(mm)); + __tlbi(aside1, asid); + __tlbi_user(aside1, asid); + } else if (scope == TLB_FLUSH_SCOPE_BROADCAST) { + asid = __TLBI_VADDR(0, ASID(mm)); + __tlbi(aside1is, asid); + __tlbi_user(aside1is, asid); + } else { + domain = pick_best_domain(mm_cpumask(mm)); + asid = __TLBI_DOMAIN(ASID(mm), domain); + __tlbi(aside1is, asid); + __tlbi_user(aside1is, asid); + } dsb(ish); mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL); } -- 2.43.0
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://atomgit.com/openeuler/kernel/merge_requests/27500 邮件列表地址:https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/2KZ... FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://atomgit.com/openeuler/kernel/merge_requests/27500 Mailing list address: https://mailweb.openeuler.org/archives/list/kernel@openeuler.org/message/2KZ...
participants (2)
-
patchwork bot -
Zeng Heng