For controlled shutdown we issue a 'system_powerdown' command
to the QEMU monitor. This triggers an ACPI event which (most)
guest OS wire up to a controlled shutdown. There is no equiv
ACPI event to trigger a controlled reboot. This patch attempts
to fake a reboot.
- In qemuDomainObjPrivatePtr we have a bool fakeReboot
flag.
- The virDomainReboot method sets this flag and then
triggers a normal 'system_powerdown'.
- The QEMU process is started with '-no-shutdown'
so that the guest CPUs pause when it powers off the
guest
- When we receive the 'POWEROFF' event from QEMU JSON
monitor if fakeReboot is not set we invoke the
qemuProcessKill command and shutdown continues
normally
- If fakeReboot was set, we spawn a background thread
which issues 'system_reset' to perform a warm reboot
of the guest hardware. Then it issues 'cont' to
start the CPUs again
In v2:
- Fix unref of virDomainObjPtr when thread creation fails
* src/qemu/qemu_command.c: Add -no-shutdown flag if
we have JSON support
* src/qemu/qemu_domain.h: Add 'fakeReboot' flag to
qemuDomainObjPrivate struct
* src/qemu/qemu_driver.c: Fake reboot using the
system_powerdown command if JSON support is available
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
src/qemu/qemu_monitor_json.c, src/qemu/qemu_monitor_json.h,
src/qemu/qemu_monitor_text.c, src/qemu/qemu_monitor_text.h: Add
binding for system_reset command
* src/qemu/qemu_process.c: Reset the guest & start CPUs if
fakeReboot is set
---
src/qemu/qemu_command.c | 7 +++
src/qemu/qemu_domain.h | 2 +
src/qemu/qemu_driver.c | 65 ++++++++++++++++++++++++++-
src/qemu/qemu_monitor.c | 19 ++++++++
src/qemu/qemu_monitor.h | 1 +
src/qemu/qemu_monitor_json.c | 19 ++++++++
src/qemu/qemu_monitor_json.h | 1 +
src/qemu/qemu_monitor_text.c | 13 +++++
src/qemu/qemu_monitor_text.h | 1 +
src/qemu/qemu_process.c | 100 +++++++++++++++++++++++++++++++++++++++++-
10 files changed, 224 insertions(+), 4 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index b517e1a..7ac1faf 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -3222,6 +3222,13 @@ qemuBuildCommandLine(virConnectPtr conn,
def->onReboot != VIR_DOMAIN_LIFECYCLE_RESTART)
virCommandAddArg(cmd, "-no-reboot");
+ /* If JSON monitor is enabled, we can receive an event
+ * when QEMU stops. If we use no-shutdown, then we can
+ * watch for this event and do a soft/warm reboot.
+ */
+ if (monitor_json)
+ virCommandAddArg(cmd, "-no-shutdown");
+
if (!(def->features & (1 << VIR_DOMAIN_FEATURE_ACPI)))
virCommandAddArg(cmd, "-no-acpi");
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 3d041fc..f282df2 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -91,6 +91,8 @@ struct _qemuDomainObjPrivate {
virBitmapPtr qemuCaps;
char *lockState;
+
+ bool fakeReboot;
};
struct qemuDomainWatchdogEvent
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 01587e8..624f57d 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -1428,7 +1428,7 @@ cleanup:
}
-static int qemudDomainShutdown(virDomainPtr dom) {
+static int qemuDomainShutdown(virDomainPtr dom) {
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
@@ -1460,6 +1460,8 @@ static int qemudDomainShutdown(virDomainPtr dom) {
ret = qemuMonitorSystemPowerdown(priv->mon);
qemuDomainObjExitMonitor(vm);
+ priv->fakeReboot = false;
+
endjob:
if (qemuDomainObjEndJob(vm) == 0)
vm = NULL;
@@ -1471,11 +1473,66 @@ cleanup:
}
+static int qemuDomainReboot(virDomainPtr dom, unsigned int flags ATTRIBUTE_UNUSED) {
+ struct qemud_driver *driver = dom->conn->privateData;
+ virDomainObjPtr vm;
+ int ret = -1;
+ qemuDomainObjPrivatePtr priv;
+
+ qemuDriverLock(driver);
+ vm = virDomainFindByUUID(&driver->domains, dom->uuid);
+ qemuDriverUnlock(driver);
+
+ if (!vm) {
+ char uuidstr[VIR_UUID_STRING_BUFLEN];
+ virUUIDFormat(dom->uuid, uuidstr);
+ qemuReportError(VIR_ERR_NO_DOMAIN,
+ _("no domain with matching uuid '%s'"),
uuidstr);
+ goto cleanup;
+ }
+ priv = vm->privateData;
+
+#if HAVE_YAJL
+ if (qemuCapsGet(priv->qemuCaps, QEMU_CAPS_MONITOR_JSON)) {
+ if (qemuDomainObjBeginJob(vm) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_OPERATION_INVALID,
+ "%s", _("domain is not running"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterMonitor(vm);
+ ret = qemuMonitorSystemPowerdown(priv->mon);
+ qemuDomainObjExitMonitor(vm);
+
+ priv->fakeReboot = true;
+
+ endjob:
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
+ } else {
+#endif
+ qemuReportError(VIR_ERR_NO_SUPPORT, "%s",
+ _("Reboot is not supported without the JSON
monitor"));
+#if HAVE_YAJL
+ }
+#endif
+
+cleanup:
+ if (vm)
+ virDomainObjUnlock(vm);
+ return ret;
+}
+
+
static int qemudDomainDestroy(virDomainPtr dom) {
struct qemud_driver *driver = dom->conn->privateData;
virDomainObjPtr vm;
int ret = -1;
virDomainEventPtr event = NULL;
+ qemuDomainObjPrivatePtr priv;
qemuDriverLock(driver);
vm = virDomainFindByUUID(&driver->domains, dom->uuid);
@@ -1487,6 +1544,9 @@ static int qemudDomainDestroy(virDomainPtr dom) {
goto cleanup;
}
+ priv = vm->privateData;
+ priv->fakeReboot = false;
+
/* Although qemuProcessStop does this already, there may
* be an outstanding job active. We want to make sure we
* can kill the process even if a job is active. Killing
@@ -8335,7 +8395,8 @@ static virDriver qemuDriver = {
.domainLookupByName = qemudDomainLookupByName, /* 0.2.0 */
.domainSuspend = qemudDomainSuspend, /* 0.2.0 */
.domainResume = qemudDomainResume, /* 0.2.0 */
- .domainShutdown = qemudDomainShutdown, /* 0.2.0 */
+ .domainShutdown = qemuDomainShutdown, /* 0.2.0 */
+ .domainReboot = qemuDomainReboot, /* 0.9.3 */
.domainDestroy = qemudDomainDestroy, /* 0.2.0 */
.domainGetOSType = qemudDomainGetOSType, /* 0.2.2 */
.domainGetMaxMemory = qemudDomainGetMaxMemory, /* 0.4.2 */
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index 89a3f64..2aa5c9a 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -1095,6 +1095,25 @@ int qemuMonitorSystemPowerdown(qemuMonitorPtr mon)
}
+int qemuMonitorSystemReset(qemuMonitorPtr mon)
+{
+ int ret;
+ VIR_DEBUG("mon=%p", mon);
+
+ if (!mon) {
+ qemuReportError(VIR_ERR_INVALID_ARG, "%s",
+ _("monitor must not be NULL"));
+ return -1;
+ }
+
+ if (mon->json)
+ ret = qemuMonitorJSONSystemReset(mon);
+ else
+ ret = qemuMonitorTextSystemReset(mon);
+ return ret;
+}
+
+
int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
int **pids)
{
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 3bb0269..ae74a3c 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -194,6 +194,7 @@ int qemuMonitorStartCPUs(qemuMonitorPtr mon,
int qemuMonitorStopCPUs(qemuMonitorPtr mon);
int qemuMonitorGetStatus(qemuMonitorPtr mon, bool *running);
+int qemuMonitorSystemReset(qemuMonitorPtr mon);
int qemuMonitorSystemPowerdown(qemuMonitorPtr mon);
int qemuMonitorGetCPUInfo(qemuMonitorPtr mon,
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 56ec65b..6cc6ea7 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -947,6 +947,25 @@ int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon)
}
+int qemuMonitorJSONSystemReset(qemuMonitorPtr mon)
+{
+ int ret;
+ virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("system_reset", NULL);
+ virJSONValuePtr reply = NULL;
+ if (!cmd)
+ return -1;
+
+ ret = qemuMonitorJSONCommand(mon, cmd, &reply);
+
+ if (ret == 0)
+ ret = qemuMonitorJSONCheckError(cmd, reply);
+
+ virJSONValueFree(cmd);
+ virJSONValueFree(reply);
+ return ret;
+}
+
+
/*
* [ { "CPU": 0, "current": true, "halted": false,
"pc": 3227107138 },
* { "CPU": 1, "current": false, "halted": true,
"pc": 7108165 } ]
diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h
index 393d8fc..a72bf7c 100644
--- a/src/qemu/qemu_monitor_json.h
+++ b/src/qemu/qemu_monitor_json.h
@@ -49,6 +49,7 @@ int qemuMonitorJSONStopCPUs(qemuMonitorPtr mon);
int qemuMonitorJSONGetStatus(qemuMonitorPtr mon, bool *running);
int qemuMonitorJSONSystemPowerdown(qemuMonitorPtr mon);
+int qemuMonitorJSONSystemReset(qemuMonitorPtr mon);
int qemuMonitorJSONGetCPUInfo(qemuMonitorPtr mon,
int **pids);
diff --git a/src/qemu/qemu_monitor_text.c b/src/qemu/qemu_monitor_text.c
index a16ea91..67333aa 100644
--- a/src/qemu/qemu_monitor_text.c
+++ b/src/qemu/qemu_monitor_text.c
@@ -417,6 +417,19 @@ int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon) {
}
+int qemuMonitorTextSystemReset(qemuMonitorPtr mon) {
+ char *info;
+
+ if (qemuMonitorHMPCommand(mon, "system_reset", &info) < 0) {
+ qemuReportError(VIR_ERR_OPERATION_FAILED,
+ "%s", _("system reset operation failed"));
+ return -1;
+ }
+ VIR_FREE(info);
+ return 0;
+}
+
+
int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
int **pids)
{
diff --git a/src/qemu/qemu_monitor_text.h b/src/qemu/qemu_monitor_text.h
index 4fa5064..7536557 100644
--- a/src/qemu/qemu_monitor_text.h
+++ b/src/qemu/qemu_monitor_text.h
@@ -46,6 +46,7 @@ int qemuMonitorTextStopCPUs(qemuMonitorPtr mon);
int qemuMonitorTextGetStatus(qemuMonitorPtr mon, bool *running);
int qemuMonitorTextSystemPowerdown(qemuMonitorPtr mon);
+int qemuMonitorTextSystemReset(qemuMonitorPtr mon);
int qemuMonitorTextGetCPUInfo(qemuMonitorPtr mon,
int **pids);
diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c
index b441137..c44c8e9 100644
--- a/src/qemu/qemu_process.c
+++ b/src/qemu/qemu_process.c
@@ -353,13 +353,103 @@ qemuProcessHandleReset(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
}
+/*
+ * Since we have the '-no-shutdown' flag set, the
+ * QEMU process will currently have guest OS shutdown
+ * and the CPUS stopped. To fake the reboot, we thus
+ * want todo a reset of the virtual hardware, followed
+ * by restart of the CPUs. This should result in the
+ * guest OS booting up again
+ */
+static void
+qemuProcessFakeReboot(void *opaque)
+{
+ struct qemud_driver *driver = qemu_driver;
+ virDomainObjPtr vm = opaque;
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ virDomainEventPtr event = NULL;
+ int ret = -1;
+ VIR_DEBUG("vm=%p", vm);
+ qemuDriverLock(driver);
+ virDomainObjLock(vm);
+ if (qemuDomainObjBeginJob(vm) < 0)
+ goto cleanup;
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ goto endjob;
+ }
+
+ qemuDomainObjEnterMonitorWithDriver(driver, vm);
+ if (qemuMonitorSystemReset(priv->mon) < 0) {
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+ goto endjob;
+ }
+ qemuDomainObjExitMonitorWithDriver(driver, vm);
+
+ if (!virDomainObjIsActive(vm)) {
+ qemuReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("guest unexpectedly quit"));
+ goto endjob;
+ }
+
+ if (qemuProcessStartCPUs(driver, vm, NULL,
+ VIR_DOMAIN_RUNNING_BOOTED) < 0) {
+ if (virGetLastError() == NULL)
+ qemuReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("resume operation failed"));
+ goto endjob;
+ }
+ event = virDomainEventNewFromObj(vm,
+ VIR_DOMAIN_EVENT_RESUMED,
+ VIR_DOMAIN_EVENT_RESUMED_UNPAUSED);
+
+ ret = 0;
+
+endjob:
+ if (qemuDomainObjEndJob(vm) == 0)
+ vm = NULL;
+
+cleanup:
+ if (vm) {
+ if (ret == -1)
+ qemuProcessKill(vm);
+ if (virDomainObjUnref(vm) > 0)
+ virDomainObjUnlock(vm);
+ }
+ if (event)
+ qemuDomainEventQueue(driver, event);
+ qemuDriverUnlock(driver);
+}
+
+
static int
qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
virDomainObjPtr vm)
{
+ qemuDomainObjPrivatePtr priv = vm->privateData;
+ VIR_DEBUG("vm=%p", vm);
+
virDomainObjLock(vm);
- ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true;
- virDomainObjUnlock(vm);
+ priv->gotShutdown = true;
+ if (priv->fakeReboot) {
+ virDomainObjRef(vm);
+ virThread th;
+ if (virThreadCreate(&th,
+ false,
+ qemuProcessFakeReboot,
+ vm) < 0) {
+ VIR_ERROR("Failed to create reboot thread, killing domain");
+ qemuProcessKill(vm);
+ if (virDomainObjUnref(vm) == 0)
+ vm = NULL;
+ }
+ } else {
+ qemuProcessKill(vm);
+ }
+ if (vm)
+ virDomainObjUnlock(vm);
return 0;
}
@@ -2087,6 +2177,11 @@ qemuProcessPrepareMonitorChr(struct qemud_driver *driver,
}
+/*
+ * Precondition: Both driver and vm must be locked,
+ * and a job must be active. This method will call
+ * {Enter,Exit}MonitorWithDriver
+ */
int
qemuProcessStartCPUs(struct qemud_driver *driver, virDomainObjPtr vm,
virConnectPtr conn, virDomainRunningReason reason)
@@ -2349,6 +2444,7 @@ int qemuProcessStart(virConnectPtr conn,
goto cleanup;
vm->def->id = driver->nextvmid++;
+ priv->fakeReboot = false;
/* Run an early hook to set-up missing devices */
if (virHookPresent(VIR_HOOK_DRIVER_QEMU)) {
--
1.7.4.4