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