diff options
author | Bob Gilligan <gilligan@vyatta.com> | 2011-01-16 22:44:12 -0800 |
---|---|---|
committer | Bob Gilligan <gilligan@vyatta.com> | 2011-01-16 23:07:47 -0800 |
commit | 4d155cbf37ca909daff06dd8c7119274d0861f6c (patch) | |
tree | fe9d460fb46097986fe234fddb3f9bd2935c8642 /src/pci.c | |
parent | 9405281a9e16e55af687c98a83d327946953cc09 (diff) | |
download | vyatta-biosdevname-4d155cbf37ca909daff06dd8c7119274d0861f6c.tar.gz vyatta-biosdevname-4d155cbf37ca909daff06dd8c7119274d0861f6c.zip |
Merge upstream version 0.3.4.
(cherry picked from commit 21eb1c8d6fefa5dcd3a9a800b4add06d5c30c01f)
Diffstat (limited to 'src/pci.c')
-rw-r--r-- | src/pci.c | 526 |
1 files changed, 446 insertions, 80 deletions
@@ -1,67 +1,317 @@ /* - * Copyright (c) 2006 Dell, Inc. + * Copyright (c) 2006-2010 Dell, Inc. * by Matt Domsch <Matt_Domsch@dell.com> * Licensed under the GNU General Public license, version 2. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <strings.h> #include <limits.h> -#include "pirq.h" +#include <unistd.h> +#include <dirent.h> #include <pci/pci.h> #include "pci.h" +#include "sysfs.h" +#include "dmidecode/dmidecode.h" +#include "pirq.h" -static int -is_parent_bridge(struct pci_dev *p, unsigned int target_bus) +static int read_pci_sysfs_path(char *buf, size_t bufsize, const struct pci_dev *pdev) { - unsigned int primary, secondary; + char path[PATH_MAX]; + char pci_name[16]; + ssize_t size; + unparse_pci_name(pci_name, sizeof(pci_name), pdev); + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s", pci_name); + size = readlink(path, buf, bufsize); + if (size == -1) + return 1; + return 0; +} - if ( (pci_read_word(p, PCI_HEADER_TYPE) & 0x7f) != PCI_HEADER_TYPE_BRIDGE) - return 0; +static int read_pci_sysfs_physfn(char *buf, size_t bufsize, const struct pci_dev *pdev) +{ + char path[PATH_MAX]; + char pci_name[16]; + ssize_t size; + unparse_pci_name(pci_name, sizeof(pci_name), pdev); + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/physfn", pci_name); + size = readlink(path, buf, bufsize); + if (size == -1) + return 1; + return 0; +} - primary=pci_read_byte(p, PCI_PRIMARY_BUS); - secondary=pci_read_byte(p, PCI_SECONDARY_BUS); +static int virtfn_filter(const struct dirent *dent) +{ + return (!strncmp(dent->d_name,"virtfn",6)); +} - if (secondary != target_bus) - return 0; +static int _read_virtfn_index(unsigned int *index, const char *path, const char *basename, const char *pci_name) +{ + char buf[PATH_MAX], *b; + char fullpath[PATH_MAX]; + ssize_t size; + unsigned int u=INT_MAX; + int scanned, rc=1; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", path, basename); + size = readlink(fullpath, buf, sizeof(buf)); + if (size > 0) { + /* form is ../0000:05:10.0 */ + b=buf+3; /* skip ../ */ + if (strlen(b) == strlen(pci_name) && + !strncmp(b, pci_name, strlen(pci_name))) { + scanned = sscanf(basename, "virtfn%u", &u); + if (scanned == 1) { + rc = 0; + *index = u; + } + } + } + return rc; +} - return 1; +static int read_virtfn_index(unsigned int *index, const struct pci_dev *pdev) +{ + char pci_name[16]; + char path[PATH_MAX]; + char cpath[PATH_MAX]; + struct dirent **namelist; + int n, rc=1; + + unparse_pci_name(pci_name, sizeof(pci_name), pdev); + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/physfn", pci_name); + if (realpath(path, cpath) == NULL) + return rc; + + n = scandir(cpath, &namelist, virtfn_filter, versionsort); + if (n < 0) + return rc; + else { + while (n--) { + if (rc) + rc = _read_virtfn_index(index, cpath, namelist[n]->d_name, pci_name); + free(namelist[n]); + } + free(namelist); + } + + return rc; } -static struct pci_dev * -find_parent(struct pci_access *pacc, unsigned int target_bus) +static int parse_pci_name(const char *s, int *domain, int *bus, int *dev, int *func) { - struct pci_dev *p; + int err; +/* The domain part was added in 2.6 kernels. Test for that first. */ + err = sscanf(s, "%x:%2x:%2x.%x", domain, bus, dev, func); + if (err != 4) { + err = sscanf(s, "%2x:%2x.%x", bus, dev, func); + if (err != 3) { + return 1; + } + } + return 0; +} + +static struct pci_dev * find_pdev_by_pci_name(struct pci_access *pacc, const char *s) +{ + int domain=0, bus=0, device=0, func=0; + if (parse_pci_name(s, &domain, &bus, &device, &func)) + return NULL; + return pci_get_dev(pacc, domain, bus, device, func); +} - for (p=pacc->devices; p; p=p->next) - if (is_parent_bridge(p, target_bus)) - return p; +static struct pci_device * +find_physfn(struct libbiosdevname_state *state, struct pci_device *dev) +{ + int rc; + char path[PATH_MAX]; + char *c; + struct pci_dev *pdev; + memset(path, 0, sizeof(path)); + rc = read_pci_sysfs_physfn(path, sizeof(path), dev->pci_dev); + if (rc != 0) + return NULL; + /* we get back a string like + ../0000:05:0.0 + where the last component is the parent device + */ + /* find the last backslash */ + c = rindex(path, '/'); + c++; + pdev = find_pdev_by_pci_name(state->pacc, c); + dev = find_dev_by_pci(state, pdev); + return dev; +} +static int is_same_pci(const struct pci_dev *a, const struct pci_dev *b) +{ + if (pci_domain_nr(a) == pci_domain_nr(b) && + a->bus == b->bus && + a->dev == b->dev && + a->func == b->func) + return 1; + return 0; +} + +static void try_add_vf_to_pf(struct libbiosdevname_state *state, struct pci_device *vf) +{ + struct pci_device *pf; + unsigned int index=0; + int rc; + pf = find_physfn(state, vf); + + if (!pf) + return; + list_add_tail(&vf->vfnode, &pf->vfs); + rc = read_virtfn_index(&index, vf->pci_dev); + if (!rc) { + vf->vf_index = index; + pf->is_sriov_physical_function = 1; + } + vf->pf = pf; + vf->physical_slot = pf->physical_slot; +} + +static struct pci_device * +find_parent(struct libbiosdevname_state *state, struct pci_device *dev) +{ + int rc; + char path[PATH_MAX]; + char *c; + struct pci_device *physfn; + struct pci_dev *pdev; + memset(path, 0, sizeof(path)); + /* if this device has a physfn pointer, then treat _that_ as the parent */ + physfn = find_physfn(state, dev); + if (physfn) { + dev->is_sriov_virtual_function=1; + return physfn; + } + + rc = read_pci_sysfs_path(path, sizeof(path), dev->pci_dev); + if (rc != 0) + return NULL; + /* we get back a string like + ../../../devices/pci0000:00/0000:00:09.0/0000:05:17.4 + where the last component is the device we asked for + */ + /* find the last backslash */ + c = rindex(path, '/'); + *c = '\0'; + /* find the last backslash again */ + c = rindex(path, '/'); + c++; + pdev = find_pdev_by_pci_name(state->pacc, c); + if (pdev) { + dev = find_dev_by_pci(state, pdev); + return dev; + } return NULL; } /* - * Check our parent in case the device itself isn't listed - * in the PCI IRQ Routing Table. This has a problem, as + * Check our parents in case the device itself isn't listed + * in the SMBIOS table. This has a problem, as * our parent bridge on a card may not be included - * in the $PIR table. In that case, it falls back to "unknown". + * in the SMBIOS table. In that case, it falls back to "unknown". */ -static int pci_dev_to_slot(struct routing_table *table, struct pci_access *pacc, struct pci_dev *p) +static int pci_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) +{ + struct pci_device *d = dev; + int slot = d->physical_slot; + while (d && slot == PHYSICAL_SLOT_UNKNOWN) { + d = find_parent(state, d); + if (d) + slot = d->physical_slot; + } + return slot; +} + +static int pirq_dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) +{ + struct pci_device *d = dev; + int slot; + slot = pirq_pci_dev_to_slot(state->pirq_table, d->pci_dev->bus, d->pci_dev->dev); + while (d && slot == PHYSICAL_SLOT_UNKNOWN) { + d = find_parent(state, d); + if (d) + slot = pirq_pci_dev_to_slot(state->pirq_table, d->pci_dev->bus, d->pci_dev->dev); + } + return slot; +} + +static void dev_to_slot(struct libbiosdevname_state *state, struct pci_device *dev) +{ + int slot; + slot = pci_dev_to_slot(state, dev); + if (slot == PHYSICAL_SLOT_UNKNOWN) { + slot = pirq_dev_to_slot(state, dev); + } + dev->physical_slot = slot; +} + +static char *read_pci_sysfs_label(const struct pci_dev *pdev) +{ + char path[PATH_MAX]; + char pci_name[16]; + int rc; + char *label = NULL; + + unparse_pci_name(pci_name, sizeof(pci_name), pdev); + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/label", pci_name); + rc = sysfs_read_file(path, &label); + if (rc == 0) + return label; + return NULL; +} + +static int read_pci_sysfs_index(unsigned int *index, const struct pci_dev *pdev) { + char path[PATH_MAX]; + char pci_name[16]; int rc; - rc = pirq_pci_dev_to_slot(table, p->bus, p->dev); - if (rc == INT_MAX) { - p = find_parent(pacc, p->bus); - if (p) - rc = pirq_pci_dev_to_slot(table, p->bus, p->dev); + char *indexstr = NULL; + unsigned int i; + unparse_pci_name(pci_name, sizeof(pci_name), pdev); + snprintf(path, sizeof(path), "/sys/bus/pci/devices/%s/index", pci_name); + rc = sysfs_read_file(path, &indexstr); + if (rc == 0) { + rc = sscanf(indexstr, "%u", &i); + if (rc == 1) { + *index = i; + return 0; + } + } + return 1; +} + +static void fill_pci_dev_sysfs(struct pci_device *dev, struct pci_dev *p) +{ + int rc; + unsigned int index = 0; + char *label = NULL; + char buf[PATH_MAX]; + unparse_pci_name(buf, sizeof(buf), p); + rc = read_pci_sysfs_index(&index, p); + if (!rc) { + dev->sysfs_index = index; + dev->uses_sysfs |= HAS_SYSFS_INDEX; + } + label = read_pci_sysfs_label(p); + if (label) { + dev->sysfs_label = label; + dev->uses_sysfs |= HAS_SYSFS_LABEL; } - return rc; } static void add_pci_dev(struct libbiosdevname_state *state, - struct routing_table *table, - struct pci_access *pacc, struct pci_dev *p) + struct pci_dev *p) { struct pci_device *dev; dev = malloc(sizeof(*dev)); @@ -71,9 +321,13 @@ static void add_pci_dev(struct libbiosdevname_state *state, } memset(dev, 0, sizeof(*dev)); INIT_LIST_HEAD(&dev->node); - memcpy(&dev->pci_dev, p, sizeof(*p)); /* This doesn't allow us to call PCI functions though */ - dev->physical_slot = pci_dev_to_slot(table, pacc, p); + INIT_LIST_HEAD(&dev->vfnode); + INIT_LIST_HEAD(&dev->vfs); + dev->pci_dev = p; + dev->physical_slot = PHYSICAL_SLOT_UNKNOWN; dev->class = pci_read_word(p, PCI_CLASS_DEVICE); + dev->vf_index = INT_MAX; + fill_pci_dev_sysfs(dev, p); list_add(&dev->node, &state->pci_devices); } @@ -81,60 +335,158 @@ void free_pci_devices(struct libbiosdevname_state *state) { struct pci_device *pos, *next; list_for_each_entry_safe(pos, next, &state->pci_devices, node) { + if (pos->smbios_label) + free(pos->smbios_label); + if (pos->sysfs_label) + free(pos->sysfs_label); list_del(&pos->node); free(pos); } } +static void set_pci_slots(struct libbiosdevname_state *state) +{ + struct pci_device *dev; + + list_for_each_entry(dev, &state->pci_devices, node) { + dev_to_slot(state, dev); + } +} + + +static int set_pci_slot_index(struct libbiosdevname_state *state) +{ + struct pci_device *pcidev; + int prevslot=-1; + int index=1; + + /* only iterate over the PCI devices, because the bios_device list may be incomplete due to renames happening in parallel */ + list_for_each_entry(pcidev, &state->pci_devices, node) { + if (pcidev->physical_slot == 0) /* skip embedded devices */ + continue; + if (!is_pci_network(pcidev)) /* only look at PCI network devices */ + continue; + if (pcidev->is_sriov_virtual_function) /* skip sriov VFs, they're handled later */ + continue; + if (pcidev->physical_slot != prevslot) { + index=1; + prevslot = pcidev->physical_slot; + } + else + index++; + pcidev->index_in_slot = index; + } + return 0; +} + +static int set_embedded_index(struct libbiosdevname_state *state) +{ + struct pci_device *pcidev; + int index=1; + + list_for_each_entry(pcidev, &state->pci_devices, node) { + if (pcidev->physical_slot != 0) /* skip non-embedded devices */ + continue; + if (!is_pci_network(pcidev)) /* only look at PCI network devices */ + continue; + if (pcidev->is_sriov_virtual_function) /* skip sriov VFs, they're handled later */ + continue; + pcidev->embedded_index = index; + pcidev->embedded_index_valid = 1; + index++; + } + return 0; +} + +static void set_sriov_pf_vf(struct libbiosdevname_state *state) +{ + struct pci_device *vf; + list_for_each_entry(vf, &state->pci_devices, node) { + if (!vf->is_sriov_virtual_function) + continue; + try_add_vf_to_pf(state, vf); + } +} + +/* + * This sorts the PCI devices by breadth-first domain/bus/dev/fn. + */ +static int sort_pci(const struct pci_device *a, const struct pci_device *b) +{ + + if (a->pci_dev->domain < b->pci_dev->domain) return -1; + else if (a->pci_dev->domain > b->pci_dev->domain) return 1; + + if (a->pci_dev->bus < b->pci_dev->bus) return -1; + else if (a->pci_dev->bus > b->pci_dev->bus) return 1; + + if (a->pci_dev->dev < b->pci_dev->dev) return -1; + else if (a->pci_dev->dev > b->pci_dev->dev) return 1; + + if (a->pci_dev->func < b->pci_dev->func) return -1; + else if (a->pci_dev->func > b->pci_dev->func) return 1; + + return 0; +} + +static void insertion_sort_devices(struct pci_device *a, struct list_head *list, + int (*cmp)(const struct pci_device *, const struct pci_device *)) +{ + struct pci_device *b; + list_for_each_entry(b, list, node) { + if (cmp(a, b) <= 0) { + list_move_tail(&a->node, &b->node); + return; + } + } + list_move_tail(&a->node, list); +} + +static void sort_device_list(struct libbiosdevname_state *state) +{ + LIST_HEAD(sorted_devices); + struct pci_device *dev, *tmp; + list_for_each_entry_safe(dev, tmp, &state->pci_devices, node) { + insertion_sort_devices(dev, &sorted_devices, sort_pci); + } + list_splice(&sorted_devices, &state->pci_devices); +} + int get_pci_devices(struct libbiosdevname_state *state) { struct pci_access *pacc; struct pci_dev *p; - struct pci_device *dev; struct routing_table *table; int rc=0; + table = pirq_alloc_read_table(); + if (table) + state->pirq_table = table; + pacc = pci_alloc(); if (!pacc) return rc; - + state->pacc = pacc; pci_init(pacc); pci_scan_bus(pacc); - table = pirq_alloc_read_table(); - if (!table) - goto out; - for (p=pacc->devices; p; p=p->next) { - dev = find_dev_by_pci(state, p); - if (!dev) - add_pci_dev(state, table, pacc, p); + add_pci_dev(state, p); } + /* ordering here is important */ + dmidecode_main(state); /* this will fail on Xen guests, that's OK */ + sort_device_list(state); + set_pci_slots(state); + set_embedded_index(state); + set_pci_slot_index(state); + set_sriov_pf_vf(state); - pirq_free_table(table); -out: - pci_cleanup(pacc); return rc; } - -static int parse_pci_name(const char *s, int *domain, int *bus, int *dev, int *func) -{ - int err; -/* The domain part was added in 2.6 kernels. Test for that first. */ - err = sscanf(s, "%x:%2x:%2x.%x", domain, bus, dev, func); - if (err != 4) { - err = sscanf(s, "%2x:%2x.%x", bus, dev, func); - if (err != 3) { - return 1; - } - } - return 0; -} - int unparse_pci_name(char *buf, int size, const struct pci_dev *pdev) { - return snprintf(buf, size, "%04x:%02x:%02x.%d", + return snprintf(buf, size, "%04x:%02x:%02x.%x", pci_domain_nr(pdev), pdev->bus, pdev->dev, pdev->func); } @@ -173,15 +525,19 @@ static int unparse_smbios_type41_type(char *buf, const int size, const int type) return (s-buf); } - int unparse_pci_device(char *buf, const int size, const struct pci_device *p) { char *s = buf; + struct pci_device *dev; + char pci_name[16]; s += snprintf(s, size-(s-buf), "PCI name : "); - s += unparse_pci_name(s, size-(s-buf), &p->pci_dev); + s += unparse_pci_name(s, size-(s-buf), p->pci_dev); s += snprintf(s, size-(s-buf), "\n"); s += snprintf(s, size-(s-buf), "PCI Slot : "); - s += unparse_location(s, size-(s-buf), p->physical_slot); + if (p->physical_slot < INT_MAX) + s += unparse_location(s, size-(s-buf), p->physical_slot); + else + s += snprintf(s, size-(s-buf), "Unknown"); s += snprintf(s, size-(s-buf), "\n"); if (p->smbios_type) { s += snprintf(s, size-(s-buf), "SMBIOS Device Type: "); @@ -189,17 +545,26 @@ int unparse_pci_device(char *buf, const int size, const struct pci_device *p) s += snprintf(s, size-(s-buf), "SMBIOS Instance: %u\n", p->smbios_instance); s += snprintf(s, size-(s-buf), "SMBIOS Enabled: %s\n", p->smbios_instance?"True":"False"); } - return (s-buf); -} + if (p->uses_smbios & HAS_SMBIOS_LABEL && p->smbios_label) + s += snprintf(s, size-(s-buf), "SMBIOS Label: %s\n", p->smbios_label); + if (p->uses_sysfs & HAS_SYSFS_INDEX) + s += snprintf(s, size-(s-buf), "sysfs Index: %u\n", p->sysfs_index); + if (p->uses_sysfs & HAS_SYSFS_LABEL) + s += snprintf(s, size-(s-buf), "sysfs Label: %s\n", p->sysfs_label); + if (p->physical_slot > 0 && !p->is_sriov_virtual_function) + s += snprintf(s, size-(s-buf), "Index in slot: %u\n", p->index_in_slot); + if (p->embedded_index_valid) + s += snprintf(s, size-(s-buf), "Embededed Index: %u\n", p->embedded_index); + + if (!list_empty(&p->vfs)) { + s += snprintf(s, size-(s-buf), "Virtual Functions:\n"); + list_for_each_entry(dev, &p->vfs, vfnode) { + unparse_pci_name(pci_name, sizeof(pci_name), dev->pci_dev); + s += snprintf(s, size-(s-buf), "%s\n", pci_name); + } + } -static int is_same_pci(const struct pci_dev *a, const struct pci_dev *b) -{ - if (pci_domain_nr(a) == pci_domain_nr(b) && - a->bus == b->bus && - a->dev == b->dev && - a->func == b->func) - return 1; - return 0; + return (s-buf); } struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state, @@ -207,7 +572,7 @@ struct pci_device * find_dev_by_pci(const struct libbiosdevname_state *state, { struct pci_device *dev; list_for_each_entry(dev, &state->pci_devices, node) { - if (is_same_pci(p, &dev->pci_dev)) + if (is_same_pci(p, dev->pci_dev)) return dev; } return NULL; @@ -217,17 +582,18 @@ struct pci_device * find_pci_dev_by_pci_addr(const struct libbiosdevname_state * const int domain, const int bus, const int device, const int func) { struct pci_device *dev; - struct pci_device p; + struct pci_dev p; + memset(&p, 0, sizeof(p)); #ifdef HAVE_STRUCT_PCI_DEV_DOMAIN - p.pci_dev.domain = domain; + p.domain = domain; #endif - p.pci_dev.bus = bus; - p.pci_dev.dev = device; - p.pci_dev.func = func; + p.bus = bus; + p.dev = device; + p.func = func; list_for_each_entry(dev, &state->pci_devices, node) { - if (is_same_pci(&p.pci_dev, &dev->pci_dev)) + if (is_same_pci(&p, dev->pci_dev)) return dev; } return NULL; |