../21-kvm-tlbflush

KVM

SDM中VMX章节总共描述了两种flush指令, 即invept <eptp>invvpid <vpid> <gva>. 结合之前的表格可以得到invvpid主要清除vpid tagged和vpid x EPTP tagged的TLB entries. 这种影响范围较小, 即只包括一个guest CPU相关的entries. 而invept则清除EPTP tagged和vpid x EPTP tagged的TLB entries. 这种影响范围则很大, 任何共用一个EPT的TLB entires都会被清楚. Guest的TLB flush包括两个指令: invpg和invpcid. invpcid和invvpid算是一对一映射的, 都只invalidate某个特定context的TLB entries. 在Guest中使用invpcid不会导致VM-exit. invpg虽然不是pcid aware, 但是其也不会触发VM-exit.

KVM的实现中不分青红皂白的直接使用invept, 导致误伤大量有效entries.

这里的原因实际上还是gVA reverse mapping缺失的问题. invvpid虽然可以单独清除某个gVA对应的TLB entries, 但是在host中做A-bit tracking只知道hVA/gPA. 而某个gPA却可能map到多个gVA. 同时host没法访问guest的rmap结构, 没法找出所有的gVA. 也就没法保证catch其他gVA的access. 为了保证正确性, 只能清除所有的TLB entries.

另外KVM的remote_tlb_flush实际上最终只会调用invept, 我们可以借此观察invept的调用次数.

WhereWhattlb_flushremote_tlb_flushtlb_local_flush_onetlb_local_flush_alltlb_remote_flushElapsed
GuestTPP124005,587,86729,65815,410,4666:11.85
HostTPP89,361,45321,220,0565,889,04022,9981,205,53532:37.02
None26,402056698632384611795794:59.50
GuestPML139,37349156067572680611807245:39.07
GuestOurs18880
sudo swapoff --all
sudo sysctl -w vm.overcommit_memory=1
echo 3 | sudo tee /proc/sys/vm/drop_caches
ulimit -n 65535
echo 3000000 | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_{min,max}_freq
echo 1 | sudo tee /proc/sys/vm/clear_all_vm_events                  

Host

host only的情况下, 扫描A-bit, linux默认不立即flush TLB而是等到context switch. 见这里.

sudo bpftrace -e 'tracepoint:tlb:tlb_flush { @[args.reason] = count() }'
Attaching 1 probe...
^C

@[4]: 42332
@[3]: 42333
@[0]: 393623
@[1]: 555818