mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-25 10:03:49 +03:00
nodedev: Expose PCI header type
If we expose this information, which is one byte in every PCI config file, we let all mgmt apps know whether the device itself is an endpoint or not so it's easier for them to decide whether such device can be passed through into a VM (endpoint) or not (*-bridge). Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1317531 Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
This commit is contained in:
parent
0d8f45246a
commit
d77ffb6876
@ -97,27 +97,38 @@
|
|||||||
<dd>
|
<dd>
|
||||||
This optional element can occur multiple times. If it
|
This optional element can occur multiple times. If it
|
||||||
exists, it has a mandatory <code>type</code> attribute
|
exists, it has a mandatory <code>type</code> attribute
|
||||||
which will be set to
|
which will be set to:
|
||||||
either <code>physical_function</code>
|
<dl>
|
||||||
or <code>virtual_functions</code>. If the type
|
<dt><code>physical_function</code></dt>
|
||||||
is <code>physical_function</code>, there will be a
|
<dd>
|
||||||
single <code>address</code> subelement which contains
|
That means there will be a single <code>address</code>
|
||||||
the PCI address of the SRIOV Physical Function (PF)
|
subelement which contains the PCI address of the SRIOV
|
||||||
that is the parent of this device (and this device is,
|
Physical Function (PF) that is the parent of this device
|
||||||
by implication, an SRIOV Virtual Function (VF)). If
|
(and this device is, by implication, an SRIOV Virtual
|
||||||
the type is <code>virtual_functions</code>, then this
|
Function (VF)).
|
||||||
device is an SRIOV PF, and the capability element will
|
</dd>
|
||||||
have a list of <code>address</code> subelements, one
|
<dt><code>virtual_function</code></dt>
|
||||||
for each VF on this PF. If the host system supports
|
<dd>
|
||||||
reporting it (via the "sriov_maxvfs" file in the
|
In this case this device is an SRIOV PF, and the capability
|
||||||
device's sysfs directory) the capability element will
|
element will have a list of <code>address</code>
|
||||||
also have an attribute named <code>maxCount</code>
|
subelements, one for each VF on this PF. If the host system
|
||||||
which is the maximum number of SRIOV VFs supported by
|
supports reporting it (via the "sriov_maxvfs" file in the
|
||||||
this device, which could be higher than the number of
|
device's sysfs directory) the capability element will also
|
||||||
VFs that are curently active <span class="since">since
|
have an attribute named <code>maxCount</code> which is the
|
||||||
1.3.0</span>; in this case, even if there are
|
maximum number of SRIOV VFs supported by this device, which
|
||||||
currently no active VFs the virtual_functions
|
could be higher than the number of VFs that are curently
|
||||||
capabililty will still be shown.
|
active <span class="since">since 1.3.0</span>; in this case,
|
||||||
|
even if there are currently no active VFs the
|
||||||
|
virtual_functions capabililty will still be shown.
|
||||||
|
</dd>
|
||||||
|
<dt><code>pci-bridge</code> or <code>cardbus-bridge</code></dt>
|
||||||
|
<dd>
|
||||||
|
This shows merely that the lower 7 bits of PCI header type
|
||||||
|
have either value of 1 or 2 respectively. Usually this
|
||||||
|
means such device cannot be used for PCI passthrough.
|
||||||
|
<span class="since">Since 1.3.3</span>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><code>numa</code></dt>
|
<dt><code>numa</code></dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
@ -168,6 +168,17 @@
|
|||||||
</element>
|
</element>
|
||||||
</optional>
|
</optional>
|
||||||
|
|
||||||
|
<optional>
|
||||||
|
<element name='capability'>
|
||||||
|
<attribute name='type'>
|
||||||
|
<choice>
|
||||||
|
<value>pci-bridge</value>
|
||||||
|
<value>cardbus-bridge</value>
|
||||||
|
</choice>
|
||||||
|
</attribute>
|
||||||
|
</element>
|
||||||
|
</optional>
|
||||||
|
|
||||||
<optional>
|
<optional>
|
||||||
<element name='pci-express'>
|
<element name='pci-express'>
|
||||||
<zeroOrMore>
|
<zeroOrMore>
|
||||||
|
@ -402,6 +402,12 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
|
|||||||
if (data->pci_dev.numa_node >= 0)
|
if (data->pci_dev.numa_node >= 0)
|
||||||
virBufferAsprintf(&buf, "<numa node='%d'/>\n",
|
virBufferAsprintf(&buf, "<numa node='%d'/>\n",
|
||||||
data->pci_dev.numa_node);
|
data->pci_dev.numa_node);
|
||||||
|
|
||||||
|
if (data->pci_dev.hdrType) {
|
||||||
|
virBufferAsprintf(&buf, "<capability type='%s'/>\n",
|
||||||
|
virPCIHeaderTypeToString(data->pci_dev.hdrType));
|
||||||
|
}
|
||||||
|
|
||||||
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE)
|
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE)
|
||||||
virPCIEDeviceInfoFormat(&buf, data->pci_dev.pci_express);
|
virPCIEDeviceInfoFormat(&buf, data->pci_dev.pci_express);
|
||||||
break;
|
break;
|
||||||
@ -1272,6 +1278,7 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
|||||||
xmlNodePtr orignode, iommuGroupNode, pciExpress;
|
xmlNodePtr orignode, iommuGroupNode, pciExpress;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
virPCIEDeviceInfoPtr pci_express = NULL;
|
virPCIEDeviceInfoPtr pci_express = NULL;
|
||||||
|
char *tmp = NULL;
|
||||||
|
|
||||||
orignode = ctxt->node;
|
orignode = ctxt->node;
|
||||||
ctxt->node = node;
|
ctxt->node = node;
|
||||||
@ -1329,6 +1336,18 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
|||||||
_("invalid NUMA node ID supplied for '%s'")) < 0)
|
_("invalid NUMA node ID supplied for '%s'")) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if ((tmp = virXPathString("string(./capability[1]/@type)", ctxt))) {
|
||||||
|
int hdrType = virPCIHeaderTypeFromString(tmp);
|
||||||
|
|
||||||
|
if (hdrType <= 0) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unknown PCI header type '%s'"), tmp);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->pci_dev.hdrType = hdrType;
|
||||||
|
}
|
||||||
|
|
||||||
if ((pciExpress = virXPathNode("./pci-express[1]", ctxt))) {
|
if ((pciExpress = virXPathNode("./pci-express[1]", ctxt))) {
|
||||||
if (VIR_ALLOC(pci_express) < 0)
|
if (VIR_ALLOC(pci_express) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@ -1343,6 +1362,7 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
out:
|
out:
|
||||||
|
VIR_FREE(tmp);
|
||||||
virPCIEDeviceInfoFree(pci_express);
|
virPCIEDeviceInfoFree(pci_express);
|
||||||
ctxt->node = orignode;
|
ctxt->node = orignode;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -119,6 +119,7 @@ typedef struct _virNodeDevCapData {
|
|||||||
unsigned int iommuGroupNumber;
|
unsigned int iommuGroupNumber;
|
||||||
int numa_node;
|
int numa_node;
|
||||||
virPCIEDeviceInfoPtr pci_express;
|
virPCIEDeviceInfoPtr pci_express;
|
||||||
|
int hdrType; /* enum virPCIHeaderType or -1 */
|
||||||
} pci_dev;
|
} pci_dev;
|
||||||
struct {
|
struct {
|
||||||
unsigned int bus;
|
unsigned int bus;
|
||||||
|
@ -2010,11 +2010,14 @@ virPCIDeviceSetUsedBy;
|
|||||||
virPCIDeviceUnbind;
|
virPCIDeviceUnbind;
|
||||||
virPCIDeviceWaitForCleanup;
|
virPCIDeviceWaitForCleanup;
|
||||||
virPCIEDeviceInfoFree;
|
virPCIEDeviceInfoFree;
|
||||||
|
virPCIGetHeaderType;
|
||||||
virPCIGetNetName;
|
virPCIGetNetName;
|
||||||
virPCIGetPhysicalFunction;
|
virPCIGetPhysicalFunction;
|
||||||
virPCIGetVirtualFunctionIndex;
|
virPCIGetVirtualFunctionIndex;
|
||||||
virPCIGetVirtualFunctionInfo;
|
virPCIGetVirtualFunctionInfo;
|
||||||
virPCIGetVirtualFunctions;
|
virPCIGetVirtualFunctions;
|
||||||
|
virPCIHeaderTypeFromString;
|
||||||
|
virPCIHeaderTypeToString;
|
||||||
virPCIIsVirtualFunction;
|
virPCIIsVirtualFunction;
|
||||||
virPCIStubDriverTypeFromString;
|
virPCIStubDriverTypeFromString;
|
||||||
virPCIStubDriverTypeToString;
|
virPCIStubDriverTypeToString;
|
||||||
|
@ -506,6 +506,9 @@ static int udevProcessPCI(struct udev_device *device,
|
|||||||
|
|
||||||
/* We need to be root to read PCI device configs */
|
/* We need to be root to read PCI device configs */
|
||||||
if (priv->privileged) {
|
if (priv->privileged) {
|
||||||
|
if (virPCIGetHeaderType(pciDev, &data->pci_dev.hdrType) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
if (virPCIDeviceIsPCIExpress(pciDev) > 0) {
|
if (virPCIDeviceIsPCIExpress(pciDev) > 0) {
|
||||||
if (VIR_ALLOC(pci_express) < 0)
|
if (VIR_ALLOC(pci_express) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -62,6 +62,12 @@ VIR_ENUM_IMPL(virPCIStubDriver, VIR_PCI_STUB_DRIVER_LAST,
|
|||||||
"vfio-pci", /* VFIO */
|
"vfio-pci", /* VFIO */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
VIR_ENUM_IMPL(virPCIHeader, VIR_PCI_HEADER_LAST,
|
||||||
|
"endpoint",
|
||||||
|
"pci-bridge",
|
||||||
|
"cardbus-bridge",
|
||||||
|
);
|
||||||
|
|
||||||
struct _virPCIDevice {
|
struct _virPCIDevice {
|
||||||
virPCIDeviceAddress address;
|
virPCIDeviceAddress address;
|
||||||
|
|
||||||
@ -2883,6 +2889,33 @@ virPCIDeviceGetLinkCapSta(virPCIDevicePtr dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
*hdrType = -1;
|
||||||
|
|
||||||
|
if ((fd = virPCIDeviceConfigOpen(dev, true)) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
type = virPCIDeviceRead8(dev, fd, PCI_HEADER_TYPE);
|
||||||
|
|
||||||
|
virPCIDeviceConfigClose(dev, fd);
|
||||||
|
|
||||||
|
type &= PCI_HEADER_TYPE_MASK;
|
||||||
|
if (type >= VIR_PCI_HEADER_LAST) {
|
||||||
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
||||||
|
_("Unknown PCI header type '%d'"), type);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*hdrType = type;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev)
|
virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev)
|
||||||
{
|
{
|
||||||
|
@ -62,6 +62,16 @@ typedef enum {
|
|||||||
|
|
||||||
VIR_ENUM_DECL(virPCIELinkSpeed)
|
VIR_ENUM_DECL(virPCIELinkSpeed)
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
VIR_PCI_HEADER_ENDPOINT = 0,
|
||||||
|
VIR_PCI_HEADER_PCI_BRIDGE,
|
||||||
|
VIR_PCI_HEADER_CARDBUS_BRIDGE,
|
||||||
|
|
||||||
|
VIR_PCI_HEADER_LAST
|
||||||
|
} virPCIHeaderType;
|
||||||
|
|
||||||
|
VIR_ENUM_DECL(virPCIHeader)
|
||||||
|
|
||||||
typedef struct _virPCIELink virPCIELink;
|
typedef struct _virPCIELink virPCIELink;
|
||||||
typedef virPCIELink *virPCIELinkPtr;
|
typedef virPCIELink *virPCIELinkPtr;
|
||||||
struct _virPCIELink {
|
struct _virPCIELink {
|
||||||
@ -223,6 +233,8 @@ int virPCIDeviceGetLinkCapSta(virPCIDevicePtr dev,
|
|||||||
unsigned int *sta_speed,
|
unsigned int *sta_speed,
|
||||||
unsigned int *sta_width);
|
unsigned int *sta_width);
|
||||||
|
|
||||||
|
int virPCIGetHeaderType(virPCIDevicePtr dev, int *hdrType);
|
||||||
|
|
||||||
void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev);
|
void virPCIEDeviceInfoFree(virPCIEDeviceInfoPtr dev);
|
||||||
|
|
||||||
#endif /* __VIR_PCI_H__ */
|
#endif /* __VIR_PCI_H__ */
|
||||||
|
15
tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml
Normal file
15
tests/nodedevschemadata/pci_0000_00_02_0_header_type.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<device>
|
||||||
|
<name>pci_0000_00_02_0</name>
|
||||||
|
<parent>computer</parent>
|
||||||
|
<capability type='pci'>
|
||||||
|
<domain>0</domain>
|
||||||
|
<bus>0</bus>
|
||||||
|
<slot>2</slot>
|
||||||
|
<function>0</function>
|
||||||
|
<product id='0x0416'>4th Gen Core Processor Integrated Graphics Controller</product>
|
||||||
|
<vendor id='0x8086'>Intel Corporation</vendor>
|
||||||
|
<iommuGroup number='1'>
|
||||||
|
<address domain='0x0000' bus='0x00' slot='0x02' function='0x0'/>
|
||||||
|
</iommuGroup>
|
||||||
|
</capability>
|
||||||
|
</device>
|
20
tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml
Normal file
20
tests/nodedevschemadata/pci_0000_00_1c_0_header_type.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<device>
|
||||||
|
<name>pci_0000_00_1c_0</name>
|
||||||
|
<parent>computer</parent>
|
||||||
|
<capability type='pci'>
|
||||||
|
<domain>0</domain>
|
||||||
|
<bus>0</bus>
|
||||||
|
<slot>28</slot>
|
||||||
|
<function>0</function>
|
||||||
|
<product id='0x8c10'>8 Series/C220 Series Chipset Family PCI Express Root Port #1</product>
|
||||||
|
<vendor id='0x8086'>Intel Corporation</vendor>
|
||||||
|
<iommuGroup number='8'>
|
||||||
|
<address domain='0x0000' bus='0x00' slot='0x1c' function='0x0'/>
|
||||||
|
</iommuGroup>
|
||||||
|
<capability type='pci-bridge'/>
|
||||||
|
<pci-express>
|
||||||
|
<link validity='cap' port='1' speed='5' width='1'/>
|
||||||
|
<link validity='sta' speed='2.5' width='1'/>
|
||||||
|
</pci-express>
|
||||||
|
</capability>
|
||||||
|
</device>
|
@ -91,6 +91,8 @@ mymain(void)
|
|||||||
DO_TEST("usb_device_1d6b_1_0000_00_1d_0");
|
DO_TEST("usb_device_1d6b_1_0000_00_1d_0");
|
||||||
DO_TEST("pci_8086_4238_pcie_wireless");
|
DO_TEST("pci_8086_4238_pcie_wireless");
|
||||||
DO_TEST("pci_8086_0c0c_snd_hda_intel");
|
DO_TEST("pci_8086_0c0c_snd_hda_intel");
|
||||||
|
DO_TEST("pci_0000_00_02_0_header_type");
|
||||||
|
DO_TEST("pci_0000_00_1c_0_header_type");
|
||||||
|
|
||||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user