Add monitor callback API domainGuestPanic, that implements 'destroy', 'restart' and 'preserve' events of the 'on_crash' in the XML when domain crashed. --- src/qemu/qemu_domain.h | 1 + src/qemu/qemu_driver.c | 92 ++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 14 ++++++- src/qemu/qemu_monitor.h | 4 ++ src/qemu/qemu_monitor_json.c | 7 ++++ src/qemu/qemu_process.c | 45 +++++++++++++++++++++- 6 files changed, 161 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index aedb66b..ac3eea9 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -172,6 +172,7 @@ struct _qemuDomainObjPrivate { typedef enum { QEMU_PROCESS_EVENT_WATCHDOG = 0, + QEMU_PROCESS_EVENT_GUESTPANIC, QEMU_PROCESS_EVENT_LAST } qemuProcessEventType; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 16f826c..24df772 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -137,6 +137,10 @@ static void processWatchdogEvent(virQEMUDriverPtr driver, virDomainObjPtr vm, int action); +static void processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action); + static void qemuProcessEventHandler(void *data, void *opaque); static int qemuStateCleanup(void); @@ -3509,18 +3513,106 @@ cleanup: virObjectUnref(cfg); } +static void +processGuestPanicEvent(virQEMUDriverPtr driver, + virDomainObjPtr vm, + int action) +{ + qemuDomainObjPrivatePtr priv = vm->privateData; + virDomainEventPtr event = NULL; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + + if (!virDomainObjIsActive(vm)) { + VIR_DEBUG("Ignoring GUEST_PANICKED event from inactive domain %s", + vm->def->name); + goto cleanup; + } + + virDomainObjSetState(vm, + VIR_DOMAIN_CRASHED, + VIR_DOMAIN_CRASHED_PANICKED); + + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_CRASHED, + VIR_DOMAIN_EVENT_CRASHED_PANICKED); + + if (event) + qemuDomainEventQueue(driver, event); + + if (virDomainLockProcessPause(driver->lockManager, vm, &priv->lockState) < 0) + VIR_WARN("Unable to release lease on %s", vm->def->name); + VIR_DEBUG("Preserving lock state '%s'", NULLSTR(priv->lockState)); + + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm) < 0) { + VIR_WARN("Unable to save status on vm %s after state change", + vm->def->name); + } + + switch (action) { + case VIR_DOMAIN_LIFECYCLE_CRASH_DESTROY: + priv->beingDestroyed = true; + + if (qemuProcessKill(vm, VIR_QEMU_PROCESS_KILL_FORCE) < 0) { + priv->beingDestroyed = false; + goto cleanup; + } + + priv->beingDestroyed = false; + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is not running")); + goto cleanup; + } + + qemuProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_CRASHED, 0); + event = virDomainEventNewFromObj(vm, + VIR_DOMAIN_EVENT_STOPPED, + VIR_DOMAIN_EVENT_STOPPED_CRASHED); + + if (event) + qemuDomainEventQueue(driver, event); + + virDomainAuditStop(vm, "destroyed"); + + if (!vm->persistent) { + qemuDomainRemoveInactive(driver, vm); + } + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_RESTART: + qemuDomainSetFakeReboot(driver, vm, true); + qemuProcessShutdownOrReboot(driver, vm); + break; + + case VIR_DOMAIN_LIFECYCLE_CRASH_PRESERVE: + break; + + default: + break; + } + +cleanup: + virObjectUnref(cfg); +} + static void qemuProcessEventHandler(void *data, void *opaque) { struct qemuProcessEvent *processEvent = data; virDomainObjPtr vm = processEvent->vm; virQEMUDriverPtr driver = opaque; + VIR_DEBUG("vm=%p", vm); + virObjectLock(vm); switch (processEvent->eventType) { case QEMU_PROCESS_EVENT_WATCHDOG: processWatchdogEvent(driver, vm, processEvent->action); break; + case QEMU_PROCESS_EVENT_GUESTPANIC: + processGuestPanicEvent(driver, vm, processEvent->action); + break; default: break; } diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 4e35f79..442f6fa 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -113,7 +113,7 @@ VIR_ENUM_IMPL(qemuMonitorVMStatus, QEMU_MONITOR_VM_STATUS_LAST, "debug", "inmigrate", "internal-error", "io-error", "paused", "postmigrate", "prelaunch", "finish-migrate", "restore-vm", - "running", "save-vm", "shutdown", "watchdog") + "running", "save-vm", "shutdown", "watchdog", "guest-panic") typedef enum { QEMU_MONITOR_BLOCK_IO_STATUS_OK, @@ -1032,6 +1032,15 @@ int qemuMonitorEmitResume(qemuMonitorPtr mon) } +int qemuMonitorEmitGuestPanic(qemuMonitorPtr mon) +{ + int ret = -1; + VIR_DEBUG("mon=%p", mon); + QEMU_MONITOR_CALLBACK(mon, ret, domainGuestPanic, mon->vm); + return ret; +} + + int qemuMonitorEmitRTCChange(qemuMonitorPtr mon, long long offset) { int ret = -1; @@ -3185,6 +3194,9 @@ int qemuMonitorVMStatusToPausedReason(const char *status) case QEMU_MONITOR_VM_STATUS_WATCHDOG: return VIR_DOMAIN_PAUSED_WATCHDOG; + case QEMU_MONITOR_VM_STATUS_GUEST_PANICKED: + return VIR_DOMAIN_PAUSED_GUEST_PANICKED; + /* unreachable from this point on */ case QEMU_MONITOR_VM_STATUS_LAST: ; diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index a607712..f0a8c6f 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -140,6 +140,8 @@ struct _qemuMonitorCallbacks { unsigned long long actual); int (*domainPMSuspendDisk)(qemuMonitorPtr mon, virDomainObjPtr vm); + int (*domainGuestPanic)(qemuMonitorPtr mon, + virDomainObjPtr vm); }; char *qemuMonitorEscapeArg(const char *in); @@ -220,6 +222,7 @@ int qemuMonitorEmitBlockJob(qemuMonitorPtr mon, int qemuMonitorEmitBalloonChange(qemuMonitorPtr mon, unsigned long long actual); int qemuMonitorEmitPMSuspendDisk(qemuMonitorPtr mon); +int qemuMonitorEmitGuestPanic(qemuMonitorPtr mon); int qemuMonitorStartCPUs(qemuMonitorPtr mon, virConnectPtr conn); @@ -239,6 +242,7 @@ typedef enum { QEMU_MONITOR_VM_STATUS_SAVE_VM, QEMU_MONITOR_VM_STATUS_SHUTDOWN, QEMU_MONITOR_VM_STATUS_WATCHDOG, + QEMU_MONITOR_VM_STATUS_GUEST_PANICKED, QEMU_MONITOR_VM_STATUS_LAST } qemuMonitorVMStatus; diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 2b73884..2dd832c 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -74,6 +74,7 @@ static void qemuMonitorJSONHandleBlockJobCanceled(qemuMonitorPtr mon, virJSONVal static void qemuMonitorJSONHandleBlockJobReady(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandleBalloonChange(qemuMonitorPtr mon, virJSONValuePtr data); static void qemuMonitorJSONHandlePMSuspendDisk(qemuMonitorPtr mon, virJSONValuePtr data); +static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data); typedef struct { const char *type; @@ -87,6 +88,7 @@ static qemuEventHandler eventHandlers[] = { { "BLOCK_JOB_COMPLETED", qemuMonitorJSONHandleBlockJobCompleted, }, { "BLOCK_JOB_READY", qemuMonitorJSONHandleBlockJobReady, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, + { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, { "POWERDOWN", qemuMonitorJSONHandlePowerdown, }, { "RESET", qemuMonitorJSONHandleReset, }, { "RESUME", qemuMonitorJSONHandleResume, }, @@ -593,6 +595,11 @@ static void qemuMonitorJSONHandleResume(qemuMonitorPtr mon, virJSONValuePtr data qemuMonitorEmitResume(mon); } +static void qemuMonitorJSONHandleGuestPanic(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED) +{ + qemuMonitorEmitGuestPanic(mon); +} + static void qemuMonitorJSONHandleRTCChange(qemuMonitorPtr mon, virJSONValuePtr data) { long long offset = 0; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 163bd96..0658d91 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -548,6 +548,7 @@ qemuProcessFakeReboot(void *opaque) qemuDomainObjPrivatePtr priv = vm->privateData; virDomainEventPtr event = NULL; virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + virDomainRunningReason reason = VIR_DOMAIN_RUNNING_BOOTED; int ret = -1; VIR_DEBUG("vm=%p", vm); virObjectLock(vm); @@ -573,8 +574,11 @@ qemuProcessFakeReboot(void *opaque) goto endjob; } + if (virDomainObjGetState(vm, NULL) == VIR_DOMAIN_CRASHED) + reason = VIR_DOMAIN_RUNNING_CRASHED; + if (qemuProcessStartCPUs(driver, vm, NULL, - VIR_DOMAIN_RUNNING_BOOTED, + reason, QEMU_ASYNC_JOB_NONE) < 0) { if (virGetLastError() == NULL) virReportError(VIR_ERR_INTERNAL_ERROR, @@ -1271,6 +1275,40 @@ qemuProcessHandlePMSuspendDisk(qemuMonitorPtr mon ATTRIBUTE_UNUSED, } +static int +qemuProcessHandleGuestPanic(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm) +{ + virQEMUDriverPtr driver = qemu_driver; + struct qemuProcessEvent *processEvent; + + virObjectLock(vm); + if (VIR_ALLOC(processEvent) < 0) { + virReportOOMError(); + goto cleanup; + } + + processEvent->eventType = QEMU_PROCESS_EVENT_GUESTPANIC; + processEvent->action = vm->def->onCrash; + processEvent->vm = vm; + /* Hold an extra reference because we can't allow 'vm' to be + * deleted before handling guest panic event is finished. + */ + virObjectRef(vm); + if (virThreadPoolSendJob(driver->workerPool, 0, processEvent) < 0) { + if (!virObjectUnref(vm)) + vm = NULL; + VIR_FREE(processEvent); + } + +cleanup: + if (vm) + virObjectUnlock(vm); + + return 0; +} + + static qemuMonitorCallbacks monitorCallbacks = { .destroy = qemuProcessHandleMonitorDestroy, .eofNotify = qemuProcessHandleMonitorEOF, @@ -1290,6 +1328,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainPMSuspend = qemuProcessHandlePMSuspend, .domainBalloonChange = qemuProcessHandleBalloonChange, .domainPMSuspendDisk = qemuProcessHandlePMSuspendDisk, + .domainGuestPanic = qemuProcessHandleGuestPanic, }; static int @@ -2674,6 +2713,10 @@ qemuProcessUpdateState(virQEMUDriverPtr driver, virDomainObjPtr vm) newState = VIR_DOMAIN_SHUTDOWN; newReason = VIR_DOMAIN_SHUTDOWN_UNKNOWN; ignore_value(VIR_STRDUP_QUIET(msg, "shutdown")); + } else if (reason == VIR_DOMAIN_PAUSED_GUEST_PANICKED) { + newState = VIR_DOMAIN_CRASHED; + newReason = VIR_DOMAIN_CRASHED_PANICKED; + ignore_value(VIR_STRDUP_QUIET(msg, "crashed")); } else { newState = VIR_DOMAIN_PAUSED; newReason = reason; -- 1.8.1.4 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list