[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[UNIKRAFT PATCH] pci_bus: implement recursive PCI bus enumeration



The PCI bus was previously enumerated in a brute-force manner, scanning
all device on all buses (256 buses and 32 devices per bus, i.e., 8192
devices in total), resulting in tens of thousands of I/O read and write
operations at startup (~55ms overhead).

This patch implements recursive PCI bus enumeration: we first enumerate
the first bus (or the first bus*es* in the case of multiple PCI host
controllers), and recursively probe further buses as we discover further
PCI-to-PCI bridges. This reduces the overhead to ~1-2ms.

Signed-off-by: Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
---
 plat/common/pci_bus.c | 187 ++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 135 insertions(+), 52 deletions(-)

diff --git a/plat/common/pci_bus.c b/plat/common/pci_bus.c
index 4077e3c..cfc404e 100644
--- a/plat/common/pci_bus.c
+++ b/plat/common/pci_bus.c
@@ -1,6 +1,7 @@
 /* TODO: SPDX Header */
 /*
  * Authors: Simon Kuenzer <simon.kuenzer@xxxxxxxxx>
+ *          Hugo Lefeuvre <hugo.lefeuvre@xxxxxxxxx>
  *
  * Copyright (c) 2018, NEC Europe Ltd., NEC Corporation. All rights reserved.
  *
@@ -71,17 +72,19 @@ static struct pci_bus_handler ph;
 #define PCI_CONFIG_ADDR             (0xCF8)
 #define PCI_CONFIG_DATA             (0xCFC)
 
-/* 8 bits for bus number, 5 bits for devices */
+/* 8 bits for bus number, 5 bits for devices, 3 for functions */
 #define PCI_MAX_BUSES               (1 << 8)
 #define PCI_MAX_DEVICES             (1 << 5)
+#define PCI_MAX_FUNCTIONS           (1 << 3)
 
 #define PCI_BUS_SHIFT               (16)
 #define PCI_DEVICE_SHIFT            (11)
+#define PCI_FUNCTION_SHIFT          (8)
 #define PCI_ENABLE_BIT              (1 << 31)
 
 #define PCI_CONF_CLASS_ID          (0x08)
-#define PCI_CONF_CLASS_ID_SHFT     (8)
-#define PCI_CONF_CLASS_ID_MASK     (0x00FFFFFF)
+#define PCI_CONF_CLASS_ID_SHFT     (16)
+#define PCI_CONF_CLASS_ID_MASK     (0xFF00)
 
 #define PCI_CONF_VENDOR_ID          (0x00)
 #define PCI_CONF_VENDOR_ID_SHFT     (0)
@@ -95,6 +98,19 @@ static struct pci_bus_handler ph;
 #define PCI_CONF_SUBSYSVEN_ID_SHFT     (0)
 #define PCI_CONF_SUBSYSVEN_ID_MASK     (0xFFFF)
 
+#define PCI_CONF_SUBCLASS_ID          (0x08)
+#define PCI_CONF_SUBCLASS_ID_SHFT     (16)
+#define PCI_CONF_SUBCLASS_ID_MASK     (0x00FF)
+
+#define PCI_CONF_SECONDARY_BUS          (0x18)
+#define PCI_CONF_SECONDARY_BUS_SHFT     (0)
+#define PCI_CONF_SECONDARY_BUS_MASK     (0xFF00)
+
+#define PCI_HEADER_TYPE_MSB_MASK   (0x80)
+#define PCI_CONF_HEADER_TYPE       (0x00)
+#define PCI_CONF_HEADER_TYPE_SHFT  (16)
+#define PCI_CONF_HEADER_TYPE_MASK  (0xFF)
+
 #define PCI_CONF_SUBSYS_ID          (0x2c)
 #define PCI_CONF_SUBSYS_ID_SHFT     (16)
 #define PCI_CONF_SUBSYS_ID_MASK     (0xFFFF)
@@ -218,66 +234,133 @@ static inline int pci_driver_add_device(struct 
pci_driver *drv,
        return 0;
 }
 
-static int pci_probe(void)
+static void probe_bus(uint32_t);
+
+/* Probe a function. Return 1 if the function does not exist in the device, 0
+ * otherwise.
+ */
+static int probe_function(uint32_t bus, uint32_t device, uint32_t function)
 {
+       uint32_t config_addr, config_data, subclass, secondary_bus;
        struct pci_address addr;
        struct pci_device_id devid;
        struct pci_driver *drv;
-       uint32_t config_addr, config_data;
-       uint32_t bus;
-       uint8_t dev;
+
+       config_addr = (PCI_ENABLE_BIT)
+                       | (bus << PCI_BUS_SHIFT)
+                       | (device << PCI_DEVICE_SHIFT)
+                       | (function << PCI_FUNCTION_SHIFT);
+
+       outl(PCI_CONFIG_ADDR, config_addr);
+       config_data = inl(PCI_CONFIG_DATA);
+
+       devid.vendor_id = config_data & PCI_DEVICE_ID_MASK;
+       if (devid.vendor_id == PCI_INVALID_ID) {
+               /* Device doesn't exist */
+               return 1;
+       }
+
+       addr.domain   = 0x0;
+       addr.bus      = bus;
+       addr.devid    = device;
+       addr.function = function;
+
+       /* These I/O reads could be batched, but it practice this does not
+        * appear to make the code more performant.
+        */
+       PCI_CONF_READ(uint32_t, &devid.class_id,
+                       config_addr, CLASS_ID);
+       PCI_CONF_READ(uint32_t, &subclass,
+                       config_addr, SUBCLASS_ID);
+       PCI_CONF_READ(uint16_t, &devid.vendor_id,
+                       config_addr, VENDOR_ID);
+       PCI_CONF_READ(uint16_t, &devid.device_id,
+                       config_addr, DEVICE_ID);
+       PCI_CONF_READ(uint16_t, &devid.subsystem_device_id,
+                       config_addr, SUBSYS_ID);
+       PCI_CONF_READ(uint16_t, &devid.subsystem_vendor_id,
+                       config_addr, SUBSYSVEN_ID);
+
+       uk_pr_info("PCI %02x:%02x.%02x (%04x %04x:%04x): ",
+                  (int) addr.bus,
+                  (int) addr.devid,
+                  (int) addr.function,
+                  (int) devid.class_id,
+                  (int) devid.vendor_id,
+                  (int) devid.device_id);
+
+       drv = pci_find_driver(&devid);
+       if (!drv) {
+               uk_pr_info("<no driver>\n");
+       } else {
+               uk_pr_info("driver %p\n", drv);
+               pci_driver_add_device(drv, &addr, &devid);
+       }
+
+       /* 0x06 = Bridge Device, 0x04 = PCI-to-PCI bridge */
+       if ((devid.class_id == 0x06) && (subclass == 0x04) ) {
+               PCI_CONF_READ(uint32_t, &secondary_bus,
+                               config_addr, SECONDARY_BUS);
+               probe_bus(secondary_bus);
+       }
+
+       return 0;
+}
+
+/* Recursive PCI enumeration: this function is called recursively by
+ * probe_function upon discovering PCI-to-PCI bridges.
+ */
+static void probe_bus(uint32_t bus)
+{
+       uint32_t config_addr, device, header_type, function = 0;
+
+       for (device = 0; device < PCI_MAX_DEVICES; ++device) {
+               if (!probe_function(bus, device, function))
+                       continue;
+
+               config_addr = (PCI_ENABLE_BIT);
+               PCI_CONF_READ(uint32_t, &header_type,
+                               config_addr, HEADER_TYPE);
+
+               /* Is this a multi-function device? */
+               if ((header_type & PCI_HEADER_TYPE_MSB_MASK) == 0)
+                       continue;
+
+               /* Check remaining functions */
+               for (function = 1; function < PCI_MAX_FUNCTIONS; function++)
+                       probe_function(bus, device, function);
+       }
+}
+
+static int pci_probe(void)
+{
+       uint32_t config_addr, function, header_type, vendor_id;
 
        uk_pr_debug("Probe PCI\n");
 
-       for (bus = 0; bus < PCI_MAX_BUSES; ++bus) {
-               for (dev = 0; dev < PCI_MAX_DEVICES; ++dev) {
-                       config_addr = (PCI_ENABLE_BIT)
-                                       | (bus << PCI_BUS_SHIFT)
-                                       | (dev << PCI_DEVICE_SHIFT);
-
-                       outl(PCI_CONFIG_ADDR, config_addr);
-                       config_data = inl(PCI_CONFIG_DATA);
-
-                       /* TODO: Retrieve the device identfier */
-                       addr.domain   = 0x0;
-                       addr.bus      = bus;
-                       addr.devid    = dev;
-                        /* TODO: Retrieve the function */
-                       addr.function = 0x0;
-
-                       devid.vendor_id = config_data & PCI_DEVICE_ID_MASK;
-                       if (devid.vendor_id == PCI_INVALID_ID) {
-                               /* Device doesn't exist */
-                               continue;
-                       }
+       config_addr = (PCI_ENABLE_BIT);
+       PCI_CONF_READ(uint32_t, &header_type,
+                       config_addr, HEADER_TYPE);
+
+       if ((header_type & PCI_HEADER_TYPE_MSB_MASK) == 0) {
+               /* Single PCI host controller */
+               probe_bus(0);
+       } else {
+               /* Multiple PCI host controllers */
+               for (function = 0; function < PCI_MAX_FUNCTIONS; function++) {
+                       config_addr = (PCI_ENABLE_BIT) |
+                                       (function << PCI_FUNCTION_SHIFT);
 
-                       PCI_CONF_READ(uint32_t, &devid.class_id,
-                                       config_addr, CLASS_ID);
-                       PCI_CONF_READ(uint16_t, &devid.vendor_id,
+                       PCI_CONF_READ(uint32_t, &vendor_id,
                                        config_addr, VENDOR_ID);
-                       PCI_CONF_READ(uint16_t, &devid.device_id,
-                                       config_addr, DEVICE_ID);
-                       PCI_CONF_READ(uint16_t, &devid.subsystem_device_id,
-                                       config_addr, SUBSYS_ID);
-                       PCI_CONF_READ(uint16_t, &devid.subsystem_vendor_id,
-                                       config_addr, SUBSYSVEN_ID);
-
-                       uk_pr_info("PCI %02x:%02x.%02x (%04x %04x:%04x): ",
-                                  (int) addr.bus,
-                                  (int) addr.devid,
-                                  (int) addr.function,
-                                  (int) devid.class_id,
-                                  (int) devid.vendor_id,
-                                  (int) devid.device_id);
-                       drv = pci_find_driver(&devid);
-                       if (!drv) {
-                               uk_pr_info("<no driver>\n");
-                               continue;
-                       }
-                       uk_pr_info("driver %p\n", drv);
-                       pci_driver_add_device(drv, &addr, &devid);
+
+                       if (vendor_id != PCI_INVALID_ID)
+                               break;
+
+                       probe_bus(function);
                }
        }
+
        return 0;
 }
 
-- 
2.7.4




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.