[RFC][PATCH 05/16] apic: Open-code timer save/restore

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Jan Kiszka <jan.kiszka@xxxxxxxxxxx>

To enable migration between accelerated and non-accelerated APIC models,
we will need to handle the timer saving and restoring specially and can
no longer rely on the automatics of VMSTATE_TIMER. Specifically,
accelerated model will not start any QEMUTimer.

This patch therefore factors out the generic bits into apic_next_timer
and introduces a post-load callback that can be implemented differently
by both models.

Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx>
---
 hw/apic.c          |   30 ++++++++++++------------------
 hw/apic_common.c   |   51 +++++++++++++++++++++++++++++++++++++++++++++++++--
 hw/apic_internal.h |    3 +++
 3 files changed, 64 insertions(+), 20 deletions(-)

diff --git a/hw/apic.c b/hw/apic.c
index 27b18d6..9b83c0c 100644
--- a/hw/apic.c
+++ b/hw/apic.c
@@ -516,25 +516,9 @@ static uint32_t apic_get_current_count(APICState *s)
 
 static void apic_timer_update(APICState *s, int64_t current_time)
 {
-    int64_t next_time, d;
-
-    if (!(s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED)) {
-        d = (current_time - s->initial_count_load_time) >>
-            s->count_shift;
-        if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
-            if (!s->initial_count)
-                goto no_timer;
-            d = ((d / ((uint64_t)s->initial_count + 1)) + 1) * ((uint64_t)s->initial_count + 1);
-        } else {
-            if (d >= s->initial_count)
-                goto no_timer;
-            d = (uint64_t)s->initial_count + 1;
-        }
-        next_time = s->initial_count_load_time + (d << s->count_shift);
-        qemu_mod_timer(s->timer, next_time);
-        s->next_time = next_time;
+    if (apic_next_timer(s, current_time)) {
+        qemu_mod_timer(s->timer, s->next_time);
     } else {
-    no_timer:
         qemu_del_timer(s->timer);
     }
 }
@@ -756,6 +740,15 @@ static const MemoryRegionOps apic_io_ops = {
     .endianness = DEVICE_NATIVE_ENDIAN,
 };
 
+static void apic_post_load(APICState *s)
+{
+    if (s->timer_expiry != -1) {
+        qemu_mod_timer(s->timer, s->timer_expiry);
+    } else {
+        qemu_del_timer(s->timer);
+    }
+}
+
 static int apic_init(SysBusDevice *dev)
 {
     APICState *s = FROM_SYSBUS(APICState, dev);
@@ -772,6 +765,7 @@ static int apic_init(SysBusDevice *dev)
     s->timer = qemu_new_timer_ns(vm_clock, apic_timer, s);
     s->set_base = apic_set_base;
     s->set_tpr = apic_set_tpr;
+    s->post_load = apic_post_load;
     local_apics[s->idx] = s;
     return 0;
 }
diff --git a/hw/apic_common.c b/hw/apic_common.c
index 7d30356..84a3a27 100644
--- a/hw/apic_common.c
+++ b/hw/apic_common.c
@@ -80,6 +80,39 @@ int apic_get_irq_delivered(void)
     return apic_irq_delivered;
 }
 
+bool apic_next_timer(APICState *s, int64_t current_time)
+{
+    int64_t d;
+
+    /* We need to store the timer state separately to support APIC
+     * implementations that maintain a non-QEMU timer, e.g. inside the
+     * host kernel. This open-coded state allows us to migrate between
+     * both models. */
+    s->timer_expiry = -1;
+
+    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_MASKED) {
+        return false;
+    }
+
+    d = (current_time - s->initial_count_load_time) >> s->count_shift;
+
+    if (s->lvt[APIC_LVT_TIMER] & APIC_LVT_TIMER_PERIODIC) {
+        if (!s->initial_count) {
+            return false;
+        }
+        d = ((d / ((uint64_t)s->initial_count + 1)) + 1) *
+            ((uint64_t)s->initial_count + 1);
+    } else {
+        if (d >= s->initial_count) {
+            return false;
+        }
+        d = (uint64_t)s->initial_count + 1;
+    }
+    s->next_time = s->initial_count_load_time + (d << s->count_shift);
+    s->timer_expiry = s->next_time;
+    return true;
+}
+
 void apic_init_reset(DeviceState *d)
 {
     APICState *s = DO_UPCAST(APICState, busdev.qdev, d);
@@ -107,7 +140,10 @@ void apic_init_reset(DeviceState *d)
     s->next_time = 0;
     s->wait_for_sipi = 1;
 
-    qemu_del_timer(s->timer);
+    if (s->timer) {
+        qemu_del_timer(s->timer);
+    }
+    s->timer_expiry = -1;
 }
 
 void apic_reset(DeviceState *d)
@@ -172,12 +208,23 @@ static int apic_load_old(QEMUFile *f, void *opaque, int version_id)
     return 0;
 }
 
+static int apic_dispatch_post_load(void *opaque, int version_id)
+{
+    APICState *s = opaque;
+
+    if (s->post_load) {
+        s->post_load(s);
+    }
+    return 0;
+}
+
 const VMStateDescription vmstate_apic = {
     .name = "apic",
     .version_id = 3,
     .minimum_version_id = 3,
     .minimum_version_id_old = 1,
     .load_state_old = apic_load_old,
+    .post_load = apic_dispatch_post_load,
     .fields      = (VMStateField[]) {
         VMSTATE_UINT32(apicbase, APICState),
         VMSTATE_UINT8(id, APICState),
@@ -197,7 +244,7 @@ const VMStateDescription vmstate_apic = {
         VMSTATE_UINT32(initial_count, APICState),
         VMSTATE_INT64(initial_count_load_time, APICState),
         VMSTATE_INT64(next_time, APICState),
-        VMSTATE_TIMER(timer, APICState),
+        VMSTATE_INT64(timer_expiry, APICState), /* open-coded timer state */
         VMSTATE_END_OF_LIST()
     }
 };
diff --git a/hw/apic_internal.h b/hw/apic_internal.h
index 36b45ce..b110cf3 100644
--- a/hw/apic_internal.h
+++ b/hw/apic_internal.h
@@ -92,17 +92,20 @@ struct APICState {
     int64_t next_time;
     int idx;
     QEMUTimer *timer;
+    int64_t timer_expiry;
     int sipi_vector;
     int wait_for_sipi;
 
     void (*set_base)(APICState *s, uint64_t val);
     void (*set_tpr)(APICState *s, uint8_t val);
+    void (*post_load)(APICState *s);
 };
 
 extern const VMStateDescription vmstate_apic;
 
 int apic_init_common(APICState *s);
 void apic_reset(DeviceState *d);
+bool apic_next_timer(APICState *s, int64_t current_time);
 void apic_set_irq_delivered(int delivered);
 
 #endif /* !QEMU_APIC_INTERNAL_H */
-- 
1.7.3.4

--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux