[libvirt] [PATCH v6 4/5] qemu: Implement 'oncrash' events when guest panicked

Chen Fan chen.fan.fnst at cn.fujitsu.com
Fri Jun 7 10:23:34 UTC 2013


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




More information about the libvir-list mailing list