KVM: arm64: pvtime: Fix stolen time accounting across migration
authorAndrew Jones <drjones@redhat.com>
Tue, 4 Aug 2020 17:06:02 +0000 (19:06 +0200)
committerMarc Zyngier <maz@kernel.org>
Fri, 21 Aug 2020 13:04:14 +0000 (14:04 +0100)
When updating the stolen time we should always read the current
stolen time from the user provided memory, not from a kernel
cache. If we use a cache then we'll end up resetting stolen time
to zero on the first update after migration.

Signed-off-by: Andrew Jones <drjones@redhat.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20200804170604.42662-5-drjones@redhat.com
arch/arm64/include/asm/kvm_host.h
arch/arm64/kvm/pvtime.c
include/linux/kvm_host.h

index 65568b2..dd9c3b2 100644 (file)
@@ -368,7 +368,6 @@ struct kvm_vcpu_arch {
 
        /* Guest PV state */
        struct {
-               u64 steal;
                u64 last_steal;
                gpa_t base;
        } steal;
index 241ded7..7523432 100644 (file)
 void kvm_update_stolen_time(struct kvm_vcpu *vcpu)
 {
        struct kvm *kvm = vcpu->kvm;
+       u64 base = vcpu->arch.steal.base;
        u64 last_steal = vcpu->arch.steal.last_steal;
-       u64 steal;
-       __le64 steal_le;
-       u64 offset;
+       u64 offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
+       u64 steal = 0;
        int idx;
-       u64 base = vcpu->arch.steal.base;
 
        if (base == GPA_INVALID)
                return;
 
-       /* Let's do the local bookkeeping */
-       steal = vcpu->arch.steal.steal;
-       vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay);
-       steal += vcpu->arch.steal.last_steal - last_steal;
-       vcpu->arch.steal.steal = steal;
-
-       steal_le = cpu_to_le64(steal);
        idx = srcu_read_lock(&kvm->srcu);
-       offset = offsetof(struct pvclock_vcpu_stolen_time, stolen_time);
-       kvm_put_guest(kvm, base + offset, steal_le);
+       if (!kvm_get_guest(kvm, base + offset, steal)) {
+               steal = le64_to_cpu(steal);
+               vcpu->arch.steal.last_steal = READ_ONCE(current->sched_info.run_delay);
+               steal += vcpu->arch.steal.last_steal - last_steal;
+               kvm_put_guest(kvm, base + offset, cpu_to_le64(steal));
+       }
        srcu_read_unlock(&kvm->srcu, idx);
 }
 
@@ -66,7 +62,6 @@ gpa_t kvm_init_stolen_time(struct kvm_vcpu *vcpu)
         * Start counting stolen time from the time the guest requests
         * the feature enabled.
         */
-       vcpu->arch.steal.steal = 0;
        vcpu->arch.steal.last_steal = current->sched_info.run_delay;
 
        idx = srcu_read_lock(&kvm->srcu);
index 84371fb..05e3c2f 100644 (file)
@@ -749,6 +749,26 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
 int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc,
                              gpa_t gpa, unsigned long len);
 
+#define __kvm_get_guest(kvm, gfn, offset, v)                           \
+({                                                                     \
+       unsigned long __addr = gfn_to_hva(kvm, gfn);                    \
+       typeof(v) __user *__uaddr = (typeof(__uaddr))(__addr + offset); \
+       int __ret = -EFAULT;                                            \
+                                                                       \
+       if (!kvm_is_error_hva(__addr))                                  \
+               __ret = get_user(v, __uaddr);                           \
+       __ret;                                                          \
+})
+
+#define kvm_get_guest(kvm, gpa, v)                                     \
+({                                                                     \
+       gpa_t __gpa = gpa;                                              \
+       struct kvm *__kvm = kvm;                                        \
+                                                                       \
+       __kvm_get_guest(__kvm, __gpa >> PAGE_SHIFT,                     \
+                       offset_in_page(__gpa), v);                      \
+})
+
 #define __kvm_put_guest(kvm, gfn, offset, v)                           \
 ({                                                                     \
        unsigned long __addr = gfn_to_hva(kvm, gfn);                    \