Re: [libvirt PATCH v4 5/6] qemu: support hotplug of vdpa devices

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 9/24/20 5:45 PM, Jonathon Jongsma wrote:
By using the new qemu monitor functions to handle passing and removing
file descriptors, we can support hotplug of vdpa devices.

Signed-off-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx>
---
  src/qemu/qemu_hotplug.c                       | 60 +++++++++++++++++--
  tests/qemuhotplugmock.c                       |  9 +++
  tests/qemuhotplugtest.c                       | 16 +++++
  .../qemuhotplug-interface-vdpa.xml            |  4 ++
  .../qemuhotplug-base-live+interface-vdpa.xml  | 57 ++++++++++++++++++
  5 files changed, 142 insertions(+), 4 deletions(-)
  create mode 100644 tests/qemuhotplugtestdevices/qemuhotplug-interface-vdpa.xml
  create mode 100644 tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-vdpa.xml

diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index 0582b78f97..3a2aff607c 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -1152,6 +1152,8 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
      virErrorPtr originalError = NULL;
      g_autofree char *slirpfdName = NULL;
      int slirpfd = -1;
+    g_autofree char *vdpafdName = NULL;
+    int vdpafd = -1;
      char **tapfdName = NULL;
      int *tapfd = NULL;
      size_t tapfdSize = 0;
@@ -1335,12 +1337,16 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
          /* hostdev interfaces were handled earlier in this function */
          break;
+ case VIR_DOMAIN_NET_TYPE_VDPA:
+        if ((vdpafd = qemuInterfaceVDPAConnect(net)) < 0)
+            goto cleanup;
+        break;
+
      case VIR_DOMAIN_NET_TYPE_SERVER:
      case VIR_DOMAIN_NET_TYPE_CLIENT:
      case VIR_DOMAIN_NET_TYPE_MCAST:
      case VIR_DOMAIN_NET_TYPE_INTERNAL:
      case VIR_DOMAIN_NET_TYPE_UDP:
-    case VIR_DOMAIN_NET_TYPE_VDPA:
      case VIR_DOMAIN_NET_TYPE_LAST:
          virReportError(VIR_ERR_OPERATION_UNSUPPORTED,
                         _("hotplug of interface type of %s is not implemented yet"),
@@ -1386,14 +1392,28 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
      for (i = 0; i < vhostfdSize; i++)
          vhostfdName[i] = g_strdup_printf("vhostfd-%s%zu", net->info.alias, i);
+ qemuDomainObjEnterMonitor(driver, vm);


So this was moved up ahead of the call to qemuBuildHostNetStr()...


+
+    if (vdpafd > 0) {
+        /* vhost-vdpa only takes a filename for the dev, but we want to pass an
+         * open fd to qemu. Passing -1 as the fdset-id will create a new fdset
+         * and return the id of that fdset */
+        qemuMonitorAddFdInfo fdinfo;
+        if (qemuMonitorAddFileHandleToSet(priv->mon, vdpafd, -1,
+                                          net->data.vdpa.devicepath,
+                                          &fdinfo) < 0) {
+            ignore_value(qemuDomainObjExitMonitor(driver, vm));
+            goto cleanup;


... and here you ExitMonitor prior to goto cleanup on failure...

+        }
+        vdpafdName = g_strdup_printf("/dev/fdset/%d", fdinfo.fdset);
+    }
+
      if (!(netprops = qemuBuildHostNetStr(net,
                                           tapfdName, tapfdSize,
                                           vhostfdName, vhostfdSize,
-                                         slirpfdName, NULL)))
+                                         slirpfdName, vdpafdName)))
          goto cleanup;


...but  here you don't. (and should)


