We've been exposing slot information in /sys/bus/pci/slots for a long time now (as long as a hotplug driver or slot detection driver like pci_slot is loaded). Let's make life better for our users and display that information in lspci. If slot entries appear in /sys/bus/pci/slots/, correlate them to PCI devices, and display the information when lspci -v is issued. If no slot entries appear in sysfs (due to no modules loaded), do nothing. Now you'll see sample output like the following: 23:01.1 Class 0c04: Device 10df:fd00 (rev 01) Subsystem: Device 10df:fd00 Physical Slot: 3 Flags: bus master, 66MHz, medium devsel, latency 248, IRQ 60 ... Signed-off-by: Alex Chiang <achiang@xxxxxx> --- v2 -> v3: - fixed indentation / style issues - fixed another memory leak - move pci_mfree(d->phy_slot) to more obvious location - set PCI_FILL_PHYS_SLOT for all devices after looking for slot info v1-> v2: - sysfs-specific fill_info - fix memory leak - document in man page lib/access.c | 1 lib/pci.h | 2 + lib/sysfs.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- lspci.c | 7 +++++- lspci.man | 4 +++ 5 files changed, 76 insertions(+), 2 deletions(-) --- diff --git a/lib/access.c b/lib/access.c index 23a821c..691df39 100644 --- a/lib/access.c +++ b/lib/access.c @@ -59,6 +59,7 @@ void pci_free_dev(struct pci_dev *d) if (d->methods->cleanup_dev) d->methods->cleanup_dev(d); pci_free_caps(d); + pci_mfree(d->phy_slot); pci_mfree(d); } diff --git a/lib/pci.h b/lib/pci.h index 452e1d8..be62297 100644 --- a/lib/pci.h +++ b/lib/pci.h @@ -129,6 +129,7 @@ struct pci_dev { pciaddr_t rom_base_addr; /* Expansion ROM base address */ pciaddr_t rom_size; /* Expansion ROM size */ struct pci_cap *first_cap; /* List of capabilities */ + char *phy_slot; /* Physical slot */ /* Fields used internally: */ struct pci_access *access; @@ -162,6 +163,7 @@ int pci_fill_info(struct pci_dev *, int flags) PCI_ABI; /* Fill in device inform #define PCI_FILL_CLASS 32 #define PCI_FILL_CAPS 64 #define PCI_FILL_EXT_CAPS 128 +#define PCI_FILL_PHYS_SLOT 256 #define PCI_FILL_RESCAN 0x10000 void pci_setup_cache(struct pci_dev *, u8 *cache, int len) PCI_ABI; diff --git a/lib/sysfs.c b/lib/sysfs.c index ca43562..5949ff1 100644 --- a/lib/sysfs.c +++ b/lib/sysfs.c @@ -177,6 +177,68 @@ static void sysfs_scan(struct pci_access *a) closedir(dir); } +static void +sysfs_fill_slots(struct pci_dev *d) +{ + struct pci_access *a = d->access; + char dirname[1024]; + DIR *dir; + struct dirent *entry; + int n; + + n = snprintf(dirname, sizeof(dirname), "%s/slots", sysfs_name(a)); + if (n < 0 || n >= (int) sizeof(dirname)) + a->error("Directory name too long"); + dir = opendir(dirname); + if (!dir) + a->error("Cannot open %s", dirname); + while ((entry = readdir(dir))) + { + char namebuf[OBJNAMELEN], buf[16]; + FILE *file; + unsigned int dom, bus, dev; + struct pci_dev *pd; + int n = snprintf(namebuf, OBJNAMELEN, "%s/%s/%s", dirname, entry->d_name, "address"); + + /* ".", ".." or a special non-device perhaps */ + if (entry->d_name[0] == '.') + continue; + + if (n < 0 || n >= OBJNAMELEN) + d->access->error("File name too long"); + file = fopen(namebuf, "r"); + if (!file) + a->error("Cannot open %s: %s", namebuf, strerror(errno)); + if (!fgets(buf, sizeof(buf), file)) + break; + if (sscanf(buf, "%x:%x:%x", &dom, &bus, &dev) < 3) + a->error("sysfs_scan: Couldn't parse entry address %s", buf); + for (pd = a->devices; pd; pd = pd->next) + { + if (dom == pd->domain && bus == pd->bus && dev == pd->dev && !pd->phy_slot) + { + pd->phy_slot = pci_malloc(a, strlen(entry->d_name) + 1); + sprintf(pd->phy_slot, "%s", entry->d_name); + } + pd->known_fields |= PCI_FILL_PHYS_SLOT; + } + fclose(file); + } + closedir(dir); +} + +static int +sysfs_fill_info(struct pci_dev *d, int flags) +{ + int ret; + + ret = pci_generic_fill_info(d, flags); + if (flags & PCI_FILL_PHYS_SLOT && !(d->known_fields & PCI_FILL_PHYS_SLOT)) + sysfs_fill_slots(d); + + return ret; +} + /* Intent of the sysfs_setup() caller */ enum { @@ -306,7 +368,7 @@ struct pci_methods pm_linux_sysfs = { sysfs_init, sysfs_cleanup, sysfs_scan, - pci_generic_fill_info, + sysfs_fill_info, sysfs_read, sysfs_write, sysfs_read_vpd, diff --git a/lspci.c b/lspci.c index d872c75..9958ef6 100644 --- a/lspci.c +++ b/lspci.c @@ -140,7 +140,7 @@ scan_device(struct pci_dev *p) d->config_cached += 64; } pci_setup_cache(p, d->config, d->config_cached); - pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES); + pci_fill_info(p, PCI_FILL_IDENT | PCI_FILL_CLASS | PCI_FILL_IRQ | PCI_FILL_BASES | PCI_FILL_ROM_BASE | PCI_FILL_SIZES | PCI_FILL_PHYS_SLOT); return d; } @@ -685,6 +685,9 @@ show_verbose(struct device *d) return; } + if (p->phy_slot) + printf("\tPhysical Slot: %s\n", p->phy_slot); + if (verbose > 1) { printf("\tControl: I/O%c Mem%c BusMaster%c SpecCycle%c MemWINV%c VGASnoop%c ParErr%c Stepping%c SERR%c FastB2B%c DisINTx%c\n", @@ -850,6 +853,8 @@ show_machine(struct device *d) printf("SDevice:\t%s\n", pci_lookup_name(pacc, sdbuf, sizeof(sdbuf), PCI_LOOKUP_SUBSYSTEM | PCI_LOOKUP_DEVICE, p->vendor_id, p->device_id, sv_id, sd_id)); } + if (p->phy_slot) + printf("PhySlot:\t%s\n", p->phy_slot); if (c = get_conf_byte(d, PCI_REVISION_ID)) printf("Rev:\t%02x\n", c); if (c = get_conf_byte(d, PCI_CLASS_PROG)) diff --git a/lspci.man b/lspci.man index dfddacd..441bc2b 100644 --- a/lspci.man +++ b/lspci.man @@ -286,6 +286,10 @@ Name of the subsystem vendor (optional). Name of the subsystem (optional). .TP +.B PhySlot +The physical slot where the device resides (optional, Linux only). + +.TP .B Rev Revision number (optional). -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html