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_level_data_s {
22   unsigned arity;
23   unsigned long totalwidth;
24   hwloc_obj_type_t type;
25   unsigned depth; /* For caches/groups */
26   hwloc_obj_cache_type_t cachetype; /* For caches */
27   hwloc_uint64_t memorysize; /* For caches/memory */
28 
29   /* the indexes= attribute before parsing */
30   const char *index_string;
31   unsigned long index_string_length;
32   /* the array of explicit indexes after parsing */
33   unsigned *index_array;
34 
35   /* used while filling the topology */
36   unsigned next_os_index; /* id of the next object for that level */
37 };
38 
39 struct hwloc_synthetic_backend_data_s {
40   /* synthetic backend parameters */
41   char *string;
42 #define HWLOC_SYNTHETIC_MAX_DEPTH 128
43   struct hwloc_synthetic_level_data_s level[HWLOC_SYNTHETIC_MAX_DEPTH];
44 };
45 
46 struct hwloc_synthetic_intlv_loop_s {
47   unsigned step;
48   unsigned nb;
49   unsigned level_depth;
50 };
51 
52 static void
hwloc_synthetic_process_level_indexes(struct hwloc_synthetic_backend_data_s * data,unsigned curleveldepth,int verbose)53 hwloc_synthetic_process_level_indexes(struct hwloc_synthetic_backend_data_s *data,
54 				      unsigned curleveldepth,
55 				      int verbose)
56 {
57   struct hwloc_synthetic_level_data_s *curlevel = &data->level[curleveldepth];
58   unsigned long total = curlevel->totalwidth;
59   const char *attr = curlevel->index_string;
60   unsigned long length = curlevel->index_string_length;
61   unsigned *array = NULL;
62   struct hwloc_synthetic_intlv_loop_s * loops = NULL;
63   size_t i;
64 
65   if (!attr)
66     return;
67 
68   array = calloc(total, sizeof(*array));
69   if (!array) {
70     if (verbose)
71       fprintf(stderr, "Failed to allocate synthetic index array of size %lu\n", total);
72     goto out;
73   }
74 
75   i = strspn(attr, "0123456789,");
76   if (i == length) {
77     /* explicit array of indexes */
78 
79     for(i=0; i<total; i++) {
80       const char *next;
81       unsigned idx = strtoul(attr, (char **) &next, 10);
82       if (next == attr) {
83 	if (verbose)
84 	  fprintf(stderr, "Failed to read synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
85 	goto out_with_array;
86       }
87 
88       array[i] = idx;
89       if (i != total-1) {
90 	if (*next != ',') {
91 	  if (verbose)
92 	    fprintf(stderr, "Missing comma after synthetic index #%lu at '%s'\n", (unsigned long) i, attr);
93 	  goto out_with_array;
94 	}
95 	attr = next+1;
96       } else {
97 	attr = next;
98       }
99     }
100     curlevel->index_array = array;
101 
102   } else {
103     /* interleaving */
104     unsigned nr_loops = 1, cur_loop;
105     unsigned minstep = total;
106     unsigned long nbs = 1;
107     unsigned j, mul;
108     const char *tmp;
109 
110     tmp = attr;
111     while (tmp) {
112       tmp = strchr(tmp, ':');
113       if (!tmp || tmp >= attr+length)
114 	break;
115       nr_loops++;
116       tmp++;
117     }
118     /* nr_loops colon-separated fields, but we may need one more at the end */
119     loops = malloc((nr_loops+1)*sizeof(*loops));
120     if (!loops) {
121       if (verbose)
122 	fprintf(stderr, "Failed to allocate synthetic index interleave loop array of size %u\n", nr_loops);
123       goto out_with_array;
124     }
125 
126     if (*attr >= '0' && *attr <= '9') {
127       /* interleaving as x*y:z*t:... */
128       unsigned step, nb;
129 
130       tmp = attr;
131       cur_loop = 0;
132       while (tmp) {
133 	char *tmp2, *tmp3;
134 	step = (unsigned) strtol(tmp, &tmp2, 0);
135 	if (tmp2 == tmp || *tmp2 != '*') {
136 	  if (verbose)
137 	    fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number before '*'\n", tmp);
138 	  goto out_with_loops;
139 	}
140 	if (!step) {
141 	  if (verbose)
142 	    fprintf(stderr, "Invalid interleaving loop with step 0 at '%s'\n", tmp);
143 	  goto out_with_loops;
144 	}
145 	tmp2++;
146 	nb = (unsigned) strtol(tmp2, &tmp3, 0);
147 	if (tmp3 == tmp2 || (*tmp3 && *tmp3 != ':' && *tmp3 != ')' && *tmp3 != ' ')) {
148 	  if (verbose)
149 	    fprintf(stderr, "Failed to read synthetic index interleaving loop '%s' without number between '*' and ':'\n", tmp);
150 	  goto out_with_loops;
151 	}
152 	if (!nb) {
153 	  if (verbose)
154 	    fprintf(stderr, "Invalid interleaving loop with number 0 at '%s'\n", tmp2);
155 	  goto out_with_loops;
156 	}
157 	loops[cur_loop].step = step;
158 	loops[cur_loop].nb = nb;
159 	if (step < minstep)
160 	  minstep = step;
161 	nbs *= nb;
162 	cur_loop++;
163 	if (*tmp3 == ')' || *tmp3 == ' ')
164 	  break;
165 	tmp = (const char*) (tmp3+1);
166       }
167 
168     } else {
169       /* interleaving as type1:type2:... */
170       hwloc_obj_type_t type;
171       hwloc_obj_cache_type_t cachetypeattr;
172       int depthattr;
173       int err;
174 
175       /* find level depths for each interleaving loop */
176       tmp = attr;
177       cur_loop = 0;
178       while (tmp) {
179 	err = hwloc_obj_type_sscanf(tmp, &type, &depthattr, &cachetypeattr, sizeof(cachetypeattr));
180 	if (err < 0) {
181 	  if (verbose)
182 	    fprintf(stderr, "Failed to read synthetic index interleaving loop type '%s'\n", tmp);
183 	  goto out_with_loops;
184 	}
185 	if (type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
186 	  if (verbose)
187 	    fprintf(stderr, "Misc object type disallowed in synthetic index interleaving loop type '%s'\n", tmp);
188 	  goto out_with_loops;
189 	}
190 	for(i=0; i<curleveldepth; i++) {
191 	  if (type != data->level[i].type)
192 	    continue;
193 	  if ((type == HWLOC_OBJ_GROUP || type == HWLOC_OBJ_CACHE)
194 	      && depthattr != -1
195 	      && (unsigned) depthattr != data->level[i].depth)
196 	    continue;
197 	  if (type == HWLOC_OBJ_CACHE
198 	      && cachetypeattr != (hwloc_obj_cache_type_t) -1
199 	      && cachetypeattr != data->level[i].cachetype)
200 	    continue;
201 	  loops[cur_loop].level_depth = (unsigned)i;
202 	  break;
203 	}
204 	if (i == curleveldepth) {
205 	  if (verbose)
206 	    fprintf(stderr, "Failed to find level for synthetic index interleaving loop type '%s' above '%s'\n",
207 		    tmp, hwloc_obj_type_string(curlevel->type));
208 	  goto out_with_loops;
209 	}
210 	tmp = strchr(tmp, ':');
211 	if (!tmp || tmp > attr+length)
212 	  break;
213 	tmp++;
214 	cur_loop++;
215       }
216 
217       /* compute actual loop step/nb */
218       for(cur_loop=0; cur_loop<nr_loops; cur_loop++) {
219 	unsigned mydepth = loops[cur_loop].level_depth;
220 	unsigned prevdepth = 0;
221 	unsigned step, nb;
222 	for(i=0; i<nr_loops; i++) {
223 	  if (loops[i].level_depth == mydepth && i != cur_loop) {
224 	    if (verbose)
225 	      fprintf(stderr, "Invalid duplicate interleaving loop type in synthetic index '%s'\n", attr);
226 	    goto out_with_loops;
227 	  }
228 	  if (loops[i].level_depth < mydepth
229 	      && loops[i].level_depth > prevdepth)
230 	    prevdepth = loops[i].level_depth;
231 	}
232 	step = curlevel->totalwidth / data->level[mydepth].totalwidth; /* number of objects below us */
233 	nb = data->level[mydepth].totalwidth / data->level[prevdepth].totalwidth; /* number of us within parent */
234 
235 	loops[cur_loop].step = step;
236 	loops[cur_loop].nb = nb;
237 	assert(nb);
238 	assert(step);
239 	if (step < minstep)
240 	  minstep = step;
241 	nbs *= nb;
242       }
243     }
244     assert(nbs);
245 
246     if (nbs != total) {
247       /* one loop of total/nbs steps is missing, add it if it's just the smallest one */
248       if (minstep == total/nbs) {
249 	loops[nr_loops].step = 1;
250 	loops[nr_loops].nb = total/nbs;
251 	nr_loops++;
252       } else {
253 	if (verbose)
254 	  fprintf(stderr, "Invalid index interleaving total width %lu instead of %lu\n", nbs, total);
255 	goto out_with_loops;
256       }
257     }
258 
259     /* generate the array of indexes */
260     mul = 1;
261     for(i=0; i<nr_loops; i++) {
262       unsigned step = loops[i].step;
263       unsigned nb = loops[i].nb;
264       for(j=0; j<total; j++)
265 	array[j] += ((j / step) % nb) * mul;
266       mul *= nb;
267     }
268 
269     /* check that we have the right values (cannot pass total, cannot give duplicate 0) */
270     for(j=0; j<total; j++) {
271       if (array[j] >= total) {
272 	if (verbose)
273 	  fprintf(stderr, "Invalid index interleaving generates out-of-range index %u\n", array[j]);
274 	goto out_with_loops;
275       }
276       if (!array[j] && j) {
277 	if (verbose)
278 	  fprintf(stderr, "Invalid index interleaving generates duplicate index values\n");
279 	goto out_with_loops;
280       }
281     }
282 
283     free(loops);
284     curlevel->index_array = array;
285   }
286 
287   return;
288 
289  out_with_loops:
290   free(loops);
291  out_with_array:
292   free(array);
293  out:
294   return;
295 }
296 
297 static hwloc_uint64_t
hwloc_synthetic_parse_memory_attr(const char * attr,const char ** endp)298 hwloc_synthetic_parse_memory_attr(const char *attr, const char **endp)
299 {
300   const char *endptr;
301   hwloc_uint64_t size;
302   size = strtoull(attr, (char **) &endptr, 0);
303   if (!hwloc_strncasecmp(endptr, "TB", 2)) {
304     size <<= 40;
305     endptr += 2;
306   } else if (!hwloc_strncasecmp(endptr, "GB", 2)) {
307     size <<= 30;
308     endptr += 2;
309   } else if (!hwloc_strncasecmp(endptr, "MB", 2)) {
310     size <<= 20;
311     endptr += 2;
312   } else if (!hwloc_strncasecmp(endptr, "kB", 2)) {
313     size <<= 10;
314     endptr += 2;
315   }
316   *endp = endptr;
317   return size;
318 }
319 
320 static int
hwloc_synthetic_parse_level_attrs(const char * attrs,const char ** next_posp,struct hwloc_synthetic_level_data_s * curlevel,int verbose)321 hwloc_synthetic_parse_level_attrs(const char *attrs, const char **next_posp,
322 				  struct hwloc_synthetic_level_data_s *curlevel,
323 				  int verbose)
324 {
325   hwloc_obj_type_t type = curlevel->type;
326   const char *next_pos;
327   hwloc_uint64_t memorysize = 0;
328   const char *index_string = NULL;
329   size_t index_string_length = 0;
330 
331   next_pos = (const char *) strchr(attrs, ')');
332   if (!next_pos) {
333     if (verbose)
334       fprintf(stderr, "Missing attribute closing bracket in synthetic string doesn't have a number of objects at '%s'\n", attrs);
335     errno = EINVAL;
336     return -1;
337   }
338 
339   while (')' != *attrs) {
340     if (HWLOC_OBJ_CACHE == type && !strncmp("size=", attrs, 5)) {
341       memorysize = hwloc_synthetic_parse_memory_attr(attrs+5, &attrs);
342 
343     } else if (HWLOC_OBJ_CACHE != type && !strncmp("memory=", attrs, 7)) {
344       memorysize = hwloc_synthetic_parse_memory_attr(attrs+7, &attrs);
345 
346     } else if (!strncmp("indexes=", attrs, 8)) {
347       index_string = attrs+8;
348       attrs += 8;
349       index_string_length = strcspn(attrs, " )");
350       attrs += index_string_length;
351 
352     } else {
353       if (verbose)
354 	fprintf(stderr, "Unknown attribute at '%s'\n", attrs);
355       errno = EINVAL;
356       return -1;
357     }
358 
359     if (' ' == *attrs)
360       attrs++;
361     else if (')' != *attrs) {
362       if (verbose)
363 	fprintf(stderr, "Missing parameter separator at '%s'\n", attrs);
364       errno = EINVAL;
365       return -1;
366     }
367   }
368 
369   curlevel->memorysize = memorysize;
370   curlevel->index_string = index_string;
371   curlevel->index_string_length = (unsigned long)index_string_length;
372   *next_posp = next_pos+1;
373   return 0;
374 }
375 
376 /* Read from description a series of integers describing a symmetrical
377    topology and update the hwloc_synthetic_backend_data_s accordingly.  On
378    success, return zero.  */
379 static int
hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s * data,const char * description)380 hwloc_backend_synthetic_init(struct hwloc_synthetic_backend_data_s *data,
381 			     const char *description)
382 {
383   const char *pos, *next_pos;
384   unsigned long item, count;
385   unsigned i;
386   int cache_depth = 0, group_depth = 0;
387   int nb_machine_levels = 0, nb_node_levels = 0;
388   int nb_pu_levels = 0;
389   int verbose = 0;
390   const char *env = getenv("HWLOC_SYNTHETIC_VERBOSE");
391   int err;
392   unsigned long totalarity = 1;
393 
394   if (env)
395     verbose = atoi(env);
396 
397   /* default values before we add root attributes */
398   data->level[0].totalwidth = 1;
399   data->level[0].type = HWLOC_OBJ_MACHINE;
400   data->level[0].index_string = NULL;
401   data->level[0].index_array = NULL;
402   data->level[0].memorysize = 0;
403   if (*description == '(') {
404     err = hwloc_synthetic_parse_level_attrs(description+1, &description, &data->level[0], verbose);
405     if (err < 0)
406       return err;
407   }
408 
409   for (pos = description, count = 1; *pos; pos = next_pos) {
410 #define HWLOC_OBJ_TYPE_UNKNOWN ((hwloc_obj_type_t) -1)
411     hwloc_obj_type_t type = HWLOC_OBJ_TYPE_UNKNOWN;
412     int typedepth = -1;
413     hwloc_obj_cache_type_t cachetype = (hwloc_obj_cache_type_t) -1;
414 
415     /* initialize parent arity to 0 so that the levels are not infinite */
416     data->level[count-1].arity = 0;
417 
418     while (*pos == ' ')
419       pos++;
420 
421     if (!*pos)
422       break;
423 
424     if (*pos < '0' || *pos > '9') {
425       if (hwloc_obj_type_sscanf(pos, &type, &typedepth, &cachetype, sizeof(cachetype)) < 0) {
426 	if (verbose)
427 	  fprintf(stderr, "Synthetic string with unknown object type at '%s'\n", pos);
428 	errno = EINVAL;
429 	goto error;
430       }
431       if (type == HWLOC_OBJ_SYSTEM || type == HWLOC_OBJ_MISC || type == HWLOC_OBJ_BRIDGE || type == HWLOC_OBJ_PCI_DEVICE || type == HWLOC_OBJ_OS_DEVICE) {
432 	if (verbose)
433 	  fprintf(stderr, "Synthetic string with disallowed object type at '%s'\n", pos);
434 	errno = EINVAL;
435 	goto error;
436       }
437 
438       next_pos = strchr(pos, ':');
439       if (!next_pos) {
440 	if (verbose)
441 	  fprintf(stderr,"Synthetic string doesn't have a `:' after object type at '%s'\n", pos);
442 	errno = EINVAL;
443 	goto error;
444       }
445       pos = next_pos + 1;
446     }
447     data->level[count].type = type;
448     data->level[count].depth = (unsigned) typedepth;
449     data->level[count].cachetype = cachetype;
450 
451     item = strtoul(pos, (char **)&next_pos, 0);
452     if (next_pos == pos) {
453       if (verbose)
454 	fprintf(stderr,"Synthetic string doesn't have a number of objects at '%s'\n", pos);
455       errno = EINVAL;
456       goto error;
457     }
458     if (!item) {
459       if (verbose)
460 	fprintf(stderr,"Synthetic string with disallow 0 number of objects at '%s'\n", pos);
461       errno = EINVAL;
462       goto error;
463     }
464 
465     totalarity *= item;
466     data->level[count].totalwidth = totalarity;
467     data->level[count].index_string = NULL;
468     data->level[count].index_array = NULL;
469     data->level[count].memorysize = 0;
470     if (*next_pos == '(') {
471       err = hwloc_synthetic_parse_level_attrs(next_pos+1, &next_pos, &data->level[count], verbose);
472       if (err < 0)
473 	goto error;
474     }
475 
476     if (count + 1 >= HWLOC_SYNTHETIC_MAX_DEPTH) {
477       if (verbose)
478 	fprintf(stderr,"Too many synthetic levels, max %d\n", HWLOC_SYNTHETIC_MAX_DEPTH);
479       errno = EINVAL;
480       goto error;
481     }
482     if (item > UINT_MAX) {
483       if (verbose)
484 	fprintf(stderr,"Too big arity, max %u\n", UINT_MAX);
485       errno = EINVAL;
486       goto error;
487     }
488 
489     data->level[count-1].arity = (unsigned)item;
490     count++;
491   }
492 
493   if (count <= 0) {
494     if (verbose)
495       fprintf(stderr, "Synthetic string doesn't contain any object\n");
496     errno = EINVAL;
497     goto error;
498   }
499 
500   for(i=count-1; i>0; i--) {
501     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
502     hwloc_obj_type_t type;
503 
504     type = curlevel->type;
505 
506     if (i == count-1 && type != HWLOC_OBJ_TYPE_UNKNOWN && type != HWLOC_OBJ_PU) {
507       if (verbose)
508 	fprintf(stderr, "Synthetic string cannot use non-PU type for last level\n");
509       errno = EINVAL;
510       return -1;
511     }
512     if (i != count-1 && type == HWLOC_OBJ_PU) {
513       if (verbose)
514 	fprintf(stderr, "Synthetic string cannot use PU type for non-last level\n");
515       errno = EINVAL;
516       return -1;
517     }
518 
519     if (type == HWLOC_OBJ_TYPE_UNKNOWN) {
520       if (i == count-1)
521 	type = HWLOC_OBJ_PU;
522       else {
523 	switch (data->level[i+1].type) {
524 	case HWLOC_OBJ_PU: type = HWLOC_OBJ_CORE; break;
525 	case HWLOC_OBJ_CORE: type = HWLOC_OBJ_CACHE; break;
526 	case HWLOC_OBJ_CACHE: type = HWLOC_OBJ_PACKAGE; break;
527 	case HWLOC_OBJ_PACKAGE: type = HWLOC_OBJ_NUMANODE; break;
528 	case HWLOC_OBJ_NUMANODE:
529 	case HWLOC_OBJ_MACHINE:
530 	case HWLOC_OBJ_GROUP: type = HWLOC_OBJ_GROUP; break;
531 	default:
532 	  assert(0);
533 	}
534       }
535       curlevel->type = type;
536     }
537     switch (type) {
538       case HWLOC_OBJ_PU:
539 	nb_pu_levels++;
540 	break;
541       case HWLOC_OBJ_CACHE:
542 	cache_depth++;
543 	break;
544       case HWLOC_OBJ_GROUP:
545 	group_depth++;
546 	break;
547       case HWLOC_OBJ_NUMANODE:
548 	nb_node_levels++;
549 	break;
550       case HWLOC_OBJ_MACHINE:
551 	nb_machine_levels++;
552 	break;
553       default:
554 	break;
555     }
556   }
557 
558   if (!nb_pu_levels) {
559     if (verbose)
560       fprintf(stderr, "Synthetic string missing ending number of PUs\n");
561     errno = EINVAL;
562     return -1;
563   }
564   if (nb_pu_levels > 1) {
565     if (verbose)
566       fprintf(stderr, "Synthetic string can not have several PU levels\n");
567     errno = EINVAL;
568     return -1;
569   }
570   if (nb_node_levels > 1) {
571     if (verbose)
572       fprintf(stderr, "Synthetic string can not have several NUMA node levels\n");
573     errno = EINVAL;
574     return -1;
575   }
576   if (nb_machine_levels > 1) {
577     if (verbose)
578       fprintf(stderr, "Synthetic string can not have several machine levels\n");
579     errno = EINVAL;
580     return -1;
581   }
582 
583   if (nb_machine_levels)
584     data->level[0].type = HWLOC_OBJ_SYSTEM;
585   else {
586     data->level[0].type = HWLOC_OBJ_MACHINE;
587     nb_machine_levels++;
588   }
589 
590   if (cache_depth == 1)
591     /* if there is a single cache level, make it L2 */
592     cache_depth = 2;
593 
594   for (i=0; i<count; i++) {
595     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
596     hwloc_obj_type_t type = curlevel->type;
597 
598     if (type == HWLOC_OBJ_GROUP) {
599       if (curlevel->depth == (unsigned)-1)
600 	curlevel->depth = group_depth--;
601 
602     } else if (type == HWLOC_OBJ_CACHE) {
603       if (curlevel->depth == (unsigned)-1)
604 	curlevel->depth = cache_depth--;
605       if (curlevel->cachetype == (hwloc_obj_cache_type_t) -1)
606 	curlevel->cachetype = curlevel->depth == 1 ? HWLOC_OBJ_CACHE_DATA : HWLOC_OBJ_CACHE_UNIFIED;
607       if (!curlevel->memorysize) {
608 	if (1 == curlevel->depth)
609 	  /* 32Kb in L1 */
610 	  curlevel->memorysize = 32*1024;
611 	else
612 	  /* *4 at each level, starting from 1MB for L2, unified */
613 	  curlevel->memorysize = 256ULL*1024 << (2*curlevel->depth);
614       }
615 
616     } else if (type == HWLOC_OBJ_NUMANODE && !curlevel->memorysize) {
617       /* 1GB in memory nodes. */
618       curlevel->memorysize = 1024*1024*1024;
619     }
620 
621     hwloc_synthetic_process_level_indexes(data, i, verbose);
622   }
623 
624   data->string = strdup(description);
625   data->level[count-1].arity = 0;
626   return 0;
627 
628  error:
629   for(i=0; i<HWLOC_SYNTHETIC_MAX_DEPTH; i++) {
630     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
631     free(curlevel->index_array);
632     if (!curlevel->arity)
633       break;
634   }
635   return -1;
636 }
637 
638 static void
hwloc_synthetic__post_look_hooks(struct hwloc_synthetic_level_data_s * curlevel,hwloc_obj_t obj)639 hwloc_synthetic__post_look_hooks(struct hwloc_synthetic_level_data_s *curlevel,
640 				 hwloc_obj_t obj)
641 {
642   switch (obj->type) {
643   case HWLOC_OBJ_GROUP:
644     obj->attr->group.depth = curlevel->depth;
645     break;
646   case HWLOC_OBJ_SYSTEM:
647     break;
648   case HWLOC_OBJ_MACHINE:
649     break;
650   case HWLOC_OBJ_NUMANODE:
651     break;
652   case HWLOC_OBJ_PACKAGE:
653     break;
654   case HWLOC_OBJ_CACHE:
655     obj->attr->cache.depth = curlevel->depth;
656     obj->attr->cache.linesize = 64;
657     obj->attr->cache.type = curlevel->cachetype;
658     obj->attr->cache.size = curlevel->memorysize;
659     break;
660   case HWLOC_OBJ_CORE:
661     break;
662   case HWLOC_OBJ_PU:
663     break;
664   case HWLOC_OBJ_BRIDGE:
665   case HWLOC_OBJ_PCI_DEVICE:
666   case HWLOC_OBJ_OS_DEVICE:
667   case HWLOC_OBJ_MISC:
668   case HWLOC_OBJ_TYPE_MAX:
669     /* Should never happen */
670     assert(0);
671     break;
672   }
673   if (curlevel->memorysize && HWLOC_OBJ_CACHE != obj->type) {
674     obj->memory.local_memory = curlevel->memorysize;
675     obj->memory.page_types_len = 1;
676     obj->memory.page_types = malloc(sizeof(*obj->memory.page_types));
677     memset(obj->memory.page_types, 0, sizeof(*obj->memory.page_types));
678     obj->memory.page_types[0].size = 4096;
679     obj->memory.page_types[0].count = curlevel->memorysize / 4096;
680   }
681 }
682 
683 /*
684  * Recursively build objects whose cpu start at first_cpu
685  * - level gives where to look in the type, arity and id arrays
686  * - the id array is used as a variable to get unique IDs for a given level.
687  * - generated memory should be added to *memory_kB.
688  * - generated cpus should be added to parent_cpuset.
689  * - next cpu number to be used should be returned.
690  */
691 static void
hwloc__look_synthetic(struct hwloc_topology * topology,struct hwloc_synthetic_backend_data_s * data,int level,hwloc_bitmap_t parent_cpuset)692 hwloc__look_synthetic(struct hwloc_topology *topology,
693 		      struct hwloc_synthetic_backend_data_s *data,
694 		      int level,
695 		      hwloc_bitmap_t parent_cpuset)
696 {
697   hwloc_obj_t obj;
698   unsigned i;
699   struct hwloc_synthetic_level_data_s *curlevel = &data->level[level];
700   hwloc_obj_type_t type = curlevel->type;
701   unsigned os_index;
702 
703   /* pre-hooks */
704   switch (type) {
705     case HWLOC_OBJ_GROUP:
706       break;
707     case HWLOC_OBJ_MACHINE:
708       break;
709     case HWLOC_OBJ_NUMANODE:
710       break;
711     case HWLOC_OBJ_PACKAGE:
712       break;
713     case HWLOC_OBJ_CACHE:
714       break;
715     case HWLOC_OBJ_CORE:
716       break;
717     case HWLOC_OBJ_PU:
718       break;
719     case HWLOC_OBJ_SYSTEM:
720     case HWLOC_OBJ_BRIDGE:
721     case HWLOC_OBJ_PCI_DEVICE:
722     case HWLOC_OBJ_OS_DEVICE:
723     case HWLOC_OBJ_MISC:
724     case HWLOC_OBJ_TYPE_MAX:
725       /* Should never happen */
726       assert(0);
727       break;
728   }
729 
730   os_index = curlevel->next_os_index++;
731   if (curlevel->index_array)
732     os_index = curlevel->index_array[os_index];
733   obj = hwloc_alloc_setup_object(type, os_index);
734   obj->cpuset = hwloc_bitmap_alloc();
735 
736   if (!curlevel->arity) {
737     hwloc_bitmap_set(obj->cpuset, os_index);
738   } else {
739     for (i = 0; i < curlevel->arity; i++)
740       hwloc__look_synthetic(topology, data, level + 1, obj->cpuset);
741   }
742 
743   if (type == HWLOC_OBJ_NUMANODE) {
744     obj->nodeset = hwloc_bitmap_alloc();
745     hwloc_bitmap_set(obj->nodeset, os_index);
746   }
747 
748   hwloc_bitmap_or(parent_cpuset, parent_cpuset, obj->cpuset);
749 
750   hwloc_synthetic__post_look_hooks(curlevel, obj);
751 
752   hwloc_insert_object_by_cpuset(topology, obj);
753 }
754 
755 static int
hwloc_look_synthetic(struct hwloc_backend * backend)756 hwloc_look_synthetic(struct hwloc_backend *backend)
757 {
758   struct hwloc_topology *topology = backend->topology;
759   struct hwloc_synthetic_backend_data_s *data = backend->private_data;
760   hwloc_bitmap_t cpuset = hwloc_bitmap_alloc();
761   unsigned i;
762 
763   assert(!topology->levels[0][0]->cpuset);
764 
765   hwloc_alloc_obj_cpusets(topology->levels[0][0]);
766 
767   topology->support.discovery->pu = 1;
768 
769   /* start with os_index 0 for each level */
770   for (i = 0; data->level[i].arity > 0; i++)
771     data->level[i].next_os_index = 0;
772   /* ... including the last one */
773   data->level[i].next_os_index = 0;
774 
775   /* update first level type according to the synthetic type array */
776   topology->levels[0][0]->type = data->level[0].type;
777   hwloc_synthetic__post_look_hooks(&data->level[0], topology->levels[0][0]);
778 
779   for (i = 0; i < data->level[0].arity; i++)
780     hwloc__look_synthetic(topology, data, 1, cpuset);
781 
782   hwloc_bitmap_free(cpuset);
783 
784   hwloc_obj_add_info(topology->levels[0][0], "Backend", "Synthetic");
785   hwloc_obj_add_info(topology->levels[0][0], "SyntheticDescription", data->string);
786   return 1;
787 }
788 
789 static void
hwloc_synthetic_backend_disable(struct hwloc_backend * backend)790 hwloc_synthetic_backend_disable(struct hwloc_backend *backend)
791 {
792   struct hwloc_synthetic_backend_data_s *data = backend->private_data;
793   unsigned i;
794   for(i=0; i<HWLOC_SYNTHETIC_MAX_DEPTH; i++) {
795     struct hwloc_synthetic_level_data_s *curlevel = &data->level[i];
796     free(curlevel->index_array);
797     if (!curlevel->arity)
798       break;
799   }
800   free(data->string);
801   free(data);
802 }
803 
804 static struct hwloc_backend *
hwloc_synthetic_component_instantiate(struct hwloc_disc_component * component,const void * _data1,const void * _data2 __hwloc_attribute_unused,const void * _data3 __hwloc_attribute_unused)805 hwloc_synthetic_component_instantiate(struct hwloc_disc_component *component,
806 				      const void *_data1,
807 				      const void *_data2 __hwloc_attribute_unused,
808 				      const void *_data3 __hwloc_attribute_unused)
809 {
810   struct hwloc_backend *backend;
811   struct hwloc_synthetic_backend_data_s *data;
812   int err;
813 
814   if (!_data1) {
815     errno = EINVAL;
816     goto out;
817   }
818 
819   backend = hwloc_backend_alloc(component);
820   if (!backend)
821     goto out;
822 
823   data = malloc(sizeof(*data));
824   if (!data) {
825     errno = ENOMEM;
826     goto out_with_backend;
827   }
828 
829   err = hwloc_backend_synthetic_init(data, (const char *) _data1);
830   if (err < 0)
831     goto out_with_data;
832 
833   backend->private_data = data;
834   backend->discover = hwloc_look_synthetic;
835   backend->disable = hwloc_synthetic_backend_disable;
836   backend->is_thissystem = 0;
837 
838   return backend;
839 
840  out_with_data:
841   free(data);
842  out_with_backend:
843   free(backend);
844  out:
845   return NULL;
846 }
847 
848 static struct hwloc_disc_component hwloc_synthetic_disc_component = {
849   HWLOC_DISC_COMPONENT_TYPE_GLOBAL,
850   "synthetic",
851   ~0,
852   hwloc_synthetic_component_instantiate,
853   30,
854   NULL
855 };
856 
857 const struct hwloc_component hwloc_synthetic_component = {
858   HWLOC_COMPONENT_ABI,
859   NULL, NULL,
860   HWLOC_COMPONENT_TYPE_DISC,
861   0,
862   &hwloc_synthetic_disc_component
863 };
864 
hwloc_topology_export_synthetic_indexes(struct hwloc_topology * topology,hwloc_obj_t obj,char * buffer,size_t buflen)865 static int hwloc_topology_export_synthetic_indexes(struct hwloc_topology * topology,
866 						   hwloc_obj_t obj,
867 						   char *buffer, size_t buflen)
868 {
869   unsigned depth = obj->depth;
870   unsigned total = topology->level_nbobjects[depth];
871   unsigned step = 1;
872   unsigned nr_loops = 0;
873   struct hwloc_synthetic_intlv_loop_s *loops = NULL, *tmploops;
874   hwloc_obj_t cur;
875   unsigned i, j;
876   ssize_t tmplen = buflen;
877   char *tmp = buffer;
878   int res, ret = 0;
879 
880   /* must start with 0 */
881   if (obj->os_index)
882     goto exportall;
883 
884   while (step != total) {
885     /* must be a divider of the total */
886     if (total % step)
887       goto exportall;
888 
889     /* look for os_index == step */
890     for(i=1; i<total; i++)
891       if (topology->levels[depth][i]->os_index == step)
892 	break;
893     if (i == total)
894       goto exportall;
895     for(j=2; j<total/i; j++)
896       if (topology->levels[depth][i*j]->os_index != step*j)
897 	break;
898 
899     nr_loops++;
900     tmploops = realloc(loops, nr_loops*sizeof(*loops));
901     if (!tmploops)
902       goto exportall;
903     loops = tmploops;
904     loops[nr_loops-1].step = i;
905     loops[nr_loops-1].nb = j;
906     step *= j;
907   }
908 
909   /* check this interleaving */
910   for(i=0; i<total; i++) {
911     unsigned ind = 0;
912     unsigned mul = 1;
913     for(j=0; j<nr_loops; j++) {
914       ind += (i / loops[j].step) % loops[j].nb * mul;
915       mul *= loops[j].nb;
916     }
917     if (topology->levels[depth][i]->os_index != ind)
918       goto exportall;
919   }
920 
921   /* success, print it */
922   for(j=0; j<nr_loops; j++) {
923     res = hwloc_snprintf(tmp, tmplen, "%u*%u%s", loops[j].step, loops[j].nb,
924 			 j == nr_loops-1 ? ")" : ":");
925     if (res < 0) {
926       free(loops);
927       return -1;
928     }
929     ret += res;
930     if (res >= tmplen)
931       res = tmplen>0 ? (int)tmplen - 1 : 0;
932     tmp += res;
933     tmplen -= res;
934   }
935 
936   if (loops)
937     free(loops);
938 
939   return ret;
940 
941  exportall:
942   if (loops)
943     free(loops);
944 
945   /* dump all indexes */
946   cur = obj;
947   while (cur) {
948     res = hwloc_snprintf(tmp, tmplen, "%u%s", cur->os_index,
949 			 cur->next_cousin ? "," : ")");
950     if (res < 0)
951       return -1;
952     ret += res;
953     if (res >= tmplen)
954       res = tmplen>0 ? (int)tmplen - 1 : 0;
955     tmp += res;
956     tmplen -= res;
957     cur = cur->next_cousin;
958   }
959   return ret;
960 }
961 
hwloc_topology_export_synthetic_obj_attr(struct hwloc_topology * topology,hwloc_obj_t obj,char * buffer,size_t buflen)962 static int hwloc_topology_export_synthetic_obj_attr(struct hwloc_topology * topology,
963 						    hwloc_obj_t obj,
964 						    char *buffer, size_t buflen)
965 {
966   const char * separator = " ";
967   const char * prefix = "(";
968   char cachesize[64] = "";
969   char memsize[64] = "";
970   int needindexes = 0;
971 
972   if (HWLOC_OBJ_CACHE == obj->type && obj->attr->cache.size) {
973     snprintf(cachesize, sizeof(cachesize), "%ssize=%llu",
974 	     prefix, (unsigned long long) obj->attr->cache.size);
975     prefix = separator;
976   }
977   if (obj->memory.local_memory) {
978     snprintf(memsize, sizeof(memsize), "%smemory=%llu",
979 	     prefix, (unsigned long long) obj->memory.local_memory);
980     prefix = separator;
981   }
982   if (obj->type == HWLOC_OBJ_PU || obj->type == HWLOC_OBJ_NUMANODE) {
983     hwloc_obj_t cur = obj;
984     while (cur) {
985       if (cur->os_index != cur->logical_index) {
986 	needindexes = 1;
987 	break;
988       }
989       cur = cur->next_cousin;
990     }
991   }
992   if (*cachesize || *memsize || needindexes) {
993     ssize_t tmplen = buflen;
994     char *tmp = buffer;
995     int res, ret = 0;
996 
997     res = hwloc_snprintf(tmp, tmplen, "%s%s%s", cachesize, memsize, needindexes ? "" : ")");
998     if (res < 0)
999       return -1;
1000     ret += res;
1001     if (res >= tmplen)
1002       res = tmplen>0 ? (int)tmplen - 1 : 0;
1003     tmp += res;
1004     tmplen -= res;
1005 
1006     if (needindexes) {
1007       res = hwloc_snprintf(tmp, tmplen, "%sindexes=", prefix);
1008       if (res < 0)
1009 	return -1;
1010       ret += res;
1011       if (res >= tmplen)
1012 	res = tmplen>0 ? (int)tmplen - 1 : 0;
1013       tmp += res;
1014       tmplen -= res;
1015 
1016       res = hwloc_topology_export_synthetic_indexes(topology, obj, tmp, tmplen);
1017       if (res < 0)
1018 	return -1;
1019       ret += res;
1020       if (res >= tmplen)
1021 	res = tmplen>0 ? (int)tmplen - 1 : 0;
1022       tmp += res;
1023       tmplen -= res;
1024     }
1025     return ret;
1026   } else {
1027     return 0;
1028   }
1029 }
1030 
1031 int
hwloc_topology_export_synthetic(struct hwloc_topology * topology,char * buffer,size_t buflen,unsigned long flags)1032 hwloc_topology_export_synthetic(struct hwloc_topology * topology,
1033 				char *buffer, size_t buflen,
1034 				unsigned long flags)
1035 {
1036   hwloc_obj_t obj = hwloc_get_root_obj(topology);
1037   ssize_t tmplen = buflen;
1038   char *tmp = buffer;
1039   int res, ret = 0;
1040   unsigned arity;
1041   const char * separator = " ";
1042   const char * prefix = "";
1043 
1044   if (flags & ~(HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES|HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
1045     errno = EINVAL;
1046     return -1;
1047   }
1048 
1049   /* TODO: add a flag to ignore symmetric_subtree and I/Os.
1050    * just assume things are symmetric with the left branches of the tree.
1051    * but the number of objects per level may be wrong, what to do with OS index array in this case?
1052    * only allow ignoring symmetric_subtree if the level width remains OK?
1053    */
1054 
1055   /* TODO: add a root object by default, with a prefix such as tree=
1056    * so that we can backward-compatibly recognize whether there's a root or not.
1057    * and add a flag to disable it.
1058    */
1059 
1060   /* TODO: flag to force all indexes, not only for PU and NUMA? */
1061 
1062   if (!obj->symmetric_subtree) {
1063     errno = EINVAL;
1064     return -1;
1065   }
1066 
1067   if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
1068     /* root attributes */
1069     res = hwloc_topology_export_synthetic_obj_attr(topology, obj, tmp, tmplen);
1070     if (res < 0)
1071       return -1;
1072     ret += res;
1073     if (ret > 0)
1074       prefix = separator;
1075     if (res >= tmplen)
1076       res = tmplen>0 ? (int)tmplen - 1 : 0;
1077     tmp += res;
1078     tmplen -= res;
1079   }
1080 
1081   arity = obj->arity;
1082   while (arity) {
1083     /* for each level */
1084     obj = obj->first_child;
1085     if (flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_EXTENDED_TYPES) {
1086       res = hwloc_snprintf(tmp, tmplen, "%s%s:%u", prefix, hwloc_obj_type_string(obj->type), arity);
1087     } else {
1088       char types[64];
1089       hwloc_obj_type_snprintf(types, sizeof(types), obj, 1);
1090       res = hwloc_snprintf(tmp, tmplen, "%s%s:%u", prefix, types, arity);
1091     }
1092     if (res < 0)
1093       return -1;
1094     ret += res;
1095     if (res >= tmplen)
1096       res = tmplen>0 ? (int)tmplen - 1 : 0;
1097     tmp += res;
1098     tmplen -= res;
1099 
1100     if (!(flags & HWLOC_TOPOLOGY_EXPORT_SYNTHETIC_FLAG_NO_ATTRS)) {
1101       /* obj attributes */
1102       res = hwloc_topology_export_synthetic_obj_attr(topology, obj, tmp, tmplen);
1103       if (res < 0)
1104 	return -1;
1105       ret += res;
1106       if (res >= tmplen)
1107 	res = tmplen>0 ? (int)tmplen - 1 : 0;
1108       tmp += res;
1109       tmplen -= res;
1110     }
1111 
1112     /* next level */
1113     prefix = separator;
1114     arity = obj->arity;
1115   }
1116 
1117   return ret;
1118 }
1119