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

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

DEFINITIONS

This source file includes following definitions.
  1. hwloc_synthetic_process_indexes
  2. hwloc_synthetic_parse_memory_attr
  3. hwloc_synthetic_parse_attrs
  4. hwloc_synthetic_free_levels
  5. hwloc_backend_synthetic_init
  6. hwloc_synthetic_set_attr
  7. hwloc_synthetic_next_index
  8. hwloc_synthetic_insert_attached
  9. hwloc__look_synthetic
  10. hwloc_look_synthetic
  11. hwloc_synthetic_backend_disable
  12. hwloc_synthetic_component_instantiate
  13. hwloc__export_synthetic_update_status
  14. hwloc__export_synthetic_add_char
  15. hwloc__export_synthetic_indexes
  16. hwloc__export_synthetic_obj_attr
  17. hwloc__export_synthetic_obj
  18. hwloc__export_synthetic_memory_children
  19. hwloc_check_memory_symmetric
  20. hwloc_topology_export_synthetic

   1 /*
   2  * Copyright © 2009 CNRS
   3  * Copyright © 2009-2018 Inria.  All rights reserved.
   4  * Copyright © 2009-2010 Université Bordeaux
   5  * Copyright © 2009-2011 Cisco Systems, Inc.  All rights reserved.
   6  * See COPYING in top-level directory.
   7  */
   8 
   9 #include <private/autogen/config.h>
  10 #include <hwloc.h>
  11 #include <private/private.h>
  12 #include <private/misc.h>
  13 #include <private/debug.h>
  14 
  15 #include <limits.h>
  16 #include <assert.h>
  17 #ifdef HAVE_STRINGS_H
  18 #include <strings.h>
  19 #endif
  20 
  21 struct hwloc_synthetic_attr_s {
  22   hwloc_obj_type_t type;
  23   unsigned depth; /* For caches/groups */
  24   hwloc_obj_cache_type_t cachetype; /* For caches */
  25   hwloc_uint64_t memorysize; /* For caches/memory */
  26 };
  27 
  28 struct hwloc_synthetic_indexes_s {
  29   /* the indexes= attribute before parsing */
  30   const char *string;
  31   unsigned long string_length;
  32   /* the array of explicit indexes after parsing */
  33   unsigned *array;
  34 
  35   /* used while filling the topology */
  36   unsigned next; /* id of the next object for that level */
  37 };
  38 
  39 struct hwloc_synthetic_level_data_s {
  40   unsigned arity;
  41   unsigned long totalwidth;
  42 
  43   struct hwloc_synthetic_attr_s attr;
  44   struct hwloc_synthetic_indexes_s indexes;
  45 
  46   struct hwloc_synthetic_attached_s {
  47     struct hwloc_synthetic_attr_s attr;
  48 
  49     struct hwloc_synthetic_attached_s *next;
  50   } *attached;
  51 };
  52 
  53 struct hwloc_synthetic_backend_data_s {
  54   /* synthetic backend parameters */
  55   char *string;
  56 
  57   unsigned long numa_attached_nr;
  58   struct hwloc_synthetic_indexes_s numa_attached_indexes;
  59 
  60 #define HWLOC_SYNTHETIC_MAX_DEPTH 128
  61   struct hwloc_synthetic_level_data_s level[HWLOC_SYNTHETIC_MAX_DEPTH];
  62 };
  63 
  64 struct hwloc_synthetic_intlv_loop_s {
  65   unsigned step;
  66   unsigned nb;
  67   unsigned level_depth;
  68 };
  69 
  70 static void
  71 hwloc_synthetic_process_indexes(struct hwloc_synthetic_backend_data_s *data,
  72                                 struct hwloc_synthetic_indexes_s *indexes,
  73                                 unsigned long total,
  74                                 int verbose)
  75 {
  76   const char *attr = indexes->string;
  77   unsigned long length = indexes->string_length;
  78   unsigned *array = NULL;
  79   size_t i;
  80 
  81   if (!attr)
  82     return;
  83 
  84   array = calloc(total, sizeof(*array));
  85   if (!array) {
  86     if (verbose)
  87       fprintf(stderr, "Failed to allocate synthetic index array of size %lu\n", total);
  88     goto out;
  89   }
  90 
  91   i = strspn(attr, "0123456789,");
  92   if (i == length) {
  93     /* explicit array of indexes */
  94 
  95     for(i=0; i<total; i++) {
  96       const char *next;
  97       unsigned idx = strtoul(attr, (char **) &next, 10);
  98       if (next == attr) {
  99         if (verbose)
 100           fprintf(stderr, "Failed to read synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
 101         goto out_with_array;
 102       }
 103 
 104       array[i] = idx;
 105       if (i != total-1) {
 106         if (*next != ',') {
 107           if (verbose)
 108             fprintf(stderr, "Missing comma after synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
 109           goto out_with_array;
 110         }
 111         attr = next+1;
 112       } else {
 113         attr = next;
 114       }
 115     }
 116     indexes->array = array;
 117 
 118   } else {
 119     /* interleaving */
 120     unsigned nr_loops = 1, cur_loop;
 121     unsigned minstep = total;
 122     unsigned long nbs = 1;
 123     unsigned j, mul;
 124     const char *tmp;
 125 
 126     tmp = attr;
 127     while (tmp) {
 128       tmp = strchr(tmp, ':');
 129       if (!tmp || tmp >= attr+length)
 130         break;
 131       nr_loops++;
 132       tmp++;
 133     }
 134 
 135    {
 136     /* nr_loops colon-separated fields, but we may need one more at the end */
 137     HWLOC_VLA(struct hwloc_synthetic_intlv_loop_s, loops, nr_loops+1);
 138 
 139     if (*attr >= '0' && *attr <= '9') {
 140       /* interleaving as x*y:z*t:... */
 141       unsigned step, nb;
 142 
 143       tmp = attr;
 144       cur_loop = 0;
 145       while (tmp) {
 146         char *tmp2, *tmp3;
 147         step = (unsigned) strtol(tmp, &tmp2, 0);
 148         if (tmp2 == tmp || *tmp2 != '*') {
 149           if (verbose)
 150             fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number before '*'\n", tmp);
 151           goto out_with_array;
 152         }
 153         if (!step) {
 154           if (verbose)
 155             fprintf(stderr, "Invalid interleaving loop with step 0 at '%s'\n", tmp);
 156           goto out_with_array;
 157         }
 158         tmp2++;
 159         nb = (unsigned) strtol(tmp2, &tmp3, 0);
 160         if (tmp3 == tmp2 || (*tmp3 && *tmp3 != ':' && *tmp3 != ')' && *tmp3 != ' ')) {
 161           if (verbose)
 162             fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number between '*' and ':'\n", tmp);
 163           goto out_with_array;
 164         }
 165         if (!nb) {
 166           if (verbose)
 167             fprintf(stderr, "Invalid interleaving loop with number 0 at '%s'\n", tmp2);
 168           goto out_with_array;
 169         }
 170         loops[cur_loop].step = step;
 171         loops[cur_loop].nb = nb;
 172         if (step < minstep)
 173           minstep = step;
 174         nbs *= nb;
 175         cur_loop++;
 176         if (*tmp3 == ')' || *tmp3 == ' ')
 177           break;
 178         tmp = (const char*) (tmp3+1);
 179       }
 180 
 181     } else {
 182       /* interleaving as type1:type2:... */
 183       hwloc_obj_type_t type;
 184       union hwloc_obj_attr_u attrs;
 185       int err;
 186 
 187       /* find level depths for each interleaving loop */
 188       tmp = attr;
 189       cur_loop = 0;
 190       while (tmp) {
 191         err = hwloc_type_sscanf(tmp, &type, &attrs, sizeof(attrs));
 192         if (err < 0) {
 193           if (verbose)
 194             fprintf(stderr, "Failed to read synthetic index interleaving loop type '%s'\n", tmp);
 195           goto out_with_array;
 196         }
 197         if (type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
 198           if (verbose)
 199             fprintf(stderr, "Misc object type disallowed in synthetic index interleaving loop type '%s'\n", tmp);
 200           goto out_with_array;
 201         }
 202         for(i=0; ; i++) {
 203           if (!data->level[i].arity) {
 204             loops[cur_loop].level_depth = (unsigned)-1;
 205             break;
 206           }
 207           if (type != data->level[i].attr.type)
 208             continue;
 209           if (type == HWLOC_OBJ_GROUP
 210               && attrs.group.depth != (unsigned) -1
 211               && attrs.group.depth != data->level[i].attr.depth)
 212             continue;
 213           loops[cur_loop].level_depth = (unsigned)i;
 214           break;
 215         }
 216         if (loops[cur_loop].level_depth == (unsigned)-1) {
 217           if (verbose)
 218             fprintf(stderr, "Failed to find level for synthetic index interleaving loop type '%s'\n",
 219                     tmp);
 220           goto out_with_array;
 221         }
 222         tmp = strchr(tmp, ':');
 223         if (!tmp || tmp > attr+length)
 224           break;
 225         tmp++;
 226         cur_loop++;
 227       }
 228 
 229       /* compute actual loop step/nb */
 230       for(cur_loop=0; cur_loop<nr_loops; cur_loop++) {
 231         unsigned mydepth = loops[cur_loop].level_depth;
 232         unsigned prevdepth = 0;
 233         unsigned step, nb;
 234         for(i=0; i<nr_loops; i++) {
 235           if (loops[i].level_depth == mydepth && i != cur_loop) {
 236             if (verbose)
 237               fprintf(stderr, "Invalid duplicate interleaving loop type in synthetic index '%s'\n", attr);
 238             goto out_with_array;
 239           }
 240           if (loops[i].level_depth < mydepth
 241               && loops[i].level_depth > prevdepth)
 242             prevdepth = loops[i].level_depth;
 243         }
 244         step = total / data->level[mydepth].totalwidth; /* number of objects below us */
 245         nb = data->level[mydepth].totalwidth / data->level[prevdepth].totalwidth; /* number of us within parent */
 246 
 247         loops[cur_loop].step = step;
 248         loops[cur_loop].nb = nb;
 249         assert(nb);
 250         assert(step);
 251         if (step < minstep)
 252           minstep = step;
 253         nbs *= nb;
 254       }
 255     }
 256     assert(nbs);
 257 
 258     if (nbs != total) {
 259       /* one loop of total/nbs steps is missing, add it if it's just the smallest one */
 260       if (minstep == total/nbs) {
 261         loops[nr_loops].step = 1;
 262         loops[nr_loops].nb = total/nbs;
 263         nr_loops++;
 264       } else {
 265         if (verbose)
 266           fprintf(stderr, "Invalid index interleaving total width %lu instead of %lu\n", nbs, total);
 267         goto out_with_array;
 268       }
 269     }
 270 
 271     /* generate the array of indexes */
 272     mul = 1;
 273     for(i=0; i<nr_loops; i++) {
 274       unsigned step = loops[i].step;
 275       unsigned nb = loops[i].nb;
 276       for(j=0; j<total; j++)
 277         array[j] += ((j / step) % nb) * mul;
 278       mul *= nb;
 279     }
 280 
 281     /* check that we have the right values (cannot pass total, cannot give duplicate 0) */
 282     for(j=0; j<total; j++) {
 283       if (array[j] >= total) {
 284         if (verbose)
 285           fprintf(stderr, "Invalid index interleaving generates out-of-range index %u\n", array[j]);
 286         goto out_with_array;
 287       }
 288       if (!array[j] && j) {
 289         if (verbose)
 290           fprintf(stderr, "Invalid index interleaving generates duplicate index values\n");
 291         goto out_with_array;
 292       }
 293     }
 294 
 295     indexes->array = array;
 296    }
 297   }
 298 
 299   return;
 300 
 301  out_with_array:
 302   free(array);
 303  out:
 304   return;
 305 }
 306 
 307 static hwloc_uint64_t
 308 hwloc_synthetic_parse_memory_attr(const char *attr, const char **endp)
 309 {
 310   const char *endptr;
 311   hwloc_uint64_t size;
 312   size = strtoull(attr, (char **) &endptr, 0);
 313   if (!hwloc_strncasecmp(endptr, "TB", 2)) {
 314     size <<= 40;
 315     endptr += 2;
 316   } else if (!hwloc_strncasecmp(endptr, "GB", 2)) {
 317     size <<= 30;
 318     endptr += 2;
 319   } else if (!hwloc_strncasecmp(endptr, "MB", 2)) {
 320     size <<= 20;
 321     endptr += 2;
 322   } else if (!hwloc_strncasecmp(endptr, "kB", 2)) {
 323     size <<= 10;
 324     endptr += 2;
 325   }
 326   *endp = endptr;
 327   return size;
 328 }
 329 
 330 static int
 331 hwloc_synthetic_parse_attrs(const char *attrs, const char **next_posp,
 332                             struct hwloc_synthetic_attr_s *sattr,
 333                             struct hwloc_synthetic_indexes_s *sind,
 334                             int verbose)
 335 {
 336   hwloc_obj_type_t type = sattr->type;
 337   const char *next_pos;
 338   hwloc_uint64_t memorysize = 0;
 339   const char *index_string = NULL;
 340   size_t index_string_length = 0;
 341 
 342   next_pos = (const char *) strchr(attrs, ')');
 343   if (!next_pos) {
 344     if (verbose)
 345       fprintf(stderr, "Missing attribute closing bracket in synthetic string doesn't have a number of objects at '%s'\n", attrs);
 346     errno = EINVAL;
 347     return -1;
 348   }
 349 
 350   while (')' != *attrs) {
 351     int iscache = hwloc__obj_type_is_cache(type);
 352 
 353     if (iscache && !strncmp("size=", attrs, 5)) {
 354       memorysize = hwloc_synthetic_parse_memory_attr(attrs+5, &attrs);
 355 
 356     } else if (!iscache && !strncmp("memory=", attrs, 7)) {
 357       memorysize = hwloc_synthetic_parse_memory_attr(attrs+7, &attrs);
 358 
 359     } else if (!strncmp("indexes=", attrs, 8)) {
 360       index_string = attrs+8;
 361       attrs += 8;
 362       index_string_length = strcspn(attrs, " )");
 363       attrs += index_string_length;
 364 
 365     } else {
 366       if (verbose)
 367         fprintf(stderr, "Unknown attribute at '%s'\n", attrs);
 368       errno = EINVAL;
 369       return -1;
 370     }
 371 
 372     if (' ' == *attrs)
 373       attrs++;
 374     else if (')' != *attrs) {
 375       if (verbose)
 376         fprintf(stderr, "Missing parameter separator at '%s'\n", attrs);
 377       errno = EINVAL;
 378       return -1;
 379     }
 380   }
 381 
 382   sattr->memorysize = memorysize;
 383 
 384   if (index_string) {
 385     if (sind->string && verbose)
 386       fprintf(stderr, "Overwriting duplicate indexes attribute with last occurence\n");
 387     sind->string = index_string;
 388     sind->string_length = (unsigned long)index_string_length;
 389   }
 390 
 391   *next_posp = next_pos+1;
 392   return 0;
 393 }
 394 
 395 /* frees level until arity = 0 */
 396 static void
 397 hwloc_synthetic_free_levels(struct hwloc_synthetic_backend_data_s *data)
 398 {
 399   unsigned i;
 400   for(i=0; i<HWLOC_SYNTHETIC_MAX_DEPTH; i++) {
 401     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
 402     struct hwloc_synthetic_attached_s **pprev = &curlevel->attached;
 403     while (*pprev) {
 404       struct hwloc_synthetic_attached_s *cur = *pprev;
 405       *pprev = cur->next;
 406       free(cur);
 407     }
 408     free(curlevel->indexes.array);
 409     if (!curlevel->arity)
 410       break;
 411   }
 412   free(data->numa_attached_indexes.array);
 413 }
 414 
 415 /* Read from description a series of integers describing a symmetrical
 416    topology and update the hwloc_synthetic_backend_data_s accordingly.  On
 417    success, return zero.  */
 418 static int
 419 hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data,
 420                              const char *description)
 421 {
 422   const char *pos, *next_pos;
 423   unsigned long item, count;
 424   unsigned i;
 425   int type_count[HWLOC_OBJ_TYPE_MAX];
 426   unsigned unset;
 427   int verbose = 0;
 428   const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE");
 429   int err;
 430   unsigned long totalarity = 1;
 431 
 432   if (env)
 433     verbose = atoi(env);
 434 
 435   data->numa_attached_nr = 0;
 436   data->numa_attached_indexes.array = NULL;
 437 
 438   /* default values before we add root attributes */
 439   data->level[0].totalwidth = 1;
 440   data->level[0].attr.type = HWLOC_OBJ_MACHINE;
 441   data->level[0].indexes.string = NULL;
 442   data->level[0].indexes.array = NULL;
 443   data->level[0].attr.memorysize = 0;
 444   data->level[0].attached = NULL;
 445   type_count[HWLOC_OBJ_MACHINE] = 1;
 446   if (*description == '(') {
 447     err = hwloc_synthetic_parse_attrs(description+1, &description, &data->level[0].attr, &data->level[0].indexes, verbose);
 448     if (err < 0)
 449       return err;
 450   }
 451 
 452   data->numa_attached_indexes.string = NULL;
 453   data->numa_attached_indexes.array = NULL;
 454 
 455   for (pos = description, count = 1; *pos; pos = next_pos) {
 456     hwloc_obj_type_t type = HWLOC_OBJ_TYPE_NONE;
 457     union hwloc_obj_attr_u attrs;
 458 
 459     /* initialize parent arity to 0 so that the levels are not infinite */
 460     data->level[count-1].arity = 0;
 461 
 462     while (*pos == ' ')
 463       pos++;
 464 
 465     if (!*pos)
 466       break;
 467 
 468     if (*pos == '[') {
 469       /* attached */
 470       struct hwloc_synthetic_attached_s *attached, **pprev;
 471       char *attr;
 472 
 473       pos++;
 474 
 475       if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) {
 476         if (verbose)
 477           fprintf(stderr, "Synthetic string with unknown attached object type at '%s'\n", pos);
 478         errno = EINVAL;
 479         goto error;
 480       }
 481       if (type != HWLOC_OBJ_NUMANODE) {
 482         if (verbose)
 483           fprintf(stderr, "Synthetic string with disallowed attached object type at '%s'\n", pos);
 484         errno = EINVAL;
 485         goto error;
 486       }
 487       data->numa_attached_nr += data->level[count-1].totalwidth;
 488 
 489       attached = malloc(sizeof(*attached));
 490       if (attached) {
 491         attached->attr.type = type;
 492         attached->attr.memorysize = 0;
 493         /* attached->attr.depth and .cachetype unused */
 494         attached->next = NULL;
 495         pprev = &data->level[count-1].attached;
 496         while (*pprev)
 497           pprev = &((*pprev)->next);
 498         *pprev = attached;
 499       }
 500 
 501       next_pos = strchr(pos, ']');
 502       if (!next_pos) {
 503         if (verbose)
 504           fprintf(stderr,"Synthetic string doesn't have a closing `]' after attached object type at '%s'\n", pos);
 505         errno = EINVAL;
 506         goto error;
 507       }
 508 
 509       attr = strchr(pos, '(');
 510       if (attr && attr < next_pos && attached) {
 511         const char *dummy;
 512         err = hwloc_synthetic_parse_attrs(attr+1, &dummy, &attached->attr, &data->numa_attached_indexes, verbose);
 513         if (err < 0)
 514           goto error;
 515       }
 516 
 517       next_pos++;
 518       continue;
 519     }
 520 
 521     /* normal level */
 522 
 523     /* reset defaults */
 524     data->level[count].indexes.string = NULL;
 525     data->level[count].indexes.array = NULL;
 526     data->level[count].attached = NULL;
 527 
 528     if (*pos < '0' || *pos > '9') {
 529       if (hwloc_type_sscanf(pos, &type, &attrs, sizeof(attrs)) < 0) {
 530         /* FIXME: allow generic "Cache" string? would require to deal with possibly duplicate cache levels */
 531         if (verbose)
 532           fprintf(stderr, "Synthetic string with unknown object type at '%s'\n", pos);
 533         errno = EINVAL;
 534         goto error;
 535       }
 536       if (type == HWLOC_OBJ_MACHINE || type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
 537         if (verbose)
 538           fprintf(stderr, "Synthetic string with disallowed object type at '%s'\n", pos);
 539         errno = EINVAL;
 540         goto error;
 541       }
 542 
 543       next_pos = strchr(pos, ':');
 544       if (!next_pos) {
 545         if (verbose)
 546           fprintf(stderr,"Synthetic string doesn't have a `:' after object type at '%s'\n", pos);
 547         errno = EINVAL;
 548         goto error;
 549       }
 550       pos = next_pos + 1;
 551     }
 552 
 553     data->level[count].attr.type = type;
 554     data->level[count].attr.depth = (unsigned) -1;
 555     data->level[count].attr.cachetype = (hwloc_obj_cache_type_t) -1;
 556     if (hwloc__obj_type_is_cache(type)) {
 557       /* these are always initialized */
 558       data->level[count].attr.depth = attrs.cache.depth;
 559       data->level[count].attr.cachetype = attrs.cache.type;
 560     } else if (type == HWLOC_OBJ_GROUP) {
 561       /* could be -1 but will be set below */
 562       data->level[count].attr.depth = attrs.group.depth;
 563     }
 564 
 565     /* number of normal children */
 566     item = strtoul(pos, (char **)&next_pos, 0);
 567     if (next_pos == pos) {
 568       if (verbose)
 569         fprintf(stderr,"Synthetic string doesn't have a number of objects at '%s'\n", pos);
 570       errno = EINVAL;
 571       goto error;
 572     }
 573     if (!item) {
 574       if (verbose)
 575         fprintf(stderr,"Synthetic string with disallow 0 number of objects at '%s'\n", pos);
 576       errno = EINVAL;
 577       goto error;
 578     }
 579     data->level[count-1].arity = (unsigned)item;
 580 
 581     totalarity *= item;
 582     data->level[count].totalwidth = totalarity;
 583     data->level[count].indexes.string = NULL;
 584     data->level[count].indexes.array = NULL;
 585     data->level[count].attr.memorysize = 0;
 586     if (*next_pos == '(') {
 587       err = hwloc_synthetic_parse_attrs(next_pos+1, &next_pos, &data->level[count].attr, &data->level[count].indexes, verbose);
 588       if (err < 0)
 589         goto error;
 590     }
 591 
 592     if (count + 1 >= HWLOC_SYNTHETIC_MAX_DEPTH) {
 593       if (verbose)
 594         fprintf(stderr,"Too many synthetic levels, max %d\n", HWLOC_SYNTHETIC_MAX_DEPTH);
 595       errno = EINVAL;
 596       goto error;
 597     }
 598     if (item > UINT_MAX) {
 599       if (verbose)
 600         fprintf(stderr,"Too big arity, max %u\n", UINT_MAX);
 601       errno = EINVAL;
 602       goto error;
 603     }
 604 
 605     count++;
 606   }
 607 
 608   if (data->level[count-1].attr.type != HWLOC_OBJ_TYPE_NONE && data->level[count-1].attr.type != HWLOC_OBJ_PU) {
 609     if (verbose)
 610       fprintf(stderr, "Synthetic string cannot use non-PU type for last level\n");
 611     errno = EINVAL;
 612     return -1;
 613   }
 614   data->level[count-1].attr.type = HWLOC_OBJ_PU;
 615 
 616   for(i=HWLOC_OBJ_TYPE_MIN; i<HWLOC_OBJ_TYPE_MAX; i++) {
 617     type_count[i] = 0;
 618   }
 619   for(i=count-1; i>0; i--) {
 620     hwloc_obj_type_t type = data->level[i].attr.type;
 621     if (type != HWLOC_OBJ_TYPE_NONE) {
 622       type_count[type]++;
 623     }
 624   }
 625 
 626   /* sanity checks */
 627   if (!type_count[HWLOC_OBJ_PU]) {
 628     if (verbose)
 629       fprintf(stderr, "Synthetic string missing ending number of PUs\n");
 630     errno = EINVAL;
 631     return -1;
 632   } else if (type_count[HWLOC_OBJ_PU] > 1) {
 633     if (verbose)
 634       fprintf(stderr, "Synthetic string cannot have several PU levels\n");
 635     errno = EINVAL;
 636     return -1;
 637   }
 638   if (type_count[HWLOC_OBJ_PACKAGE] > 1) {
 639     if (verbose)
 640       fprintf(stderr, "Synthetic string cannot have several package levels\n");
 641     errno = EINVAL;
 642     return -1;
 643   }
 644   if (type_count[HWLOC_OBJ_NUMANODE] > 1) {
 645     if (verbose)
 646       fprintf(stderr, "Synthetic string cannot have several NUMA node levels\n");
 647     errno = EINVAL;
 648     return -1;
 649   }
 650   if (type_count[HWLOC_OBJ_NUMANODE] && data->numa_attached_nr) {
 651     if (verbose)
 652       fprintf(stderr,"Synthetic string cannot have NUMA nodes both as a level and attached\n");
 653     errno = EINVAL;
 654     return -1;
 655   }
 656   if (type_count[HWLOC_OBJ_CORE] > 1) {
 657     if (verbose)
 658       fprintf(stderr, "Synthetic string cannot have several core levels\n");
 659     errno = EINVAL;
 660     return -1;
 661   }
 662 
 663   /* deal with missing intermediate levels */
 664   unset = 0;
 665   for(i=1; i<count-1; i++) {
 666     if (data->level[i].attr.type == HWLOC_OBJ_TYPE_NONE)
 667       unset++;
 668   }
 669   if (unset && unset != count-2) {
 670     if (verbose)
 671       fprintf(stderr, "Synthetic string cannot mix unspecified and specified types for levels\n");
 672     errno = EINVAL;
 673     return -1;
 674   }
 675   if (unset) {
 676     /* we want in priority: numa, package, core, up to 3 caches, groups */
 677     unsigned _count = count;
 678     unsigned neednuma = 0;
 679     unsigned needpack = 0;
 680     unsigned needcore = 0;
 681     unsigned needcaches = 0;
 682     unsigned needgroups = 0;
 683     /* 2 levels for machine and PU */
 684     _count -= 2;
 685 
 686     neednuma = (_count >= 1 && !data->numa_attached_nr);
 687     _count -= neednuma;
 688 
 689     needpack = (_count >= 1);
 690     _count -= needpack;
 691 
 692     needcore = (_count >= 1);
 693     _count -= needcore;
 694 
 695     needcaches = (_count > 4 ? 4 : _count);
 696     _count -= needcaches;
 697 
 698     needgroups = _count;
 699 
 700     /* we place them in order: groups, package, numa, caches, core */
 701     for(i = 0; i < needgroups; i++) {
 702       unsigned depth = 1 + i;
 703       data->level[depth].attr.type = HWLOC_OBJ_GROUP;
 704       type_count[HWLOC_OBJ_GROUP]++;
 705     }
 706     if (needpack) {
 707       unsigned depth = 1 + needgroups;
 708       data->level[depth].attr.type = HWLOC_OBJ_PACKAGE;
 709       type_count[HWLOC_OBJ_PACKAGE] = 1;
 710     }
 711     if (neednuma) {
 712       unsigned depth = 1 + needgroups + needpack;
 713       data->level[depth].attr.type = HWLOC_OBJ_NUMANODE;
 714       type_count[HWLOC_OBJ_NUMANODE] = 1;
 715     }
 716     if (needcaches) {
 717       /* priority: l2, l1, l3, l1i */
 718       /* order: l3, l2, l1, l1i */
 719       unsigned l3depth = 1 + needgroups + needpack + neednuma;
 720       unsigned l2depth = l3depth + (needcaches >= 3);
 721       unsigned l1depth = l2depth + 1;
 722       unsigned l1idepth = l1depth + 1;
 723       if (needcaches >= 3) {
 724         data->level[l3depth].attr.type = HWLOC_OBJ_L3CACHE;
 725         data->level[l3depth].attr.depth = 3;
 726         data->level[l3depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED;
 727         type_count[HWLOC_OBJ_L3CACHE] = 1;
 728       }
 729       data->level[l2depth].attr.type = HWLOC_OBJ_L2CACHE;
 730       data->level[l2depth].attr.depth = 2;
 731       data->level[l2depth].attr.cachetype = HWLOC_OBJ_CACHE_UNIFIED;
 732       type_count[HWLOC_OBJ_L2CACHE] = 1;
 733       if (needcaches >= 2) {
 734         data->level[l1depth].attr.type = HWLOC_OBJ_L1CACHE;
 735         data->level[l1depth].attr.depth = 1;
 736         data->level[l1depth].attr.cachetype = HWLOC_OBJ_CACHE_DATA;
 737         type_count[HWLOC_OBJ_L1CACHE] = 1;
 738       }
 739       if (needcaches >= 4) {
 740         data->level[l1idepth].attr.type = HWLOC_OBJ_L1ICACHE;
 741         data->level[l1idepth].attr.depth = 1;
 742         data->level[l1idepth].attr.cachetype = HWLOC_OBJ_CACHE_INSTRUCTION;
 743         type_count[HWLOC_OBJ_L1ICACHE] = 1;
 744       }
 745     }
 746     if (needcore) {
 747       unsigned depth = 1 + needgroups + needpack + neednuma + needcaches;
 748       data->level[depth].attr.type = HWLOC_OBJ_CORE;
 749       type_count[HWLOC_OBJ_CORE] = 1;
 750     }
 751   }
 752 
 753   /* enforce a NUMA level */
 754   if (!type_count[HWLOC_OBJ_NUMANODE] && !data->numa_attached_nr) {
 755     /* insert a NUMA level below the automatic machine root */
 756     if (verbose)
 757       fprintf(stderr, "Inserting a NUMA level with a single object at depth 1\n");
 758     /* move existing levels by one */
 759     memmove(&data->level[2], &data->level[1], count*sizeof(struct hwloc_synthetic_level_data_s));
 760     data->level[1].attr.type = HWLOC_OBJ_NUMANODE;
 761     data->level[1].indexes.string = NULL;
 762     data->level[1].indexes.array = NULL;
 763     data->level[1].attr.memorysize = 0;
 764     data->level[1].totalwidth = data->level[0].totalwidth;
 765     /* update arity to insert a single NUMA node per parent */
 766     data->level[1].arity = data->level[0].arity;
 767     data->level[0].arity = 1;
 768     count++;
 769   }
 770 
 771   for (i=0; i<count; i++) {
 772     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
 773     hwloc_obj_type_t type = curlevel->attr.type;
 774 
 775     if (type == HWLOC_OBJ_GROUP) {
 776       if (curlevel->attr.depth == (unsigned)-1)
 777         curlevel->attr.depth = type_count[HWLOC_OBJ_GROUP]--;
 778 
 779     } else if (hwloc__obj_type_is_cache(type)) {
 780       if (!curlevel->attr.memorysize) {
 781         if (1 == curlevel->attr.depth)
 782           /* 32Kb in L1 */
 783           curlevel->attr.memorysize = 32*1024;
 784         else
 785           /* *4 at each level, starting from 1MB for L2, unified */
 786           curlevel->attr.memorysize = 256*1024 << (2*curlevel->attr.depth);
 787       }
 788 
 789     } else if (type == HWLOC_OBJ_NUMANODE && !curlevel->attr.memorysize) {
 790       /* 1GB in memory nodes. */
 791       curlevel->attr.memorysize = 1024*1024*1024;
 792     }
 793 
 794     hwloc_synthetic_process_indexes(data, &data->level[i].indexes, data->level[i].totalwidth, verbose);
 795   }
 796 
 797   hwloc_synthetic_process_indexes(data, &data->numa_attached_indexes, data->numa_attached_nr, verbose);
 798 
 799   data->string = strdup(description);
 800   data->level[count-1].arity = 0;
 801   return 0;
 802 
 803  error:
 804   hwloc_synthetic_free_levels(data);
 805   return -1;
 806 }
 807 
 808 static void
 809 hwloc_synthetic_set_attr(struct hwloc_synthetic_attr_s *sattr,
 810                          hwloc_obj_t obj)
 811 {
 812   switch (obj->type) {
 813   case HWLOC_OBJ_GROUP:
 814     obj->attr->group.kind = HWLOC_GROUP_KIND_SYNTHETIC;
 815     obj->attr->group.subkind = sattr->depth-1;
 816     break;
 817   case HWLOC_OBJ_MACHINE:
 818     break;
 819   case HWLOC_OBJ_NUMANODE:
 820     obj->attr->numanode.local_memory = sattr->memorysize;
 821     obj->attr->numanode.page_types_len = 1;
 822     obj->attr->numanode.page_types = malloc(sizeof(*obj->attr->numanode.page_types));
 823     memset(obj->attr->numanode.page_types, 0, sizeof(*obj->attr->numanode.page_types));
 824     obj->attr->numanode.page_types[0].size = 4096;
 825     obj->attr->numanode.page_types[0].count = sattr->memorysize / 4096;
 826     break;
 827   case HWLOC_OBJ_PACKAGE:
 828     break;
 829   case HWLOC_OBJ_L1CACHE:
 830   case HWLOC_OBJ_L2CACHE:
 831   case HWLOC_OBJ_L3CACHE:
 832   case HWLOC_OBJ_L4CACHE:
 833   case HWLOC_OBJ_L5CACHE:
 834   case HWLOC_OBJ_L1ICACHE:
 835   case HWLOC_OBJ_L2ICACHE:
 836   case HWLOC_OBJ_L3ICACHE:
 837     obj->attr->cache.depth = sattr->depth;
 838     obj->attr->cache.linesize = 64;
 839     obj->attr->cache.type = sattr->cachetype;
 840     obj->attr->cache.size = sattr->memorysize;
 841     break;
 842   case HWLOC_OBJ_CORE:
 843     break;
 844   case HWLOC_OBJ_PU:
 845     break;
 846   default:
 847     /* Should never happen */
 848     assert(0);
 849     break;
 850   }
 851 }
 852 
 853 static unsigned
 854 hwloc_synthetic_next_index(struct hwloc_synthetic_indexes_s *indexes, hwloc_obj_type_t type)
 855 {
 856   unsigned os_index = indexes->next++;
 857 
 858   if (indexes->array)
 859     os_index = indexes->array[os_index];
 860   else if (hwloc__obj_type_is_cache(type) || type == HWLOC_OBJ_GROUP)
 861     /* don't enforce useless os_indexes for Caches and Groups */
 862     os_index = HWLOC_UNKNOWN_INDEX;
 863 
 864   return os_index;
 865 }
 866 
 867 static void
 868 hwloc_synthetic_insert_attached(struct hwloc_topology *topology,
 869                                 struct hwloc_synthetic_backend_data_s *data,
 870                                 struct hwloc_synthetic_attached_s *attached,
 871                                 hwloc_bitmap_t set)
 872 {
 873   hwloc_obj_t child;
 874   unsigned attached_os_index;
 875 
 876   if (!attached)
 877     return;
 878 
 879   assert(attached->attr.type == HWLOC_OBJ_NUMANODE);
 880 
 881   attached_os_index = hwloc_synthetic_next_index(&data->numa_attached_indexes, HWLOC_OBJ_NUMANODE);
 882 
 883   child = hwloc_alloc_setup_object(topology, attached->attr.type, attached_os_index);
 884   child->cpuset = hwloc_bitmap_dup(set);
 885 
 886   child->nodeset = hwloc_bitmap_alloc();
 887   hwloc_bitmap_set(child->nodeset, attached_os_index);
 888 
 889   hwloc_synthetic_set_attr(&attached->attr, child);
 890 
 891   hwloc_insert_object_by_cpuset(topology, child);
 892 
 893   hwloc_synthetic_insert_attached(topology, data, attached->next, set);
 894 }
 895 
 896 /*
 897  * Recursively build objects whose cpu start at first_cpu
 898  * - level gives where to look in the type, arity and id arrays
 899  * - the id array is used as a variable to get unique IDs for a given level.
 900  * - generated memory should be added to *memory_kB.
 901  * - generated cpus should be added to parent_cpuset.
 902  * - next cpu number to be used should be returned.
 903  */
 904 static void
 905 hwloc__look_synthetic(struct hwloc_topology *topology,
 906                       struct hwloc_synthetic_backend_data_s *data,
 907                       int level,
 908                       hwloc_bitmap_t parent_cpuset)
 909 {
 910   hwloc_obj_t obj;
 911   unsigned i;
 912   struct hwloc_synthetic_level_data_s *curlevel = &data->level[level];
 913   hwloc_obj_type_t type = curlevel->attr.type;
 914   hwloc_bitmap_t set;
 915   unsigned os_index;
 916 
 917   assert(hwloc__obj_type_is_normal(type) || type == HWLOC_OBJ_NUMANODE);
 918   assert(type != HWLOC_OBJ_MACHINE);
 919 
 920   os_index = hwloc_synthetic_next_index(&curlevel->indexes, type);
 921 
 922   set = hwloc_bitmap_alloc();
 923   if (!curlevel->arity) {
 924     hwloc_bitmap_set(set, os_index);
 925   } else {
 926     for (i = 0; i < curlevel->arity; i++)
 927       hwloc__look_synthetic(topology, data, level + 1, set);
 928   }
 929 
 930   hwloc_bitmap_or(parent_cpuset, parent_cpuset, set);
 931 
 932   if (hwloc_filter_check_keep_object_type(topology, type)) {
 933     obj = hwloc_alloc_setup_object(topology, type, os_index);
 934     obj->cpuset = hwloc_bitmap_dup(set);
 935 
 936     if (type == HWLOC_OBJ_NUMANODE) {
 937       obj->nodeset = hwloc_bitmap_alloc();
 938       hwloc_bitmap_set(obj->nodeset, os_index);
 939     }
 940 
 941     hwloc_synthetic_set_attr(&curlevel->attr, obj);
 942 
 943     hwloc_insert_object_by_cpuset(topology, obj);
 944   }
 945 
 946   hwloc_synthetic_insert_attached(topology, data, curlevel->attached, set);
 947 
 948   hwloc_bitmap_free(set);
 949 }
 950 
 951 static int
 952 hwloc_look_synthetic(struct hwloc_backend *backend)
 953 {
 954   struct hwloc_topology *topology = backend->topology;
 955   struct hwloc_synthetic_backend_data_s *data = backend->private_data;
 956   hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
 957   unsigned i;
 958 
 959   assert(!topology->levels[0][0]->cpuset);
 960 
 961   hwloc_alloc_root_sets(topology->levels[0][0]);
 962 
 963   topology->support.discovery->pu = 1;
 964   topology->support.discovery->numa = 1; /* we add a single NUMA node if none is given */
 965   topology->support.discovery->numa_memory = 1; /* specified or default size */
 966 
 967   /* start with os_index 0 for each level */
 968   for (i = 0; data->level[i].arity > 0; i++)
 969     data->level[i].indexes.next = 0;
 970   data->numa_attached_indexes.next = 0;
 971   /* ... including the last one */
 972   data->level[i].indexes.next = 0;
 973 
 974   /* update first level type according to the synthetic type array */
 975   topology->levels[0][0]->type = data->level[0].attr.type;
 976   hwloc_synthetic_set_attr(&data->level[0].attr, topology->levels[0][0]);
 977 
 978   for (i = 0; i < data->level[0].arity; i++)
 979     hwloc__look_synthetic(topology, data, 1, cpuset);
 980 
 981   hwloc_synthetic_insert_attached(topology, data, data->level[0].attached, cpuset);
 982 
 983   hwloc_bitmap_free(cpuset);
 984 
 985   hwloc_obj_add_info(topology->levels[0][0], "Backend", "Synthetic");
 986   hwloc_obj_add_info(topology->levels[0][0], "SyntheticDescription", data->string);
 987   return 0;
 988 }
 989 
 990 static void
 991 hwloc_synthetic_backend_disable(struct hwloc_backend *backend)
 992 {
 993   struct hwloc_synthetic_backend_data_s *data = backend->private_data;
 994   hwloc_synthetic_free_levels(data);
 995   free(data->string);
 996   free(data);
 997 }
 998 
 999 static struct hwloc_backend *
1000 hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component,
1001                                       const void *_data1,
1002                                       const void *_data2 __hwloc_attribute_unused,
1003                                       const void *_data3 __hwloc_attribute_unused)
1004 {
1005   struct hwloc_backend *backend;
1006   struct hwloc_synthetic_backend_data_s *data;
1007   int err;
1008 
1009   if (!_data1) {
1010     const char *env = getenv("HWLOC_SYNTHETIC");
1011     if (env) {
1012       /* 'synthetic' was given in HWLOC_COMPONENTS without a description */
1013       _data1 = env;
1014     } else {
1015       errno = EINVAL;
1016       goto out;
1017     }
1018   }
1019 
1020   backend = hwloc_backend_alloc(component);
1021   if (!backend)
1022     goto out;
1023 
1024   data = malloc(sizeof(*data));
1025   if (!data) {
1026     errno = ENOMEM;
1027     goto out_with_backend;
1028   }
1029 
1030   err = hwloc_backend_synthetic_init(data, (const char *) _data1);
1031   if (err < 0)
1032     goto out_with_data;
1033 
1034   backend->private_data = data;
1035   backend->discover = hwloc_look_synthetic;
1036   backend->disable = hwloc_synthetic_backend_disable;
1037   backend->is_thissystem = 0;
1038 
1039   return backend;
1040 
1041  out_with_data:
1042   free(data);
1043  out_with_backend:
1044   free(backend);
1045  out:
1046   return NULL;
1047 }
1048 
1049 static struct hwloc_disc_component hwloc_synthetic_disc_component = {
1050   HWLOC_DISC_COMPONENT_TYPE_GLOBAL,
1051   "synthetic",
1052   ~0,
1053   hwloc_synthetic_component_instantiate,
1054   30,
1055   1,
1056   NULL
1057 };
1058 
1059 const struct hwloc_component hwloc_synthetic_component = {
1060   HWLOC_COMPONENT_ABI,
1061   NULL, NULL,
1062   HWLOC_COMPONENT_TYPE_DISC,
1063   0,
1064   &hwloc_synthetic_disc_component
1065 };
1066 
1067 static __hwloc_inline int
1068 hwloc__export_synthetic_update_status(int *ret, char **tmp, ssize_t *tmplen, int res)
1069 {
1070   if (res < 0)
1071     return -1;
1072   *ret += res;
1073   if (res >= *tmplen)
1074     res = *tmplen>0 ? (int)(*tmplen) - 1 : 0;
1075   *tmp += res;
1076   *tmplen -= res;
1077   return 0;
1078 }
1079 
1080 static __hwloc_inline void
1081 hwloc__export_synthetic_add_char(int *ret, char **tmp, ssize_t *tmplen, char c)
1082 {
1083   if (*tmplen > 1) {
1084     (*tmp)[0] = c;
1085     (*tmp)[1] = '\0';
1086     (*tmp)++;
1087     (*tmplen)--;
1088   }
1089   (*ret)++;
1090 }
1091 
1092 static int
1093 hwloc__export_synthetic_indexes(hwloc_obj_t *level, unsigned total,
1094                                 char *buffer, size_t buflen)
1095 {
1096   unsigned step = 1;
1097   unsigned nr_loops = 0;
1098   struct hwloc_synthetic_intlv_loop_s *loops = NULL, *tmploops;
1099   hwloc_obj_t cur;
1100   unsigned i, j;
1101   ssize_t tmplen = buflen;
1102   char *tmp = buffer;
1103   int res, ret = 0;
1104 
1105   /* must start with 0 */
1106   if (level[0]->os_index)
1107     goto exportall;
1108 
1109   while (step != total) {
1110     /* must be a divider of the total */
1111     if (total % step)
1112       goto exportall;
1113 
1114     /* look for os_index == step */
1115     for(i=1; i<total; i++)
1116       if (level[i]->os_index == step)
1117         break;
1118     if (i == total)
1119       goto exportall;
1120     for(j=2; j<total/i; j++)
1121       if (level[i*j]->os_index != step*j)
1122         break;
1123 
1124     nr_loops++;
1125     tmploops = realloc(loops, nr_loops*sizeof(*loops));
1126     if (!tmploops)
1127       goto exportall;
1128     loops = tmploops;
1129     loops[nr_loops-1].step = i;
1130     loops[nr_loops-1].nb = j;
1131     step *= j;
1132   }
1133 
1134   /* check this interleaving */
1135   for(i=0; i<total; i++) {
1136     unsigned ind = 0;
1137     unsigned mul = 1;
1138     for(j=0; j<nr_loops; j++) {
1139       ind += (i / loops[j].step) % loops[j].nb * mul;
1140       mul *= loops[j].nb;
1141     }
1142     if (level[i]->os_index != ind)
1143       goto exportall;
1144   }
1145 
1146   /* success, print it */
1147   for(j=0; j<nr_loops; j++) {
1148     res = hwloc_snprintf(tmp, tmplen, "%u*%u%s", loops[j].step, loops[j].nb,
1149                          j == nr_loops-1 ? ")" : ":");
1150     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0) {
1151       free(loops);
1152       return -1;
1153     }
1154   }
1155 
1156   free(loops);
1157   return ret;
1158 
1159  exportall:
1160   free(loops);
1161 
1162   /* dump all indexes */
1163   cur = level[0];
1164   while (cur) {
1165     res = snprintf(tmp, tmplen, "%u%s", cur->os_index,
1166                    cur->next_cousin ? "," : ")");
1167     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1168       return -1;
1169     cur = cur->next_cousin;
1170   }
1171   return ret;
1172 }
1173 
1174 static int
1175 hwloc__export_synthetic_obj_attr(struct hwloc_topology * topology,
1176                                  hwloc_obj_t obj,
1177                                  char *buffer, size_t buflen)
1178 {
1179   const char * separator = " ";
1180   const char * prefix = "(";
1181   char cachesize[64] = "";
1182   char memsize[64] = "";
1183   int needindexes = 0;
1184 
1185   if (hwloc__obj_type_is_cache(obj->type) && obj->attr->cache.size) {
1186     snprintf(cachesize, sizeof(cachesize), "%ssize=%llu",
1187              prefix, (unsigned long long) obj->attr->cache.size);
1188     prefix = separator;
1189   }
1190   if (obj->type == HWLOC_OBJ_NUMANODE && obj->attr->numanode.local_memory) {
1191     snprintf(memsize, sizeof(memsize), "%smemory=%llu",
1192              prefix, (unsigned long long) obj->attr->numanode.local_memory);
1193     prefix = separator;
1194   }
1195   if (!obj->logical_index /* only display indexes once per level (not for non-first NUMA children, etc.) */
1196       && (obj->type == HWLOC_OBJ_PU || obj->type == HWLOC_OBJ_NUMANODE)) {
1197     hwloc_obj_t cur = obj;
1198     while (cur) {
1199       if (cur->os_index != cur->logical_index) {
1200         needindexes = 1;
1201         break;
1202       }
1203       cur = cur->next_cousin;
1204     }
1205   }
1206   if (*cachesize || *memsize || needindexes) {
1207     ssize_t tmplen = buflen;
1208     char *tmp = buffer;
1209     int res, ret = 0;
1210 
1211     res = hwloc_snprintf(tmp, tmplen, "%s%s%s", cachesize, memsize, needindexes ? "" : ")");
1212     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1213       return -1;
1214 
1215     if (needindexes) {
1216       unsigned total;
1217       hwloc_obj_t *level;
1218 
1219       if (obj->depth < 0) {
1220         assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE);
1221         total = topology->slevels[HWLOC_SLEVEL_NUMANODE].nbobjs;
1222         level = topology->slevels[HWLOC_SLEVEL_NUMANODE].objs;
1223       } else {
1224         total = topology->level_nbobjects[obj->depth];
1225         level = topology->levels[obj->depth];
1226       }
1227 
1228       res = snprintf(tmp, tmplen, "%sindexes=", prefix);
1229       if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1230         return -1;
1231 
1232       res = hwloc__export_synthetic_indexes(level, total, tmp, tmplen);
1233       if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1234         return -1;
1235     }
1236     return ret;
1237   } else {
1238     return 0;
1239   }
1240 }
1241 
1242 static int
1243 hwloc__export_synthetic_obj(struct hwloc_topology * topology, unsigned long flags,
1244                             hwloc_obj_t obj, unsigned arity,
1245                             char *buffer, size_t buflen)
1246 {
1247   char aritys[12] = "";
1248   ssize_t tmplen = buflen;
1249   char *tmp = buffer;
1250   int res, ret = 0;
1251 
1252   /* <type>:<arity>, except for root */
1253   if (arity != (unsigned)-1)
1254     snprintf(aritys, sizeof(aritys), ":%u", arity);
1255   if (hwloc__obj_type_is_cache(obj->type)
1256       && (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES)) {
1257     /* v1 uses generic "Cache" for non-extended type name */
1258     res = hwloc_snprintf(tmp, tmplen, "Cache%s", aritys);
1259 
1260   } else if (obj->type == HWLOC_OBJ_PACKAGE
1261              && (flags & (HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES
1262                           |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1))) {
1263     /* if exporting to v1 or without extended-types, use all-v1-compatible Socket name */
1264     res = hwloc_snprintf(tmp, tmplen, "Socket%s", aritys);
1265 
1266   } else if (obj->type == HWLOC_OBJ_GROUP /* don't export group depth */
1267       || flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) {
1268     res = hwloc_snprintf(tmp, tmplen, "%s%s", hwloc_obj_type_string(obj->type), aritys);
1269   } else {
1270     char types[64];
1271     hwloc_obj_type_snprintf(types, sizeof(types), obj, 1);
1272     res = hwloc_snprintf(tmp, tmplen, "%s%s", types, aritys);
1273   }
1274   if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1275     return -1;
1276 
1277   if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
1278     /* obj attributes */
1279     res = hwloc__export_synthetic_obj_attr(topology, obj, tmp, tmplen);
1280     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1281       return -1;
1282   }
1283 
1284   return ret;
1285 }
1286 
1287 static int
1288 hwloc__export_synthetic_memory_children(struct hwloc_topology * topology, unsigned long flags,
1289                                         hwloc_obj_t parent,
1290                                         char *buffer, size_t buflen,
1291                                         int needprefix, int verbose)
1292 {
1293   hwloc_obj_t mchild;
1294   ssize_t tmplen = buflen;
1295   char *tmp = buffer;
1296   int res, ret = 0;
1297 
1298   mchild = parent->memory_first_child;
1299   if (!mchild)
1300     return 0;
1301 
1302   if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) {
1303     /* v1: export a single NUMA child */
1304     if (parent->memory_arity > 1 || mchild->type != HWLOC_OBJ_NUMANODE) {
1305       /* not supported */
1306       if (verbose)
1307         fprintf(stderr, "Cannot export to synthetic v1 if multiple memory children are attached to the same location.\n");
1308       errno = EINVAL;
1309       return -1;
1310     }
1311 
1312     if (needprefix)
1313       hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
1314 
1315     res = hwloc__export_synthetic_obj(topology, flags, mchild, 1, tmp, tmplen);
1316     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1317       return -1;
1318     return ret;
1319   }
1320 
1321   while (mchild) {
1322     /* v2: export all NUMA children */
1323 
1324     assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */
1325 
1326     if (needprefix)
1327       hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
1328 
1329     hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, '[');
1330 
1331     res = hwloc__export_synthetic_obj(topology, flags, mchild, (unsigned)-1, tmp, tmplen);
1332     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1333       return -1;
1334 
1335     hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ']');
1336 
1337     needprefix = 1;
1338     mchild = mchild->next_sibling;
1339   }
1340 
1341   return ret;
1342 }
1343 
1344 static int
1345 hwloc_check_memory_symmetric(struct hwloc_topology * topology)
1346 {
1347   hwloc_bitmap_t remaining_nodes;
1348 
1349   remaining_nodes = hwloc_bitmap_dup(hwloc_get_root_obj(topology)->nodeset);
1350   if (!remaining_nodes)
1351     /* assume asymmetric */
1352     return -1;
1353 
1354   while (!hwloc_bitmap_iszero(remaining_nodes)) {
1355     unsigned idx;
1356     hwloc_obj_t node;
1357     hwloc_obj_t first_parent;
1358     unsigned i;
1359 
1360     idx = hwloc_bitmap_first(remaining_nodes);
1361     node = hwloc_get_numanode_obj_by_os_index(topology, idx);
1362     assert(node);
1363 
1364     first_parent = node->parent;
1365     assert(hwloc__obj_type_is_normal(first_parent->type)); /* only depth-1 memory children for now */
1366 
1367     /* check whether all object on parent's level have same number of NUMA children */
1368     for(i=0; i<hwloc_get_nbobjs_by_depth(topology, first_parent->depth); i++) {
1369       hwloc_obj_t parent, mchild;
1370 
1371       parent = hwloc_get_obj_by_depth(topology, first_parent->depth, i);
1372       assert(parent);
1373 
1374       /* must have same memory arity */
1375       if (parent->memory_arity != first_parent->memory_arity)
1376         goto out_with_bitmap;
1377 
1378       /* clear these NUMA children from remaining_nodes */
1379       mchild = parent->memory_first_child;
1380       while (mchild) {
1381         assert(mchild->type == HWLOC_OBJ_NUMANODE); /* only NUMA node memory children for now */
1382         hwloc_bitmap_clr(remaining_nodes, mchild->os_index); /* cannot use parent->nodeset, some normal children may have other NUMA nodes */
1383         mchild = mchild->next_sibling;
1384       }
1385     }
1386   }
1387 
1388   hwloc_bitmap_free(remaining_nodes);
1389   return 0;
1390 
1391  out_with_bitmap:
1392   hwloc_bitmap_free(remaining_nodes);
1393   return -1;
1394 }
1395 
1396 int
1397 hwloc_topology_export_synthetic(struct hwloc_topology * topology,
1398                                 char *buffer, size_t buflen,
1399                                 unsigned long flags)
1400 {
1401   hwloc_obj_t obj = hwloc_get_root_obj(topology);
1402   ssize_t tmplen = buflen;
1403   char *tmp = buffer;
1404   int res, ret = 0;
1405   unsigned arity;
1406   int needprefix = 0;
1407   int verbose = 0;
1408   const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE");
1409 
1410   if (env)
1411     verbose = atoi(env);
1412 
1413   if (!topology->is_loaded) {
1414     errno = EINVAL;
1415     return -1;
1416   }
1417 
1418   if (flags & ~(HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES
1419                 |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS
1420                 |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1
1421                 |HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
1422     errno = EINVAL;
1423     return -1;
1424   }
1425 
1426   /* TODO: add a flag to ignore symmetric_subtree and I/Os.
1427    * just assume things are symmetric with the left branches of the tree.
1428    * but the number of objects per level may be wrong, what to do with OS index array in this case?
1429    * only allow ignoring symmetric_subtree if the level width remains OK?
1430    */
1431 
1432   /* TODO: add a root object by default, with a prefix such as tree=
1433    * so that we can backward-compatibly recognize whether there's a root or not.
1434    * and add a flag to disable it.
1435    */
1436 
1437   /* TODO: flag to force all indexes, not only for PU and NUMA? */
1438 
1439   if (!obj->symmetric_subtree) {
1440     if (verbose)
1441       fprintf(stderr, "Cannot export to synthetic unless topology is symmetric (root->symmetric_subtree must be set).\n");
1442     errno = EINVAL;
1443     return -1;
1444   }
1445 
1446   if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)
1447       && hwloc_check_memory_symmetric(topology) < 0) {
1448     if (verbose)
1449       fprintf(stderr, "Cannot export to synthetic unless memory is attached symmetrically.\n");
1450     errno = EINVAL;
1451     return -1;
1452   }
1453 
1454   if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_V1) {
1455     /* v1 requires all NUMA at the same level */
1456     hwloc_obj_t node;
1457     signed pdepth;
1458 
1459     node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0);
1460     assert(hwloc__obj_type_is_normal(node->parent->type)); /* only depth-1 memory children for now */
1461     pdepth = node->parent->depth;
1462 
1463     while ((node = node->next_cousin) != NULL) {
1464       assert(hwloc__obj_type_is_normal(node->parent->type)); /* only depth-1 memory children for now */
1465       if (node->parent->depth != pdepth) {
1466         if (verbose)
1467           fprintf(stderr, "Cannot export to synthetic v1 if memory is attached to parents at different depths.\n");
1468         errno = EINVAL;
1469         return -1;
1470       }
1471     }
1472   }
1473 
1474   /* we're good, start exporting */
1475 
1476   if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
1477     /* obj attributes */
1478     res = hwloc__export_synthetic_obj_attr(topology, obj, tmp, tmplen);
1479     if (res > 0)
1480       needprefix = 1;
1481     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1482       return -1;
1483   }
1484 
1485   if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
1486     res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, needprefix, verbose);
1487     if (res > 0)
1488       needprefix = 1;
1489     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1490       return -1;
1491   }
1492 
1493   arity = obj->arity;
1494   while (arity) {
1495     /* for each level */
1496     obj = obj->first_child;
1497 
1498     if (needprefix)
1499       hwloc__export_synthetic_add_char(&ret, &tmp, &tmplen, ' ');
1500 
1501     res = hwloc__export_synthetic_obj(topology, flags, obj, arity, tmp, tmplen);
1502     if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1503       return -1;
1504 
1505     if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_IGNORE_MEMORY)) {
1506       res = hwloc__export_synthetic_memory_children(topology, flags, obj, tmp, tmplen, 1, verbose);
1507       if (hwloc__export_synthetic_update_status(&ret, &tmp, &tmplen, res) < 0)
1508         return -1;
1509     }
1510 
1511     /* next level */
1512     needprefix = 1;
1513     arity = obj->arity;
1514   }
1515 
1516   return ret;
1517 }

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