d76c62
From 73c83c67608896704bfeb55b7f973f3e5ee12c96 Mon Sep 17 00:00:00 2001
d76c62
Message-Id: <73c83c67608896704bfeb55b7f973f3e5ee12c96@dist-git>
d76c62
From: Michal Privoznik <mprivozn@redhat.com>
d76c62
Date: Tue, 17 Mar 2020 16:08:02 +0100
d76c62
Subject: [PATCH] qemu: Create multipath targets for PRs
d76c62
d76c62
If a disk has persistent reservations enabled, qemu-pr-helper
d76c62
might open not only /dev/mapper/control but also individual
d76c62
targets of the multipath device. We are already querying for them
d76c62
in CGroups, but now we have to create them in the namespace too.
d76c62
This was brought up in [1].
d76c62
d76c62
1: https://bugzilla.redhat.com/show_bug.cgi?id=1711045#c61
d76c62
d76c62
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
d76c62
Tested-by: Lin Ma <LMa@suse.com>
d76c62
Reviewed-by: Jim Fehlig <jfehlig@suse.com>
d76c62
(cherry picked from commit a30078cb832646177defd256e77c632905f1e6d0)
d76c62
d76c62
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1814157
d76c62
d76c62
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
d76c62
d76c62
Conflicts:
d76c62
src/qemu/qemu_domain.c: There are two conflicts. The first one is
d76c62
in the first hunk because 7e0d11be5b0 is not backported. The
d76c62
second conflict is in the third hunk and it is because
d76c62
db780004a9d and friends which switches the code over to use
d76c62
autofree and drops needless cleanup labels are not backported.
d76c62
d76c62
Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
d76c62
Message-Id: <7a59996bd80955551d37f775e8d682649b1f4a45.1584457667.git.mprivozn@redhat.com>
d76c62
Acked-by: Peter Krempa <pkrempa@redhat.com>
d76c62
---
d76c62
 src/qemu/qemu_domain.c                        | 64 ++++++++++------
d76c62
 src/util/virdevmapper.h                       |  4 +-
d76c62
 src/util/virutil.h                            |  2 +-
d76c62
 tests/qemuhotplugmock.c                       | 75 +++++++++++++++++++
d76c62
 tests/qemuhotplugtest.c                       | 13 ++++
d76c62
 .../qemuhotplug-disk-scsi-multipath.xml       |  8 ++
d76c62
 ...uhotplug-base-live+disk-scsi-multipath.xml | 62 +++++++++++++++
d76c62
 7 files changed, 204 insertions(+), 24 deletions(-)
d76c62
 create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml
d76c62
 create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml
d76c62
d76c62
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
d76c62
index a273aefa6b..36a63449b2 100644
d76c62
--- a/src/qemu/qemu_domain.c
d76c62
+++ b/src/qemu/qemu_domain.c
d76c62
@@ -62,6 +62,7 @@
d76c62
 #include "virdomainsnapshotobjlist.h"
d76c62
 #include "virdomaincheckpointobjlist.h"
d76c62
 #include "backup_conf.h"
d76c62
+#include "virdevmapper.h"
d76c62
 
d76c62
 #ifdef MAJOR_IN_MKDEV
d76c62
 # include <sys/mkdev.h>
d76c62
@@ -14866,6 +14867,9 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED,
d76c62
     int ret = -1;
d76c62
 
