diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index bcdd518be6..4b467afa81 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -62,6 +62,7 @@ #include "virdomaincheckpointobjlist.h" #include "backup_conf.h" #include "virutil.h" +#include "virdevmapper.h" #ifdef __linux__ # include @@ -14623,6 +14624,9 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED, bool hasNVMe = false; for (next = disk->src; virStorageSourceIsBacking(next); next = next->backingStore) { + VIR_AUTOSTRINGLIST targetPaths = NULL; + size_t i; + if (next->type == VIR_STORAGE_TYPE_NVME) { g_autofree char *nvmePath = NULL; @@ -14641,6 +14645,19 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED, if (qemuDomainCreateDevice(next->path, data, false) < 0) return -1; + + if (virDevMapperGetTargets(next->path, &targetPaths) < 0 && + errno != ENOSYS && errno != EBADF) { + virReportSystemError(errno, + _("Unable to get devmapper targets for %s"), + next->path); + return -1; + } + + for (i = 0; targetPaths && targetPaths[i]; i++) { + if (qemuDomainCreateDevice(targetPaths[i], data, false) < 0) + return -1; + } } } @@ -15656,21 +15673,19 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, virStorageSourcePtr src) { virStorageSourcePtr next; - char **paths = NULL; + VIR_AUTOSTRINGLIST paths = NULL; size_t npaths = 0; bool hasNVMe = false; - g_autofree char *dmPath = NULL; - g_autofree char *vfioPath = NULL; - int ret = -1; for (next = src; virStorageSourceIsBacking(next); next = next->backingStore) { + VIR_AUTOSTRINGLIST targetPaths = NULL; g_autofree char *tmpPath = NULL; if (next->type == VIR_STORAGE_TYPE_NVME) { hasNVMe = true; if (!(tmpPath = virPCIDeviceAddressGetIOMMUGroupDev(&next->nvme->pciAddr))) - goto cleanup; + return -1; } else { if (virStorageSourceIsEmpty(next) || !virStorageSourceIsLocalStorage(next)) { @@ -15681,30 +15696,35 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm, tmpPath = g_strdup(next->path); } - if (VIR_APPEND_ELEMENT(paths, npaths, tmpPath) < 0) - goto cleanup; + if (virStringListAdd(&paths, tmpPath) < 0) + return -1; + + if (virDevMapperGetTargets(next->path, &targetPaths) < 0 && + errno != ENOSYS && errno != EBADF) { + virReportSystemError(errno, + _("Unable to get devmapper targets for %s"), + next->path); + return -1; + } + + if (virStringListMerge(&paths, &targetPaths) < 0) + return -1; } /* qemu-pr-helper might require access to /dev/mapper/control. */ - if (src->pr) { - dmPath = g_strdup(QEMU_DEVICE_MAPPER_CONTROL_PATH); - if (VIR_APPEND_ELEMENT_COPY(paths, npaths, dmPath) < 0) - goto cleanup; - } + if (src->pr && + virStringListAdd(&paths, QEMU_DEVICE_MAPPER_CONTROL_PATH) < 0) + return -1; - if (hasNVMe) { - vfioPath = g_strdup(QEMU_DEV_VFIO); - if (VIR_APPEND_ELEMENT(paths, npaths, vfioPath) < 0) - goto cleanup; - } + if (hasNVMe && + virStringListAdd(&paths, QEMU_DEV_VFIO) < 0) + return -1; + npaths = virStringListLength((const char **) paths); if (qemuDomainNamespaceMknodPaths(vm, (const char **) paths, npaths) < 0) - goto cleanup; + return -1; - ret = 0; - cleanup: - virStringListFreeCount(paths, npaths); - return ret; + return 0; } diff --git a/src/util/virdevmapper.h b/src/util/virdevmapper.h index e576d2bf7e..87bbc63cfd 100644 --- a/src/util/virdevmapper.h +++ b/src/util/virdevmapper.h @@ -20,6 +20,8 @@ #pragma once +#include "internal.h" + int virDevMapperGetTargets(const char *path, - char ***devPaths); + char ***devPaths) G_GNUC_NO_INLINE; diff --git a/src/util/virutil.h b/src/util/virutil.h index cc6b82a177..ee23f0c1f4 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -120,7 +120,7 @@ bool virValidateWWN(const char *wwn); int virGetDeviceID(const char *path, int *maj, - int *min); + int *min) G_GNUC_NO_INLINE; int virSetDeviceUnprivSGIO(const char *path, const char *sysfs_dir, int unpriv_sgio); diff --git a/tests/qemuhotplugmock.c b/tests/qemuhotplugmock.c index 43a9d79051..8e5b07788d 100644 --- a/tests/qemuhotplugmock.c +++ b/tests/qemuhotplugmock.c @@ -19,7 +19,24 @@ #include #include "qemu/qemu_hotplug.h" +#include "qemu/qemu_process.h" #include "conf/domain_conf.h" +#include "virdevmapper.h" +#include "virutil.h" +#include "virmock.h" + +static int (*real_virGetDeviceID)(const char *path, int *maj, int *min); +static bool (*real_virFileExists)(const char *path); + +static void +init_syms(void) +{ + if (real_virFileExists) + return; + + VIR_MOCK_REAL_INIT(virGetDeviceID); + VIR_MOCK_REAL_INIT(virFileExists); +} unsigned long long qemuDomainGetUnplugTimeout(virDomainObjPtr vm G_GNUC_UNUSED) @@ -31,3 +48,61 @@ qemuDomainGetUnplugTimeout(virDomainObjPtr vm G_GNUC_UNUSED) return 200; return 100; } + + +int +virDevMapperGetTargets(const char *path, + char ***devPaths) +{ + *devPaths = NULL; + + if (STREQ(path, "/dev/mapper/virt")) { + *devPaths = g_new(char *, 4); + (*devPaths)[0] = g_strdup("/dev/block/8:0"); /* /dev/sda */ + (*devPaths)[1] = g_strdup("/dev/block/8:16"); /* /dev/sdb */ + (*devPaths)[2] = g_strdup("/dev/block/8:32"); /* /dev/sdc */ + (*devPaths)[3] = NULL; + } + + return 0; +} + + +int +virGetDeviceID(const char *path, int *maj, int *min) +{ + init_syms(); + + if (STREQ(path, "/dev/mapper/virt")) { + *maj = 254; + *min = 0; + return 0; + } + + return real_virGetDeviceID(path, maj, min); +} + + +bool +virFileExists(const char *path) +{ + init_syms(); + + if (STREQ(path, "/dev/mapper/virt")) + return true; + + return real_virFileExists(path); +} + + +int +qemuProcessStartManagedPRDaemon(virDomainObjPtr vm G_GNUC_UNUSED) +{ + return 0; +} + + +void +qemuProcessKillManagedPRDaemon(virDomainObjPtr vm G_GNUC_UNUSED) +{ +} diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c index e008c1bf0d..8b411d63f0 100644 --- a/tests/qemuhotplugtest.c +++ b/tests/qemuhotplugtest.c @@ -87,6 +87,8 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt, virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VNC); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE); virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE); + virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER); + virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK); if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0) return -1; @@ -750,6 +752,17 @@ mymain(void) "device_del", QMP_DEVICE_DELETED("scsi3-0-5-6") QMP_OK, "human-monitor-command", HMP("")); + DO_TEST_ATTACH("base-live", "disk-scsi-multipath", false, true, + "object-add", QMP_OK, + "human-monitor-command", HMP("OK\\r\\n"), + "device_add", QMP_OK); + DO_TEST_DETACH("base-live", "disk-scsi-multipath", true, true, + "device_del", QMP_OK, + "human-monitor-command", HMP("")); + DO_TEST_DETACH("base-live", "disk-scsi-multipath", false, false, + "device_del", QMP_DEVICE_DELETED("scsi0-0-0-0") QMP_OK, + "human-monitor-command", HMP("")); + DO_TEST_ATTACH("base-live", "qemu-agent", false, true, "chardev-add", QMP_OK, "device_add", QMP_OK); diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml b/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml new file mode 100644 index 0000000000..5a6f955284 --- /dev/null +++ b/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml @@ -0,0 +1,8 @@ + + + + + + +
+ diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml new file mode 100644 index 0000000000..40af064d10 --- /dev/null +++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml @@ -0,0 +1,62 @@ + + hotplug + d091ea82-29e6-2e34-3005-f02617b36e87 + 4194304 + 4194304 + 4 + + hvm + + + + + + + + + destroy + restart + restart + + /usr/bin/qemu-system-x86_64 + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + + + + + +
+ + + + + + + + + + +