* [RFC 00/12] arm64: Add support for TLBI domains
@ 2026-10-01 11:06 Kristina Martšenko
2026-10-01 11:06 ` [RFC 01/12] arm64: sysreg: Add definitions for FEAT_TLBID Kristina Martšenko
` (13 more replies)
0 siblings, 14 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Hi,
This series allows the kernel to use TLBI domains. TLBI domains (FEAT_TLBID) is
an Arm architecture feature that enables a TLB invalidation to target a subset
of CPUs instead of the whole system. It can improve performance on systems with
many CPUs. TLBID is not yet part of the Arm Architecture Reference Manual but
some of it is described in the latest XML [1].
The goal of this series is to send TLBI commands only to the CPUs where a
process has run (i.e. where its page tables have been installed). The last
patch explains a bit about how it works. This is an RFC since I'm not familiar
with the kernel's TLB/mm code and I'd like feedback on whether the general
approach is correct.
A few notes and open questions:
* Domains are described in a new "TLBI" ACPI table. This is not yet part of
the ACPI specification. There was an initial proposal [2] but this series
uses a newer WIP version of the table that aims to be published by the end
of the year.
* There is currently no device tree binding since the feature is mainly meant
for server-class systems.
* It's also possible to use TLBI domains to limit a KVM guest's TLB
invalidations to CPUs where the guest has run. I'll try to include that in
the next version. This series only disables domains for guests.
* With this series a long-running process may eventually run on all CPUs and
just issue broadcast TLBIs. It may be worth periodically reducing the set
of CPUs but it's not clear when.
* There are currently no performance numbers since there is no hardware with
this feature yet.
* The series has been tested on an Arm FVP which supports the TLBID
architecture but does not have any domains (except domain 0).
Any feedback is welcome.
Thanks,
Kristina
[1] https://support.arm.com/architectures/a-profile-architecture#Downloads
[2] https://github.com/tianocore/edk2/issues/11999
Kristina Martšenko (12):
arm64: sysreg: Add definitions for FEAT_TLBID
arm64: Detect FEAT_TLBID
KVM: arm64: Hide TLBID from guest sysregs
KVM: arm64: Hide TLBID from guest instructions
ACPICA: Add TLBI table definition
ACPI: TLBI: Parse domains from table
arm64: tlbid: Set up CPU domain bitmaps
efi/arm: Check return value of init_new_context()
arm64: tlbid: Track the TLBI domain of a task
arm64: Support TLBIP instructions
arm64: tlbid: Pass domain to TLBI instructions
arm64: tlbid: Add documentation
Documentation/arch/arm64/booting.rst | 10 ++
arch/arm64/Kconfig | 12 ++
arch/arm64/include/asm/acpi.h | 10 ++
arch/arm64/include/asm/cpu.h | 1 +
arch/arm64/include/asm/cpufeature.h | 15 ++
arch/arm64/include/asm/el2_setup.h | 76 +++++++-
arch/arm64/include/asm/kvm_emulate.h | 5 +
arch/arm64/include/asm/mmu.h | 13 ++
arch/arm64/include/asm/mmu_context.h | 9 +-
arch/arm64/include/asm/tlbflush.h | 219 +++++++++++++++++-------
arch/arm64/kernel/cpufeature.c | 25 +++
arch/arm64/kernel/cpuinfo.c | 3 +
arch/arm64/kvm/config.c | 6 +
arch/arm64/kvm/emulate-nested.c | 1 +
arch/arm64/kvm/hyp/include/hyp/switch.h | 7 +-
arch/arm64/kvm/hyp/nvhe/tlb.c | 2 +-
arch/arm64/kvm/hyp/vhe/tlb.c | 4 +-
arch/arm64/kvm/sys_regs.c | 3 +
arch/arm64/mm/Makefile | 1 +
arch/arm64/mm/context.c | 6 +-
arch/arm64/mm/tlbid.c | 210 +++++++++++++++++++++++
arch/arm64/tools/cpucaps | 1 +
arch/arm64/tools/sysreg | 53 +++++-
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/init.c | 2 +
drivers/acpi/arm64/init.h | 1 +
drivers/acpi/arm64/tlbi.c | 196 +++++++++++++++++++++
drivers/firmware/efi/arm-runtime.c | 3 +-
include/acpi/actbl2.h | 32 ++++
30 files changed, 858 insertions(+), 72 deletions(-)
create mode 100644 arch/arm64/mm/tlbid.c
create mode 100644 drivers/acpi/arm64/tlbi.c
base-commit: 93f51579e7df248780214094418f205253383cc5
--
2.43.0
^ permalink raw reply [flat|nested] 20+ messages in thread
* [RFC 01/12] arm64: sysreg: Add definitions for FEAT_TLBID
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 02/12] arm64: Detect FEAT_TLBID Kristina Martšenko
` (12 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Describe new system registers and fields added by FEAT_TLBID, so that
later patches can use them. This is based on the latest system register
specifications (version 2026-09, see Link).
The size and number of fields in VTLBID* registers is determined by
information in other registers at runtime, so can't be described
statically here.
Link: https://support.arm.com/architectures/a-profile-architecture#Downloads
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/tools/sysreg | 53 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 50 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/tools/sysreg b/arch/arm64/tools/sysreg
index e2d37ee221b8..b134418ff3aa 100644
--- a/arch/arm64/tools/sysreg
+++ b/arch/arm64/tools/sysreg
@@ -4331,7 +4331,9 @@ Field 0 nPMECR_EL1
EndSysreg
Sysreg HFGRTR2_EL2 3 4 3 1 2
-Res0 63:15
+Res0 63:31
+Field 30 nTLBIDIDR_EL1
+Res0 29:15
Field 14 nACTLRALIAS_EL1
Field 13 nACTLRMASK_EL1
Field 12 nTCR2ALIAS_EL1
@@ -4551,14 +4553,16 @@ Fields ZCR_ELx
EndSysreg
Sysreg HCRX_EL2 3 4 1 2 2
-Res0 63:35
+Res0 63:37
+Field 36 FNB
+Field 35 VTLBIDOSEn
Field 34 NVnTTLBOS
Field 33 NVnTTLBIS
Field 32 NVnTTLB
Res0 31:28
Field 27 NVTGE
Field 26 SRMASKEn
-Res0 25
+Field 25 VTLBIDEn
Field 24 PACMEn
Field 23 EnFPM
Field 22 GCSEn
@@ -4708,6 +4712,38 @@ Res0 25:19
Field 18:0 INDEX
EndSysreg
+Sysreg VTLBID0_EL2 3 4 2 8 0
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBID1_EL2 3 4 2 8 1
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBID2_EL2 3 4 2 8 2
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBID3_EL2 3 4 2 8 3
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBIDOS0_EL2 3 4 2 9 0
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBIDOS1_EL2 3 4 2 9 1
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBIDOS2_EL2 3 4 2 9 2
+Field 63:0 TD
+EndSysreg
+
+Sysreg VTLBIDOS3_EL2 3 4 2 9 3
+Field 63:0 TD
+EndSysreg
+
Sysreg DACR32_EL2 3 4 3 0 0
Res0 63:32
Field 31:30 D15
@@ -5755,6 +5791,17 @@ Res0 16
Field 15:0 PARTID_MAX
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
+
Sysreg LORID_EL1 3 0 10 4 7
Res0 63:24
Field 23:16 LD
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 02/12] arm64: Detect FEAT_TLBID
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
2026-10-01 11:06 ` [RFC 01/12] arm64: sysreg: Add definitions for FEAT_TLBID Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 03/12] KVM: arm64: Hide TLBID from guest sysregs Kristina Martšenko
` (11 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Detect if FEAT_TLBID is supported by the system. Detect TLBID
configuration from TLBIDIDR_EL1. This information will be used in later
patches.
Set HFGRTR2_EL2.nTLBIDIDR_EL1 so that the kernel can access TLBIDIDR_EL1
at EL1 (in case of nVHE/hVHE).
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/cpu.h | 1 +
arch/arm64/include/asm/cpufeature.h | 7 +++++++
arch/arm64/include/asm/el2_setup.h | 9 ++++++++-
arch/arm64/kernel/cpufeature.c | 25 +++++++++++++++++++++++++
arch/arm64/kernel/cpuinfo.c | 3 +++
arch/arm64/tools/cpucaps | 1 +
6 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
index 3c008821219c..967ccdedf526 100644
--- a/arch/arm64/include/asm/cpu.h
+++ b/arch/arm64/include/asm/cpu.h
@@ -48,6 +48,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 4f04ad82ea34..d4e7497143b2 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -627,6 +627,13 @@ static inline bool id_aa64pfr1_mpamfrac(u64 pfr1)
return val > 0;
}
+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_boot_cpu_features(void);
void __init setup_system_features(void);
void __init setup_user_features(void);
diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index aa8ec9df8024..bd70a470596c 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -438,9 +438,16 @@
orr x0, x0, #HDFGRTR2_EL2_nPMSDSFR_EL1
.Lskip_spefds_\@:
+ mov x1, xzr
+ mrs_s x2, SYS_ID_AA64MMFR4_EL1
+ ubfx x2, x2, #ID_AA64MMFR4_EL1_TLBID_SHIFT, #ID_AA64MMFR4_EL1_TLBID_WIDTH
+ cbz x2, .Lskip_tlbid_\@
+ orr x1, x1, #HFGRTR2_EL2_nTLBIDIDR_EL1
+
+.Lskip_tlbid_\@:
msr_s SYS_HDFGRTR2_EL2, x0
msr_s SYS_HDFGWTR2_EL2, x0
- msr_s SYS_HFGRTR2_EL2, xzr
+ msr_s SYS_HFGRTR2_EL2, x1
msr_s SYS_HFGWTR2_EL2, xzr
msr_s SYS_HFGITR2_EL2, xzr
.Lskip_fgt2_\@:
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32102c3912fa..39a30226ccca 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -521,6 +521,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_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR4_EL1_NV_frac_SHIFT, 4, 0),
ARM64_FTR_END,
@@ -740,6 +741,14 @@ static const struct arm64_ftr_bits ftr_mpamidr[] = {
ARM64_FTR_END,
};
+static const struct arm64_ftr_bits ftr_tlbididr[] = {
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, TLBIDIDR_EL1_NVOS_SHIFT, TLBIDIDR_EL1_NVOS_WIDTH, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, TLBIDIDR_EL1_NOS_SHIFT, TLBIDIDR_EL1_NOS_WIDTH, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, TLBIDIDR_EL1_NVIS_SHIFT, TLBIDIDR_EL1_NVIS_WIDTH, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, TLBIDIDR_EL1_NIS_SHIFT, TLBIDIDR_EL1_NIS_WIDTH, 0),
+ ARM64_FTR_END,
+};
+
/*
* Common ftr bits for a 32bit register with all hidden, strict
* attributes, with 4bit feature fields and a default safe value of
@@ -864,6 +873,7 @@ static const struct __ftr_reg_entry {
/* Op1 = 0, CRn = 10, CRm = 4 */
ARM64_FTR_REG(SYS_MPAMIDR_EL1, ftr_mpamidr),
+ ARM64_FTR_REG(SYS_TLBIDIDR_EL1, ftr_tlbididr),
/* Op1 = 1, CRn = 0, CRm = 0 */
ARM64_FTR_REG(SYS_GMID_EL1, ftr_gmid),
@@ -1259,6 +1269,9 @@ void __init init_cpu_features(struct cpuinfo_arm64 *info)
if (gmid_el1_accessible(info))
init_cpu_ftr_reg(SYS_GMID_EL1, info->reg_gmid);
+
+ if (id_aa64mmfr4_tlbid(info->reg_id_aa64mmfr4))
+ init_cpu_ftr_reg(SYS_TLBIDIDR_EL1, info->reg_tlbididr);
}
static void update_cpu_ftr_reg(struct arm64_ftr_reg *reg, u64 new)
@@ -1523,6 +1536,11 @@ void update_cpu_features(int cpu,
taint |= check_update_ftr_reg(SYS_GMID_EL1, cpu,
info->reg_gmid, boot->reg_gmid);
+ if (id_aa64mmfr4_tlbid(info->reg_id_aa64mmfr4)) {
+ taint |= check_update_ftr_reg(SYS_TLBIDIDR_EL1, cpu,
+ info->reg_tlbididr, boot->reg_tlbididr);
+ }
+
/*
* If we don't have AArch32 at all then skip the checks entirely
* as the register values may be UNKNOWN and we're not going to be
@@ -3234,6 +3252,13 @@ static const struct arm64_cpu_capabilities arm64_features[] = {
ARM64_CPUID_FIELDS(ID_AA64ISAR3_EL1, LSUI, IMP)
},
#endif
+ {
+ .desc = "TLBI domains (TLBID)",
+ .capability = ARM64_HAS_TLBID,
+ .type = ARM64_CPUCAP_SYSTEM_FEATURE,
+ .matches = has_cpuid_feature,
+ ARM64_CPUID_FIELDS(ID_AA64MMFR4_EL1, TLBID, IMP)
+ },
{},
};
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 45c63f3d75c5..2d4fc664231c 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -524,6 +524,9 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
info->reg_smidr = read_cpuid(SMIDR_EL1) & ~SMIDR_EL1_SMPS;
}
+ if (id_aa64mmfr4_tlbid(info->reg_id_aa64mmfr4))
+ info->reg_tlbididr = read_cpuid(TLBIDIDR_EL1);
+
cpuinfo_detect_icache_policy(info);
}
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 2775ba3359cf..d9ed599dee70 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -67,6 +67,7 @@ HAS_STAGE2_FWB
HAS_TCR2
HAS_TIDCP1
HAS_TLB_RANGE
+HAS_TLBID
HAS_VA52
HAS_VIRT_HOST_EXTN
HAS_WFXT
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 03/12] KVM: arm64: Hide TLBID from guest sysregs
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
2026-10-01 11:06 ` [RFC 01/12] arm64: sysreg: Add definitions for FEAT_TLBID Kristina Martšenko
2026-10-01 11:06 ` [RFC 02/12] arm64: Detect FEAT_TLBID Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 04/12] KVM: arm64: Hide TLBID from guest instructions Kristina Martšenko
` (10 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Don't expose FEAT_TLBID to guests. It wouldn't be useful in the general
case since a guest doesn't know which domains exist and its vCPUs can be
moved between any physical CPUs.
Try to hide TLBID sysregs and sysreg fields from the guest:
* Report the feature as not supported in the ID register
ID_AA64MMFR4_EL1.TLBID.
* Configure the TLBIDIDR_EL1 sysreg to UNDEF via the FGU mechanism.
Sanitize a nested hypervisor's HFGRTR2.nTLBIDIDR.
* Sanitize a nested hypervisor's HCRX.{VTLBIDEn,VTLBIDOSEn,FNB}.
* VTLBID{OS}<n>_EL2 registers are VNCR-backed and not exposed to
guests (apart from being able to read/write into the VNCR page) so
no need to handle them in sys_reg_descs or elsewhere.
* The guest is free to set SCTLR2_EL{1,2}.TLBOSNIS, which shouldn't
matter to KVM.
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/kvm/config.c | 6 ++++++
arch/arm64/kvm/emulate-nested.c | 1 +
arch/arm64/kvm/sys_regs.c | 3 +++
3 files changed, 10 insertions(+)
diff --git a/arch/arm64/kvm/config.c b/arch/arm64/kvm/config.c
index 1053676551af..6c116aa1864a 100644
--- a/arch/arm64/kvm/config.c
+++ b/arch/arm64/kvm/config.c
@@ -226,6 +226,7 @@ struct reg_feat_map_desc {
#define FEAT_S2PIE ID_AA64MMFR3_EL1, S2PIE, IMP
#define FEAT_GCIE ID_AA64PFR2_EL1, GCIE, IMP
#define FEAT_NV3 ID_AA64MMFR4_EL1, NV_frac, NV3
+#define FEAT_TLBID ID_AA64MMFR4_EL1, TLBID, IMP
static bool not_feat_aa64el3(struct kvm *kvm)
{
@@ -813,6 +814,7 @@ static const struct reg_bits_to_feat_map hfgrtr2_feat_map[] = {
HFGRTR2_EL2_nTCRMASK_EL1,
FEAT_SRMASK),
NEEDS_FEAT(HFGRTR2_EL2_nRCWSMASK_EL1, FEAT_THE),
+ NEEDS_FEAT(HFGRTR2_EL2_nTLBIDIDR_EL1, FEAT_TLBID),
};
static const DECLARE_FEAT_MAP_FGT(hfgrtr2_desc, hfgrtr2_masks,
@@ -911,6 +913,10 @@ static const struct reg_bits_to_feat_map hcrx_feat_map[] = {
HCRX_EL2_NVnTTLBOS,
FEAT_NV3),
NEEDS_FEAT(HCRX_EL2_SRMASKEn, FEAT_SRMASK),
+ NEEDS_FEAT(HCRX_EL2_FNB |
+ HCRX_EL2_VTLBIDOSEn |
+ HCRX_EL2_VTLBIDEn,
+ FEAT_TLBID),
NEEDS_FEAT(HCRX_EL2_PACMEn, feat_pauth_lr),
NEEDS_FEAT(HCRX_EL2_EnFPM, FEAT_FPMR),
NEEDS_FEAT(HCRX_EL2_GCSEn, FEAT_GCS),
diff --git a/arch/arm64/kvm/emulate-nested.c b/arch/arm64/kvm/emulate-nested.c
index 625604019fb3..cd8ad03028e7 100644
--- a/arch/arm64/kvm/emulate-nested.c
+++ b/arch/arm64/kvm/emulate-nested.c
@@ -1440,6 +1440,7 @@ static const struct encoding_to_trap_config encoding_to_fgt[] __initconst = {
SR_FGT(SYS_TCRALIAS_EL1, HFGRTR2, nTCRALIAS_EL1, 0),
SR_FGT(SYS_TCRMASK_EL1, HFGRTR2, nTCRMASK_EL1, 0),
SR_FGT(SYS_ERXGSR_EL1, HFGRTR2, nERXGSR_EL1, 0),
+ SR_FGT(SYS_TLBIDIDR_EL1, HFGRTR2, nTLBIDIDR_EL1, 0),
/* HFGITR_EL2 */
SR_FGT(OP_AT_S1E1A, HFGITR, ATS1E1A, 1),
diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
index 44aae52c473d..6a025e1f3161 100644
--- a/arch/arm64/kvm/sys_regs.c
+++ b/arch/arm64/kvm/sys_regs.c
@@ -1984,6 +1984,9 @@ static u64 __kvm_read_sanitised_id_reg(const struct kvm_vcpu *vcpu,
if (!system_supports_poe())
val &= ~ID_AA64MMFR3_EL1_S1POE;
break;
+ case SYS_ID_AA64MMFR4_EL1:
+ val &= ~ID_AA64MMFR4_EL1_TLBID;
+ break;
case SYS_ID_MMFR4_EL1:
val &= ~ID_MMFR4_EL1_CCIDX;
break;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 04/12] KVM: arm64: Hide TLBID from guest instructions
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (2 preceding siblings ...)
2026-10-01 11:06 ` [RFC 03/12] KVM: arm64: Hide TLBID from guest sysregs Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 05/12] ACPICA: Add TLBI table definition Kristina Martšenko
` (9 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Since we don't expose TLBID to guests, try to hide TLBI domains from
guest TLBI{P} instructions:
* Map all guest virtual domains to the broadcast domain 0 in
VTLBID{OS}<n>_EL2. Enable the mapping with HCRX_EL2.VTLBID{OS}En.
If the guest issues a TLBI to a TLBI domain, it will instead go to
all agents in the Inner or Outer Shareable domain (as it does today).
* TLBID introduces some TLBIP instructions. A guest can execute TLBIP
instructions unless KVM or a nested hypervisor configures TLBI
instructions to trap. If trapped, then a TLBIP will trap with EC
0x14. In this case KVM currently injects an UNDEF (and prints an
error). This patch doesn't change that, as we don't support TLBIP
instructions in guests.
* KVM emulates some guest TLBI instructions. Ignore the TLBI domain in
ASID* TLBI instructions. In other types of TLBIs the domain is
implicitly ignored. (*ALL* instructions have the domain in an
optional register argument, which KVM does not pass. *VA*/*IPA*
instructions don't have a domain, only their TLBIP variants do.)
Assisted-by: LLM
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/el2_setup.h | 67 +++++++++++++++++++++++++
arch/arm64/include/asm/kvm_emulate.h | 5 ++
arch/arm64/include/asm/tlbflush.h | 2 +
arch/arm64/kvm/hyp/include/hyp/switch.h | 7 ++-
arch/arm64/kvm/hyp/vhe/tlb.c | 2 +
5 files changed, 81 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/include/asm/el2_setup.h b/arch/arm64/include/asm/el2_setup.h
index bd70a470596c..df8751fdc66d 100644
--- a/arch/arm64/include/asm/el2_setup.h
+++ b/arch/arm64/include/asm/el2_setup.h
@@ -189,6 +189,72 @@
msr vttbr_el2, xzr
.endm
+#define VTLBID_ISH 0
+#define VTLBID_OSH 1
+
+.macro __init_vtlbid_regs sh
+ mrs_s x0, SYS_TLBIDIDR_EL1
+
+ .if \sh == VTLBID_ISH
+ ubfx x1, x0, #TLBIDIDR_EL1_NIS_SHIFT, #TLBIDIDR_EL1_NIS_WIDTH
+ ubfx x2, x0, #TLBIDIDR_EL1_NVIS_SHIFT, #TLBIDIDR_EL1_NVIS_WIDTH
+ .else
+ ubfx x1, x0, #TLBIDIDR_EL1_NOS_SHIFT, #TLBIDIDR_EL1_NOS_WIDTH
+ ubfx x2, x0, #TLBIDIDR_EL1_NVOS_SHIFT, #TLBIDIDR_EL1_NVOS_WIDTH
+ .endif
+ cbz x1, .Lskip_vtlbid_regs_\@
+
+ /* Map all virtual domains to the broadcast-all domain */
+ .if \sh == VTLBID_ISH
+ msr_s SYS_VTLBID0_EL2, xzr
+ .else
+ msr_s SYS_VTLBIDOS0_EL2, xzr
+ .endif
+
+ /*
+ * VTLBID1_EL2 is implemented if either NIS <= 8 && NVIS >= 4,
+ * or NIS > 8 && NVIS >= 3. Similarly for VTLBIDOS1_EL2.
+ */
+ cmp x1, #8
+ cset x0, ls
+ add x0, x0, #3
+ cmp x2, x0
+ b.lo .Lskip_vtlbid_regs_\@
+ .if \sh == VTLBID_ISH
+ msr_s SYS_VTLBID1_EL2, xzr
+ .else
+ msr_s SYS_VTLBIDOS1_EL2, xzr
+ .endif
+
+ /*
+ * VTLBID{2,3}_EL2 are implemented if either NIS <= 8 && NVIS >= 5,
+ * or NIS > 8 && NVIS >= 4. Similarly for VTLBIDOS{2,3}_EL2.
+ */
+ add x0, x0, #1
+ cmp x2, x0
+ b.lo .Lskip_vtlbid_regs_\@
+ .if \sh == VTLBID_ISH
+ msr_s SYS_VTLBID2_EL2, xzr
+ msr_s SYS_VTLBID3_EL2, xzr
+ .else
+ msr_s SYS_VTLBIDOS2_EL2, xzr
+ msr_s SYS_VTLBIDOS3_EL2, xzr
+ .endif
+.Lskip_vtlbid_regs_\@:
+.endm
+
+/* TLBID domain mapping */
+.macro __init_el2_vtlbid
+ mrs_s x0, SYS_ID_AA64MMFR4_EL1
+ ubfx x0, x0, #ID_AA64MMFR4_EL1_TLBID_SHIFT, #ID_AA64MMFR4_EL1_TLBID_WIDTH
+ cbz x0, .Lskip_vtlbid_\@
+
+ __init_vtlbid_regs VTLBID_ISH
+ __init_vtlbid_regs VTLBID_OSH
+
+.Lskip_vtlbid_\@:
+.endm
+
/* GICv3 system register access */
.macro __init_el2_gicv3
mrs x0, id_aa64pfr0_el1
@@ -469,6 +535,7 @@
__init_el2_brbe
__init_el2_lor
__init_el2_stage2
+ __init_el2_vtlbid
__init_el2_gicv3
__init_el2_gicv5
__init_el2_hstr
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h
index a3c1928bdf74..306e51fce221 100644
--- a/arch/arm64/include/asm/kvm_emulate.h
+++ b/arch/arm64/include/asm/kvm_emulate.h
@@ -734,6 +734,11 @@ static inline void vcpu_set_hcrx(struct kvm_vcpu *vcpu)
vcpu->arch.hcrx_el2 |= HCRX_EL2_NVnTTLBOS;
}
}
+
+ if (cpus_have_final_cap(ARM64_HAS_TLBID))
+ vcpu->arch.hcrx_el2 |= (HCRX_EL2_VTLBIDEn |
+ HCRX_EL2_VTLBIDOSEn);
+
}
}
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index 14a78ac0f800..b51df8e0ce63 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -46,6 +46,8 @@
__tlbi(op, (arg) | USER_ASID_FLAG); \
} while (0)
+#define TLBI_TLBID_MASK GENMASK_ULL(15, 0)
+
/* This macro creates a properly formatted VA operand for the TLBI */
#define __TLBI_VADDR(addr, asid) \
({ \
diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h
index 1ce7130e2549..f67a4131c083 100644
--- a/arch/arm64/kvm/hyp/include/hyp/switch.h
+++ b/arch/arm64/kvm/hyp/include/hyp/switch.h
@@ -340,9 +340,12 @@ static inline void __deactivate_traps_mpam(void)
* the PTW is a thing. It really isn't.
*
* - EnIDCP128: We don't allow IMPDEF sysregs -- full stop.
+ *
+ * - VTLBID{OS}En: We decide which domain the guest's TLBIs are broadcast to.
*/
-#define NV_HCRX_GUEST_EXCLUDE (HCRX_EL2_TMEA | HCRX_EL2_PTTWI | \
- HCRX_EL2_EnIDCP128)
+#define NV_HCRX_GUEST_EXCLUDE (HCRX_EL2_TMEA | HCRX_EL2_PTTWI | \
+ HCRX_EL2_EnIDCP128 | HCRX_EL2_VTLBIDEn | \
+ HCRX_EL2_VTLBIDOSEn)
static inline void __activate_traps_common(struct kvm_vcpu *vcpu)
{
diff --git a/arch/arm64/kvm/hyp/vhe/tlb.c b/arch/arm64/kvm/hyp/vhe/tlb.c
index c386d9f1c101..e2e6a2df1533 100644
--- a/arch/arm64/kvm/hyp/vhe/tlb.c
+++ b/arch/arm64/kvm/hyp/vhe/tlb.c
@@ -226,6 +226,7 @@ void __kvm_flush_vm_context(void)
* - a non-shareable TLBI is upgraded to being inner-shareable
* - an outer-shareable TLBI is also mapped to inner-shareable
* - an nXS TLBI is upgraded to XS
+ * - any TLBID domain is upgraded to broadcast-all
*/
int __kvm_tlbi_s1e2(struct kvm_s2_mmu *mmu, u64 va, u64 sys_encoding)
{
@@ -291,6 +292,7 @@ int __kvm_tlbi_s1e2(struct kvm_s2_mmu *mmu, u64 va, u64 sys_encoding)
case OP_TLBI_ASIDE1NXS:
case OP_TLBI_ASIDE1ISNXS:
case OP_TLBI_ASIDE1OSNXS:
+ va &= ~TLBI_TLBID_MASK;
__tlbi(aside1is, va);
break;
case OP_TLBI_VAAE1:
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 05/12] ACPICA: Add TLBI table definition
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (3 preceding siblings ...)
2026-10-01 11:06 ` [RFC 04/12] KVM: arm64: Hide TLBID from guest instructions Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 06/12] ACPI: TLBI: Parse domains from table Kristina Martšenko
` (8 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Add the definition of the new Arm TLBI ACPI table.
NOTE: This patch is not yet part of ACPICA and should not be merged as
is. The TLBI table description is not yet public and may change. This
definition is just an initial proposal of how it will look.
Assisted-by: LLM
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
include/acpi/actbl2.h | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/include/acpi/actbl2.h b/include/acpi/actbl2.h
index baef525367b5..68849c9f7cf6 100644
--- a/include/acpi/actbl2.h
+++ b/include/acpi/actbl2.h
@@ -61,6 +61,7 @@
#define ACPI_SIG_SVKL "SVKL" /* Storage Volume Key Location Table */
#define ACPI_SIG_SWFT "SWFT" /* SoundWire File Table */
#define ACPI_SIG_TDEL "TDEL" /* TD Event Log Table */
+#define ACPI_SIG_TLBI "TLBI" /* TLB Invalidate Domain Table */
/*
* All tables must be byte-packed to match the ACPI specification, since
@@ -3701,6 +3702,37 @@ struct acpi_table_tdel {
u64 log_area_start_address;
};
+/*******************************************************************************
+ *
+ * TLBI - TLBI Domain Table
+ *
+ ******************************************************************************/
+
+struct acpi_table_tlbi {
+ struct acpi_table_header header;
+ u32 domain_count;
+ u32 domains_offset;
+};
+
+struct acpi_tlbi_domain {
+ u32 length;
+ u32 id;
+ u32 descendant_count;
+ u32 descendants_offset;
+};
+
+struct acpi_tlbi_descendant {
+ u32 type;
+ u32 identifier;
+ u32 length;
+};
+
+enum acpi_tlbi_descendant_type {
+ ACPI_TLBI_DESCENDANT_DOMAIN = 0,
+ ACPI_TLBI_DESCENDANT_CPU = 1,
+ ACPI_TLBI_DESCENDANT_SMMU = 2,
+};
+
/* Reset to default packing */
#pragma pack()
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 06/12] ACPI: TLBI: Parse domains from table
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (4 preceding siblings ...)
2026-10-01 11:06 ` [RFC 05/12] ACPICA: Add TLBI table definition Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 07/12] arm64: tlbid: Set up CPU domain bitmaps Kristina Martšenko
` (7 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Parse the Arm TLBI ACPI table.
The table contains a list of TLBI domains. Each domain is identified by
a domain number (which can be passed to a TLBI instruction). Each domain
contains a list of descendants, which are CPUs, SMMUs and subdomains.
CPUs are identified by their ACPI Processor UID in GICC MADT, SMMUs by
their Identifier in IORT, and subdomains by their offset in the TLBI
table itself. This allows to describe a hierarchy of domains and the
CPUs and SMMUs that are part of each domain.
Parse the domains from the table. For each domain, build a cpumask of
the CPUs that are part of it. Only add CPUs that have not been disabled.
Parse the domains recursively. Use an arbitrary depth limit of 10 to
prevent infinite recursion in case of firmware bugs. For simplicity each
subdomain hierarchy may be parsed multiple times.
Validate that domain 0 (the fallback broadcast-all domain) always
exists and contains all CPUs. Also check that domain IDs fit into the
architectural limit of 16 bits and what the CPUs support in TLBIDIDR.
Sanity check the table itself (i.e. table sizes), as usually done in
ACPI parsing.
The list of domains and their CPUs will be used in the next patch to
set up domain management in arm64 code. Place common structures in the
arm64 ACPI header. The ARM64_TLBID kconfig symbol will be used later to
detect compiler support and disable all TLBID code.
Assisted-by: LLM
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/Kconfig | 5 +
arch/arm64/include/asm/acpi.h | 9 ++
drivers/acpi/arm64/Kconfig | 3 +
drivers/acpi/arm64/Makefile | 1 +
drivers/acpi/arm64/init.c | 2 +
drivers/acpi/arm64/init.h | 1 +
drivers/acpi/arm64/tlbi.c | 188 ++++++++++++++++++++++++++++++++++
7 files changed, 209 insertions(+)
create mode 100644 drivers/acpi/arm64/tlbi.c
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index b5a51b0ef944..46701449fef2 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -2329,6 +2329,11 @@ config ARM64_LSUI
endmenu # "ARMv9.6 architectural feature"
+config ARM64_TLBID
+ def_bool y
+ depends on ACPI
+ select ACPI_TLBI
+
config ARM64_SVE
bool "ARM Scalable Vector Extension support"
default y
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index 8a54ca6ba602..fb0bddd3b7ef 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -10,6 +10,7 @@
#define _ASM_ACPI_H
#include <linux/cpuidle.h>
+#include <linux/cpumask.h>
#include <linux/efi.h>
#include <linux/memblock.h>
#include <linux/psci.h>
@@ -171,6 +172,14 @@ static inline int acpi_numa_get_nid(unsigned int cpu) { return NUMA_NO_NODE; }
static inline void acpi_map_cpus_to_nodes(void) { }
#endif /* CONFIG_ACPI_NUMA */
+#ifdef CONFIG_ARM64_TLBID
+#define MAX_TLBI_DOMAINS (1UL << 16)
+struct tlbi_domain {
+ u16 id;
+ cpumask_var_t cpus;
+};
+#endif
+
#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE
#endif /*_ASM_ACPI_H*/
diff --git a/drivers/acpi/arm64/Kconfig b/drivers/acpi/arm64/Kconfig
index f2fd79f22e7d..d7ba9add438e 100644
--- a/drivers/acpi/arm64/Kconfig
+++ b/drivers/acpi/arm64/Kconfig
@@ -24,3 +24,6 @@ config ACPI_APMT
config ACPI_MPAM
bool
+
+config ACPI_TLBI
+ bool
diff --git a/drivers/acpi/arm64/Makefile b/drivers/acpi/arm64/Makefile
index 9390b57cb564..9cee81ce834b 100644
--- a/drivers/acpi/arm64/Makefile
+++ b/drivers/acpi/arm64/Makefile
@@ -5,6 +5,7 @@ obj-$(CONFIG_ACPI_FFH) += ffh.o
obj-$(CONFIG_ACPI_GTDT) += gtdt.o
obj-$(CONFIG_ACPI_IORT) += iort.o
obj-$(CONFIG_ACPI_MPAM) += mpam.o
+obj-$(CONFIG_ACPI_TLBI) += tlbi.o
obj-$(CONFIG_ACPI_PROCESSOR_IDLE) += cpuidle.o
obj-$(CONFIG_ARM_AMBA) += amba.o
obj-y += dma.o init.o
diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c
index 7a47d8095a7d..3affc7abf156 100644
--- a/drivers/acpi/arm64/init.c
+++ b/drivers/acpi/arm64/init.c
@@ -10,6 +10,8 @@ void __init acpi_arch_init(void)
acpi_apmt_init();
if (IS_ENABLED(CONFIG_ACPI_IORT))
acpi_iort_init();
+ if (IS_ENABLED(CONFIG_ACPI_TLBI))
+ acpi_tlbi_init();
if (IS_ENABLED(CONFIG_ARM_AMBA))
acpi_amba_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/tlbi.c b/drivers/acpi/arm64/tlbi.c
new file mode 100644
index 000000000000..b705a5b19842
--- /dev/null
+++ b/drivers/acpi/arm64/tlbi.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Arm TLBI Domain Table (TLBI) support
+ *
+ * Copyright (C) 2026 Arm Ltd.
+ * Author: Kristina Martsenko <kristina.martsenko@arm.com>
+ */
+
+#define pr_fmt(fmt) "ACPI: TLBI: " fmt
+
+#include <linux/acpi.h>
+#include <linux/cpufeature.h>
+#include <linux/limits.h>
+#include <linux/slab.h>
+
+#include "init.h"
+
+#define MAX_HIERARCHY_DEPTH 10
+
+struct acpi_table_tlbi *tlbi;
+static struct tlbi_domain *domains;
+static int domain_count;
+
+static int __init validate_tlbi_domains(void)
+{
+ u64 tlbididr = read_sanitised_ftr_reg(SYS_TLBIDIDR_EL1);
+ int domain_bits = FIELD_GET(TLBIDIDR_EL1_NIS, tlbididr);
+ int domain0_idx = -1;
+ int cpu;
+
+ if (domain_bits > 16)
+ return -EINVAL;
+
+ for (int i = 0; i < domain_count; i++) {
+ u16 max_domain_id = (1 << domain_bits) - 1;
+
+ if (domains[i].id > max_domain_id &&
+ !cpumask_empty(domains[i].cpus))
+ return -EINVAL;
+
+ if (!domains[i].id)
+ domain0_idx = i;
+ }
+
+ if (domain0_idx == -1)
+ return -EINVAL;
+
+ for_each_possible_cpu(cpu) {
+ if (!cpumask_test_cpu(cpu, domains[domain0_idx].cpus))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct acpi_tlbi_descendant *__init tlbi_get_descendant(u32 offset)
+{
+ struct acpi_tlbi_descendant *descendant;
+
+ descendant = ACPI_ADD_PTR(struct acpi_tlbi_descendant, tlbi, offset);
+ if (descendant->length < sizeof(*descendant) ||
+ offset + descendant->length > tlbi->header.length)
+ return NULL;
+
+ return descendant;
+}
+
+static struct acpi_tlbi_domain *__init tlbi_get_domain(u32 offset)
+{
+ struct acpi_tlbi_domain *domain;
+
+ domain = ACPI_ADD_PTR(struct acpi_tlbi_domain, tlbi, offset);
+ if (domain->length < sizeof(*domain) ||
+ offset + domain->length > tlbi->header.length)
+ return NULL;
+
+ return domain;
+}
+
+static int __init parse_tlbi_domain(struct acpi_tlbi_domain *domain,
+ struct cpumask *cpus, int depth)
+{
+ u32 offset = domain->descendants_offset;
+
+ if (depth > MAX_HIERARCHY_DEPTH)
+ return -EINVAL;
+
+ for (int i = 0; i < domain->descendant_count; i++) {
+ struct acpi_tlbi_descendant *desc = tlbi_get_descendant(offset);
+
+ if (!desc)
+ return -EINVAL;
+
+ switch (desc->type) {
+ case ACPI_TLBI_DESCENDANT_DOMAIN: {
+ struct acpi_tlbi_domain *child;
+ int ret;
+
+ child = tlbi_get_domain(desc->identifier);
+ if (!child)
+ return -EINVAL;
+
+ ret = parse_tlbi_domain(child, cpus, depth + 1);
+ if (ret)
+ return ret;
+ break;
+ }
+ case ACPI_TLBI_DESCENDANT_CPU: {
+ int cpu = get_cpu_for_acpi_id(desc->identifier);
+
+ if (cpu >= 0 && cpu_possible(cpu))
+ cpumask_set_cpu(cpu, cpus);
+ break;
+ }
+ case ACPI_TLBI_DESCENDANT_SMMU:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ offset += desc->length;
+ }
+
+ return 0;
+}
+
+static int __init parse_tlbi_table(void)
+{
+ struct acpi_table_header *table __free(acpi_put_table) =
+ acpi_get_table_pointer(ACPI_SIG_TLBI, 0);
+ u32 offset;
+ int ret;
+
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+
+ tlbi = (struct acpi_table_tlbi *)table;
+ if (tlbi->header.length < sizeof(*tlbi) ||
+ tlbi->domain_count > MAX_TLBI_DOMAINS)
+ return -EINVAL;
+ if (!tlbi->domain_count)
+ return -ENOENT;
+
+ domain_count = tlbi->domain_count;
+ domains = kcalloc(domain_count, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ offset = tlbi->domains_offset;
+
+ for (int i = 0; i < domain_count; i++) {
+ struct acpi_tlbi_domain *domain = tlbi_get_domain(offset);
+
+ if (!domain || domain->id > U16_MAX)
+ return -EINVAL;
+
+ domains[i].id = domain->id;
+ if (!zalloc_cpumask_var(&domains[i].cpus, GFP_KERNEL))
+ return -ENOMEM;
+
+ ret = parse_tlbi_domain(domain, domains[i].cpus, 0);
+ if (ret)
+ return ret;
+
+ offset += domain->length;
+ }
+
+ ret = validate_tlbi_domains();
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void __init acpi_tlbi_init(void)
+{
+ int ret;
+
+ if (!cpus_have_final_cap(ARM64_HAS_TLBID))
+ return;
+
+ ret = parse_tlbi_table();
+ if (ret) {
+ if (ret != -ENOENT)
+ pr_err("Failed to parse ACPI table\n");
+ return;
+ }
+}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 07/12] arm64: tlbid: Set up CPU domain bitmaps
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (5 preceding siblings ...)
2026-10-01 11:06 ` [RFC 06/12] ACPI: TLBI: Parse domains from table Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 08/12] efi/arm: Check return value of init_new_context() Kristina Martšenko
` (6 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
For each CPU, set up a bitmap of TLBI domains that contain the CPU.
This will be used in later patches to select a domain that contains a
given set of CPUs.
Sort the bitmap by size, with the largest domain at the smallest index.
This will be used to select the smallest domain. Domains are sorted
based on the number of CPUs that they contain. Domains may also contain
SMMUs but we ignore that for now.
Do the setup in arm64 code, near to where it will be used. The ACPI
table parser calls the arm64 setup code directly after parsing the TLBI
table, and passes a list of parsed domains. Afterwards, the parser's own
temporary structures are freed. Alternatively the arm64 setup could be
done from a subsys_initcall_sync().
Add a static key that will be used to check if TLBI domains are enabled.
Disable domains for nVHE EL2 hypervisor code since it would otherwise
need access to the static key (since it #includes tlbflush.h which will
use system_supports_tlbid()) but it wouldn't use domains anyway.
Assisted-by: LLM
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/acpi.h | 1 +
arch/arm64/include/asm/cpufeature.h | 8 +++
arch/arm64/mm/Makefile | 1 +
arch/arm64/mm/tlbid.c | 104 ++++++++++++++++++++++++++++
drivers/acpi/arm64/tlbi.c | 8 +++
5 files changed, 122 insertions(+)
create mode 100644 arch/arm64/mm/tlbid.c
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index fb0bddd3b7ef..5d173c26b56c 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -178,6 +178,7 @@ struct tlbi_domain {
u16 id;
cpumask_var_t cpus;
};
+int arm64_tlbid_init(struct tlbi_domain *domains, int count);
#endif
#define ACPI_TABLE_UPGRADE_MAX_PHYS MEMBLOCK_ALLOC_ACCESSIBLE
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index d4e7497143b2..ff88fd516181 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -885,6 +885,14 @@ static inline bool system_supports_bbml3(void)
return alternative_has_cap_unlikely(ARM64_HAS_BBML3);
}
+DECLARE_STATIC_KEY_FALSE(tlbid_enabled);
+static inline bool system_supports_tlbid(void)
+{
+ return IS_ENABLED(CONFIG_ARM64_TLBID) &&
+ !is_nvhe_hyp_code() &&
+ static_branch_unlikely(&tlbid_enabled);
+}
+
int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt);
bool try_emulate_mrs(struct pt_regs *regs, u32 isn);
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile
index c26489cf96cd..cfd3255d8f0a 100644
--- a/arch/arm64/mm/Makefile
+++ b/arch/arm64/mm/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_TRANS_TABLE) += trans_pgd-asm.o
obj-$(CONFIG_DEBUG_VIRTUAL) += physaddr.o
obj-$(CONFIG_ARM64_MTE) += mteswap.o
obj-$(CONFIG_ARM64_GCS) += gcs.o
+obj-$(CONFIG_ARM64_TLBID) += tlbid.o
KASAN_SANITIZE_physaddr.o += n
obj-$(CONFIG_KASAN) += kasan_init.o
diff --git a/arch/arm64/mm/tlbid.c b/arch/arm64/mm/tlbid.c
new file mode 100644
index 000000000000..0c26ace39592
--- /dev/null
+++ b/arch/arm64/mm/tlbid.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * TLBI domains (FEAT_TLBID) support
+ *
+ * Copyright (C) 2026 Arm Ltd.
+ * Author: Kristina Martsenko <kristina.martsenko@arm.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/bitops.h>
+#include <linux/bitmap.h>
+#include <linux/jump_label.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+DEFINE_STATIC_KEY_FALSE(tlbid_enabled);
+
+static u16 *domain_ids;
+static unsigned long **cpu_domains;
+static int domain_count;
+
+static int __init init_cpu_domain_bitmaps(struct tlbi_domain *domains)
+{
+ int cpu;
+
+ cpu_domains = kcalloc(nr_cpu_ids, sizeof(*cpu_domains), GFP_KERNEL);
+ if (!cpu_domains)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ cpu_domains[cpu] = bitmap_zalloc(domain_count, GFP_KERNEL);
+ if (!cpu_domains[cpu])
+ return -ENOMEM;
+
+ for (int i = 0; i < domain_count; i++) {
+ if (cpumask_test_cpu(cpu, domains[i].cpus))
+ set_bit(i, cpu_domains[cpu]);
+ }
+ }
+
+ return 0;
+}
+
+static int __init store_domain_ids(struct tlbi_domain *domains)
+{
+ domain_ids = kmalloc_array(domain_count, sizeof(*domain_ids),
+ GFP_KERNEL);
+ if (!domain_ids)
+ return -ENOMEM;
+
+ for (int i = 0; i < domain_count; i++)
+ domain_ids[i] = domains[i].id;
+
+ return 0;
+}
+
+static int __init tlbi_domain_cmp(const void *a, const void *b)
+{
+ const struct tlbi_domain *d1 = a;
+ const struct tlbi_domain *d2 = b;
+
+ /* Sort domains from largest to smallest */
+ return cmp_int(cpumask_weight(d2->cpus), cpumask_weight(d1->cpus));
+}
+
+static struct tlbi_domain *__init sort_domains_by_size(struct tlbi_domain *domains)
+{
+ struct tlbi_domain *domains_sorted;
+
+ domains_sorted = kmemdup(domains, domain_count * sizeof(*domains),
+ GFP_KERNEL);
+ if (!domains_sorted)
+ return NULL;
+
+ sort(domains_sorted, domain_count, sizeof(*domains_sorted),
+ tlbi_domain_cmp, NULL);
+
+ return domains_sorted;
+}
+
+int __init arm64_tlbid_init(struct tlbi_domain *domains_in, int count)
+{
+ struct tlbi_domain *domains;
+ int ret;
+
+ domain_count = count;
+
+ domains = sort_domains_by_size(domains_in);
+ if (!domains)
+ return -ENOMEM;
+
+ ret = store_domain_ids(domains);
+ if (ret)
+ return ret;
+
+ ret = init_cpu_domain_bitmaps(domains);
+ if (ret)
+ return ret;
+
+ static_branch_enable(&tlbid_enabled);
+
+ kfree(domains);
+ return 0;
+}
diff --git a/drivers/acpi/arm64/tlbi.c b/drivers/acpi/arm64/tlbi.c
index b705a5b19842..47f7b6ccecf9 100644
--- a/drivers/acpi/arm64/tlbi.c
+++ b/drivers/acpi/arm64/tlbi.c
@@ -185,4 +185,12 @@ void __init acpi_tlbi_init(void)
pr_err("Failed to parse ACPI table\n");
return;
}
+
+ ret = arm64_tlbid_init(domains, domain_count);
+ if (ret)
+ pr_err("Failed to initialize domains\n");
+
+ for (int i = 0; i < domain_count; i++)
+ free_cpumask_var(domains[i].cpus);
+ kfree(domains);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 08/12] efi/arm: Check return value of init_new_context()
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (6 preceding siblings ...)
2026-10-01 11:06 ` [RFC 07/12] arm64: tlbid: Set up CPU domain bitmaps Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-04 8:00 ` Ard Biesheuvel
2026-10-01 11:06 ` [RFC 09/12] arm64: tlbid: Track the TLBI domain of a task Kristina Martšenko
` (5 subsequent siblings)
13 siblings, 1 reply; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
init_new_context() will soon be able to return an error. Check for
errors before continuing.
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
drivers/firmware/efi/arm-runtime.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c
index 3167cab62014..7ef29cd0bfb7 100644
--- a/drivers/firmware/efi/arm-runtime.c
+++ b/drivers/firmware/efi/arm-runtime.c
@@ -55,7 +55,8 @@ static bool __init efi_virtmap_init(void)
efi_mm.pgd = pgd_alloc(&efi_mm);
mm_init_cpumask(&efi_mm);
- init_new_context(NULL, &efi_mm);
+ if (init_new_context(NULL, &efi_mm))
+ return false;
for_each_efi_memory_desc(md) {
phys_addr_t phys = md->phys_addr;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 09/12] arm64: tlbid: Track the TLBI domain of a task
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (7 preceding siblings ...)
2026-10-01 11:06 ` [RFC 08/12] efi/arm: Check return value of init_new_context() Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 10/12] arm64: Support TLBIP instructions Kristina Martšenko
` (4 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Maintain a bitmap of valid TLBI domains for each mm. A later patch will
issue TLBIs for an mm to one of the domains in its bitmap.
As a performance optimization, we want to avoid sending TLB
invalidations to CPUs that have never installed a particular mm, since
their TLBs can't contain the mm's PTEs. To achieve this, have each mm
contain a bitmap of valid TLBI domains. A domain is valid if it contains
all the CPUs that the mm has been installed on. Initially all domains
are valid. Whenever an mm is installed on a new CPU, its set of domains
is reduced (using a bitwise AND) to only include those domains that
contain the new CPU.
Use atomic operations to reduce the bitmap. In case two threads update
the bitmap concurrently, the result will be the same.
efi_mm is initialized very early, before TLBI domains are initialized,
so e.g. system_supports_tlbid() will return false. Just configure efi_mm
to use the largest domain in the system (containing all CPUs).
Note: The initial bitmap is all ones. If a TLBI were issued before the
mm is installed, then any domain could be selected, regardless of which
CPUs are in it. (The bits above the last valid domain will be ignored.)
This is fine, since at this point no TLBs actually need to be
invalidated, since the mm hasn't been installed anywhere yet.
Note: destroy_tlbid_context() doesn't need to check mm_is_efi() since
efi_mm is never freed.
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/mmu.h | 11 ++++++
arch/arm64/include/asm/mmu_context.h | 9 ++++-
arch/arm64/mm/context.c | 6 ++--
arch/arm64/mm/tlbid.c | 52 ++++++++++++++++++++++++++++
4 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index 5e1211c540ab..fc4e759f9f64 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -25,6 +25,7 @@ typedef struct {
void *vdso;
unsigned long flags;
u8 pkey_allocation_map;
+ atomic64_t *tlbid_domains;
} mm_context_t;
/*
@@ -106,5 +107,15 @@ static inline void kpti_install_ng_mappings(void) {}
extern bool page_alloc_available;
+#ifdef CONFIG_ARM64_TLBID
+int init_tlbid_context(struct mm_struct *mm);
+void destroy_tlbid_context(struct mm_struct *mm);
+void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu);
+#else
+static inline int init_tlbid_context(struct mm_struct *mm) { return 0; }
+static inline void destroy_tlbid_context(struct mm_struct *mm) {}
+static inline void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu) {}
+#endif
+
#endif /* !__ASSEMBLER__ */
#endif
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
index 803b68758152..5bf2d3f73e1c 100644
--- a/arch/arm64/include/asm/mmu_context.h
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -21,6 +21,7 @@
#include <asm/cpufeature.h>
#include <asm/daifflags.h>
#include <asm/gcs.h>
+#include <asm/mmu.h>
#include <asm/proc-fns.h>
#include <asm/cputype.h>
#include <asm/sysreg.h>
@@ -172,7 +173,13 @@ init_new_context(struct task_struct *tsk, struct mm_struct *mm)
/* pkey 0 is the default, so always reserve it. */
mm->context.pkey_allocation_map = BIT(0);
- return 0;
+ return init_tlbid_context(mm);
+}
+
+#define destroy_context(mm) destroy_context(mm)
+static inline void destroy_context(struct mm_struct *mm)
+{
+ destroy_tlbid_context(mm);
}
static inline void arch_dup_pkeys(struct mm_struct *oldmm,
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
index 0f4a28b87469..21146d57e8b7 100644
--- a/arch/arm64/mm/context.c
+++ b/arch/arm64/mm/context.c
@@ -13,6 +13,7 @@
#include <linux/mm.h>
#include <asm/cpufeature.h>
+#include <asm/mmu.h>
#include <asm/mmu_context.h>
#include <asm/smp.h>
#include <asm/tlbflush.h>
@@ -215,7 +216,7 @@ static u64 new_context(struct mm_struct *mm)
void check_and_switch_context(struct mm_struct *mm)
{
unsigned long flags;
- unsigned int cpu;
+ unsigned int cpu = smp_processor_id();
u64 asid, old_active_asid;
if (system_supports_cnp())
@@ -251,7 +252,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 +262,8 @@ void check_and_switch_context(struct mm_struct *mm)
arm64_apply_bp_hardening();
+ update_tlbid_domains(mm, cpu);
+
/*
* Defer TTBR0_EL1 setting for user threads to uaccess_enable() when
* emulating PAN.
diff --git a/arch/arm64/mm/tlbid.c b/arch/arm64/mm/tlbid.c
index 0c26ace39592..b44fab2dca20 100644
--- a/arch/arm64/mm/tlbid.c
+++ b/arch/arm64/mm/tlbid.c
@@ -7,18 +7,70 @@
*/
#include <linux/acpi.h>
+#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
+#include <linux/efi.h>
#include <linux/jump_label.h>
+#include <linux/mm_types.h>
#include <linux/slab.h>
#include <linux/sort.h>
+#include <asm/mmu.h>
+
DEFINE_STATIC_KEY_FALSE(tlbid_enabled);
static u16 *domain_ids;
static unsigned long **cpu_domains;
static int domain_count;
+void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu)
+{
+ if (!system_supports_tlbid())
+ return;
+
+ for (int i = 0; i < BITS_TO_U64(domain_count); i++)
+ atomic64_and(cpu_domains[cpu][i], &mm->context.tlbid_domains[i]);
+}
+
+int init_tlbid_context(struct mm_struct *mm)
+{
+ bool is_efi = mm_is_efi(mm);
+ atomic64_t *domains;
+ int ndomains, nwords;
+
+ /* TLBI domain support not yet known at EFI init time */
+ if (!system_supports_tlbid() && !is_efi)
+ return 0;
+
+ ndomains = is_efi ? MAX_TLBI_DOMAINS : domain_count;
+ nwords = BITS_TO_U64(ndomains);
+ domains = kcalloc(nwords, sizeof(*domains), GFP_KERNEL);
+ if (!domains)
+ return -ENOMEM;
+
+ if (is_efi) {
+ /*
+ * Use a safe domain since updates to domains would be lost if
+ * efi_mm were installed before TLBID is enabled.
+ */
+ atomic64_set(domains, 1);
+ } else {
+ for (int i = 0; i < nwords; i++)
+ atomic64_set(&domains[i], -1);
+ }
+
+ mm->context.tlbid_domains = domains;
+
+ return 0;
+}
+
+void destroy_tlbid_context(struct mm_struct *mm)
+{
+ if (system_supports_tlbid())
+ kfree(mm->context.tlbid_domains);
+}
+
static int __init init_cpu_domain_bitmaps(struct tlbi_domain *domains)
{
int cpu;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 10/12] arm64: Support TLBIP instructions
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (8 preceding siblings ...)
2026-10-01 11:06 ` [RFC 09/12] arm64: tlbid: Track the TLBI domain of a task Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 11/12] arm64: tlbid: Pass domain to TLBI instructions Kristina Martšenko
` (3 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Modify the TLBI helper functions in tlbflush.h to issue TLBIP
instructions instead of TLBI when requested. These will be used to pass
a TLBI domain ID to TLBI operations.
FEAT_TLBID introduces TLBIP instructions that behave like TLBI but take
a 128-bit value as an argument (passed in two consecutive GPRs). TLBID
uses these to pass a TLBI domain ID to *VA* and *IPA* type instructions.
TLBID does not introduce non-shareable TLBIP instructions, so assert
that those are not requested.
Use local register variables to ensure that the two GPRs are
consecutive. Alternatively we could use the %H operand modifier but LLVM
doesn't support it.
Check for assembler support of TLBIP instructions. TLBIP won't be used
(nor assembled) when CONFIG_ARM64_TLBID=n.
For now there should be no functional change, as no callers request
TLBIP. A later patch will change this.
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/Kconfig | 9 +-
arch/arm64/include/asm/tlbflush.h | 131 ++++++++++++++++++++++--------
2 files changed, 103 insertions(+), 37 deletions(-)
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 46701449fef2..a6525754492b 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -2329,9 +2329,16 @@ config ARM64_LSUI
endmenu # "ARMv9.6 architectural feature"
+config AS_HAS_TLBID
+ def_bool $(as-instr,.arch_extension tlbid)
+ # https://github.com/llvm/llvm-project/pull/178913
+ depends on AS_IS_GNU || (AS_IS_LLVM && AS_VERSION >= 230000)
+ help
+ Supported by LLVM 23+ and binutils 2.46+.
+
config ARM64_TLBID
def_bool y
- depends on ACPI
+ depends on ACPI && AS_HAS_TLBID
select ACPI_TLBI
config ARM64_SVE
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index b51df8e0ce63..dac35568570f 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -41,11 +41,26 @@
#define __tlbi(op, ...) __TLBI_N(op, ##__VA_ARGS__, 1, 0)
+#define __tlbip(op, arg) do { \
+ register u64 low asm ("x0") = (u64)arg; \
+ register u64 high asm ("x1") = (u64)(arg >> 64); \
+ \
+ asm (ARM64_ASM_PREAMBLE ".arch_extension tlbid\n" \
+ "tlbip " #op ", %0, %1\n" \
+ : : "r" (low), "r" (high)); \
+} while (0)
+
#define __tlbi_user(op, arg) do { \
if (arm64_kernel_unmapped_at_el0()) \
__tlbi(op, (arg) | USER_ASID_FLAG); \
} while (0)
+#define __tlbip_user(op, arg) do { \
+ if (arm64_kernel_unmapped_at_el0()) { \
+ __tlbip(op, (arg) | (u128)USER_ASID_FLAG); \
+ } \
+} while (0)
+
#define TLBI_TLBID_MASK GENMASK_ULL(15, 0)
/* This macro creates a properly formatted VA operand for the TLBI */
@@ -132,49 +147,75 @@ static inline void sme_dvmsync_batch(void)
#define TLBI_TTL_UNKNOWN INT_MAX
-typedef void (*tlbi_op)(u64 arg);
+typedef void (*tlbi_op)(bool tlbip, u128 arg);
-static __always_inline void vae1is(u64 arg)
+static __always_inline void vae1is(bool tlbip, u128 arg)
{
- __tlbi(vae1is, arg);
- __tlbi_user(vae1is, arg);
+ if (tlbip) {
+ __tlbip(vae1is, arg);
+ __tlbip_user(vae1is, arg);
+ } else {
+ __tlbi(vae1is, (u64)arg);
+ __tlbi_user(vae1is, (u64)arg);
+ }
}
-static __always_inline void vae2is(u64 arg)
+static __always_inline void vae2is(bool tlbip, u128 arg)
{
- __tlbi(vae2is, arg);
+ if (tlbip)
+ __tlbip(vae2is, arg);
+ else
+ __tlbi(vae2is, (u64)arg);
}
-static __always_inline void vale1(u64 arg)
+static __always_inline void vale1(bool tlbip, u128 arg)
{
- __tlbi(vale1, arg);
- __tlbi_user(vale1, arg);
+ compiletime_assert(!tlbip, "Unsupported TLBIP operand");
+
+ __tlbi(vale1, (u64)arg);
+ __tlbi_user(vale1, (u64)arg);
}
-static __always_inline void vale1is(u64 arg)
+static __always_inline void vale1is(bool tlbip, u128 arg)
{
- __tlbi(vale1is, arg);
- __tlbi_user(vale1is, arg);
+ if (tlbip) {
+ __tlbip(vale1is, arg);
+ __tlbip_user(vale1is, arg);
+ } else {
+ __tlbi(vale1is, (u64)arg);
+ __tlbi_user(vale1is, (u64)arg);
+ }
}
-static __always_inline void vale2is(u64 arg)
+static __always_inline void vale2is(bool tlbip, u128 arg)
{
- __tlbi(vale2is, arg);
+ if (tlbip)
+ __tlbip(vale2is, arg);
+ else
+ __tlbi(vale2is, (u64)arg);
}
-static __always_inline void vaale1is(u64 arg)
+static __always_inline void vaale1is(bool tlbip, u128 arg)
{
- __tlbi(vaale1is, arg);
+ if (tlbip)
+ __tlbip(vaale1is, arg);
+ else
+ __tlbi(vaale1is, (u64)arg);
}
-static __always_inline void ipas2e1(u64 arg)
+static __always_inline void ipas2e1(bool tlbip, u128 arg)
{
- __tlbi(ipas2e1, arg);
+ compiletime_assert(!tlbip, "Unsupported TLBIP operand");
+
+ __tlbi(ipas2e1, (u64)arg);
}
-static __always_inline void ipas2e1is(u64 arg)
+static __always_inline void ipas2e1is(bool tlbip, u128 arg)
{
- __tlbi(ipas2e1is, arg);
+ if (tlbip)
+ __tlbi(ipas2e1is, arg);
+ else
+ __tlbi(ipas2e1is, (u64)arg);
}
static __always_inline void __tlbi_level_asid(tlbi_op op, u64 addr, u32 level,
@@ -188,7 +229,7 @@ static __always_inline void __tlbi_level_asid(tlbi_op op, u64 addr, u32 level,
FIELD_MODIFY(TLBI_TTL_MASK, &arg, ttl);
}
- op(arg);
+ op(false, arg);
}
static inline void __tlbi_level(tlbi_op op, u64 addr, u32 level)
@@ -445,32 +486,50 @@ static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
* operations can only span an even number of pages. We save this for last to
* ensure 64KB start alignment is maintained for the LPA2 case.
*/
-static __always_inline void rvae1is(u64 arg)
-{
- __tlbi(rvae1is, arg);
- __tlbi_user(rvae1is, arg);
+static __always_inline void rvae1is(bool tlbip, u128 arg)
+{
+ if (tlbip) {
+ __tlbip(rvae1is, arg);
+ __tlbip_user(rvae1is, arg);
+ } else {
+ __tlbi(rvae1is, (u64)arg);
+ __tlbi_user(rvae1is, (u64)arg);
+ }
}
-static __always_inline void rvale1(u64 arg)
+static __always_inline void rvale1(bool tlbip, u128 arg)
{
- __tlbi(rvale1, arg);
- __tlbi_user(rvale1, arg);
+ compiletime_assert(!tlbip, "Unsupported TLBIP operand");
+
+ __tlbi(rvale1, (u64)arg);
+ __tlbi_user(rvale1, (u64)arg);
}
-static __always_inline void rvale1is(u64 arg)
+static __always_inline void rvale1is(bool tlbip, u128 arg)
{
- __tlbi(rvale1is, arg);
- __tlbi_user(rvale1is, arg);
+ if (tlbip) {
+ __tlbip(rvale1is, arg);
+ __tlbip_user(rvale1is, arg);
+ } else {
+ __tlbi(rvale1is, (u64)arg);
+ __tlbi_user(rvale1is, (u64)arg);
+ }
}
-static __always_inline void rvaale1is(u64 arg)
+static __always_inline void rvaale1is(bool tlbip, u128 arg)
{
- __tlbi(rvaale1is, arg);
+ if (tlbip)
+ __tlbip(rvaale1is, arg);
+ else
+ __tlbi(rvaale1is, (u64)arg);
}
-static __always_inline void ripas2e1is(u64 arg)
+static __always_inline void ripas2e1is(bool tlbip, u128 arg)
{
- __tlbi(ripas2e1is, arg);
+ if (tlbip)
+ __tlbip(ripas2e1is, arg);
+ else
+ __tlbi(ripas2e1is, (u64)arg);
}
static __always_inline void __tlbi_range(tlbi_op op, u64 addr,
@@ -486,7 +545,7 @@ static __always_inline void __tlbi_range(tlbi_op op, u64 addr,
arg |= FIELD_PREP(TLBIR_TG_MASK, get_trans_granule());
arg |= FIELD_PREP(TLBIR_ASID_MASK, asid);
- op(arg);
+ op(false, arg);
}
static __always_inline void __flush_tlb_range_op(tlbi_op lop, tlbi_op rop,
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 11/12] arm64: tlbid: Pass domain to TLBI instructions
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (9 preceding siblings ...)
2026-10-01 11:06 ` [RFC 10/12] arm64: Support TLBIP instructions Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-01 11:06 ` [RFC 12/12] arm64: tlbid: Add documentation Kristina Martšenko
` (2 subsequent siblings)
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Update the TLB invalidation interfaces in tlbflush.h to pass a TLBI
domain to TLBI instructions (when available). This may improve
performance by sending the TLBI to only the subset of CPUs that needs
it.
Only use TLBI domains for user mappings, as the kernel is mapped on all
CPUs. Use the mm domain bitmap to choose the smallest domain that
contains all the CPUs that an mm has been installed on.
Most invalidations need to use the TLBIP instruction in order to use a
TLBI domain. The exception is TLBI ASID* (flush_tlb_mm()) which can pass
a domain to a regular TLBI instruction.
To avoid runtime checks on the TLBI domain, use TLBIP instructions for all
invalidations (except local/non-shareable) when supported. When no
domain is needed, callers pass 0 which is the fallback domain
broadcasting to the entire Inner Shareable domain. Local invalidations
continue to use TLBI instructions since the TLBID architecture does not
have non-shareable versions of TLBIP instructions.
The mm domain bitmap may be updated concurrently by another CPU while it
is being read. Use a DSB to ensure that invalidations don't miss any CPU
that has relevant mappings.
Assisted-by: LLM
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
arch/arm64/include/asm/mmu.h | 2 +
arch/arm64/include/asm/tlbflush.h | 90 ++++++++++++++++++++++---------
arch/arm64/kvm/hyp/nvhe/tlb.c | 2 +-
arch/arm64/kvm/hyp/vhe/tlb.c | 2 +-
arch/arm64/mm/tlbid.c | 36 +++++++++++++
5 files changed, 104 insertions(+), 28 deletions(-)
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
index fc4e759f9f64..ae43a91ddabc 100644
--- a/arch/arm64/include/asm/mmu.h
+++ b/arch/arm64/include/asm/mmu.h
@@ -111,10 +111,12 @@ extern bool page_alloc_available;
int init_tlbid_context(struct mm_struct *mm);
void destroy_tlbid_context(struct mm_struct *mm);
void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu);
+u16 get_tlbid_domain(struct mm_struct *mm);
#else
static inline int init_tlbid_context(struct mm_struct *mm) { return 0; }
static inline void destroy_tlbid_context(struct mm_struct *mm) {}
static inline void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu) {}
+static inline u16 get_tlbid_domain(struct mm_struct *mm) { return 0; }
#endif
#endif /* !__ASSEMBLER__ */
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index dac35568570f..864d7459d5be 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -61,7 +61,9 @@
} \
} while (0)
+#define TLBI_ASID_MASK GENMASK_ULL(63, 48)
#define TLBI_TLBID_MASK GENMASK_ULL(15, 0)
+#define TLBIP_ADDR_MASK GENMASK_U128(107, 64)
/* This macro creates a properly formatted VA operand for the TLBI */
#define __TLBI_VADDR(addr, asid) \
@@ -144,6 +146,7 @@ static inline void sme_dvmsync_batch(void)
* in asm/stage2_pgtable.h.
*/
#define TLBI_TTL_MASK GENMASK_ULL(47, 44)
+#define TLBIP_TTL64 BIT(32)
#define TLBI_TTL_UNKNOWN INT_MAX
@@ -219,22 +222,38 @@ static __always_inline void ipas2e1is(bool tlbip, u128 arg)
}
static __always_inline void __tlbi_level_asid(tlbi_op op, u64 addr, u32 level,
- u16 asid)
+ u16 asid, bool local, int domain)
{
- u64 arg = __TLBI_VADDR(addr, asid);
+ bool tlbip = system_supports_tlbid() && !local;
+ u128 arg = 0;
+
+ if (tlbip) {
+ arg |= (((u128)addr >> 12) << 64) & TLBIP_ADDR_MASK;
+ arg |= FIELD_PREP(TLBI_ASID_MASK, asid);
+ arg |= domain;
+ } else {
+ arg |= __TLBI_VADDR(addr, asid);
+ }
if (alternative_has_cap_unlikely(ARM64_HAS_ARMv8_4_TTL) && level <= 3) {
u64 ttl = level | (get_trans_granule() << 2);
+ arg |= FIELD_PREP(TLBI_TTL_MASK, ttl);
- FIELD_MODIFY(TLBI_TTL_MASK, &arg, ttl);
+ if (tlbip)
+ arg |= TLBIP_TTL64;
}
- op(false, arg);
+ op(tlbip, arg);
}
static inline void __tlbi_level(tlbi_op op, u64 addr, u32 level)
{
- __tlbi_level_asid(op, addr, level, 0);
+ __tlbi_level_asid(op, addr, level, 0, false, 0);
+}
+
+static inline void __tlbi_level_local(tlbi_op op, u64 addr, u32 level)
+{
+ __tlbi_level_asid(op, addr, level, 0, true, -1);
}
/*
@@ -418,12 +437,13 @@ static inline void flush_tlb_all(void)
static inline void flush_tlb_mm(struct mm_struct *mm)
{
- unsigned long asid;
+ unsigned long arg;
dsb(ishst);
- asid = __TLBI_VADDR(0, ASID(mm));
- __tlbi(aside1is, asid);
- __tlbi_user(aside1is, asid);
+ arg = __TLBI_VADDR(0, ASID(mm));
+ arg |= get_tlbid_domain(mm);
+ __tlbi(aside1is, arg);
+ __tlbi_user(aside1is, arg);
__tlbi_sync_s1ish(mm);
mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
}
@@ -534,24 +554,38 @@ static __always_inline void ripas2e1is(bool tlbip, u128 arg)
static __always_inline void __tlbi_range(tlbi_op op, u64 addr,
u16 asid, int scale, int num,
- u32 level, bool lpa2)
+ u32 level, bool lpa2, bool local,
+ int domain)
{
- u64 arg = 0;
+ bool tlbip = system_supports_tlbid() && !local;
+ u128 arg = 0;
+
+ if (tlbip) {
+ arg |= (((u128)addr >> 12) << 64) & TLBIP_ADDR_MASK;
+ arg |= domain;
+ } else {
+ arg |= FIELD_PREP(TLBIR_BADDR_MASK, addr >> (lpa2 ? 16 : PAGE_SHIFT));
+ }
+
+ if (level <= 3) {
+ arg |= FIELD_PREP(TLBIR_TTL_MASK, level);
+ if (tlbip)
+ arg |= TLBIP_TTL64;
+ }
- arg |= FIELD_PREP(TLBIR_BADDR_MASK, addr >> (lpa2 ? 16 : PAGE_SHIFT));
- arg |= FIELD_PREP(TLBIR_TTL_MASK, level > 3 ? 0 : level);
arg |= FIELD_PREP(TLBIR_NUM_MASK, num);
arg |= FIELD_PREP(TLBIR_SCALE_MASK, scale);
arg |= FIELD_PREP(TLBIR_TG_MASK, get_trans_granule());
arg |= FIELD_PREP(TLBIR_ASID_MASK, asid);
- op(false, arg);
+ op(tlbip, arg);
}
static __always_inline void __flush_tlb_range_op(tlbi_op lop, tlbi_op rop,
u64 start, size_t pages,
u64 stride, u16 asid,
- u32 level, bool lpa2)
+ u32 level, bool lpa2,
+ bool local, int domain)
{
u64 addr = start, end = start + pages * PAGE_SIZE;
int scale = 3;
@@ -569,23 +603,24 @@ static __always_inline void __flush_tlb_range_op(tlbi_op lop, tlbi_op rop,
num = __TLBI_RANGE_NUM(pages, scale);
if (num >= 0) {
- __tlbi_range(rop, addr, asid, scale, num, level, lpa2);
+ __tlbi_range(rop, addr, asid, scale, num, level, lpa2,
+ local, domain);
addr += __TLBI_RANGE_PAGES(num, scale) << PAGE_SHIFT;
}
scale--;
continue;
invalidate_one:
- __tlbi_level_asid(lop, addr, level, asid);
+ __tlbi_level_asid(lop, addr, level, asid, local, domain);
addr += stride;
}
}
-#define __flush_s1_tlb_range_op(op, start, pages, stride, asid, tlb_level) \
- __flush_tlb_range_op(op, r##op, start, pages, stride, asid, tlb_level, lpa2_is_enabled())
+#define __flush_s1_tlb_range_op(op, start, pages, stride, asid, tlb_level, local, domain) \
+ __flush_tlb_range_op(op, r##op, start, pages, stride, asid, tlb_level, lpa2_is_enabled(), local, domain)
#define __flush_s2_tlb_range_op(op, start, pages, stride, tlb_level) \
- __flush_tlb_range_op(op, r##op, start, pages, stride, 0, tlb_level, kvm_lpa2_is_enabled())
+ __flush_tlb_range_op(op, r##op, start, pages, stride, 0, tlb_level, kvm_lpa2_is_enabled(), false, 0)
static inline bool __flush_tlb_range_limit_excess(unsigned long pages,
unsigned long stride)
@@ -626,6 +661,7 @@ static __always_inline void __do_flush_tlb_range(struct vm_area_struct *vma,
{
struct mm_struct *mm = vma->vm_mm;
unsigned long asid, pages;
+ int domain;
pages = (end - start) >> PAGE_SHIFT;
@@ -634,21 +670,23 @@ static __always_inline void __do_flush_tlb_range(struct vm_area_struct *vma,
return;
}
- if (!(flags & TLBF_NOBROADCAST))
+ if (!(flags & TLBF_NOBROADCAST)) {
dsb(ishst);
- else
+ domain = get_tlbid_domain(mm);
+ } else {
dsb(nshst);
+ }
asid = ASID(mm);
switch (flags & (TLBF_NOWALKCACHE | TLBF_NOBROADCAST)) {
case TLBF_NONE:
__flush_s1_tlb_range_op(vae1is, start, pages, stride,
- asid, tlb_level);
+ asid, tlb_level, false, domain);
break;
case TLBF_NOWALKCACHE:
__flush_s1_tlb_range_op(vale1is, start, pages, stride,
- asid, tlb_level);
+ asid, tlb_level, false, domain);
break;
case TLBF_NOBROADCAST:
/* Combination unused */
@@ -656,7 +694,7 @@ static __always_inline void __do_flush_tlb_range(struct vm_area_struct *vma,
break;
case TLBF_NOWALKCACHE | TLBF_NOBROADCAST:
__flush_s1_tlb_range_op(vale1, start, pages, stride,
- asid, tlb_level);
+ asid, tlb_level, true, -1);
break;
}
@@ -725,7 +763,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
dsb(ishst);
__flush_s1_tlb_range_op(vaale1is, start, pages, stride, 0,
- TLBI_TTL_UNKNOWN);
+ TLBI_TTL_UNKNOWN, false, 0);
__tlbi_sync_s1ish_kernel();
isb();
}
diff --git a/arch/arm64/kvm/hyp/nvhe/tlb.c b/arch/arm64/kvm/hyp/nvhe/tlb.c
index fdb90483340c..f7cac7f1f4fe 100644
--- a/arch/arm64/kvm/hyp/nvhe/tlb.c
+++ b/arch/arm64/kvm/hyp/nvhe/tlb.c
@@ -187,7 +187,7 @@ void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu,
* Instead, we invalidate Stage-2 for this IPA, and the
* whole of Stage-1. Weep...
*/
- __tlbi_level(ipas2e1, ipa, level);
+ __tlbi_level_local(ipas2e1, ipa, level);
/*
* We have to ensure completion of the invalidation at Stage-2,
diff --git a/arch/arm64/kvm/hyp/vhe/tlb.c b/arch/arm64/kvm/hyp/vhe/tlb.c
index e2e6a2df1533..ac1c5e623cf3 100644
--- a/arch/arm64/kvm/hyp/vhe/tlb.c
+++ b/arch/arm64/kvm/hyp/vhe/tlb.c
@@ -135,7 +135,7 @@ void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu,
* Instead, we invalidate Stage-2 for this IPA, and the
* whole of Stage-1. Weep...
*/
- __tlbi_level(ipas2e1, ipa, level);
+ __tlbi_level_local(ipas2e1, ipa, level);
/*
* We have to ensure completion of the invalidation at Stage-2,
diff --git a/arch/arm64/mm/tlbid.c b/arch/arm64/mm/tlbid.c
index b44fab2dca20..c748e710e2ee 100644
--- a/arch/arm64/mm/tlbid.c
+++ b/arch/arm64/mm/tlbid.c
@@ -10,6 +10,7 @@
#include <linux/atomic.h>
#include <linux/bitops.h>
#include <linux/bitmap.h>
+#include <linux/bug.h>
#include <linux/efi.h>
#include <linux/jump_label.h>
#include <linux/mm_types.h>
@@ -24,6 +25,33 @@ static u16 *domain_ids;
static unsigned long **cpu_domains;
static int domain_count;
+u16 get_tlbid_domain(struct mm_struct *mm)
+{
+ unsigned long mask = BITMAP_LAST_WORD_MASK(domain_count);
+ int idx = (domain_count - 1) / 64;
+ unsigned long domain_idx;
+ unsigned long domains;
+
+ if (!system_supports_tlbid())
+ return 0;
+
+ /* Open-coded find_last_bit() that uses atomic reads */
+ do {
+ domains = atomic64_read(&mm->context.tlbid_domains[idx]);
+ domains &= mask;
+ if (domains) {
+ domain_idx = idx * 64 + __fls(domains);
+ break;
+ }
+ mask = ~0UL;
+ } while (idx--);
+
+ if (WARN_ON(idx < 0))
+ return 0;
+
+ return domain_ids[domain_idx];
+}
+
void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu)
{
if (!system_supports_tlbid())
@@ -31,6 +59,14 @@ void update_tlbid_domains(struct mm_struct *mm, unsigned int cpu)
for (int i = 0; i < BITS_TO_U64(domain_count); i++)
atomic64_and(cpu_domains[cpu][i], &mm->context.tlbid_domains[i]);
+
+ /*
+ * Pairs with the DSB before reading tlbid_domains in the TLBI paths.
+ * Either a concurrent PTE update is visible to this CPU before it uses
+ * the mm, or the PTE updater observes the reduced domain bitmap and
+ * includes this CPU in its TLBI.
+ */
+ dsb(ishst);
}
int init_tlbid_context(struct mm_struct *mm)
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* [RFC 12/12] arm64: tlbid: Add documentation
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (10 preceding siblings ...)
2026-10-01 11:06 ` [RFC 11/12] arm64: tlbid: Pass domain to TLBI instructions Kristina Martšenko
@ 2026-10-01 11:06 ` Kristina Martšenko
2026-10-03 16:12 ` [RFC 00/12] arm64: Add support for TLBI domains Marc Zyngier
2026-10-04 18:06 ` Zi Yan
13 siblings, 0 replies; 20+ messages in thread
From: Kristina Martšenko @ 2026-10-01 11:06 UTC (permalink / raw)
To: linux-arm-kernel, linux-acpi, kvmarm, linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Add documentation to describe how the kernel manages TLBI domains. The
bitmap approach is based on ideas from Miko Lenczewski.
Also document booting requirements for TLBI domains.
Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
---
Documentation/arch/arm64/booting.rst | 10 ++++++++++
arch/arm64/mm/tlbid.c | 18 ++++++++++++++++++
2 files changed, 28 insertions(+)
diff --git a/Documentation/arch/arm64/booting.rst b/Documentation/arch/arm64/booting.rst
index 13ef311dace8..6a2c46cc4b22 100644
--- a/Documentation/arch/arm64/booting.rst
+++ b/Documentation/arch/arm64/booting.rst
@@ -568,6 +568,16 @@ Before jumping into the kernel, the following conditions must be met:
- HCRX_EL2.EnASR (bit 2) must be initialised to 0b1.
+ For CPUs with TLBI domains (FEAT_TLBID):
+
+ - If EL3 is present and the kernel is entered at EL2:
+
+ - SCR_EL3.VTLBIDEn (bit 56) must be initialised to 0b1.
+
+ - If EL2 is present and the kernel is entered at EL1:
+
+ - HFGRTR2_EL2.nTLBIDIDR_EL1 (bit 30) must be initialised to 0b1.
+
The requirements described above for CPU mode, caches, MMUs, architected
timers, coherency and system registers apply to all CPUs. All CPUs must
enter the kernel in the same exception level. Where the values documented
diff --git a/arch/arm64/mm/tlbid.c b/arch/arm64/mm/tlbid.c
index c748e710e2ee..2b3e41e6ac22 100644
--- a/arch/arm64/mm/tlbid.c
+++ b/arch/arm64/mm/tlbid.c
@@ -19,6 +19,24 @@
#include <asm/mmu.h>
+/*
+ * FEAT_TLBID enables a TLB invalidation to target a subset of CPUs instead of
+ * the whole Inner Shareable domain. It can improve performance on systems with
+ * many CPUs.
+ *
+ * A system with FEAT_TLBID contains a number of TLBI domains. Each domain
+ * contains a number of CPUs. Domain 0 is always present and contains all CPUs.
+ *
+ * For each CPU we maintain a bitmap of domains that the CPU belongs to. For
+ * each mm we maintain a bitmap of domains that contain all the CPUs that the
+ * mm has been installed on. When an mm is installed on a new CPU we reduce its
+ * bitmap to only include domains that include the new CPU.
+ *
+ * When issuing a TLBI we use the smallest domain for the mm instead of
+ * broadcasting to all CPUs. The domain bitmap is ordered by size, with the
+ * highest bit corresponding to the smallest domain.
+ */
+
DEFINE_STATIC_KEY_FALSE(tlbid_enabled);
static u16 *domain_ids;
--
2.43.0
^ permalink raw reply related [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (11 preceding siblings ...)
2026-10-01 11:06 ` [RFC 12/12] arm64: tlbid: Add documentation Kristina Martšenko
@ 2026-10-03 16:12 ` Marc Zyngier
2026-10-05 8:40 ` Oliver Upton
2026-10-04 18:06 ` Zi Yan
13 siblings, 1 reply; 20+ messages in thread
From: Marc Zyngier @ 2026-10-03 16:12 UTC (permalink / raw)
To: Kristina Martšenko
Cc: linux-arm-kernel, linux-acpi, kvmarm, linux-efi, Catalin Marinas,
Will Deacon, Mark Rutland, Ryan Roberts, David Hildenbrand,
Lorenzo Stoakes, Linu Cherian, Anshuman Khandual,
Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Oliver Upton,
Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Hi Kristina,
On Thu, 01 Oct 2026 12:06:43 +0100,
Kristina Martšenko <kristina.martsenko@arm.com> wrote:
[...]
> * It's also possible to use TLBI domains to limit a KVM guest's TLB
> invalidations to CPUs where the guest has run. I'll try to include that in
> the next version. This series only disables domains for guests.
My take on this is that just like MPAM, it makes little sense to
expose this to guests. As long as the L0 hypervisor has the means that
to scope TLBIs to the relevant domains (subset of CPUs to start with,
but hopefully SMMUs eventually), we're good.
> * With this series a long-running process may eventually run on all CPUs and
> just issue broadcast TLBIs. It may be worth periodically reducing the set
> of CPUs but it's not clear when.
I had a think about that some time ago, and thought that you were sure
to be sub-optimal when the number of CPUs you have run on is greater
than the number of threads in your process.
But I'm sure there are better heuristics! ;-)
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 08/12] efi/arm: Check return value of init_new_context()
2026-10-01 11:06 ` [RFC 08/12] efi/arm: Check return value of init_new_context() Kristina Martšenko
@ 2026-10-04 8:00 ` Ard Biesheuvel
0 siblings, 0 replies; 20+ messages in thread
From: Ard Biesheuvel @ 2026-10-04 8:00 UTC (permalink / raw)
To: Kristina Martšenko, linux-arm-kernel, Linux ACPI, kvmarm,
linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ilias Apalodimas
On Thu, 1 Oct 2026, at 13:06, Kristina Martšenko wrote:
> init_new_context() will soon be able to return an error. Check for
> errors before continuing.
>
> Signed-off-by: Kristina Martšenko <kristina.martsenko@arm.com>
> ---
> drivers/firmware/efi/arm-runtime.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/firmware/efi/arm-runtime.c
> b/drivers/firmware/efi/arm-runtime.c
> index 3167cab62014..7ef29cd0bfb7 100644
> --- a/drivers/firmware/efi/arm-runtime.c
> +++ b/drivers/firmware/efi/arm-runtime.c
> @@ -55,7 +55,8 @@ static bool __init efi_virtmap_init(void)
>
> efi_mm.pgd = pgd_alloc(&efi_mm);
> mm_init_cpumask(&efi_mm);
> - init_new_context(NULL, &efi_mm);
> + if (init_new_context(NULL, &efi_mm))
> + return false;
>
Can we make this a WARN_ON() so any failures are not misidentified
as invalid or missing virtual mappings?
With that,
Acked-by: Ard Biesheuvel <ardb@kernel.org>
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
` (12 preceding siblings ...)
2026-10-03 16:12 ` [RFC 00/12] arm64: Add support for TLBI domains Marc Zyngier
@ 2026-10-04 18:06 ` Zi Yan
2026-10-05 6:29 ` Will Deacon
2026-10-05 11:07 ` Catalin Marinas
13 siblings, 2 replies; 20+ messages in thread
From: Zi Yan @ 2026-10-04 18:06 UTC (permalink / raw)
To: Kristina Martšenko, linux-arm-kernel, linux-acpi, kvmarm,
linux-efi
Cc: Catalin Marinas, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas,
linux-mm
On Thu Oct 1, 2026 at 7:06 AM EDT, Kristina Martšenko wrote:
> Hi,
>
> This series allows the kernel to use TLBI domains. TLBI domains (FEAT_TLBID) is
> an Arm architecture feature that enables a TLB invalidation to target a subset
> of CPUs instead of the whole system. It can improve performance on systems with
> many CPUs. TLBID is not yet part of the Arm Architecture Reference Manual but
> some of it is described in the latest XML [1].
>
> The goal of this series is to send TLBI commands only to the CPUs where a
> process has run (i.e. where its page tables have been installed). The last
IIUC, mm_cpumask() is used to store this information on x86 and other
archs. I see you put this information in tlbid_domains of mm_context_t
from patch 9, although in the form of tlb id domains. I wonder why not
make arm64 also update mm_cpumask() like x86 and other archs and derive
the tlbid domains based on mm_cpumask() and a cpu-to-domain mapping
(added in patch 7).
Thanks.
> patch explains a bit about how it works. This is an RFC since I'm not familiar
> with the kernel's TLB/mm code and I'd like feedback on whether the general
> approach is correct.
>
> A few notes and open questions:
>
> * Domains are described in a new "TLBI" ACPI table. This is not yet part of
> the ACPI specification. There was an initial proposal [2] but this series
> uses a newer WIP version of the table that aims to be published by the end
> of the year.
>
> * There is currently no device tree binding since the feature is mainly meant
> for server-class systems.
>
> * It's also possible to use TLBI domains to limit a KVM guest's TLB
> invalidations to CPUs where the guest has run. I'll try to include that in
> the next version. This series only disables domains for guests.
>
> * With this series a long-running process may eventually run on all CPUs and
> just issue broadcast TLBIs. It may be worth periodically reducing the set
> of CPUs but it's not clear when.
>
> * There are currently no performance numbers since there is no hardware with
> this feature yet.
>
> * The series has been tested on an Arm FVP which supports the TLBID
> architecture but does not have any domains (except domain 0).
>
> Any feedback is welcome.
>
> Thanks,
> Kristina
>
> [1] https://support.arm.com/architectures/a-profile-architecture#Downloads
> [2] https://github.com/tianocore/edk2/issues/11999
>
>
> Kristina Martšenko (12):
> arm64: sysreg: Add definitions for FEAT_TLBID
> arm64: Detect FEAT_TLBID
> KVM: arm64: Hide TLBID from guest sysregs
> KVM: arm64: Hide TLBID from guest instructions
> ACPICA: Add TLBI table definition
> ACPI: TLBI: Parse domains from table
> arm64: tlbid: Set up CPU domain bitmaps
> efi/arm: Check return value of init_new_context()
> arm64: tlbid: Track the TLBI domain of a task
> arm64: Support TLBIP instructions
> arm64: tlbid: Pass domain to TLBI instructions
> arm64: tlbid: Add documentation
>
> Documentation/arch/arm64/booting.rst | 10 ++
> arch/arm64/Kconfig | 12 ++
> arch/arm64/include/asm/acpi.h | 10 ++
> arch/arm64/include/asm/cpu.h | 1 +
> arch/arm64/include/asm/cpufeature.h | 15 ++
> arch/arm64/include/asm/el2_setup.h | 76 +++++++-
> arch/arm64/include/asm/kvm_emulate.h | 5 +
> arch/arm64/include/asm/mmu.h | 13 ++
> arch/arm64/include/asm/mmu_context.h | 9 +-
> arch/arm64/include/asm/tlbflush.h | 219 +++++++++++++++++-------
> arch/arm64/kernel/cpufeature.c | 25 +++
> arch/arm64/kernel/cpuinfo.c | 3 +
> arch/arm64/kvm/config.c | 6 +
> arch/arm64/kvm/emulate-nested.c | 1 +
> arch/arm64/kvm/hyp/include/hyp/switch.h | 7 +-
> arch/arm64/kvm/hyp/nvhe/tlb.c | 2 +-
> arch/arm64/kvm/hyp/vhe/tlb.c | 4 +-
> arch/arm64/kvm/sys_regs.c | 3 +
> arch/arm64/mm/Makefile | 1 +
> arch/arm64/mm/context.c | 6 +-
> arch/arm64/mm/tlbid.c | 210 +++++++++++++++++++++++
> arch/arm64/tools/cpucaps | 1 +
> arch/arm64/tools/sysreg | 53 +++++-
> drivers/acpi/arm64/Kconfig | 3 +
> drivers/acpi/arm64/Makefile | 1 +
> drivers/acpi/arm64/init.c | 2 +
> drivers/acpi/arm64/init.h | 1 +
> drivers/acpi/arm64/tlbi.c | 196 +++++++++++++++++++++
> drivers/firmware/efi/arm-runtime.c | 3 +-
> include/acpi/actbl2.h | 32 ++++
> 30 files changed, 858 insertions(+), 72 deletions(-)
> create mode 100644 arch/arm64/mm/tlbid.c
> create mode 100644 drivers/acpi/arm64/tlbi.c
>
>
> base-commit: 93f51579e7df248780214094418f205253383cc5
--
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-04 18:06 ` Zi Yan
@ 2026-10-05 6:29 ` Will Deacon
2026-10-05 18:13 ` Zi Yan
2026-10-05 11:07 ` Catalin Marinas
1 sibling, 1 reply; 20+ messages in thread
From: Will Deacon @ 2026-10-05 6:29 UTC (permalink / raw)
To: Zi Yan
Cc: Kristina Martšenko, linux-arm-kernel, linux-acpi, kvmarm,
linux-efi, Catalin Marinas, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas,
linux-mm
On Sun, Oct 04, 2026 at 02:06:48PM -0400, Zi Yan wrote:
> On Thu Oct 1, 2026 at 7:06 AM EDT, Kristina Martšenko wrote:
> > This series allows the kernel to use TLBI domains. TLBI domains (FEAT_TLBID) is
> > an Arm architecture feature that enables a TLB invalidation to target a subset
> > of CPUs instead of the whole system. It can improve performance on systems with
> > many CPUs. TLBID is not yet part of the Arm Architecture Reference Manual but
> > some of it is described in the latest XML [1].
> >
> > The goal of this series is to send TLBI commands only to the CPUs where a
> > process has run (i.e. where its page tables have been installed). The last
>
> IIUC, mm_cpumask() is used to store this information on x86 and other
> archs. I see you put this information in tlbid_domains of mm_context_t
> from patch 9, although in the form of tlb id domains. I wonder why not
> make arm64 also update mm_cpumask() like x86 and other archs and derive
> the tlbid domains based on mm_cpumask() and a cpu-to-domain mapping
> (added in patch 7).
Historically, maintaining mm_cpumask() was a horrible source of contention
when we looked at it in the past. Maybe that's no longer true, but it
would be good to see some numbers if we want to start keeping that thing
accurate on arm64.
Will
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-03 16:12 ` [RFC 00/12] arm64: Add support for TLBI domains Marc Zyngier
@ 2026-10-05 8:40 ` Oliver Upton
0 siblings, 0 replies; 20+ messages in thread
From: Oliver Upton @ 2026-10-05 8:40 UTC (permalink / raw)
To: Marc Zyngier
Cc: Kristina Martšenko, linux-arm-kernel, linux-acpi, kvmarm,
linux-efi, Catalin Marinas, Will Deacon, Mark Rutland,
Ryan Roberts, David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Fuad Tabba, Joey Gouly, Steffen Eiden, Suzuki K Poulose,
Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas
Hey,
On Sat, Oct 03, 2026 at 05:12:50PM +0100, Marc Zyngier wrote:
> Hi Kristina,
>
> On Thu, 01 Oct 2026 12:06:43 +0100,
> Kristina Martšenko <kristina.martsenko@arm.com> wrote:
>
> [...]
>
> > * It's also possible to use TLBI domains to limit a KVM guest's TLB
> > invalidations to CPUs where the guest has run. I'll try to include that in
> > the next version. This series only disables domains for guests.
>
> My take on this is that just like MPAM, it makes little sense to
> expose this to guests. As long as the L0 hypervisor has the means that
> to scope TLBIs to the relevant domains (subset of CPUs to start with,
> but hopefully SMMUs eventually), we're good.
Err... I think TLBID is quite useful in a VM and unlike MPAM should
actually be virtualizable.
Think of a case where a VM spans multiple chiplets, the VMM will likely
affine vCPU threads to a particular chiplet and present a virtual
topology to the guest. If those underlying chiplets map to TLBI domains
then it seems beneficial to expose virtual domains to the guest.
Even for VMs that fit within a single domain, you'll still need VMM
participation to scope the virtual domain correctly and affine the vCPU
threads appropriately.
I don't think this needs to happen as part of the initial enablement, no
issues with hiding the feature for now. We can revisit the topic later
how the feature is expected to work for KVM.
Thanks,
Oliver
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-04 18:06 ` Zi Yan
2026-10-05 6:29 ` Will Deacon
@ 2026-10-05 11:07 ` Catalin Marinas
1 sibling, 0 replies; 20+ messages in thread
From: Catalin Marinas @ 2026-10-05 11:07 UTC (permalink / raw)
To: Zi Yan
Cc: Kristina Martšenko, linux-arm-kernel, linux-acpi, kvmarm,
linux-efi, Will Deacon, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas,
linux-mm
On Sun, Oct 04, 2026 at 02:06:48PM -0400, Zi Yan wrote:
> On Thu Oct 1, 2026 at 7:06 AM EDT, Kristina Martšenko wrote:
> > This series allows the kernel to use TLBI domains. TLBI domains (FEAT_TLBID) is
> > an Arm architecture feature that enables a TLB invalidation to target a subset
> > of CPUs instead of the whole system. It can improve performance on systems with
> > many CPUs. TLBID is not yet part of the Arm Architecture Reference Manual but
> > some of it is described in the latest XML [1].
> >
> > The goal of this series is to send TLBI commands only to the CPUs where a
> > process has run (i.e. where its page tables have been installed). The last
>
> IIUC, mm_cpumask() is used to store this information on x86 and other
> archs. I see you put this information in tlbid_domains of mm_context_t
> from patch 9, although in the form of tlb id domains. I wonder why not
> make arm64 also update mm_cpumask() like x86 and other archs and derive
> the tlbid domains based on mm_cpumask() and a cpu-to-domain mapping
> (added in patch 7).
One reason is that it's cheaper to compute the tlbi domains at context
switch time (bitmap-and IIRC) than working from mm_cpumask() every time
we do a TLBI.
That said, suggestions for a quick algorithm are welcome. Subject to the
race conditions Will mentioned, we could use the mm_cpumask() for a
quick check if we ever ran on that CPU and avoid recomputing the tlbi
domains.
--
Catalin
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: [RFC 00/12] arm64: Add support for TLBI domains
2026-10-05 6:29 ` Will Deacon
@ 2026-10-05 18:13 ` Zi Yan
0 siblings, 0 replies; 20+ messages in thread
From: Zi Yan @ 2026-10-05 18:13 UTC (permalink / raw)
To: Will Deacon
Cc: Kristina Martšenko, linux-arm-kernel, linux-acpi, kvmarm,
linux-efi, Catalin Marinas, Mark Rutland, Ryan Roberts,
David Hildenbrand, Lorenzo Stoakes, Linu Cherian,
Anshuman Khandual, Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla,
Marc Zyngier, Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Ard Biesheuvel, Ilias Apalodimas,
linux-mm
On 5 Oct 2026, at 2:29, Will Deacon wrote:
> On Sun, Oct 04, 2026 at 02:06:48PM -0400, Zi Yan wrote:
>> On Thu Oct 1, 2026 at 7:06 AM EDT, Kristina Martšenko wrote:
>>> This series allows the kernel to use TLBI domains. TLBI domains (FEAT_TLBID) is
>>> an Arm architecture feature that enables a TLB invalidation to target a subset
>>> of CPUs instead of the whole system. It can improve performance on systems with
>>> many CPUs. TLBID is not yet part of the Arm Architecture Reference Manual but
>>> some of it is described in the latest XML [1].
>>>
>>> The goal of this series is to send TLBI commands only to the CPUs where a
>>> process has run (i.e. where its page tables have been installed). The last
>>
>> IIUC, mm_cpumask() is used to store this information on x86 and other
>> archs. I see you put this information in tlbid_domains of mm_context_t
>> from patch 9, although in the form of tlb id domains. I wonder why not
>> make arm64 also update mm_cpumask() like x86 and other archs and derive
>> the tlbid domains based on mm_cpumask() and a cpu-to-domain mapping
>> (added in patch 7).
>
> Historically, maintaining mm_cpumask() was a horrible source of contention
> when we looked at it in the past. Maybe that's no longer true, but it
Do you have a pointer to the cost you have in mind? LLM can only finds some
overhead reduction work done by Rik for x86[1][2].
[1] https://lore.kernel.org/all/20180716190337.26133-8-riel@surriel.com/T/#u
[2] https://lore.kernel.org/all/20241114152723.1294686-2-riel@surriel.com/
BTW, patch 9 is updating tlbid domains unconditionally. It can be optimized
in the same way as Rik did for x86 mm_cpumask(), read the tlbid domain first
and only update if CPU is in a new domain.
> would be good to see some numbers if we want to start keeping that thing
> accurate on arm64.
Best Regards,
Yan, Zi
^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2026-10-05 18:14 UTC | newest]
Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-01 11:06 [RFC 00/12] arm64: Add support for TLBI domains Kristina Martšenko
2026-10-01 11:06 ` [RFC 01/12] arm64: sysreg: Add definitions for FEAT_TLBID Kristina Martšenko
2026-10-01 11:06 ` [RFC 02/12] arm64: Detect FEAT_TLBID Kristina Martšenko
2026-10-01 11:06 ` [RFC 03/12] KVM: arm64: Hide TLBID from guest sysregs Kristina Martšenko
2026-10-01 11:06 ` [RFC 04/12] KVM: arm64: Hide TLBID from guest instructions Kristina Martšenko
2026-10-01 11:06 ` [RFC 05/12] ACPICA: Add TLBI table definition Kristina Martšenko
2026-10-01 11:06 ` [RFC 06/12] ACPI: TLBI: Parse domains from table Kristina Martšenko
2026-10-01 11:06 ` [RFC 07/12] arm64: tlbid: Set up CPU domain bitmaps Kristina Martšenko
2026-10-01 11:06 ` [RFC 08/12] efi/arm: Check return value of init_new_context() Kristina Martšenko
2026-10-04 8:00 ` Ard Biesheuvel
2026-10-01 11:06 ` [RFC 09/12] arm64: tlbid: Track the TLBI domain of a task Kristina Martšenko
2026-10-01 11:06 ` [RFC 10/12] arm64: Support TLBIP instructions Kristina Martšenko
2026-10-01 11:06 ` [RFC 11/12] arm64: tlbid: Pass domain to TLBI instructions Kristina Martšenko
2026-10-01 11:06 ` [RFC 12/12] arm64: tlbid: Add documentation Kristina Martšenko
2026-10-03 16:12 ` [RFC 00/12] arm64: Add support for TLBI domains Marc Zyngier
2026-10-05 8:40 ` Oliver Upton
2026-10-04 18:06 ` Zi Yan
2026-10-05 6:29 ` Will Deacon
2026-10-05 18:13 ` Zi Yan
2026-10-05 11:07 ` Catalin Marinas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox