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