conf: support manually specifying VFIO variant driver in <hostdev> XML

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@redhat.com>
Reviewed-by: Peter Krempa <pkrempa@redhat.com>
This commit is contained in:
Laine Stump 2024-01-04 20:12:51 -05:00
parent 956e1ca6aa
commit 8b93d78c83
17 changed files with 138 additions and 33 deletions

View File

@ -4508,17 +4508,39 @@ 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)`

View File

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

View File

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

View File

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

View File

@ -2638,6 +2638,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:
@ -29949,6 +29950,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:
@ -30050,6 +30052,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:

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
<network>
<name>hostdev</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward mode='hostdev' managed='yes'>
<driver model='vfio-pci-igb'/>
<pf dev='eth2'/>
</forward>
</network>

View File

@ -0,0 +1,8 @@
<network>
<name>hostdev</name>
<uuid>81ff0d90-c91e-6742-64da-4a736edb9a9b</uuid>
<forward mode='hostdev' managed='yes'>
<driver model='vfio-pci-igb'/>
<pf dev='eth2'/>
</forward>
</network>

View File

@ -146,6 +146,8 @@ mymain(void)
DO_TEST_FLAGS("passthrough-pf", VIR_NETWORK_XML_INACTIVE);
DO_TEST("hostdev");
DO_TEST_FLAGS("hostdev-pf", VIR_NETWORK_XML_INACTIVE);
DO_TEST_FLAGS("hostdev-pf-driver-model", VIR_NETWORK_XML_INACTIVE);
DO_TEST("passthrough-address-crash");
DO_TEST("nat-network-explicit-flood");
DO_TEST("host-bridge-no-flood");

View File

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

View File

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

View File

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

View File

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

View File

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