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