With the QMP mode monitor, it is possible to get a notification when the guest is paused indirectly (eg as result of a disk IO error). 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 NB this patch does not make the error behaviour configurable. It just copes with the pause transition, if QEMU is setup to do that by default (current upstream now works this way for disks). * 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_driver.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor.c | 18 ++++++++++++++++++ src/qemu/qemu_monitor.h | 16 ++++++++++++++++ src/qemu/qemu_monitor_json.c | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 0 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_driver.c b/src/qemu/qemu_driver.c index 8766ca2..ae19097 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -824,9 +824,51 @@ cleanup: return ret; } + +static int +qemuHandleDiskIOError(qemuMonitorPtr mon ATTRIBUTE_UNUSED, + virDomainObjPtr vm, + int action, + const char *diskalias ATTRIBUTE_UNUSED) +{ + struct qemud_driver *driver = qemu_driver; + virDomainEventPtr event = NULL; + + VIR_DEBUG("Received IO error on %p '%s': action=%d disk=%s", vm, vm->def->name, action, diskalias); + + if (action != QEMU_MONITOR_DISK_IO_ERROR_STOP) + return 0; + + virDomainObjLock(vm); + + if (action == QEMU_MONITOR_DISK_IO_ERROR_STOP && + vm->state == VIR_DOMAIN_RUNNING) { + VIR_DEBUG("Transitioned guest %s to paused state", vm->def->name); + + 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 diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b88532c..7205001 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -791,6 +791,24 @@ int qemuMonitorEmitStop(qemuMonitorPtr mon) } +int qemuMonitorEmitDiskIOError(qemuMonitorPtr mon, + int action, + const char *deviceAlias) +{ + int ret = -1; + VIR_DEBUG("mon=%p action=%d deviceAlias=%s", mon, action, deviceAlias); + + qemuMonitorRef(mon); + qemuMonitorUnlock(mon); + if (mon->cb && mon->cb->diskIOError) + ret = mon->cb->diskIOError(mon, mon->vm, action, 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..75a2853 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -60,6 +60,14 @@ struct _qemuMonitorMessage { void *passwordOpaque; }; +typedef enum { + QEMU_MONITOR_DISK_IO_ERROR_STOP, + QEMU_MONITOR_DISK_IO_ERROR_REPORT, + QEMU_MONITOR_DISK_IO_ERROR_IGNORE, + + QEMU_MONITOR_DISK_IO_ERROR_LAST +} qemuMonitorDiskIOErrorAction; + typedef struct _qemuMonitorCallbacks qemuMonitorCallbacks; typedef qemuMonitorCallbacks *qemuMonitorCallbacksPtr; struct _qemuMonitorCallbacks { @@ -86,6 +94,11 @@ struct _qemuMonitorCallbacks { virDomainObjPtr vm); int (*domainStop)(qemuMonitorPtr mon, virDomainObjPtr vm); + + int (*diskIOError)(qemuMonitorPtr mon, + virDomainObjPtr vm, + int actOBion, + const char *diskAlias); }; @@ -122,6 +135,9 @@ int qemuMonitorEmitShutdown(qemuMonitorPtr mon); int qemuMonitorEmitReset(qemuMonitorPtr mon); int qemuMonitorEmitPowerdown(qemuMonitorPtr mon); int qemuMonitorEmitStop(qemuMonitorPtr mon); +int qemuMonitorEmitDiskIOError(qemuMonitorPtr mon, + int action, + 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 7b45594..8b3cda1 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, }, }; @@ -496,6 +498,38 @@ static void qemuMonitorJSONHandleStop(qemuMonitorPtr mon, virJSONValuePtr data A } +VIR_ENUM_DECL(qemuMonitorDiskIOErrorAction) +VIR_ENUM_IMPL(qemuMonitorDiskIOErrorAction, QEMU_MONITOR_DISK_IO_ERROR_LAST, + "stop", "report", "ignore"); + + +static void qemuMonitorJSONHandleDiskIOError(qemuMonitorPtr mon, virJSONValuePtr data) +{ + const char *device; + const char *action; + int actionID; + + /* Throughout here we try our best to carry on upon errors, + since its imporatant to get as much info as possible out + to the application */ + + if ((action = virJSONValueObjectGetString(data, "action")) == NULL) { + VIR_WARN0("Missing action in disk io error event"); + action = "ignore"; + } + + if ((device = virJSONValueObjectGetString(data, "device")) == NULL) { + VIR_WARN0("missing device in disk io error event"); + } + + if ((actionID = qemuMonitorDiskIOErrorActionTypeFromString(action)) < 0) { + VIR_WARN("unknown disk io error action '%s'", action); + actionID = QEMU_MONITOR_DISK_IO_ERROR_IGNORE; + } + + qemuMonitorEmitDiskIOError(mon, actionID, device); +} + int qemuMonitorJSONSetCapabilities(qemuMonitorPtr mon) { -- 1.6.2.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list