Enable <interface type='vdpa'> for qemu domains. This provides basic support and does not support hotplug or migration. Signed-off-by: Jonathon Jongsma <jjongsma@xxxxxxxxxx> --- src/qemu/qemu_command.c | 28 ++++++++++++-- src/qemu/qemu_command.h | 3 +- src/qemu/qemu_domain.c | 6 +++ src/qemu/qemu_hotplug.c | 12 +++--- src/qemu/qemu_interface.c | 23 ++++++++++++ src/qemu/qemu_interface.h | 2 + src/qemu/qemu_migration.c | 10 ++++- .../net-vdpa.x86_64-latest.args | 37 +++++++++++++++++++ tests/qemuxml2argvmock.c | 11 +++++- tests/qemuxml2argvtest.c | 1 + 10 files changed, 122 insertions(+), 11 deletions(-) create mode 100644 tests/qemuxml2argvdata/net-vdpa.x86_64-latest.args diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 7b7176eb72..b9830292ee 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -3553,7 +3553,8 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, size_t tapfdSize, char **vhostfd, size_t vhostfdSize, - const char *slirpfd) + const char *slirpfd, + const char *vdpadev) { bool is_tap = false; virDomainNetType netType = virDomainNetGetActualType(net); @@ -3692,6 +3693,12 @@ qemuBuildHostNetStr(virDomainNetDefPtr net, break; case VIR_DOMAIN_NET_TYPE_VDPA: + /* Caller will pass the fd to qemu with add-fd */ + if (virJSONValueObjectCreate(&netprops, "s:type", "vhost-vdpa", NULL) < 0 || + virJSONValueObjectAppendString(netprops, "vhostdev", vdpadev) < 0) + return NULL; + break; + case VIR_DOMAIN_NET_TYPE_HOSTDEV: /* Should have been handled earlier via PCI/USB hotplug code. */ case VIR_DOMAIN_NET_TYPE_LAST: @@ -8017,6 +8024,8 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, char **tapfdName = NULL; char **vhostfdName = NULL; g_autofree char *slirpfdName = NULL; + g_autofree char *vdpafdName = NULL; + int vdpafd = -1; virDomainNetType actualType = virDomainNetGetActualType(net); const virNetDevBandwidth *actualBandwidth; bool requireNicdev = false; @@ -8102,13 +8111,17 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, break; + case VIR_DOMAIN_NET_TYPE_VDPA: + if ((vdpafd = qemuInterfaceVDPAConnect(net)) < 0) + goto cleanup; + break; + case VIR_DOMAIN_NET_TYPE_USER: 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: /* nada */ break; @@ -8225,13 +8238,22 @@ qemuBuildInterfaceCommandLine(virQEMUDriverPtr driver, vhostfd[i] = -1; } + if (vdpafd > 0) { + virCommandPassFD(cmd, vdpafd, VIR_COMMAND_PASS_FD_CLOSE_PARENT); + g_autofree char *fdset = qemuVirCommandGetFDSet(cmd, vdpafd); + if (!fdset) + goto cleanup; + virCommandAddArgList(cmd, "-add-fd", fdset, NULL); + vdpafdName = qemuVirCommandGetDevSet(cmd, vdpafd); + } + if (chardev) virCommandAddArgList(cmd, "-chardev", chardev, NULL); if (!(hostnetprops = qemuBuildHostNetStr(net, tapfdName, tapfdSize, vhostfdName, vhostfdSize, - slirpfdName))) + slirpfdName, vdpafdName))) goto cleanup; if (!(host = virQEMUBuildNetdevCommandlineFromJSON(hostnetprops, diff --git a/src/qemu/qemu_command.h b/src/qemu/qemu_command.h index 89d99b111f..8db51f93b1 100644 --- a/src/qemu/qemu_command.h +++ b/src/qemu/qemu_command.h @@ -99,7 +99,8 @@ virJSONValuePtr qemuBuildHostNetStr(virDomainNetDefPtr net, size_t tapfdSize, char **vhostfd, size_t vhostfdSize, - const char *slirpfd); + const char *slirpfd, + const char *vdpadev); /* Current, best practice */ char *qemuBuildNicDevStr(virDomainDefPtr def, diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 680e7d5bf8..b94a181043 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -4488,6 +4488,12 @@ qemuDomainValidateActualNetDef(const virDomainNetDef *net, macstr, virDomainNetTypeToString(actualType)); return -1; } + if (actualType == VIR_DOMAIN_NET_TYPE_VDPA && + !virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_VHOST_VDPA)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vDPA device is not supported with this QEMU binary")); + return -1; + } return 0; } diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 78dd5e9f19..45c07a7f4e 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -1389,7 +1389,7 @@ qemuDomainAttachNetDevice(virQEMUDriverPtr driver, if (!(netprops = qemuBuildHostNetStr(net, tapfdName, tapfdSize, vhostfdName, vhostfdSize, - slirpfdName))) + slirpfdName, NULL))) goto cleanup; qemuDomainObjEnterMonitor(driver, vm); @@ -3484,8 +3484,9 @@ qemuDomainChangeNet(virQEMUDriverPtr driver, olddev = *devslot; oldType = virDomainNetGetActualType(olddev); - if (oldType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - /* no changes are possible to a type='hostdev' interface */ + if (oldType == VIR_DOMAIN_NET_TYPE_HOSTDEV || + oldType == VIR_DOMAIN_NET_TYPE_VDPA) { + /* no changes are possible to a type='hostdev' or type='vdpa' interface */ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("cannot change config of '%s' network type"), virDomainNetTypeToString(oldType)); @@ -3672,8 +3673,9 @@ qemuDomainChangeNet(virQEMUDriverPtr driver, newType = virDomainNetGetActualType(newdev); - if (newType == VIR_DOMAIN_NET_TYPE_HOSTDEV) { - /* can't turn it into a type='hostdev' interface */ + if (newType == VIR_DOMAIN_NET_TYPE_HOSTDEV || + newType == VIR_DOMAIN_NET_TYPE_VDPA) { + /* can't turn it into a type='hostdev' or type='vdpa' interface */ virReportError(VIR_ERR_OPERATION_UNSUPPORTED, _("cannot change network interface type to '%s'"), virDomainNetTypeToString(newType)); diff --git a/src/qemu/qemu_interface.c b/src/qemu/qemu_interface.c index 33157dbbed..a91563dd72 100644 --- a/src/qemu/qemu_interface.c +++ b/src/qemu/qemu_interface.c @@ -641,6 +641,29 @@ qemuInterfaceBridgeConnect(virDomainDefPtr def, } +/* qemuInterfaceVDPAConnect: + * @net: pointer to the VM's interface description + * + * returns: file descriptor of the vdpa device + * + * Called *only* called if actualType is VIR_DOMAIN_NET_TYPE_VDPA + */ +int +qemuInterfaceVDPAConnect(virDomainNetDefPtr net) +{ + int fd; + + if ((fd = open(net->data.vdpa.devicepath, O_RDWR)) < 0) { + virReportSystemError(errno, + _("Unable to open '%s' for vdpa device"), + net->data.vdpa.devicepath); + return -1; + } + + return fd; +} + + qemuSlirpPtr qemuInterfacePrepareSlirp(virQEMUDriverPtr driver, virDomainNetDefPtr net) diff --git a/src/qemu/qemu_interface.h b/src/qemu/qemu_interface.h index 3dcefc6a12..1ba24f0a6f 100644 --- a/src/qemu/qemu_interface.h +++ b/src/qemu/qemu_interface.h @@ -58,3 +58,5 @@ int qemuInterfaceOpenVhostNet(virDomainDefPtr def, qemuSlirpPtr qemuInterfacePrepareSlirp(virQEMUDriverPtr driver, virDomainNetDefPtr net); + +int qemuInterfaceVDPAConnect(virDomainNetDefPtr net) G_GNUC_NO_INLINE; diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 142faa2cf9..238abadd6a 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -1281,7 +1281,15 @@ qemuMigrationSrcIsAllowed(virQEMUDriverPtr driver, for (i = 0; i < vm->def->nnets; i++) { virDomainNetDefPtr net = vm->def->nets[i]; - qemuSlirpPtr slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; + qemuSlirpPtr slirp; + + if (net->type == VIR_DOMAIN_NET_TYPE_VDPA) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("a vDPA device cannot be migrated")); + return false; + } + + slirp = QEMU_DOMAIN_NETWORK_PRIVATE(net)->slirp; if (slirp && !qemuSlirpHasFeature(slirp, QEMU_SLIRP_FEATURE_MIGRATE)) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", diff --git a/tests/qemuxml2argvdata/net-vdpa.x86_64-latest.args b/tests/qemuxml2argvdata/net-vdpa.x86_64-latest.args new file mode 100644 index 0000000000..8e76ac7794 --- /dev/null +++ b/tests/qemuxml2argvdata/net-vdpa.x86_64-latest.args @@ -0,0 +1,37 @@ +LC_ALL=C \ +PATH=/bin \ +HOME=/tmp/lib/domain--1-QEMUGuest1 \ +USER=test \ +LOGNAME=test \ +XDG_DATA_HOME=/tmp/lib/domain--1-QEMUGuest1/.local/share \ +XDG_CACHE_HOME=/tmp/lib/domain--1-QEMUGuest1/.cache \ +XDG_CONFIG_HOME=/tmp/lib/domain--1-QEMUGuest1/.config \ +QEMU_AUDIO_DRV=none \ +/usr/bin/qemu-system-i386 \ +-name guest=QEMUGuest1,debug-threads=on \ +-S \ +-object secret,id=masterKey0,format=raw,\ +file=/tmp/lib/domain--1-QEMUGuest1/master-key.aes \ +-machine pc,accel=tcg,usb=off,dump-guest-core=off \ +-cpu qemu64 \ +-m 214 \ +-overcommit mem-lock=off \ +-smp 1,sockets=1,cores=1,threads=1 \ +-uuid c7a5fdbd-edaf-9455-926a-d65c16db1809 \ +-display none \ +-no-user-config \ +-nodefaults \ +-chardev socket,id=charmonitor,fd=1729,server,nowait \ +-mon chardev=charmonitor,id=monitor,mode=control \ +-rtc base=utc \ +-no-shutdown \ +-no-acpi \ +-boot strict=on \ +-device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 \ +-add-fd set=0,fd=1732 \ +-netdev vhost-vdpa,vhostdev=/dev/fdset/0,id=hostnet0 \ +-device virtio-net-pci,netdev=hostnet0,id=net0,mac=52:54:00:95:db:c0,bus=pci.0,\ +addr=0x2 \ +-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,\ +resourcecontrol=deny \ +-msg timestamp=on diff --git a/tests/qemuxml2argvmock.c b/tests/qemuxml2argvmock.c index e5841bc8e3..516776697f 100644 --- a/tests/qemuxml2argvmock.c +++ b/tests/qemuxml2argvmock.c @@ -205,7 +205,7 @@ virHostGetDRMRenderNode(void) static void (*real_virCommandPassFD)(virCommandPtr cmd, int fd, unsigned int flags); -static const int testCommandPassSafeFDs[] = { 1730, 1731 }; +static const int testCommandPassSafeFDs[] = { 1730, 1731, 1732 }; void virCommandPassFD(virCommandPtr cmd, @@ -283,3 +283,12 @@ qemuBuildTPMOpenBackendFDs(const char *tpmdev G_GNUC_UNUSED, *cancelfd = 1731; return 0; } + + +int +qemuInterfaceVDPAConnect(virDomainNetDefPtr net G_GNUC_UNUSED) +{ + if (fcntl(1732, F_GETFD) != -1) + abort(); + return 1732; +} diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index e93948e3fc..000d6919f2 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -1446,6 +1446,7 @@ mymain(void) QEMU_CAPS_DEVICE_VFIO_PCI); DO_TEST_FAILURE("net-hostdev-fail", QEMU_CAPS_DEVICE_VFIO_PCI); + DO_TEST_CAPS_LATEST("net-vdpa"); DO_TEST("hostdev-pci-multifunction", QEMU_CAPS_KVM, -- 2.26.2