This patch makes it possible to manually specify which VFIO variant
driver to use for PCI hostdev device assignment, so that, e.g. you
could force use of a VFIO "variant" driver, with e.g.
<driver model='mlx5_vfio_pci'/>
or alternately to force use of the generic vfio-pci driver with
<driver model='vfio-pci'/>
when libvirt would have normally (after applying a subsequent patch)
found a "better match" for a device in the active kernel's
modules.alias file. (The main potential use of this manual override
would probably be to work around a bug in a new VFIO variant driver by
temporarily not using that driver).
Signed-off-by: Laine Stump <laine(a)redhat.com>
---
Change from V2: use new <driver model='blah'/> instead of repurposing
existing <driver name='blah'/>
docs/formatdomain.rst | 54 +++++++++++++------
docs/formatnetwork.rst | 21 ++++----
src/conf/device_conf.c | 10 ++++
src/conf/device_conf.h | 4 ++
src/conf/domain_conf.c | 3 ++
src/conf/network_conf.c | 2 +
src/conf/schemas/basictypes.rng | 21 +++++---
src/conf/virnetworkportdef.c | 1 +
src/network/bridge_driver.c | 1 +
tests/networkxml2xmlin/hostdev-pf-old.xml | 8 +++
tests/networkxml2xmlout/hostdev-pf-old.xml | 8 +++
tests/networkxml2xmltest.c | 6 +++
.../hostdev-vfio.x86_64-latest.args | 5 +-
tests/qemuxml2argvdata/hostdev-vfio.xml | 18 +++++++
.../hostdev-vfio.x86_64-latest.xml | 23 +++++++-
.../plug-hostdev-pci-unmanaged.xml | 1 -
.../plug-hostdev-pci.xml | 1 -
17 files changed, 148 insertions(+), 39 deletions(-)
create mode 100644 tests/networkxml2xmlin/hostdev-pf-old.xml
create mode 100644 tests/networkxml2xmlout/hostdev-pf-old.xml
diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst
index c08033f940..36c74d9eae 100644
--- a/docs/formatdomain.rst
+++ b/docs/formatdomain.rst
@@ -4500,24 +4500,46 @@ or:
an error. See the `Device Addresses`_ section for more details on the address
element.
``driver``
- PCI devices can have an optional ``driver`` subelement that specifies which
- backend driver to use for PCI device assignment. Use the ``name`` attribute
- to select either "vfio" (for the new VFIO device assignment backend, which
is
- compatible with UEFI SecureBoot) or "kvm" (the legacy device assignment
- handled directly by the KVM kernel module) :since:`Since 1.0.5 (QEMU and KVM
- only, requires kernel 3.6 or newer)` . When specified, device assignment will
- fail if the requested method of device assignment isn't available on the
- host. When not specified, the default is "vfio" on systems where the VFIO
- driver is available and loaded, and "kvm" on older systems, or those where
- the VFIO driver hasn't been loaded :since:`Since 1.1.3` (prior to that the
- default was always "kvm").
+ PCI hostdev devices can have an optional ``driver`` subelement that
+ specifies which host driver to bind to the device when preparing it
+ for assignment to a guest. :since:`Since 10.0.0 (useful for QEMU and
+ KVM only)`. This is done by setting the ``<driver>`` element's ``model``
+ attribute, for example::
+
+ ...
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver model='vfio-pci-igb'/>
+ ...
+
+ tells libvirt to bind the driver "vfio-pci-igb" to the device on
+ the host before handing it off to QEMU for assignment to the
+ guest. Normally libvirt will bind the device to the "best match"
+ VFIO-type driver that it finds in the kernel's modules.alias file
+ (based on matching the corresponding fields of the device's
+ modalias file in sysfs) or to the generic "vfio-pci" driver if no
+ better match is found (vfio-pci is always used prior to libvirt
+ 10.0.0), but in cases when the correct driver isn't listed in
+ modules.alias then the desired device-specific driver can be forced
+ by setting driver name, or if the device-specific driver that is
+ found is "problematic" in some way, the generic vfio-pci driver
+ similarly be forced.
+
+ (Note: :since:`Since 1.0.5, the ``name`` attribute has been
+ described to be used to select the type of PCI device assignment
+ ("vfio", "kvm", or "xen"), but those values have been
mostly
+ useless, since the type of device assignment is actually determined
+ by which hypservisor is in use. This means that you may
+ occasionally see ``<driver name='vfio'/>`` or ``<driver
+ name='xen'/>`` in a domain's status XML, or more rarely in config,
+ but those specific values are essentially ignored.)
+
``readonly``
- Indicates that the device is readonly, only supported by SCSI host device
- now. :since:`Since 1.0.6 (QEMU and KVM only)`
+ Indicates that the device is readonly, only supported by SCSI host
+ device now. :since:`Since 1.0.6 (QEMU and KVM only)`
``shareable``
- If present, this indicates the device is expected to be shared between
- domains (assuming the hypervisor and OS support this). Only supported by SCSI
- host device. :since:`Since 1.0.6`
+ If present, this indicates the device is expected to be shared
+ between domains (assuming the hypervisor and OS support this). Only
+ supported by SCSI host device. :since:`Since 1.0.6`
Note: Although ``shareable`` was introduced :since:`in 1.0.6` , it did not
work as as expected until :since:`1.2.2` .
diff --git a/docs/formatnetwork.rst b/docs/formatnetwork.rst
index 5d300a035e..d4181ac029 100644
--- a/docs/formatnetwork.rst
+++ b/docs/formatnetwork.rst
@@ -315,17 +315,14 @@ to the physical LAN (if at all).
guest, use the traditional ``<hostdev>`` device definition. :since:` Since
0.10.0`
- To force use of a particular type of device assignment, a <forward
- type='hostdev'> interface can have an optional ``driver`` sub-element
with
- a ``name`` attribute set to either "vfio" (VFIO is a new method of
device
- assignment that is compatible with UEFI Secure Boot) or "kvm" (the
legacy
- device assignment handled directly by the KVM kernel module) :since:`Since
- 1.0.5 (QEMU and KVM only, requires kernel 3.6 or newer)` . When specified,
- device assignment will fail if the requested method of device assignment
- isn't available on the host. When not specified, the default is
"vfio" on
- systems where the VFIO driver is available and loaded, and "kvm" on
older
- systems, or those where the VFIO driver hasn't been loaded :since:`Since
- 1.1.3` (prior to that the default was always "kvm").
+ To force use of a particular device-specific VFIO driver when
+ assigning the devices to a guest, a <forward type='hostdev'>
+ interface can have an optional ``driver`` sub-element with a
+ ``model`` attribute set to the name of the driver to use
+ :since:`Since 10.0.0 (QEMU only)`. When not specified, libvirt
+ will attempt to find a suitable VFIO variant driver for the
+ device, and if not found it will use the generic driver
+ "vfio-pci".
Note that this "intelligent passthrough" of network devices is very
similar to the functionality of a standard ``<hostdev>`` device, the
@@ -337,7 +334,7 @@ to the physical LAN (if at all).
to the guest domain), or if you are using a version of libvirt older than
0.10.0, you should use a standard ``<hostdev>`` device definition in the
domain's configuration to assign the device to the guest instead of
- defining an ``<interface type='network'>`` pointing to a
+ defining an ``<interface type='network'>`` pointing to a
network with ``<forward mode='hostdev'/>``.
As mentioned above, a ``<forward>`` element can have multiple
``<interface>``
diff --git a/src/conf/device_conf.c b/src/conf/device_conf.c
index 68a8c7690a..f840efc1b5 100644
--- a/src/conf/device_conf.c
+++ b/src/conf/device_conf.c
@@ -67,6 +67,7 @@ virDeviceHostdevPCIDriverInfoParseXML(xmlNodePtr node,
return -1;
}
+ driver->model = virXMLPropString(node, "model");
return 0;
}
@@ -90,11 +91,20 @@ virDeviceHostdevPCIDriverInfoFormat(virBuffer *buf,
virBufferAsprintf(&driverAttrBuf, " name='%s'",
driverName);
}
+ virBufferEscapeString(&driverAttrBuf, " model='%s'",
driver->model);
+
virXMLFormatElement(buf, "driver", &driverAttrBuf, NULL);
return 0;
}
+void
+virDeviceHostdevPCIDriverInfoClear(virDeviceHostdevPCIDriverInfo *driver)
+{
+ VIR_FREE(driver->model);
+}
+
+
static int
virZPCIDeviceAddressParseXML(xmlNodePtr node,
virPCIDeviceAddress *addr)
diff --git a/src/conf/device_conf.h b/src/conf/device_conf.h
index 0b3f17a3aa..2d674ecd85 100644
--- a/src/conf/device_conf.h
+++ b/src/conf/device_conf.h
@@ -46,6 +46,7 @@ VIR_ENUM_DECL(virDeviceHostdevPCIDriverName);
struct _virDeviceHostdevPCIDriverInfo {
virDeviceHostdevPCIDriverName name;
+ char *model;
};
typedef enum {
@@ -192,6 +193,9 @@ int virDeviceHostdevPCIDriverInfoParseXML(xmlNodePtr node,
int virDeviceHostdevPCIDriverInfoFormat(virBuffer *buf,
const virDeviceHostdevPCIDriverInfo *driver);
+void virDeviceHostdevPCIDriverInfoPostParse(virDeviceHostdevPCIDriverInfo *driver);
+void virDeviceHostdevPCIDriverInfoClear(virDeviceHostdevPCIDriverInfo *driver);
+
void virDomainDeviceInfoClear(virDomainDeviceInfo *info);
void virDomainDeviceInfoFree(virDomainDeviceInfo *info);
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 0f137543e0..02fd5815cc 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -2636,6 +2636,7 @@ virDomainHostdevDefClear(virDomainHostdevDef *def)
VIR_FREE(def->source.subsys.u.scsi_host.wwpn);
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
+ virDeviceHostdevPCIDriverInfoClear(&def->source.subsys.u.pci.driver);
g_clear_pointer(&def->source.subsys.u.pci.origstates, virBitmapFree);
break;
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
@@ -29898,6 +29899,7 @@ virDomainNetDefActualFromNetworkPort(virDomainNetDef *iface,
actual->data.hostdev.def.source.subsys.type =
VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI;
actual->data.hostdev.def.source.subsys.u.pci.addr =
port->plug.hostdevpci.addr;
actual->data.hostdev.def.source.subsys.u.pci.driver.name =
port->plug.hostdevpci.driver.name;
+ actual->data.hostdev.def.source.subsys.u.pci.driver.model =
g_strdup(port->plug.hostdevpci.driver.model);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
@@ -29999,6 +30001,7 @@ virDomainNetDefActualToNetworkPort(virDomainDef *dom,
port->plug.hostdevpci.managed =
virTristateBoolFromBool(actual->data.hostdev.def.managed);
port->plug.hostdevpci.addr =
actual->data.hostdev.def.source.subsys.u.pci.addr;
port->plug.hostdevpci.driver.name =
actual->data.hostdev.def.source.subsys.u.pci.driver.name;
+ port->plug.hostdevpci.driver.model =
g_strdup(actual->data.hostdev.def.source.subsys.u.pci.driver.model);
break;
case VIR_DOMAIN_NET_TYPE_CLIENT:
diff --git a/src/conf/network_conf.c b/src/conf/network_conf.c
index 890c16b3b1..5c781d06af 100644
--- a/src/conf/network_conf.c
+++ b/src/conf/network_conf.c
@@ -229,6 +229,8 @@ virNetworkForwardDefClear(virNetworkForwardDef *def)
{
size_t i;
+ virDeviceHostdevPCIDriverInfoClear(&def->driver);
+
for (i = 0; i < def->npfs && def->pfs; i++)
virNetworkForwardPfDefClear(&def->pfs[i]);
VIR_FREE(def->pfs);
diff --git a/src/conf/schemas/basictypes.rng b/src/conf/schemas/basictypes.rng
index 8d5f4475ca..b65d210091 100644
--- a/src/conf/schemas/basictypes.rng
+++ b/src/conf/schemas/basictypes.rng
@@ -658,13 +658,20 @@
<define name="hostdevDriver">
<element name="driver">
- <attribute name="name">
- <choice>
- <value>kvm</value>
- <value>vfio</value>
- <value>xen</value>
- </choice>
- </attribute>
+ <optional>
+ <attribute name="name">
+ <choice>
+ <value>kvm</value>
+ <value>vfio</value>
+ <value>xen</value>
+ </choice>
+ </attribute>
+ </optional>
+ <optional>
+ <attribute name="model">
+ <ref name="genericName"/>
+ </attribute>
+ </optional>
<empty/>
</element>
</define>
diff --git a/src/conf/virnetworkportdef.c b/src/conf/virnetworkportdef.c
index 49d00b2ea6..64db63ae66 100644
--- a/src/conf/virnetworkportdef.c
+++ b/src/conf/virnetworkportdef.c
@@ -64,6 +64,7 @@ virNetworkPortDefFree(virNetworkPortDef *def)
break;
case VIR_NETWORK_PORT_PLUG_TYPE_HOSTDEV_PCI:
+ virDeviceHostdevPCIDriverInfoClear(&def->plug.hostdevpci.driver);
break;
case VIR_NETWORK_PORT_PLUG_TYPE_LAST:
diff --git a/src/network/bridge_driver.c b/src/network/bridge_driver.c
index d156333626..9921c7cd14 100644
--- a/src/network/bridge_driver.c
+++ b/src/network/bridge_driver.c
@@ -3931,6 +3931,7 @@ networkAllocatePort(virNetworkObj *obj,
}
port->plug.hostdevpci.addr = dev->device.pci;
port->plug.hostdevpci.driver.name = netdef->forward.driver.name;
+ port->plug.hostdevpci.driver.model =
g_strdup(netdef->forward.driver.model);
port->plug.hostdevpci.managed =
virTristateBoolFromBool(netdef->forward.managed);
if (port->virtPortProfile) {
diff --git a/tests/networkxml2xmlin/hostdev-pf-old.xml
b/tests/networkxml2xmlin/hostdev-pf-old.xml
new file mode 100644
index 0000000000..5b8f59858c
--- /dev/null
+++ b/tests/networkxml2xmlin/hostdev-pf-old.xml
@@ -0,0 +1,8 @@
+<network>
+ <name>hostdev</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward mode='hostdev' managed='yes'>
+ <driver name='vfio'/>
+ <pf dev='eth2'/>
+ </forward>
+</network>
diff --git a/tests/networkxml2xmlout/hostdev-pf-old.xml
b/tests/networkxml2xmlout/hostdev-pf-old.xml
new file mode 100644
index 0000000000..5b8f59858c
--- /dev/null
+++ b/tests/networkxml2xmlout/hostdev-pf-old.xml
@@ -0,0 +1,8 @@
+<network>
+ <name>hostdev</name>
+ <uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
+ <forward mode='hostdev' managed='yes'>
+ <driver name='vfio'/>
+ <pf dev='eth2'/>
+ </forward>
+</network>
diff --git a/tests/networkxml2xmltest.c b/tests/networkxml2xmltest.c
index b0814c7529..928f28b579 100644
--- a/tests/networkxml2xmltest.c
+++ b/tests/networkxml2xmltest.c
@@ -146,6 +146,12 @@ mymain(void)
DO_TEST_FLAGS("passthrough-pf", VIR_NETWORK_XML_INACTIVE);
DO_TEST("hostdev");
DO_TEST_FLAGS("hostdev-pf", VIR_NETWORK_XML_INACTIVE);
+
+ /* libvirt pre-9.9.0 used "name='vfio'" which should be
+ * automatically translated to "type='vfio'" by new parser
+ */
+ DO_TEST_FLAGS("hostdev-pf-old", VIR_NETWORK_XML_INACTIVE);
+
DO_TEST("passthrough-address-crash");
DO_TEST("nat-network-explicit-flood");
DO_TEST("host-bridge-no-flood");
diff --git a/tests/qemuxml2argvdata/hostdev-vfio.x86_64-latest.args
b/tests/qemuxml2argvdata/hostdev-vfio.x86_64-latest.args
index c1f9258844..8529cde269 100644
--- a/tests/qemuxml2argvdata/hostdev-vfio.x86_64-latest.args
+++ b/tests/qemuxml2argvdata/hostdev-vfio.x86_64-latest.args
@@ -32,6 +32,9 @@ XDG_CONFIG_HOME=/var/lib/libvirt/qemu/domain--1-QEMUGuest2/.config \
-device
'{"driver":"ide-hd","bus":"ide.0","unit":0,"drive":"libvirt-1-format","id":"ide0-0-0","bootindex":1}'
\
-audiodev
'{"id":"audio1","driver":"none"}' \
-device
'{"driver":"vfio-pci","host":"0000:06:12.1","id":"hostdev0","bus":"pci.0","addr":"0x2"}'
\
--device
'{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x3"}'
\
+-device
'{"driver":"vfio-pci","host":"0000:06:12.2","id":"hostdev1","bus":"pci.0","addr":"0x3"}'
\
+-device
'{"driver":"vfio-pci","host":"0000:06:12.3","id":"hostdev2","bus":"pci.0","addr":"0x4"}'
\
+-device
'{"driver":"vfio-pci","host":"0000:06:12.4","id":"hostdev3","bus":"pci.0","addr":"0x5"}'
\
+-device
'{"driver":"virtio-balloon-pci","id":"balloon0","bus":"pci.0","addr":"0x6"}'
\
-sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny \
-msg timestamp=on
diff --git a/tests/qemuxml2argvdata/hostdev-vfio.xml
b/tests/qemuxml2argvdata/hostdev-vfio.xml
index a03870f6e0..812bac2cfd 100644
--- a/tests/qemuxml2argvdata/hostdev-vfio.xml
+++ b/tests/qemuxml2argvdata/hostdev-vfio.xml
@@ -29,6 +29,24 @@
<address domain='0x0000' bus='0x06' slot='0x12'
function='0x1'/>
</source>
</hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x2'/>
+ </source>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver model='vfio-pci-igb'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x3'/>
+ </source>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio' model='vfio-pci-igb'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x4'/>
+ </source>
+ </hostdev>
<memballoon model='virtio'/>
</devices>
</domain>
diff --git a/tests/qemuxml2xmloutdata/hostdev-vfio.x86_64-latest.xml
b/tests/qemuxml2xmloutdata/hostdev-vfio.x86_64-latest.xml
index 3915b515f2..2042ba6c16 100644
--- a/tests/qemuxml2xmloutdata/hostdev-vfio.x86_64-latest.xml
+++ b/tests/qemuxml2xmloutdata/hostdev-vfio.x86_64-latest.xml
@@ -39,8 +39,29 @@
</source>
<address type='pci' domain='0x0000' bus='0x00'
slot='0x02' function='0x0'/>
</hostdev>
- <memballoon model='virtio'>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x2'/>
+ </source>
<address type='pci' domain='0x0000' bus='0x00'
slot='0x03' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver model='vfio-pci-igb'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x3'/>
+ </source>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x04' function='0x0'/>
+ </hostdev>
+ <hostdev mode='subsystem' type='pci' managed='yes'>
+ <driver name='vfio' model='vfio-pci-igb'/>
+ <source>
+ <address domain='0x0000' bus='0x06' slot='0x12'
function='0x4'/>
+ </source>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x05' function='0x0'/>
+ </hostdev>
+ <memballoon model='virtio'>
+ <address type='pci' domain='0x0000' bus='0x00'
slot='0x06' function='0x0'/>
</memballoon>
</devices>
</domain>
diff --git a/tests/virnetworkportxml2xmldata/plug-hostdev-pci-unmanaged.xml
b/tests/virnetworkportxml2xmldata/plug-hostdev-pci-unmanaged.xml
index da5f568031..fa974affee 100644
--- a/tests/virnetworkportxml2xmldata/plug-hostdev-pci-unmanaged.xml
+++ b/tests/virnetworkportxml2xmldata/plug-hostdev-pci-unmanaged.xml
@@ -6,7 +6,6 @@
</owner>
<mac address='52:54:00:7b:35:93'/>
<plug type='hostdev-pci' managed='no'>
- <driver name='vfio'/>
<address domain='0x0001' bus='0x02' slot='0x03'
function='0x4'/>
</plug>
</networkport>
diff --git a/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
index cc4419f3fd..7354e1d48c 100644
--- a/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
+++ b/tests/virnetworkportxml2xmldata/plug-hostdev-pci.xml
@@ -6,7 +6,6 @@
</owner>
<mac address='52:54:00:7b:35:93'/>
<plug type='hostdev-pci' managed='yes'>
- <driver name='vfio'/>
<address domain='0x0001' bus='0x02' slot='0x03'
function='0x4'/>
</plug>
</networkport>
--
2.43.0