[libvirt] [PATCH] Support reboots with the QEMU driver

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 * 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 | 95 +++++++++++++++++++++++++++++++++++++++++- 10 files changed, 220 insertions(+), 3 deletions(-) diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6346243..9ae1409 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3207,6 +3207,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 bacf5b5..b8469b6 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -89,6 +89,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 853c84c..4c827cf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1429,7 +1429,7 @@ cleanup: } -static int qemudDomainShutdown(virDomainPtr dom) { +static int qemuDomainShutdown(virDomainPtr dom) { struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; int ret = -1; @@ -1461,6 +1461,8 @@ static int qemudDomainShutdown(virDomainPtr dom) { ret = qemuMonitorSystemPowerdown(priv->mon); qemuDomainObjExitMonitor(vm); + priv->fakeReboot = false; + endjob: if (qemuDomainObjEndJob(vm) == 0) vm = NULL; @@ -1472,11 +1474,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); @@ -1488,6 +1545,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 @@ -8146,7 +8206,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 1428921..1dafc4b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1096,6 +1096,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 6838417..694d8a9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -348,12 +348,99 @@ 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; + 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); + } + } else { + qemuProcessKill(vm); + } virDomainObjUnlock(vm); return 0; @@ -1920,6 +2007,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) @@ -2182,6 +2274,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

At 06/16/2011 01:06 AM, Daniel P. Berrange Write:
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
* 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 | 95 +++++++++++++++++++++++++++++++++++++++++- 10 files changed, 220 insertions(+), 3 deletions(-)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 6346243..9ae1409 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3207,6 +3207,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 bacf5b5..b8469b6 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -89,6 +89,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 853c84c..4c827cf 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -1429,7 +1429,7 @@ cleanup: }
-static int qemudDomainShutdown(virDomainPtr dom) { +static int qemuDomainShutdown(virDomainPtr dom) { struct qemud_driver *driver = dom->conn->privateData; virDomainObjPtr vm; int ret = -1; @@ -1461,6 +1461,8 @@ static int qemudDomainShutdown(virDomainPtr dom) { ret = qemuMonitorSystemPowerdown(priv->mon); qemuDomainObjExitMonitor(vm);
+ priv->fakeReboot = false; + endjob: if (qemuDomainObjEndJob(vm) == 0) vm = NULL; @@ -1472,11 +1474,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); @@ -1488,6 +1545,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 @@ -8146,7 +8206,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 1428921..1dafc4b 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1096,6 +1096,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 6838417..694d8a9 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -348,12 +348,99 @@ 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;
I think we should get vm->privateData after we lock vm.
+ VIR_DEBUG("vm=%p", vm); + virDomainObjLock(vm); - ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true; + 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);
We should hold an extra reference the vm here. If the vm is not persistent and we destroy the vm before the thread runs, libvirtd may crash(I think, not test it).
+ } + } else { + qemuProcessKill(vm); + } virDomainObjUnlock(vm);
return 0; @@ -1920,6 +2007,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) @@ -2182,6 +2274,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)) {

On Fri, Jun 17, 2011 at 10:52:03AM +0800, Wen Congyang wrote:
At 06/16/2011 01:06 AM, Daniel P. Berrange Write:
+ + static int qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData;
I think we should get vm->privateData after we lock vm.
Yep, good point.
+ VIR_DEBUG("vm=%p", vm); + virDomainObjLock(vm); - ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true; + 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);
We should hold an extra reference the vm here. If the vm is not persistent and we destroy the vm before the thread runs, libvirtd may crash(I think, not test it).
I'm already taking an extra reference just before starting the thread. I need to release that actually in virThreadCreate fails to run.
+ } + } else { + qemuProcessKill(vm); + } virDomainObjUnlock(vm);
return 0;
Daiel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

At 06/17/2011 09:26 PM, Daniel P. Berrange Write:
On Fri, Jun 17, 2011 at 10:52:03AM +0800, Wen Congyang wrote:
At 06/16/2011 01:06 AM, Daniel P. Berrange Write:
+ + static int qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData;
I think we should get vm->privateData after we lock vm.
Yep, good point.
+ VIR_DEBUG("vm=%p", vm); + virDomainObjLock(vm); - ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true; + 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);
We should hold an extra reference the vm here. If the vm is not persistent and we destroy the vm before the thread runs, libvirtd may crash(I think, not test it).
I'm already taking an extra reference just before starting the thread. I need to release that actually in virThreadCreate fails to run.
Ah, yes, you have already taken an extrareference. Another question: I reboot and migrate domain like this: # virsh reboot vm1; virsh migrate vm1 --p2p qemu+tls://<dest host>/system When the domain is migrated to the dested, it isnot rebooted, but shut down. I think we should continue reboot after migration.
+ } + } else { + qemuProcessKill(vm); + } virDomainObjUnlock(vm);
return 0;
Daiel

On Mon, Jun 20, 2011 at 03:19:27PM +0800, Wen Congyang wrote:
At 06/17/2011 09:26 PM, Daniel P. Berrange Write:
On Fri, Jun 17, 2011 at 10:52:03AM +0800, Wen Congyang wrote:
At 06/16/2011 01:06 AM, Daniel P. Berrange Write:
+ + static int qemuProcessHandleShutdown(qemuMonitorPtr mon ATTRIBUTE_UNUSED, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv = vm->privateData;
I think we should get vm->privateData after we lock vm.
Yep, good point.
+ VIR_DEBUG("vm=%p", vm); + virDomainObjLock(vm); - ((qemuDomainObjPrivatePtr) vm->privateData)->gotShutdown = true; + 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);
We should hold an extra reference the vm here. If the vm is not persistent and we destroy the vm before the thread runs, libvirtd may crash(I think, not test it).
I'm already taking an extra reference just before starting the thread. I need to release that actually in virThreadCreate fails to run.
Ah, yes, you have already taken an extrareference.
Another question: I reboot and migrate domain like this: # virsh reboot vm1; virsh migrate vm1 --p2p qemu+tls://<dest host>/system
When the domain is migrated to the dested, it isnot rebooted, but shut down.
Yeah that is pretty much expected due to the way we fake the reboot. It might be possible to work this work across migration by passing the reboot flag from qemuDomainObjPrivate in the migration cookie XML. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|
participants (2)
-
Daniel P. Berrange
-
Wen Congyang