(NB: this change does put qemuBuildHostNetStr() inside the Monitor section, but it just does a bit of string shuffling/creation, so that's not a big deal)


- qemuDomainObjEnterMonitor(driver, vm);
-


(^^ old location of qemuDomainObjEnterMonitor())


      if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER) {
          if (qemuMonitorAttachCharDev(priv->mon, charDevAlias, net->data.vhostuser) < 0) {
              ignore_value(qemuDomainObjExitMonitor(driver, vm));
@@ -1518,6 +1538,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver,
      VIR_FREE(vhostfdName);
      virDomainCCWAddressSetFree(ccwaddrs);
      VIR_FORCE_CLOSE(slirpfd);
+    VIR_FORCE_CLOSE(vdpafd);
return ret; @@ -4586,8 +4607,39 @@ qemuDomainRemoveNetDevice(virQEMUDriverPtr driver,
               * to just ignore the error and carry on.
               */
          }
+    } else if (actualType == VIR_DOMAIN_NET_TYPE_VDPA) {
+        int vdpafdset = -1;
+        g_autoptr(qemuMonitorFdsets) fdsets = NULL;
+
+        /* query qemu for which fdset is associated with the fd that we passed
+         * to qemu via 'add-fd' for this vdpa device. If we don't remove the
+         * fd, qemu will keep it open */
+        if (qemuMonitorQueryFdsets(priv->mon, &fdsets) == 0) {
+            for (i = 0; i < fdsets->nfdsets && vdpafdset < 0; i++) {
+                size_t j;
+                qemuMonitorFdsetInfoPtr set = &fdsets->fdsets[i];
+
+                for (j = 0; j < set->nfds; j++) {
+                    qemuMonitorFdsetFdInfoPtr fdinfo = &set->fds[j];
+                    if (STREQ_NULLABLE(fdinfo->opaque, net->data.vdpa.devicepath)) {
+                        vdpafdset = set->id;
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (vdpafdset < 0) {
+            VIR_WARN("Cannot determine fdset for vdpa device");
+        } else {
+            if (qemuMonitorRemoveFdset(priv->mon, vdpafdset) < 0) {
+                /* if it fails, there's not much we can do... just carry on */
+                VIR_WARN("failed to close vdpa device");
+            }
+        }


I agree there's not much we can do here to make the situation better, but is it really going to be okay to inform the user that the device is now free, since it apparently isn't? If we go ahead and send the DEVICE_DELETED event up to the application, then it will think that the same vdpa device is now available to be re-used elsewhere. Do you have an idea what are the odds on that being true? (I don't, that's why I'm asking :-)).


It may be safer to return failure, so the device is just stuck shown as in-use by this guest; that would be bad, but maybe not as bad as if it was still actually being used by this guest somehow (possible, since the fd couldn't be deleted), and a 2nd guest started using it too. (I really don't know what the consequences of any of this might be; just trying to inject my sunny disposition into the mix; truthfully I'd be willing to accept either way, just wanted to make sure it's considered).

      }
+
      if (qemuDomainObjExitMonitor(driver, vm) < 0)
          return -1;
diff --git a/tests/qemuhotplugmock.c b/tests/qemuhotplugmock.c
index 29fac8a598..d2e32ecf7e 100644
--- a/tests/qemuhotplugmock.c
+++ b/tests/qemuhotplugmock.c
@@ -19,11 +19,13 @@
  #include <config.h>
#include "qemu/qemu_hotplug.h"
+#include "qemu/qemu_interface.h"
  #include "qemu/qemu_process.h"
  #include "conf/domain_conf.h"
  #include "virdevmapper.h"
  #include "virutil.h"
  #include "virmock.h"
+#include <fcntl.h>
static int (*real_virGetDeviceID)(const char *path, int *maj, int *min);
  static bool (*real_virFileExists)(const char *path);
@@ -106,3 +108,10 @@ void
  qemuProcessKillManagedPRDaemon(virDomainObjPtr vm G_GNUC_UNUSED)
  {
  }
+
+int
+qemuInterfaceVDPAConnect(virDomainNetDefPtr net G_GNUC_UNUSED)
+{
+    /* need a valid fd or sendmsg won't work. Just open /dev/null */
+    return open("/dev/null", O_RDONLY);
+}
diff --git a/tests/qemuhotplugtest.c b/tests/qemuhotplugtest.c
index 2d12cacf28..b7cebfc0e7 100644
--- a/tests/qemuhotplugtest.c
+++ b/tests/qemuhotplugtest.c
@@ -89,6 +89,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
      virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE);
      virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER);
      virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK);
+    virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_NETDEV_VHOST_VDPA);
if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0)
          return -1;
@@ -140,6 +141,9 @@ testQemuHotplugAttach(virDomainObjPtr vm,
      case VIR_DOMAIN_DEVICE_HOSTDEV:
          ret = qemuDomainAttachHostDevice(&driver, vm, dev->data.hostdev);
          break;
+    case VIR_DOMAIN_DEVICE_NET:
+        ret = qemuDomainAttachNetDevice(&driver, vm, dev->data.net);
+        break;


Nice attention to detail - nobody before you has bothered with a hotplug test for a network device :-)


      default:
          VIR_TEST_VERBOSE("device type '%s' cannot be attached",
                  virDomainDeviceTypeToString(dev->type));
@@ -162,6 +166,7 @@ testQemuHotplugDetach(virDomainObjPtr vm,
      case VIR_DOMAIN_DEVICE_SHMEM:
      case VIR_DOMAIN_DEVICE_WATCHDOG:
      case VIR_DOMAIN_DEVICE_HOSTDEV:
+    case VIR_DOMAIN_DEVICE_NET:
          ret = qemuDomainDetachDeviceLive(vm, dev, &driver, async);
          break;
      default:
@@ -823,6 +828,17 @@ mymain(void)
      DO_TEST_DETACH("pseries-base-live", "hostdev-pci", false, false,
                     "device_del", QMP_DEVICE_DELETED("hostdev0") QMP_OK);
+ DO_TEST_ATTACH("base-live", "interface-vdpa", false, true,
+                   "add-fd", "{ \"return\": { \"fdset-id\": 1, \"fd\": 95 }}",
+                   "netdev_add", QMP_OK, "device_add", QMP_OK);
+    DO_TEST_DETACH("base-live", "interface-vdpa", false, false,
+                   "device_del", QMP_DEVICE_DELETED("net0") QMP_OK,
+                   "netdev_del", QMP_OK,
+                   "query-fdsets",
+                   "{ \"return\": [{\"fds\": [{\"fd\": 95, \"opaque\": \"/dev/vhost-vdpa-0\"}], \"fdset-id\": 1}]}",
+                   "remove-fd", QMP_OK
+                   );
+
      DO_TEST_ATTACH("base-live", "watchdog", false, true,
                     "watchdog-set-action", QMP_OK,
                     "device_add", QMP_OK);
diff --git a/tests/qemuhotplugtestdevices/qemuhotplug-interface-vdpa.xml b/tests/qemuhotplugtestdevices/qemuhotplug-interface-vdpa.xml
new file mode 100644
index 0000000000..e42ca08d31
--- /dev/null
+++ b/tests/qemuhotplugtestdevices/qemuhotplug-interface-vdpa.xml
@@ -0,0 +1,4 @@
+<interface type='vdpa'>
+    <mac address='52:54:00:39:5f:04'/>
+    <source dev='/dev/vhost-vdpa-0'/>
+</interface>
diff --git a/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-vdpa.xml b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-vdpa.xml
new file mode 100644
index 0000000000..066180bb3c
--- /dev/null
+++ b/tests/qemuhotplugtestdomains/qemuhotplug-base-live+interface-vdpa.xml
@@ -0,0 +1,57 @@
+<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>
+    <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>
+    <interface type='vdpa'>
+      <mac address='52:54:00:39:5f:04'/>
+      <source dev='/dev/vhost-vdpa-0'/>
+      <model type='virtio'/>
+      <alias name='net0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
+    </interface>
+    <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>


With the ExitMonitor() added where indicated, consideration of possibly failing if the fd can't be deleted, and (as with the rest of the series) as long as it's been possible to test with real hardware:


Reviewed-by: Laine Stump <laine@xxxxxxxxxx>





[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux