From: David Woodhouse <[email protected]> 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 <[email protected]> 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

