Skip to content

Commit 0823570

Browse files
vittyvkbonzini
authored andcommitted
KVM: x86: hyper-v: Introduce TLB flush fifo
To allow flushing individual GVAs instead of always flushing the whole VPID a per-vCPU structure to pass the requests is needed. Use standard 'kfifo' to queue two types of entries: individual GVA (GFN + up to 4095 following GFNs in the lower 12 bits) and 'flush all'. The size of the fifo is arbitrarily set to '16'. Note, kvm_hv_flush_tlb() only queues 'flush all' entries for now and kvm_hv_vcpu_flush_tlb() doesn't actually read the fifo just resets the queue before returning -EOPNOTSUPP (which triggers full TLB flush) so the functional change is very small but the infrastructure is prepared to handle individual GVA flush requests. Reviewed-by: Sean Christopherson <seanjc@google.com> Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20221101145426.251680-10-vkuznets@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent adc43ca commit 0823570

File tree

5 files changed

+92
-3
lines changed

5 files changed

+92
-3
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <linux/clocksource.h>
2626
#include <linux/irqbypass.h>
2727
#include <linux/hyperv.h>
28+
#include <linux/kfifo.h>
2829

2930
#include <asm/apic.h>
3031
#include <asm/pvclock-abi.h>
@@ -618,6 +619,23 @@ struct kvm_vcpu_hv_synic {
618619
bool dont_zero_synic_pages;
619620
};
620621

622+
/* The maximum number of entries on the TLB flush fifo. */
623+
#define KVM_HV_TLB_FLUSH_FIFO_SIZE (16)
624+
/*
625+
* Note: the following 'magic' entry is made up by KVM to avoid putting
626+
* anything besides GVA on the TLB flush fifo. It is theoretically possible
627+
* to observe a request to flush 4095 PFNs starting from 0xfffffffffffff000
628+
* which will look identical. KVM's action to 'flush everything' instead of
629+
* flushing these particular addresses is, however, fully legitimate as
630+
* flushing more than requested is always OK.
631+
*/
632+
#define KVM_HV_TLB_FLUSHALL_ENTRY ((u64)-1)
633+
634+
struct kvm_vcpu_hv_tlb_flush_fifo {
635+
spinlock_t write_lock;
636+
DECLARE_KFIFO(entries, u64, KVM_HV_TLB_FLUSH_FIFO_SIZE);
637+
};
638+
621639
/* Hyper-V per vcpu emulation context */
622640
struct kvm_vcpu_hv {
623641
struct kvm_vcpu *vcpu;
@@ -639,6 +657,8 @@ struct kvm_vcpu_hv {
639657
u32 nested_eax; /* HYPERV_CPUID_NESTED_FEATURES.EAX */
640658
u32 nested_ebx; /* HYPERV_CPUID_NESTED_FEATURES.EBX */
641659
} cpuid_cache;
660+
661+
struct kvm_vcpu_hv_tlb_flush_fifo tlb_flush_fifo;
642662
};
643663

644664
/* Xen HVM per vcpu emulation context */

arch/x86/kvm/hyperv.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <linux/kvm_host.h>
3030
#include <linux/highmem.h>
3131
#include <linux/sched/cputime.h>
32+
#include <linux/spinlock.h>
3233
#include <linux/eventfd.h>
3334

3435
#include <asm/apicdef.h>
@@ -954,6 +955,9 @@ int kvm_hv_vcpu_init(struct kvm_vcpu *vcpu)
954955

955956
hv_vcpu->vp_index = vcpu->vcpu_idx;
956957

958+
INIT_KFIFO(hv_vcpu->tlb_flush_fifo.entries);
959+
spin_lock_init(&hv_vcpu->tlb_flush_fifo.write_lock);
960+
957961
return 0;
958962
}
959963

@@ -1783,6 +1787,37 @@ static u64 kvm_get_sparse_vp_set(struct kvm *kvm, struct kvm_hv_hcall *hc,
17831787
var_cnt * sizeof(*sparse_banks));
17841788
}
17851789

