root/opal/mca/hwloc/hwloc201/hwloc/hwloc/topology-pci.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hwloc_look_pci
  2. hwloc_pci_component_instantiate
  3. hwloc_pci_component_init

   1 /*
   2  * Copyright © 2009 CNRS
   3  * Copyright © 2009-2018 Inria.  All rights reserved.
   4  * Copyright © 2009-2011, 2013 Université Bordeaux
   5  * Copyright © 2014 Cisco Systems, Inc.  All rights reserved.
   6  * Copyright © 2015      Research Organization for Information Science
   7  *                       and Technology (RIST). All rights reserved.
   8  * See COPYING in top-level directory.
   9  */
  10 
  11 #include <private/autogen/config.h>
  12 #include <hwloc.h>
  13 #include <hwloc/helper.h>
  14 #include <hwloc/plugins.h>
  15 
  16 /* private headers allowed for convenience because this plugin is built within hwloc */
  17 #include <private/debug.h>
  18 #include <private/misc.h>
  19 
  20 #include <stdio.h>
  21 #include <fcntl.h>
  22 #include <string.h>
  23 #include <assert.h>
  24 #include <stdarg.h>
  25 #ifdef HWLOC_LINUX_SYS
  26 #include <dirent.h>
  27 #endif
  28 
  29 #include <pciaccess.h>
  30 
  31 #ifndef PCI_HEADER_TYPE
  32 #define PCI_HEADER_TYPE 0x0e
  33 #endif
  34 #ifndef PCI_HEADER_TYPE_BRIDGE
  35 #define PCI_HEADER_TYPE_BRIDGE 1
  36 #endif
  37 
  38 #ifndef PCI_CLASS_DEVICE
  39 #define PCI_CLASS_DEVICE 0x0a
  40 #endif
  41 #ifndef PCI_CLASS_BRIDGE_PCI
  42 #define PCI_CLASS_BRIDGE_PCI 0x0604
  43 #endif
  44 
  45 #ifndef PCI_REVISION_ID
  46 #define PCI_REVISION_ID 0x08
  47 #endif
  48 
  49 #ifndef PCI_SUBSYSTEM_VENDOR_ID
  50 #define PCI_SUBSYSTEM_VENDOR_ID 0x2c
  51 #endif
  52 #ifndef PCI_SUBSYSTEM_ID
  53 #define PCI_SUBSYSTEM_ID 0x2e
  54 #endif
  55 
  56 #ifndef PCI_PRIMARY_BUS
  57 #define PCI_PRIMARY_BUS 0x18
  58 #endif
  59 #ifndef PCI_SECONDARY_BUS
  60 #define PCI_SECONDARY_BUS 0x19
  61 #endif
  62 #ifndef PCI_SUBORDINATE_BUS
  63 #define PCI_SUBORDINATE_BUS 0x1a
  64 #endif
  65 
  66 #ifndef PCI_CAP_ID_EXP
  67 #define PCI_CAP_ID_EXP 0x10
  68 #endif
  69 
  70 #ifndef PCI_CAP_NORMAL
  71 #define PCI_CAP_NORMAL 1
  72 #endif
  73 
  74 #define CONFIG_SPACE_CACHESIZE 256
  75 
  76 #ifdef HWLOC_WIN_SYS
  77 #error pciaccess locking currently not implemented on Windows
  78 
  79 #elif defined HWLOC_HAVE_PTHREAD_MUTEX
  80 /* pthread mutex if available (except on windows) */
  81 #include <pthread.h>
  82 static pthread_mutex_t hwloc_pciaccess_mutex = PTHREAD_MUTEX_INITIALIZER;
  83 #define HWLOC_PCIACCESS_LOCK() pthread_mutex_lock(&hwloc_pciaccess_mutex)
  84 #define HWLOC_PCIACCESS_UNLOCK() pthread_mutex_unlock(&hwloc_pciaccess_mutex)
  85 
  86 #else /* HWLOC_WIN_SYS || HWLOC_HAVE_PTHREAD_MUTEX */
  87 #error No mutex implementation available
  88 #endif
  89 
  90 static int
  91 hwloc_look_pci(struct hwloc_backend *backend)
  92 {
  93   struct hwloc_topology *topology = backend->topology;
  94   enum hwloc_type_filter_e pfilter, bfilter;
  95   struct hwloc_obj *tree = NULL, *tmp;
  96   int ret;
  97   struct pci_device_iterator *iter;
  98   struct pci_device *pcidev;
  99 
 100   hwloc_topology_get_type_filter(topology, HWLOC_OBJ_PCI_DEVICE, &pfilter);
 101   hwloc_topology_get_type_filter(topology, HWLOC_OBJ_BRIDGE, &bfilter);
 102   if (bfilter == HWLOC_TYPE_FILTER_KEEP_NONE
 103       && pfilter == HWLOC_TYPE_FILTER_KEEP_NONE)
 104     return 0;
 105 
 106   /* don't do anything if another backend attached PCI already
 107    * (they are attached to root until later in the core discovery)
 108    */
 109   tmp = hwloc_get_root_obj(topology)->io_first_child;
 110   while (tmp) {
 111     if (tmp->type == HWLOC_OBJ_PCI_DEVICE
 112         || (tmp->type == HWLOC_OBJ_BRIDGE && tmp->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI)) {
 113       hwloc_debug("%s", "PCI objects already added, ignoring linuxpci backend.\n");
 114       return 0;
 115     }
 116     tmp = tmp->next_sibling;
 117   }
 118 
 119   hwloc_debug("%s", "\nScanning PCI buses...\n");
 120 
 121   /* pciaccess isn't thread-safe. it uses a single global variable that doesn't have
 122    * refcounting, and is dynamically reallocated when vendor/device names are needed, etc.
 123    */
 124   HWLOC_PCIACCESS_LOCK();
 125 
 126   /* initialize PCI scanning */
 127   ret = pci_system_init();
 128   if (ret) {
 129     HWLOC_PCIACCESS_UNLOCK();
 130     hwloc_debug("%s", "Can not initialize libpciaccess\n");
 131     return -1;
 132   }
 133 
 134   iter = pci_slot_match_iterator_create(NULL);
 135 
 136   /* iterate over devices */
 137   for (pcidev = pci_device_next(iter);
 138        pcidev;
 139        pcidev = pci_device_next(iter))
 140   {
 141     const char *vendorname, *devicename;
 142     unsigned char config_space_cache[CONFIG_SPACE_CACHESIZE];
 143     hwloc_obj_type_t type;
 144     struct hwloc_obj *obj;
 145     unsigned domain;
 146     unsigned device_class;
 147     unsigned short tmp16;
 148     unsigned offset;
 149 
 150     /* initialize the config space in case we fail to read it (missing permissions, etc). */
 151     memset(config_space_cache, 0xff, CONFIG_SPACE_CACHESIZE);
 152     pci_device_probe(pcidev);
 153     pci_device_cfg_read(pcidev, config_space_cache, 0, CONFIG_SPACE_CACHESIZE, NULL);
 154 
 155     /* try to read the domain */
 156     domain = pcidev->domain;
 157 
 158     /* try to read the device_class */
 159     device_class = pcidev->device_class >> 8;
 160 
 161     /* bridge or pci dev? */
 162     type = hwloc_pcidisc_check_bridge_type(device_class, config_space_cache);
 163 
 164     /* filtered? */
 165     if (type == HWLOC_OBJ_PCI_DEVICE) {
 166       enum hwloc_type_filter_e filter;
 167       hwloc_topology_get_type_filter(topology, HWLOC_OBJ_PCI_DEVICE, &filter);
 168       if (filter == HWLOC_TYPE_FILTER_KEEP_NONE)
 169         continue;
 170       if (filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT
 171           && !hwloc_filter_check_pcidev_subtype_important(device_class))
 172         continue;
 173     } else if (type == HWLOC_OBJ_BRIDGE) {
 174       enum hwloc_type_filter_e filter;
 175       hwloc_topology_get_type_filter(topology, HWLOC_OBJ_BRIDGE, &filter);
 176       if (filter == HWLOC_TYPE_FILTER_KEEP_NONE)
 177         continue;
 178       /* HWLOC_TYPE_FILTER_KEEP_IMPORTANT filtered later in the core */
 179     }
 180 
 181     /* fixup SR-IOV buggy VF device/vendor IDs */
 182     if (0xffff == pcidev->vendor_id && 0xffff == pcidev->device_id) {
 183       /* SR-IOV puts ffff:ffff in Virtual Function config space.
 184        * The actual VF device ID is stored at a special (dynamic) location in the Physical Function config space.
 185        * VF and PF have the same vendor ID.
 186        *
 187        * libpciaccess just returns ffff:ffff, needs to be fixed.
 188        * linuxpci is OK because sysfs files are already fixed in the kernel.
 189        * (pciutils is OK when it uses those Linux sysfs files.)
 190        *
 191        * Reading these files is an easy way to work around the libpciaccess issue on Linux,
 192        * but we have no way to know if this is caused by SR-IOV or not.
 193        *
 194        * TODO:
 195        *  If PF has CAP_ID_PCIX or CAP_ID_EXP (offset>0),
 196        *  look for extended capability PCI_EXT_CAP_ID_SRIOV (need extended config space (more than 256 bytes)),
 197        *  then read the VF device ID after it (PCI_IOV_DID bytes later).
 198        *  Needs access to extended config space (needs root on Linux).
 199        * TODO:
 200        *  Add string info attributes in VF and PF objects?
 201        */
 202 #ifdef HWLOC_LINUX_SYS
 203       /* Workaround for Linux (the kernel returns the VF device/vendor IDs). */
 204       char path[64];
 205       char value[16];
 206       FILE *file;
 207       size_t read;
 208 
 209       snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/vendor",
 210                domain, pcidev->bus, pcidev->dev, pcidev->func);
 211       file = fopen(path, "r");
 212       if (file) {
 213         read = fread(value, 1, sizeof(value), file);
 214         fclose(file);
 215         if (read)
 216           /* fixup the pciaccess struct so that pci_device_get_vendor_name() is correct later. */
 217           pcidev->vendor_id = strtoul(value, NULL, 16);
 218       }
 219 
 220       snprintf(path, sizeof(path), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/device",
 221                domain, pcidev->bus, pcidev->dev, pcidev->func);
 222       file = fopen(path, "r");
 223       if (file) {
 224         read = fread(value, 1, sizeof(value), file);
 225         fclose(file);
 226         if (read)
 227           /* fixup the pciaccess struct so that pci_device_get_device_name() is correct later. */
 228           pcidev->device_id = strtoul(value, NULL, 16);
 229       }
 230 #endif
 231     }
 232 
 233     obj = hwloc_alloc_setup_object(topology, type, HWLOC_UNKNOWN_INDEX);
 234     obj->attr->pcidev.domain = domain;
 235     obj->attr->pcidev.bus = pcidev->bus;
 236     obj->attr->pcidev.dev = pcidev->dev;
 237     obj->attr->pcidev.func = pcidev->func;
 238     obj->attr->pcidev.vendor_id = pcidev->vendor_id;
 239     obj->attr->pcidev.device_id = pcidev->device_id;
 240     obj->attr->pcidev.class_id = device_class;
 241     obj->attr->pcidev.revision = config_space_cache[PCI_REVISION_ID];
 242 
 243     obj->attr->pcidev.linkspeed = 0; /* unknown */
 244     offset = hwloc_pcidisc_find_cap(config_space_cache, PCI_CAP_ID_EXP);
 245 
 246     if (offset > 0 && offset + 20 /* size of PCI express block up to link status */ <= CONFIG_SPACE_CACHESIZE)
 247       hwloc_pcidisc_find_linkspeed(config_space_cache, offset, &obj->attr->pcidev.linkspeed);
 248 
 249     if (type == HWLOC_OBJ_BRIDGE) {
 250       if (hwloc_pcidisc_setup_bridge_attr(obj, config_space_cache) < 0)
 251         continue;
 252     }
 253 
 254     if (obj->type == HWLOC_OBJ_PCI_DEVICE) {
 255       memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_VENDOR_ID], sizeof(tmp16));
 256       obj->attr->pcidev.subvendor_id = tmp16;
 257       memcpy(&tmp16, &config_space_cache[PCI_SUBSYSTEM_ID], sizeof(tmp16));
 258       obj->attr->pcidev.subdevice_id = tmp16;
 259     } else {
 260       /* TODO:
 261        * bridge must lookup PCI_CAP_ID_SSVID and then look at offset+PCI_SSVID_VENDOR/DEVICE_ID
 262        * cardbus must look at PCI_CB_SUBSYSTEM_VENDOR_ID and PCI_CB_SUBSYSTEM_ID
 263        */
 264     }
 265 
 266     /* get the vendor name */
 267     vendorname = pci_device_get_vendor_name(pcidev);
 268     if (vendorname && *vendorname)
 269       hwloc_obj_add_info(obj, "PCIVendor", vendorname);
 270 
 271     /* get the device name */
 272     devicename = pci_device_get_device_name(pcidev);
 273     if (devicename && *devicename)
 274       hwloc_obj_add_info(obj, "PCIDevice", devicename);
 275 
 276     hwloc_debug("  %04x:%02x:%02x.%01x %04x %04x:%04x %s %s\n",
 277                 domain, pcidev->bus, pcidev->dev, pcidev->func,
 278                 device_class, pcidev->vendor_id, pcidev->device_id,
 279                 vendorname && *vendorname ? vendorname : "??",
 280                 devicename && *devicename ? devicename : "??");
 281 
 282     hwloc_pcidisc_tree_insert_by_busid(&tree, obj);
 283   }
 284 
 285   /* finalize device scanning */
 286   pci_iterator_destroy(iter);
 287   pci_system_cleanup();
 288   HWLOC_PCIACCESS_UNLOCK();
 289 
 290   hwloc_pcidisc_tree_attach(topology, tree);
 291   return 0;
 292 }
 293 
 294 static struct hwloc_backend *
 295 hwloc_pci_component_instantiate(struct hwloc_disc_component *component,
 296                                    const void *_data1 __hwloc_attribute_unused,
 297                                    const void *_data2 __hwloc_attribute_unused,
 298                                    const void *_data3 __hwloc_attribute_unused)
 299 {
 300   struct hwloc_backend *backend;
 301 
 302 #ifdef HWLOC_SOLARIS_SYS
 303   if ((uid_t)0 != geteuid())
 304     return NULL;
 305 #endif
 306 
 307   backend = hwloc_backend_alloc(component);
 308   if (!backend)
 309     return NULL;
 310   backend->discover = hwloc_look_pci;
 311   return backend;
 312 }
 313 
 314 static struct hwloc_disc_component hwloc_pci_disc_component = {
 315   HWLOC_DISC_COMPONENT_TYPE_MISC,
 316   "pci",
 317   HWLOC_DISC_COMPONENT_TYPE_GLOBAL,
 318   hwloc_pci_component_instantiate,
 319   20,
 320   1,
 321   NULL
 322 };
 323 
 324 static int
 325 hwloc_pci_component_init(unsigned long flags)
 326 {
 327   if (flags)
 328     return -1;
 329   if (hwloc_plugin_check_namespace("pci", "hwloc_backend_alloc") < 0)
 330     return -1;
 331   return 0;
 332 }
 333 
 334 #ifdef HWLOC_INSIDE_PLUGIN
 335 HWLOC_DECLSPEC extern const struct hwloc_component hwloc_pci_component;
 336 #endif
 337 
 338 const struct hwloc_component hwloc_pci_component = {
 339   HWLOC_COMPONENT_ABI,
 340   hwloc_pci_component_init, NULL,
 341   HWLOC_COMPONENT_TYPE_DISC,
 342   0,
 343   &hwloc_pci_disc_component
 344 };

/* [<][>][^][v][top][bottom][index][help] */