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