Blob Blame History Raw
From 73c83c67608896704bfeb55b7f973f3e5ee12c96 Mon Sep 17 00:00:00 2001
Message-Id: <73c83c67608896704bfeb55b7f973f3e5ee12c96@dist-git>
From: Michal Privoznik <mprivozn@redhat.com>
Date: Tue, 17 Mar 2020 16:08:02 +0100
Subject: [PATCH] qemu: Create multipath targets for PRs

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>
(cherry picked from commit a30078cb832646177defd256e77c632905f1e6d0)

Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1814157

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>

Conflicts:
src/qemu/qemu_domain.c: There are two conflicts. The first one is
in the first hunk because 7e0d11be5b0 is not backported. The
second conflict is in the third hunk and it is because
db780004a9d and friends which switches the code over to use
autofree and drops needless cleanup labels are not backported.

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Message-Id: <7a59996bd80955551d37f775e8d682649b1f4a45.1584457667.git.mprivozn@redhat.com>
Acked-by: Peter Krempa <pkrempa@redhat.com>
---
 src/qemu/qemu_domain.c                        | 64 ++++++++++------
 src/util/virdevmapper.h                       |  4 +-
 src/util/virutil.h                            |  2 +-
 tests/qemuhotplugmock.c                       | 75 +++++++++++++++++++
 tests/qemuhotplugtest.c                       | 13 ++++
 .../qemuhotplug-disk-scsi-multipath.xml       |  8 ++
 ...uhotplug-base-live+disk-scsi-multipath.xml | 62 +++++++++++++++
 7 files changed, 204 insertions(+), 24 deletions(-)
 create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-disk-scsi-multipath.xml
 create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml

diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index a273aefa6b..36a63449b2 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -62,6 +62,7 @@
 #include "virdomainsnapshotobjlist.h"
 #include "virdomaincheckpointobjlist.h"
 #include "backup_conf.h"
+#include "virdevmapper.h"
 
 #ifdef MAJOR_IN_MKDEV
 # include <sys/mkdev.h>
@@ -14866,6 +14867,9 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED,
     int ret = -1;
 
     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;
 
@@ -14884,6 +14888,19 @@ qemuDomainSetupDisk(virQEMUDriverConfigPtr cfg G_GNUC_UNUSED,
 
             if (qemuDomainCreateDevice(next->path, data, false) < 0)
                 goto cleanup;
+
+            if (virDevMapperGetTargets(next->path, &targetPaths) < 0 &&
+                errno != ENOSYS && errno != EBADF) {
+                virReportSystemError(errno,
+                                     _("Unable to get devmapper targets for %s"),
+                                     next->path);
+                goto cleanup;
+            }
+
+            for (i = 0; targetPaths && targetPaths[i]; i++) {
+                if (qemuDomainCreateDevice(targetPaths[i], data, false) < 0)
+                    goto cleanup;
+            }
         }
     }
 
@@ -15910,21 +15927,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)) {
@@ -15935,30 +15950,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 a2530e21b5..58c45a6447 100644
--- a/src/util/virutil.h
+++ b/src/util/virutil.h
@@ -122,7 +122,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,
                            int unpriv_sgio);
 int virGetDeviceUnprivSGIO(const char *path,
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 <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)
+{
+}
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
index a60c8d1c93..2105c70ca8 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;
@@ -743,6 +745,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 @@
+<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
index 0000000000..40af064d10
--- /dev/null
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+disk-scsi-multipath.xml
@@ -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>
-- 
2.25.1