[RFC PATCH 17/55] KVM: x86: hyper-v: flush the calling plane's TLB on flush hypercalls
From: David Woodhouse
Date: Sat Oct 17 2026 - 08:40:59 EST
From: David Woodhouse <dwmw@xxxxxxxxxxxx>
The Hyper-V TLB flush hypercalls resolve their target vCPUs through
the plane-0 array, so a flush issued from a non-zero plane flushed
only the plane-0 vCPUs and the caller's own plane kept stale
translations. The Windows secure kernel repopulated a PFN-database
page table entry, flushed, then took a fatal page fault on the very
address it had just mapped, because VTL1's TLB still held the old
read-only zero-page translation.
Resolve each flush target to the vCPU of the calling plane (falling
back to plane 0 where no such plane vCPU exists — it then has no TLB
to flush either) and raise the flush request on that vCPU directly.
Signed-off-by: David Woodhouse <dwmw@xxxxxxxxxxxx>
Assisted-by: LLM
---
arch/x86/kvm/hyperv.c | 34 ++++++++++++++++++++++++++++++++--
1 file changed, 32 insertions(+), 2 deletions(-)
diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c
index eefc18c750f1..797c5cbfbbaf 100644
--- a/arch/x86/kvm/hyperv.c
+++ b/arch/x86/kvm/hyperv.c
@@ -2122,6 +2122,23 @@ static int kvm_hv_hypercall_check_params(struct kvm_vcpu *vcpu,
return 0;
}
+/*
+ * A TLB flush hypercall flushes the caller's VTL view of the target VPs:
+ * resolve the target to the vCPU of the caller's plane, which has its own
+ * roots and TLB tag. Fall back to plane 0 when the plane vCPU does not
+ * exist (it then has no TLB to flush either).
+ */
+static struct kvm_vcpu *hv_flush_plane_vcpu(struct kvm_vcpu *caller,
+ struct kvm_vcpu *v)
+{
+ struct kvm_vcpu *pv;
+
+ if (!caller->plane_level)
+ return v;
+ pv = v->common->vcpus[caller->plane_level];
+ return pv ?: v;
+}
+
static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
{
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
@@ -2257,12 +2274,18 @@ static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
*/
if (all_cpus && !is_guest_mode(vcpu)) {
kvm_for_each_vcpu(i, v, kvm) {
+ v = hv_flush_plane_vcpu(vcpu, v);
tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
hv_tlb_flush_enqueue(v, tlb_flush_fifo,
tlb_flush_entries, hc->rep_cnt);
+ if (vcpu->plane_level) {
+ kvm_make_request(KVM_REQ_HV_TLB_FLUSH, v);
+ kvm_vcpu_kick(v);
+ }
}
- kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
+ if (!vcpu->plane_level)
+ kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
} else if (!is_guest_mode(vcpu)) {
sparse_set_to_vcpu_mask(kvm, sparse_banks, valid_bank_mask, vcpu_mask);
@@ -2270,12 +2293,19 @@ static s64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
v = kvm_get_vcpu(kvm, i);
if (!v)
continue;
+ v = hv_flush_plane_vcpu(vcpu, v);
tlb_flush_fifo = kvm_hv_get_tlb_flush_fifo(v, false);
hv_tlb_flush_enqueue(v, tlb_flush_fifo,
tlb_flush_entries, hc->rep_cnt);
+ if (vcpu->plane_level) {
+ kvm_make_request(KVM_REQ_HV_TLB_FLUSH, v);
+ kvm_vcpu_kick(v);
+ }
}
- kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH, vcpu_mask);
+ if (!vcpu->plane_level)
+ kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH,
+ vcpu_mask);
} else {
struct kvm_vcpu_hv *hv_v;
--
2.50.1