d76c62
     for (next = disk->src; virStorageSourceIsBacking(next); next = next->backingStore) {
d76c62
+        VIR_AUTOSTRINGLIST targetPaths = NULL;
d76c62
+        size_t i;
d76c62
+
d76c62
         if (next->type == VIR_STORAGE_TYPE_NVME) {
d76c62
             g_autofree char *nvmePath = NULL;
d76c62
 
d76c62
@@ -14884,6 +14888,19 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED,
d76c62
 
d76c62
             if (qemuDomainCreateDevice(next->path, data, false) < 0)
d76c62
                 goto cleanup;
d76c62
+
d76c62
+            if (virDevMapperGetTargets(next->path, &targetPaths) < 0 &&
d76c62
+                errno != ENOSYS && errno != EBADF) {
d76c62
+                virReportSystemError(errno,
d76c62
+                                     _("Unable to get devmapper targets for %s"),
d76c62
+                                     next->path);
d76c62
+                goto cleanup;
d76c62
+            }
d76c62
+
d76c62
+            for (i = 0; targetPaths && targetPaths[i]; i++) {
d76c62
+                if (qemuDomainCreateDevice(targetPaths[i], data, false) < 0)
d76c62
+                    goto cleanup;
d76c62
+            }
d76c62
         }
d76c62
     }
d76c62
 
d76c62
@@ -15910,21 +15927,19 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm,
d76c62
                              virStorageSourcePtr src)
d76c62
 {
d76c62
     virStorageSourcePtr next;
d76c62
-    char **paths = NULL;
d76c62
+    VIR_AUTOSTRINGLIST paths = NULL;
d76c62
     size_t npaths = 0;
d76c62
     bool hasNVMe = false;
d76c62
-    g_autofree char *dmPath = NULL;
d76c62
-    g_autofree char *vfioPath = NULL;
d76c62
-    int ret = -1;
d76c62
 
d76c62
     for (next = src; virStorageSourceIsBacking(next); next = next->backingStore) {
d76c62
+        VIR_AUTOSTRINGLIST targetPaths = NULL;
d76c62
         g_autofree char *tmpPath = NULL;
d76c62
 
d76c62
         if (next->type == VIR_STORAGE_TYPE_NVME) {
d76c62
             hasNVMe = true;
d76c62
 
d76c62
             if (!(tmpPath = virPCIDeviceAddressGetIOMMUGroupDev(&next->nvme->pciAddr)))
d76c62
-                goto cleanup;
d76c62
+                return -1;
d76c62
         } else {
d76c62
             if (virStorageSourceIsEmpty(next) ||
d76c62
                 !virStorageSourceIsLocalStorage(next)) {
d76c62
@@ -15935,30 +15950,35 @@ qemuDomainNamespaceSetupDisk(virDomainObjPtr vm,
d76c62
             tmpPath = g_strdup(next->path);
d76c62
         }
d76c62
 
d76c62
-        if (VIR_APPEND_ELEMENT(paths, npaths, tmpPath) < 0)
d76c62
-            goto cleanup;
d76c62
+        if (virStringListAdd(&paths, tmpPath) < 0)
d76c62
+            return -1;
d76c62
+
d76c62
+        if (virDevMapperGetTargets(next->path, &targetPaths) < 0 &&
d76c62
+            errno != ENOSYS && errno != EBADF) {
d76c62
+            virReportSystemError(errno,
d76c62
+                                 _("Unable to get devmapper targets for %s"),
d76c62
+                                 next->path);
d76c62
+            return -1;
d76c62
+        }
d76c62
+
d76c62
+        if (virStringListMerge(&paths, &targetPaths) < 0)
d76c62
+            return -1;
d76c62
     }
d76c62
 
d76c62
     /* qemu-pr-helper might require access to /dev/mapper/control. */
d76c62
-    if (src->pr) {
d76c62
-        dmPath = g_strdup(QEMU_DEVICE_MAPPER_CONTROL_PATH);
d76c62
-        if (VIR_APPEND_ELEMENT_COPY(paths, npaths, dmPath) < 0)
d76c62
-            goto cleanup;
d76c62
-    }
d76c62
+    if (src->pr &&
d76c62
+        virStringListAdd(&paths, QEMU_DEVICE_MAPPER_CONTROL_PATH) < 0)
d76c62
+        return -1;
d76c62
 
d76c62
-    if (hasNVMe) {
d76c62
-        vfioPath = g_strdup(QEMU_DEV_VFIO);
d76c62
-        if (VIR_APPEND_ELEMENT(paths, npaths, vfioPath) < 0)
d76c62
-            goto cleanup;
d76c62
-    }
d76c62
+    if (hasNVMe &&
d76c62
+        virStringListAdd(&paths, QEMU_DEV_VFIO) < 0)
d76c62
+        return -1;
d76c62
 
d76c62
+    npaths = virStringListLength((const char **) paths);
d76c62
     if (qemuDomainNamespaceMknodPaths(vm, (const char **) paths, npaths) < 0)
d76c62
-        goto cleanup;
d76c62
+        return -1;
d76c62
 
d76c62
-    ret = 0;
d76c62
- cleanup:
d76c62
-    virStringListFreeCount(paths, npaths);
d76c62
-    return ret;
d76c62
+    return 0;
d76c62
 }
d76c62
 
d76c62
 
d76c62
diff --git a/src/util/virdevmapper.h b/src/util/virdevmapper.h
d76c62
index e576d2bf7e..87bbc63cfd 100644
d76c62
--- a/src/util/virdevmapper.h
d76c62
+++ b/src/util/virdevmapper.h
d76c62
@@ -20,6 +20,8 @@
d76c62
 
d76c62
 #pragma once
d76c62
 
d76c62
+#include "internal.h"
d76c62
+
d76c62
 int
d76c62
 virDevMapperGetTargets(const char *path,
d76c62
-                       char ***devPaths);
d76c62
+                       char ***devPaths) G_GNUC_NO_INLINE;
d76c62
diff --git a/src/util/virutil.h b/src/util/virutil.h
d76c62
index a2530e21b5..58c45a6447 100644
d76c62
--- a/src/util/virutil.h
d76c62
+++ b/src/util/virutil.h
d76c62
@@ -122,7 +122,7 @@ bool virValidateWWN(const char *wwn);
d76c62
 
d76c62
 int virGetDeviceID(const char *path,
d76c62
                    int *maj,
d76c62
-                   int *min);
d76c62
+                   int *min) G_GNUC_NO_INLINE;
d76c62
 int virSetDeviceUnprivSGIO(const char *path,
d76c62
                            int unpriv_sgio);
d76c62
 int virGetDeviceUnprivSGIO(const char *path,
d76c62
diff --git a/tests/qemuhotplugmock.c b/tests/qemuhotplugmock.c
d76c62
index 43a9d79051..8e5b07788d 100644
d76c62
--- a/tests/qemuhotplugmock.c
d76c62
+++ b/tests/qemuhotplugmock.c
d76c62
@@ -19,7 +19,24 @@
d76c62
 #include <config.h>
d76c62
 
d76c62
 #include "qemu/qemu_hotplug.h"
d76c62
+#include "qemu/qemu_process.h"
d76c62
 #include "conf/domain_conf.h"
d76c62
+#include "virdevmapper.h"
d76c62
+#include "virutil.h"
d76c62
+#include "virmock.h"
d76c62
+
d76c62
+static int (*real_virGetDeviceID)(const char *path, int *maj, int *min);
d76c62
+static bool (*real_virFileExists)(const char *path);
d76c62
+
d76c62
+static void
d76c62
+init_syms(void)
d76c62
+{
d76c62
+    if (real_virFileExists)
d76c62
+        return;
d76c62
+
d76c62
+    VIR_MOCK_REAL_INIT(virGetDeviceID);
d76c62
+    VIR_MOCK_REAL_INIT(virFileExists);
d76c62
+}
d76c62
 
d76c62
 unsigned long long
d76c62
 qemuDomainGetUnplugTimeout(virDomainObjPtr vm G_GNUC_UNUSED)
d76c62
@@ -31,3 +48,61 @@ qemuDomainGetUnplugTimeout(virDomainObjPtr vm G_GNUC_UNUSED)
d76c62
         return 200;
d76c62
     return 100;
d76c62
 }
