[libvirt] [PATCH] qemu: Issue rtc_reset_reinjection command after guest-set-time

An advice appeared there on the qemu-devel list [1]. When a domain is suspended and then resumed guest kernel is not aware of this. So we've introduced virDomainSetTime API that resets the time within guest using qemu-ga. On the other hand, qemu itself is trying to make RTC beat faster to catch the difference. But if we don't tell qemu that guest's time was reset via the other method, both mechanisms are applied resulting in again wrong guest time. In order to avoid summing both corrections we need to tell qemu that it should not use the RTC injection if the guest time is set via guest agent. 1: http://www.mail-archive.com/qemu-devel@nongnu.org/msg236435.html Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_driver.c | 10 ++++++++++ src/qemu/qemu_monitor.c | 33 +++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.h | 2 ++ src/qemu/qemu_monitor_json.c | 21 +++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 2 ++ 5 files changed, 68 insertions(+) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index b6219ba..bdfd155 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -16879,6 +16879,16 @@ qemuDomainSetTime(virDomainPtr dom, rv = qemuAgentSetTime(priv->agent, seconds, nseconds, rtcSync); qemuDomainObjExitAgent(vm); + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + qemuDomainObjEnterMonitor(driver, vm); + rv = qemuMonitorRTCResetReinjection(priv->mon); + qemuDomainObjExitMonitor(driver, vm); + if (rv < 0) goto endjob; diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 3d9f87b..77627bc 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -4037,3 +4037,36 @@ qemuMonitorGetGuestCPU(qemuMonitorPtr mon, return qemuMonitorJSONGetGuestCPU(mon, arch, data); } + +/** + * qemuMonitorRTCResetReinjection: + * @mon: Pointer to the monitor + * + * Issue rtc-reset-reinjection command. + * This should be used in cases where guest time is restored via + * guest agent so RTC injection is not needed (in fact it will + * confuse guest's RTC). + * + * Returns 0 on success + * -1 on error. + */ +int +qemuMonitorRTCResetReinjection(qemuMonitorPtr mon) +{ + + VIR_DEBUG("mon=%p", mon); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (!mon->json) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return qemuMonitorJSONRTCResetReinjection(mon); +} diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index c3695f2..4fd6f01 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -790,6 +790,8 @@ int qemuMonitorGetGuestCPU(qemuMonitorPtr mon, virArch arch, virCPUDataPtr *data); +int qemuMonitorRTCResetReinjection(qemuMonitorPtr mon); + /** * When running two dd process and using <> redirection, we need a * shell that will not truncate files. These two strings serve that diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index a62c02f..538110c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -5851,3 +5851,24 @@ qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, return -1; } } + +int +qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection", + NULL))) + return ret; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index 5f6c846..d8c9308 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -437,4 +437,6 @@ int qemuMonitorJSONGetDeviceAliases(qemuMonitorPtr mon, int qemuMonitorJSONGetGuestCPU(qemuMonitorPtr mon, virArch arch, virCPUDataPtr *data); + +int qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon); #endif /* QEMU_MONITOR_JSON_H */ -- 1.8.5.5

On 08/13/2014 06:51 AM, Michal Privoznik wrote: s/_/-/2 in the subject line
An advice appeared there on the qemu-devel list [1]. When a domain is suspended and then resumed guest kernel is not aware of this. So we've introduced virDomainSetTime API that resets the time within guest using qemu-ga. On the other hand, qemu itself is trying to make RTC beat faster to catch the difference. But if we don't tell qemu that guest's time was reset via the other method, both mechanisms are applied resulting in again wrong guest time. In order to avoid summing both corrections we need to tell qemu that it should not use the RTC injection if the guest time is set via guest agent.
1: http://www.mail-archive.com/qemu-devel@nongnu.org/msg236435.html
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
+++ b/src/qemu/qemu_driver.c @@ -16879,6 +16879,16 @@ qemuDomainSetTime(virDomainPtr dom, rv = qemuAgentSetTime(priv->agent, seconds, nseconds, rtcSync); qemuDomainObjExitAgent(vm);
+ if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + qemuDomainObjEnterMonitor(driver, vm); + rv = qemuMonitorRTCResetReinjection(priv->mon); + qemuDomainObjExitMonitor(driver, vm);
This forces the command to fail if qemu is too old to have rtc-reset-reinjection but the agent is new enough to set time. Should you make this code conditional on whether qemu supports the QMP command?
} + +int +qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection", + NULL))) + return ret; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +}
Is it worth enhancing the testsuite to add coverage for this command and expected response? -- Eric Blake eblake redhat com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On 13.08.2014 18:30, Eric Blake wrote:
On 08/13/2014 06:51 AM, Michal Privoznik wrote:
s/_/-/2 in the subject line
An advice appeared there on the qemu-devel list [1]. When a domain is suspended and then resumed guest kernel is not aware of this. So we've introduced virDomainSetTime API that resets the time within guest using qemu-ga. On the other hand, qemu itself is trying to make RTC beat faster to catch the difference. But if we don't tell qemu that guest's time was reset via the other method, both mechanisms are applied resulting in again wrong guest time. In order to avoid summing both corrections we need to tell qemu that it should not use the RTC injection if the guest time is set via guest agent.
1: http://www.mail-archive.com/qemu-devel@nongnu.org/msg236435.html
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> ---
+++ b/src/qemu/qemu_driver.c @@ -16879,6 +16879,16 @@ qemuDomainSetTime(virDomainPtr dom, rv = qemuAgentSetTime(priv->agent, seconds, nseconds, rtcSync); qemuDomainObjExitAgent(vm);
+ if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto endjob; + } + + qemuDomainObjEnterMonitor(driver, vm); + rv = qemuMonitorRTCResetReinjection(priv->mon); + qemuDomainObjExitMonitor(driver, vm);
This forces the command to fail if qemu is too old to have rtc-reset-reinjection but the agent is new enough to set time. Should you make this code conditional on whether qemu supports the QMP command?
I'm not sure. If that's the case, both corrections will apply so guest ends up with incorrect time anyway. And if the API is to guarantee correctly set time, it must fail if such guarantees can't be made IMO.
} + +int +qemuMonitorJSONRTCResetReinjection(qemuMonitorPtr mon) +{ + int ret = -1; + virJSONValuePtr cmd; + virJSONValuePtr reply = NULL; + + if (!(cmd = qemuMonitorJSONMakeCommand("rtc-reset-reinjection", + NULL))) + return ret; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +}
Is it worth enhancing the testsuite to add coverage for this command and expected response?
Yeah. I'll post v2. Michal
participants (2)
-
Eric Blake
-
Michal Privoznik