[libvirt] [PATCH] Automatically pause QEMU guests when an error occurs

Daniel P. Berrange berrange at redhat.com
Tue Feb 16 17:04:04 UTC 2010


With the QMP mode monitor, it is possible to get a notification
that a disk I/O error occurs ina guest. This patch enables such
reporting and when receiving an error updates libvirt's view
of the guest to indicate that it is now paused. It also emits
an event

  VIR_DOMAIN_EVENT_SUSPENDED

with a detail of:

  VIR_DOMAIN_EVENT_SUSPENDED_IOERROR

* include/libvirt/libvirt.h.in: Add VIR_DOMAIN_EVENT_SUSPENDED_IOERROR
* src/qemu/qemu_driver.c: Update VM state to paused when IO error
  occurrs
* src/qemu/qemu_monitor.c, src/qemu/qemu_monitor.h,
  src/qemu/qemu_monitor_json.c: Wire up handlers for disk IO errors
---
 include/libvirt/libvirt.h.in |    1 +
 src/qemu/qemu_conf.c         |    7 +++++--
 src/qemu/qemu_conf.h         |    2 +-
 src/qemu/qemu_driver.c       |   38 +++++++++++++++++++++++++++++++++++---
 src/qemu/qemu_monitor.c      |   16 ++++++++++++++++
 src/qemu/qemu_monitor.h      |    5 +++++
 src/qemu/qemu_monitor_json.c |    6 ++++++
 7 files changed, 69 insertions(+), 6 deletions(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index 260505e..b7a6922 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1361,6 +1361,7 @@ typedef enum {
 typedef enum {
     VIR_DOMAIN_EVENT_SUSPENDED_PAUSED = 0,   /* Normal suspend due to admin pause */
     VIR_DOMAIN_EVENT_SUSPENDED_MIGRATED = 1, /* Suspended for offline migration */
+    VIR_DOMAIN_EVENT_SUSPENDED_IOERROR = 2,  /* Suspended due to a disk I/O error */
 } virDomainEventSuspendedDetailType;
 
 /**
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index c9fe55b..20076bc 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -2385,6 +2385,9 @@ qemuBuildDriveStr(virDomainDiskDefPtr disk,
     } else if (disk->shared && !disk->readonly) {
         virBufferAddLit(&opt, ",cache=off");
     }
+    if (qemuCmdFlags & QEMUD_CMD_FLAG_MONITOR_JSON) {
+        virBufferVSprintf(&opt, ",werror=stop,rerror=stop");
+    }
 
     if (virBufferError(&opt)) {
         virReportOOMError();
@@ -2400,7 +2403,7 @@ error:
 
 
 char *
-qemuBuildDriveDevStr(virDomainDiskDefPtr disk)
+qemuBuildDriveDevStr(virDomainDiskDefPtr disk, int qemuCmdFlags ATTRIBUTE_UNUSED)
 {
     virBuffer opt = VIR_BUFFER_INITIALIZER;
     const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
@@ -3578,7 +3581,7 @@ int qemudBuildCommandLine(virConnectPtr conn,
                 } else {
                     ADD_ARG_LIT("-device");
 
-                    if (!(optstr = qemuBuildDriveDevStr(disk)))
+                    if (!(optstr = qemuBuildDriveDevStr(disk, qemuCmdFlags)))
                         goto error;
                     ADD_ARG(optstr);
                 }
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index 7041489..ec8033a 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -214,7 +214,7 @@ char *qemuBuildDriveStr(virDomainDiskDefPtr disk,
                         unsigned long long qemuCmdFlags);
 
 /* Current, best practice */
-char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk);
+char * qemuBuildDriveDevStr(virDomainDiskDefPtr disk, int qemuCmdFlags);
 /* Current, best practice */
 char * qemuBuildControllerDevStr(virDomainControllerDefPtr def);
 
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 77306f3..23073bc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -824,9 +824,41 @@ cleanup:
     return ret;
 }
 
+
+static int
+qemuHandleDiskIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED,
+                      virDomainObjPtr vm,
+                      const char *diskalias ATTRIBUTE_UNUSED)
+{
+    struct qemud_driver *driver = qemu_driver;
+    virDomainEventPtr event = NULL;
+
+    VIR_DEBUG("Received IO error on %p '%s': %s", vm, vm->def->name, diskalias);
+    virDomainObjLock(vm);
+
+    vm->state = VIR_DOMAIN_PAUSED;
+    event = virDomainEventNewFromObj(vm,
+                                     VIR_DOMAIN_EVENT_SUSPENDED,
+                                     VIR_DOMAIN_EVENT_SUSPENDED_IOERROR);
+
+    if (virDomainSaveStatus(driver->caps, driver->stateDir, vm) < 0)
+        VIR_WARN("Unable to save status on vm %s after IO error", vm->def->name);
+
+    virDomainObjUnlock(vm);
+
+    if (event) {
+        qemuDriverLock(driver);
+        qemuDomainEventQueue(driver, event);
+        qemuDriverUnlock(driver);
+    }
+    return 0;
+}
+
+
 static qemuMonitorCallbacks monitorCallbacks = {
     .eofNotify = qemuHandleMonitorEOF,
     .diskSecretLookup = findVolumeQcowPassphrase,
+    .diskIOError = qemuHandleDiskIOError,
 };
 
 static int
