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 | 62 +++++++++++++++++-- tests/qemuhotplugmock.c | 9 +++ tests/qemuhotplugtest.c | 16 +++++ .../qemuhotplug-interface-vdpa.xml | 4 ++ .../qemuhotplug-base-live+interface-vdpa.xml | 57 +++++++++++++++++ 5 files changed, 144 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 6864e8b47a..f999f5cd07 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1157,6 +1157,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; @@ -1334,12 +1336,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,13 +1392,29 @@ 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 accepts a filename. We can pass an open fd by + * filename if we add the fd to an fdset and then pass a filename of + * /dev/fdset/$FDSETID. */ + 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))) { + ignore_value(qemuDomainObjExitMonitor(driver, vm)); goto cleanup; - - qemuDomainObjEnterMonitor(driver, vm); + } if (actualType == VIR_DOMAIN_NET_TYPE_VHOSTUSER) { if (qemuMonitorAttachCharDev(priv->mon, charDevAlias, net->data.vhostuser) < 0) { @@ -1518,6 +1540,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, VIR_FREE(vhostfdName); virDomainCCWAddressSetFree(ccwaddrs); VIR_FORCE_CLOSE(slirpfd); + VIR_FORCE_CLOSE(vdpafd); return ret; @@ -4595,8 +4618,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