arm64 TLBI 可以指定核的范围吗
arm64 TLBI 能否指定核范围?
直接结论
不能。 TLBI 指令编码中没有 CPU 位图(cpumask)参数,它只能通过助记符后缀选择”广播作用域”(本地 PE / Inner Shareable 域 / Outer Shareable 域),作用域边界由硬件互连拓扑静态决定。要实现”精确到指定 CPU 子集”,必须在软件层面用 IPI 把”本地 TLBI”(无后缀变体)投递到目标核上执行。
一、指令编码层面:TLBI 的作用域后缀
| 助记符形式 | 作用域 | 说明 |
|---|---|---|
TLBI VAE1, X10(无后缀) |
仅当前 PE(Non-shareable) | 本地失效,不广播 |
TLBI VAE1IS, X10 |
Inner Shareable 域内所有 PE | 最常见的内核路径(is 后缀) |
TLBI VAE1OS, X10 |
Outer Shareable 域内所有 PE(ARMv8.4+) | 跨域广播用 |
TLBI RVAE1IS, X10 |
IS 域 + VA 地址范围(FEAT_TLBIRANGE,ARMv8.4+) | 见下方”范围”澄清 |
关键语义:
- “范围”只能选域,不能选 CPU 子集。”域”(Inner/Outer Shareable)是系统静态划分的(由 CMN/CCN 互连、MPIDR 亲和层级决定),软件无法在运行期把任意几个 CPU 划成一个域。
- 无后缀 = 本地。这是实现”指定核”的指令级原子构件:每个目标核自己执行一条本地 TLBI。
- “范围”歧义澄清:
FEAT_TLBIRANGE(RVAE1IS系列)的”range”指 VA 地址范围(一次失效一段连续虚拟地址),与”CPU 核范围”无关。内核中__flush_tlb_range_op的r##op宏(arch/arm64/include/asm/tlbflush.h:510)正是这个含义。
二、软件层面:实现”指定核范围”的三种机制
机制 1:IPI 精确投递 + 目标核执行本地 TLBI(最精确)
典型实例是 KVM stage-2 页表维护。nVHE 模式下 hypervisor 通过 kvm_call_hyp 只向”持有该 VMID 的 pCPU 集合”发 IPI,目标核执行无后缀(本地) TLBI:
// arch/arm64/kvm/hyp/nvhe/tlb.c:177
void __kvm_tlb_flush_vmid_ipa_nsh(struct kvm_s2_mmu *mmu,
phys_addr_t ipa, int level)
{
...
__tlbi_level(ipas2e1, ipa, level); // 无 IS 后缀:本地失效
dsb(nsh);
__tlbi(vmalle1); // 本地失效 Stage-1
dsb(nsh);
isb();
...
}
对比同一文件中的广播版本(__kvm_tlb_flush_vmid_ipa,tlb.c:147)用的是 ipas2e1is + dsb(ish)。同一个功能维护两条路径的原因正是:_nsh 版本已经通过 IPI 把执行范围限定在目标核集合上,再做 Inner Shareable 广播纯属浪费——这就是”指定核范围”在软件中的标准实现模式。
机制 2:内核 mm 层 TLBF_NOBROADCAST 标志
内核 TLB flush 框架显式区分”广播 / 仅本地”:
// arch/arm64/include/asm/tlbflush.h:558
/* Perform the tlbi locally without broadcasting to other CPUs. */
#define TLBF_NOBROADCAST ((__force tlbf_t)BIT(3))
__do_flush_tlb_range(tlbflush.h:561)根据标志选择指令变体:
switch (flags & (TLBF_NOWALKCACHE | TLBF_NOBROADCAST)) {
case TLBF_NONE: // 默认:广播
__flush_s1_tlb_range_op(vae1is, ...); // IS 变体 + dsb(ish) 收尾
break;
case TLBF_NOWALKCACHE | TLBF_NOBROADCAST: // 仅本地
__flush_s1_tlb_range_op(vale1, ...); // 无后缀变体 + dsb(nsh) 收尾
break;
...
}
实际调用者(这些场景都已知”只有本核可能命中该映射”):
flush_tlb_fix_spurious_fault(arch/arm64/include/asm/pgtable.h:104)——spurious fault 修复,只有触发缺页的核可能有该 TLB 项;arch/arm64/mm/fault.c:264——hugepage/table 变更路径;arch/arm64/mm/contpte.c:690——contiguous PTE 展开。
这是”范围由软件静态推断为单核”时内核主动降级为本地 TLBI 的典型优化。
机制 3:位图延迟执行(异步、按需)
ASID rollover 时 flush_context() 只置位 tlb_flush_pending 位图(arch/arm64/mm/context.c:28,131),各 CPU 在 switch_mm 时自行执行本地 TLB 维护(context.c:255)。这是把”一次全系统同步”拆解为”各核自主执行本地 TLBI + dsb(nsh)“,避免同步等待串行化。
三、关于你贴的 BBM 文档示例
STR X11, [X1] ; update the translation table entry, marking location as invalid
DSB ISH ; ensures visibility of the update to translation table walks
TLBI VAE1, X10 ; assumes we are in the EL1
DSB ISH ; ensures completion of the TLB invalidation
ISB ; synchronise context on this PE
需要注意:TLBI VAE1 无后缀在正式编码中是本地(Non-shareable)操作,只作用于当前 PE;若文档意图是”inner shareability domain 内所有 observer 不再存在旧翻译缓存”,则该处指令应为 TLBI VAE1IS(或该示例隐含单 PE 上下文)。这也再次印证:域级广播与否完全由 IS/OS 后缀决定,指令本身没有任何按核选择的机制。
四、实践建议(多 die / 鲲鹏 2P 平台)
| 场景 | 推荐做法 |
|---|---|
| 需要失效”全系统所有核” | TLBI xxxIS + dsb(ish);注意多 die 平台上 IS 域可能只覆盖单 die,跨 die 需用 OS 变体或 IPI |
| 需要失效”任意 CPU 子集” | smp_call_function_many(mask, fn) / kvm_call_hyp,fn 内执行无后缀 TLBI + dsb(nsh) + isb |
| 单核场景(spurious fault、本地映射修改) | 内核 TLBF_NOBROADCAST 已自动走本地路径,OOT 补丁应复用该标志而非自己广播 |
| 判断 IS 域边界 | 查平台互连文档(如 CMN 拓扑)+ MPIDR_EL1 亲和层级,决定 IS vs OS 选择 |
核心结论:TLBI 的”作用域”只能是静态共享域或本地 PE;”指定核范围”必须由软件(IPI 投递)实现,指令层面不存在单条 TLBI 完成该需求的方式——这与上一轮 DSB 的结论一致,二者在设计上是配套的(本地 TLBI 必须配本地 dsb(nsh),域级 TLBI 必须配域级 dsb(ish/osh))。
参考来源
本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。