]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
KVM: x86: Use gfn_to_pfn_cache for pv_time
authorDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 9 Feb 2022 18:10:29 +0000 (18:10 +0000)
committerDavid Woodhouse <dwmw@amazon.co.uk>
Wed, 9 Feb 2022 21:25:43 +0000 (21:25 +0000)
Add a new kvm_setup_guest_pvclock() which parallels the existing
kvm_setup_pvclock_page(). The latter will be removed once we convert
all users to the gfn_to_pfn_cache version.

Using the new cache, we can potentially let kvm_set_guest_paused() set
the PVCLOCK_GUEST_STOPPED bit directly rather than having to delegate
to the vCPU via KVM_REQ_CLOCK_UPDATE. But not yet.

Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/x86.c

index 1e73053fd2bf94db15846b1019dabfc34250ac2c..c64b80c07fdd9be13634655ef166c7e39e044d04 100644 (file)
@@ -747,8 +747,7 @@ struct kvm_vcpu_arch {
        gpa_t time;
        struct pvclock_vcpu_time_info hv_clock;
        unsigned int hw_tsc_khz;
-       struct gfn_to_hva_cache pv_time;
-       bool pv_time_enabled;
+       struct gfn_to_pfn_cache pv_time;
        /* set guest stopped flag in pvclock flags field */
        bool pvclock_set_guest_stopped_request;
 
index 5d0191bf30b36601c4c840d6f11af572cf4c8444..992baebf0a58da3cd632acb855e66b34575f2e43 100644 (file)
@@ -2209,14 +2209,14 @@ static void kvm_write_system_time(struct kvm_vcpu *vcpu, gpa_t system_time,
        kvm_make_request(KVM_REQ_GLOBAL_CLOCK_UPDATE, vcpu);
 
        /* we verify if the enable bit is set... */
-       vcpu->arch.pv_time_enabled = false;
-       if (!(system_time & 1))
-               return;
-
-       if (!kvm_gfn_to_hva_cache_init(vcpu->kvm,
-                                      &vcpu->arch.pv_time, system_time & ~1ULL,
-                                      sizeof(struct pvclock_vcpu_time_info)))
-               vcpu->arch.pv_time_enabled = true;
+       if (system_time & 1) {
+               kvm_gfn_to_pfn_cache_init(vcpu->kvm, &vcpu->arch.pv_time, vcpu,
+                                         false, true, system_time & ~1ULL,
+                                         sizeof(struct pvclock_vcpu_time_info),
+                                         false);
+       } else {
+               kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.pv_time);
+       }
 
        return;
 }
@@ -2919,6 +2919,56 @@ u64 get_kvmclock_ns(struct kvm *kvm)
        return data.clock;
 }
 
+static void kvm_setup_guest_pvclock(struct kvm_vcpu *v,
+                                   struct gfn_to_pfn_cache *gpc,
+                                   unsigned int offset)
+{
+       struct kvm_vcpu_arch *vcpu = &v->arch;
+       struct pvclock_vcpu_time_info *guest_hv_clock;
+       unsigned long flags;
+
+       read_lock_irqsave(&gpc->lock, flags);
+       while (!kvm_gfn_to_pfn_cache_check(v->kvm, gpc, gpc->gpa,
+                                          offset + sizeof(*guest_hv_clock))) {
+               read_unlock_irqrestore(&gpc->lock, flags);
+
+               if (kvm_gfn_to_pfn_cache_refresh(v->kvm, gpc, gpc->gpa,
+                                                offset + sizeof(*guest_hv_clock),
+                                                true))
+                       return;
+
+               read_lock_irqsave(&gpc->lock, flags);
+       }
+
+       guest_hv_clock = (void *)(gpc->khva + offset);
+
+       /*
+        * This VCPU is paused, but it's legal for a guest to read another
+        * VCPU's kvmclock, so we really have to follow the specification where
+        * it says that version is odd if data is being modified, and even after
+        * it is consistent.
+        */
+
+       guest_hv_clock->version = vcpu->hv_clock.version = (guest_hv_clock->version + 1) | 1;
+       smp_wmb();
+
+       /* retain PVCLOCK_GUEST_STOPPED if set in guest copy */
+       vcpu->hv_clock.flags |= (guest_hv_clock->flags & PVCLOCK_GUEST_STOPPED);
+
+       if (vcpu->pvclock_set_guest_stopped_request) {
+               vcpu->hv_clock.flags |= PVCLOCK_GUEST_STOPPED;
+               vcpu->pvclock_set_guest_stopped_request = false;
+       }
+
+       memcpy(guest_hv_clock, &vcpu->hv_clock, sizeof(*guest_hv_clock));
+       smp_wmb();
+
+       guest_hv_clock->version = ++vcpu->hv_clock.version;
+       read_unlock_irqrestore(&gpc->lock, flags);
+
+       trace_kvm_pvclock_update(v->vcpu_id, &vcpu->hv_clock);
+}
+
 static void kvm_setup_pvclock_page(struct kvm_vcpu *v,
                                   struct gfn_to_hva_cache *cache,
                                   unsigned int offset)
@@ -3064,8 +3114,8 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
 
        vcpu->hv_clock.flags = pvclock_flags;
 
-       if (vcpu->pv_time_enabled)
-               kvm_setup_pvclock_page(v, &vcpu->pv_time, 0);
+       if (vcpu->pv_time.active)
+               kvm_setup_guest_pvclock(v, &vcpu->pv_time, 0);
        if (vcpu->xen.vcpu_info_set)
                kvm_setup_pvclock_page(v, &vcpu->xen.vcpu_info_cache,
                                       offsetof(struct compat_vcpu_info, time));
@@ -3259,7 +3309,7 @@ static int kvm_pv_enable_async_pf_int(struct kvm_vcpu *vcpu, u64 data)
 
 static void kvmclock_reset(struct kvm_vcpu *vcpu)
 {
-       vcpu->arch.pv_time_enabled = false;
+       kvm_gfn_to_pfn_cache_destroy(vcpu->kvm, &vcpu->arch.pv_time);
        vcpu->arch.time = 0;
 }
 
@@ -5058,7 +5108,7 @@ static int kvm_vcpu_ioctl_x86_set_xcrs(struct kvm_vcpu *vcpu,
  */
 static int kvm_set_guest_paused(struct kvm_vcpu *vcpu)
 {
-       if (!vcpu->arch.pv_time_enabled)
+       if (!vcpu->arch.pv_time.active)
                return -EINVAL;
        vcpu->arch.pvclock_set_guest_stopped_request = true;
        kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu);
@@ -6122,7 +6172,7 @@ static int kvm_arch_suspend_notifier(struct kvm *kvm)
 
        mutex_lock(&kvm->lock);
        kvm_for_each_vcpu(i, vcpu, kvm) {
-               if (!vcpu->arch.pv_time_enabled)
+               if (!vcpu->arch.pv_time.active)
                        continue;
 
                ret = kvm_set_guest_paused(vcpu);