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