]> xenbits.xensource.com Git - libvirt.git/commitdiff
qemu: Create multipath targets for PRs
authorMichal Privoznik <mprivozn@redhat.com>
Wed, 13 Nov 2019 14:34:50 +0000 (15:34 +0100)
committerMichal Privoznik <mprivozn@redhat.com>
Thu, 12 Mar 2020 07:04:40 +0000 (08:04 +0100)
If a disk has persistent reservations enabled, qemu-pr-helper
might open not only /dev/mapper/control but also individual
targets of the multipath device. We are already querying for them
in CGroups, but now we have to create them in the namespace too.
This was brought up in [1].

1: https://bugzilla.redhat.com/show_bug.cgi?id=1711045#c61

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Tested-by: Lin Ma <LMa@suse.com>
Reviewed-by: Jim Fehlig <jfehlig@suse.com>
src/qemu/qemu_domain.c
src/util/virdevmapper.h
src/util/virutil.h
tests/qemuhotplugmock.c
tests/qemuhotplugtest.c
tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml [new file with mode: 0644]
tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml [new file with mode: 0644]

index bcdd518be684cacb9a0371cefddb560b3e2ddab8..4b467afa8134d903bdf07f496cd72a748dea21ec 100644 (file)
@@ -62,6 +62,7 @@
 #include "virdomaincheckpointobjlist.h"
 #include "backup_conf.h"
 #include "virutil.h"
+#include "virdevmapper.h"
 
 #ifdef __linux__
 # include <sys/sysmacros.h>
@@ -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;
 }
 
 
index e576d2bf7e0afcc590bd4f3f0fdf675cee6af901..87bbc63cfd1afd260d4b4e4f2342c5d15ebdd4f2 100644 (file)
@@ -20,6 +20,8 @@
 
 #pragma once
 
+#include "internal.h"
+
 int
 virDevMapperGetTargets(const char *path,
-                       char ***devPaths);
+                       char ***devPaths) G_GNUC_NO_INLINE;
index cc6b82a1772c4429766719ca654687f207f4b900..ee23f0c1f407a9382ca4ff31c842e57128159e8e 100644 (file)
@@ -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);
index 43a9d7905157025496cfb56f9aa4d8b8e3cd3e44..8e5b07788ddc038571817d19ca7aecf2974305c4 100644 (file)
 #include <config.h>
 
 #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)
+{
+}
index e008c1bf0d49e349ecfbad90088d7cc1e903dfcd..8b411d63f0d1ae48df3e60fcad45753aacf5f9f8 100644 (file)
@@ -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 (file)
index 0000000..5a6f955
--- /dev/null
@@ -0,0 +1,8 @@
+<disk type='block' device='lun'>
+    <driver name='qemu' type='raw'/>
+    <source dev='/dev/mapper/virt'>
+        <reservations managed='yes'/>
+    </source>
+    <target dev='sda' bus='scsi'/>
+  <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+</disk>
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 (file)
index 0000000..40af064
--- /dev/null
@@ -0,0 +1,62 @@
+<domain type='kvm' id='7'>
+  <name>hotplug</name>
+  <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
+  <memory unit='KiB'>4194304</memory>
+  <currentMemory unit='KiB'>4194304</currentMemory>
+  <vcpu placement='static'>4</vcpu>
+  <os>
+    <type arch='x86_64' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <pae/>
+  </features>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>restart</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='block' device='lun'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/virt'>
+        <reservations managed='yes'>
+          <source type='unix' path='/tmp/lib/domain-7-hotplug/pr-helper0.sock' mode='client'/>
+        </reservations>
+      </source>
+      <backingStore/>
+      <target dev='sda' bus='scsi'/>
+      <alias name='scsi0-0-0-0'/>
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
+    </disk>
+    <controller type='usb' index='0'>
+      <alias name='usb'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
+    </controller>
+    <controller type='ide' index='0'>
+      <alias name='ide'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
+    </controller>
+    <controller type='scsi' index='0' model='virtio-scsi'>
+      <alias name='scsi0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
+    </controller>
+    <controller type='pci' index='0' model='pci-root'>
+      <alias name='pci'/>
+    </controller>
+    <controller type='virtio-serial' index='0'>
+      <alias name='virtio-serial0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
+    </controller>
+    <input type='mouse' bus='ps2'>
+      <alias name='input0'/>
+    </input>
+    <input type='keyboard' bus='ps2'>
+      <alias name='input1'/>
+    </input>
+    <memballoon model='none'/>
+  </devices>
+  <seclabel type='none' model='none'/>
+</domain>