| From 88475933b0e1c6d10b7a4d6171927fc0a0a17443 Mon Sep 17 00:00:00 2001 |
| From: Vitaly Kuznetsov <vkuznets@redhat.com> |
| Date: Wed, 20 Mar 2019 18:43:20 +0100 |
| Subject: x86: kvm: hyper-v: deal with buggy TLB flush requests from WS2012 |
| |
| [ Upstream commit da66761c2d93a46270d69001abb5692717495a68 ] |
| |
| It was reported that with some special Multi Processor Group configuration, |
| e.g: |
| bcdedit.exe /set groupsize 1 |
| bcdedit.exe /set maxgroup on |
| bcdedit.exe /set groupaware on |
| for a 16-vCPU guest WS2012 shows BSOD on boot when PV TLB flush mechanism |
| is in use. |
| |
| Tracing kvm_hv_flush_tlb immediately reveals the issue: |
| |
| kvm_hv_flush_tlb: processor_mask 0x0 address_space 0x0 flags 0x2 |
| |
| The only flag set in this request is HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES, |
| however, processor_mask is 0x0 and no HV_FLUSH_ALL_PROCESSORS is specified. |
| We don't flush anything and apparently it's not what Windows expects. |
| |
| TLFS doesn't say anything about such requests and newer Windows versions |
| seem to be unaffected. This all feels like a WS2012 bug, which is, however, |
| easy to workaround in KVM: let's flush everything when we see an empty |
| flush request, over-flushing doesn't hurt. |
| |
| Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> |
| Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/kvm/hyperv.c | 11 ++++++++++- |
| 1 file changed, 10 insertions(+), 1 deletion(-) |
| |
| diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c |
| index 01d209ab5481b..229d996051653 100644 |
| --- a/arch/x86/kvm/hyperv.c |
| +++ b/arch/x86/kvm/hyperv.c |
| @@ -1291,7 +1291,16 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *current_vcpu, u64 ingpa, |
| flush.address_space, flush.flags); |
| |
| sparse_banks[0] = flush.processor_mask; |
| - all_cpus = flush.flags & HV_FLUSH_ALL_PROCESSORS; |
| + |
| + /* |
| + * Work around possible WS2012 bug: it sends hypercalls |
| + * with processor_mask = 0x0 and HV_FLUSH_ALL_PROCESSORS clear, |
| + * while also expecting us to flush something and crashing if |
| + * we don't. Let's treat processor_mask == 0 same as |
| + * HV_FLUSH_ALL_PROCESSORS. |
| + */ |
| + all_cpus = (flush.flags & HV_FLUSH_ALL_PROCESSORS) || |
| + flush.processor_mask == 0; |
| } else { |
| if (unlikely(kvm_read_guest(kvm, ingpa, &flush_ex, |
| sizeof(flush_ex)))) |
| -- |
| 2.20.1 |
| |