d76c62
+
d76c62
+
d76c62
+int
d76c62
+virDevMapperGetTargets(const char *path,
d76c62
+                       char ***devPaths)
d76c62
+{
d76c62
+    *devPaths = NULL;
d76c62
+
d76c62
+    if (STREQ(path, "/dev/mapper/virt")) {
d76c62
+        *devPaths = g_new(char *, 4);
d76c62
+        (*devPaths)[0] = g_strdup("/dev/block/8:0");  /* /dev/sda */
d76c62
+        (*devPaths)[1] = g_strdup("/dev/block/8:16"); /* /dev/sdb */
d76c62
+        (*devPaths)[2] = g_strdup("/dev/block/8:32"); /* /dev/sdc */
d76c62
+        (*devPaths)[3] = NULL;
d76c62
+    }
d76c62
+
d76c62
+    return 0;
d76c62
+}
d76c62
+
d76c62
+
d76c62
+int
d76c62
+virGetDeviceID(const char *path, int *maj, int *min)
d76c62
+{
d76c62
+    init_syms();
d76c62
+
d76c62
+    if (STREQ(path, "/dev/mapper/virt")) {
d76c62
+        *maj = 254;
d76c62
+        *min = 0;
d76c62
+        return 0;
d76c62
+    }
d76c62
+
d76c62
+    return real_virGetDeviceID(path, maj, min);
d76c62
+}
d76c62
+
d76c62
+
d76c62
+bool
d76c62
+virFileExists(const char *path)
d76c62
+{
d76c62
+    init_syms();
d76c62
+
d76c62
+    if (STREQ(path, "/dev/mapper/virt"))
d76c62
+        return true;
d76c62
+
d76c62
+    return real_virFileExists(path);
d76c62
+}
d76c62
+
d76c62
+
d76c62
+int
d76c62
+qemuProcessStartManagedPRDaemon(virDomainObjPtr vm G_GNUC_UNUSED)
d76c62
+{
d76c62
+    return 0;
d76c62
+}
d76c62
+
d76c62
+
d76c62
+void
d76c62
+qemuProcessKillManagedPRDaemon(virDomainObjPtr vm G_GNUC_UNUSED)
d76c62
+{
d76c62
+}
d76c62
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
d76c62
index a60c8d1c93..2105c70ca8 100644
d76c62
--- a/tests/qemuhotplugtest.c
d76c62
+++ b/tests/qemuhotplugtest.c
d76c62
@@ -87,6 +87,8 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
d76c62
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_VNC);
d76c62
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE);
d76c62
     virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE);
d76c62
+    virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER);
d76c62
+    virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK);
d76c62
 
d76c62
     if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0)
d76c62
         return -1;
d76c62
@@ -743,6 +745,17 @@ mymain(void)
d76c62
                    "device_del", QMP_DEVICE_DELETED("scsi3-0-5-6") QMP_OK,
d76c62
                    "human-monitor-command", HMP(""));
d76c62
 
d76c62
+    DO_TEST_ATTACH("base-live", "disk-scsi-multipath", false, true,
d76c62
+                   "object-add", QMP_OK,
d76c62
+                   "human-monitor-command", HMP("OK\\r\\n"),
d76c62
+                   "device_add", QMP_OK);
d76c62
+    DO_TEST_DETACH("base-live", "disk-scsi-multipath", true, true,
d76c62
+                   "device_del", QMP_OK,
d76c62
+                   "human-monitor-command", HMP(""));
d76c62
+    DO_TEST_DETACH("base-live", "disk-scsi-multipath", false, false,
d76c62
+                   "device_del", QMP_DEVICE_DELETED("scsi0-0-0-0") QMP_OK,
d76c62
+                   "human-monitor-command", HMP(""));
d76c62
+
d76c62
     DO_TEST_ATTACH("base-live", "qemu-agent", false, true,
d76c62
                    "chardev-add", QMP_OK,
d76c62
                    "device_add", QMP_OK);
d76c62
diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml b/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml
d76c62
new file mode 100644
d76c62
index 0000000000..5a6f955284
d76c62
--- /dev/null
d76c62
+++ b/tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml
d76c62
@@ -0,0 +1,8 @@
d76c62
+<disk type='block' device='lun'>
d76c62
+    <driver name='qemu' type='raw'/>
d76c62
+    <source dev='/dev/mapper/virt'>
d76c62
+        <reservations managed='yes'/>
d76c62
+    </source>
d76c62
+    <target dev='sda' bus='scsi'/>
d76c62
+  <address type='drive' controller='0' bus='0' target='0' unit='0'/>
d76c62
+</disk>
d76c62
diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml
d76c62
new file mode 100644
d76c62
index 0000000000..40af064d10
d76c62
--- /dev/null
d76c62
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml
d76c62
@@ -0,0 +1,62 @@
d76c62
+<domain type='kvm' id='7'>
d76c62
+  <name>hotplug</name>
d76c62
+  <uuid>d091ea82-29e6-2e34-3005-f02617b36e87</uuid>
d76c62
+  <memory unit='KiB'>4194304</memory>
d76c62
+  <currentMemory unit='KiB'>4194304</currentMemory>
d76c62
+  <vcpu placement='static'>4</vcpu>
d76c62
+  <os>
d76c62
+    <type arch='x86_64' machine='pc'>hvm</type>
d76c62
+    <boot dev='hd'/>
d76c62
+  </os>
d76c62
+  <features>
d76c62
+    <acpi/>
d76c62
+    <apic/>
d76c62
+    <pae/>
d76c62
+  </features>
d76c62
+  <clock offset='utc'/>
d76c62
+  <on_poweroff>destroy</on_poweroff>
d76c62
+  <on_reboot>restart</on_reboot>
d76c62
+  <on_crash>restart</on_crash>
d76c62
+  <devices>
d76c62
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
d76c62
+    <disk type='block' device='lun'>
d76c62
+      <driver name='qemu' type='raw'/>
d76c62
+      <source dev='/dev/mapper/virt'>
d76c62
+        <reservations managed='yes'>
d76c62
+          <source type='unix' path='/tmp/lib/domain-7-hotplug/pr-helper0.sock' mode='client'/>
d76c62
+        </reservations>
d76c62
+      </source>
d76c62
+      <backingStore/>
d76c62
+      <target dev='sda' bus='scsi'/>
d76c62
+      <alias name='scsi0-0-0-0'/>
d76c62
+      <address type='drive' controller='0' bus='0' target='0' unit='0'/>
d76c62
+    </disk>
d76c62
+    <controller type='usb' index='0'>
d76c62
+      <alias name='usb'/>
d76c62
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x2'/>
d76c62
+    </controller>
d76c62
+    <controller type='ide' index='0'>
d76c62
+      <alias name='ide'/>
d76c62
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x1'/>
d76c62
+    </controller>
d76c62
+    <controller type='scsi' index='0' model='virtio-scsi'>
d76c62
+      <alias name='scsi0'/>
d76c62
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
d76c62
+    </controller>
d76c62
+    <controller type='pci' index='0' model='pci-root'>
d76c62
+      <alias name='pci'/>
d76c62
+    </controller>
d76c62
+    <controller type='virtio-serial' index='0'>
d76c62
+      <alias name='virtio-serial0'/>
d76c62
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x04' function='0x0'/>
d76c62
+    </controller>
d76c62
+    <input type='mouse' bus='ps2'>
d76c62
+      <alias name='input0'/>
d76c62
+    </input>
d76c62
+    <input type='keyboard' bus='ps2'>
d76c62
+      <alias name='input1'/>
d76c62
+    </input>
d76c62
+    <memballoon model='none'/>
d76c62
+  </devices>
d76c62
+  <seclabel type='none' model='none'/>
d76c62
+</domain>
d76c62
-- 
d76c62
2.25.1
d76c62