[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH for-4.1] pl031: Correctly migrate state when usi
From: |
Dr. David Alan Gilbert |
Subject: |
Re: [Qemu-devel] [PATCH for-4.1] pl031: Correctly migrate state when using -rtc clock=host |
Date: |
Wed, 10 Jul 2019 14:11:18 +0100 |
User-agent: |
Mutt/1.12.0 (2019-05-25) |
* Peter Maydell (address@hidden) wrote:
> The PL031 RTC tracks the difference between the guest RTC
> and the host RTC using a tick_offset field. For migration,
> however, we currently always migrate the offset between
> the guest and the vm_clock, even if the RTC clock is not
> the same as the vm_clock; this was an attempt to retain
> migration backwards compatibility.
>
> Unfortunately this results in the RTC behaving oddly across
> a VM state save and restore -- since the VM clock stands still
> across save-then-restore, regardless of how much real world
> time has elapsed, the guest RTC ends up out of sync with the
> host RTC in the restored VM.
>
> Fix this by migrating the raw tick_offset. To retain migration
> compatibility as far as possible, we have a new property
> migrate-tick-offset; by default this is 'true' and we will
> migrate the true tick offset in a new subsection; if the
> incoming data has no subsection we fall back to the old
> vm_clock-based offset information, so old->new migration
> compatibility is preserved. For complete new->old migration
> compatibility, the property is set to 'false' for 4.0 and
> earlier machine types (this will only affect 'virt-4.0'
> and below, as none of the other pl031-using machines are
> versioned).
>
> Reported-by: Russell King <address@hidden>
> Signed-off-by: Peter Maydell <address@hidden>
Yes, I think so;
Reviewed-by: Dr. David Alan Gilbert <address@hidden>
> ---
> I think this is correct, and it makes the "rtc clock should
> not stay still across a save/reload" work, but I feel like
> there might be some subtlety I've missed here. Review
> definitely needed...
>
> include/hw/timer/pl031.h | 2 +
> hw/core/machine.c | 1 +
> hw/timer/pl031.c | 92 ++++++++++++++++++++++++++++++++++++++--
> 3 files changed, 91 insertions(+), 4 deletions(-)
>
> diff --git a/include/hw/timer/pl031.h b/include/hw/timer/pl031.h
> index 8857c24ca5d..8c3f555ee28 100644
> --- a/include/hw/timer/pl031.h
> +++ b/include/hw/timer/pl031.h
> @@ -33,6 +33,8 @@ typedef struct PL031State {
> */
> uint32_t tick_offset_vmstate;
> uint32_t tick_offset;
> + bool tick_offset_migrated;
> + bool migrate_tick_offset;
>
> uint32_t mr;
> uint32_t lr;
> diff --git a/hw/core/machine.c b/hw/core/machine.c
> index 2be19ec0cd5..37a1111da1d 100644
> --- a/hw/core/machine.c
> +++ b/hw/core/machine.c
> @@ -34,6 +34,7 @@ GlobalProperty hw_compat_4_0[] = {
> { "virtio-vga", "edid", "false" },
> { "virtio-gpu-pci", "edid", "false" },
> { "virtio-device", "use-started", "false" },
> + { "pl031", "migrate-tick-offset", "false" },
> };
> const size_t hw_compat_4_0_len = G_N_ELEMENTS(hw_compat_4_0);
>
> diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c
> index 3378084f4a8..1a7e2ee06b3 100644
> --- a/hw/timer/pl031.c
> +++ b/hw/timer/pl031.c
> @@ -199,29 +199,94 @@ static int pl031_pre_save(void *opaque)
> {
> PL031State *s = opaque;
>
> - /* tick_offset is base_time - rtc_clock base time. Instead, we want to
> - * store the base time relative to the QEMU_CLOCK_VIRTUAL for
> backwards-compatibility. */
> + /*
> + * The PL031 device model code uses the tick_offset field, which is
> + * the offset between what the guest RTC should read and what the
> + * QEMU rtc_clock reads:
> + * guest_rtc = rtc_clock + tick_offset
> + * and so
> + * tick_offset = guest_rtc - rtc_clock
> + *
> + * We want to migrate this offset, which sounds straightforward.
> + * Unfortunately older versions of QEMU migrated a conversion of this
> + * offset into an offset from the vm_clock. (This was in turn an
> + * attempt to be compatible with even older QEMU versions, but it
> + * has incorrect behaviour if the rtc_clock is not the same as the
> + * vm_clock.) So we put the actual tick_offset into a migration
> + * subsection, and the backwards-compatible time-relative-to-vm_clock
> + * in the main migration state.
> + *
> + * Calculate base time relative to QEMU_CLOCK_VIRTUAL:
> + */
> int64_t delta = qemu_clock_get_ns(rtc_clock) -
> qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> s->tick_offset_vmstate = s->tick_offset + delta / NANOSECONDS_PER_SECOND;
>
> return 0;
> }
>
> +static int pl031_pre_load(void *opaque)
> +{
> + PL031State *s = opaque;
> +
> + s->tick_offset_migrated = false;
> + return 0;
> +}
> +
> static int pl031_post_load(void *opaque, int version_id)
> {
> PL031State *s = opaque;
>
> - int64_t delta = qemu_clock_get_ns(rtc_clock) -
> qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> - s->tick_offset = s->tick_offset_vmstate - delta / NANOSECONDS_PER_SECOND;
> + /*
> + * If we got the tick_offset subsection, then we can just use
> + * the value in that. Otherwise the source is an older QEMU and
> + * has given us the offset from the vm_clock; convert it back to
> + * an offset from the rtc_clock. This will cause time to incorrectly
> + * go backwards compared to the host RTC, but this is unavoidable.
> + */
> +
> + if (!s->tick_offset_migrated) {
> + int64_t delta = qemu_clock_get_ns(rtc_clock) -
> + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> + s->tick_offset = s->tick_offset_vmstate -
> + delta / NANOSECONDS_PER_SECOND;
> + }
> pl031_set_alarm(s);
> return 0;
> }
>
> +static int pl031_tick_offset_post_load(void *opaque, int version_id)
> +{
> + PL031State *s = opaque;
> +
> + s->tick_offset_migrated = true;
> + return 0;
> +}
> +
> +static bool pl031_tick_offset_needed(void *opaque)
> +{
> + PL031State *s = opaque;
> +
> + return s->migrate_tick_offset;
> +}
> +
> +static const VMStateDescription vmstate_pl031_tick_offset = {
> + .name = "pl031/tick-offset",
> + .version_id = 1,
> + .minimum_version_id = 1,
> + .needed = pl031_tick_offset_needed,
> + .post_load = pl031_tick_offset_post_load,
> + .fields = (VMStateField[]) {
> + VMSTATE_UINT32(tick_offset, PL031State),
> + VMSTATE_END_OF_LIST()
> + }
> +};
> +
> static const VMStateDescription vmstate_pl031 = {
> .name = "pl031",
> .version_id = 1,
> .minimum_version_id = 1,
> .pre_save = pl031_pre_save,
> + .pre_load = pl031_pre_load,
> .post_load = pl031_post_load,
> .fields = (VMStateField[]) {
> VMSTATE_UINT32(tick_offset_vmstate, PL031State),
> @@ -231,14 +296,33 @@ static const VMStateDescription vmstate_pl031 = {
> VMSTATE_UINT32(im, PL031State),
> VMSTATE_UINT32(is, PL031State),
> VMSTATE_END_OF_LIST()
> + },
> + .subsections = (const VMStateDescription*[]) {
> + &vmstate_pl031_tick_offset,
> + NULL
> }
> };
>
> +static Property pl031_properties[] = {
> + /*
> + * True to correctly migrate the tick offset of the RTC. False to
> + * obtain backward migration compatibility with older QEMU versions,
> + * at the expense of the guest RTC going backwards compared with the
> + * host RTC when the VM is saved/restored if using -rtc host.
> + * (Even if set to 'true' older QEMU can migrate forward to newer QEMU;
> + * 'false' also permits newer QEMU to migrate to older QEMU.)
> + */
> + DEFINE_PROP_BOOL("migrate-tick-offset",
> + PL031State, migrate_tick_offset, true),
> + DEFINE_PROP_END_OF_LIST()
> +};
> +
> static void pl031_class_init(ObjectClass *klass, void *data)
> {
> DeviceClass *dc = DEVICE_CLASS(klass);
>
> dc->vmsd = &vmstate_pl031;
> + dc->props = pl031_properties;
> }
>
> static const TypeInfo pl031_info = {
> --
> 2.20.1
>
--
Dr. David Alan Gilbert / address@hidden / Manchester, UK