all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Jakob Klocker <j.klocker@proxmox.com>
To: pve-devel@lists.proxmox.com
Cc: Jakob Klocker <j.klocker@proxmox.com>
Subject: [PATCH qemu-server 1/2] fix #5032: qemu: sync guest time on resume and snapshot of saved state
Date: Fri, 12 Jun 2026 14:29:41 +0200	[thread overview]
Message-ID: <20260612122942.181958-2-j.klocker@proxmox.com> (raw)
In-Reply-To: <20260612122942.181958-1-j.klocker@proxmox.com>

When a VM is resumed from a saved state (hibernation or snapshot with
RAM), the guest clock may be stale. A time skew can occur when
creating a snapshot of a running VM.

Add a new agent option `set-time-on-resume` to automatically
synchronize the guest time with the host using the QEMU Guest Agent.

Trigger time synchronization:
- after restoring a VM from a saved state or snapshot with RAM
- after taking a snapshot

This ensures consistent guest time after rollback, restore, or
snapshot operations when the guest OS clock does not automatically
correct itself.

Link: https://bugzilla.proxmox.com/show_bug.cgi?id=5032

Signed-off-by: Jakob Klocker <j.klocker@proxmox.com>
---
 src/PVE/QemuConfig.pm       |  7 ++++++
 src/PVE/QemuServer.pm       | 10 ++++++++
 src/PVE/QemuServer/Agent.pm | 47 +++++++++++++++++++++++++++++++++++++
 3 files changed, 64 insertions(+)

diff --git a/src/PVE/QemuConfig.pm b/src/PVE/QemuConfig.pm
index 80a3999e..1950c328 100644
--- a/src/PVE/QemuConfig.pm
+++ b/src/PVE/QemuConfig.pm
@@ -383,6 +383,13 @@ sub __snapshot_create_vol_snapshots_hook {
                     next;
                 }
             }
+            if ($snap->{vmstate}) {
+                my $conf = $class->load_config($vmid);
+                if (PVE::QemuServer::Agent::should_set_time_on_resume($conf->{agent})) {
+                    eval { PVE::QemuServer::Agent::guest_set_time($vmid); 1 }
+                        or warn "could not sync guest time after snapshot - $@";
+                }
+            }
         }
     }
 }
diff --git a/src/PVE/QemuServer.pm b/src/PVE/QemuServer.pm
index 239b0cab..99bdd9f5 100644
--- a/src/PVE/QemuServer.pm
+++ b/src/PVE/QemuServer.pm
@@ -5983,6 +5983,16 @@ sub vm_start_nolock {
         );
     }
 
+    my $from_saved_state = $resume || ($statefile && !$migratedfrom);
+
+    if (
+        $from_saved_state
+        && PVE::QemuServer::Agent::should_set_time_on_resume($conf->{agent})
+    ) {
+        eval { PVE::QemuServer::Agent::guest_set_time($vmid); 1 }
+            or warn "could not sync guest time after snapshot - $@";
+    }
+
     return $res;
 }
 
diff --git a/src/PVE/QemuServer/Agent.pm b/src/PVE/QemuServer/Agent.pm
index be6df443..be8bbae6 100644
--- a/src/PVE/QemuServer/Agent.pm
+++ b/src/PVE/QemuServer/Agent.pm
@@ -4,6 +4,7 @@ use v5.36;
 
 use JSON;
 use MIME::Base64 qw(decode_base64 encode_base64);
+use Time::HiRes ();
 
 use PVE::JSONSchema;
 
@@ -18,6 +19,8 @@ our @EXPORT_OK = qw(
     get_qga_key
     parse_guest_agent
     qga_check_running
+    should_set_time_on_resume
+    guest_set_time
 );
 
 our $agent_fmt = {
@@ -52,6 +55,21 @@ our $agent_fmt = {
         optional => 1,
         default => 1,
     },
+    'set-time-on-resume' => {
+        description => "Update the guest clock through QGA after resuming from"
+            . " hibernation or rolling back to a snapshot with RAM.",
+        verbose_description =>
+            "Whether to issue the guest-set-time QEMU guest agent command after the VM"
+            . " resumes with a restored RAM state, that is, when waking from hibernation"
+            . " or after rolling back to a snapshot that includes RAM. In these cases the"
+            . " guest's clock still reflects the time the state was saved. With this"
+            . " option enabled, the clock is synchronized to the host's current time,"
+            . " provided the QEMU Guest Agent option is enabled in the guest's"
+            . " configuration and the agent is running inside of the guest.",
+        type => 'boolean',
+        optional => 1,
+        default => 1,
+    },
     type => {
         description => "Select the agent type",
         type => 'string',
@@ -332,4 +350,33 @@ sub guest_fs_freeze_applicable($agent_str, $vmid, $logfunc = undef) {
     return 1;
 }
 
+=head3 should_set_time_on_resume
+
+Returns whether the guest's clock should be synchronized to the host's via the QEMU Guest Agent
+when the VM is resumed from saved state. Does B<not> check whether the agent is actually running.
+
+=cut
+
+sub should_set_time_on_resume($agent_str) {
+    my $agent = parse_guest_agent($agent_str);
+    return 0 if !$agent->{enabled};
+    return $agent->{'set-time-on-resume'} // 1;
+}
+
+=head3 guest_set_time
+
+Sets the guest's clock via the QEMU Guest Agent's C<guest-set-time> command. If C<$time_ns>
+(nanoseconds since the UNIX epoch, UTC) is not given, the current host time is used. Passing
+an explicit time is required because the agent's argument-less form reads the guest's RTC,
+which may itself be stale after a vmstate snapshot or resume.
+
+=cut
+
+sub guest_set_time($vmid, $time_ns = undef) {
+    $time_ns //= int(Time::HiRes::time() * 1_000_000_000);
+    my $res = PVE::QemuServer::Monitor::mon_cmd($vmid, 'guest-set-time', time => $time_ns);
+    check_agent_error($res, "unable to set guest time");
+    return;
+}
+
 1;
-- 
2.47.3




  reply	other threads:[~2026-06-12 12:30 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-12 12:29 [PATCH manager/qemu-server 0/2] fix #5032: sync guest time after restore from saved state Jakob Klocker
2026-06-12 12:29 ` Jakob Klocker [this message]
2026-06-12 12:29 ` [PATCH pve-manager 2/2] fix #5032: ui: qemu agent: add set-time-on-resume option Jakob Klocker

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=20260612122942.181958-2-j.klocker@proxmox.com \
    --to=j.klocker@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal