TLB保存类型
TLB在硬件内部一般有三种保存情况:
stage1 TLB,host使用的TLB。
combine TLB,也就是stage1和stage2最后综合得到的TLB。
stage2 TLB,也就是保存IPA->PA的对应的TLB。
注意,在虚拟化的场景之下,如果只有combine TLB,TLB miss的成本会比较高,因为
为了地址翻译还需要做stage2的page table walk。有了stage2 TLB,可以立即得到IPA
对应的实际物理地址。
从一条TLB覆盖范围看,又可以分为三种情况:
覆盖一个基础页的大小。
覆盖一个block页的大小。
覆盖一个CONT PTE的大小。
一般来说一个PE有一个独立的结构来保存TLB,但是,ARM上新增加了CnP这个特性,可以多个
PE之间共享TLB。具体定义是,如果多个PE的TTBRx_ELx的页表是一样的(包括VMID,ASID),
同时TTBRx_ELx.CNP配置为1,那么多个PE上的相同TLB可以共享一个TLB。实际实现时,一般
是一个物理核的多个逻辑核共享TLB。
注意,在虚拟化的情况下,需要TTBRx_EL1.CNP和VTTBR_EL2.CNP都是1,才会创建共享的TLB。
一个直观的硬件实现是,当硬件发现满足共享TLB的条件的时候,就去查找有无共享TLB,如果
没有就创建一个TLB,并打上共享的标记,如果找见共享TLB就直接使用。
TLBI指令
从如上的TLB类型的角度看下TLBI指令都有哪些。TLB是key-value的存储结构,key是一组数据
的集合,硬件执行TLBI指令就是根据TLBI指令带的输入,去匹配TLB的key,匹配上了,就把
对应的TLB无效化。
一个贴近真实硬件的条目模型如下:
1 | struct tlb_entry { |
TLBI指令名可以拆成三段来读:TLBI <操作><级别><范围>。
- 操作:
- VA : 按VA失效(带ASID)。
- VAA : 按VA失效,但忽略ASID(all-ASID)。
- ASID : 按ASID整体失效。
- VMALL : 当前VMID下,该regime的stage1/combined条目全清(不按地址)。
- VMALLS12 : 当前VMID下,stage1 + stage2 + combined全清。
- IPAS2 : 按IPA失效,只失效stage2条目。
- ALL : 该级别regime全清(不看VMID/ASID)。
- R* : 按地址范围(Range)失效,一条指令失效一段区间。
- 级别: E1/E2/E3。
- 范围: 无后缀=本PE local; IS=inner-shareable广播; OS=outer-shareable; nXS=不等XS。
注意,如上ELx级别不是直接指对应EL级别的TLB,ELx结合当前机器所处在的状态(HCR_EL2.E2H/TGE)
会最终决定无效化作用于哪个translation regime,translation regime的定义可以参考这里。
简单讲,EL1&0 translation regime表示虚机S1+S2的翻译,EL2&0 translation regime表示
VHE host的翻译,EL2 translation regime表示nVHE hypervisor的翻译。
VHE场景下,TLBI xEL1x + TGE为1,表示EL2&0 translation regime,TLBI xEL1x + TGE为0
表示EL1&0 translation regime。所以,guest/host内核使用相同的TLBI xEL1x指令,在guest
和host下均可以做正确的TLBI操作。
TLBI xIPAx表示对stage2 TLB的无效化。
TLBI使用场景
Host上TLBI的使用
todo: Host上使用TLBI的场景。
虚机上TLBI的使用
从三个视角看下:guest自己发TLBI,host上刷stage2 TLB,host(KVM)替guest无效TLB。
- Guest在EL1自己发TLBI
Guest执行tlbi vae1is无效化TLB为EL1&0 regime,而当前VTTBR_EL2.VMID就是该guest的VMID,
硬件自动把失效锁定在这台guest。默认KVM不trap guest的TLBI(HCR_EL2.TTLB=0),guest的
TLBI原生执行、原生按VMID隔离,host完全不参与。IS广播在物理上发给inner-shareable域内
所有PE,但每个PE按标签匹配:别的guest(VMID不同)、host(regime=EL2&0,压根不匹配)
都不会误伤。
- 无效stage2 TLB
S2页表发生变化,都要刷新S2 TLB。由于如上的存储结构,无效stage2 TLB后,还要无效下
虚机的combined TLB:
1 | //__kvm_tlb_flush_vmid_ipa |
- Host(KVM)在EL2替guest无效化guest TLB
如上,如果stage2页表有变动,就要无效S2 TLB,S1 combined的TLB信息也不对了,这时就
需要EL2把EL1的combined TLB也刷掉。
看下具体做法。VHE下host平时是{E2H,TGE}={1,1},此时tlbi *E1打的是EL2&0 regime对应
的TLB(host自己),打不到guest的EL1&0 regime的TLB。所以KVM的做法是临时把TGE清0:
1 | // arch/arm64/kvm/hyp/vhe/tlb.c __tlb_switch_to_guest |
刷完再把TGE置回1。
虚机上CnP的软件支持
如下是和虚机上CnP软硬件逻辑相关的一个问题。这个是一个独立问题,先在这个文档中记录。
kvm_arch_vcpu_load里会有:
1 | /* |
上线的时候检测,如果这个物理CPU跑过同一个VM上的其它vCPU,就先无效下这个物理CPU
上虚机的TLB,因为其它vCPU的TLB可能和上线vCPU的ASID/VA相同、PA不同(what?)。这个支
持确保vCPU的TLB始终是private的。
但是,如上逻辑在使能CnP的虚机还正确么?当前的软件是,只要检测到host支持CnP,S2就
会把CnP配置上,guest默认也会配置上CnP,这样,从硬件配置角度看,虚机的CnP是全部打
开的。
当多个vCPU同时在多个thread上运行时,CnP会实际运行:
1 | ASID1 VA1 PA1 ASID1 VA1 PA2 |
这样逻辑上会错,但是这种情况是违反ARM构架的。(上面的场景也可能是CnP打开的, 为啥
上面就不违反ARM构架?)