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(a)redhat.com>
---
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);
+
+ 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;
+ }
+ vdpafdName = g_strdup_printf("/dev/fdset/%d", fdinfo.fdset);
+ }
+
if (!(netprops = qemuBuildHostNetStr(net,
tapfdName, tapfdSize,
vhostfdName, vhostfdSize,
- slirpfdName, NULL)))
+ slirpfdName, vdpafdName)))
goto cleanup;
- qemuDomainObjEnterMonitor(driver, vm);
-
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");
+ }
+ }
}
+
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;
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>
--
2.26.2