Blame SOURCES/kvm-target-arm-kvm-Implement-virtual-time-adjustment.patch

ddf19c
From 5388ea3fc0737d1a659256ff3663057bef484c19 Mon Sep 17 00:00:00 2001
ddf19c
From: Andrew Jones <drjones@redhat.com>
ddf19c
Date: Fri, 31 Jan 2020 14:23:13 +0000
ddf19c
Subject: [PATCH 11/15] target/arm/kvm: Implement virtual time adjustment
ddf19c
MIME-Version: 1.0
ddf19c
Content-Type: text/plain; charset=UTF-8
ddf19c
Content-Transfer-Encoding: 8bit
ddf19c
ddf19c
RH-Author: Andrew Jones <drjones@redhat.com>
ddf19c
Message-id: <20200131142314.13175-5-drjones@redhat.com>
ddf19c
Patchwork-id: 93622
ddf19c
O-Subject: [RHEL-AV-8.2.0 qemu-kvm PATCH 4/5] target/arm/kvm: Implement virtual time adjustment
ddf19c
Bugzilla: 1647366
ddf19c
RH-Acked-by: Philippe Mathieu-Daudé <philmd@redhat.com>
ddf19c
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
ddf19c
RH-Acked-by: Gavin Shan <gshan@redhat.com>
ddf19c
ddf19c
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1647366
ddf19c
ddf19c
Author: Andrew Jones <drjones@redhat.com>
ddf19c
Date:   Thu, 30 Jan 2020 16:02:06 +0000
ddf19c
ddf19c
    target/arm/kvm: Implement virtual time adjustment
ddf19c
ddf19c
    When a VM is stopped (such as when it's paused) guest virtual time
ddf19c
    should stop counting. Otherwise, when the VM is resumed it will
ddf19c
    experience time jumps and its kernel may report soft lockups. Not
ddf19c
    counting virtual time while the VM is stopped has the side effect
ddf19c
    of making the guest's time appear to lag when compared with real
ddf19c
    time, and even with time derived from the physical counter. For
ddf19c
    this reason, this change, which is enabled by default, comes with
ddf19c
    a KVM CPU feature allowing it to be disabled, restoring legacy
ddf19c
    behavior.
ddf19c
ddf19c
    This patch only provides the implementation of the virtual time
ddf19c
    adjustment. A subsequent patch will provide the CPU property
ddf19c
    allowing the change to be enabled and disabled.
ddf19c
ddf19c
    Reported-by: Bijan Mottahedeh <bijan.mottahedeh@oracle.com>
ddf19c
    Signed-off-by: Andrew Jones <drjones@redhat.com>
ddf19c
    Message-id: 20200120101023.16030-6-drjones@redhat.com
ddf19c
    Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
ddf19c
    Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
ddf19c
ddf19c
(cherry picked from commit e5ac4200b4cddf44df9adbef677af0d1f1c579c6)
ddf19c
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
ddf19c
---
ddf19c
 target/arm/cpu.h     |  7 ++++
ddf19c
 target/arm/kvm.c     | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ddf19c
 target/arm/kvm32.c   |  3 ++
ddf19c
 target/arm/kvm64.c   |  3 ++
ddf19c
 target/arm/kvm_arm.h | 38 ++++++++++++++++++++++
ddf19c
 target/arm/machine.c |  7 ++++
ddf19c
 6 files changed, 150 insertions(+)
ddf19c
ddf19c
diff --git a/target/arm/cpu.h b/target/arm/cpu.h
ddf19c
index 82dd3cc..fbd8ea0 100644
ddf19c
--- a/target/arm/cpu.h
ddf19c
+++ b/target/arm/cpu.h
ddf19c
@@ -821,6 +821,13 @@ struct ARMCPU {
ddf19c
     /* KVM init features for this CPU */
ddf19c
     uint32_t kvm_init_features[7];
ddf19c
 
ddf19c
+    /* KVM CPU state */
ddf19c
+
ddf19c
+    /* KVM virtual time adjustment */
ddf19c
+    bool kvm_adjvtime;
ddf19c
+    bool kvm_vtime_dirty;
ddf19c
+    uint64_t kvm_vtime;
ddf19c
+
ddf19c
     /* Uniprocessor system with MP extensions */
ddf19c
     bool mp_is_up;
ddf19c
 
ddf19c
diff --git a/target/arm/kvm.c b/target/arm/kvm.c
ddf19c
index 5b82cef..26d7f8b 100644
ddf19c
--- a/target/arm/kvm.c
ddf19c
+++ b/target/arm/kvm.c
ddf19c
@@ -359,6 +359,22 @@ static int compare_u64(const void *a, const void *b)
ddf19c
     return 0;
ddf19c
 }
ddf19c
 
ddf19c
+/*
ddf19c
+ * cpreg_values are sorted in ascending order by KVM register ID
ddf19c
+ * (see kvm_arm_init_cpreg_list). This allows us to cheaply find
ddf19c
+ * the storage for a KVM register by ID with a binary search.
ddf19c
+ */
ddf19c
+static uint64_t *kvm_arm_get_cpreg_ptr(ARMCPU *cpu, uint64_t regidx)
ddf19c
+{
ddf19c
+    uint64_t *res;
ddf19c
+
ddf19c
+    res = bsearch(&regidx, cpu->cpreg_indexes, cpu->cpreg_array_len,
ddf19c
+                  sizeof(uint64_t), compare_u64);
ddf19c
+    assert(res);
ddf19c
+
ddf19c
+    return &cpu->cpreg_values[res - cpu->cpreg_indexes];
ddf19c
+}
ddf19c
+
ddf19c
 /* Initialize the ARMCPU cpreg list according to the kernel's
ddf19c
  * definition of what CPU registers it knows about (and throw away
ddf19c
  * the previous TCG-created cpreg list).
ddf19c
@@ -512,6 +528,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level)
ddf19c
     return ok;
ddf19c
 }
ddf19c
 
ddf19c
+void kvm_arm_cpu_pre_save(ARMCPU *cpu)
ddf19c
+{
ddf19c
+    /* KVM virtual time adjustment */
ddf19c
+    if (cpu->kvm_vtime_dirty) {
ddf19c
+        *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT) = cpu->kvm_vtime;
ddf19c
+    }
ddf19c
+}
ddf19c
+
ddf19c
+void kvm_arm_cpu_post_load(ARMCPU *cpu)
ddf19c
+{
ddf19c
+    /* KVM virtual time adjustment */
ddf19c
+    if (cpu->kvm_adjvtime) {
ddf19c
+        cpu->kvm_vtime = *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT);
ddf19c
+        cpu->kvm_vtime_dirty = true;
ddf19c
+    }
ddf19c
+}
ddf19c
+
ddf19c
 void kvm_arm_reset_vcpu(ARMCPU *cpu)
ddf19c
 {
ddf19c
     int ret;
ddf19c
@@ -579,6 +612,50 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu)
ddf19c
     return 0;
ddf19c
 }
ddf19c
 
ddf19c
+void kvm_arm_get_virtual_time(CPUState *cs)
ddf19c
+{
ddf19c
+    ARMCPU *cpu = ARM_CPU(cs);
ddf19c
+    struct kvm_one_reg reg = {
ddf19c
+        .id = KVM_REG_ARM_TIMER_CNT,
ddf19c
+        .addr = (uintptr_t)&cpu->kvm_vtime,
ddf19c
+    };
ddf19c
+    int ret;
ddf19c
+
ddf19c
+    if (cpu->kvm_vtime_dirty) {
ddf19c
+        return;
ddf19c
+    }
ddf19c
+
ddf19c
+    ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®);
ddf19c
+    if (ret) {
ddf19c
+        error_report("Failed to get KVM_REG_ARM_TIMER_CNT");
ddf19c
+        abort();
ddf19c
+    }
ddf19c
+
ddf19c
+    cpu->kvm_vtime_dirty = true;
ddf19c
+}
ddf19c
+
ddf19c
+void kvm_arm_put_virtual_time(CPUState *cs)
ddf19c
+{
ddf19c
+    ARMCPU *cpu = ARM_CPU(cs);
ddf19c
+    struct kvm_one_reg reg = {
ddf19c
+        .id = KVM_REG_ARM_TIMER_CNT,
ddf19c
+        .addr = (uintptr_t)&cpu->kvm_vtime,
ddf19c
+    };
ddf19c
+    int ret;
ddf19c
+
ddf19c
+    if (!cpu->kvm_vtime_dirty) {
ddf19c
+        return;
ddf19c
+    }
ddf19c
+
ddf19c
+    ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®);
ddf19c
+    if (ret) {
ddf19c
+        error_report("Failed to set KVM_REG_ARM_TIMER_CNT");
ddf19c
+        abort();
ddf19c
+    }
ddf19c
+
ddf19c
+    cpu->kvm_vtime_dirty = false;
ddf19c
+}
ddf19c
+
ddf19c
 int kvm_put_vcpu_events(ARMCPU *cpu)
ddf19c
 {
ddf19c
     CPUARMState *env = &cpu->env;
ddf19c
@@ -690,6 +767,21 @@ MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run)
ddf19c
     return MEMTXATTRS_UNSPECIFIED;
ddf19c
 }
ddf19c
 
ddf19c
+void kvm_arm_vm_state_change(void *opaque, int running, RunState state)
ddf19c
+{
ddf19c
+    CPUState *cs = opaque;
ddf19c
+    ARMCPU *cpu = ARM_CPU(cs);
ddf19c
+
ddf19c
+    if (running) {
ddf19c
+        if (cpu->kvm_adjvtime) {
ddf19c
+            kvm_arm_put_virtual_time(cs);
ddf19c
+        }
ddf19c
+    } else {
ddf19c
+        if (cpu->kvm_adjvtime) {
ddf19c
+            kvm_arm_get_virtual_time(cs);
ddf19c
+        }
ddf19c
+    }
ddf19c
+}
ddf19c
 
ddf19c
 int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run)
ddf19c
 {
ddf19c
diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c
ddf19c
index 32bf8d6..3a8b437 100644
ddf19c
--- a/target/arm/kvm32.c
ddf19c
+++ b/target/arm/kvm32.c
ddf19c
@@ -16,6 +16,7 @@
ddf19c
 #include "qemu-common.h"
ddf19c
 #include "cpu.h"
ddf19c
 #include "qemu/timer.h"
ddf19c
+#include "sysemu/runstate.h"
ddf19c
 #include "sysemu/kvm.h"
ddf19c
 #include "kvm_arm.h"
ddf19c
 #include "internals.h"
ddf19c
@@ -198,6 +199,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
ddf19c
         return -EINVAL;
ddf19c
     }
ddf19c
 
ddf19c
+    qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
ddf19c
+
ddf19c
     /* Determine init features for this CPU */
ddf19c
     memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
ddf19c
     if (cpu->start_powered_off) {
ddf19c
diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c
ddf19c
index 666a81a..d368189 100644
ddf19c
--- a/target/arm/kvm64.c
ddf19c
+++ b/target/arm/kvm64.c
ddf19c
@@ -23,6 +23,7 @@
ddf19c
 #include "qemu/host-utils.h"
ddf19c
 #include "qemu/main-loop.h"
ddf19c
 #include "exec/gdbstub.h"
ddf19c
+#include "sysemu/runstate.h"
ddf19c
 #include "sysemu/kvm.h"
ddf19c
 #include "sysemu/kvm_int.h"
ddf19c
 #include "kvm_arm.h"
ddf19c
@@ -735,6 +736,8 @@ int kvm_arch_init_vcpu(CPUState *cs)
ddf19c
         return -EINVAL;
ddf19c
     }
ddf19c
 
ddf19c
+    qemu_add_vm_change_state_handler(kvm_arm_vm_state_change, cs);
ddf19c
+
ddf19c
     /* Determine init features for this CPU */
ddf19c
     memset(cpu->kvm_init_features, 0, sizeof(cpu->kvm_init_features));
ddf19c
     if (cpu->start_powered_off) {
ddf19c
diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h
ddf19c
index b48a9c9..01a9a18 100644
ddf19c
--- a/target/arm/kvm_arm.h
ddf19c
+++ b/target/arm/kvm_arm.h
ddf19c
@@ -128,6 +128,23 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level);
ddf19c
 bool write_kvmstate_to_list(ARMCPU *cpu);
ddf19c
 
ddf19c
 /**
ddf19c
+ * kvm_arm_cpu_pre_save:
ddf19c
+ * @cpu: ARMCPU
ddf19c
+ *
ddf19c
+ * Called after write_kvmstate_to_list() from cpu_pre_save() to update
ddf19c
+ * the cpreg list with KVM CPU state.
ddf19c
+ */
ddf19c
+void kvm_arm_cpu_pre_save(ARMCPU *cpu);
ddf19c
+
ddf19c
+/**
ddf19c
+ * kvm_arm_cpu_post_load:
ddf19c
+ * @cpu: ARMCPU
ddf19c
+ *
ddf19c
+ * Called from cpu_post_load() to update KVM CPU state from the cpreg list.
ddf19c
+ */
ddf19c
+void kvm_arm_cpu_post_load(ARMCPU *cpu);
ddf19c
+
ddf19c
+/**
ddf19c
  * kvm_arm_reset_vcpu:
ddf19c
  * @cpu: ARMCPU
ddf19c
  *
ddf19c
@@ -292,6 +309,24 @@ int kvm_arm_sync_mpstate_to_kvm(ARMCPU *cpu);
ddf19c
  */
ddf19c
 int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu);
ddf19c
 
ddf19c
+/**
ddf19c
+ * kvm_arm_get_virtual_time:
ddf19c
+ * @cs: CPUState
ddf19c
+ *
ddf19c
+ * Gets the VCPU's virtual counter and stores it in the KVM CPU state.
ddf19c
+ */
ddf19c
+void kvm_arm_get_virtual_time(CPUState *cs);
ddf19c
+
ddf19c
+/**
ddf19c
+ * kvm_arm_put_virtual_time:
ddf19c
+ * @cs: CPUState
ddf19c
+ *
ddf19c
+ * Sets the VCPU's virtual counter to the value stored in the KVM CPU state.
ddf19c
+ */
ddf19c
+void kvm_arm_put_virtual_time(CPUState *cs);
ddf19c
+
ddf19c
+void kvm_arm_vm_state_change(void *opaque, int running, RunState state);
ddf19c
+
ddf19c
 int kvm_arm_vgic_probe(void);
ddf19c
 
ddf19c
 void kvm_arm_pmu_set_irq(CPUState *cs, int irq);
ddf19c
@@ -339,6 +374,9 @@ static inline void kvm_arm_pmu_set_irq(CPUState *cs, int irq) {}
ddf19c
 static inline void kvm_arm_pmu_init(CPUState *cs) {}
ddf19c
 
ddf19c
 static inline void kvm_arm_sve_get_vls(CPUState *cs, unsigned long *map) {}
ddf19c
+
ddf19c
+static inline void kvm_arm_get_virtual_time(CPUState *cs) {}
ddf19c
+static inline void kvm_arm_put_virtual_time(CPUState *cs) {}
ddf19c
 #endif
ddf19c
 
ddf19c
 static inline const char *gic_class_name(void)
ddf19c
diff --git a/target/arm/machine.c b/target/arm/machine.c
ddf19c
index eb28b23..241890a 100644
ddf19c
--- a/target/arm/machine.c
ddf19c
+++ b/target/arm/machine.c
ddf19c
@@ -642,6 +642,12 @@ static int cpu_pre_save(void *opaque)
ddf19c
             /* This should never fail */
ddf19c
             abort();
ddf19c
         }
ddf19c
+
ddf19c
+        /*
ddf19c
+         * kvm_arm_cpu_pre_save() must be called after
ddf19c
+         * write_kvmstate_to_list()
ddf19c
+         */
ddf19c
+        kvm_arm_cpu_pre_save(cpu);
ddf19c
     } else {
ddf19c
         if (!write_cpustate_to_list(cpu, false)) {
ddf19c
             /* This should never fail. */
ddf19c
@@ -744,6 +750,7 @@ static int cpu_post_load(void *opaque, int version_id)
ddf19c
          * we're using it.
ddf19c
          */
ddf19c
         write_list_to_cpustate(cpu);
ddf19c
+        kvm_arm_cpu_post_load(cpu);
ddf19c
     } else {
ddf19c
         if (!write_list_to_cpustate(cpu)) {
ddf19c
             return -1;
ddf19c
-- 
ddf19c
1.8.3.1
ddf19c