diff --git a/docs/formatnode.html.in b/docs/formatnode.html.in
index d82bd3eb0e..5932811129 100644
--- a/docs/formatnode.html.in
+++ b/docs/formatnode.html.in
@@ -117,6 +117,21 @@
node
attribute tells which NUMA node is the PCI
device associated with.
+
pci-express
+
+ This optional element contains information on PCI Express part of
+ the device. For example, it can contain a child element
+ link
which addresses the PCI Express device's link.
+ While a device has it's own capabilities
+ (validity='cap'
), the actual run time capabilities
+ are negotiated on the device initialization
+ (validity='sta'
). The link
element then
+ contains three attributes: port
which says in which
+ port is the device plugged in, speed
(in
+ GigaTransfers per second) and width
for the number
+ of lanes used. Since the port can't be negotiated, it's not
+ exposed in ./pci-express/link/[@validity='sta']
.
+
usb_device
@@ -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>
diff --git a/docs/schemas/nodedev.rng b/docs/schemas/nodedev.rng
index 505f913354..b6de20183a 100644
--- a/docs/schemas/nodedev.rng
+++ b/docs/schemas/nodedev.rng
@@ -168,6 +168,35 @@
+
+
+
+
+
+
+ cap
+ sta
+
+
+
+
+
+
+
+
+
+
+ [0-9]+(.[0-9]+)?
+
+
+
+
+
+
+
+
+
+
diff --git a/src/conf/node_device_conf.c b/src/conf/node_device_conf.c
index 12e40e66ff..cb85914f67 100644
--- a/src/conf/node_device_conf.c
+++ b/src/conf/node_device_conf.c
@@ -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, " 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, " \n");
+ return;
+ }
+
+ virBufferAddLit(buf, "\n");
+ virBufferAdjustIndent(buf, 2);
+
+ virPCIELinkFormat(buf, info->link_cap, "cap");
+ virPCIELinkFormat(buf, info->link_sta, "sta");
+
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, " \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, " \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, "%d \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;
}
diff --git a/src/conf/node_device_conf.h b/src/conf/node_device_conf.h
index d1311ddb7b..61d2e19371 100644
--- a/src/conf/node_device_conf.h
+++ b/src/conf/node_device_conf.h
@@ -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;
diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
index a8e74e4577..bb6a0b90fa 100644
--- a/src/node_device/node_device_udev.c
+++ b/src/node_device/node_device_udev.c
@@ -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;
}
diff --git a/tests/nodedevschemadata/pci_8086_0c0c_snd_hda_intel.xml b/tests/nodedevschemadata/pci_8086_0c0c_snd_hda_intel.xml
new file mode 100644
index 0000000000..8e8990073d
--- /dev/null
+++ b/tests/nodedevschemadata/pci_8086_0c0c_snd_hda_intel.xml
@@ -0,0 +1,16 @@
+
+ pci_0000_00_03_0
+ computer
+
+ 0
+ 0
+ 3
+ 0
+ Xeon E3-1200 v3/4th Gen Core Processor HD Audio Controller
+ Intel Corporation
+
+
+
+
+
+
diff --git a/tests/nodedevschemadata/pci_8086_4238_pcie_wireless.xml b/tests/nodedevschemadata/pci_8086_4238_pcie_wireless.xml
new file mode 100644
index 0000000000..18172e900b
--- /dev/null
+++ b/tests/nodedevschemadata/pci_8086_4238_pcie_wireless.xml
@@ -0,0 +1,26 @@
+
+ pci_0000_03_00_0
+ pci_0000_00_1c_1
+
+ 0
+ 3
+ 0
+ 0
+ Centrino Ultimate-N 6300
+ Intel Corporation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/nodedevxml2xmltest.c b/tests/nodedevxml2xmltest.c
index 9390bf5de8..fe1fd8823c 100644
--- a/tests/nodedevxml2xmltest.c
+++ b/tests/nodedevxml2xmltest.c
@@ -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;
}