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

Daniel Veillard veillard at redhat.com
Wed Feb 17 21:32:24 UTC 2010


On Tue, Feb 16, 2010 at 05:04:04PM +0000, Daniel P. Berrange wrote:
> 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
> 

  ACK, I think that adding this unconditionally if supported is the
right thing to do.

Daniel

-- 
Daniel Veillard      | libxml Gnome XML XSLT toolkit  http://xmlsoft.org/
daniel at veillard.com  | Rpmfind RPM search engine http://rpmfind.net/
http://veillard.com/ | virtualization library  http://libvirt.org/




More information about the libvir-list mailing list