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

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

DEFINITIONS

This source file includes following definitions.
  1. hwloc_pci_forced_locality_parse_one
  2. hwloc_pci_forced_locality_parse
  3. hwloc_pci_discovery_init
  4. hwloc_pci_discovery_prepare
  5. hwloc_pci_discovery_exit
  6. hwloc_pci_traverse_print_cb
  7. hwloc_pci_traverse
  8. hwloc_pci_compare_busids
  9. hwloc_pci_add_object
  10. hwloc_pcidisc_tree_insert_by_busid
  11. hwloc_pcidisc_tree_attach
  12. hwloc_pci_fixup_busid_parent
  13. hwloc__pci_find_busid_parent
  14. hwloc_pcidisc_find_busid_parent
  15. hwloc_pci_belowroot_apply_locality
  16. hwloc__pci_belowroot_find_by_busid
  17. hwloc_pcidisc_find_by_busid
  18. hwloc_pcidisc_find_cap
  19. hwloc_pcidisc_find_linkspeed
  20. hwloc_pcidisc_check_bridge_type
  21. hwloc_pcidisc_setup_bridge_attr
  22. hwloc_pci_class_string

   1 /*
   2  * Copyright © 2009-2017 Inria.  All rights reserved.
   3  * See COPYING in top-level directory.
   4  */
   5 
   6 #include <private/autogen/config.h>
   7 #include <hwloc.h>
   8 #include <hwloc/plugins.h>
   9 #include <private/private.h>
  10 #include <private/debug.h>
  11 #include <private/misc.h>
  12 
  13 #include <fcntl.h>
  14 #ifdef HAVE_UNISTD_H
  15 #include <unistd.h>
  16 #endif
  17 #include <sys/stat.h>
  18 
  19 #ifdef HWLOC_WIN_SYS
  20 #include <io.h>
  21 #define open _open
  22 #define read _read
  23 #define close _close
  24 #endif
  25 
  26 static void
  27 hwloc_pci_forced_locality_parse_one(struct hwloc_topology *topology,
  28                                     const char *string /* must contain a ' ' */,
  29                                     unsigned *allocated)
  30 {
  31   unsigned nr = topology->pci_forced_locality_nr;
  32   unsigned domain, bus_first, bus_last, dummy;
  33   hwloc_bitmap_t set;
  34   char *tmp;
  35 
  36   if (sscanf(string, "%x:%x-%x %x", &domain, &bus_first, &bus_last, &dummy) == 4) {
  37     /* fine */
  38   } else if (sscanf(string, "%x:%x %x", &domain, &bus_first, &dummy) == 3) {
  39     bus_last = bus_first;
  40   } else if (sscanf(string, "%x %x", &domain, &dummy) == 2) {
  41     bus_first = 0;
  42     bus_last = 255;
  43   } else
  44     return;
  45 
  46   tmp = strchr(string, ' ');
  47   if (!tmp)
  48     return;
  49   tmp++;
  50 
  51   set = hwloc_bitmap_alloc();
  52   hwloc_bitmap_sscanf(set, tmp);
  53 
  54   if (!*allocated) {
  55     topology->pci_forced_locality = malloc(sizeof(*topology->pci_forced_locality));
  56     if (!topology->pci_forced_locality)
  57       goto out_with_set; /* failed to allocate, ignore this forced locality */
  58     *allocated = 1;
  59   } else if (nr >= *allocated) {
  60     struct hwloc_pci_forced_locality_s *tmplocs;
  61     tmplocs = realloc(topology->pci_forced_locality,
  62                       2 * *allocated * sizeof(*topology->pci_forced_locality));
  63     if (!tmplocs)
  64       goto out_with_set; /* failed to allocate, ignore this forced locality */
  65     topology->pci_forced_locality = tmplocs;
  66     *allocated *= 2;
  67   }
  68 
  69   topology->pci_forced_locality[nr].domain = domain;
  70   topology->pci_forced_locality[nr].bus_first = bus_first;
  71   topology->pci_forced_locality[nr].bus_last = bus_last;
  72   topology->pci_forced_locality[nr].cpuset = set;
  73   topology->pci_forced_locality_nr++;
  74   return;
  75 
  76  out_with_set:
  77   hwloc_bitmap_free(set);
  78   return;
  79 }
  80 
  81 static void
  82 hwloc_pci_forced_locality_parse(struct hwloc_topology *topology, const char *_env)
  83 {
  84   char *env = strdup(_env);
  85   unsigned allocated = 0;
  86   char *tmp = env;
  87 
  88   while (1) {
  89     size_t len = strcspn(tmp, ";\r\n");
  90     char *next = NULL;
  91 
  92     if (tmp[len] != '\0') {
  93       tmp[len] = '\0';
  94       if (tmp[len+1] != '\0')
  95         next = &tmp[len]+1;
  96     }
  97 
  98     hwloc_pci_forced_locality_parse_one(topology, tmp, &allocated);
  99 
 100     if (next)
 101       tmp = next;
 102     else
 103       break;
 104   }
 105 
 106   free(env);
 107 }
 108 
 109 void
 110 hwloc_pci_discovery_init(struct hwloc_topology *topology)
 111 {
 112   topology->need_pci_belowroot_apply_locality = 0;
 113 
 114   topology->pci_has_forced_locality = 0;
 115   topology->pci_forced_locality_nr = 0;
 116   topology->pci_forced_locality = NULL;
 117 }
 118 
 119 void
 120 hwloc_pci_discovery_prepare(struct hwloc_topology *topology)
 121 {
 122   char *env;
 123 
 124   env = getenv("HWLOC_PCI_LOCALITY");
 125   if (env) {
 126     int fd;
 127 
 128     topology->pci_has_forced_locality = 1;
 129 
 130     fd = open(env, O_RDONLY);
 131     if (fd >= 0) {
 132       struct stat st;
 133       char *buffer;
 134       int err = fstat(fd, &st);
 135       if (!err) {
 136         if (st.st_size <= 64*1024) { /* random limit large enough to store multiple cpusets for thousands of PUs */
 137           buffer = malloc(st.st_size+1);
 138           if (read(fd, buffer, st.st_size) == st.st_size) {
 139             buffer[st.st_size] = '\0';
 140             hwloc_pci_forced_locality_parse(topology, buffer);
 141           }
 142           free(buffer);
 143         } else {
 144           fprintf(stderr, "Ignoring HWLOC_PCI_LOCALITY file `%s' too large (%lu bytes)\n",
 145                   env, (unsigned long) st.st_size);
 146         }
 147       }
 148       close(fd);
 149     } else
 150       hwloc_pci_forced_locality_parse(topology, env);
 151   }
 152 }
 153 
 154 void
 155 hwloc_pci_discovery_exit(struct hwloc_topology *topology __hwloc_attribute_unused)
 156 {
 157   unsigned i;
 158   for(i=0; i<topology->pci_forced_locality_nr; i++)
 159     hwloc_bitmap_free(topology->pci_forced_locality[i].cpuset);
 160   free(topology->pci_forced_locality);
 161 
 162   hwloc_pci_discovery_init(topology);
 163 }
 164 
 165 #ifdef HWLOC_DEBUG
 166 static void
 167 hwloc_pci_traverse_print_cb(void * cbdata __hwloc_attribute_unused,
 168                             struct hwloc_obj *pcidev)
 169 {
 170   char busid[14];
 171   hwloc_obj_t parent;
 172 
 173   /* indent */
 174   parent = pcidev->parent;
 175   while (parent) {
 176     hwloc_debug("%s", "  ");
 177     parent = parent->parent;
 178   }
 179 
 180   snprintf(busid, sizeof(busid), "%04x:%02x:%02x.%01x",
 181            pcidev->attr->pcidev.domain, pcidev->attr->pcidev.bus, pcidev->attr->pcidev.dev, pcidev->attr->pcidev.func);
 182 
 183   if (pcidev->type == HWLOC_OBJ_BRIDGE) {
 184     if (pcidev->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_HOST)
 185       hwloc_debug("HostBridge");
 186     else
 187       hwloc_debug("%s Bridge [%04x:%04x]", busid,
 188                   pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id);
 189     hwloc_debug(" to %04x:[%02x:%02x]\n",
 190                 pcidev->attr->bridge.downstream.pci.domain, pcidev->attr->bridge.downstream.pci.secondary_bus, pcidev->attr->bridge.downstream.pci.subordinate_bus);
 191   } else
 192     hwloc_debug("%s Device [%04x:%04x (%04x:%04x) rev=%02x class=%04x]\n", busid,
 193                 pcidev->attr->pcidev.vendor_id, pcidev->attr->pcidev.device_id,
 194                 pcidev->attr->pcidev.subvendor_id, pcidev->attr->pcidev.subdevice_id,
 195                 pcidev->attr->pcidev.revision, pcidev->attr->pcidev.class_id);
 196 }
 197 
 198 static void
 199 hwloc_pci_traverse(void * cbdata, struct hwloc_obj *tree,
 200                    void (*cb)(void * cbdata, struct hwloc_obj *))
 201 {
 202   hwloc_obj_t child;
 203   cb(cbdata, tree);
 204   for_each_io_child(child, tree) {
 205     if (child->type == HWLOC_OBJ_BRIDGE)
 206       hwloc_pci_traverse(cbdata, child, cb);
 207   }
 208 }
 209 #endif /* HWLOC_DEBUG */
 210 
 211 enum hwloc_pci_busid_comparison_e {
 212   HWLOC_PCI_BUSID_LOWER,
 213   HWLOC_PCI_BUSID_HIGHER,
 214   HWLOC_PCI_BUSID_INCLUDED,
 215   HWLOC_PCI_BUSID_SUPERSET
 216 };
 217 
 218 static enum hwloc_pci_busid_comparison_e
 219 hwloc_pci_compare_busids(struct hwloc_obj *a, struct hwloc_obj *b)
 220 {
 221 #ifdef HWLOC_DEBUG
 222   if (a->type == HWLOC_OBJ_BRIDGE)
 223     assert(a->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI);
 224   if (b->type == HWLOC_OBJ_BRIDGE)
 225     assert(b->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI);
 226 #endif
 227 
 228   if (a->attr->pcidev.domain < b->attr->pcidev.domain)
 229     return HWLOC_PCI_BUSID_LOWER;
 230   if (a->attr->pcidev.domain > b->attr->pcidev.domain)
 231     return HWLOC_PCI_BUSID_HIGHER;
 232 
 233   if (a->type == HWLOC_OBJ_BRIDGE
 234       && b->attr->pcidev.bus >= a->attr->bridge.downstream.pci.secondary_bus
 235       && b->attr->pcidev.bus <= a->attr->bridge.downstream.pci.subordinate_bus)
 236     return HWLOC_PCI_BUSID_SUPERSET;
 237   if (b->type == HWLOC_OBJ_BRIDGE
 238       && a->attr->pcidev.bus >= b->attr->bridge.downstream.pci.secondary_bus
 239       && a->attr->pcidev.bus <= b->attr->bridge.downstream.pci.subordinate_bus)
 240     return HWLOC_PCI_BUSID_INCLUDED;
 241 
 242   if (a->attr->pcidev.bus < b->attr->pcidev.bus)
 243     return HWLOC_PCI_BUSID_LOWER;
 244   if (a->attr->pcidev.bus > b->attr->pcidev.bus)
 245     return HWLOC_PCI_BUSID_HIGHER;
 246 
 247   if (a->attr->pcidev.dev < b->attr->pcidev.dev)
 248     return HWLOC_PCI_BUSID_LOWER;
 249   if (a->attr->pcidev.dev > b->attr->pcidev.dev)
 250     return HWLOC_PCI_BUSID_HIGHER;
 251 
 252   if (a->attr->pcidev.func < b->attr->pcidev.func)
 253     return HWLOC_PCI_BUSID_LOWER;
 254   if (a->attr->pcidev.func > b->attr->pcidev.func)
 255     return HWLOC_PCI_BUSID_HIGHER;
 256 
 257   /* Should never reach here.  Abort on both debug builds and
 258      non-debug builds */
 259   assert(0);
 260   fprintf(stderr, "Bad assertion in hwloc %s:%d (aborting)\n", __FILE__, __LINE__);
 261   exit(1);
 262 }
 263 
 264 static void
 265 hwloc_pci_add_object(struct hwloc_obj *parent, struct hwloc_obj **parent_io_first_child_p, struct hwloc_obj *new)
 266 {
 267   struct hwloc_obj **curp, **childp;
 268 
 269   curp = parent_io_first_child_p;
 270   while (*curp) {
 271     enum hwloc_pci_busid_comparison_e comp = hwloc_pci_compare_busids(new, *curp);
 272     switch (comp) {
 273     case HWLOC_PCI_BUSID_HIGHER:
 274       /* go further */
 275       curp = &(*curp)->next_sibling;
 276       continue;
 277     case HWLOC_PCI_BUSID_INCLUDED:
 278       /* insert new below current bridge */
 279       hwloc_pci_add_object(*curp, &(*curp)->io_first_child, new);
 280       return;
 281     case HWLOC_PCI_BUSID_LOWER:
 282     case HWLOC_PCI_BUSID_SUPERSET: {
 283       /* insert new before current */
 284       new->next_sibling = *curp;
 285       *curp = new;
 286       new->parent = parent;
 287       if (new->type == HWLOC_OBJ_BRIDGE) {
 288         /* look at remaining siblings and move some below new */
 289         childp = &new->io_first_child;
 290         curp = &new->next_sibling;
 291         while (*curp) {
 292           hwloc_obj_t cur = *curp;
 293           if (hwloc_pci_compare_busids(new, cur) == HWLOC_PCI_BUSID_LOWER) {
 294             /* this sibling remains under root, after new. */
 295             if (cur->attr->pcidev.domain > new->attr->pcidev.domain
 296                 || cur->attr->pcidev.bus > new->attr->bridge.downstream.pci.subordinate_bus)
 297               /* this sibling is even above new's subordinate bus, no other sibling could go below new */
 298               return;
 299             curp = &cur->next_sibling;
 300           } else {
 301             /* this sibling goes under new */
 302             *childp = cur;
 303             *curp = cur->next_sibling;
 304             (*childp)->parent = new;
 305             (*childp)->next_sibling = NULL;
 306             childp = &(*childp)->next_sibling;
 307           }
 308         }
 309       }
 310       return;
 311     }
 312     }
 313   }
 314   /* add to the end of the list if higher than everybody */
 315   new->parent = parent;
 316   new->next_sibling = NULL;
 317   *curp = new;
 318 }
 319 
 320 void
 321 hwloc_pcidisc_tree_insert_by_busid(struct hwloc_obj **treep,
 322                                    struct hwloc_obj *obj)
 323 {
 324   hwloc_pci_add_object(NULL /* no parent on top of tree */, treep, obj);
 325 }
 326 
 327 int
 328 hwloc_pcidisc_tree_attach(struct hwloc_topology *topology, struct hwloc_obj *old_tree)
 329 {
 330   struct hwloc_obj **next_hb_p;
 331   enum hwloc_type_filter_e bfilter;
 332 
 333   if (!old_tree)
 334     /* found nothing, exit */
 335     return 0;
 336 
 337 #ifdef HWLOC_DEBUG
 338   hwloc_debug("%s", "\nPCI hierarchy:\n");
 339   hwloc_pci_traverse(NULL, old_tree, hwloc_pci_traverse_print_cb);
 340   hwloc_debug("%s", "\n");
 341 #endif
 342 
 343   next_hb_p = &hwloc_get_root_obj(topology)->io_first_child;
 344   while (*next_hb_p)
 345     next_hb_p = &((*next_hb_p)->next_sibling);
 346 
 347   bfilter = topology->type_filter[HWLOC_OBJ_BRIDGE];
 348   if (bfilter == HWLOC_TYPE_FILTER_KEEP_NONE) {
 349     *next_hb_p = old_tree;
 350     topology->modified = 1;
 351     goto done;
 352   }
 353 
 354   /*
 355    * tree points to all objects connected to any upstream bus in the machine.
 356    * We now create one real hostbridge object per upstream bus.
 357    * It's not actually a PCI device so we have to create it.
 358    */
 359   while (old_tree) {
 360     /* start a new host bridge */
 361     struct hwloc_obj *hostbridge = hwloc_alloc_setup_object(topology, HWLOC_OBJ_BRIDGE, HWLOC_UNKNOWN_INDEX);
 362     struct hwloc_obj **dstnextp = &hostbridge->io_first_child;
 363     struct hwloc_obj **srcnextp = &old_tree;
 364     struct hwloc_obj *child = *srcnextp;
 365     unsigned short current_domain = child->attr->pcidev.domain;
 366     unsigned char current_bus = child->attr->pcidev.bus;
 367     unsigned char current_subordinate = current_bus;
 368 
 369     hwloc_debug("Starting new PCI hostbridge %04x:%02x\n", current_domain, current_bus);
 370 
 371   next_child:
 372     /* remove next child from tree */
 373     *srcnextp = child->next_sibling;
 374     /* append it to hostbridge */
 375     *dstnextp = child;
 376     child->parent = hostbridge;
 377     child->next_sibling = NULL;
 378     dstnextp = &child->next_sibling;
 379 
 380     /* compute hostbridge secondary/subordinate buses */
 381     if (child->type == HWLOC_OBJ_BRIDGE
 382         && child->attr->bridge.downstream.pci.subordinate_bus > current_subordinate)
 383       current_subordinate = child->attr->bridge.downstream.pci.subordinate_bus;
 384 
 385     /* use next child if it has the same domains/bus */
 386     child = *srcnextp;
 387     if (child
 388         && child->attr->pcidev.domain == current_domain
 389         && child->attr->pcidev.bus == current_bus)
 390       goto next_child;
 391 
 392     /* finish setting up this hostbridge */
 393     hostbridge->attr->bridge.upstream_type = HWLOC_OBJ_BRIDGE_HOST;
 394     hostbridge->attr->bridge.downstream_type = HWLOC_OBJ_BRIDGE_PCI;
 395     hostbridge->attr->bridge.downstream.pci.domain = current_domain;
 396     hostbridge->attr->bridge.downstream.pci.secondary_bus = current_bus;
 397     hostbridge->attr->bridge.downstream.pci.subordinate_bus = current_subordinate;
 398     hwloc_debug("New PCI hostbridge %04x:[%02x-%02x]\n",
 399                 current_domain, current_bus, current_subordinate);
 400 
 401     *next_hb_p = hostbridge;
 402     next_hb_p = &hostbridge->next_sibling;
 403     topology->modified = 1; /* needed in case somebody reconnects levels before the core calls hwloc_pci_belowroot_apply_locality()
 404                              * or if hwloc_pci_belowroot_apply_locality() keeps hostbridges below root.
 405                              */
 406   }
 407 
 408  done:
 409   topology->need_pci_belowroot_apply_locality = 1;
 410   return 0;
 411 }
 412 
 413 static struct hwloc_obj *
 414 hwloc_pci_fixup_busid_parent(struct hwloc_topology *topology __hwloc_attribute_unused,
 415                              struct hwloc_pcidev_attr_s *busid,
 416                              struct hwloc_obj *parent)
 417 {
 418   /* Xeon E5v3 in cluster-on-die mode only have PCI on the first NUMA node of each package.
 419    * but many dual-processor host report the second PCI hierarchy on 2nd NUMA of first package.
 420    */
 421   if (parent->depth >= 2
 422       && parent->type == HWLOC_OBJ_NUMANODE
 423       && parent->sibling_rank == 1 && parent->parent->arity == 2
 424       && parent->parent->type == HWLOC_OBJ_PACKAGE
 425       && parent->parent->sibling_rank == 0 && parent->parent->parent->arity == 2) {
 426     const char *cpumodel = hwloc_obj_get_info_by_name(parent->parent, "CPUModel");
 427     if (cpumodel && strstr(cpumodel, "Xeon")) {
 428       if (!hwloc_hide_errors()) {
 429         fprintf(stderr, "****************************************************************************\n");
 430         fprintf(stderr, "* hwloc %s has encountered an incorrect PCI locality information.\n", HWLOC_VERSION);
 431         fprintf(stderr, "* PCI bus %04x:%02x is supposedly close to 2nd NUMA node of 1st package,\n",
 432                 busid->domain, busid->bus);
 433         fprintf(stderr, "* however hwloc believes this is impossible on this architecture.\n");
 434         fprintf(stderr, "* Therefore the PCI bus will be moved to 1st NUMA node of 2nd package.\n");
 435         fprintf(stderr, "*\n");
 436         fprintf(stderr, "* If you feel this fixup is wrong, disable it by setting in your environment\n");
 437         fprintf(stderr, "* HWLOC_PCI_%04x_%02x_LOCALCPUS= (empty value), and report the problem\n",
 438                 busid->domain, busid->bus);
 439         fprintf(stderr, "* to the hwloc's user mailing list together with the XML output of lstopo.\n");
 440         fprintf(stderr, "*\n");
 441         fprintf(stderr, "* You may silence this message by setting HWLOC_HIDE_ERRORS=1 in your environment.\n");
 442         fprintf(stderr, "****************************************************************************\n");
 443       }
 444       return parent->parent->next_sibling->first_child;
 445     }
 446   }
 447 
 448   return parent;
 449 }
 450 
 451 static struct hwloc_obj *
 452 hwloc__pci_find_busid_parent(struct hwloc_topology *topology, struct hwloc_pcidev_attr_s *busid)
 453 {
 454   hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
 455   hwloc_obj_t parent;
 456   int forced = 0;
 457   int noquirks = 0;
 458   unsigned i;
 459   int err;
 460 
 461   /* try to match a forced locality */
 462   if (topology->pci_has_forced_locality) {
 463     for(i=0; i<topology->pci_forced_locality_nr; i++) {
 464       if (busid->domain == topology->pci_forced_locality[i].domain
 465           && busid->bus >= topology->pci_forced_locality[i].bus_first
 466           && busid->bus <= topology->pci_forced_locality[i].bus_last) {
 467         hwloc_bitmap_copy(cpuset, topology->pci_forced_locality[i].cpuset);
 468         forced = 1;
 469         break;
 470       }
 471     }
 472     /* if pci locality was forced, even empty, don't let quirks change what the OS reports */
 473     noquirks = 1;
 474   }
 475 
 476   /* deprecated force locality variables */
 477   if (!forced) {
 478     const char *env;
 479     char envname[256];
 480     /* override the cpuset with the environment if given */
 481     snprintf(envname, sizeof(envname), "HWLOC_PCI_%04x_%02x_LOCALCPUS",
 482              busid->domain, busid->bus);
 483     env = getenv(envname);
 484     if (env) {
 485       static int reported = 0;
 486       if (!topology->pci_has_forced_locality && !reported) {
 487         fprintf(stderr, "Environment variable %s is deprecated, please use HWLOC_PCI_LOCALITY instead.\n", env);
 488         reported = 1;
 489       }
 490       if (*env) {
 491         /* force the cpuset */
 492         hwloc_debug("Overriding localcpus using %s in the environment\n", envname);
 493         hwloc_bitmap_sscanf(cpuset, env);
 494         forced = 1;
 495       }
 496       /* if env exists, even empty, don't let quirks change what the OS reports */
 497       noquirks = 1;
 498     }
 499   }
 500 
 501   if (!forced) {
 502     /* get the cpuset by asking the OS backend. */
 503     struct hwloc_backend *backend = topology->get_pci_busid_cpuset_backend;
 504     if (backend)
 505       err = backend->get_pci_busid_cpuset(backend, busid, cpuset);
 506     else
 507       err = -1;
 508     if (err < 0)
 509       /* if we got nothing, assume this PCI bus is attached to the top of hierarchy */
 510       hwloc_bitmap_copy(cpuset, hwloc_topology_get_topology_cpuset(topology));
 511   }
 512 
 513   hwloc_debug_bitmap("Attaching PCI tree to cpuset %s\n", cpuset);
 514 
 515   parent = hwloc_find_insert_io_parent_by_complete_cpuset(topology, cpuset);
 516   if (parent) {
 517     if (!noquirks)
 518       /* We found a valid parent. Check that the OS didn't report invalid locality */
 519       parent = hwloc_pci_fixup_busid_parent(topology, busid, parent);
 520   } else {
 521     /* Fallback to root */
 522     parent = hwloc_get_root_obj(topology);
 523   }
 524 
 525   hwloc_bitmap_free(cpuset);
 526   return parent;
 527 }
 528 
 529 struct hwloc_obj *
 530 hwloc_pcidisc_find_busid_parent(struct hwloc_topology *topology,
 531                                 unsigned domain, unsigned bus, unsigned dev, unsigned func)
 532 {
 533   struct hwloc_pcidev_attr_s busid;
 534   busid.domain = domain;
 535   busid.bus = bus;
 536   busid.dev = dev;
 537   busid.func = func;
 538   return hwloc__pci_find_busid_parent(topology, &busid);
 539 }
 540 
 541 int
 542 hwloc_pci_belowroot_apply_locality(struct hwloc_topology *topology)
 543 {
 544   struct hwloc_obj *root = hwloc_get_root_obj(topology);
 545   struct hwloc_obj **listp, *obj;
 546 
 547   if (!topology->need_pci_belowroot_apply_locality)
 548     return 0;
 549   topology->need_pci_belowroot_apply_locality = 0;
 550 
 551   /* root->io_first_child contains some PCI hierarchies, any maybe some non-PCI things.
 552    * insert the PCI trees according to their PCI-locality.
 553    */
 554   listp = &root->io_first_child;
 555   while ((obj = *listp) != NULL) {
 556     struct hwloc_pcidev_attr_s *busid;
 557     struct hwloc_obj *parent;
 558 
 559     /* skip non-PCI objects */
 560     if (obj->type != HWLOC_OBJ_PCI_DEVICE
 561         && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI)
 562         && !(obj->type == HWLOC_OBJ_BRIDGE && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) {
 563       listp = &obj->next_sibling;
 564       continue;
 565     }
 566 
 567     if (obj->type == HWLOC_OBJ_PCI_DEVICE
 568         || (obj->type == HWLOC_OBJ_BRIDGE
 569             && obj->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI))
 570       busid = &obj->attr->pcidev;
 571     else {
 572       /* hostbridges don't have a PCI busid for looking up locality, use their first child if PCI */
 573       hwloc_obj_t child = obj->io_first_child;
 574       if (child && (child->type == HWLOC_OBJ_PCI_DEVICE
 575                     || (child->type == HWLOC_OBJ_BRIDGE
 576                         && child->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)))
 577         busid = &obj->io_first_child->attr->pcidev;
 578       else
 579         continue;
 580     }
 581 
 582     /* attach the object (and children) where it belongs */
 583     parent = hwloc__pci_find_busid_parent(topology, busid);
 584     if (parent == root) {
 585       /* keep this object here */
 586       listp = &obj->next_sibling;
 587     } else {
 588       /* dequeue this object */
 589       *listp = obj->next_sibling;
 590       obj->next_sibling = NULL;
 591       hwloc_insert_object_by_parent(topology, parent, obj);
 592     }
 593   }
 594 
 595   return 0;
 596 }
 597 
 598 static struct hwloc_obj *
 599 hwloc__pci_belowroot_find_by_busid(hwloc_obj_t parent,
 600                                    unsigned domain, unsigned bus, unsigned dev, unsigned func)
 601 {
 602   hwloc_obj_t child;
 603 
 604   for_each_io_child(child, parent) {
 605     if (child->type == HWLOC_OBJ_PCI_DEVICE
 606         || (child->type == HWLOC_OBJ_BRIDGE
 607             && child->attr->bridge.upstream_type == HWLOC_OBJ_BRIDGE_PCI)) {
 608       if (child->attr->pcidev.domain == domain
 609           && child->attr->pcidev.bus == bus
 610           && child->attr->pcidev.dev == dev
 611           && child->attr->pcidev.func == func)
 612         /* that's the right bus id */
 613         return child;
 614       if (child->attr->pcidev.domain > domain
 615           || (child->attr->pcidev.domain == domain
 616               && child->attr->pcidev.bus > bus))
 617         /* bus id too high, won't find anything later, return parent */
 618         return parent;
 619       if (child->type == HWLOC_OBJ_BRIDGE
 620           && child->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
 621           && child->attr->bridge.downstream.pci.domain == domain
 622           && child->attr->bridge.downstream.pci.secondary_bus <= bus
 623           && child->attr->bridge.downstream.pci.subordinate_bus >= bus)
 624         /* not the right bus id, but it's included in the bus below that bridge */
 625         return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func);
 626 
 627     } else if (child->type == HWLOC_OBJ_BRIDGE
 628                && child->attr->bridge.upstream_type != HWLOC_OBJ_BRIDGE_PCI
 629                && child->attr->bridge.downstream_type == HWLOC_OBJ_BRIDGE_PCI
 630                /* non-PCI to PCI bridge, just look at the subordinate bus */
 631                && child->attr->bridge.downstream.pci.domain == domain
 632                && child->attr->bridge.downstream.pci.secondary_bus <= bus
 633                && child->attr->bridge.downstream.pci.subordinate_bus >= bus) {
 634       /* contains our bus, recurse */
 635       return hwloc__pci_belowroot_find_by_busid(child, domain, bus, dev, func);
 636     }
 637   }
 638   /* didn't find anything, return parent */
 639   return parent;
 640 }
 641 
 642 struct hwloc_obj *
 643 hwloc_pcidisc_find_by_busid(struct hwloc_topology *topology,
 644                             unsigned domain, unsigned bus, unsigned dev, unsigned func)
 645 {
 646   hwloc_obj_t root = hwloc_get_root_obj(topology);
 647   hwloc_obj_t parent = hwloc__pci_belowroot_find_by_busid(root, domain, bus, dev, func);
 648   if (parent == root)
 649     return NULL;
 650   else
 651     return parent;
 652 }
 653 
 654 #define HWLOC_PCI_STATUS 0x06
 655 #define HWLOC_PCI_STATUS_CAP_LIST 0x10
 656 #define HWLOC_PCI_CAPABILITY_LIST 0x34
 657 #define HWLOC_PCI_CAP_LIST_ID 0
 658 #define HWLOC_PCI_CAP_LIST_NEXT 1
 659 
 660 unsigned
 661 hwloc_pcidisc_find_cap(const unsigned char *config, unsigned cap)
 662 {
 663   unsigned char seen[256] = { 0 };
 664   unsigned char ptr; /* unsigned char to make sure we stay within the 256-byte config space */
 665 
 666   if (!(config[HWLOC_PCI_STATUS] & HWLOC_PCI_STATUS_CAP_LIST))
 667     return 0;
 668 
 669   for (ptr = config[HWLOC_PCI_CAPABILITY_LIST] & ~3;
 670        ptr; /* exit if next is 0 */
 671        ptr = config[ptr + HWLOC_PCI_CAP_LIST_NEXT] & ~3) {
 672     unsigned char id;
 673 
 674     /* Looped around! */
 675     if (seen[ptr])
 676       break;
 677     seen[ptr] = 1;
 678 
 679     id = config[ptr + HWLOC_PCI_CAP_LIST_ID];
 680     if (id == cap)
 681       return ptr;
 682     if (id == 0xff) /* exit if id is 0 or 0xff */
 683       break;
 684   }
 685   return 0;
 686 }
 687 
 688 #define HWLOC_PCI_EXP_LNKSTA 0x12
 689 #define HWLOC_PCI_EXP_LNKSTA_SPEED 0x000f
 690 #define HWLOC_PCI_EXP_LNKSTA_WIDTH 0x03f0
 691 
 692 int
 693 hwloc_pcidisc_find_linkspeed(const unsigned char *config,
 694                              unsigned offset, float *linkspeed)
 695 {
 696   unsigned linksta, speed, width;
 697   float lanespeed;
 698 
 699   memcpy(&linksta, &config[offset + HWLOC_PCI_EXP_LNKSTA], 4);
 700   speed = linksta & HWLOC_PCI_EXP_LNKSTA_SPEED; /* PCIe generation */
 701   width = (linksta & HWLOC_PCI_EXP_LNKSTA_WIDTH) >> 4; /* how many lanes */
 702   /* PCIe Gen1 = 2.5GT/s signal-rate per lane with 8/10 encoding    = 0.25GB/s data-rate per lane
 703    * PCIe Gen2 = 5  GT/s signal-rate per lane with 8/10 encoding    = 0.5 GB/s data-rate per lane
 704    * PCIe Gen3 = 8  GT/s signal-rate per lane with 128/130 encoding = 1   GB/s data-rate per lane
 705    * PCIe Gen4 = 16 GT/s signal-rate per lane with 128/130 encoding = 2   GB/s data-rate per lane
 706    */
 707 
 708   /* lanespeed in Gbit/s */
 709   if (speed <= 2)
 710     lanespeed = 2.5f * speed * 0.8f;
 711   else
 712     lanespeed = 8.0f * (1<<(speed-3)) * 128/130; /* assume Gen5 will be 32 GT/s and so on */
 713 
 714   /* linkspeed in GB/s */
 715   *linkspeed = lanespeed * width / 8;
 716   return 0;
 717 }
 718 
 719 #define HWLOC_PCI_HEADER_TYPE 0x0e
 720 #define HWLOC_PCI_HEADER_TYPE_BRIDGE 1
 721 #define HWLOC_PCI_CLASS_BRIDGE_PCI 0x0604
 722 
 723 hwloc_obj_type_t
 724 hwloc_pcidisc_check_bridge_type(unsigned device_class, const unsigned char *config)
 725 {
 726   unsigned char headertype;
 727 
 728   if (device_class != HWLOC_PCI_CLASS_BRIDGE_PCI)
 729     return HWLOC_OBJ_PCI_DEVICE;
 730 
 731   headertype = config[HWLOC_PCI_HEADER_TYPE] & 0x7f;
 732   return (headertype == HWLOC_PCI_HEADER_TYPE_BRIDGE)
 733     ? HWLOC_OBJ_BRIDGE : HWLOC_OBJ_PCI_DEVICE;
 734 }
 735 
 736 #define HWLOC_PCI_PRIMARY_BUS 0x18
 737 #define HWLOC_PCI_SECONDARY_BUS 0x19
 738 #define HWLOC_PCI_SUBORDINATE_BUS 0x1a
 739 
 740 int
 741 hwloc_pcidisc_setup_bridge_attr(hwloc_obj_t obj,
 742                                 const unsigned char *config)
 743 {
 744   struct hwloc_bridge_attr_s *battr = &obj->attr->bridge;
 745   struct hwloc_pcidev_attr_s *pattr = &battr->upstream.pci;
 746 
 747   if (config[HWLOC_PCI_PRIMARY_BUS] != pattr->bus) {
 748     /* Sometimes the config space contains 00 instead of the actual primary bus number.
 749      * Always trust the bus ID because it was built by the system which has more information
 750      * to workaround such problems (e.g. ACPI information about PCI parent/children).
 751      */
 752     hwloc_debug("  %04x:%02x:%02x.%01x bridge with (ignored) invalid PCI_PRIMARY_BUS %02x\n",
 753                 pattr->domain, pattr->bus, pattr->dev, pattr->func, config[HWLOC_PCI_PRIMARY_BUS]);
 754   }
 755 
 756   obj->type = HWLOC_OBJ_BRIDGE;
 757   battr->upstream_type = HWLOC_OBJ_BRIDGE_PCI;
 758   battr->downstream_type = HWLOC_OBJ_BRIDGE_PCI;
 759   battr->downstream.pci.domain = pattr->domain;
 760   battr->downstream.pci.secondary_bus = config[HWLOC_PCI_SECONDARY_BUS];
 761   battr->downstream.pci.subordinate_bus = config[HWLOC_PCI_SUBORDINATE_BUS];
 762 
 763   if (battr->downstream.pci.secondary_bus <= pattr->bus
 764       || battr->downstream.pci.subordinate_bus <= pattr->bus
 765       || battr->downstream.pci.secondary_bus > battr->downstream.pci.subordinate_bus) {
 766     /* This should catch most cases of invalid bridge information
 767      * (e.g. 00 for secondary and subordinate).
 768      * Ideally we would also check that [secondary-subordinate] is included
 769      * in the parent bridge [secondary+1:subordinate]. But that's hard to do
 770      * because objects may be discovered out of order (especially in the fsroot case).
 771      */
 772     hwloc_debug("  %04x:%02x:%02x.%01x bridge has invalid secondary-subordinate buses [%02x-%02x]\n",
 773                 pattr->domain, pattr->bus, pattr->dev, pattr->func,
 774                 battr->downstream.pci.secondary_bus, battr->downstream.pci.subordinate_bus);
 775     hwloc_free_unlinked_object(obj);
 776     return -1;
 777   }
 778 
 779   return 0;
 780 }
 781 
 782 const char *
 783 hwloc_pci_class_string(unsigned short class_id)
 784 {
 785   /* See https://pci-ids.ucw.cz/read/PD/ */
 786   switch ((class_id & 0xff00) >> 8) {
 787     case 0x00:
 788       switch (class_id) {
 789         case 0x0001: return "VGA";
 790       }
 791       break;
 792     case 0x01:
 793       switch (class_id) {
 794         case 0x0100: return "SCSI";
 795         case 0x0101: return "IDE";
 796         case 0x0102: return "Floppy";
 797         case 0x0103: return "IPI";
 798         case 0x0104: return "RAID";
 799         case 0x0105: return "ATA";
 800         case 0x0106: return "SATA";
 801         case 0x0107: return "SAS";
 802         case 0x0108: return "NVMExp";
 803       }
 804       return "Storage";
 805     case 0x02:
 806       switch (class_id) {
 807         case 0x0200: return "Ethernet";
 808         case 0x0201: return "TokenRing";
 809         case 0x0202: return "FDDI";
 810         case 0x0203: return "ATM";
 811         case 0x0204: return "ISDN";
 812         case 0x0205: return "WorldFip";
 813         case 0x0206: return "PICMG";
 814         case 0x0207: return "InfiniBand";
 815         case 0x0208: return "Fabric";
 816       }
 817       return "Network";
 818     case 0x03:
 819       switch (class_id) {
 820         case 0x0300: return "VGA";
 821         case 0x0301: return "XGA";
 822         case 0x0302: return "3D";
 823       }
 824       return "Display";
 825     case 0x04:
 826       switch (class_id) {
 827         case 0x0400: return "MultimediaVideo";
 828         case 0x0401: return "MultimediaAudio";
 829         case 0x0402: return "Telephony";
 830         case 0x0403: return "AudioDevice";
 831       }
 832       return "Multimedia";
 833     case 0x05:
 834       switch (class_id) {
 835         case 0x0500: return "RAM";
 836         case 0x0501: return "Flash";
 837       }
 838       return "Memory";
 839     case 0x06:
 840       switch (class_id) {
 841         case 0x0600: return "HostBridge";
 842         case 0x0601: return "ISABridge";
 843         case 0x0602: return "EISABridge";
 844         case 0x0603: return "MicroChannelBridge";
 845         case 0x0604: return "PCIBridge";
 846         case 0x0605: return "PCMCIABridge";
 847         case 0x0606: return "NubusBridge";
 848         case 0x0607: return "CardBusBridge";
 849         case 0x0608: return "RACEwayBridge";
 850         case 0x0609: return "SemiTransparentPCIBridge";
 851         case 0x060a: return "InfiniBandPCIHostBridge";
 852       }
 853       return "Bridge";
 854     case 0x07:
 855       switch (class_id) {
 856         case 0x0700: return "Serial";
 857         case 0x0701: return "Parallel";
 858         case 0x0702: return "MultiportSerial";
 859         case 0x0703: return "Model";
 860         case 0x0704: return "GPIB";
 861         case 0x0705: return "SmartCard";
 862       }
 863       return "Communication";
 864     case 0x08:
 865       switch (class_id) {
 866         case 0x0800: return "PIC";
 867         case 0x0801: return "DMA";
 868         case 0x0802: return "Timer";
 869         case 0x0803: return "RTC";
 870         case 0x0804: return "PCIHotPlug";
 871         case 0x0805: return "SDHost";
 872         case 0x0806: return "IOMMU";
 873       }
 874       return "SystemPeripheral";
 875     case 0x09:
 876       switch (class_id) {
 877         case 0x0900: return "Keyboard";
 878         case 0x0901: return "DigitizerPen";
 879         case 0x0902: return "Mouse";
 880         case 0x0903: return "Scanern";
 881         case 0x0904: return "Gameport";
 882       }
 883       return "Input";
 884     case 0x0a:
 885       return "DockingStation";
 886     case 0x0b:
 887       switch (class_id) {
 888         case 0x0b00: return "386";
 889         case 0x0b01: return "486";
 890         case 0x0b02: return "Pentium";
 891 /* 0x0b03 and 0x0b04 might be Pentium and P6 ? */
 892         case 0x0b10: return "Alpha";
 893         case 0x0b20: return "PowerPC";
 894         case 0x0b30: return "MIPS";
 895         case 0x0b40: return "Co-Processor";
 896       }
 897       return "Processor";
 898     case 0x0c:
 899       switch (class_id) {
 900         case 0x0c00: return "FireWire";
 901         case 0x0c01: return "ACCESS";
 902         case 0x0c02: return "SSA";
 903         case 0x0c03: return "USB";
 904         case 0x0c04: return "FibreChannel";
 905         case 0x0c05: return "SMBus";
 906         case 0x0c06: return "InfiniBand";
 907         case 0x0c07: return "IPMI-SMIC";
 908         case 0x0c08: return "SERCOS";
 909         case 0x0c09: return "CANBUS";
 910       }
 911       return "SerialBus";
 912     case 0x0d:
 913       switch (class_id) {
 914         case 0x0d00: return "IRDA";
 915         case 0x0d01: return "ConsumerIR";
 916         case 0x0d10: return "RF";
 917         case 0x0d11: return "Bluetooth";
 918         case 0x0d12: return "Broadband";
 919         case 0x0d20: return "802.1a";
 920         case 0x0d21: return "802.1b";
 921       }
 922       return "Wireless";
 923     case 0x0e:
 924       switch (class_id) {
 925         case 0x0e00: return "I2O";
 926       }
 927       return "Intelligent";
 928     case 0x0f:
 929       return "Satellite";
 930     case 0x10:
 931       return "Encryption";
 932     case 0x11:
 933       return "SignalProcessing";
 934     case 0x12:
 935       return "ProcessingAccelerator";
 936     case 0x13:
 937       return "Instrumentation";
 938     case 0x40:
 939       return "Co-Processor";
 940   }
 941   return "Other";
 942 }

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