qemu: Implement 'oncrash' events when guest panicked

Add monitor callback API domainGuestPanic, that implements
'destroy', 'restart' and 'preserve' events of the 'on_crash'
in the XML when domain crashed.
This commit is contained in:
Chen Fan 2013-06-07 18:23:34 +08:00 committed by Eric Blake
parent e8ccf7ed8a
commit 9aa527dccb
6 changed files with 161 additions and 2 deletions

View File

@ -172,6 +172,7 @@ struct _qemuDomainObjPrivate {
typedef enum {
QEMU_PROCESS_EVENT_WATCHDOG = 0,
QEMU_PROCESS_EVENT_GUESTPANIC,
QEMU_PROCESS_EVENT_LAST
} qemuProcessEventType;

View File

@ -139,6 +139,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);
@ -3629,18 +3633,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;
}

View File

@ -114,7 +114,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,
@ -1033,6 +1033,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;
@ -3186,6 +3195,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:
;

View File

@ -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;

View File

@ -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;

View File

@ -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,
@ -1284,6 +1288,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,
@ -1303,6 +1341,7 @@ static qemuMonitorCallbacks monitorCallbacks = {
.domainPMSuspend = qemuProcessHandlePMSuspend,
.domainBalloonChange = qemuProcessHandleBalloonChange,
.domainPMSuspendDisk = qemuProcessHandlePMSuspendDisk,
.domainGuestPanic = qemuProcessHandleGuestPanic,
};
static int
@ -2682,6 +2721,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;