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 uses the post-load callback to implemented model-specific logic. Signed-off-by: Jan Kiszka <jan.kiszka@xxxxxxxxxxx> --- hw/apic.c | 33 +++++++++++++++------------------ hw/apic_common.c | 41 +++++++++++++++++++++++++++++++++++++++-- hw/apic_internal.h | 2 ++ 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/hw/apic.c b/hw/apic.c index 8f1d5aa..94b0841 100644 --- a/hw/apic.c +++ b/hw/apic.c @@ -527,25 +527,9 @@ static uint32_t apic_get_current_count(APICCommonState *s) static void apic_timer_update(APICCommonState *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); } } @@ -759,6 +743,18 @@ static void apic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) } } +static int apic_post_load(void *opaque, int version_id) +{ + APICState *s = opaque; + + if (s->apic.timer_expiry != -1) { + qemu_mod_timer(s->apic.timer, s->apic.timer_expiry); + } else { + qemu_del_timer(s->apic.timer); + } + return 0; +} + static const MemoryRegionOps apic_io_ops = { .old_mmio = { .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, @@ -781,6 +777,7 @@ static const VMStateDescription vmstate_apic = { .version_id = APIC_VMSTATE_VERSION, .minimum_version_id = 3, .minimum_version_id_old = 1, + .post_load = apic_post_load, .fields = (VMStateField[]) { VMSTATE_STRUCT(apic, APICState, 0, vmstate_apic_common, APICCommonState), diff --git a/hw/apic_common.c b/hw/apic_common.c index 455d44c..1aeb909 100644 --- a/hw/apic_common.c +++ b/hw/apic_common.c @@ -95,6 +95,39 @@ void apic_deliver_nmi(DeviceState *d) info->external_nmi(s); } +bool apic_next_timer(APICCommonState *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) { APICCommonState *s = DO_UPCAST(APICCommonState, busdev.qdev, d); @@ -122,7 +155,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_common(DeviceState *d) @@ -212,7 +248,8 @@ const VMStateDescription vmstate_apic_common = { VMSTATE_UINT32(initial_count, APICCommonState), VMSTATE_INT64(initial_count_load_time, APICCommonState), VMSTATE_INT64(next_time, APICCommonState), - VMSTATE_TIMER(timer, APICCommonState), + VMSTATE_INT64(timer_expiry, + APICCommonState), /* open-coded timer state */ VMSTATE_END_OF_LIST() } }; diff --git a/hw/apic_internal.h b/hw/apic_internal.h index 2ca18d4..eaddf7f 100644 --- a/hw/apic_internal.h +++ b/hw/apic_internal.h @@ -92,6 +92,7 @@ struct APICCommonState { int64_t next_time; int idx; QEMUTimer *timer; + int64_t timer_expiry; int sipi_vector; int wait_for_sipi; }; @@ -115,6 +116,7 @@ extern const VMStateDescription vmstate_apic_common; DEFINE_PROP_PTR("cpu_env", cont_type, cont_var.cpu_env) void apic_report_irq_delivered(int delivered); +bool apic_next_timer(APICCommonState *s, int64_t current_time); void apic_reset_common(DeviceState *d); int apic_init_common(SysBusDevice *d); -- 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