This test asserts that the vhost-net commandline is generated for KVM and MSHV accelerator options. $ VIR_TEST_RANGE=2-10 VIR_TEST_DEBUG=1 meson test -C build \ --verbose qemucommandutiltest TEST: qemucommandutiltest 2) KVM vhost ... OK 3) KVM vhost multiqueue ... OK 4) MSHV vhost ... OK 5) MSHV vhost multiqueue ... OK 6) MSHV default ... OK 7) MSHV qemu backend ... OK 8) TCG vhost ... OK 9) MSHV vhost unavailable ... OK 10) MSHV default unavailable ... OK Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> --- tests/qemucommandutiltest.c | 128 ++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/tests/qemucommandutiltest.c b/tests/qemucommandutiltest.c index 58493c7..f499d42 100644 --- a/tests/qemucommandutiltest.c +++ b/tests/qemucommandutiltest.c @@ -19,9 +19,15 @@ #include <config.h> +#include <fcntl.h> + #include "util/virjson.h" #include "util/virqemu.h" #include "testutils.h" +#include "qemu/qemu_command.h" +#include "qemu/qemu_domain.h" +#include "qemu/qemu_interface.h" +#include "virfile.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -64,6 +70,111 @@ testQemuCommandBuildFromJSON(const void *opaque) return 0; } +struct testVhostNetInfo { + virDomainVirtType virtType; + virDomainNetDriverType driver; + unsigned int queues; + unsigned int expectedVhostFDs; + bool unavailable; + int expectedResult; +}; + +static int +testOpenVhostNet(const void *opaque) +{ + const struct testVhostNetInfo *info = opaque; + g_autoptr(virDomainXMLOption) xmlopt = NULL; + g_autoptr(virDomainDef) def = virDomainDefNew(NULL); + virDomainObj vm = { .def = def }; + g_autoptr(virDomainNetDef) net = NULL; + g_autoptr(virJSONValue) props = NULL; + g_autoptr(virCommand) cmd = virCommandNew("/bin/true"); + g_auto(virBuffer) expected = VIR_BUFFER_INITIALIZER; + g_autofree char *expectedFDs = NULL; + qemuDomainNetworkPrivate *netpriv; + GSList *n; + bool vhost = false; + unsigned int queues = info->queues ? info->queues : 1; + size_t i; + int rc; + + if (!def || + !(xmlopt = virDomainXMLOptionNew(NULL, + &virQEMUDriverPrivateDataCallbacks, + NULL, NULL, NULL, NULL)) || + !(net = virDomainNetDefNew(xmlopt))) + return -1; + + def->name = g_strdup("vhost-test"); + def->virtType = info->virtType; + def->os.arch = VIR_ARCH_X86_64; + net->type = VIR_DOMAIN_NET_TYPE_ETHERNET; + net->model = VIR_DOMAIN_NET_MODEL_VIRTIO; + net->info.alias = g_strdup("net0"); + net->driver.virtio.name = info->driver; + net->driver.virtio.queues = info->queues; + /* Exercise the real device-opening code without requiring vhost support. */ + net->backend.vhost = g_strdup(info->unavailable ? + abs_builddir "/missing-vhost-net" : "/dev/null"); + netpriv = QEMU_DOMAIN_NETWORK_PRIVATE(net); + + rc = qemuInterfaceOpenVhostNet(&vm, net); + if (virTestCompareToULL(info->expectedResult != 0, rc != 0) < 0 || + virTestCompareToULL(info->expectedVhostFDs, + g_slist_length(netpriv->vhostfds)) < 0) + return -1; + + if (rc < 0) { + virResetLastError(); + return 0; + } + + for (i = 0; i < queues; i++) { + VIR_AUTOCLOSE fd = open("/dev/null", O_RDWR); + g_autofree char *name = g_strdup_printf("tapfd-net0%zu", i); + + if (fd < 0) { + virReportSystemError(errno, "%s", "Unable to open /dev/null"); + return -1; + } + netpriv->tapfds = g_slist_append(netpriv->tapfds, + qemuFDPassDirectNew(name, &fd)); + } + + for (n = netpriv->tapfds; n; n = n->next) + qemuFDPassDirectTransferCommand(n->data, cmd); + + for (n = netpriv->vhostfds; n; n = n->next) { + qemuFDPassDirectTransferCommand(n->data, cmd); + virBufferAsprintf(&expected, "%s:", qemuFDPassDirectGetPath(n->data)); + } + virBufferTrim(&expected, ":"); + expectedFDs = virBufferContentAndReset(&expected); + + if (!(props = qemuBuildHostNetProps(&vm, net))) + return -1; + + if (!info->expectedVhostFDs) { + if (virJSONValueObjectHasKey(props, "vhost") || + virJSONValueObjectHasKey(props, "vhostfd") || + virJSONValueObjectHasKey(props, "vhostfds")) { + fprintf(stderr, "Unexpected vhost properties\n"); + return -1; + } + return 0; + } + + if (virJSONValueObjectGetBoolean(props, "vhost", &vhost) < 0 || !vhost) { + fprintf(stderr, "Missing vhost=true\n"); + return -1; + } + + return virTestCompareToString(expectedFDs, + virJSONValueObjectGetString(props, + queues > 1 ? + "vhostfds" : "vhostfd")); +} + static int mymain(void) { @@ -110,6 +221,23 @@ mymain(void) "server.2.type=tcp," "server.2.host=example.com"); +#define DO_TEST_VHOST_NET(name, virt, driver, queues, nfds, unavailable, result) \ + do { \ + const struct testVhostNetInfo info = { virt, driver, queues, nfds, unavailable, result }; \ + if (virTestRun(name, testOpenVhostNet, &info) < 0) \ + ret = -1; \ + } while (0) + + DO_TEST_VHOST_NET("KVM vhost", VIR_DOMAIN_VIRT_KVM, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 0, 1, false, 0); + DO_TEST_VHOST_NET("KVM vhost multiqueue", VIR_DOMAIN_VIRT_KVM, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 8, 8, false, 0); + DO_TEST_VHOST_NET("MSHV vhost", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 0, 1, false, 0); + DO_TEST_VHOST_NET("MSHV vhost multiqueue", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 8, 8, false, 0); + DO_TEST_VHOST_NET("MSHV default", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_DEFAULT, 8, 8, false, 0); + DO_TEST_VHOST_NET("MSHV qemu backend", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_QEMU, 8, 0, false, 0); + DO_TEST_VHOST_NET("TCG vhost", VIR_DOMAIN_VIRT_QEMU, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 8, 0, false, 0); + DO_TEST_VHOST_NET("MSHV vhost unavailable", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_VHOST, 8, 0, true, -1); + DO_TEST_VHOST_NET("MSHV default unavailable", VIR_DOMAIN_VIRT_HYPERV, VIR_DOMAIN_NET_DRIVER_TYPE_DEFAULT, 8, 0, true, 0); + return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE; } -- 2.34.1