qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: "Dr. David Alan Gilbert" <dgilbert@redhat.com>
To: Peter Maydell <peter.maydell@linaro.org>
Cc: Paolo Bonzini <pbonzini@redhat.com>,
	Russell King <rmk@armlinux.org.uk>,
	qemu-arm@nongnu.org, qemu-devel@nongnu.org
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	[thread overview]
Message-ID: <20190710131118.GC2682@work-vm> (raw)
In-Reply-To: <20190709143912.28905-1-peter.maydell@linaro.org>

* Peter Maydell (peter.maydell@linaro.org) 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 <rmk@armlinux.org.uk>
> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Yes, I think so;



Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com>

> ---
> 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 / dgilbert@redhat.com / Manchester, UK


      parent reply	other threads:[~2019-07-10 13:12 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-07-09 14:39 [Qemu-devel] [PATCH for-4.1] pl031: Correctly migrate state when using -rtc clock=host Peter Maydell
2019-07-09 17:21 ` no-reply
2019-07-10  5:17 ` Paolo Bonzini
2019-07-10 13:11 ` Dr. David Alan Gilbert [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190710131118.GC2682@work-vm \
    --to=dgilbert@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    --cc=rmk@armlinux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).