1790+
static void hv_tlb_flush_enqueue(struct kvm_vcpu *vcpu)
1791+
{
1792+
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
1793+
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
1794+
u64 flush_all_entry = KVM_HV_TLB_FLUSHALL_ENTRY;
1795+
1796+
if (!hv_vcpu)
1797+
return;
1798+
1799+
tlb_flush_fifo = &hv_vcpu->tlb_flush_fifo;
1800+
1801+
kfifo_in_spinlocked_noirqsave(&tlb_flush_fifo->entries, &flush_all_entry,
1802+
1, &tlb_flush_fifo->write_lock);
1803+
}
1804+
1805+
int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu)
1806+
{
1807+
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
1808+
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
1809+
1810+
if (!hv_vcpu)
1811+
return -EINVAL;
1812+
1813+
tlb_flush_fifo = &hv_vcpu->tlb_flush_fifo;
1814+
1815+
kfifo_reset_out(&tlb_flush_fifo->entries);
1816+
1817+
/* Precise flushing isn't implemented yet. */
1818+
return -EOPNOTSUPP;
1819+
}
1820+
17861821
static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
17871822
{
17881823
struct kvm *kvm = vcpu->kvm;
@@ -1791,6 +1826,8 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
17911826
DECLARE_BITMAP(vcpu_mask, KVM_MAX_VCPUS);
17921827
u64 valid_bank_mask;
17931828
u64 sparse_banks[KVM_HV_MAX_SPARSE_VCPU_SET_BITS];
1829+
struct kvm_vcpu *v;
1830+
unsigned long i;
17941831
bool all_cpus;
17951832

17961833
/*
@@ -1870,10 +1907,20 @@ static u64 kvm_hv_flush_tlb(struct kvm_vcpu *vcpu, struct kvm_hv_hcall *hc)
18701907
* analyze it here, flush TLB regardless of the specified address space.
18711908
*/
18721909
if (all_cpus) {
1910+
kvm_for_each_vcpu(i, v, kvm)
1911+
hv_tlb_flush_enqueue(v);
1912+
18731913
kvm_make_all_cpus_request(kvm, KVM_REQ_HV_TLB_FLUSH);
18741914
} else {
18751915
sparse_set_to_vcpu_mask(kvm, sparse_banks, valid_bank_mask, vcpu_mask);
18761916

1917+
for_each_set_bit(i, vcpu_mask, KVM_MAX_VCPUS) {
1918+
v = kvm_get_vcpu(kvm, i);
1919+
if (!v)
1920+
continue;
1921+
hv_tlb_flush_enqueue(v);
1922+
}
1923+
18771924
kvm_make_vcpus_request_mask(kvm, KVM_REQ_HV_TLB_FLUSH, vcpu_mask);
18781925
}
18791926

arch/x86/kvm/hyperv.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,19 @@ int kvm_vm_ioctl_hv_eventfd(struct kvm *kvm, struct kvm_hyperv_eventfd *args);
151151
int kvm_get_hv_cpuid(struct kvm_vcpu *vcpu, struct kvm_cpuid2 *cpuid,
152152
struct kvm_cpuid_entry2 __user *entries);
153153

154+
static inline void kvm_hv_vcpu_purge_flush_tlb(struct kvm_vcpu *vcpu)
155+
{
156+
struct kvm_vcpu_hv_tlb_flush_fifo *tlb_flush_fifo;
157+
struct kvm_vcpu_hv *hv_vcpu = to_hv_vcpu(vcpu);
158+
159+
if (!hv_vcpu || !kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu))
160+
return;
161+
162+
tlb_flush_fifo = &hv_vcpu->tlb_flush_fifo;
163+
164+
kfifo_reset_out(&tlb_flush_fifo->entries);
165+
}
166+
167+
int kvm_hv_vcpu_flush_tlb(struct kvm_vcpu *vcpu);
168+
154169
#endif

arch/x86/kvm/svm/svm.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3727,7 +3727,7 @@ static void svm_flush_tlb_current(struct kvm_vcpu *vcpu)
37273727
* A TLB flush for the current ASID flushes both "host" and "guest" TLB
37283728
* entries, and thus is a superset of Hyper-V's fine grained flushing.
37293729
*/
3730-
kvm_clear_request(KVM_REQ_HV_TLB_FLUSH, vcpu);
3730+
kvm_hv_vcpu_purge_flush_tlb(vcpu);
37313731

37323732
/*
37333733
* Flush only the current ASID even if the TLB flush was invoked via

arch/x86/kvm/x86.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3425,7 +3425,7 @@ static void kvm_vcpu_flush_tlb_guest(struct kvm_vcpu *vcpu)
34253425
* Flushing all "guest" TLB is always a superset of Hyper-V's fine
34263426
* grained flushing.
34273427
*/
3428-
kvm_clear_request(KVM_REQ_HV_TLB_FLUSH, vcpu);
3428+
kvm_hv_vcpu_purge_flush_tlb(vcpu);
34293429
}
34303430

34313431

@@ -10256,7 +10256,14 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
1025610256

1025710257
kvm_service_local_tlb_flush_requests(vcpu);
1025810258

10259-
if (kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu))
10259+
/*
10260+
* Fall back to a "full" guest flush if Hyper-V's precise
10261+
* flushing fails. Note, Hyper-V's flushing is per-vCPU, but
10262+
* the flushes are considered "remote" and not "local" because
10263+
* the requests can be initiated from other vCPUs.
10264+
*/
10265+
if (kvm_check_request(KVM_REQ_HV_TLB_FLUSH, vcpu) &&
10266+
kvm_hv_vcpu_flush_tlb(vcpu))
1026010267
kvm_vcpu_flush_tlb_guest(vcpu);
1026110268

1026210269
if (kvm_check_request(KVM_REQ_REPORT_TPR_ACCESS, vcpu)) {

0 commit comments

Comments
 (0)