The commit produces this nice output: virsh # emulatorcaps /usr/bin/qemu-system-x86_64 kvm pc-1.3 <emulatorCapabilities> <path>/usr/bin/qemu-system-x86_64</path> <domain>kvm</domain> <machine>pc-1.3</machine> <arch>x86_64</arch> <vcpu>255</vcpu> <devices> <hostdev> <driver> <enum name='driver'> <value>kvm</value> <value>vfio</value> </enum> </driver> </hostdev> </devices> </emulatorCapabilities> Signed-off-by: Michal Privoznik <mprivozn@xxxxxxxxxx> --- docs/formatemulatorcaps.html.in | 63 +++++++++++++++ docs/schemas/emulatorcapability.rng | 49 ++++++++++++ src/libvirt_private.syms | 1 + src/qemu/qemu_capabilities.c | 78 ++++++++++-------- src/qemu/qemu_capabilities.h | 3 + src/qemu/qemu_capabilitiespriv.h | 55 +++++++++++++ src/qemu/qemu_driver.c | 92 ++++++++++++++++++++++ tests/Makefile.am | 18 +++++ .../viremulatorcaps-qemu-kvm-vfio.xml | 17 ++++ tests/viremulatorcapabilitiestest.c | 72 ++++++++++++++++- tests/virhostdevmock.c | 40 ++++++++++ 11 files changed, 456 insertions(+), 32 deletions(-) create mode 100644 src/qemu/qemu_capabilitiespriv.h create mode 100644 tests/viremulatorcapabilitiesdata/viremulatorcaps-qemu-kvm-vfio.xml create mode 100644 tests/virhostdevmock.c diff --git a/docs/formatemulatorcaps.html.in b/docs/formatemulatorcaps.html.in index beea1a9..ba87ff1 100644 --- a/docs/formatemulatorcaps.html.in +++ b/docs/formatemulatorcaps.html.in @@ -48,5 +48,68 @@ <dd>The domain's <a href="formatdomain.html#elementsOSBIOS">machine type</a>.</dd> </dl> + + <p>Besides these three elements, depending on the hypervisor and the driver + used other elements may occur. For example:</p> +<pre> +<arch>x86_64</arch> +<vcpu>255</vcpu> +<devices/> +</pre> + <p>Where:</p> + <dl> + <dt>arch</dt> + <dd>Denotes the <a + href="formatdomain.html#elementsOSBIOS">architecture</a> of the + domain.</dd> + + <dt>vcpu</dt> + <dd>Tells the maximum virtual CPU count supported by the underlying + hypervisor.</dd> + + <dt>devices</dt> + <dd>Contains info on supported <a href="#elementsDevices">devices</a> features</dd> + </dl> + + <h3><a name="elementsDevices">Devices</a></h3> + <p>The final set of XML elements is used to describe device capabilities + with respect to the hypervisor and host capabilities. For example:</p> +<pre> +<devices> + <hostdev> + <driver> + <enum name='driver'> + <value>kvm</value> + <value>vfio</value> + </enum> + </driver> + </hostdev> +</devices> +</pre> + <p>The aim is to keep the element names as consistent with <a + href="formatdomain.html">domain element names</a> as possible.</p> + + <h4><a name="elementsHostDev">Host device assignment</a></h4> + <p>When deciding on the most suitable device passthrough mode, look into + this part of the XML document. For instance, in the following snippet both + VFIO and legacy KVM passthrough are supported:</p> +<pre> +<devices> + <hostdev> + <driver> + <enum name='driver'> + <value>kvm</value> + <value>vfio</value> + </enum> + </driver> + </hostdev> +</devices> +</pre> + + <p>The <code>hostdev</code> element can contain the following elements:</p> + <dl> + <dt><code>driver</code></dt> + <dd>Here are the information on hostdev driver gathered.</dd> + </dl> </body> </html> diff --git a/docs/schemas/emulatorcapability.rng b/docs/schemas/emulatorcapability.rng index 2548cef..0c4a0cb 100644 --- a/docs/schemas/emulatorcapability.rng +++ b/docs/schemas/emulatorcapability.rng @@ -20,7 +20,56 @@ <element name='machine'> <text/> </element> + <optional> + <element name='arch'> + <text/> + </element> + <element name='vcpu'> + <ref name='unsignedInt'/> + </element> + <ref name='devices'/> + </optional> </interleave> </element> </define> + + <define name='devices'> + <element name='devices'> + <interleave> + <ref name='hostdev'/> + </interleave> + </element> + </define> + + <define name='hostdev'> + <element name='hostdev'> + <interleave> + <ref name='driver'/> + </interleave> + </element> + </define> + + <define name='driver'> + <element name='driver'> + <interleave> + <ref name='enum-driver'/> + </interleave> + </element> + </define> + + <define name='enum-driver'> + <element name='enum'> + <attribute name='name'> + <value>driver</value> + </attribute> + <zeroOrMore> + <element name='value'> + <choice> + <value>kvm</value> + <value>vfio</value> + </choice> + </element> + </zeroOrMore> + </element> + </define> </grammar> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 2784610..68ad1c7 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -426,6 +426,7 @@ virDomainVideoTypeFromString; virDomainVideoTypeToString; virDomainVirtioEventIdxTypeFromString; virDomainVirtioEventIdxTypeToString; +virDomainVirtTypeFromString; virDomainVirtTypeToString; virDomainWatchdogActionTypeFromString; virDomainWatchdogActionTypeToString; diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 245d6b5..68828e3 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -24,6 +24,7 @@ #include <config.h> #include "qemu_capabilities.h" +#include "qemu_capabilitiespriv.h" #include "viralloc.h" #include "vircrypto.h" #include "virlog.h" @@ -39,6 +40,7 @@ #include "virnodesuspend.h" #include "qemu_monitor.h" #include "virstring.h" +#include "virhostdev.h" #include <fcntl.h> #include <sys/stat.h> @@ -259,37 +261,6 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, ); -/* - * Update the XML parser/formatter when adding more - * information to this struct so that it gets cached - * correctly. It does not have to be ABI-stable, as - * the cache will be discarded & repopulated if the - * timestamp on the libvirtd binary changes. - */ -struct _virQEMUCaps { - virObject object; - - bool usedQMP; - - char *binary; - time_t ctime; - - virBitmapPtr flags; - - unsigned int version; - unsigned int kvmVersion; - - virArch arch; - - size_t ncpuDefinitions; - char **cpuDefinitions; - - size_t nmachineTypes; - char **machineTypes; - char **machineAliases; - unsigned int *machineMaxCpus; -}; - struct _virQEMUCapsCache { virMutex lock; virHashTablePtr binaries; @@ -3509,3 +3480,48 @@ virQEMUCapsSupportsChardev(virDomainDefPtr def, (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE && chr->targetType == VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO)); } + +int +virQEMUCapsFormatBuf(virBufferPtr buf, + void *opaque, + const char *machine) +{ + virQEMUCapsPtr qemuCaps = (virQEMUCapsPtr) opaque; + bool hostkvm = virHostdevHostSupportsPassthroughLegacy(); + bool hostvfio = virHostdevHostSupportsPassthroughVFIO(); + bool qemukvm = virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIDEVICE) || + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE); + bool qemuvfio = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI); + int maxcpus = virQEMUCapsGetMachineMaxCpus(qemuCaps, machine); + + virBufferAsprintf(buf, "<arch>%s</arch>\n", virArchToString(qemuCaps->arch)); + + if (maxcpus > 0) + virBufferAsprintf(buf, "<vcpu>%d</vcpu>\n", maxcpus); + + /* So far we want to print the rest iff both host and qemu support kvm/vfio */ + if (!((hostkvm && qemukvm) || (hostvfio && qemuvfio))) + return 0; + + virBufferAddLit(buf, "<devices>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<hostdev>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<driver>\n"); + virBufferAdjustIndent(buf, 2); + virBufferAddLit(buf, "<enum name='driver'>\n"); + virBufferAdjustIndent(buf, 2); + if (hostkvm && qemukvm) + virBufferAddLit(buf, "<value>kvm</value>\n"); + if (hostvfio && qemuvfio) + virBufferAddLit(buf, "<value>vfio</value>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</enum>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</driver>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</hostdev>\n"); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</devices>\n"); + return 0; +} diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index d755caa..121d1ba 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -307,4 +307,7 @@ int virQEMUCapsInitGuestFromBinary(virCapsPtr caps, virQEMUCapsPtr kvmbinCaps, virArch guestarch); +int virQEMUCapsFormatBuf(virBufferPtr buf, + void *opaque, + const char *machine); #endif /* __QEMU_CAPABILITIES_H__*/ diff --git a/src/qemu/qemu_capabilitiespriv.h b/src/qemu/qemu_capabilitiespriv.h new file mode 100644 index 0000000..9ed6853 --- /dev/null +++ b/src/qemu/qemu_capabilitiespriv.h @@ -0,0 +1,55 @@ +/* + * qemu_capabilitiespriv.h: private declarations for QEMU capabilities + * + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#ifndef __QEMU_CAPABILITIESPRIV_H__ +# define __QEMU_CAPABILITIESPRIV_H__ + +/* + * Update the XML parser/formatter when adding more + * information to this struct so that it gets cached + * correctly. It does not have to be ABI-stable, as + * the cache will be discarded & repopulated if the + * timestamp on the libvirtd binary changes. + */ +struct _virQEMUCaps { + virObject object; + + bool usedQMP; + + char *binary; + time_t ctime; + + virBitmapPtr flags; + + unsigned int version; + unsigned int kvmVersion; + + virArch arch; + + size_t ncpuDefinitions; + char **cpuDefinitions; + + size_t nmachineTypes; + char **machineTypes; + char **machineAliases; + unsigned int *machineMaxCpus; +}; + +#endif /* __QEMU_CAPABILITIESPRIV_H__*/ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index ac6aee5..b449770 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -95,6 +95,7 @@ #include "viraccessapicheckqemu.h" #include "storage/storage_driver.h" #include "virhostdev.h" +#include "viremulator_capabilities.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -16897,6 +16898,96 @@ qemuNodeGetFreePages(virConnectPtr conn, } +static char * +qemuConnectGetEmulatorCapabilities(virConnectPtr conn, + const char *emulatorbin, + const char *machine, + const char *virttype_str, + unsigned int flags) +{ + char *ret = NULL; + virQEMUDriverPtr driver = conn->privateData; + virQEMUCapsPtr qemuCaps = NULL; + virEmulatorCapabilitiesPtr emulCaps = NULL; + int virttype; /* virDomainVirtType */ + size_t ncanonicalMachine, i; + const char **canonicalMachine; + + virCheckFlags(0, ret); + virCheckNonNullArgGoto(emulatorbin, cleanup); + virCheckNonNullArgGoto(virttype_str, cleanup); + + if (virConnectGetEmulatorCapabilitiesEnsureACL(conn) < 0) + goto cleanup; + + if ((virttype = virDomainVirtTypeFromString(virttype_str)) < 0) { + virReportError(VIR_ERR_INVALID_ARG, + _("unknown virttype: %s"), virttype_str); + goto cleanup; + } + + if (!(qemuCaps = virQEMUCapsCacheLookup(driver->qemuCapsCache, + emulatorbin))) + goto cleanup; + + if (machine) { + const char *machine_tmp; + + if (!(machine_tmp = virQEMUCapsGetCanonicalMachine(qemuCaps, + machine))) { + /* This should never ever happen (TM) */ + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("an error that should never " + "ever happen just occurred")); + goto cleanup; + } + machine = machine_tmp; + } + + /* The virQEMUCapsGetMachineTypes expects char *** but we want to stress + * the fact that we are not going to change machine types array, so we are + * using const char *** */ + if (!(ncanonicalMachine = virQEMUCapsGetMachineTypes(qemuCaps, + (char ***) &canonicalMachine))) { + virReportError(VIR_ERR_INVALID_ARG, + _(" emulator doesn't support any machines: %s"), + emulatorbin); + goto cleanup; + } + + if (machine) { + for (i = 0; i < ncanonicalMachine; i++) { + if (STREQ(machine, canonicalMachine[i])) + break; + } + + if (i == ncanonicalMachine) { + virReportError(VIR_ERR_INVALID_ARG, + _("the machine '%s' is not supported by emulator '%s'"), + machine, emulatorbin); + goto cleanup; + } + } else { + /* The default machine type is at the first position */ + machine = canonicalMachine[0]; + } + + if (!(emulCaps = virEmulatorCapabilitiesNew(emulatorbin, + machine, + virttype))) + goto cleanup; + + virEmulatorCapabilitiesSetPrivate(emulCaps, qemuCaps, virQEMUCapsFormatBuf); + + ret = virEmulatorCapabilitiesFormat(emulCaps); + + cleanup: + virObjectUnref(emulCaps); + virObjectUnref(qemuCaps); + return ret; +} + + static virDriver qemuDriver = { .no = VIR_DRV_QEMU, .name = QEMU_DRIVER_NAME, @@ -17092,6 +17183,7 @@ static virDriver qemuDriver = { .domainGetTime = qemuDomainGetTime, /* 1.2.5 */ .domainSetTime = qemuDomainSetTime, /* 1.2.5 */ .nodeGetFreePages = qemuNodeGetFreePages, /* 1.2.6 */ + .connectGetEmulatorCapabilities = qemuConnectGetEmulatorCapabilities, /* 1.2.6 */ }; diff --git a/tests/Makefile.am b/tests/Makefile.am index e7d9f94..fcd65b5 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -388,6 +388,9 @@ if WITH_QEMU test_libraries += libqemumonitortestutils.la \ qemuxml2argvmock.la \ $(NULL) +if WITH_LINUX +test_libraries += virhostdevmock.la +endif WITH_LINUX endif WITH_QEMU if WITH_BHYVE @@ -831,6 +834,21 @@ vircaps2xmltest_LDADD = $(LDADDS) viremulatorcapabilitiestest_SOURCES = \ viremulatorcapabilitiestest.c testutils.h testutils.c viremulatorcapabilitiestest_LDADD = $(LDADDS) +if WITH_QEMU +viremulatorcapabilitiestest_LDADD += $(qemu_LDADDS) +endif WITH_QEMU + +if WITH_LINUX +virhostdevmock_la_SOURCES = \ + virhostdevmock.c +virhostdevmock_la_CFLAGS = $(AM_CFLAGS) +virhostdevmock_la_LIBADD = $(GNULIB_LIBS) \ + ../src/libvirt.la +virhostdevmock_la_LDFLAGS = -module -avoid-version \ + -rpath /evil/libtool/hack/to/force/shared/lib/creation +else ! WITH_LINUX + EXTRA_DIST += virhostdevmock.c +endif ! WITH_LINUX if WITH_LIBVIRTD libvirtdconftest_SOURCES = \ diff --git a/tests/viremulatorcapabilitiesdata/viremulatorcaps-qemu-kvm-vfio.xml b/tests/viremulatorcapabilitiesdata/viremulatorcaps-qemu-kvm-vfio.xml new file mode 100644 index 0000000..e1d4bab --- /dev/null +++ b/tests/viremulatorcapabilitiesdata/viremulatorcaps-qemu-kvm-vfio.xml @@ -0,0 +1,17 @@ +<emulatorCapabilities> + <path>/usr/bin/qemu-system-x86_64</path> + <domain>kvm</domain> + <machine>pc-i440fx-2.1</machine> + <arch>x86_64</arch> + <vcpu>255</vcpu> + <devices> + <hostdev> + <driver> + <enum name='driver'> + <value>kvm</value> + <value>vfio</value> + </enum> + </driver> + </hostdev> + </devices> +</emulatorCapabilities> diff --git a/tests/viremulatorcapabilitiestest.c b/tests/viremulatorcapabilitiestest.c index 448f0cf..25076a7 100644 --- a/tests/viremulatorcapabilitiestest.c +++ b/tests/viremulatorcapabilitiestest.c @@ -24,7 +24,8 @@ #include "testutils.h" #include "viremulator_capabilities.h" - +#include "qemu/qemu_capabilities.h" +#include "qemu/qemu_capabilitiespriv.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -96,11 +97,50 @@ test_virEmulatorCapabilitiesFormat(const void *opaque) return ret; } +static virQEMUCapsPtr +buildQEMUCaps(void) +{ + virQEMUCapsPtr qemuCaps = NULL; + + if (!(qemuCaps = virQEMUCapsNew())) + return NULL; + + if (VIR_ALLOC_N(qemuCaps->machineTypes, 1) < 0 || + VIR_ALLOC_N(qemuCaps->machineAliases, 1) < 0 || + VIR_ALLOC_N(qemuCaps->machineMaxCpus, 1) < 0) + goto error; + + if (VIR_STRDUP(qemuCaps->machineTypes[0], "pc-i440fx-2.1") < 0 || + VIR_STRDUP(qemuCaps->machineAliases[0], "pc") < 0) + goto error; + qemuCaps->machineMaxCpus[0] = 255; + qemuCaps->nmachineTypes++; + + qemuCaps->arch = VIR_ARCH_X86_64; + + virQEMUCapsSet(qemuCaps, QEMU_CAPS_PCIDEVICE); + virQEMUCapsSet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI); + + return qemuCaps; + error: + virObjectUnref(qemuCaps); + return NULL; +} + static int mymain(void) { int ret = 0; +#ifdef WITH_QEMU + virQEMUCapsPtr qemuCaps; + + if (!(qemuCaps = buildQEMUCaps())) { + fprintf(stderr, "unable to build QEMU capabilities\n"); + return EXIT_FAILURE; + } +#endif + #define DO_TEST(Filename, Emulatorbin, Machine, Type, ...) \ do { \ struct test_virEmulatorCapabilitiesFormatData data = {.filename = Filename, \ @@ -109,9 +149,39 @@ mymain(void) ret = -1; \ } while (0) +#ifdef WITH_QEMU +# define DO_TEST_QEMU(Filename, Emulatorbin, Machine, Type, ...) \ + do { \ + struct test_virEmulatorCapabilitiesFormatData data = {.filename = Filename, \ + .emulatorbin = Emulatorbin, .machine = Machine, .type = Type, \ + .privateData = qemuCaps, .formatFunc = virQEMUCapsFormatBuf, __VA_ARGS__}; \ + if (virtTestRun(Filename, test_virEmulatorCapabilitiesFormat, &data) < 0) \ + ret = -1; \ + } while (0) +#endif + DO_TEST("basic", "/bin/emulatorbin", "my-machine-type", VIR_DOMAIN_VIRT_UML); +#ifdef WITH_QEMU + /* In the following test we are expecting host to support both KVM and + * VFIO. However, this is something we must mock and therefore the test can + * be run on the Linux platform only. */ +# ifdef __linux__ + DO_TEST_QEMU("qemu-kvm-vfio", "/usr/bin/qemu-system-x86_64", + "pc-i440fx-2.1", VIR_DOMAIN_VIRT_KVM); +# else +# error Port me +# endif +#endif + +#ifdef WITH_QEMU + virObjectUnref(qemuCaps); +#endif return ret; } +#ifdef __linux__ +VIRT_TEST_MAIN_PRELOAD(mymain, abs_builddir "/.libs/virhostdevmock.so") +#else VIRT_TEST_MAIN(mymain) +#endif diff --git a/tests/virhostdevmock.c b/tests/virhostdevmock.c new file mode 100644 index 0000000..cb08034 --- /dev/null +++ b/tests/virhostdevmock.c @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2014 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <http://www.gnu.org/licenses/>. + * + * Author: Michal Privoznik <mprivozn@xxxxxxxxxx> + */ + +#include <config.h> + +#ifdef __linux__ +# include "virhostdev.h" + +bool +virHostdevHostSupportsPassthroughLegacy(void) +{ + return true; +} + +bool +virHostdevHostSupportsPassthroughVFIO(void) +{ + return true; +} + +#else +/* Nothing to override on non-__linux__ platforms */ +#endif -- 1.8.5.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list