mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
nodedev: Introduce <pci-express/> to PCI devices
This new element is there to represent PCI-Express capabilities of a PCI devices, like link speed, number of lanes, etc. Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
This commit is contained in:
parent
a22a7a5ef3
commit
16ebf10f34
@ -117,6 +117,21 @@
|
||||
<code>node</code> attribute tells which NUMA node is the PCI
|
||||
device associated with.
|
||||
</dd>
|
||||
<dt><code>pci-express</code></dt>
|
||||
<dd>
|
||||
This optional element contains information on PCI Express part of
|
||||
the device. For example, it can contain a child element
|
||||
<code>link</code> which addresses the PCI Express device's link.
|
||||
While a device has it's own capabilities
|
||||
(<code>validity='cap'</code>), the actual run time capabilities
|
||||
are negotiated on the device initialization
|
||||
(<code>validity='sta'</code>). The <code>link</code> element then
|
||||
contains three attributes: <code>port</code> which says in which
|
||||
port is the device plugged in, <code>speed</code> (in
|
||||
GigaTransfers per second) and <code>width</code> for the number
|
||||
of lanes used. Since the port can't be negotiated, it's not
|
||||
exposed in <code>./pci-express/link/[@validity='sta']</code>.
|
||||
</dd>
|
||||
</dl>
|
||||
</dd>
|
||||
<dt><code>usb_device</code></dt>
|
||||
@ -305,6 +320,10 @@
|
||||
<address domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
|
||||
<address domain='0x0000' bus='0x02' slot='0x00' function='0x1'/>
|
||||
</iommuGroup>
|
||||
<pci-express>
|
||||
<link validity='cap' port='1' speed='2.5' width='1'/>
|
||||
<link validity='sta' speed='2.5' width='1'/>
|
||||
</pci-express>
|
||||
</capability>
|
||||
</device>
|
||||
</pre>
|
||||
|
@ -168,6 +168,35 @@
|
||||
</element>
|
||||
</optional>
|
||||
|
||||
<optional>
|
||||
<element name='pci-express'>
|
||||
<zeroOrMore>
|
||||
<element name='link'>
|
||||
<attribute name='validity'>
|
||||
<choice>
|
||||
<value>cap</value>
|
||||
<value>sta</value>
|
||||
</choice>
|
||||
</attribute>
|
||||
<optional>
|
||||
<attribute name='port'>
|
||||
<ref name='unsignedInt'/>
|
||||
</attribute>
|
||||
</optional>
|
||||
<optional>
|
||||
<attribute name='speed'>
|
||||
<data type="string">
|
||||
<param name="pattern">[0-9]+(.[0-9]+)?</param>
|
||||
</data>
|
||||
</attribute>
|
||||
</optional>
|
||||
<attribute name='width'>
|
||||
<ref name='unsignedInt'/>
|
||||
</attribute>
|
||||
</element>
|
||||
</zeroOrMore>
|
||||
</element>
|
||||
</optional>
|
||||
</define>
|
||||
|
||||
<define name='capusbdev'>
|
||||
|
@ -58,6 +58,9 @@ VIR_ENUM_IMPL(virNodeDevNetCap, VIR_NODE_DEV_CAP_NET_LAST,
|
||||
"80203",
|
||||
"80211")
|
||||
|
||||
VIR_ENUM_IMPL(virPCIELinkSpeed, VIR_PCIE_LINK_SPEED_LAST,
|
||||
"", "2.5", "5", "8")
|
||||
|
||||
static int
|
||||
virNodeDevCapsDefParseString(const char *xpath,
|
||||
xmlXPathContextPtr ctxt,
|
||||
@ -218,6 +221,43 @@ void virNodeDeviceObjRemove(virNodeDeviceObjListPtr devs,
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
virPCIELinkFormat(virBufferPtr buf,
|
||||
virPCIELinkPtr lnk,
|
||||
const char *attrib)
|
||||
{
|
||||
if (!lnk)
|
||||
return;
|
||||
|
||||
virBufferAsprintf(buf, "<link validity='%s'", attrib);
|
||||
if (lnk->port >= 0)
|
||||
virBufferAsprintf(buf, " port='%d'", lnk->port);
|
||||
if (lnk->speed)
|
||||
virBufferAsprintf(buf, " speed='%s'",
|
||||
virPCIELinkSpeedTypeToString(lnk->speed));
|
||||
virBufferAsprintf(buf, " width='%d'", lnk->width);
|
||||
virBufferAddLit(buf, "/>\n");
|
||||
}
|
||||
|
||||
static void
|
||||
virPCIEDeviceInfoFormat(virBufferPtr buf,
|
||||
virPCIEDeviceInfoPtr info)
|
||||
{
|
||||
if (!info->link_cap && !info->link_sta) {
|
||||
virBufferAddLit(buf, "<pci-express/>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
virBufferAddLit(buf, "<pci-express>\n");
|
||||
virBufferAdjustIndent(buf, 2);
|
||||
|
||||
virPCIELinkFormat(buf, info->link_cap, "cap");
|
||||
virPCIELinkFormat(buf, info->link_sta, "sta");
|
||||
|
||||
virBufferAdjustIndent(buf, -2);
|
||||
virBufferAddLit(buf, "</pci-express>\n");
|
||||
}
|
||||
|
||||
char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
|
||||
{
|
||||
virBuffer buf = VIR_BUFFER_INITIALIZER;
|
||||
@ -349,6 +389,8 @@ char *virNodeDeviceDefFormat(const virNodeDeviceDef *def)
|
||||
if (data->pci_dev.numa_node >= 0)
|
||||
virBufferAsprintf(&buf, "<numa node='%d'/>\n",
|
||||
data->pci_dev.numa_node);
|
||||
if (data->pci_dev.flags & VIR_NODE_DEV_CAP_FLAG_PCIE)
|
||||
virPCIEDeviceInfoFormat(&buf, data->pci_dev.pci_express);
|
||||
break;
|
||||
case VIR_NODE_DEV_CAP_USB_DEV:
|
||||
virBufferAsprintf(&buf, "<bus>%d</bus>\n", data->usb_dev.bus);
|
||||
@ -1086,6 +1128,86 @@ virNodeDevCapPCIDevIommuGroupParseXML(xmlXPathContextPtr ctxt,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
virPCIEDeviceInfoLinkParseXML(xmlXPathContextPtr ctxt,
|
||||
xmlNodePtr linkNode,
|
||||
virPCIELinkPtr lnk)
|
||||
{
|
||||
xmlNodePtr origNode = ctxt->node;
|
||||
int ret = -1, speed;
|
||||
char *speedStr = NULL, *portStr = NULL;
|
||||
|
||||
ctxt->node = linkNode;
|
||||
|
||||
if (virXPathUInt("number(./@width)", ctxt, &lnk->width) < 0) {
|
||||
virReportError(VIR_ERR_XML_DETAIL, "%s",
|
||||
_("mandatory attribute 'width' is missing or malformed"));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((speedStr = virXPathString("string(./@speed)", ctxt))) {
|
||||
if ((speed = virPCIELinkSpeedTypeFromString(speedStr)) < 0) {
|
||||
virReportError(VIR_ERR_XML_DETAIL,
|
||||
_("malformed 'speed' attribute: %s"),
|
||||
speedStr);
|
||||
goto cleanup;
|
||||
}
|
||||
lnk->speed = speed;
|
||||
}
|
||||
|
||||
if ((portStr = virXPathString("string(./@port)", ctxt))) {
|
||||
if (virStrToLong_i(portStr, NULL, 10, &lnk->port) < 0) {
|
||||
virReportError(VIR_ERR_XML_DETAIL,
|
||||
_("malformed 'port' attribute: %s"),
|
||||
portStr);
|
||||
goto cleanup;
|
||||
}
|
||||
} else {
|
||||
lnk->port = -1;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
VIR_FREE(portStr);
|
||||
VIR_FREE(speedStr);
|
||||
ctxt->node = origNode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
virPCIEDeviceInfoParseXML(xmlXPathContextPtr ctxt,
|
||||
xmlNodePtr pciExpressNode,
|
||||
virPCIEDeviceInfoPtr pci_express)
|
||||
{
|
||||
xmlNodePtr lnk, origNode = ctxt->node;
|
||||
int ret = -1;
|
||||
|
||||
ctxt->node = pciExpressNode;
|
||||
|
||||
if ((lnk = virXPathNode("./link[@validity='cap']", ctxt))) {
|
||||
if (VIR_ALLOC(pci_express->link_cap) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virPCIEDeviceInfoLinkParseXML(ctxt, lnk,
|
||||
pci_express->link_cap) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((lnk = virXPathNode("./link[@validity='sta']", ctxt))) {
|
||||
if (VIR_ALLOC(pci_express->link_sta) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (virPCIEDeviceInfoLinkParseXML(ctxt, lnk,
|
||||
pci_express->link_sta) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
cleanup:
|
||||
ctxt->node = origNode;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
||||
@ -1093,8 +1215,9 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
||||
xmlNodePtr node,
|
||||
union _virNodeDevCapData *data)
|
||||
{
|
||||
xmlNodePtr orignode, iommuGroupNode;
|
||||
xmlNodePtr orignode, iommuGroupNode, pciExpress;
|
||||
int ret = -1;
|
||||
virPCIEDeviceInfoPtr pci_express = NULL;
|
||||
|
||||
orignode = ctxt->node;
|
||||
ctxt->node = node;
|
||||
@ -1152,8 +1275,21 @@ virNodeDevCapPCIDevParseXML(xmlXPathContextPtr ctxt,
|
||||
_("invalid NUMA node ID supplied for '%s'")) < 0)
|
||||
goto out;
|
||||
|
||||
if ((pciExpress = virXPathNode("./pci-express[1]", ctxt))) {
|
||||
if (VIR_ALLOC(pci_express) < 0)
|
||||
goto out;
|
||||
|
||||
if (virPCIEDeviceInfoParseXML(ctxt, pciExpress, pci_express) < 0)
|
||||
goto out;
|
||||
|
||||
data->pci_dev.pci_express = pci_express;
|
||||
pci_express = NULL;
|
||||
data->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCIE;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
VIR_FREE(pci_express);
|
||||
ctxt->node = orignode;
|
||||
return ret;
|
||||
}
|
||||
|
@ -76,10 +76,38 @@ typedef enum {
|
||||
} virNodeDevSCSIHostCapFlags;
|
||||
|
||||
typedef enum {
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION = (1 << 0),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_PHYSICAL_FUNCTION = (1 << 0),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCI_VIRTUAL_FUNCTION = (1 << 1),
|
||||
VIR_NODE_DEV_CAP_FLAG_PCIE = (1 << 2),
|
||||
} virNodeDevPCICapFlags;
|
||||
|
||||
typedef enum {
|
||||
VIR_PCIE_LINK_SPEED_NA = 0,
|
||||
VIR_PCIE_LINK_SPEED_25,
|
||||
VIR_PCIE_LINK_SPEED_5,
|
||||
VIR_PCIE_LINK_SPEED_8,
|
||||
VIR_PCIE_LINK_SPEED_LAST
|
||||
} virPCIELinkSpeed;
|
||||
|
||||
VIR_ENUM_DECL(virPCIELinkSpeed)
|
||||
|
||||
typedef struct _virPCIELink virPCIELink;
|
||||
typedef virPCIELink *virPCIELinkPtr;
|
||||
struct _virPCIELink {
|
||||
int port;
|
||||
virPCIELinkSpeed speed;
|
||||
unsigned int width;
|
||||
};
|
||||
|
||||
typedef struct _virPCIEDeviceInfo virPCIEDeviceInfo;
|
||||
typedef virPCIEDeviceInfo *virPCIEDeviceInfoPtr;
|
||||
struct _virPCIEDeviceInfo {
|
||||
/* Not all PCI Express devices has link. For example this 'Root Complex
|
||||
* Integrated Endpoint' and 'Root Complex Event Collector' don't have it. */
|
||||
virPCIELink *link_cap; /* PCIe device link capabilities */
|
||||
virPCIELink *link_sta; /* Actually negotiated capabilities */
|
||||
};
|
||||
|
||||
typedef struct _virNodeDevCapsDef virNodeDevCapsDef;
|
||||
typedef virNodeDevCapsDef *virNodeDevCapsDefPtr;
|
||||
struct _virNodeDevCapsDef {
|
||||
@ -117,6 +145,7 @@ struct _virNodeDevCapsDef {
|
||||
size_t nIommuGroupDevices;
|
||||
unsigned int iommuGroupNumber;
|
||||
int numa_node;
|
||||
virPCIEDeviceInfoPtr pci_express;
|
||||
} pci_dev;
|
||||
struct {
|
||||
unsigned int bus;
|
||||
|
@ -426,6 +426,8 @@ static int udevProcessPCI(struct udev_device *device,
|
||||
const char *syspath = NULL;
|
||||
union _virNodeDevCapData *data = &def->caps->data;
|
||||
virPCIDeviceAddress addr;
|
||||
virPCIEDeviceInfoPtr pci_express = NULL;
|
||||
virPCIDevicePtr pciDev = NULL;
|
||||
int tmpGroup, ret = -1;
|
||||
char *p;
|
||||
int rc;
|
||||
@ -535,9 +537,41 @@ static int udevProcessPCI(struct udev_device *device,
|
||||
data->pci_dev.iommuGroupNumber = tmpGroup;
|
||||
}
|
||||
|
||||
if (!(pciDev = virPCIDeviceNew(data->pci_dev.domain,
|
||||
data->pci_dev.bus,
|
||||
data->pci_dev.slot,
|
||||
data->pci_dev.function)))
|
||||
goto out;
|
||||
|
||||
if (virPCIDeviceIsPCIExpress(pciDev) > 0) {
|
||||
if (VIR_ALLOC(pci_express) < 0)
|
||||
goto out;
|
||||
|
||||
if (virPCIDeviceHasPCIExpressLink(pciDev) > 0) {
|
||||
if (VIR_ALLOC(pci_express->link_cap) < 0 ||
|
||||
VIR_ALLOC(pci_express->link_sta) < 0)
|
||||
goto out;
|
||||
|
||||
if (virPCIDeviceGetLinkCapSta(pciDev,
|
||||
&pci_express->link_cap->port,
|
||||
&pci_express->link_cap->speed,
|
||||
&pci_express->link_cap->width,
|
||||
&pci_express->link_sta->speed,
|
||||
&pci_express->link_sta->width) < 0)
|
||||
goto out;
|
||||
|
||||
pci_express->link_sta->port = -1; /* PCIe can't negotiate port. Yet :) */
|
||||
}
|
||||
data->pci_dev.flags |= VIR_NODE_DEV_CAP_FLAG_PCIE;
|
||||
data->pci_dev.pci_express = pci_express;
|
||||
pci_express = NULL;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
virPCIDeviceFree(pciDev);
|
||||
VIR_FREE(pci_express);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
16
tests/nodedevschemadata/pci_8086_0c0c_snd_hda_intel.xml
Normal file
16
tests/nodedevschemadata/pci_8086_0c0c_snd_hda_intel.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<device>
|
||||
<name>pci_0000_00_03_0</name>
|
||||
<parent>computer</parent>
|
||||
<capability type='pci'>
|
||||
<domain>0</domain>
|
||||
<bus>0</bus>
|
||||
<slot>3</slot>
|
||||
<function>0</function>
|
||||
<product id='0x0c0c'>Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller</product>
|
||||
<vendor id='0x8086'>Intel Corporation</vendor>
|
||||
<iommuGroup number='2'>
|
||||
<address domain='0x0000' bus='0x00' slot='0x03' function='0x0'/>
|
||||
</iommuGroup>
|
||||
<pci-express/>
|
||||
</capability>
|
||||
</device>
|
26
tests/nodedevschemadata/pci_8086_4238_pcie_wireless.xml
Normal file
26
tests/nodedevschemadata/pci_8086_4238_pcie_wireless.xml
Normal file
@ -0,0 +1,26 @@
|
||||
<device>
|
||||
<name>pci_0000_03_00_0</name>
|
||||
<parent>pci_0000_00_1c_1</parent>
|
||||
<capability type='pci'>
|
||||
<domain>0</domain>
|
||||
<bus>3</bus>
|
||||
<slot>0</slot>
|
||||
<function>0</function>
|
||||
<product id='0x4238'>Centrino Ultimate-N 6300</product>
|
||||
<vendor id='0x8086'>Intel Corporation</vendor>
|
||||
<iommuGroup number='8'>
|
||||
<address domain='0x0000' bus='0x00' slot='0x1c' function='0x0'/>
|
||||
<address domain='0x0000' bus='0x00' slot='0x1c' function='0x1'/>
|
||||
<address domain='0x0000' bus='0x00' slot='0x1c' function='0x3'/>
|
||||
<address domain='0x0000' bus='0x00' slot='0x1c' function='0x4'/>
|
||||
<address domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
|
||||
<address domain='0x0000' bus='0x0d' slot='0x00' function='0x0'/>
|
||||
<address domain='0x0000' bus='0x0d' slot='0x00' function='0x1'/>
|
||||
<address domain='0x0000' bus='0x0d' slot='0x00' function='0x3'/>
|
||||
</iommuGroup>
|
||||
<pci-express>
|
||||
<link validity='cap' port='1' speed='2.5' width='1'/>
|
||||
<link validity='sta' speed='2.5' width='1'/>
|
||||
</pci-express>
|
||||
</capability>
|
||||
</device>
|
@ -88,6 +88,8 @@ mymain(void)
|
||||
DO_TEST("storage_serial_SATA_HTS721010G9SA00_MPCZ12Y0GNGWSE");
|
||||
DO_TEST("usb_device_1d6b_1_0000_00_1d_0_if0");
|
||||
DO_TEST("usb_device_1d6b_1_0000_00_1d_0");
|
||||
DO_TEST("pci_8086_4238_pcie_wireless");
|
||||
DO_TEST("pci_8086_0c0c_snd_hda_intel");
|
||||
|
||||
return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user