dm: PTM: Get # of children of a pci bridge or pci bus

Add functionality to build and cache pci hierarchy, which are mainly
    used to retrieve # of children of pci bridge or pci bus.

    get_device_count_on_bus(): get # of child devices on a pci bus
    get_device_count_on_bridge(): recursively get # of child devices on a pci bridge
    scan_pci(): build and cache pci hierarchy
    pci_find_root_port(): find root port of a pci device
    clean_pci_cache(): free pci cache
    scan_pci_test(): test of scan pci hierarchy (disabled)

Tracked-On: #5915
Signed-off-by: Rong Liu <rong.l.liu@intel.com>
Acked-by: Yu Wang <yu1.wang@intel.com>
This commit is contained in:
Rong Liu 2021-04-28 21:15:08 +00:00 committed by wenlingz
parent d3185f8a0c
commit a7b0d9848f
2 changed files with 238 additions and 0 deletions

View File

@ -16,6 +16,35 @@
#include "pci_util.h"
#include "log.h"
#define MAX_LEN (PCI_BUSMAX + 1)
struct pci_device_info {
bool is_bridge;
int primary_bus;
int secondary_bus;
int subordinate_bus;
uint16_t bdf;
struct pci_device_info *parent; /* pointer to its parent bridge */
struct pci_device_info *clist; /* children list */
/* cache of all pci devices
* FIXME: remove PCI_DEVICE_Q. To cleanup pci device cache:
* remove children, then remove parents
*/
TAILQ_ENTRY(pci_device_info) PCI_DEVICE_Q;
};
TAILQ_HEAD(ALL_PCI_DEVICES, pci_device_info);
static struct ALL_PCI_DEVICES pci_device_q;
static int pci_scanned;
/* map bus to bridge
* assume only one pci domain in the system so the
* bus # in the pci hierarchy is unique.
*/
static struct pci_device_info *bus_map[MAX_LEN];
/* find position of specified pci capability register*/
int pci_find_cap(struct pci_device *pdev, const int cap_id)
{
@ -101,3 +130,204 @@ bool is_bridge(struct pci_device *pdev)
return ((hdr_type & PCIM_HDRTYPE) == PCIM_HDRTYPE_BRIDGE);
}
/* add new_child to parent's child list */
static inline void
add_child(struct pci_device_info *new_child,
struct pci_device_info *parent)
{
new_child->clist = parent->clist;
parent->clist = new_child;
// it is important to reset the new_child's clist to null
new_child->clist = NULL;
}
/* get number of children on bus */
int get_device_count_on_bus(int bus)
{
int count = 0;
struct pci_device_info *bridge = bus_map[bus];
struct pci_device_info *next;
if (bridge == NULL)
return 0;
next = bridge->clist;
while (next != NULL) {
if (next->is_bridge)
count += get_device_count_on_bridge(bridge);
else
count++;
next = next->clist;
}
return count;
}
/* get number of children on bridge */
int get_device_count_on_bridge(const struct pci_device_info *bridge_info)
{
int count = 0;
int i, ret;
if (bridge_info == NULL)
return 0;
for (i = bridge_info->secondary_bus; i <= bridge_info->subordinate_bus; i++) {
ret = get_device_count_on_bus(i);
pr_info("%s: device_count on bus[%x]=%d.\n", __func__, i, ret);
count += ret;
}
return count;
}
/* build and cache pci hierarchy in one pci domain
* @pre: only one pci domain in the system. Need revisit
* if there is more than one domain in the system.
*/
int scan_pci(void)
{
int error = 0;
struct pci_device_iterator *iter;
struct pci_device *dev;
int i;
TAILQ_INIT(&pci_device_q);
pci_scanned = 1;
iter = pci_slot_match_iterator_create(NULL);
while ((dev = pci_device_next(iter)) != NULL) {
struct pci_device_info *pci_info = calloc(1, sizeof(struct pci_device_info));
if (pci_info == NULL) {
error = -ENOMEM;
pr_err("%s, %s: calloc", __func__, __LINE__);
goto done;
}
pci_info->parent = NULL;
pci_info->clist = NULL;
pci_info->bdf = PCI_BDF(dev->bus, dev->dev, dev->func);
TAILQ_INSERT_TAIL(&pci_device_q, pci_info, PCI_DEVICE_Q);
if (pci_info->bdf == 0) { /* host bridge */
bus_map[0] = pci_info;
continue;
}
if (is_bridge(dev)) {
pci_info->is_bridge = true;
pci_device_get_bridge_buses(dev, &(pci_info->primary_bus),
&(pci_info->secondary_bus), &(pci_info->subordinate_bus));
// put bus and bridge into bus_map
for (i = pci_info->secondary_bus; i <= pci_info->subordinate_bus; i++)
bus_map[i] = pci_info;
} else
pci_info->is_bridge = false;
// set its parent bridge
pci_info->parent = bus_map[dev->bus];
// add itself to its parent bridge's child list
add_child(pci_info, pci_info->parent);
}
done:
if (error < 0)
clean_pci_cache();
pci_iterator_destroy(iter);
return error;
}
/* find bdf of pci device pdev's root port */
struct pci_device * pci_find_root_port(const struct pci_device *pdev)
{
struct pci_device *bdev;
int bus, dev, func;
struct pci_device_info *brdg = bus_map[pdev->bus];
for (;;) {
if (brdg == NULL)
return NULL;
bus = (brdg->bdf >> 8) & 0xFF;
dev = (brdg->bdf >> 3) & 0x1F;
func = brdg->bdf & 0x7;
bdev = pci_device_find_by_slot(0, bus, dev, func);
if (is_root_port(bdev))
return bdev;
brdg = brdg->parent;
}
return NULL;
}
/* clean cache of pci device: free all allocated memory */
void clean_pci_cache(void)
{
if (!pci_scanned)
return;
while (!TAILQ_EMPTY(&pci_device_q)) {
struct pci_device_info *p = TAILQ_FIRST(&pci_device_q);
TAILQ_REMOVE(&pci_device_q, p, PCI_DEVICE_Q);
}
pci_scanned = 0;
}
#ifdef SCAN_PCI_TEST
static void
scan_pci_test(void)
{
struct pci_device_info *device_info;
struct pci_device *pdev;
int bdf, parent_bdf;
printf("%s enter.\n", __func__);
if (!pci_scanned) {
printf("%s: no pci hierarchy to test.\n", __func__);
return;
}
TAILQ_FOREACH(device_info, &pci_device_q, PCI_DEVICE_Q) {
bdf = device_info->bdf;
if (bdf == 0) {
printf("%s: host bridge.\n", __func__);
continue;
}
parent_bdf = device_info->parent->bdf;
printf("%s: [%x:%x.%x]: [%x:%x.%x]\n", __func__, (bdf >> 8) & 0xFF, (bdf >> 3) & 0x1F, bdf & 0x7,
(parent_bdf >> 8) & 0xFF, (parent_bdf >> 3) & 0x1F, parent_bdf & 0x7);
pdev = pci_device_find_by_slot(0, (bdf >> 8) & 0xFF, (bdf >> 3) & 0x1F, bdf & 0x7);
if (is_bridge(pdev)) {
struct pci_device_info *child;
child = device_info->clist;
while(child != NULL) {
printf("\t\t: child: [%x:%x.%x]\n", (child->bdf >> 8) & 0xFF, (child->bdf >> 3) & 0x1F, child->bdf & 0x7);
child = child->clist;
}
}
}
}
#endif

View File

@ -11,10 +11,18 @@
#include <stdbool.h>
#include "pciaccess.h"
struct pci_device_info;
int pci_find_cap(struct pci_device *pdev, const int cap_id);
int pci_find_ext_cap(struct pci_device *pdev, int cap_id);
int pci_get_pcie_type(struct pci_device *dev);
bool is_root_port(struct pci_device *pdev);
bool is_bridge(struct pci_device *pdev);
int get_device_count_on_bus(int bus);
int get_device_count_on_bridge(const struct pci_device_info *bridge_info);
int scan_pci(void);
struct pci_device * pci_find_root_port(const struct pci_device *pdev);
void clean_pci_cache(void);
#endif