Report any stored I/O error messages reported by the hypervisor when reporting messages of a domain. As the I/O error may be already stale we report also the timestamp when it was recorded. Example message: I/O error: disk='vda', index='1', path='/dev/mapper/errdev0', timestamp='2025-01-28 15:47:52.776+0000', message'Input/output error' Signed-off-by: Peter Krempa <pkrempa@xxxxxxxxxx> --- include/libvirt/libvirt-domain.h | 3 ++ src/conf/domain_conf.c | 50 ++++++++++++++++++++++++++++++++ src/conf/domain_conf.h | 5 ++++ src/libvirt_private.syms | 1 + src/libxl/libxl_driver.c | 3 +- src/qemu/qemu_driver.c | 15 +++++++++- src/test/test_driver.c | 3 +- 7 files changed, 77 insertions(+), 3 deletions(-) diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h index 74016c6c46..9e9016cfe7 100644 --- a/include/libvirt/libvirt-domain.h +++ b/include/libvirt/libvirt-domain.h @@ -6520,6 +6520,9 @@ int virDomainAuthorizedSSHKeysSet(virDomainPtr domain, typedef enum { VIR_DOMAIN_MESSAGE_DEPRECATION = (1 << 0), /* (Since: 7.1.0) */ VIR_DOMAIN_MESSAGE_TAINTING = (1 << 1), /* (Since: 7.1.0) */ + VIR_DOMAIN_MESSAGE_IOERRORS = (1 << 2), /* Report available stored I/O + errors messages for disk images + (Since: 11.1.0) */ } virDomainMessageType; int virDomainGetMessages(virDomainPtr domain, diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 00d486e774..548bc82308 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -31682,6 +31682,47 @@ virHostdevIsPCIDevice(const virDomainHostdevDef *hostdev) } +static void +virDomainObjGetMessagesIOErrorsSrc(virStorageSource *src, + const char *diskdst, + GPtrArray *m) +{ + if (!src || + !src->ioerror_message) + return; + + g_ptr_array_add(m, g_strdup_printf(_("I/O error: disk='%1$s', index='%2$d', path='%3$s', timestamp='%4$s', message'%5$s'"), + NULLSTR_MINUS(diskdst), + src->id, + NULLSTR_MINUS(src->path), + src->ioerror_timestamp, + src->ioerror_message)); +} + + +void +virDomainObjGetMessagesIOErrorsChain(virStorageSource *src, + const char *diskdst, + GPtrArray *m) +{ + virStorageSource *n; + + for (n = src; n; n = n->backingStore) { + virDomainObjGetMessagesIOErrorsSrc(n, diskdst, m); + virDomainObjGetMessagesIOErrorsSrc(n->dataFileStore, diskdst, m); + } +} + + +static void +virDomainObjGetMessagesIOErrorsDisk(virDomainDiskDef *disk, + GPtrArray *m) +{ + virDomainObjGetMessagesIOErrorsChain(disk->src, disk->dst, m); + virDomainObjGetMessagesIOErrorsChain(disk->mirror, disk->dst, m); +} + + /** * virDomainObjGetMessages: * @vm: domain object @@ -31710,6 +31751,15 @@ virDomainObjGetMessages(virDomainObj *vm, vm->deprecations[i])); } } + + if (!flags || (flags & VIR_DOMAIN_MESSAGE_IOERRORS)) { + if (vm->def->os.loader) + virDomainObjGetMessagesIOErrorsChain(vm->def->os.loader->nvram, NULL, m); + + for (i = 0; i < vm->def->ndisks; i++) + virDomainObjGetMessagesIOErrorsDisk(vm->def->disks[i], m); + } + } bool diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index e996d3c0de..e51c74b6d1 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -4588,6 +4588,11 @@ bool virHostdevIsPCIDevice(const virDomainHostdevDef *hostdev) ATTRIBUTE_NONNULL(1); +void +virDomainObjGetMessagesIOErrorsChain(virStorageSource *src, + const char *diskdst, + GPtrArray *m); + void virDomainObjGetMessages(virDomainObj *vm, GPtrArray *m, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2fe0a07944..406e6583a3 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -592,6 +592,7 @@ virDomainObjEndAPI; virDomainObjFormat; virDomainObjGetDefs; virDomainObjGetMessages; +virDomainObjGetMessagesIOErrorsChain; virDomainObjGetMetadata; virDomainObjGetOneDef; virDomainObjGetOneDefState; diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c index 426c2b4278..a76545c9ff 100644 --- a/src/libxl/libxl_driver.c +++ b/src/libxl/libxl_driver.c @@ -6580,7 +6580,8 @@ libxlDomainGetMessages(virDomainPtr dom, int ret = -1; virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION | - VIR_DOMAIN_MESSAGE_TAINTING, -1); + VIR_DOMAIN_MESSAGE_TAINTING | + VIR_DOMAIN_MESSAGE_IOERRORS, -1); if (!(vm = libxlDomObjFromDomain(dom))) return -1; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8327e7079c..50733a5b3a 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -19821,9 +19821,11 @@ qemuDomainGetMessages(virDomainPtr dom, g_autoptr(GPtrArray) m = g_ptr_array_new_with_free_func(g_free); virDomainObj *vm = NULL; int rv = -1; + qemuDomainObjPrivate *priv; virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION | - VIR_DOMAIN_MESSAGE_TAINTING, -1); + VIR_DOMAIN_MESSAGE_TAINTING | + VIR_DOMAIN_MESSAGE_IOERRORS, -1); if (!(vm = qemuDomainObjFromDomain(dom))) return -1; @@ -19831,8 +19833,19 @@ qemuDomainGetMessages(virDomainPtr dom, if (virDomainGetMessagesEnsureACL(dom->conn, vm->def) < 0) goto cleanup; + priv = vm->privateData; + virDomainObjGetMessages(vm, m, flags); + if (priv->backup) { + size_t i; + + for (i = 0; i < priv->backup->ndisks; i++) + virDomainObjGetMessagesIOErrorsChain(priv->backup->disks[i].store, + priv->backup->disks[i].name, + m); + } + rv = m->len; if (m->len > 0) { g_ptr_array_add(m, NULL); diff --git a/src/test/test_driver.c b/src/test/test_driver.c index a10ec3bc41..6f18b2b2c8 100644 --- a/src/test/test_driver.c +++ b/src/test/test_driver.c @@ -9527,7 +9527,8 @@ testDomainGetMessages(virDomainPtr dom, int rv = -1; virCheckFlags(VIR_DOMAIN_MESSAGE_DEPRECATION | - VIR_DOMAIN_MESSAGE_TAINTING, -1); + VIR_DOMAIN_MESSAGE_TAINTING | + VIR_DOMAIN_MESSAGE_IOERRORS, -1); if (!(vm = testDomObjFromDomain(dom))) return -1; -- 2.48.1