diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 9ad8ffa422..810dac209d 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1348,6 +1348,18 @@ qemuMonitorEmitDeviceDeleted(qemuMonitor *mon, } +void +qemuMonitorEmitDeviceUnplugErr(qemuMonitor *mon, + const char *devPath, + const char *devAlias) +{ + VIR_DEBUG("mon=%p", mon); + + QEMU_MONITOR_CALLBACK(mon, domainDeviceUnplugError, mon->vm, + devPath, devAlias); +} + + void qemuMonitorEmitNicRxFilterChanged(qemuMonitor *mon, const char *devAlias) diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index cd1c1c4291..0dd7b1c4e2 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -294,6 +294,11 @@ typedef void (*qemuMonitorDomainDeviceDeletedCallback)(qemuMonitor *mon, virDomainObj *vm, const char *devAlias, void *opaque); +typedef void (*qemuMonitorDomainDeviceUnplugErrCallback)(qemuMonitor *mon, + virDomainObj *vm, + const char *devPath, + const char *devAlias, + void *opaque); typedef void (*qemuMonitorDomainNicRxFilterChangedCallback)(qemuMonitor *mon, virDomainObj *vm, const char *devAlias, @@ -454,6 +459,7 @@ struct _qemuMonitorCallbacks { qemuMonitorDomainGuestCrashloadedCallback domainGuestCrashloaded; qemuMonitorDomainMemoryFailureCallback domainMemoryFailure; qemuMonitorDomainMemoryDeviceSizeChange domainMemoryDeviceSizeChange; + qemuMonitorDomainDeviceUnplugErrCallback domainDeviceUnplugError; }; qemuMonitor *qemuMonitorOpen(virDomainObj *vm, @@ -542,6 +548,9 @@ void qemuMonitorEmitGuestPanic(qemuMonitor *mon, qemuMonitorEventPanicInfo *info); void qemuMonitorEmitDeviceDeleted(qemuMonitor *mon, const char *devAlias); +void qemuMonitorEmitDeviceUnplugErr(qemuMonitor *mon, + const char *devPath, + const char *devAlias); void qemuMonitorEmitNicRxFilterChanged(qemuMonitor *mon, const char *devAlias); void qemuMonitorEmitSerialChange(qemuMonitor *mon, diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 9186d59ca2..4669b9135d 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -113,6 +113,7 @@ static void qemuMonitorJSONHandlePRManagerStatusChanged(qemuMonitor *mon, virJSO static void qemuMonitorJSONHandleRdmaGidStatusChanged(qemuMonitor *mon, virJSONValue *data); static void qemuMonitorJSONHandleMemoryFailure(qemuMonitor *mon, virJSONValue *data); static void qemuMonitorJSONHandleMemoryDeviceSizeChange(qemuMonitor *mon, virJSONValue *data); +static void qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data); typedef struct { const char *type; @@ -129,6 +130,7 @@ static qemuEventHandler eventHandlers[] = { { "BLOCK_WRITE_THRESHOLD", qemuMonitorJSONHandleBlockThreshold, }, { "DEVICE_DELETED", qemuMonitorJSONHandleDeviceDeleted, }, { "DEVICE_TRAY_MOVED", qemuMonitorJSONHandleTrayChange, }, + { "DEVICE_UNPLUG_GUEST_ERROR", qemuMonitorJSONHandleDeviceUnplugErr, }, { "DUMP_COMPLETED", qemuMonitorJSONHandleDumpCompleted, }, { "GUEST_CRASHLOADED", qemuMonitorJSONHandleGuestCrashloaded, }, { "GUEST_PANICKED", qemuMonitorJSONHandleGuestPanic, }, @@ -1111,6 +1113,23 @@ qemuMonitorJSONHandleDeviceDeleted(qemuMonitor *mon, virJSONValue *data) } +static void +qemuMonitorJSONHandleDeviceUnplugErr(qemuMonitor *mon, virJSONValue *data) +{ + const char *device; + const char *path; + + if (!(path = virJSONValueObjectGetString(data, "path"))) { + VIR_DEBUG("missing path in device unplug guest error event"); + return; + } + + device = virJSONValueObjectGetString(data, "device"); + + qemuMonitorEmitDeviceUnplugErr(mon, path, device); +} + + static void qemuMonitorJSONHandleNicRxFilterChanged(qemuMonitor *mon, virJSONValue *data) { diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index ec5e6999f5..b6c81dd23a 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -1322,6 +1322,42 @@ qemuProcessHandleDeviceDeleted(qemuMonitor *mon G_GNUC_UNUSED, } +static void +qemuProcessHandleDeviceUnplugErr(qemuMonitor *mon G_GNUC_UNUSED, + virDomainObj *vm, + const char *devPath, + const char *devAlias, + void *opaque) +{ + virQEMUDriver *driver = opaque; + virObjectEvent *event = NULL; + + virObjectLock(vm); + + VIR_DEBUG("Device %s QOM path %s failed to be removed from domain %p %s", + devAlias, devPath, vm, vm->def->name); + + /* + * DEVICE_UNPLUG_GUEST_ERROR will always contain the QOM path + * but QEMU will not guarantee that devAlias will be provided. + * + * However, given that all Libvirt devices have a devAlias, we + * can ignore the case where QEMU emitted this event without it. + */ + if (!devAlias) + goto cleanup; + + qemuDomainSignalDeviceRemoval(vm, devAlias, + QEMU_DOMAIN_UNPLUGGING_DEVICE_STATUS_GUEST_REJECTED); + + event = virDomainEventDeviceRemovalFailedNewFromObj(vm, devAlias); + + cleanup: + virObjectUnlock(vm); + virObjectEventStateQueue(driver->domainEventState, event); +} + + /** * * Meaning of fields reported by the event according to the ACPI standard: @@ -1891,6 +1927,7 @@ static qemuMonitorCallbacks monitorCallbacks = { .domainGuestCrashloaded = qemuProcessHandleGuestCrashloaded, .domainMemoryFailure = qemuProcessHandleMemoryFailure, .domainMemoryDeviceSizeChange = qemuProcessHandleMemoryDeviceSizeChange, + .domainDeviceUnplugError = qemuProcessHandleDeviceUnplugErr, }; static void