qemu: support hotplug of vdpa devices

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@redhat.com>
This commit is contained in:
Jonathon Jongsma 2020-10-14 12:08:29 -05:00 committed by Laine Stump
parent b8998cc670
commit b79abf9c3c
5 changed files with 144 additions and 4 deletions

View File

@ -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;
@ -4598,8 +4621,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;

View File

@ -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);
}

View File

@ -90,6 +90,7 @@ qemuHotplugCreateObjects(virDomainXMLOptionPtr xmlopt,
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_PR_MANAGER_HELPER);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_SCSI_BLOCK);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_DEVICE_USB_KBD);
virQEMUCapsSet(priv->qemuCaps, QEMU_CAPS_NETDEV_VHOST_VDPA);
if (qemuTestCapsCacheInsert(driver.qemuCapsCache, priv->qemuCaps) < 0)
return -1;
@ -141,6 +142,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));
@ -163,6 +167,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:
@ -824,6 +829,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);

View File

@ -0,0 +1,4 @@
<interface type='vdpa'>
<mac address='52:54:00:39:5f:04'/>
<source dev='/dev/vhost-vdpa-0'/>
</interface>

View File

@ -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>