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