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:
Michal Privoznik 2014-05-15 10:13:45 +02:00
parent a22a7a5ef3
commit 16ebf10f34
8 changed files with 294 additions and 3 deletions

View File

@ -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 @@
&lt;address domain='0x0000' bus='0x02' slot='0x00' function='0x0'/&gt;
&lt;address domain='0x0000' bus='0x02' slot='0x00' function='0x1'/&gt;
&lt;/iommuGroup&gt;
&lt;pci-express&gt;
&lt;link validity='cap' port='1' speed='2.5' width='1'/&gt;
&lt;link validity='sta' speed='2.5' width='1'/&gt;
&lt;/pci-express&gt;
&lt;/capability&gt;
&lt;/device&gt;
</pre>

View File

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

View File

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

View File

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

View File

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

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

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

View File

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