@@ -5353,7 +5385,7 @@ static int qemudDomainAttachPciDiskDevice(struct qemud_driver *driver,
         if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
             goto error;
 
-        if (!(devstr = qemuBuildDriveDevStr(disk)))
+        if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags)))
             goto error;
     }
 
@@ -5548,7 +5580,7 @@ static int qemudDomainAttachSCSIDisk(struct qemud_driver *driver,
     if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) {
         if (qemuAssignDeviceDiskAlias(disk, qemuCmdFlags) < 0)
             goto error;
-        if (!(devstr = qemuBuildDriveDevStr(disk)))
+        if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags)))
             goto error;
     }
 
@@ -5652,7 +5684,7 @@ static int qemudDomainAttachUsbMassstorageDevice(struct qemud_driver *driver,
             goto error;
         if (!(drivestr = qemuBuildDriveStr(disk, 0, qemuCmdFlags)))
             goto error;
-        if (!(devstr = qemuBuildDriveDevStr(disk)))
+        if (!(devstr = qemuBuildDriveDevStr(disk, qemuCmdFlags)))
             goto error;
     }
 
diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c
index b88532c..3f8772a 100644
--- a/src/qemu/qemu_monitor.c
+++ b/src/qemu/qemu_monitor.c
@@ -791,6 +791,22 @@ int qemuMonitorEmitStop(qemuMonitorPtr mon)
 }
 
 
+int qemuMonitorEmitDiskIOError(qemuMonitorPtr mon, const char *deviceAlias)
+{
+    int ret = -1;
+    VIR_DEBUG("mon=%p deviceAlias=%s", mon, deviceAlias);
+
+    qemuMonitorRef(mon);
+    qemuMonitorUnlock(mon);
+    if (mon->cb && mon->cb->diskIOError)
+        ret = mon->cb->diskIOError(mon, mon->vm, deviceAlias);
+    qemuMonitorLock(mon);
+    qemuMonitorUnref(mon);
+
+    return ret;
+}
+
+
 int qemuMonitorSetCapabilities(qemuMonitorPtr mon)
 {
     int ret;
diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h
index 0ac3957..6b9fd50 100644
--- a/src/qemu/qemu_monitor.h
+++ b/src/qemu/qemu_monitor.h
@@ -86,6 +86,10 @@ struct _qemuMonitorCallbacks {
                            virDomainObjPtr vm);
     int (*domainStop)(qemuMonitorPtr mon,
                       virDomainObjPtr vm);
+
+    int (*diskIOError)(qemuMonitorPtr mon,
+                       virDomainObjPtr vm,
+                       const char *diskAlias);
 };
 
 
@@ -122,6 +126,7 @@ int qemuMonitorEmitShutdown(qemuMonitorPtr mon);
 int qemuMonitorEmitReset(qemuMonitorPtr mon);
 int qemuMonitorEmitPowerdown(qemuMonitorPtr mon);
 int qemuMonitorEmitStop(qemuMonitorPtr mon);
+int qemuMonitorEmitDiskIOError(qemuMonitorPtr mon, const char *deviceAlias);
 
 int qemuMonitorStartCPUs(qemuMonitorPtr mon,
                          virConnectPtr conn);
diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c
index 49e0370..c20d063 100644
--- a/src/qemu/qemu_monitor_json.c
+++ b/src/qemu/qemu_monitor_json.c
@@ -49,6 +49,7 @@ static void qemuMonitorJSONHandleShutdown(qemuMonitorPtr mon, virJSONValuePtr da
 static void qemuMonitorJSONHandleReset(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandlePowerdown(qemuMonitorPtr mon, virJSONValuePtr data);
 static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data);
+static void qemuMonitorJSONHandleDiskIOError(qemuMonitorPtr mon, virJSONValuePtr data);
 
 struct {
     const char *type;
@@ -58,6 +59,7 @@ struct {
     { "RESET", qemuMonitorJSONHandleReset, },
     { "POWERDOWN", qemuMonitorJSONHandlePowerdown, },
     { "STOP", qemuMonitorJSONHandleStop, },
+    { "BLOCK_IO_ERROR", qemuMonitorJSONHandleDiskIOError, },
 };
 
 
@@ -495,6 +497,10 @@ static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data A
     qemuMonitorEmitStop(mon);
 }
 
+static void qemuMonitorJSONHandleDiskIOError(qemuMonitorPtr mon, virJSONValuePtr data ATTRIBUTE_UNUSED)
+{
+    qemuMonitorEmitDiskIOError(mon, NULL);
+}
 
 int
 qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon)
-- 
1.6.2.5




More information about the libvir-list mailing list