1 /*
2 * Copyright © 2009 CNRS
3 * Copyright © 2009-2021 Inria. All rights reserved.
4 * Copyright © 2009-2012, 2020 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
11 #define _ATFILE_SOURCE
12 #include <assert.h>
13 #include <sys/types.h>
14 #ifdef HAVE_DIRENT_H
15 #include <dirent.h>
16 #endif
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #include <string.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <limits.h>
26 #include <float.h>
27
28 #include "hwloc.h"
29 #include "private/private.h"
30 #include "private/debug.h"
31 #include "private/misc.h"
32
33 #ifdef HAVE_MACH_MACH_INIT_H
34 #include <mach/mach_init.h>
35 #endif
36 #ifdef HAVE_MACH_INIT_H
37 #include <mach_init.h>
38 #endif
39 #ifdef HAVE_MACH_MACH_HOST_H
40 #include <mach/mach_host.h>
41 #endif
42
43 #ifdef HAVE_SYS_PARAM_H
44 #include <sys/param.h>
45 #endif
46
47 #ifdef HAVE_SYS_SYSCTL_H
48 #include <sys/sysctl.h>
49 #endif
50
51 #ifdef HWLOC_WIN_SYS
52 #include <windows.h>
53 #endif
54
hwloc_get_api_version(void)55 unsigned hwloc_get_api_version(void)
56 {
57 return HWLOC_API_VERSION;
58 }
59
hwloc_topology_abi_check(hwloc_topology_t topology)60 int hwloc_topology_abi_check(hwloc_topology_t topology)
61 {
62 return topology->topology_abi != HWLOC_TOPOLOGY_ABI ? -1 : 0;
63 }
64
hwloc_hide_errors(void)65 int hwloc_hide_errors(void)
66 {
67 static int hide = 0;
68 static int checked = 0;
69 if (!checked) {
70 const char *envvar = getenv("HWLOC_HIDE_ERRORS");
71 if (envvar)
72 hide = atoi(envvar);
73 checked = 1;
74 }
75 return hide;
76 }
77
78
79 /* format the obj info to print in error messages */
80 static void
report_insert_error_format_obj(char * buf,size_t buflen,hwloc_obj_t obj)81 report_insert_error_format_obj(char *buf, size_t buflen, hwloc_obj_t obj)
82 {
83 char typestr[64];
84 char *cpusetstr;
85 char *nodesetstr = NULL;
86
87 hwloc_obj_type_snprintf(typestr, sizeof(typestr), obj, 0);
88 hwloc_bitmap_asprintf(&cpusetstr, obj->cpuset);
89 if (obj->nodeset) /* may be missing during insert */
90 hwloc_bitmap_asprintf(&nodesetstr, obj->nodeset);
91 if (obj->os_index != HWLOC_UNKNOWN_INDEX)
92 snprintf(buf, buflen, "%s (P#%u cpuset %s%s%s)",
93 typestr, obj->os_index, cpusetstr,
94 nodesetstr ? " nodeset " : "",
95 nodesetstr ? nodesetstr : "");
96 else
97 snprintf(buf, buflen, "%s (cpuset %s%s%s)",
98 typestr, cpusetstr,
99 nodesetstr ? " nodeset " : "",
100 nodesetstr ? nodesetstr : "");
101 free(cpusetstr);
102 free(nodesetstr);
103 }
104
report_insert_error(hwloc_obj_t new,hwloc_obj_t old,const char * msg,const char * reason)105 static void report_insert_error(hwloc_obj_t new, hwloc_obj_t old, const char *msg, const char *reason)
106 {
107 static int reported = 0;
108
109 if (reason && !reported && !hwloc_hide_errors()) {
110 char newstr[512];
111 char oldstr[512];
112 report_insert_error_format_obj(newstr, sizeof(newstr), new);
113 report_insert_error_format_obj(oldstr, sizeof(oldstr), old);
114
115 fprintf(stderr, "****************************************************************************\n");
116 fprintf(stderr, "* hwloc %s received invalid information from the operating system.\n", HWLOC_VERSION);
117 fprintf(stderr, "*\n");
118 fprintf(stderr, "* Failed with: %s\n", msg);
119 fprintf(stderr, "* while inserting %s at %s\n", newstr, oldstr);
120 fprintf(stderr, "* coming from: %s\n", reason);
121 fprintf(stderr, "*\n");
122 fprintf(stderr, "* The following FAQ entry in the hwloc documentation may help:\n");
123 fprintf(stderr, "* What should I do when hwloc reports \"operating system\" warnings?\n");
124 fprintf(stderr, "* Otherwise please report this error message to the hwloc user's mailing list,\n");
125 #ifdef HWLOC_LINUX_SYS
126 fprintf(stderr, "* along with the files generated by the hwloc-gather-topology script.\n");
127 #else
128 fprintf(stderr, "* along with any relevant topology information from your platform.\n");
129 #endif
130 fprintf(stderr, "* \n");
131 fprintf(stderr, "* hwloc will now ignore this invalid topology information and continue.\n");
132 fprintf(stderr, "****************************************************************************\n");
133 reported = 1;
134 }
135 }
136
137 #if defined(HAVE_SYSCTLBYNAME)
hwloc_get_sysctlbyname(const char * name,int64_t * ret)138 int hwloc_get_sysctlbyname(const char *name, int64_t *ret)
139 {
140 union {
141 int32_t i32;
142 int64_t i64;
143 } n;
144 size_t size = sizeof(n);
145 if (sysctlbyname(name, &n, &size, NULL, 0))
146 return -1;
147 switch (size) {
148 case sizeof(n.i32):
149 *ret = n.i32;
150 break;
151 case sizeof(n.i64):
152 *ret = n.i64;
153 break;
154 default:
155 return -1;
156 }
157 return 0;
158 }
159 #endif
160
161 #if defined(HAVE_SYSCTL)
hwloc_get_sysctl(int name[],unsigned namelen,int64_t * ret)162 int hwloc_get_sysctl(int name[], unsigned namelen, int64_t *ret)
163 {
164 union {
165 int32_t i32;
166 int64_t i64;
167 } n;
168 size_t size = sizeof(n);
169 if (sysctl(name, namelen, &n, &size, NULL, 0))
170 return -1;
171 switch (size) {
172 case sizeof(n.i32):
173 *ret = n.i32;
174 break;
175 case sizeof(n.i64):
176 *ret = n.i64;
177 break;
178 default:
179 return -1;
180 }
181 return 0;
182 }
183 #endif
184
185 /* Return the OS-provided number of processors.
186 * Assumes topology->is_thissystem is true.
187 */
188 #ifndef HWLOC_WIN_SYS /* The windows implementation is in topology-windows.c */
189 int
hwloc_fallback_nbprocessors(unsigned flags)190 hwloc_fallback_nbprocessors(unsigned flags) {
191 int n;
192
193 if (flags & HWLOC_FALLBACK_NBPROCESSORS_INCLUDE_OFFLINE) {
194 /* try to get all CPUs for Linux and Solaris that can handle offline CPUs */
195 #if HAVE_DECL__SC_NPROCESSORS_CONF
196 n = sysconf(_SC_NPROCESSORS_CONF);
197 #elif HAVE_DECL__SC_NPROC_CONF
198 n = sysconf(_SC_NPROC_CONF);
199 #else
200 n = -1;
201 #endif
202 if (n != -1)
203 return n;
204 }
205
206 /* try getting only online CPUs, or whatever we can get */
207 #if HAVE_DECL__SC_NPROCESSORS_ONLN
208 n = sysconf(_SC_NPROCESSORS_ONLN);
209 #elif HAVE_DECL__SC_NPROC_ONLN
210 n = sysconf(_SC_NPROC_ONLN);
211 #elif HAVE_DECL__SC_NPROCESSORS_CONF
212 n = sysconf(_SC_NPROCESSORS_CONF);
213 #elif HAVE_DECL__SC_NPROC_CONF
214 n = sysconf(_SC_NPROC_CONF);
215 #elif defined(HAVE_HOST_INFO) && HAVE_HOST_INFO
216 struct host_basic_info info;
217 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
218 host_info(mach_host_self(), HOST_BASIC_INFO, (integer_t*) &info, &count);
219 n = info.avail_cpus;
220 #elif defined(HAVE_SYSCTLBYNAME)
221 int64_t nn;
222 if (hwloc_get_sysctlbyname("hw.ncpu", &nn))
223 nn = -1;
224 n = nn;
225 #elif defined(HAVE_SYSCTL) && HAVE_DECL_CTL_HW && HAVE_DECL_HW_NCPU
226 static int name[2] = {CTL_HW, HW_NCPU};
227 int64_t nn;
228 if (hwloc_get_sysctl(name, sizeof(name)/sizeof(*name), &nn))
229 n = -1;
230 n = nn;
231 #else
232 #ifdef __GNUC__
233 #warning No known way to discover number of available processors on this system
234 #endif
235 n = -1;
236 #endif
237 return n;
238 }
239
240 int64_t
hwloc_fallback_memsize(void)241 hwloc_fallback_memsize(void) {
242 int64_t size;
243 #if defined(HAVE_HOST_INFO) && HAVE_HOST_INFO
244 struct host_basic_info info;
245 mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT;
246 host_info(mach_host_self(), HOST_BASIC_INFO, (integer_t*) &info, &count);
247 size = info.memory_size;
248 #elif defined(HAVE_SYSCTL) && HAVE_DECL_CTL_HW && (HAVE_DECL_HW_REALMEM64 || HAVE_DECL_HW_MEMSIZE64 || HAVE_DECL_HW_PHYSMEM64 || HAVE_DECL_HW_USERMEM64 || HAVE_DECL_HW_REALMEM || HAVE_DECL_HW_MEMSIZE || HAVE_DECL_HW_PHYSMEM || HAVE_DECL_HW_USERMEM)
249 #if HAVE_DECL_HW_MEMSIZE64
250 static int name[2] = {CTL_HW, HW_MEMSIZE64};
251 #elif HAVE_DECL_HW_REALMEM64
252 static int name[2] = {CTL_HW, HW_REALMEM64};
253 #elif HAVE_DECL_HW_PHYSMEM64
254 static int name[2] = {CTL_HW, HW_PHYSMEM64};
255 #elif HAVE_DECL_HW_USERMEM64
256 static int name[2] = {CTL_HW, HW_USERMEM64};
257 #elif HAVE_DECL_HW_MEMSIZE
258 static int name[2] = {CTL_HW, HW_MEMSIZE};
259 #elif HAVE_DECL_HW_REALMEM
260 static int name[2] = {CTL_HW, HW_REALMEM};
261 #elif HAVE_DECL_HW_PHYSMEM
262 static int name[2] = {CTL_HW, HW_PHYSMEM};
263 #elif HAVE_DECL_HW_USERMEM
264 static int name[2] = {CTL_HW, HW_USERMEM};
265 #endif
266 if (hwloc_get_sysctl(name, sizeof(name)/sizeof(*name), &size))
267 size = -1;
268 #elif defined(HAVE_SYSCTLBYNAME)
269 if (hwloc_get_sysctlbyname("hw.memsize", &size) &&
270 hwloc_get_sysctlbyname("hw.realmem", &size) &&
271 hwloc_get_sysctlbyname("hw.physmem", &size) &&
272 hwloc_get_sysctlbyname("hw.usermem", &size))
273 size = -1;
274 #else
275 size = -1;
276 #endif
277 return size;
278 }
279 #endif /* !HWLOC_WIN_SYS */
280
281 /*
282 * Use the given number of processors to set a PU level.
283 */
284 void
hwloc_setup_pu_level(struct hwloc_topology * topology,unsigned nb_pus)285 hwloc_setup_pu_level(struct hwloc_topology *topology,
286 unsigned nb_pus)
287 {
288 struct hwloc_obj *obj;
289 unsigned oscpu,cpu;
290
291 hwloc_debug("%s", "\n\n * CPU cpusets *\n\n");
292 for (cpu=0,oscpu=0; cpu<nb_pus; oscpu++)
293 {
294 obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_PU, oscpu);
295 obj->cpuset = hwloc_bitmap_alloc();
296 hwloc_bitmap_only(obj->cpuset, oscpu);
297
298 hwloc_debug_2args_bitmap("cpu %u (os %u) has cpuset %s\n",
299 cpu, oscpu, obj->cpuset);
300 hwloc__insert_object_by_cpuset(topology, NULL, obj, "core:pulevel");
301
302 cpu++;
303 }
304 }
305
306 /* Traverse children of a parent in a safe way: reread the next pointer as
307 * appropriate to prevent crash on child deletion: */
308 #define for_each_child_safe(child, parent, pchild) \
309 for (pchild = &(parent)->first_child, child = *pchild; \
310 child; \
311 /* Check whether the current child was not dropped. */ \
312 (*pchild == child ? pchild = &(child->next_sibling) : NULL), \
313 /* Get pointer to next child. */ \
314 child = *pchild)
315 #define for_each_memory_child_safe(child, parent, pchild) \
316 for (pchild = &(parent)->memory_first_child, child = *pchild; \
317 child; \
318 /* Check whether the current child was not dropped. */ \
319 (*pchild == child ? pchild = &(child->next_sibling) : NULL), \
320 /* Get pointer to next child. */ \
321 child = *pchild)
322 #define for_each_io_child_safe(child, parent, pchild) \
323 for (pchild = &(parent)->io_first_child, child = *pchild; \
324 child; \
325 /* Check whether the current child was not dropped. */ \
326 (*pchild == child ? pchild = &(child->next_sibling) : NULL), \
327 /* Get pointer to next child. */ \
328 child = *pchild)
329 #define for_each_misc_child_safe(child, parent, pchild) \
330 for (pchild = &(parent)->misc_first_child, child = *pchild; \
331 child; \
332 /* Check whether the current child was not dropped. */ \
333 (*pchild == child ? pchild = &(child->next_sibling) : NULL), \
334 /* Get pointer to next child. */ \
335 child = *pchild)
336
337 #ifdef HWLOC_DEBUG
338 /* Just for debugging. */
339 static void
hwloc_debug_print_object(int indent __hwloc_attribute_unused,hwloc_obj_t obj)340 hwloc_debug_print_object(int indent __hwloc_attribute_unused, hwloc_obj_t obj)
341 {
342 char type[64], idx[12], attr[1024], *cpuset = NULL;
343 hwloc_debug("%*s", 2*indent, "");
344 hwloc_obj_type_snprintf(type, sizeof(type), obj, 1);
345 if (obj->os_index != HWLOC_UNKNOWN_INDEX)
346 snprintf(idx, sizeof(idx), "#%u", obj->os_index);
347 else
348 *idx = '\0';
349 hwloc_obj_attr_snprintf(attr, sizeof(attr), obj, " ", 1);
350 hwloc_debug("%s%s%s%s%s", type, idx, *attr ? "(" : "", attr, *attr ? ")" : "");
351 if (obj->name)
352 hwloc_debug(" name \"%s\"", obj->name);
353 if (obj->subtype)
354 hwloc_debug(" subtype \"%s\"", obj->subtype);
355 if (obj->cpuset) {
356 hwloc_bitmap_asprintf(&cpuset, obj->cpuset);
357 hwloc_debug(" cpuset %s", cpuset);
358 free(cpuset);
359 }
360 if (obj->complete_cpuset) {
361 hwloc_bitmap_asprintf(&cpuset, obj->complete_cpuset);
362 hwloc_debug(" complete %s", cpuset);
363 free(cpuset);
364 }
365 if (obj->nodeset) {
366 hwloc_bitmap_asprintf(&cpuset, obj->nodeset);
367 hwloc_debug(" nodeset %s", cpuset);
368 free(cpuset);
369 }
370 if (obj->complete_nodeset) {
371 hwloc_bitmap_asprintf(&cpuset, obj->complete_nodeset);
372 hwloc_debug(" completeN %s", cpuset);
373 free(cpuset);
374 }
375 if (obj->arity)
376 hwloc_debug(" arity %u", obj->arity);
377 hwloc_debug("%s", "\n");
378 }
379
380 static void
hwloc_debug_print_objects(int indent __hwloc_attribute_unused,hwloc_obj_t obj)381 hwloc_debug_print_objects(int indent __hwloc_attribute_unused, hwloc_obj_t obj)
382 {
383 if (hwloc_debug_enabled() >= 2) {
384 hwloc_obj_t child;
385 hwloc_debug_print_object(indent, obj);
386 for_each_child (child, obj)
387 hwloc_debug_print_objects(indent + 1, child);
388 for_each_memory_child (child, obj)
389 hwloc_debug_print_objects(indent + 1, child);
390 for_each_io_child (child, obj)
391 hwloc_debug_print_objects(indent + 1, child);
392 for_each_misc_child (child, obj)
393 hwloc_debug_print_objects(indent + 1, child);
394 }
395 }
396 #else /* !HWLOC_DEBUG */
397 #define hwloc_debug_print_object(indent, obj) do { /* nothing */ } while (0)
398 #define hwloc_debug_print_objects(indent, obj) do { /* nothing */ } while (0)
399 #endif /* !HWLOC_DEBUG */
400
hwloc__free_infos(struct hwloc_info_s * infos,unsigned count)401 void hwloc__free_infos(struct hwloc_info_s *infos, unsigned count)
402 {
403 unsigned i;
404 for(i=0; i<count; i++) {
405 free(infos[i].name);
406 free(infos[i].value);
407 }
408 free(infos);
409 }
410
hwloc__add_info(struct hwloc_info_s ** infosp,unsigned * countp,const char * name,const char * value)411 int hwloc__add_info(struct hwloc_info_s **infosp, unsigned *countp, const char *name, const char *value)
412 {
413 unsigned count = *countp;
414 struct hwloc_info_s *infos = *infosp;
415 #define OBJECT_INFO_ALLOC 8
416 /* nothing allocated initially, (re-)allocate by multiple of 8 */
417 unsigned alloccount = (count + 1 + (OBJECT_INFO_ALLOC-1)) & ~(OBJECT_INFO_ALLOC-1);
418 if (count != alloccount) {
419 struct hwloc_info_s *tmpinfos = realloc(infos, alloccount*sizeof(*infos));
420 if (!tmpinfos)
421 /* failed to allocate, ignore this info */
422 goto out_with_array;
423 *infosp = infos = tmpinfos;
424 }
425 infos[count].name = strdup(name);
426 if (!infos[count].name)
427 goto out_with_array;
428 infos[count].value = strdup(value);
429 if (!infos[count].value)
430 goto out_with_name;
431 *countp = count+1;
432 return 0;
433
434 out_with_name:
435 free(infos[count].name);
436 out_with_array:
437 /* don't bother reducing the array */
438 return -1;
439 }
440
hwloc__add_info_nodup(struct hwloc_info_s ** infosp,unsigned * countp,const char * name,const char * value,int replace)441 int hwloc__add_info_nodup(struct hwloc_info_s **infosp, unsigned *countp,
442 const char *name, const char *value,
443 int replace)
444 {
445 struct hwloc_info_s *infos = *infosp;
446 unsigned count = *countp;
447 unsigned i;
448 for(i=0; i<count; i++) {
449 if (!strcmp(infos[i].name, name)) {
450 if (replace) {
451 char *new = strdup(value);
452 if (!new)
453 return -1;
454 free(infos[i].value);
455 infos[i].value = new;
456 }
457 return 0;
458 }
459 }
460 return hwloc__add_info(infosp, countp, name, value);
461 }
462
hwloc__move_infos(struct hwloc_info_s ** dst_infosp,unsigned * dst_countp,struct hwloc_info_s ** src_infosp,unsigned * src_countp)463 int hwloc__move_infos(struct hwloc_info_s **dst_infosp, unsigned *dst_countp,
464 struct hwloc_info_s **src_infosp, unsigned *src_countp)
465 {
466 unsigned dst_count = *dst_countp;
467 struct hwloc_info_s *dst_infos = *dst_infosp;
468 unsigned src_count = *src_countp;
469 struct hwloc_info_s *src_infos = *src_infosp;
470 unsigned i;
471 #define OBJECT_INFO_ALLOC 8
472 /* nothing allocated initially, (re-)allocate by multiple of 8 */
473 unsigned alloccount = (dst_count + src_count + (OBJECT_INFO_ALLOC-1)) & ~(OBJECT_INFO_ALLOC-1);
474 if (dst_count != alloccount) {
475 struct hwloc_info_s *tmp_infos = realloc(dst_infos, alloccount*sizeof(*dst_infos));
476 if (!tmp_infos)
477 /* Failed to realloc, ignore the appended infos */
478 goto drop;
479 dst_infos = tmp_infos;
480 }
481 for(i=0; i<src_count; i++, dst_count++) {
482 dst_infos[dst_count].name = src_infos[i].name;
483 dst_infos[dst_count].value = src_infos[i].value;
484 }
485 *dst_infosp = dst_infos;
486 *dst_countp = dst_count;
487 free(src_infos);
488 *src_infosp = NULL;
489 *src_countp = 0;
490 return 0;
491
492 drop:
493 /* drop src infos, don't modify dst_infos at all */
494 for(i=0; i<src_count; i++) {
495 free(src_infos[i].name);
496 free(src_infos[i].value);
497 }
498 free(src_infos);
499 *src_infosp = NULL;
500 *src_countp = 0;
501 return -1;
502 }
503
hwloc_obj_add_info(hwloc_obj_t obj,const char * name,const char * value)504 int hwloc_obj_add_info(hwloc_obj_t obj, const char *name, const char *value)
505 {
506 return hwloc__add_info(&obj->infos, &obj->infos_count, name, value);
507 }
508
509 /* This function may be called with topology->tma set, it cannot free() or realloc() */
hwloc__tma_dup_infos(struct hwloc_tma * tma,struct hwloc_info_s ** newip,unsigned * newcp,struct hwloc_info_s * oldi,unsigned oldc)510 int hwloc__tma_dup_infos(struct hwloc_tma *tma,
511 struct hwloc_info_s **newip, unsigned *newcp,
512 struct hwloc_info_s *oldi, unsigned oldc)
513 {
514 struct hwloc_info_s *newi;
515 unsigned i, j;
516 newi = hwloc_tma_calloc(tma, oldc * sizeof(*newi));
517 if (!newi)
518 return -1;
519 for(i=0; i<oldc; i++) {
520 newi[i].name = hwloc_tma_strdup(tma, oldi[i].name);
521 newi[i].value = hwloc_tma_strdup(tma, oldi[i].value);
522 if (!newi[i].name || !newi[i].value)
523 goto failed;
524 }
525 *newip = newi;
526 *newcp = oldc;
527 return 0;
528
529 failed:
530 assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */
531 for(j=0; j<=i; j++) {
532 free(newi[i].name);
533 free(newi[i].value);
534 }
535 free(newi);
536 *newip = NULL;
537 return -1;
538 }
539
540 static void
hwloc__free_object_contents(hwloc_obj_t obj)541 hwloc__free_object_contents(hwloc_obj_t obj)
542 {
543 switch (obj->type) {
544 case HWLOC_OBJ_NUMANODE:
545 free(obj->attr->numanode.page_types);
546 break;
547 default:
548 break;
549 }
550 hwloc__free_infos(obj->infos, obj->infos_count);
551 free(obj->attr);
552 free(obj->children);
553 free(obj->subtype);
554 free(obj->name);
555 hwloc_bitmap_free(obj->cpuset);
556 hwloc_bitmap_free(obj->complete_cpuset);
557 hwloc_bitmap_free(obj->nodeset);
558 hwloc_bitmap_free(obj->complete_nodeset);
559 }
560
561 /* Free an object and all its content. */
562 void
hwloc_free_unlinked_object(hwloc_obj_t obj)563 hwloc_free_unlinked_object(hwloc_obj_t obj)
564 {
565 hwloc__free_object_contents(obj);
566 free(obj);
567 }
568
569 /* Replace old with contents of new object, and make new freeable by the caller.
570 * Requires reconnect (for siblings pointers and group depth),
571 * fixup of sets (only the main cpuset was likely compared before merging),
572 * and update of total_memory and group depth.
573 */
574 static void
hwloc_replace_linked_object(hwloc_obj_t old,hwloc_obj_t new)575 hwloc_replace_linked_object(hwloc_obj_t old, hwloc_obj_t new)
576 {
577 /* drop old fields */
578 hwloc__free_object_contents(old);
579 /* copy old tree pointers to new */
580 new->parent = old->parent;
581 new->next_sibling = old->next_sibling;
582 new->first_child = old->first_child;
583 new->memory_first_child = old->memory_first_child;
584 new->io_first_child = old->io_first_child;
585 new->misc_first_child = old->misc_first_child;
586 /* copy new contents to old now that tree pointers are OK */
587 memcpy(old, new, sizeof(*old));
588 /* clear new to that we may free it */
589 memset(new, 0,sizeof(*new));
590 }
591
592 /* Remove an object and its children from its parent and free them.
593 * Only updates next_sibling/first_child pointers,
594 * so may only be used during early discovery or during destroy.
595 */
596 static void
unlink_and_free_object_and_children(hwloc_obj_t * pobj)597 unlink_and_free_object_and_children(hwloc_obj_t *pobj)
598 {
599 hwloc_obj_t obj = *pobj, child, *pchild;
600
601 for_each_child_safe(child, obj, pchild)
602 unlink_and_free_object_and_children(pchild);
603 for_each_memory_child_safe(child, obj, pchild)
604 unlink_and_free_object_and_children(pchild);
605 for_each_io_child_safe(child, obj, pchild)
606 unlink_and_free_object_and_children(pchild);
607 for_each_misc_child_safe(child, obj, pchild)
608 unlink_and_free_object_and_children(pchild);
609
610 *pobj = obj->next_sibling;
611 hwloc_free_unlinked_object(obj);
612 }
613
614 /* Free an object and its children without unlinking from parent.
615 */
616 void
hwloc_free_object_and_children(hwloc_obj_t obj)617 hwloc_free_object_and_children(hwloc_obj_t obj)
618 {
619 unlink_and_free_object_and_children(&obj);
620 }
621
622 /* Free an object, its next siblings and their children without unlinking from parent.
623 */
624 void
hwloc_free_object_siblings_and_children(hwloc_obj_t obj)625 hwloc_free_object_siblings_and_children(hwloc_obj_t obj)
626 {
627 while (obj)
628 unlink_and_free_object_and_children(&obj);
629 }
630
631 /* insert the (non-empty) list of sibling starting at firstnew as new children of newparent,
632 * and return the address of the pointer to the next one
633 */
634 static hwloc_obj_t *
insert_siblings_list(hwloc_obj_t * firstp,hwloc_obj_t firstnew,hwloc_obj_t newparent)635 insert_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent)
636 {
637 hwloc_obj_t tmp;
638 assert(firstnew);
639 *firstp = tmp = firstnew;
640 tmp->parent = newparent;
641 while (tmp->next_sibling) {
642 tmp = tmp->next_sibling;
643 tmp->parent = newparent;
644 }
645 return &tmp->next_sibling;
646 }
647
648 /* Take the new list starting at firstnew and prepend it to the old list starting at *firstp,
649 * and mark the new children as children of newparent.
650 * May be used during early or late discovery (updates prev_sibling and sibling_rank).
651 * List firstnew must be non-NULL.
652 */
653 static void
prepend_siblings_list(hwloc_obj_t * firstp,hwloc_obj_t firstnew,hwloc_obj_t newparent)654 prepend_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent)
655 {
656 hwloc_obj_t *tmpp, tmp, last;
657 unsigned length;
658
659 /* update parent pointers and find the length and end of the new list */
660 for(length = 0, tmpp = &firstnew, last = NULL ; *tmpp; length++, last = *tmpp, tmpp = &((*tmpp)->next_sibling))
661 (*tmpp)->parent = newparent;
662
663 /* update sibling_rank */
664 for(tmp = *firstp; tmp; tmp = tmp->next_sibling)
665 tmp->sibling_rank += length; /* if it wasn't initialized yet, it'll be overwritten later */
666
667 /* place the existing list at the end of the new one */
668 *tmpp = *firstp;
669 if (*firstp)
670 (*firstp)->prev_sibling = last;
671
672 /* use the beginning of the new list now */
673 *firstp = firstnew;
674 }
675
676 /* Take the new list starting at firstnew and append it to the old list starting at *firstp,
677 * and mark the new children as children of newparent.
678 * May be used during early or late discovery (updates prev_sibling and sibling_rank).
679 */
680 static void
append_siblings_list(hwloc_obj_t * firstp,hwloc_obj_t firstnew,hwloc_obj_t newparent)681 append_siblings_list(hwloc_obj_t *firstp, hwloc_obj_t firstnew, hwloc_obj_t newparent)
682 {
683 hwloc_obj_t *tmpp, tmp, last;
684 unsigned length;
685
686 /* find the length and end of the existing list */
687 for(length = 0, tmpp = firstp, last = NULL ; *tmpp; length++, last = *tmpp, tmpp = &((*tmpp)->next_sibling));
688
689 /* update parent pointers and sibling_rank */
690 for(tmp = firstnew; tmp; tmp = tmp->next_sibling) {
691 tmp->parent = newparent;
692 tmp->sibling_rank += length; /* if it wasn't set yet, it'll be overwritten later */
693 }
694
695 /* place new list at the end of the old one */
696 *tmpp = firstnew;
697 if (firstnew)
698 firstnew->prev_sibling = last;
699 }
700
701 /* Remove an object from its parent and free it.
702 * Only updates next_sibling/first_child pointers,
703 * so may only be used during early discovery.
704 *
705 * Children are inserted in the parent.
706 * If children should be inserted somewhere else (e.g. when merging with a child),
707 * the caller should move them before calling this function.
708 */
709 static void
unlink_and_free_single_object(hwloc_obj_t * pparent)710 unlink_and_free_single_object(hwloc_obj_t *pparent)
711 {
712 hwloc_obj_t old = *pparent;
713 hwloc_obj_t *lastp;
714
715 if (old->type == HWLOC_OBJ_MISC) {
716 /* Misc object */
717
718 /* no normal children */
719 assert(!old->first_child);
720 /* no memory children */
721 assert(!old->memory_first_child);
722 /* no I/O children */
723 assert(!old->io_first_child);
724
725 if (old->misc_first_child)
726 /* insert old misc object children as new siblings below parent instead of old */
727 lastp = insert_siblings_list(pparent, old->misc_first_child, old->parent);
728 else
729 lastp = pparent;
730 /* append old siblings back */
731 *lastp = old->next_sibling;
732
733 } else if (hwloc__obj_type_is_io(old->type)) {
734 /* I/O object */
735
736 /* no normal children */
737 assert(!old->first_child);
738 /* no memory children */
739 assert(!old->memory_first_child);
740
741 if (old->io_first_child)
742 /* insert old I/O object children as new siblings below parent instead of old */
743 lastp = insert_siblings_list(pparent, old->io_first_child, old->parent);
744 else
745 lastp = pparent;
746 /* append old siblings back */
747 *lastp = old->next_sibling;
748
749 /* append old Misc children to parent */
750 if (old->misc_first_child)
751 append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent);
752
753 } else if (hwloc__obj_type_is_memory(old->type)) {
754 /* memory object */
755
756 /* no normal children */
757 assert(!old->first_child);
758 /* no I/O children */
759 assert(!old->io_first_child);
760
761 if (old->memory_first_child)
762 /* insert old memory object children as new siblings below parent instead of old */
763 lastp = insert_siblings_list(pparent, old->memory_first_child, old->parent);
764 else
765 lastp = pparent;
766 /* append old siblings back */
767 *lastp = old->next_sibling;
768
769 /* append old Misc children to parent */
770 if (old->misc_first_child)
771 append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent);
772
773 } else {
774 /* Normal object */
775
776 if (old->first_child)
777 /* insert old object children as new siblings below parent instead of old */
778 lastp = insert_siblings_list(pparent, old->first_child, old->parent);
779 else
780 lastp = pparent;
781 /* append old siblings back */
782 *lastp = old->next_sibling;
783
784 /* append old memory, I/O and Misc children to parent
785 * old->parent cannot be NULL (removing root), misc children should have been moved by the caller earlier.
786 */
787 if (old->memory_first_child)
788 append_siblings_list(&old->parent->memory_first_child, old->memory_first_child, old->parent);
789 if (old->io_first_child)
790 append_siblings_list(&old->parent->io_first_child, old->io_first_child, old->parent);
791 if (old->misc_first_child)
792 append_siblings_list(&old->parent->misc_first_child, old->misc_first_child, old->parent);
793 }
794
795 hwloc_free_unlinked_object(old);
796 }
797
798 /* This function may use a tma, it cannot free() or realloc() */
799 static int
hwloc__duplicate_object(struct hwloc_topology * newtopology,struct hwloc_obj * newparent,struct hwloc_obj * newobj,struct hwloc_obj * src)800 hwloc__duplicate_object(struct hwloc_topology *newtopology,
801 struct hwloc_obj *newparent,
802 struct hwloc_obj *newobj,
803 struct hwloc_obj *src)
804 {
805 struct hwloc_tma *tma = newtopology->tma;
806 hwloc_obj_t *level;
807 unsigned level_width;
808 size_t len;
809 unsigned i;
810 hwloc_obj_t child, prev;
811 int err = 0;
812
813 /* either we're duplicating to an already allocated new root, which has no newparent,
814 * or we're duplicating to a non-yet allocated new non-root, which will have a newparent.
815 */
816 assert(!newparent == !!newobj);
817
818 if (!newobj) {
819 newobj = hwloc_alloc_setup_object(newtopology, src->type, src->os_index);
820 if (!newobj)
821 return -1;
822 }
823
824 /* duplicate all non-object-pointer fields */
825 newobj->logical_index = src->logical_index;
826 newobj->depth = src->depth;
827 newobj->sibling_rank = src->sibling_rank;
828
829 newobj->type = src->type;
830 newobj->os_index = src->os_index;
831 newobj->gp_index = src->gp_index;
832 newobj->symmetric_subtree = src->symmetric_subtree;
833
834 if (src->name)
835 newobj->name = hwloc_tma_strdup(tma, src->name);
836 if (src->subtype)
837 newobj->subtype = hwloc_tma_strdup(tma, src->subtype);
838 newobj->userdata = src->userdata;
839
840 newobj->total_memory = src->total_memory;
841
842 memcpy(newobj->attr, src->attr, sizeof(*newobj->attr));
843
844 if (src->type == HWLOC_OBJ_NUMANODE && src->attr->numanode.page_types_len) {
845 len = src->attr->numanode.page_types_len * sizeof(struct hwloc_memory_page_type_s);
846 newobj->attr->numanode.page_types = hwloc_tma_malloc(tma, len);
847 memcpy(newobj->attr->numanode.page_types, src->attr->numanode.page_types, len);
848 }
849
850 newobj->cpuset = hwloc_bitmap_tma_dup(tma, src->cpuset);
851 newobj->complete_cpuset = hwloc_bitmap_tma_dup(tma, src->complete_cpuset);
852 newobj->nodeset = hwloc_bitmap_tma_dup(tma, src->nodeset);
853 newobj->complete_nodeset = hwloc_bitmap_tma_dup(tma, src->complete_nodeset);
854
855 hwloc__tma_dup_infos(tma, &newobj->infos, &newobj->infos_count, src->infos, src->infos_count);
856
857 /* find our level */
858 if (src->depth < 0) {
859 i = HWLOC_SLEVEL_FROM_DEPTH(src->depth);
860 level = newtopology->slevels[i].objs;
861 level_width = newtopology->slevels[i].nbobjs;
862 /* deal with first/last pointers of special levels, even if not really needed */
863 if (!newobj->logical_index)
864 newtopology->slevels[i].first = newobj;
865 if (newobj->logical_index == newtopology->slevels[i].nbobjs - 1)
866 newtopology->slevels[i].last = newobj;
867 } else {
868 level = newtopology->levels[src->depth];
869 level_width = newtopology->level_nbobjects[src->depth];
870 }
871 /* place us for real */
872 assert(newobj->logical_index < level_width);
873 level[newobj->logical_index] = newobj;
874 /* link to already-inserted cousins */
875 if (newobj->logical_index > 0 && level[newobj->logical_index-1]) {
876 newobj->prev_cousin = level[newobj->logical_index-1];
877 level[newobj->logical_index-1]->next_cousin = newobj;
878 }
879 if (newobj->logical_index < level_width-1 && level[newobj->logical_index+1]) {
880 newobj->next_cousin = level[newobj->logical_index+1];
881 level[newobj->logical_index+1]->prev_cousin = newobj;
882 }
883
884 /* prepare for children */
885 if (src->arity) {
886 newobj->children = hwloc_tma_malloc(tma, src->arity * sizeof(*newobj->children));
887 if (!newobj->children)
888 return -1;
889 }
890 newobj->arity = src->arity;
891 newobj->memory_arity = src->memory_arity;
892 newobj->io_arity = src->io_arity;
893 newobj->misc_arity = src->misc_arity;
894
895 /* actually insert children now */
896 for_each_child(child, src) {
897 err = hwloc__duplicate_object(newtopology, newobj, NULL, child);
898 if (err < 0)
899 goto out_with_children;
900 }
901 for_each_memory_child(child, src) {
902 err = hwloc__duplicate_object(newtopology, newobj, NULL, child);
903 if (err < 0)
904 return err;
905 }
906 for_each_io_child(child, src) {
907 err = hwloc__duplicate_object(newtopology, newobj, NULL, child);
908 if (err < 0)
909 goto out_with_children;
910 }
911 for_each_misc_child(child, src) {
912 err = hwloc__duplicate_object(newtopology, newobj, NULL, child);
913 if (err < 0)
914 goto out_with_children;
915 }
916
917 out_with_children:
918
919 /* link children if all of them where inserted */
920 if (!err) {
921 /* only next_sibling is set by insert_by_parent().
922 * sibling_rank was set above.
923 */
924 if (newobj->arity) {
925 newobj->children[0]->prev_sibling = NULL;
926 for(i=1; i<newobj->arity; i++)
927 newobj->children[i]->prev_sibling = newobj->children[i-1];
928 newobj->last_child = newobj->children[newobj->arity-1];
929 }
930 if (newobj->memory_arity) {
931 child = newobj->memory_first_child;
932 prev = NULL;
933 while (child) {
934 child->prev_sibling = prev;
935 prev = child;
936 child = child->next_sibling;
937 }
938 }
939 if (newobj->io_arity) {
940 child = newobj->io_first_child;
941 prev = NULL;
942 while (child) {
943 child->prev_sibling = prev;
944 prev = child;
945 child = child->next_sibling;
946 }
947 }
948 if (newobj->misc_arity) {
949 child = newobj->misc_first_child;
950 prev = NULL;
951 while (child) {
952 child->prev_sibling = prev;
953 prev = child;
954 child = child->next_sibling;
955 }
956 }
957 }
958
959 /* some children insertion may have failed, but some children may have been inserted below us already.
960 * keep inserting ourself and let the caller clean the entire tree if we return an error.
961 */
962
963 if (newparent) {
964 /* no need to check the children insert order here, the source topology
965 * is supposed to be OK already, and we have debug asserts.
966 */
967 hwloc_insert_object_by_parent(newtopology, newparent, newobj);
968
969 /* place us inside our parent children array */
970 if (hwloc__obj_type_is_normal(newobj->type))
971 newparent->children[newobj->sibling_rank] = newobj;
972 }
973
974 return err;
975 }
976
977 static int
978 hwloc__topology_init (struct hwloc_topology **topologyp, unsigned nblevels, struct hwloc_tma *tma);
979
980 /* This function may use a tma, it cannot free() or realloc() */
981 int
hwloc__topology_dup(hwloc_topology_t * newp,hwloc_topology_t old,struct hwloc_tma * tma)982 hwloc__topology_dup(hwloc_topology_t *newp,
983 hwloc_topology_t old,
984 struct hwloc_tma *tma)
985 {
986 hwloc_topology_t new;
987 hwloc_obj_t newroot;
988 hwloc_obj_t oldroot = hwloc_get_root_obj(old);
989 unsigned i;
990 int err;
991
992 if (!old->is_loaded) {
993 errno = EINVAL;
994 return -1;
995 }
996
997 err = hwloc__topology_init(&new, old->nb_levels_allocated, tma);
998 if (err < 0)
999 goto out;
1000
1001 new->flags = old->flags;
1002 memcpy(new->type_filter, old->type_filter, sizeof(old->type_filter));
1003 new->is_thissystem = old->is_thissystem;
1004 new->is_loaded = 1;
1005 new->pid = old->pid;
1006 new->next_gp_index = old->next_gp_index;
1007
1008 memcpy(&new->binding_hooks, &old->binding_hooks, sizeof(old->binding_hooks));
1009
1010 memcpy(new->support.discovery, old->support.discovery, sizeof(*old->support.discovery));
1011 memcpy(new->support.cpubind, old->support.cpubind, sizeof(*old->support.cpubind));
1012 memcpy(new->support.membind, old->support.membind, sizeof(*old->support.membind));
1013 memcpy(new->support.misc, old->support.misc, sizeof(*old->support.misc));
1014
1015 new->allowed_cpuset = hwloc_bitmap_tma_dup(tma, old->allowed_cpuset);
1016 new->allowed_nodeset = hwloc_bitmap_tma_dup(tma, old->allowed_nodeset);
1017
1018 new->userdata_export_cb = old->userdata_export_cb;
1019 new->userdata_import_cb = old->userdata_import_cb;
1020 new->userdata_not_decoded = old->userdata_not_decoded;
1021
1022 assert(!old->machine_memory.local_memory);
1023 assert(!old->machine_memory.page_types_len);
1024 assert(!old->machine_memory.page_types);
1025
1026 for(i = HWLOC_OBJ_TYPE_MIN; i < HWLOC_OBJ_TYPE_MAX; i++)
1027 new->type_depth[i] = old->type_depth[i];
1028
1029 /* duplicate levels and we'll place objects there when duplicating objects */
1030 new->nb_levels = old->nb_levels;
1031 assert(new->nb_levels_allocated >= new->nb_levels);
1032 for(i=1 /* root level already allocated */ ; i<new->nb_levels; i++) {
1033 new->level_nbobjects[i] = old->level_nbobjects[i];
1034 new->levels[i] = hwloc_tma_calloc(tma, new->level_nbobjects[i] * sizeof(*new->levels[i]));
1035 }
1036 for(i=0; i<HWLOC_NR_SLEVELS; i++) {
1037 new->slevels[i].nbobjs = old->slevels[i].nbobjs;
1038 if (new->slevels[i].nbobjs)
1039 new->slevels[i].objs = hwloc_tma_calloc(tma, new->slevels[i].nbobjs * sizeof(*new->slevels[i].objs));
1040 }
1041
1042 /* recursively duplicate object children */
1043 newroot = hwloc_get_root_obj(new);
1044 err = hwloc__duplicate_object(new, NULL, newroot, oldroot);
1045 if (err < 0)
1046 goto out_with_topology;
1047
1048 err = hwloc_internal_distances_dup(new, old);
1049 if (err < 0)
1050 goto out_with_topology;
1051
1052 err = hwloc_internal_memattrs_dup(new, old);
1053 if (err < 0)
1054 goto out_with_topology;
1055
1056 err = hwloc_internal_cpukinds_dup(new, old);
1057 if (err < 0)
1058 goto out_with_topology;
1059
1060 /* we connected everything during duplication */
1061 new->modified = 0;
1062
1063 /* no need to duplicate backends, topology is already loaded */
1064 new->backends = NULL;
1065 new->get_pci_busid_cpuset_backend = NULL;
1066
1067 #ifndef HWLOC_DEBUG
1068 if (getenv("HWLOC_DEBUG_CHECK"))
1069 #endif
1070 hwloc_topology_check(new);
1071
1072 *newp = new;
1073 return 0;
1074
1075 out_with_topology:
1076 assert(!tma || !tma->dontfree); /* this tma cannot fail to allocate */
1077 hwloc_topology_destroy(new);
1078 out:
1079 return -1;
1080 }
1081
1082 int
hwloc_topology_dup(hwloc_topology_t * newp,hwloc_topology_t old)1083 hwloc_topology_dup(hwloc_topology_t *newp,
1084 hwloc_topology_t old)
1085 {
1086 return hwloc__topology_dup(newp, old, NULL);
1087 }
1088
1089 /* WARNING: The indexes of this array MUST match the ordering that of
1090 the obj_order_type[] array, below. Specifically, the values must
1091 be laid out such that:
1092
1093 obj_order_type[obj_type_order[N]] = N
1094
1095 for all HWLOC_OBJ_* values of N. Put differently:
1096
1097 obj_type_order[A] = B
1098
1099 where the A values are in order of the hwloc_obj_type_t enum, and
1100 the B values are the corresponding indexes of obj_order_type.
1101
1102 We can't use C99 syntax to initialize this in a little safer manner
1103 -- bummer. :-(
1104
1105 Correctness is asserted in hwloc_topology_init() when debug is enabled.
1106 */
1107 /***** Make sure you update obj_type_priority[] below as well. *****/
1108 static const unsigned obj_type_order[] = {
1109 /* first entry is HWLOC_OBJ_MACHINE */ 0,
1110 /* next entry is HWLOC_OBJ_PACKAGE */ 4,
1111 /* next entry is HWLOC_OBJ_CORE */ 14,
1112 /* next entry is HWLOC_OBJ_PU */ 18,
1113 /* next entry is HWLOC_OBJ_L1CACHE */ 12,
1114 /* next entry is HWLOC_OBJ_L2CACHE */ 10,
1115 /* next entry is HWLOC_OBJ_L3CACHE */ 8,
1116 /* next entry is HWLOC_OBJ_L4CACHE */ 7,
1117 /* next entry is HWLOC_OBJ_L5CACHE */ 6,
1118 /* next entry is HWLOC_OBJ_L1ICACHE */ 13,
1119 /* next entry is HWLOC_OBJ_L2ICACHE */ 11,
1120 /* next entry is HWLOC_OBJ_L3ICACHE */ 9,
1121 /* next entry is HWLOC_OBJ_GROUP */ 1,
1122 /* next entry is HWLOC_OBJ_NUMANODE */ 3,
1123 /* next entry is HWLOC_OBJ_BRIDGE */ 15,
1124 /* next entry is HWLOC_OBJ_PCI_DEVICE */ 16,
1125 /* next entry is HWLOC_OBJ_OS_DEVICE */ 17,
1126 /* next entry is HWLOC_OBJ_MISC */ 19,
1127 /* next entry is HWLOC_OBJ_MEMCACHE */ 2,
1128 /* next entry is HWLOC_OBJ_DIE */ 5
1129 };
1130
1131 #ifndef NDEBUG /* only used in debug check assert if !NDEBUG */
1132 static const hwloc_obj_type_t obj_order_type[] = {
1133 HWLOC_OBJ_MACHINE,
1134 HWLOC_OBJ_GROUP,
1135 HWLOC_OBJ_MEMCACHE,
1136 HWLOC_OBJ_NUMANODE,
1137 HWLOC_OBJ_PACKAGE,
1138 HWLOC_OBJ_DIE,
1139 HWLOC_OBJ_L5CACHE,
1140 HWLOC_OBJ_L4CACHE,
1141 HWLOC_OBJ_L3CACHE,
1142 HWLOC_OBJ_L3ICACHE,
1143 HWLOC_OBJ_L2CACHE,
1144 HWLOC_OBJ_L2ICACHE,
1145 HWLOC_OBJ_L1CACHE,
1146 HWLOC_OBJ_L1ICACHE,
1147 HWLOC_OBJ_CORE,
1148 HWLOC_OBJ_BRIDGE,
1149 HWLOC_OBJ_PCI_DEVICE,
1150 HWLOC_OBJ_OS_DEVICE,
1151 HWLOC_OBJ_PU,
1152 HWLOC_OBJ_MISC /* Misc is always a leaf */
1153 };
1154 #endif
1155 /***** Make sure you update obj_type_priority[] below as well. *****/
1156
1157 /* priority to be used when merging identical parent/children object
1158 * (in merge_useless_child), keep the highest priority one.
1159 *
1160 * Always keep Machine/NUMANode/PU/PCIDev/OSDev
1161 * then Core
1162 * then Package
1163 * then Die
1164 * then Cache,
1165 * then Instruction Caches
1166 * then always drop Group/Misc/Bridge.
1167 *
1168 * Some type won't actually ever be involved in such merging.
1169 */
1170 /***** Make sure you update this array when changing the list of types. *****/
1171 static const int obj_type_priority[] = {
1172 /* first entry is HWLOC_OBJ_MACHINE */ 90,
1173 /* next entry is HWLOC_OBJ_PACKAGE */ 40,
1174 /* next entry is HWLOC_OBJ_CORE */ 60,
1175 /* next entry is HWLOC_OBJ_PU */ 100,
1176 /* next entry is HWLOC_OBJ_L1CACHE */ 20,
1177 /* next entry is HWLOC_OBJ_L2CACHE */ 20,
1178 /* next entry is HWLOC_OBJ_L3CACHE */ 20,
1179 /* next entry is HWLOC_OBJ_L4CACHE */ 20,
1180 /* next entry is HWLOC_OBJ_L5CACHE */ 20,
1181 /* next entry is HWLOC_OBJ_L1ICACHE */ 19,
1182 /* next entry is HWLOC_OBJ_L2ICACHE */ 19,
1183 /* next entry is HWLOC_OBJ_L3ICACHE */ 19,
1184 /* next entry is HWLOC_OBJ_GROUP */ 0,
1185 /* next entry is HWLOC_OBJ_NUMANODE */ 100,
1186 /* next entry is HWLOC_OBJ_BRIDGE */ 0,
1187 /* next entry is HWLOC_OBJ_PCI_DEVICE */ 100,
1188 /* next entry is HWLOC_OBJ_OS_DEVICE */ 100,
1189 /* next entry is HWLOC_OBJ_MISC */ 0,
1190 /* next entry is HWLOC_OBJ_MEMCACHE */ 19,
1191 /* next entry is HWLOC_OBJ_DIE */ 30
1192 };
1193
hwloc_compare_types(hwloc_obj_type_t type1,hwloc_obj_type_t type2)1194 int hwloc_compare_types (hwloc_obj_type_t type1, hwloc_obj_type_t type2)
1195 {
1196 unsigned order1 = obj_type_order[type1];
1197 unsigned order2 = obj_type_order[type2];
1198
1199 /* only normal objects are comparable. others are only comparable with machine */
1200 if (!hwloc__obj_type_is_normal(type1)
1201 && hwloc__obj_type_is_normal(type2) && type2 != HWLOC_OBJ_MACHINE)
1202 return HWLOC_TYPE_UNORDERED;
1203 if (!hwloc__obj_type_is_normal(type2)
1204 && hwloc__obj_type_is_normal(type1) && type1 != HWLOC_OBJ_MACHINE)
1205 return HWLOC_TYPE_UNORDERED;
1206
1207 return order1 - order2;
1208 }
1209
1210 enum hwloc_obj_cmp_e {
1211 HWLOC_OBJ_EQUAL = HWLOC_BITMAP_EQUAL, /**< \brief Equal */
1212 HWLOC_OBJ_INCLUDED = HWLOC_BITMAP_INCLUDED, /**< \brief Strictly included into */
1213 HWLOC_OBJ_CONTAINS = HWLOC_BITMAP_CONTAINS, /**< \brief Strictly contains */
1214 HWLOC_OBJ_INTERSECTS = HWLOC_BITMAP_INTERSECTS, /**< \brief Intersects, but no inclusion! */
1215 HWLOC_OBJ_DIFFERENT = HWLOC_BITMAP_DIFFERENT /**< \brief No intersection */
1216 };
1217
1218 static enum hwloc_obj_cmp_e
hwloc_type_cmp(hwloc_obj_t obj1,hwloc_obj_t obj2)1219 hwloc_type_cmp(hwloc_obj_t obj1, hwloc_obj_t obj2)
1220 {
1221 hwloc_obj_type_t type1 = obj1->type;
1222 hwloc_obj_type_t type2 = obj2->type;
1223 int compare;
1224
1225 compare = hwloc_compare_types(type1, type2);
1226 if (compare == HWLOC_TYPE_UNORDERED)
1227 return HWLOC_OBJ_DIFFERENT; /* we cannot do better */
1228 if (compare > 0)
1229 return HWLOC_OBJ_INCLUDED;
1230 if (compare < 0)
1231 return HWLOC_OBJ_CONTAINS;
1232
1233 if (obj1->type == HWLOC_OBJ_GROUP
1234 && (obj1->attr->group.kind != obj2->attr->group.kind
1235 || obj1->attr->group.subkind != obj2->attr->group.subkind))
1236 return HWLOC_OBJ_DIFFERENT; /* we cannot do better */
1237
1238 return HWLOC_OBJ_EQUAL;
1239 }
1240
1241 /*
1242 * How to compare objects based on cpusets.
1243 */
1244 static int
hwloc_obj_cmp_sets(hwloc_obj_t obj1,hwloc_obj_t obj2)1245 hwloc_obj_cmp_sets(hwloc_obj_t obj1, hwloc_obj_t obj2)
1246 {
1247 hwloc_bitmap_t set1, set2;
1248
1249 assert(!hwloc__obj_type_is_special(obj1->type));
1250 assert(!hwloc__obj_type_is_special(obj2->type));
1251
1252 /* compare cpusets first */
1253 if (obj1->complete_cpuset && obj2->complete_cpuset) {
1254 set1 = obj1->complete_cpuset;
1255 set2 = obj2->complete_cpuset;
1256 } else {
1257 set1 = obj1->cpuset;
1258 set2 = obj2->cpuset;
1259 }
1260 if (set1 && set2 && !hwloc_bitmap_iszero(set1) && !hwloc_bitmap_iszero(set2))
1261 return hwloc_bitmap_compare_inclusion(set1, set2);
1262
1263 return HWLOC_OBJ_DIFFERENT;
1264 }
1265
1266 /* Compare object cpusets based on complete_cpuset if defined (always correctly ordered),
1267 * or fallback to the main cpusets (only correctly ordered during early insert before disallowed bits are cleared).
1268 *
1269 * This is the sane way to compare object among a horizontal level.
1270 */
1271 int
hwloc__object_cpusets_compare_first(hwloc_obj_t obj1,hwloc_obj_t obj2)1272 hwloc__object_cpusets_compare_first(hwloc_obj_t obj1, hwloc_obj_t obj2)
1273 {
1274 if (obj1->complete_cpuset && obj2->complete_cpuset)
1275 return hwloc_bitmap_compare_first(obj1->complete_cpuset, obj2->complete_cpuset);
1276 else if (obj1->cpuset && obj2->cpuset)
1277 return hwloc_bitmap_compare_first(obj1->cpuset, obj2->cpuset);
1278 return 0;
1279 }
1280
1281 /*
1282 * How to insert objects into the topology.
1283 *
1284 * Note: during detection, only the first_child and next_sibling pointers are
1285 * kept up to date. Others are computed only once topology detection is
1286 * complete.
1287 */
1288
1289 /* merge new object attributes in old.
1290 * use old if defined, otherwise use new.
1291 */
1292 static void
merge_insert_equal(hwloc_obj_t new,hwloc_obj_t old)1293 merge_insert_equal(hwloc_obj_t new, hwloc_obj_t old)
1294 {
1295 if (old->os_index == HWLOC_UNKNOWN_INDEX)
1296 old->os_index = new->os_index;
1297
1298 if (new->infos_count) {
1299 /* FIXME: dedup */
1300 hwloc__move_infos(&old->infos, &old->infos_count,
1301 &new->infos, &new->infos_count);
1302 }
1303
1304 if (new->name && !old->name) {
1305 old->name = new->name;
1306 new->name = NULL;
1307 }
1308 if (new->subtype && !old->subtype) {
1309 old->subtype = new->subtype;
1310 new->subtype = NULL;
1311 }
1312
1313 /* Ignore userdata. It will be NULL before load().
1314 * It may be non-NULL if alloc+insert_group() after load().
1315 */
1316
1317 switch(new->type) {
1318 case HWLOC_OBJ_NUMANODE:
1319 if (new->attr->numanode.local_memory && !old->attr->numanode.local_memory) {
1320 /* no memory in old, use new memory */
1321 old->attr->numanode.local_memory = new->attr->numanode.local_memory;
1322 free(old->attr->numanode.page_types);
1323 old->attr->numanode.page_types_len = new->attr->numanode.page_types_len;
1324 old->attr->numanode.page_types = new->attr->numanode.page_types;
1325 new->attr->numanode.page_types = NULL;
1326 new->attr->numanode.page_types_len = 0;
1327 }
1328 /* old->attr->numanode.total_memory will be updated by propagate_total_memory() */
1329 break;
1330 case HWLOC_OBJ_L1CACHE:
1331 case HWLOC_OBJ_L2CACHE:
1332 case HWLOC_OBJ_L3CACHE:
1333 case HWLOC_OBJ_L4CACHE:
1334 case HWLOC_OBJ_L5CACHE:
1335 case HWLOC_OBJ_L1ICACHE:
1336 case HWLOC_OBJ_L2ICACHE:
1337 case HWLOC_OBJ_L3ICACHE:
1338 if (!old->attr->cache.size)
1339 old->attr->cache.size = new->attr->cache.size;
1340 if (!old->attr->cache.linesize)
1341 old->attr->cache.size = new->attr->cache.linesize;
1342 if (!old->attr->cache.associativity)
1343 old->attr->cache.size = new->attr->cache.linesize;
1344 break;
1345 default:
1346 break;
1347 }
1348 }
1349
1350 /* returns the result of merge, or NULL if not merged */
1351 static __hwloc_inline hwloc_obj_t
hwloc__insert_try_merge_group(hwloc_topology_t topology,hwloc_obj_t old,hwloc_obj_t new)1352 hwloc__insert_try_merge_group(hwloc_topology_t topology, hwloc_obj_t old, hwloc_obj_t new)
1353 {
1354 if (new->type == HWLOC_OBJ_GROUP && old->type == HWLOC_OBJ_GROUP) {
1355 /* which group do we keep? */
1356 if (new->attr->group.dont_merge) {
1357 if (old->attr->group.dont_merge)
1358 /* nobody wants to be merged */
1359 return NULL;
1360
1361 /* keep the new one, it doesn't want to be merged */
1362 hwloc_replace_linked_object(old, new);
1363 topology->modified = 1;
1364 return new;
1365
1366 } else {
1367 if (old->attr->group.dont_merge)
1368 /* keep the old one, it doesn't want to be merged */
1369 return old;
1370
1371 /* compare subkinds to decide which group to keep */
1372 if (new->attr->group.kind < old->attr->group.kind) {
1373 /* keep smaller kind */
1374 hwloc_replace_linked_object(old, new);
1375 topology->modified = 1;
1376 }
1377 return old;
1378 }
1379 }
1380
1381 if (new->type == HWLOC_OBJ_GROUP && !new->attr->group.dont_merge) {
1382
1383 if (old->type == HWLOC_OBJ_PU && new->attr->group.kind == HWLOC_GROUP_KIND_MEMORY)
1384 /* Never merge Memory groups with PU, we don't want to attach Memory under PU */
1385 return NULL;
1386
1387 /* Remove the Group now. The normal ignore code path wouldn't tell us whether the Group was removed or not,
1388 * while some callers need to know (at least hwloc_topology_insert_group()).
1389 */
1390 return old;
1391
1392 } else if (old->type == HWLOC_OBJ_GROUP && !old->attr->group.dont_merge) {
1393
1394 if (new->type == HWLOC_OBJ_PU && old->attr->group.kind == HWLOC_GROUP_KIND_MEMORY)
1395 /* Never merge Memory groups with PU, we don't want to attach Memory under PU */
1396 return NULL;
1397
1398 /* Replace the Group with the new object contents
1399 * and let the caller free the new object
1400 */
1401 hwloc_replace_linked_object(old, new);
1402 topology->modified = 1;
1403 return old;
1404
1405 } else {
1406 /* cannot merge */
1407 return NULL;
1408 }
1409 }
1410
1411 /*
1412 * The main insertion routine, only used for CPU-side object (normal types)
1413 * uisng cpuset only (or complete_cpuset).
1414 *
1415 * Try to insert OBJ in CUR, recurse if needed.
1416 * Returns the object if it was inserted,
1417 * the remaining object it was merged,
1418 * NULL if failed to insert.
1419 */
1420 static struct hwloc_obj *
hwloc___insert_object_by_cpuset(struct hwloc_topology * topology,hwloc_obj_t cur,hwloc_obj_t obj,const char * reason)1421 hwloc___insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t cur, hwloc_obj_t obj,
1422 const char *reason)
1423 {
1424 hwloc_obj_t child, next_child = NULL, tmp;
1425 /* These will always point to the pointer to their next last child. */
1426 hwloc_obj_t *cur_children = &cur->first_child;
1427 hwloc_obj_t *obj_children = &obj->first_child;
1428 /* Pointer where OBJ should be put */
1429 hwloc_obj_t *putp = NULL; /* OBJ position isn't found yet */
1430
1431 assert(!hwloc__obj_type_is_memory(obj->type));
1432
1433 /* Iteration with prefetching to be completely safe against CHILD removal.
1434 * The list is already sorted by cpuset, and there's no intersection between siblings.
1435 */
1436 for (child = cur->first_child, child ? next_child = child->next_sibling : NULL;
1437 child;
1438 child = next_child, child ? next_child = child->next_sibling : NULL) {
1439
1440 int res = hwloc_obj_cmp_sets(obj, child);
1441 int setres = res;
1442
1443 if (res == HWLOC_OBJ_EQUAL) {
1444 hwloc_obj_t merged = hwloc__insert_try_merge_group(topology, child, obj);
1445 if (merged)
1446 return merged;
1447 /* otherwise compare actual types to decide of the inclusion */
1448 res = hwloc_type_cmp(obj, child);
1449 }
1450
1451 switch (res) {
1452 case HWLOC_OBJ_EQUAL:
1453 /* Two objects with same type.
1454 * Groups are handled above.
1455 */
1456 merge_insert_equal(obj, child);
1457 /* Already present, no need to insert. */
1458 return child;
1459
1460 case HWLOC_OBJ_INCLUDED:
1461 /* OBJ is strictly contained is some child of CUR, go deeper. */
1462 return hwloc___insert_object_by_cpuset(topology, child, obj, reason);
1463
1464 case HWLOC_OBJ_INTERSECTS:
1465 report_insert_error(obj, child, "intersection without inclusion", reason);
1466 goto putback;
1467
1468 case HWLOC_OBJ_DIFFERENT:
1469 /* OBJ should be a child of CUR before CHILD, mark its position if not found yet. */
1470 if (!putp && hwloc__object_cpusets_compare_first(obj, child) < 0)
1471 /* Don't insert yet, there could be intersect errors later */
1472 putp = cur_children;
1473 /* Advance cur_children. */
1474 cur_children = &child->next_sibling;
1475 break;
1476
1477 case HWLOC_OBJ_CONTAINS:
1478 /* OBJ contains CHILD, remove CHILD from CUR */
1479 *cur_children = child->next_sibling;
1480 child->next_sibling = NULL;
1481 /* Put CHILD in OBJ */
1482 *obj_children = child;
1483 obj_children = &child->next_sibling;
1484 child->parent = obj;
1485 if (setres == HWLOC_OBJ_EQUAL) {
1486 obj->memory_first_child = child->memory_first_child;
1487 child->memory_first_child = NULL;
1488 for(tmp=obj->memory_first_child; tmp; tmp = tmp->next_sibling)
1489 tmp->parent = obj;
1490 }
1491 break;
1492 }
1493 }
1494 /* cur/obj_children points to last CUR/OBJ child next_sibling pointer, which must be NULL. */
1495 assert(!*obj_children);
1496 assert(!*cur_children);
1497
1498 /* Put OBJ where it belongs, or in last in CUR's children. */
1499 if (!putp)
1500 putp = cur_children;
1501 obj->next_sibling = *putp;
1502 *putp = obj;
1503 obj->parent = cur;
1504
1505 topology->modified = 1;
1506 return obj;
1507
1508 putback:
1509 /* OBJ cannot be inserted.
1510 * Put-back OBJ children in CUR and return an error.
1511 */
1512 if (putp)
1513 cur_children = putp; /* No need to try to insert before where OBJ was supposed to go */
1514 else
1515 cur_children = &cur->first_child; /* Start from the beginning */
1516 /* We can insert in order, but there can be holes in the middle. */
1517 while ((child = obj->first_child) != NULL) {
1518 /* Remove from OBJ */
1519 obj->first_child = child->next_sibling;
1520 /* Find child position in CUR, and reinsert it. */
1521 while (*cur_children && hwloc__object_cpusets_compare_first(*cur_children, child) < 0)
1522 cur_children = &(*cur_children)->next_sibling;
1523 child->next_sibling = *cur_children;
1524 *cur_children = child;
1525 child->parent = cur;
1526 }
1527 return NULL;
1528 }
1529
1530 /* this differs from hwloc_get_obj_covering_cpuset() by:
1531 * - not looking at the parent cpuset first, which means we can insert
1532 * below root even if root PU bits are not set yet (PU are inserted later).
1533 * - returning the first child that exactly matches instead of walking down in case
1534 * of identical children.
1535 */
1536 static struct hwloc_obj *
hwloc__find_obj_covering_memory_cpuset(struct hwloc_topology * topology,hwloc_obj_t parent,hwloc_bitmap_t cpuset)1537 hwloc__find_obj_covering_memory_cpuset(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_bitmap_t cpuset)
1538 {
1539 hwloc_obj_t child = hwloc_get_child_covering_cpuset(topology, cpuset, parent);
1540 if (!child)
1541 return parent;
1542 if (child && hwloc_bitmap_isequal(child->cpuset, cpuset))
1543 return child;
1544 return hwloc__find_obj_covering_memory_cpuset(topology, child, cpuset);
1545 }
1546
1547 static struct hwloc_obj *
hwloc__find_insert_memory_parent(struct hwloc_topology * topology,hwloc_obj_t obj,const char * reason)1548 hwloc__find_insert_memory_parent(struct hwloc_topology *topology, hwloc_obj_t obj,
1549 const char *reason)
1550 {
1551 hwloc_obj_t parent, group, result;
1552
1553 if (hwloc_bitmap_iszero(obj->cpuset)) {
1554 /* CPU-less go in dedicated group below root */
1555 parent = topology->levels[0][0];
1556
1557 } else {
1558 /* find the highest obj covering the cpuset */
1559 parent = hwloc__find_obj_covering_memory_cpuset(topology, topology->levels[0][0], obj->cpuset);
1560 if (!parent) {
1561 /* fallback to root */
1562 parent = hwloc_get_root_obj(topology);
1563 }
1564
1565 if (parent->type == HWLOC_OBJ_PU) {
1566 /* Never attach to PU, try parent */
1567 parent = parent->parent;
1568 assert(parent);
1569 }
1570
1571 /* TODO: if root->cpuset was updated earlier, we would be sure whether the group will remain identical to root */
1572 if (parent != topology->levels[0][0] && hwloc_bitmap_isequal(parent->cpuset, obj->cpuset))
1573 /* that parent is fine */
1574 return parent;
1575 }
1576
1577 if (!hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP))
1578 /* even if parent isn't perfect, we don't want an intermediate group */
1579 return parent;
1580
1581 /* need to insert an intermediate group for attaching the NUMA node */
1582 group = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
1583 if (!group)
1584 /* failed to create the group, fallback to larger parent */
1585 return parent;
1586
1587 group->attr->group.kind = HWLOC_GROUP_KIND_MEMORY;
1588 group->cpuset = hwloc_bitmap_dup(obj->cpuset);
1589 group->complete_cpuset = hwloc_bitmap_dup(obj->complete_cpuset);
1590 /* we could duplicate nodesets too but hwloc__insert_object_by_cpuset()
1591 * doesn't actually need it. and it could prevent future calls from reusing
1592 * that groups for other NUMA nodes.
1593 */
1594 if (!group->cpuset != !obj->cpuset
1595 || !group->complete_cpuset != !obj->complete_cpuset) {
1596 /* failed to create the group, fallback to larger parent */
1597 hwloc_free_unlinked_object(group);
1598 return parent;
1599 }
1600
1601 result = hwloc__insert_object_by_cpuset(topology, parent, group, reason);
1602 if (!result) {
1603 /* failed to insert, fallback to larger parent */
1604 return parent;
1605 }
1606
1607 assert(result == group);
1608 return group;
1609 }
1610
1611 /* only works for MEMCACHE and NUMAnode with a single bit in nodeset */
1612 static hwloc_obj_t
hwloc___attach_memory_object_by_nodeset(struct hwloc_topology * topology,hwloc_obj_t parent,hwloc_obj_t obj,const char * reason)1613 hwloc___attach_memory_object_by_nodeset(struct hwloc_topology *topology, hwloc_obj_t parent,
1614 hwloc_obj_t obj, const char *reason)
1615 {
1616 hwloc_obj_t *curp = &parent->memory_first_child;
1617 unsigned first = hwloc_bitmap_first(obj->nodeset);
1618
1619 while (*curp) {
1620 hwloc_obj_t cur = *curp;
1621 unsigned curfirst = hwloc_bitmap_first(cur->nodeset);
1622
1623 if (first < curfirst) {
1624 /* insert before cur */
1625 obj->next_sibling = cur;
1626 *curp = obj;
1627 obj->memory_first_child = NULL;
1628 obj->parent = parent;
1629 topology->modified = 1;
1630 return obj;
1631 }
1632
1633 if (first == curfirst) {
1634 /* identical nodeset */
1635 if (obj->type == HWLOC_OBJ_NUMANODE) {
1636 if (cur->type == HWLOC_OBJ_NUMANODE) {
1637 /* identical NUMA nodes? ignore the new one */
1638 report_insert_error(obj, cur, "NUMAnodes with identical nodesets", reason);
1639 return NULL;
1640 }
1641 assert(cur->type == HWLOC_OBJ_MEMCACHE);
1642 /* insert the new NUMA node below that existing memcache */
1643 return hwloc___attach_memory_object_by_nodeset(topology, cur, obj, reason);
1644
1645 } else {
1646 assert(obj->type == HWLOC_OBJ_MEMCACHE);
1647 if (cur->type == HWLOC_OBJ_MEMCACHE) {
1648 if (cur->attr->cache.depth == obj->attr->cache.depth)
1649 /* memcache with same nodeset and depth, ignore the new one */
1650 return NULL;
1651 if (cur->attr->cache.depth > obj->attr->cache.depth)
1652 /* memcache with higher cache depth is actually *higher* in the hierarchy
1653 * (depth starts from the NUMA node).
1654 * insert the new memcache below the existing one
1655 */
1656 return hwloc___attach_memory_object_by_nodeset(topology, cur, obj, reason);
1657 }
1658 /* insert the memcache above the existing memcache or numa node */
1659 obj->next_sibling = cur->next_sibling;
1660 cur->next_sibling = NULL;
1661 obj->memory_first_child = cur;
1662 cur->parent = obj;
1663 *curp = obj;
1664 obj->parent = parent;
1665 topology->modified = 1;
1666 return obj;
1667 }
1668 }
1669
1670 curp = &cur->next_sibling;
1671 }
1672
1673 /* append to the end of the list */
1674 obj->next_sibling = NULL;
1675 *curp = obj;
1676 obj->memory_first_child = NULL;
1677 obj->parent = parent;
1678 topology->modified = 1;
1679 return obj;
1680 }
1681
1682 /* Attach the given memory object below the given normal parent.
1683 *
1684 * Only the nodeset is used to find the location inside memory children below parent.
1685 *
1686 * Nodeset inclusion inside the given memory hierarchy is guaranteed by this function,
1687 * but nodesets are not propagated to CPU-side parent yet. It will be done by
1688 * propagate_nodeset() later.
1689 */
1690 struct hwloc_obj *
hwloc__attach_memory_object(struct hwloc_topology * topology,hwloc_obj_t parent,hwloc_obj_t obj,const char * reason)1691 hwloc__attach_memory_object(struct hwloc_topology *topology, hwloc_obj_t parent,
1692 hwloc_obj_t obj, const char *reason)
1693 {
1694 hwloc_obj_t result;
1695
1696 assert(parent);
1697 assert(hwloc__obj_type_is_normal(parent->type));
1698
1699 /* Check the nodeset */
1700 if (!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset))
1701 return NULL;
1702 /* Initialize or check the complete nodeset */
1703 if (!obj->complete_nodeset) {
1704 obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset);
1705 } else if (!hwloc_bitmap_isincluded(obj->nodeset, obj->complete_nodeset)) {
1706 return NULL;
1707 }
1708 /* Neither ACPI nor Linux support multinode mscache */
1709 assert(hwloc_bitmap_weight(obj->nodeset) == 1);
1710
1711 #if 0
1712 /* TODO: enable this instead of hack in fixup_sets once NUMA nodes are inserted late */
1713 /* copy the parent cpuset in case it's larger than expected.
1714 * we could also keep the cpuset smaller than the parent and say that a normal-parent
1715 * can have multiple memory children with smaller cpusets.
1716 * However, the user decided the ignore Groups, so hierarchy/locality loss is expected.
1717 */
1718 hwloc_bitmap_copy(obj->cpuset, parent->cpuset);
1719 hwloc_bitmap_copy(obj->complete_cpuset, parent->complete_cpuset);
1720 #endif
1721
1722 result = hwloc___attach_memory_object_by_nodeset(topology, parent, obj, reason);
1723 if (result == obj) {
1724 /* Add the bit to the top sets, and to the parent CPU-side object */
1725 if (obj->type == HWLOC_OBJ_NUMANODE) {
1726 hwloc_bitmap_set(topology->levels[0][0]->nodeset, obj->os_index);
1727 hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index);
1728 }
1729 }
1730 if (result != obj) {
1731 /* either failed to insert, or got merged, free the original object */
1732 hwloc_free_unlinked_object(obj);
1733 }
1734 return result;
1735 }
1736
1737 /* insertion routine that lets you change the error reporting callback */
1738 struct hwloc_obj *
hwloc__insert_object_by_cpuset(struct hwloc_topology * topology,hwloc_obj_t root,hwloc_obj_t obj,const char * reason)1739 hwloc__insert_object_by_cpuset(struct hwloc_topology *topology, hwloc_obj_t root,
1740 hwloc_obj_t obj, const char *reason)
1741 {
1742 struct hwloc_obj *result;
1743
1744 #ifdef HWLOC_DEBUG
1745 assert(!hwloc__obj_type_is_special(obj->type));
1746
1747 /* we need at least one non-NULL set (normal or complete, cpuset or nodeset) */
1748 assert(obj->cpuset || obj->complete_cpuset || obj->nodeset || obj->complete_nodeset);
1749 /* we support the case where all of them are empty.
1750 * it may happen when hwloc__find_insert_memory_parent()
1751 * inserts a Group for a CPU-less NUMA-node.
1752 */
1753 #endif
1754
1755 if (hwloc__obj_type_is_memory(obj->type)) {
1756 if (!root) {
1757 root = hwloc__find_insert_memory_parent(topology, obj, reason);
1758 if (!root) {
1759 hwloc_free_unlinked_object(obj);
1760 return NULL;
1761 }
1762 }
1763 return hwloc__attach_memory_object(topology, root, obj, reason);
1764 }
1765
1766 if (!root)
1767 /* Start at the top. */
1768 root = topology->levels[0][0];
1769
1770 result = hwloc___insert_object_by_cpuset(topology, root, obj, reason);
1771 if (result && result->type == HWLOC_OBJ_PU) {
1772 /* Add the bit to the top sets */
1773 if (hwloc_bitmap_isset(result->cpuset, result->os_index))
1774 hwloc_bitmap_set(topology->levels[0][0]->cpuset, result->os_index);
1775 hwloc_bitmap_set(topology->levels[0][0]->complete_cpuset, result->os_index);
1776 }
1777 if (result != obj) {
1778 /* either failed to insert, or got merged, free the original object */
1779 hwloc_free_unlinked_object(obj);
1780 }
1781 return result;
1782 }
1783
1784 /* the default insertion routine warns in case of error.
1785 * it's used by most backends */
1786 void
hwloc_insert_object_by_parent(struct hwloc_topology * topology,hwloc_obj_t parent,hwloc_obj_t obj)1787 hwloc_insert_object_by_parent(struct hwloc_topology *topology, hwloc_obj_t parent, hwloc_obj_t obj)
1788 {
1789 hwloc_obj_t *current;
1790
1791 if (obj->type == HWLOC_OBJ_MISC) {
1792 /* Append to the end of the Misc list */
1793 for (current = &parent->misc_first_child; *current; current = &(*current)->next_sibling);
1794 } else if (hwloc__obj_type_is_io(obj->type)) {
1795 /* Append to the end of the I/O list */
1796 for (current = &parent->io_first_child; *current; current = &(*current)->next_sibling);
1797 } else if (hwloc__obj_type_is_memory(obj->type)) {
1798 /* Append to the end of the memory list */
1799 for (current = &parent->memory_first_child; *current; current = &(*current)->next_sibling);
1800 /* Add the bit to the top sets */
1801 if (obj->type == HWLOC_OBJ_NUMANODE) {
1802 if (hwloc_bitmap_isset(obj->nodeset, obj->os_index))
1803 hwloc_bitmap_set(topology->levels[0][0]->nodeset, obj->os_index);
1804 hwloc_bitmap_set(topology->levels[0][0]->complete_nodeset, obj->os_index);
1805 }
1806 } else {
1807 /* Append to the end of the list.
1808 * The caller takes care of inserting children in the right cpuset order, without intersection between them.
1809 * Duplicating doesn't need to check the order since the source topology is supposed to be OK already.
1810 * XML reorders if needed, and fails on intersecting siblings.
1811 * Other callers just insert random objects such as I/O or Misc, no cpuset issue there.
1812 */
1813 for (current = &parent->first_child; *current; current = &(*current)->next_sibling);
1814 /* Add the bit to the top sets */
1815 if (obj->type == HWLOC_OBJ_PU) {
1816 if (hwloc_bitmap_isset(obj->cpuset, obj->os_index))
1817 hwloc_bitmap_set(topology->levels[0][0]->cpuset, obj->os_index);
1818 hwloc_bitmap_set(topology->levels[0][0]->complete_cpuset, obj->os_index);
1819 }
1820 }
1821
1822 *current = obj;
1823 obj->parent = parent;
1824 obj->next_sibling = NULL;
1825 topology->modified = 1;
1826 }
1827
1828 hwloc_obj_t
hwloc_alloc_setup_object(hwloc_topology_t topology,hwloc_obj_type_t type,unsigned os_index)1829 hwloc_alloc_setup_object(hwloc_topology_t topology,
1830 hwloc_obj_type_t type, unsigned os_index)
1831 {
1832 struct hwloc_obj *obj = hwloc_tma_malloc(topology->tma, sizeof(*obj));
1833 if (!obj)
1834 return NULL;
1835 memset(obj, 0, sizeof(*obj));
1836 obj->type = type;
1837 obj->os_index = os_index;
1838 obj->gp_index = topology->next_gp_index++;
1839 obj->attr = hwloc_tma_malloc(topology->tma, sizeof(*obj->attr));
1840 if (!obj->attr) {
1841 assert(!topology->tma || !topology->tma->dontfree); /* this tma cannot fail to allocate */
1842 free(obj);
1843 return NULL;
1844 }
1845 memset(obj->attr, 0, sizeof(*obj->attr));
1846 /* do not allocate the cpuset here, let the caller do it */
1847 return obj;
1848 }
1849
1850 hwloc_obj_t
hwloc_topology_alloc_group_object(struct hwloc_topology * topology)1851 hwloc_topology_alloc_group_object(struct hwloc_topology *topology)
1852 {
1853 if (!topology->is_loaded) {
1854 /* this could actually work, see insert() below */
1855 errno = EINVAL;
1856 return NULL;
1857 }
1858 if (topology->adopted_shmem_addr) {
1859 errno = EPERM;
1860 return NULL;
1861 }
1862 return hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
1863 }
1864
1865 static void hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root);
1866 static void propagate_total_memory(hwloc_obj_t obj);
1867 static void hwloc_set_group_depth(hwloc_topology_t topology);
1868
1869 hwloc_obj_t
hwloc_topology_insert_group_object(struct hwloc_topology * topology,hwloc_obj_t obj)1870 hwloc_topology_insert_group_object(struct hwloc_topology *topology, hwloc_obj_t obj)
1871 {
1872 hwloc_obj_t res, root;
1873 int cmp;
1874
1875 if (!topology->is_loaded) {
1876 /* this could actually work, we would just need to disable connect_children/levels below */
1877 hwloc_free_unlinked_object(obj);
1878 errno = EINVAL;
1879 return NULL;
1880 }
1881 if (topology->adopted_shmem_addr) {
1882 errno = EPERM;
1883 return NULL;
1884 }
1885
1886 if (topology->type_filter[HWLOC_OBJ_GROUP] == HWLOC_TYPE_FILTER_KEEP_NONE) {
1887 hwloc_free_unlinked_object(obj);
1888 errno = EINVAL;
1889 return NULL;
1890 }
1891
1892 root = hwloc_get_root_obj(topology);
1893 if (obj->cpuset)
1894 hwloc_bitmap_and(obj->cpuset, obj->cpuset, root->cpuset);
1895 if (obj->complete_cpuset)
1896 hwloc_bitmap_and(obj->complete_cpuset, obj->complete_cpuset, root->complete_cpuset);
1897 if (obj->nodeset)
1898 hwloc_bitmap_and(obj->nodeset, obj->nodeset, root->nodeset);
1899 if (obj->complete_nodeset)
1900 hwloc_bitmap_and(obj->complete_nodeset, obj->complete_nodeset, root->complete_nodeset);
1901
1902 if ((!obj->cpuset || hwloc_bitmap_iszero(obj->cpuset))
1903 && (!obj->complete_cpuset || hwloc_bitmap_iszero(obj->complete_cpuset))) {
1904 /* we'll insert by cpuset, so build cpuset from the nodeset */
1905 hwloc_const_bitmap_t nodeset = obj->nodeset ? obj->nodeset : obj->complete_nodeset;
1906 hwloc_obj_t numa;
1907
1908 if ((!obj->nodeset || hwloc_bitmap_iszero(obj->nodeset))
1909 && (!obj->complete_nodeset || hwloc_bitmap_iszero(obj->complete_nodeset))) {
1910 hwloc_free_unlinked_object(obj);
1911 errno = EINVAL;
1912 return NULL;
1913 }
1914
1915 if (!obj->cpuset) {
1916 obj->cpuset = hwloc_bitmap_alloc();
1917 if (!obj->cpuset) {
1918 hwloc_free_unlinked_object(obj);
1919 return NULL;
1920 }
1921 }
1922
1923 numa = NULL;
1924 while ((numa = hwloc_get_next_obj_by_type(topology, HWLOC_OBJ_NUMANODE, numa)) != NULL)
1925 if (hwloc_bitmap_isset(nodeset, numa->os_index))
1926 hwloc_bitmap_or(obj->cpuset, obj->cpuset, numa->cpuset);
1927 }
1928 /* FIXME insert by nodeset to group NUMAs even if CPUless? */
1929
1930 cmp = hwloc_obj_cmp_sets(obj, root);
1931 if (cmp == HWLOC_OBJ_INCLUDED) {
1932 res = hwloc__insert_object_by_cpuset(topology, NULL, obj, NULL /* do not show errors on stdout */);
1933 } else {
1934 /* just merge root */
1935 res = root;
1936 }
1937
1938 if (!res)
1939 return NULL;
1940
1941 if (res != obj && res->type != HWLOC_OBJ_GROUP)
1942 /* merged, not into a Group, nothing to update */
1943 return res;
1944
1945 /* res == obj means that the object was inserted.
1946 * We need to reconnect levels, fill all its cpu/node sets,
1947 * compute its total memory, group depth, etc.
1948 *
1949 * res != obj usually means that our new group was merged into an
1950 * existing object, no need to recompute anything.
1951 * However, if merging with an existing group, depending on their kinds,
1952 * the contents of obj may overwrite the contents of the old group.
1953 * This requires reconnecting levels, filling sets, recomputing total memory, etc.
1954 */
1955
1956 /* properly inserted */
1957 hwloc_obj_add_children_sets(res);
1958 if (hwloc_topology_reconnect(topology, 0) < 0)
1959 return NULL;
1960
1961 hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]);
1962 hwloc_set_group_depth(topology);
1963
1964 #ifndef HWLOC_DEBUG
1965 if (getenv("HWLOC_DEBUG_CHECK"))
1966 #endif
1967 hwloc_topology_check(topology);
1968
1969 return res;
1970 }
1971
1972 hwloc_obj_t
hwloc_topology_insert_misc_object(struct hwloc_topology * topology,hwloc_obj_t parent,const char * name)1973 hwloc_topology_insert_misc_object(struct hwloc_topology *topology, hwloc_obj_t parent, const char *name)
1974 {
1975 hwloc_obj_t obj;
1976
1977 if (topology->type_filter[HWLOC_OBJ_MISC] == HWLOC_TYPE_FILTER_KEEP_NONE) {
1978 errno = EINVAL;
1979 return NULL;
1980 }
1981
1982 if (!topology->is_loaded) {
1983 errno = EINVAL;
1984 return NULL;
1985 }
1986 if (topology->adopted_shmem_addr) {
1987 errno = EPERM;
1988 return NULL;
1989 }
1990
1991 obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MISC, HWLOC_UNKNOWN_INDEX);
1992 if (name)
1993 obj->name = strdup(name);
1994
1995 hwloc_insert_object_by_parent(topology, parent, obj);
1996
1997 /* FIXME: only connect misc parent children and misc level,
1998 * but this API is likely not performance critical anyway
1999 */
2000 hwloc_topology_reconnect(topology, 0);
2001
2002 #ifndef HWLOC_DEBUG
2003 if (getenv("HWLOC_DEBUG_CHECK"))
2004 #endif
2005 hwloc_topology_check(topology);
2006
2007 return obj;
2008 }
2009
2010 /* assuming set is included in the topology complete_cpuset
2011 * and all objects have a proper complete_cpuset,
2012 * return the best one containing set.
2013 * if some object are equivalent (same complete_cpuset), return the highest one.
2014 */
2015 static hwloc_obj_t
hwloc_get_highest_obj_covering_complete_cpuset(hwloc_topology_t topology,hwloc_const_cpuset_t set)2016 hwloc_get_highest_obj_covering_complete_cpuset (hwloc_topology_t topology, hwloc_const_cpuset_t set)
2017 {
2018 hwloc_obj_t current = hwloc_get_root_obj(topology);
2019 hwloc_obj_t child;
2020
2021 if (hwloc_bitmap_isequal(set, current->complete_cpuset))
2022 /* root cpuset is exactly what we want, no need to look at children, we want the highest */
2023 return current;
2024
2025 recurse:
2026 /* find the right child */
2027 for_each_child(child, current) {
2028 if (hwloc_bitmap_isequal(set, child->complete_cpuset))
2029 /* child puset is exactly what we want, no need to look at children, we want the highest */
2030 return child;
2031 if (!hwloc_bitmap_iszero(child->complete_cpuset) && hwloc_bitmap_isincluded(set, child->complete_cpuset))
2032 break;
2033 }
2034
2035 if (child) {
2036 current = child;
2037 goto recurse;
2038 }
2039
2040 /* no better child */
2041 return current;
2042 }
2043
2044 hwloc_obj_t
hwloc_find_insert_io_parent_by_complete_cpuset(struct hwloc_topology * topology,hwloc_cpuset_t cpuset)2045 hwloc_find_insert_io_parent_by_complete_cpuset(struct hwloc_topology *topology, hwloc_cpuset_t cpuset)
2046 {
2047 hwloc_obj_t group_obj, largeparent, parent;
2048
2049 /* restrict to the existing complete cpuset to avoid errors later */
2050 hwloc_bitmap_and(cpuset, cpuset, hwloc_topology_get_complete_cpuset(topology));
2051 if (hwloc_bitmap_iszero(cpuset))
2052 /* remaining cpuset is empty, invalid */
2053 return NULL;
2054
2055 largeparent = hwloc_get_highest_obj_covering_complete_cpuset(topology, cpuset);
2056 if (hwloc_bitmap_isequal(largeparent->complete_cpuset, cpuset)
2057 || !hwloc_filter_check_keep_object_type(topology, HWLOC_OBJ_GROUP))
2058 /* Found a valid object (normal case) */
2059 return largeparent;
2060
2061 /* we need to insert an intermediate group */
2062 group_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_GROUP, HWLOC_UNKNOWN_INDEX);
2063 if (!group_obj)
2064 /* Failed to insert the exact Group, fallback to largeparent */
2065 return largeparent;
2066
2067 group_obj->complete_cpuset = hwloc_bitmap_dup(cpuset);
2068 hwloc_bitmap_and(cpuset, cpuset, hwloc_topology_get_topology_cpuset(topology));
2069 group_obj->cpuset = hwloc_bitmap_dup(cpuset);
2070 group_obj->attr->group.kind = HWLOC_GROUP_KIND_IO;
2071 parent = hwloc__insert_object_by_cpuset(topology, largeparent, group_obj, "topology:io_parent");
2072 if (!parent)
2073 /* Failed to insert the Group, maybe a conflicting cpuset */
2074 return largeparent;
2075
2076 /* Group couldn't get merged or we would have gotten the right largeparent earlier */
2077 assert(parent == group_obj);
2078
2079 /* Group inserted without being merged, everything OK, setup its sets */
2080 hwloc_obj_add_children_sets(group_obj);
2081
2082 return parent;
2083 }
2084
hwloc_memory_page_type_compare(const void * _a,const void * _b)2085 static int hwloc_memory_page_type_compare(const void *_a, const void *_b)
2086 {
2087 const struct hwloc_memory_page_type_s *a = _a;
2088 const struct hwloc_memory_page_type_s *b = _b;
2089 /* consider 0 as larger so that 0-size page_type go to the end */
2090 if (!b->size)
2091 return -1;
2092 /* don't cast a-b in int since those are ullongs */
2093 if (b->size == a->size)
2094 return 0;
2095 return a->size < b->size ? -1 : 1;
2096 }
2097
2098 /* Propagate memory counts */
2099 static void
propagate_total_memory(hwloc_obj_t obj)2100 propagate_total_memory(hwloc_obj_t obj)
2101 {
2102 hwloc_obj_t child;
2103 unsigned i;
2104
2105 /* reset total before counting local and children memory */
2106 obj->total_memory = 0;
2107
2108 /* Propagate memory up. */
2109 for_each_child(child, obj) {
2110 propagate_total_memory(child);
2111 obj->total_memory += child->total_memory;
2112 }
2113 for_each_memory_child(child, obj) {
2114 propagate_total_memory(child);
2115 obj->total_memory += child->total_memory;
2116 }
2117 /* No memory under I/O or Misc */
2118
2119 if (obj->type == HWLOC_OBJ_NUMANODE) {
2120 obj->total_memory += obj->attr->numanode.local_memory;
2121
2122 if (obj->attr->numanode.page_types_len) {
2123 /* By the way, sort the page_type array.
2124 * Cannot do it on insert since some backends (e.g. XML) add page_types after inserting the object.
2125 */
2126 qsort(obj->attr->numanode.page_types, obj->attr->numanode.page_types_len, sizeof(*obj->attr->numanode.page_types), hwloc_memory_page_type_compare);
2127 /* Ignore 0-size page_types, they are at the end */
2128 for(i=obj->attr->numanode.page_types_len; i>=1; i--)
2129 if (obj->attr->numanode.page_types[i-1].size)
2130 break;
2131 obj->attr->numanode.page_types_len = i;
2132 }
2133 }
2134 }
2135
2136 /* Now that root sets are ready, propagate them to children
2137 * by allocating missing sets and restricting existing ones.
2138 */
2139 static void
fixup_sets(hwloc_obj_t obj)2140 fixup_sets(hwloc_obj_t obj)
2141 {
2142 int in_memory_list;
2143 hwloc_obj_t child;
2144
2145 child = obj->first_child;
2146 in_memory_list = 0;
2147 /* iterate over normal children first, we'll come back for memory children later */
2148
2149 /* FIXME: if memory objects are inserted late, we should update their cpuset and complete_cpuset at insertion instead of here */
2150 iterate:
2151 while (child) {
2152 /* our cpuset must be included in our parent's one */
2153 hwloc_bitmap_and(child->cpuset, child->cpuset, obj->cpuset);
2154 hwloc_bitmap_and(child->nodeset, child->nodeset, obj->nodeset);
2155 /* our complete_cpuset must be included in our parent's one, but can be larger than our cpuset */
2156 if (child->complete_cpuset) {
2157 hwloc_bitmap_and(child->complete_cpuset, child->complete_cpuset, obj->complete_cpuset);
2158 } else {
2159 child->complete_cpuset = hwloc_bitmap_dup(child->cpuset);
2160 }
2161 if (child->complete_nodeset) {
2162 hwloc_bitmap_and(child->complete_nodeset, child->complete_nodeset, obj->complete_nodeset);
2163 } else {
2164 child->complete_nodeset = hwloc_bitmap_dup(child->nodeset);
2165 }
2166
2167 if (hwloc_obj_type_is_memory(child->type)) {
2168 /* update memory children cpusets in case some CPU-side parent was removed */
2169 hwloc_bitmap_copy(child->cpuset, obj->cpuset);
2170 hwloc_bitmap_copy(child->complete_cpuset, obj->complete_cpuset);
2171 }
2172
2173 fixup_sets(child);
2174 child = child->next_sibling;
2175 }
2176
2177 /* switch to memory children list if any */
2178 if (!in_memory_list && obj->memory_first_child) {
2179 child = obj->memory_first_child;
2180 in_memory_list = 1;
2181 goto iterate;
2182 }
2183
2184 /* No sets in I/O or Misc */
2185 }
2186
2187 /* Setup object cpusets/nodesets by OR'ing its children. */
2188 int
hwloc_obj_add_other_obj_sets(hwloc_obj_t dst,hwloc_obj_t src)2189 hwloc_obj_add_other_obj_sets(hwloc_obj_t dst, hwloc_obj_t src)
2190 {
2191 #define ADD_OTHER_OBJ_SET(_dst, _src, _set) \
2192 if ((_src)->_set) { \
2193 if (!(_dst)->_set) \
2194 (_dst)->_set = hwloc_bitmap_alloc(); \
2195 hwloc_bitmap_or((_dst)->_set, (_dst)->_set, (_src)->_set); \
2196 }
2197 ADD_OTHER_OBJ_SET(dst, src, cpuset);
2198 ADD_OTHER_OBJ_SET(dst, src, complete_cpuset);
2199 ADD_OTHER_OBJ_SET(dst, src, nodeset);
2200 ADD_OTHER_OBJ_SET(dst, src, complete_nodeset);
2201 return 0;
2202 }
2203
2204 int
hwloc_obj_add_children_sets(hwloc_obj_t obj)2205 hwloc_obj_add_children_sets(hwloc_obj_t obj)
2206 {
2207 hwloc_obj_t child;
2208 for_each_child(child, obj) {
2209 hwloc_obj_add_other_obj_sets(obj, child);
2210 }
2211 /* No need to look at Misc children, they contain no PU. */
2212 return 0;
2213 }
2214
2215 /* CPU objects are inserted by cpusets, we know their cpusets are properly included.
2216 * We just need fixup_sets() to make sure they aren't too wide.
2217 *
2218 * Within each memory hierarchy, nodeset are consistent as well.
2219 * However they must be propagated to their CPU-side parents.
2220 *
2221 * A memory object nodeset consists of NUMA nodes below it.
2222 * A normal object nodeset consists in NUMA nodes attached to any
2223 * of its children or parents.
2224 */
2225 static void
propagate_nodeset(hwloc_obj_t obj)2226 propagate_nodeset(hwloc_obj_t obj)
2227 {
2228 hwloc_obj_t child;
2229
2230 /* Start our nodeset from the parent one.
2231 * It was emptied at root, and it's being filled with local nodes
2232 * in that branch of the tree as we recurse down.
2233 */
2234 if (!obj->nodeset)
2235 obj->nodeset = hwloc_bitmap_alloc();
2236 if (obj->parent)
2237 hwloc_bitmap_copy(obj->nodeset, obj->parent->nodeset);
2238 else
2239 hwloc_bitmap_zero(obj->nodeset);
2240
2241 /* Don't clear complete_nodeset, just make sure it contains nodeset.
2242 * We cannot clear the complete_nodeset at root and rebuild it down because
2243 * some bits may correspond to offline/disallowed NUMA nodes missing in the topology.
2244 */
2245 if (!obj->complete_nodeset)
2246 obj->complete_nodeset = hwloc_bitmap_dup(obj->nodeset);
2247 else
2248 hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, obj->nodeset);
2249
2250 /* now add our local nodeset */
2251 for_each_memory_child(child, obj) {
2252 /* add memory children nodesets to ours */
2253 hwloc_bitmap_or(obj->nodeset, obj->nodeset, child->nodeset);
2254 hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, child->complete_nodeset);
2255 /* no need to recurse because hwloc__attach_memory_object()
2256 * makes sure nodesets are consistent within each memory hierarchy.
2257 */
2258 }
2259
2260 /* Propagate our nodeset to CPU children. */
2261 for_each_child(child, obj) {
2262 propagate_nodeset(child);
2263 }
2264
2265 /* Propagate CPU children specific nodesets back to us.
2266 *
2267 * We cannot merge these two loops because we don't want to first child
2268 * nodeset to be propagated back to us and then down to the second child.
2269 * Each child may have its own local nodeset,
2270 * each of them is propagated to us, but not to other children.
2271 */
2272 for_each_child(child, obj) {
2273 hwloc_bitmap_or(obj->nodeset, obj->nodeset, child->nodeset);
2274 hwloc_bitmap_or(obj->complete_nodeset, obj->complete_nodeset, child->complete_nodeset);
2275 }
2276
2277 /* No nodeset under I/O or Misc */
2278
2279 }
2280
2281 static void
remove_unused_sets(hwloc_topology_t topology,hwloc_obj_t obj)2282 remove_unused_sets(hwloc_topology_t topology, hwloc_obj_t obj)
2283 {
2284 hwloc_obj_t child;
2285
2286 hwloc_bitmap_and(obj->cpuset, obj->cpuset, topology->allowed_cpuset);
2287 hwloc_bitmap_and(obj->nodeset, obj->nodeset, topology->allowed_nodeset);
2288
2289 for_each_child(child, obj)
2290 remove_unused_sets(topology, child);
2291 for_each_memory_child(child, obj)
2292 remove_unused_sets(topology, child);
2293 /* No cpuset under I/O or Misc */
2294 }
2295
2296 static void
hwloc__filter_bridges(hwloc_topology_t topology,hwloc_obj_t root,unsigned depth)2297 hwloc__filter_bridges(hwloc_topology_t topology, hwloc_obj_t root, unsigned depth)
2298 {
2299 hwloc_obj_t child, *pchild;
2300
2301 /* filter I/O children and recurse */
2302 for_each_io_child_safe(child, root, pchild) {
2303 enum hwloc_type_filter_e filter = topology->type_filter[child->type];
2304
2305 /* recurse into grand-children */
2306 hwloc__filter_bridges(topology, child, depth+1);
2307
2308 child->attr->bridge.depth = depth;
2309
2310 if (child->type == HWLOC_OBJ_BRIDGE
2311 && filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT
2312 && !child->io_first_child) {
2313 unlink_and_free_single_object(pchild);
2314 topology->modified = 1;
2315 }
2316 }
2317 }
2318
2319 static void
hwloc_filter_bridges(hwloc_topology_t topology,hwloc_obj_t parent)2320 hwloc_filter_bridges(hwloc_topology_t topology, hwloc_obj_t parent)
2321 {
2322 hwloc_obj_t child = parent->first_child;
2323 while (child) {
2324 hwloc_filter_bridges(topology, child);
2325 child = child->next_sibling;
2326 }
2327
2328 hwloc__filter_bridges(topology, parent, 0);
2329 }
2330
2331 void
hwloc__reorder_children(hwloc_obj_t parent)2332 hwloc__reorder_children(hwloc_obj_t parent)
2333 {
2334 /* move the children list on the side */
2335 hwloc_obj_t *prev, child, children = parent->first_child;
2336 parent->first_child = NULL;
2337 while (children) {
2338 /* dequeue child */
2339 child = children;
2340 children = child->next_sibling;
2341 /* find where to enqueue it */
2342 prev = &parent->first_child;
2343 while (*prev && hwloc__object_cpusets_compare_first(child, *prev) > 0)
2344 prev = &((*prev)->next_sibling);
2345 /* enqueue */
2346 child->next_sibling = *prev;
2347 *prev = child;
2348 }
2349 /* No ordering to enforce for Misc or I/O children. */
2350 }
2351
2352 /* Remove all normal children whose cpuset is empty,
2353 * and memory children whose nodeset is empty.
2354 * Also don't remove objects that have I/O children, but ignore Misc.
2355 */
2356 static void
remove_empty(hwloc_topology_t topology,hwloc_obj_t * pobj)2357 remove_empty(hwloc_topology_t topology, hwloc_obj_t *pobj)
2358 {
2359 hwloc_obj_t obj = *pobj, child, *pchild;
2360
2361 for_each_child_safe(child, obj, pchild)
2362 remove_empty(topology, pchild);
2363 for_each_memory_child_safe(child, obj, pchild)
2364 remove_empty(topology, pchild);
2365 /* No cpuset under I/O or Misc */
2366
2367 if (obj->first_child /* only remove if all children were removed above, so that we don't remove parents of NUMAnode */
2368 || obj->memory_first_child /* only remove if no memory attached there */
2369 || obj->io_first_child /* only remove if no I/O is attached there */)
2370 /* ignore Misc */
2371 return;
2372
2373 if (hwloc__obj_type_is_normal(obj->type)) {
2374 if (!hwloc_bitmap_iszero(obj->cpuset))
2375 return;
2376 } else {
2377 assert(hwloc__obj_type_is_memory(obj->type));
2378 if (!hwloc_bitmap_iszero(obj->nodeset))
2379 return;
2380 }
2381
2382 hwloc_debug("%s", "\nRemoving empty object ");
2383 hwloc_debug_print_object(0, obj);
2384 unlink_and_free_single_object(pobj);
2385 topology->modified = 1;
2386 }
2387
2388 /* reset type depth before modifying levels (either reconnecting or filtering/keep_structure) */
2389 static void
hwloc_reset_normal_type_depths(hwloc_topology_t topology)2390 hwloc_reset_normal_type_depths(hwloc_topology_t topology)
2391 {
2392 unsigned i;
2393 for (i=HWLOC_OBJ_TYPE_MIN; i<=HWLOC_OBJ_GROUP; i++)
2394 topology->type_depth[i] = HWLOC_TYPE_DEPTH_UNKNOWN;
2395 /* type contiguity is asserted in topology_check() */
2396 topology->type_depth[HWLOC_OBJ_DIE] = HWLOC_TYPE_DEPTH_UNKNOWN;
2397 }
2398
2399 static int
hwloc_dont_merge_group_level(hwloc_topology_t topology,unsigned i)2400 hwloc_dont_merge_group_level(hwloc_topology_t topology, unsigned i)
2401 {
2402 unsigned j;
2403
2404 /* Don't merge some groups in that level? */
2405 for(j=0; j<topology->level_nbobjects[i]; j++)
2406 if (topology->levels[i][j]->attr->group.dont_merge)
2407 return 1;
2408
2409 return 0;
2410 }
2411
2412 /* compare i-th and i-1-th levels structure */
2413 static int
hwloc_compare_levels_structure(hwloc_topology_t topology,unsigned i)2414 hwloc_compare_levels_structure(hwloc_topology_t topology, unsigned i)
2415 {
2416 int checkmemory = (topology->levels[i][0]->type == HWLOC_OBJ_PU);
2417 unsigned j;
2418
2419 if (topology->level_nbobjects[i-1] != topology->level_nbobjects[i])
2420 return -1;
2421
2422 for(j=0; j<topology->level_nbobjects[i]; j++) {
2423 if (topology->levels[i-1][j] != topology->levels[i][j]->parent)
2424 return -1;
2425 if (topology->levels[i-1][j]->arity != 1)
2426 return -1;
2427 if (checkmemory && topology->levels[i-1][j]->memory_arity)
2428 /* don't merge PUs if there's memory above */
2429 return -1;
2430 }
2431 /* same number of objects with arity 1 above, no problem */
2432 return 0;
2433 }
2434
2435 /* return > 0 if any level was removed, which means reconnect is needed */
2436 static void
hwloc_filter_levels_keep_structure(hwloc_topology_t topology)2437 hwloc_filter_levels_keep_structure(hwloc_topology_t topology)
2438 {
2439 unsigned i, j;
2440 int res = 0;
2441
2442 /* start from the bottom since we'll remove intermediate levels */
2443 for(i=topology->nb_levels-1; i>0; i--) {
2444 int replacechild = 0, replaceparent = 0;
2445 hwloc_obj_t obj1 = topology->levels[i-1][0];
2446 hwloc_obj_t obj2 = topology->levels[i][0];
2447 hwloc_obj_type_t type1 = obj1->type;
2448 hwloc_obj_type_t type2 = obj2->type;
2449
2450 /* Check whether parents and/or children can be replaced */
2451 if (topology->type_filter[type1] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) {
2452 /* Parents can be ignored in favor of children. */
2453 replaceparent = 1;
2454 if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i-1))
2455 replaceparent = 0;
2456 }
2457 if (topology->type_filter[type2] == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) {
2458 /* Children can be ignored in favor of parents. */
2459 replacechild = 1;
2460 if (type1 == HWLOC_OBJ_GROUP && hwloc_dont_merge_group_level(topology, i))
2461 replacechild = 0;
2462 }
2463 if (!replacechild && !replaceparent)
2464 /* no ignoring */
2465 continue;
2466 /* Decide which one to actually replace */
2467 if (replaceparent && replacechild) {
2468 /* If both may be replaced, look at obj_type_priority */
2469 if (obj_type_priority[type1] >= obj_type_priority[type2])
2470 replaceparent = 0;
2471 else
2472 replacechild = 0;
2473 }
2474 /* Are these levels actually identical? */
2475 if (hwloc_compare_levels_structure(topology, i) < 0)
2476 continue;
2477 hwloc_debug("may merge levels #%u=%s and #%u=%s\n",
2478 i-1, hwloc_obj_type_string(type1), i, hwloc_obj_type_string(type2));
2479
2480 /* OK, remove intermediate objects from the tree. */
2481 for(j=0; j<topology->level_nbobjects[i]; j++) {
2482 hwloc_obj_t parent = topology->levels[i-1][j];
2483 hwloc_obj_t child = topology->levels[i][j];
2484 unsigned k;
2485 if (replacechild) {
2486 /* move child's children to parent */
2487 parent->first_child = child->first_child;
2488 parent->last_child = child->last_child;
2489 parent->arity = child->arity;
2490 free(parent->children);
2491 parent->children = child->children;
2492 child->children = NULL;
2493 /* update children parent */
2494 for(k=0; k<parent->arity; k++)
2495 parent->children[k]->parent = parent;
2496 /* append child memory/io/misc children to parent */
2497 if (child->memory_first_child) {
2498 append_siblings_list(&parent->memory_first_child, child->memory_first_child, parent);
2499 parent->memory_arity += child->memory_arity;
2500 }
2501 if (child->io_first_child) {
2502 append_siblings_list(&parent->io_first_child, child->io_first_child, parent);
2503 parent->io_arity += child->io_arity;
2504 }
2505 if (child->misc_first_child) {
2506 append_siblings_list(&parent->misc_first_child, child->misc_first_child, parent);
2507 parent->misc_arity += child->misc_arity;
2508 }
2509 hwloc_free_unlinked_object(child);
2510 } else {
2511 /* replace parent with child in grand-parent */
2512 if (parent->parent) {
2513 parent->parent->children[parent->sibling_rank] = child;
2514 child->sibling_rank = parent->sibling_rank;
2515 if (!parent->sibling_rank) {
2516 parent->parent->first_child = child;
2517 /* child->prev_sibling was already NULL, child was single */
2518 } else {
2519 child->prev_sibling = parent->parent->children[parent->sibling_rank-1];
2520 child->prev_sibling->next_sibling = child;
2521 }
2522 if (parent->sibling_rank == parent->parent->arity-1) {
2523 parent->parent->last_child = child;
2524 /* child->next_sibling was already NULL, child was single */
2525 } else {
2526 child->next_sibling = parent->parent->children[parent->sibling_rank+1];
2527 child->next_sibling->prev_sibling = child;
2528 }
2529 /* update child parent */
2530 child->parent = parent->parent;
2531 } else {
2532 /* make child the new root */
2533 topology->levels[0][0] = child;
2534 child->parent = NULL;
2535 }
2536 /* prepend parent memory/io/misc children to child */
2537 if (parent->memory_first_child) {
2538 prepend_siblings_list(&child->memory_first_child, parent->memory_first_child, child);
2539 child->memory_arity += parent->memory_arity;
2540 }
2541 if (parent->io_first_child) {
2542 prepend_siblings_list(&child->io_first_child, parent->io_first_child, child);
2543 child->io_arity += parent->io_arity;
2544 }
2545 if (parent->misc_first_child) {
2546 prepend_siblings_list(&child->misc_first_child, parent->misc_first_child, child);
2547 child->misc_arity += parent->misc_arity;
2548 }
2549 hwloc_free_unlinked_object(parent);
2550 /* prev/next_sibling will be updated below in another loop */
2551 }
2552 }
2553 if (replaceparent && i>1) {
2554 /* Update sibling list within modified parent->parent arrays */
2555 for(j=0; j<topology->level_nbobjects[i]; j++) {
2556 hwloc_obj_t child = topology->levels[i][j];
2557 unsigned rank = child->sibling_rank;
2558 child->prev_sibling = rank > 0 ? child->parent->children[rank-1] : NULL;
2559 child->next_sibling = rank < child->parent->arity-1 ? child->parent->children[rank+1] : NULL;
2560 }
2561 }
2562
2563 /* Update levels so that the next reconnect isn't confused */
2564 if (replaceparent) {
2565 /* Removing level i-1, so move levels [i..nb_levels-1] to [i-1..] */
2566 free(topology->levels[i-1]);
2567 memmove(&topology->levels[i-1],
2568 &topology->levels[i],
2569 (topology->nb_levels-i)*sizeof(topology->levels[i]));
2570 memmove(&topology->level_nbobjects[i-1],
2571 &topology->level_nbobjects[i],
2572 (topology->nb_levels-i)*sizeof(topology->level_nbobjects[i]));
2573 hwloc_debug("removed parent level %s at depth %u\n",
2574 hwloc_obj_type_string(type1), i-1);
2575 } else {
2576 /* Removing level i, so move levels [i+1..nb_levels-1] and later to [i..] */
2577 free(topology->levels[i]);
2578 memmove(&topology->levels[i],
2579 &topology->levels[i+1],
2580 (topology->nb_levels-1-i)*sizeof(topology->levels[i]));
2581 memmove(&topology->level_nbobjects[i],
2582 &topology->level_nbobjects[i+1],
2583 (topology->nb_levels-1-i)*sizeof(topology->level_nbobjects[i]));
2584 hwloc_debug("removed child level %s at depth %u\n",
2585 hwloc_obj_type_string(type2), i);
2586 }
2587 topology->level_nbobjects[topology->nb_levels-1] = 0;
2588 topology->levels[topology->nb_levels-1] = NULL;
2589 topology->nb_levels--;
2590
2591 res++;
2592 }
2593
2594 if (res > 0) {
2595 /* Update object and type depths if some levels were removed */
2596 hwloc_reset_normal_type_depths(topology);
2597 for(i=0; i<topology->nb_levels; i++) {
2598 hwloc_obj_type_t type = topology->levels[i][0]->type;
2599 for(j=0; j<topology->level_nbobjects[i]; j++)
2600 topology->levels[i][j]->depth = (int)i;
2601 if (topology->type_depth[type] == HWLOC_TYPE_DEPTH_UNKNOWN)
2602 topology->type_depth[type] = (int)i;
2603 else
2604 topology->type_depth[type] = HWLOC_TYPE_DEPTH_MULTIPLE;
2605 }
2606 }
2607 }
2608
2609 static void
hwloc_propagate_symmetric_subtree(hwloc_topology_t topology,hwloc_obj_t root)2610 hwloc_propagate_symmetric_subtree(hwloc_topology_t topology, hwloc_obj_t root)
2611 {
2612 hwloc_obj_t child;
2613 unsigned arity = root->arity;
2614 hwloc_obj_t *array;
2615 int ok;
2616
2617 /* assume we're not symmetric by default */
2618 root->symmetric_subtree = 0;
2619
2620 /* if no child, we are symmetric */
2621 if (!arity)
2622 goto good;
2623
2624 /* FIXME ignore memory just like I/O and Misc? */
2625
2626 /* look at normal children only, I/O and Misc are ignored.
2627 * return if any child is not symmetric.
2628 */
2629 ok = 1;
2630 for_each_child(child, root) {
2631 hwloc_propagate_symmetric_subtree(topology, child);
2632 if (!child->symmetric_subtree)
2633 ok = 0;
2634 }
2635 if (!ok)
2636 return;
2637 /* Misc and I/O children do not care about symmetric_subtree */
2638
2639 /* if single child is symmetric, we're good */
2640 if (arity == 1)
2641 goto good;
2642
2643 /* now check that children subtrees are identical.
2644 * just walk down the first child in each tree and compare their depth and arities
2645 */
2646 array = malloc(arity * sizeof(*array));
2647 if (!array)
2648 return;
2649 memcpy(array, root->children, arity * sizeof(*array));
2650 while (1) {
2651 unsigned i;
2652 /* check current level arities and depth */
2653 for(i=1; i<arity; i++)
2654 if (array[i]->depth != array[0]->depth
2655 || array[i]->arity != array[0]->arity) {
2656 free(array);
2657 return;
2658 }
2659 if (!array[0]->arity)
2660 /* no more children level, we're ok */
2661 break;
2662 /* look at first child of each element now */
2663 for(i=0; i<arity; i++)
2664 array[i] = array[i]->first_child;
2665 }
2666 free(array);
2667
2668 /* everything went fine, we're symmetric */
2669 good:
2670 root->symmetric_subtree = 1;
2671 }
2672
hwloc_set_group_depth(hwloc_topology_t topology)2673 static void hwloc_set_group_depth(hwloc_topology_t topology)
2674 {
2675 unsigned groupdepth = 0;
2676 unsigned i, j;
2677 for(i=0; i<topology->nb_levels; i++)
2678 if (topology->levels[i][0]->type == HWLOC_OBJ_GROUP) {
2679 for (j = 0; j < topology->level_nbobjects[i]; j++)
2680 topology->levels[i][j]->attr->group.depth = groupdepth;
2681 groupdepth++;
2682 }
2683 }
2684
2685 /*
2686 * Initialize handy pointers in the whole topology.
2687 * The topology only had first_child and next_sibling pointers.
2688 * When this funtions return, all parent/children pointers are initialized.
2689 * The remaining fields (levels, cousins, logical_index, depth, ...) will
2690 * be setup later in hwloc_connect_levels().
2691 *
2692 * Can be called several times, so may have to update the array.
2693 */
2694 static void
hwloc_connect_children(hwloc_obj_t parent)2695 hwloc_connect_children(hwloc_obj_t parent)
2696 {
2697 unsigned n, oldn = parent->arity;
2698 hwloc_obj_t child, prev_child;
2699 int ok;
2700
2701 /* Main children list */
2702
2703 ok = 1;
2704 prev_child = NULL;
2705 for (n = 0, child = parent->first_child;
2706 child;
2707 n++, prev_child = child, child = child->next_sibling) {
2708 child->sibling_rank = n;
2709 child->prev_sibling = prev_child;
2710 /* already OK in the array? */
2711 if (n >= oldn || parent->children[n] != child)
2712 ok = 0;
2713 /* recurse */
2714 hwloc_connect_children(child);
2715 }
2716 parent->last_child = prev_child;
2717 parent->arity = n;
2718 if (!n) {
2719 /* no need for an array anymore */
2720 free(parent->children);
2721 parent->children = NULL;
2722 goto memory;
2723 }
2724 if (ok)
2725 /* array is already OK (even if too large) */
2726 goto memory;
2727
2728 /* alloc a larger array if needed */
2729 if (oldn < n) {
2730 free(parent->children);
2731 parent->children = malloc(n * sizeof(*parent->children));
2732 }
2733 /* refill */
2734 for (n = 0, child = parent->first_child;
2735 child;
2736 n++, child = child->next_sibling) {
2737 parent->children[n] = child;
2738 }
2739
2740
2741
2742 memory:
2743 /* Memory children list */
2744
2745 prev_child = NULL;
2746 for (n = 0, child = parent->memory_first_child;
2747 child;
2748 n++, prev_child = child, child = child->next_sibling) {
2749 child->parent = parent;
2750 child->sibling_rank = n;
2751 child->prev_sibling = prev_child;
2752 hwloc_connect_children(child);
2753 }
2754 parent->memory_arity = n;
2755
2756 /* I/O children list */
2757
2758 prev_child = NULL;
2759 for (n = 0, child = parent->io_first_child;
2760 child;
2761 n++, prev_child = child, child = child->next_sibling) {
2762 child->parent = parent;
2763 child->sibling_rank = n;
2764 child->prev_sibling = prev_child;
2765 hwloc_connect_children(child);
2766 }
2767 parent->io_arity = n;
2768
2769 /* Misc children list */
2770
2771 prev_child = NULL;
2772 for (n = 0, child = parent->misc_first_child;
2773 child;
2774 n++, prev_child = child, child = child->next_sibling) {
2775 child->parent = parent;
2776 child->sibling_rank = n;
2777 child->prev_sibling = prev_child;
2778 hwloc_connect_children(child);
2779 }
2780 parent->misc_arity = n;
2781 }
2782
2783 /*
2784 * Check whether there is an object strictly below ROOT that has the same type as OBJ
2785 */
2786 static int
find_same_type(hwloc_obj_t root,hwloc_obj_t obj)2787 find_same_type(hwloc_obj_t root, hwloc_obj_t obj)
2788 {
2789 hwloc_obj_t child;
2790
2791 for_each_child (child, root) {
2792 if (hwloc_type_cmp(child, obj) == HWLOC_OBJ_EQUAL)
2793 return 1;
2794 if (find_same_type(child, obj))
2795 return 1;
2796 }
2797
2798 return 0;
2799 }
2800
2801 static int
hwloc_build_level_from_list(struct hwloc_special_level_s * slevel)2802 hwloc_build_level_from_list(struct hwloc_special_level_s *slevel)
2803 {
2804 unsigned i, nb;
2805 struct hwloc_obj * obj;
2806
2807 /* count */
2808 obj = slevel->first;
2809 i = 0;
2810 while (obj) {
2811 i++;
2812 obj = obj->next_cousin;
2813 }
2814 nb = i;
2815
2816 if (nb) {
2817 /* allocate and fill level */
2818 slevel->objs = malloc(nb * sizeof(struct hwloc_obj *));
2819 if (!slevel->objs)
2820 return -1;
2821
2822 obj = slevel->first;
2823 i = 0;
2824 while (obj) {
2825 obj->logical_index = i;
2826 slevel->objs[i] = obj;
2827 i++;
2828 obj = obj->next_cousin;
2829 }
2830 }
2831
2832 slevel->nbobjs = nb;
2833 return 0;
2834 }
2835
2836 static void
hwloc_append_special_object(struct hwloc_special_level_s * level,hwloc_obj_t obj)2837 hwloc_append_special_object(struct hwloc_special_level_s *level, hwloc_obj_t obj)
2838 {
2839 if (level->first) {
2840 obj->prev_cousin = level->last;
2841 obj->prev_cousin->next_cousin = obj;
2842 level->last = obj;
2843 } else {
2844 obj->prev_cousin = NULL;
2845 level->first = level->last = obj;
2846 }
2847 }
2848
2849 /* Append special objects to their lists */
2850 static void
hwloc_list_special_objects(hwloc_topology_t topology,hwloc_obj_t obj)2851 hwloc_list_special_objects(hwloc_topology_t topology, hwloc_obj_t obj)
2852 {
2853 hwloc_obj_t child;
2854
2855 if (obj->type == HWLOC_OBJ_NUMANODE) {
2856 obj->next_cousin = NULL;
2857 obj->depth = HWLOC_TYPE_DEPTH_NUMANODE;
2858 /* Insert the main NUMA node list */
2859 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_NUMANODE], obj);
2860
2861 /* Recurse, NUMA nodes only have Misc children */
2862 for_each_misc_child(child, obj)
2863 hwloc_list_special_objects(topology, child);
2864
2865 } else if (obj->type == HWLOC_OBJ_MEMCACHE) {
2866 obj->next_cousin = NULL;
2867 obj->depth = HWLOC_TYPE_DEPTH_MEMCACHE;
2868 /* Insert the main MemCache list */
2869 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_MEMCACHE], obj);
2870
2871 /* Recurse, MemCaches have NUMA nodes or Misc children */
2872 for_each_memory_child(child, obj)
2873 hwloc_list_special_objects(topology, child);
2874 for_each_misc_child(child, obj)
2875 hwloc_list_special_objects(topology, child);
2876
2877 } else if (obj->type == HWLOC_OBJ_MISC) {
2878 obj->next_cousin = NULL;
2879 obj->depth = HWLOC_TYPE_DEPTH_MISC;
2880 /* Insert the main Misc list */
2881 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_MISC], obj);
2882 /* Recurse, Misc only have Misc children */
2883 for_each_misc_child(child, obj)
2884 hwloc_list_special_objects(topology, child);
2885
2886 } else if (hwloc__obj_type_is_io(obj->type)) {
2887 obj->next_cousin = NULL;
2888
2889 if (obj->type == HWLOC_OBJ_BRIDGE) {
2890 obj->depth = HWLOC_TYPE_DEPTH_BRIDGE;
2891 /* Insert in the main bridge list */
2892 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_BRIDGE], obj);
2893
2894 } else if (obj->type == HWLOC_OBJ_PCI_DEVICE) {
2895 obj->depth = HWLOC_TYPE_DEPTH_PCI_DEVICE;
2896 /* Insert in the main pcidev list */
2897 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_PCIDEV], obj);
2898
2899 } else if (obj->type == HWLOC_OBJ_OS_DEVICE) {
2900 obj->depth = HWLOC_TYPE_DEPTH_OS_DEVICE;
2901 /* Insert in the main osdev list */
2902 hwloc_append_special_object(&topology->slevels[HWLOC_SLEVEL_OSDEV], obj);
2903 }
2904
2905 /* Recurse, I/O only have I/O and Misc children */
2906 for_each_io_child(child, obj)
2907 hwloc_list_special_objects(topology, child);
2908 for_each_misc_child(child, obj)
2909 hwloc_list_special_objects(topology, child);
2910
2911 } else {
2912 /* Recurse */
2913 for_each_child(child, obj)
2914 hwloc_list_special_objects(topology, child);
2915 for_each_memory_child(child, obj)
2916 hwloc_list_special_objects(topology, child);
2917 for_each_io_child(child, obj)
2918 hwloc_list_special_objects(topology, child);
2919 for_each_misc_child(child, obj)
2920 hwloc_list_special_objects(topology, child);
2921 }
2922 }
2923
2924 /* Build I/O levels */
2925 static int
hwloc_connect_io_misc_levels(hwloc_topology_t topology)2926 hwloc_connect_io_misc_levels(hwloc_topology_t topology)
2927 {
2928 unsigned i;
2929
2930 for(i=0; i<HWLOC_NR_SLEVELS; i++)
2931 free(topology->slevels[i].objs);
2932 memset(&topology->slevels, 0, sizeof(topology->slevels));
2933
2934 hwloc_list_special_objects(topology, topology->levels[0][0]);
2935
2936 for(i=0; i<HWLOC_NR_SLEVELS; i++) {
2937 if (hwloc_build_level_from_list(&topology->slevels[i]) < 0)
2938 return -1;
2939 }
2940
2941 return 0;
2942 }
2943
2944 /*
2945 * Do the remaining work that hwloc_connect_children() did not do earlier.
2946 * Requires object arity and children list to be properly initialized (by hwloc_connect_children()).
2947 */
2948 static int
hwloc_connect_levels(hwloc_topology_t topology)2949 hwloc_connect_levels(hwloc_topology_t topology)
2950 {
2951 unsigned l, i=0;
2952 hwloc_obj_t *objs, *taken_objs, *new_objs, top_obj, root;
2953 unsigned n_objs, n_taken_objs, n_new_objs;
2954
2955 /* reset non-root levels (root was initialized during init and will not change here) */
2956 for(l=1; l<topology->nb_levels; l++)
2957 free(topology->levels[l]);
2958 memset(topology->levels+1, 0, (topology->nb_levels-1)*sizeof(*topology->levels));
2959 memset(topology->level_nbobjects+1, 0, (topology->nb_levels-1)*sizeof(*topology->level_nbobjects));
2960 topology->nb_levels = 1;
2961
2962 /* initialize all non-IO/non-Misc depths to unknown */
2963 hwloc_reset_normal_type_depths(topology);
2964
2965 /* initialize root type depth */
2966 root = topology->levels[0][0];
2967 root->depth = 0;
2968 topology->type_depth[root->type] = 0;
2969 /* root level */
2970 root->logical_index = 0;
2971 root->prev_cousin = NULL;
2972 root->next_cousin = NULL;
2973 /* root as a child of nothing */
2974 root->parent = NULL;
2975 root->sibling_rank = 0;
2976 root->prev_sibling = NULL;
2977 root->next_sibling = NULL;
2978
2979 /* Start with children of the whole system. */
2980 n_objs = topology->levels[0][0]->arity;
2981 objs = malloc(n_objs * sizeof(objs[0]));
2982 if (!objs) {
2983 errno = ENOMEM;
2984 return -1;
2985 }
2986 memcpy(objs, topology->levels[0][0]->children, n_objs*sizeof(objs[0]));
2987
2988 /* Keep building levels while there are objects left in OBJS. */
2989 while (n_objs) {
2990 /* At this point, the objs array contains only objects that may go into levels */
2991
2992 /* First find which type of object is the topmost.
2993 * Don't use PU if there are other types since we want to keep PU at the bottom.
2994 */
2995
2996 /* Look for the first non-PU object, and use the first PU if we really find nothing else */
2997 for (i = 0; i < n_objs; i++)
2998 if (objs[i]->type != HWLOC_OBJ_PU)
2999 break;
3000 top_obj = i == n_objs ? objs[0] : objs[i];
3001
3002 /* See if this is actually the topmost object */
3003 for (i = 0; i < n_objs; i++) {
3004 if (hwloc_type_cmp(top_obj, objs[i]) != HWLOC_OBJ_EQUAL) {
3005 if (find_same_type(objs[i], top_obj)) {
3006 /* OBJS[i] is strictly above an object of the same type as TOP_OBJ, so it
3007 * is above TOP_OBJ. */
3008 top_obj = objs[i];
3009 }
3010 }
3011 }
3012
3013 /* Now peek all objects of the same type, build a level with that and
3014 * replace them with their children. */
3015
3016 /* allocate enough to take all current objects and an ending NULL */
3017 taken_objs = malloc((n_objs+1) * sizeof(taken_objs[0]));
3018 if (!taken_objs) {
3019 free(objs);
3020 errno = ENOMEM;
3021 return -1;
3022 }
3023
3024 /* allocate enough to keep all current objects or their children */
3025 n_new_objs = 0;
3026 for (i = 0; i < n_objs; i++) {
3027 if (objs[i]->arity)
3028 n_new_objs += objs[i]->arity;
3029 else
3030 n_new_objs++;
3031 }
3032 new_objs = malloc(n_new_objs * sizeof(new_objs[0]));
3033 if (!new_objs) {
3034 free(objs);
3035 free(taken_objs);
3036 errno = ENOMEM;
3037 return -1;
3038 }
3039
3040 /* now actually take these objects */
3041 n_new_objs = 0;
3042 n_taken_objs = 0;
3043 for (i = 0; i < n_objs; i++)
3044 if (hwloc_type_cmp(top_obj, objs[i]) == HWLOC_OBJ_EQUAL) {
3045 /* Take it, add main children. */
3046 taken_objs[n_taken_objs++] = objs[i];
3047 if (objs[i]->arity)
3048 memcpy(&new_objs[n_new_objs], objs[i]->children, objs[i]->arity * sizeof(new_objs[0]));
3049 n_new_objs += objs[i]->arity;
3050 } else {
3051 /* Leave it. */
3052 new_objs[n_new_objs++] = objs[i];
3053 }
3054
3055 if (!n_new_objs) {
3056 free(new_objs);
3057 new_objs = NULL;
3058 }
3059
3060 /* Ok, put numbers in the level and link cousins. */
3061 for (i = 0; i < n_taken_objs; i++) {
3062 taken_objs[i]->depth = (int) topology->nb_levels;
3063 taken_objs[i]->logical_index = i;
3064 if (i) {
3065 taken_objs[i]->prev_cousin = taken_objs[i-1];
3066 taken_objs[i-1]->next_cousin = taken_objs[i];
3067 }
3068 }
3069 taken_objs[0]->prev_cousin = NULL;
3070 taken_objs[n_taken_objs-1]->next_cousin = NULL;
3071
3072 /* One more level! */
3073 hwloc_debug("--- %s level", hwloc_obj_type_string(top_obj->type));
3074 hwloc_debug(" has number %u\n\n", topology->nb_levels);
3075
3076 if (topology->type_depth[top_obj->type] == HWLOC_TYPE_DEPTH_UNKNOWN)
3077 topology->type_depth[top_obj->type] = (int) topology->nb_levels;
3078 else
3079 topology->type_depth[top_obj->type] = HWLOC_TYPE_DEPTH_MULTIPLE; /* mark as unknown */
3080
3081 taken_objs[n_taken_objs] = NULL;
3082
3083 if (topology->nb_levels == topology->nb_levels_allocated) {
3084 /* extend the arrays of levels */
3085 void *tmplevels, *tmpnbobjs;
3086 tmplevels = realloc(topology->levels,
3087 2 * topology->nb_levels_allocated * sizeof(*topology->levels));
3088 tmpnbobjs = realloc(topology->level_nbobjects,
3089 2 * topology->nb_levels_allocated * sizeof(*topology->level_nbobjects));
3090 if (!tmplevels || !tmpnbobjs) {
3091 fprintf(stderr, "hwloc failed to realloc level arrays to %u\n", topology->nb_levels_allocated * 2);
3092
3093 /* if one realloc succeeded, make sure the caller will free the new buffer */
3094 if (tmplevels)
3095 topology->levels = tmplevels;
3096 if (tmpnbobjs)
3097 topology->level_nbobjects = tmpnbobjs;
3098 /* the realloc that failed left topology->level_foo untouched, will be freed by the caller */
3099
3100 free(objs);
3101 free(taken_objs);
3102 free(new_objs);
3103 errno = ENOMEM;
3104 return -1;
3105 }
3106 topology->levels = tmplevels;
3107 topology->level_nbobjects = tmpnbobjs;
3108 memset(topology->levels + topology->nb_levels_allocated,
3109 0, topology->nb_levels_allocated * sizeof(*topology->levels));
3110 memset(topology->level_nbobjects + topology->nb_levels_allocated,
3111 0, topology->nb_levels_allocated * sizeof(*topology->level_nbobjects));
3112 topology->nb_levels_allocated *= 2;
3113 }
3114 /* add the new level */
3115 topology->level_nbobjects[topology->nb_levels] = n_taken_objs;
3116 topology->levels[topology->nb_levels] = taken_objs;
3117
3118 topology->nb_levels++;
3119
3120 free(objs);
3121
3122 /* Switch to new_objs */
3123 objs = new_objs;
3124 n_objs = n_new_objs;
3125 }
3126
3127 /* It's empty now. */
3128 free(objs);
3129
3130 return 0;
3131 }
3132
3133 int
hwloc_topology_reconnect(struct hwloc_topology * topology,unsigned long flags)3134 hwloc_topology_reconnect(struct hwloc_topology *topology, unsigned long flags)
3135 {
3136 if (flags) {
3137 errno = EINVAL;
3138 return -1;
3139 }
3140 if (!topology->modified)
3141 return 0;
3142
3143 hwloc_connect_children(topology->levels[0][0]);
3144
3145 if (hwloc_connect_levels(topology) < 0)
3146 return -1;
3147
3148 if (hwloc_connect_io_misc_levels(topology) < 0)
3149 return -1;
3150
3151 topology->modified = 0;
3152
3153 return 0;
3154 }
3155
3156 /* for regression testing, make sure the order of io devices
3157 * doesn't change with the dentry order in the filesystem
3158 *
3159 * Only needed for OSDev for now.
3160 */
3161 static hwloc_obj_t
hwloc_debug_insert_osdev_sorted(hwloc_obj_t queue,hwloc_obj_t obj)3162 hwloc_debug_insert_osdev_sorted(hwloc_obj_t queue, hwloc_obj_t obj)
3163 {
3164 hwloc_obj_t *pcur = &queue;
3165 while (*pcur && strcmp((*pcur)->name, obj->name) < 0)
3166 pcur = &((*pcur)->next_sibling);
3167 obj->next_sibling = *pcur;
3168 *pcur = obj;
3169 return queue;
3170 }
3171
3172 static void
hwloc_debug_sort_children(hwloc_obj_t root)3173 hwloc_debug_sort_children(hwloc_obj_t root)
3174 {
3175 hwloc_obj_t child;
3176
3177 if (root->io_first_child) {
3178 hwloc_obj_t osdevqueue, *pchild;
3179
3180 pchild = &root->io_first_child;
3181 osdevqueue = NULL;
3182 while ((child = *pchild) != NULL) {
3183 if (child->type != HWLOC_OBJ_OS_DEVICE) {
3184 /* keep non-osdev untouched */
3185 pchild = &child->next_sibling;
3186 continue;
3187 }
3188
3189 /* dequeue this child */
3190 *pchild = child->next_sibling;
3191 child->next_sibling = NULL;
3192
3193 /* insert in osdev queue in order */
3194 osdevqueue = hwloc_debug_insert_osdev_sorted(osdevqueue, child);
3195 }
3196
3197 /* requeue the now-sorted osdev queue */
3198 *pchild = osdevqueue;
3199 }
3200
3201 /* Recurse */
3202 for_each_child(child, root)
3203 hwloc_debug_sort_children(child);
3204 for_each_memory_child(child, root)
3205 hwloc_debug_sort_children(child);
3206 for_each_io_child(child, root)
3207 hwloc_debug_sort_children(child);
3208 /* no I/O under Misc */
3209 }
3210
hwloc_alloc_root_sets(hwloc_obj_t root)3211 void hwloc_alloc_root_sets(hwloc_obj_t root)
3212 {
3213 /*
3214 * All sets are initially NULL.
3215 *
3216 * At least one backend should call this function to initialize all sets at once.
3217 * XML uses it lazily in case only some sets were given in the XML import.
3218 *
3219 * Other backends can check root->cpuset != NULL to see if somebody
3220 * discovered things before them.
3221 */
3222 if (!root->cpuset)
3223 root->cpuset = hwloc_bitmap_alloc();
3224 if (!root->complete_cpuset)
3225 root->complete_cpuset = hwloc_bitmap_alloc();
3226 if (!root->nodeset)
3227 root->nodeset = hwloc_bitmap_alloc();
3228 if (!root->complete_nodeset)
3229 root->complete_nodeset = hwloc_bitmap_alloc();
3230 }
3231
3232 static void
hwloc_discover_by_phase(struct hwloc_topology * topology,struct hwloc_disc_status * dstatus,const char * phasename __hwloc_attribute_unused)3233 hwloc_discover_by_phase(struct hwloc_topology *topology,
3234 struct hwloc_disc_status *dstatus,
3235 const char *phasename __hwloc_attribute_unused)
3236 {
3237 struct hwloc_backend *backend;
3238 hwloc_debug("%s phase discovery...\n", phasename);
3239 for(backend = topology->backends; backend; backend = backend->next) {
3240 if (dstatus->phase & dstatus->excluded_phases)
3241 break;
3242 if (!(backend->phases & dstatus->phase))
3243 continue;
3244 if (!backend->discover)
3245 continue;
3246 hwloc_debug("%s phase discovery in component %s...\n", phasename, backend->component->name);
3247 backend->discover(backend, dstatus);
3248 hwloc_debug_print_objects(0, topology->levels[0][0]);
3249 }
3250 }
3251
3252 /* Main discovery loop */
3253 static int
hwloc_discover(struct hwloc_topology * topology,struct hwloc_disc_status * dstatus)3254 hwloc_discover(struct hwloc_topology *topology,
3255 struct hwloc_disc_status *dstatus)
3256 {
3257 const char *env;
3258
3259 topology->modified = 0; /* no need to reconnect yet */
3260
3261 topology->allowed_cpuset = hwloc_bitmap_alloc_full();
3262 topology->allowed_nodeset = hwloc_bitmap_alloc_full();
3263
3264 /* discover() callbacks should use hwloc_insert to add objects initialized
3265 * through hwloc_alloc_setup_object.
3266 * For node levels, nodeset and memory must be initialized.
3267 * For cache levels, memory and type/depth must be initialized.
3268 * For group levels, depth must be initialized.
3269 */
3270
3271 /* There must be at least a PU object for each logical processor, at worse
3272 * produced by hwloc_setup_pu_level()
3273 */
3274
3275 /* To be able to just use hwloc__insert_object_by_cpuset to insert the object
3276 * in the topology according to the cpuset, the cpuset field must be
3277 * initialized.
3278 */
3279
3280 /* A priori, All processors are visible in the topology, and allowed
3281 * for the application.
3282 *
3283 * - If some processors exist but topology information is unknown for them
3284 * (and thus the backend couldn't create objects for them), they should be
3285 * added to the complete_cpuset field of the lowest object where the object
3286 * could reside.
3287 *
3288 * - If some processors are not allowed for the application (e.g. for
3289 * administration reasons), they should be dropped from the allowed_cpuset
3290 * field.
3291 *
3292 * The same applies to the node sets complete_nodeset and allowed_cpuset.
3293 *
3294 * If such field doesn't exist yet, it can be allocated, and initialized to
3295 * zero (for complete), or to full (for allowed). The values are
3296 * automatically propagated to the whole tree after detection.
3297 */
3298
3299 if (topology->backend_phases & HWLOC_DISC_PHASE_GLOBAL) {
3300 /* usually, GLOBAL is alone.
3301 * but HWLOC_ANNOTATE_GLOBAL_COMPONENTS=1 allows optional ANNOTATE steps.
3302 */
3303 struct hwloc_backend *global_backend = topology->backends;
3304 assert(global_backend);
3305 assert(global_backend->phases == HWLOC_DISC_PHASE_GLOBAL);
3306
3307 /*
3308 * Perform the single-component-based GLOBAL discovery
3309 */
3310 hwloc_debug("GLOBAL phase discovery...\n");
3311 hwloc_debug("GLOBAL phase discovery with component %s...\n", global_backend->component->name);
3312 dstatus->phase = HWLOC_DISC_PHASE_GLOBAL;
3313 global_backend->discover(global_backend, dstatus);
3314 hwloc_debug_print_objects(0, topology->levels[0][0]);
3315 }
3316 /* Don't explicitly ignore other phases, in case there's ever
3317 * a need to bring them back.
3318 * The component with usually exclude them by default anyway.
3319 * Except if annotating global components is explicitly requested.
3320 */
3321
3322 if (topology->backend_phases & HWLOC_DISC_PHASE_CPU) {
3323 /*
3324 * Discover CPUs first
3325 */
3326 dstatus->phase = HWLOC_DISC_PHASE_CPU;
3327 hwloc_discover_by_phase(topology, dstatus, "CPU");
3328 }
3329
3330 if (!(topology->backend_phases & (HWLOC_DISC_PHASE_GLOBAL|HWLOC_DISC_PHASE_CPU))) {
3331 hwloc_debug("No GLOBAL or CPU component phase found\n");
3332 /* we'll fail below */
3333 }
3334
3335 /* One backend should have called hwloc_alloc_root_sets()
3336 * and set bits during PU and NUMA insert.
3337 */
3338 if (!topology->levels[0][0]->cpuset || hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) {
3339 hwloc_debug("%s", "No PU added by any CPU or GLOBAL component phase\n");
3340 errno = EINVAL;
3341 return -1;
3342 }
3343
3344 /*
3345 * Memory-specific discovery
3346 */
3347 if (topology->backend_phases & HWLOC_DISC_PHASE_MEMORY) {
3348 dstatus->phase = HWLOC_DISC_PHASE_MEMORY;
3349 hwloc_discover_by_phase(topology, dstatus, "MEMORY");
3350 }
3351
3352 if (/* check if getting the sets of locally allowed resources is possible */
3353 topology->binding_hooks.get_allowed_resources
3354 && topology->is_thissystem
3355 /* check whether it has been done already */
3356 && !(dstatus->flags & HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES)
3357 /* check whether it was explicitly requested */
3358 && ((topology->flags & HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES) != 0
3359 || ((env = getenv("HWLOC_THISSYSTEM_ALLOWED_RESOURCES")) != NULL && atoi(env)))) {
3360 /* OK, get the sets of locally allowed resources */
3361 topology->binding_hooks.get_allowed_resources(topology);
3362 dstatus->flags |= HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES;
3363 }
3364
3365 /* If there's no NUMA node, add one with all the memory.
3366 * root->complete_nodeset wouldn't be empty if any NUMA was ever added:
3367 * - insert_by_cpuset() adds bits whe PU/NUMA are added.
3368 * - XML takes care of sanitizing nodesets.
3369 */
3370 if (hwloc_bitmap_iszero(topology->levels[0][0]->complete_nodeset)) {
3371 hwloc_obj_t node;
3372 hwloc_debug("%s", "\nAdd missing single NUMA node\n");
3373 node = hwloc_alloc_setup_object(topology, HWLOC_OBJ_NUMANODE, 0);
3374 node->cpuset = hwloc_bitmap_dup(topology->levels[0][0]->cpuset);
3375 node->nodeset = hwloc_bitmap_alloc();
3376 /* other nodesets will be filled below */
3377 hwloc_bitmap_set(node->nodeset, 0);
3378 memcpy(&node->attr->numanode, &topology->machine_memory, sizeof(topology->machine_memory));
3379 memset(&topology->machine_memory, 0, sizeof(topology->machine_memory));
3380 hwloc__insert_object_by_cpuset(topology, NULL, node, "core:defaultnumanode");
3381 } else {
3382 /* if we're sure we found all NUMA nodes without their sizes (x86 backend?),
3383 * we could split topology->total_memory in all of them.
3384 */
3385 free(topology->machine_memory.page_types);
3386 memset(&topology->machine_memory, 0, sizeof(topology->machine_memory));
3387 }
3388
3389 hwloc_debug("%s", "\nFixup root sets\n");
3390 hwloc_bitmap_and(topology->levels[0][0]->cpuset, topology->levels[0][0]->cpuset, topology->levels[0][0]->complete_cpuset);
3391 hwloc_bitmap_and(topology->levels[0][0]->nodeset, topology->levels[0][0]->nodeset, topology->levels[0][0]->complete_nodeset);
3392
3393 hwloc_bitmap_and(topology->allowed_cpuset, topology->allowed_cpuset, topology->levels[0][0]->cpuset);
3394 hwloc_bitmap_and(topology->allowed_nodeset, topology->allowed_nodeset, topology->levels[0][0]->nodeset);
3395
3396 hwloc_debug("%s", "\nPropagate sets\n");
3397 /* cpuset are already there thanks to the _by_cpuset insertion,
3398 * but nodeset have to be propagated below and above NUMA nodes
3399 */
3400 propagate_nodeset(topology->levels[0][0]);
3401 /* now fixup parent/children sets */
3402 fixup_sets(topology->levels[0][0]);
3403
3404 hwloc_debug_print_objects(0, topology->levels[0][0]);
3405
3406 if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) {
3407 hwloc_debug("%s", "\nRemoving unauthorized sets from all sets\n");
3408 remove_unused_sets(topology, topology->levels[0][0]);
3409 hwloc_debug_print_objects(0, topology->levels[0][0]);
3410 }
3411
3412 /* see if we should ignore the root now that we know how many children it has */
3413 if (!hwloc_filter_check_keep_object(topology, topology->levels[0][0])
3414 && topology->levels[0][0]->first_child && !topology->levels[0][0]->first_child->next_sibling) {
3415 hwloc_obj_t oldroot = topology->levels[0][0];
3416 hwloc_obj_t newroot = oldroot->first_child;
3417 /* switch to the new root */
3418 newroot->parent = NULL;
3419 topology->levels[0][0] = newroot;
3420 /* move oldroot memory/io/misc children before newroot children */
3421 if (oldroot->memory_first_child)
3422 prepend_siblings_list(&newroot->memory_first_child, oldroot->memory_first_child, newroot);
3423 if (oldroot->io_first_child)
3424 prepend_siblings_list(&newroot->io_first_child, oldroot->io_first_child, newroot);
3425 if (oldroot->misc_first_child)
3426 prepend_siblings_list(&newroot->misc_first_child, oldroot->misc_first_child, newroot);
3427 /* destroy oldroot and use the new one */
3428 hwloc_free_unlinked_object(oldroot);
3429 }
3430
3431 /*
3432 * All object cpusets and nodesets are properly set now.
3433 */
3434
3435 /* Now connect handy pointers to make remaining discovery easier. */
3436 hwloc_debug("%s", "\nOk, finished tweaking, now connect\n");
3437 if (hwloc_topology_reconnect(topology, 0) < 0)
3438 return -1;
3439 hwloc_debug_print_objects(0, topology->levels[0][0]);
3440
3441 /*
3442 * Additional discovery
3443 */
3444 if (topology->backend_phases & HWLOC_DISC_PHASE_PCI) {
3445 dstatus->phase = HWLOC_DISC_PHASE_PCI;
3446 hwloc_discover_by_phase(topology, dstatus, "PCI");
3447 }
3448 if (topology->backend_phases & HWLOC_DISC_PHASE_IO) {
3449 dstatus->phase = HWLOC_DISC_PHASE_IO;
3450 hwloc_discover_by_phase(topology, dstatus, "IO");
3451 }
3452 if (topology->backend_phases & HWLOC_DISC_PHASE_MISC) {
3453 dstatus->phase = HWLOC_DISC_PHASE_MISC;
3454 hwloc_discover_by_phase(topology, dstatus, "MISC");
3455 }
3456 if (topology->backend_phases & HWLOC_DISC_PHASE_ANNOTATE) {
3457 dstatus->phase = HWLOC_DISC_PHASE_ANNOTATE;
3458 hwloc_discover_by_phase(topology, dstatus, "ANNOTATE");
3459 }
3460
3461 if (getenv("HWLOC_DEBUG_SORT_CHILDREN"))
3462 hwloc_debug_sort_children(topology->levels[0][0]);
3463
3464 /* Remove some stuff */
3465
3466 hwloc_debug("%s", "\nRemoving bridge objects if needed\n");
3467 hwloc_filter_bridges(topology, topology->levels[0][0]);
3468 hwloc_debug_print_objects(0, topology->levels[0][0]);
3469
3470 hwloc_debug("%s", "\nRemoving empty objects\n");
3471 remove_empty(topology, &topology->levels[0][0]);
3472 if (!topology->levels[0][0]) {
3473 fprintf(stderr, "Topology became empty, aborting!\n");
3474 return -1;
3475 }
3476 if (hwloc_bitmap_iszero(topology->levels[0][0]->cpuset)) {
3477 fprintf(stderr, "Topology does not contain any PU, aborting!\n");
3478 return -1;
3479 }
3480 if (hwloc_bitmap_iszero(topology->levels[0][0]->nodeset)) {
3481 fprintf(stderr, "Topology does not contain any NUMA node, aborting!\n");
3482 return -1;
3483 }
3484 hwloc_debug_print_objects(0, topology->levels[0][0]);
3485
3486 /* Reconnect things after all these changes.
3487 * Often needed because of Groups inserted for I/Os.
3488 * And required for KEEP_STRUCTURE below.
3489 */
3490 if (hwloc_topology_reconnect(topology, 0) < 0)
3491 return -1;
3492
3493 hwloc_debug("%s", "\nRemoving levels with HWLOC_TYPE_FILTER_KEEP_STRUCTURE\n");
3494 hwloc_filter_levels_keep_structure(topology);
3495 hwloc_debug_print_objects(0, topology->levels[0][0]);
3496
3497 /* accumulate children memory in total_memory fields (only once parent is set) */
3498 hwloc_debug("%s", "\nPropagate total memory up\n");
3499 propagate_total_memory(topology->levels[0][0]);
3500
3501 /* setup the symmetric_subtree attribute */
3502 hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]);
3503
3504 /* apply group depths */
3505 hwloc_set_group_depth(topology);
3506
3507 /* add some identification attributes if not loading from XML */
3508 if (topology->backends
3509 && strcmp(topology->backends->component->name, "xml")
3510 && !getenv("HWLOC_DONT_ADD_VERSION_INFO")) {
3511 char *value;
3512 /* add a hwlocVersion */
3513 hwloc_obj_add_info(topology->levels[0][0], "hwlocVersion", HWLOC_VERSION);
3514 /* add a ProcessName */
3515 value = hwloc_progname(topology);
3516 if (value) {
3517 hwloc_obj_add_info(topology->levels[0][0], "ProcessName", value);
3518 free(value);
3519 }
3520 }
3521
3522 return 0;
3523 }
3524
3525 /* To be called before discovery is actually launched,
3526 * Resets everything in case a previous load initialized some stuff.
3527 */
3528 void
hwloc_topology_setup_defaults(struct hwloc_topology * topology)3529 hwloc_topology_setup_defaults(struct hwloc_topology *topology)
3530 {
3531 struct hwloc_obj *root_obj;
3532
3533 /* reset support */
3534 memset(&topology->binding_hooks, 0, sizeof(topology->binding_hooks));
3535 memset(topology->support.discovery, 0, sizeof(*topology->support.discovery));
3536 memset(topology->support.cpubind, 0, sizeof(*topology->support.cpubind));
3537 memset(topology->support.membind, 0, sizeof(*topology->support.membind));
3538 memset(topology->support.misc, 0, sizeof(*topology->support.misc));
3539
3540 /* Only the System object on top by default */
3541 topology->next_gp_index = 1; /* keep 0 as an invalid value */
3542 topology->nb_levels = 1; /* there's at least SYSTEM */
3543 topology->levels[0] = hwloc_tma_malloc (topology->tma, sizeof (hwloc_obj_t));
3544 topology->level_nbobjects[0] = 1;
3545
3546 /* Machine-wide memory */
3547 topology->machine_memory.local_memory = 0;
3548 topology->machine_memory.page_types_len = 0;
3549 topology->machine_memory.page_types = NULL;
3550
3551 /* Allowed stuff */
3552 topology->allowed_cpuset = NULL;
3553 topology->allowed_nodeset = NULL;
3554
3555 /* NULLify other special levels */
3556 memset(&topology->slevels, 0, sizeof(topology->slevels));
3557 /* assert the indexes of special levels */
3558 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_NUMANODE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_NUMANODE));
3559 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_MISC == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_MISC));
3560 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_BRIDGE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_BRIDGE));
3561 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_PCIDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_PCI_DEVICE));
3562 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_OSDEV == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_OS_DEVICE));
3563 HWLOC_BUILD_ASSERT(HWLOC_SLEVEL_MEMCACHE == HWLOC_SLEVEL_FROM_DEPTH(HWLOC_TYPE_DEPTH_MEMCACHE));
3564
3565 /* sane values to type_depth */
3566 hwloc_reset_normal_type_depths(topology);
3567 topology->type_depth[HWLOC_OBJ_NUMANODE] = HWLOC_TYPE_DEPTH_NUMANODE;
3568 topology->type_depth[HWLOC_OBJ_MISC] = HWLOC_TYPE_DEPTH_MISC;
3569 topology->type_depth[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_DEPTH_BRIDGE;
3570 topology->type_depth[HWLOC_OBJ_PCI_DEVICE] = HWLOC_TYPE_DEPTH_PCI_DEVICE;
3571 topology->type_depth[HWLOC_OBJ_OS_DEVICE] = HWLOC_TYPE_DEPTH_OS_DEVICE;
3572 topology->type_depth[HWLOC_OBJ_MEMCACHE] = HWLOC_TYPE_DEPTH_MEMCACHE;
3573
3574 /* Create the actual machine object, but don't touch its attributes yet
3575 * since the OS backend may still change the object into something else
3576 * (for instance System)
3577 */
3578 root_obj = hwloc_alloc_setup_object(topology, HWLOC_OBJ_MACHINE, 0);
3579 topology->levels[0][0] = root_obj;
3580 }
3581
3582 static void hwloc__topology_filter_init(struct hwloc_topology *topology);
3583
3584 /* This function may use a tma, it cannot free() or realloc() */
3585 static int
hwloc__topology_init(struct hwloc_topology ** topologyp,unsigned nblevels,struct hwloc_tma * tma)3586 hwloc__topology_init (struct hwloc_topology **topologyp,
3587 unsigned nblevels,
3588 struct hwloc_tma *tma)
3589 {
3590 struct hwloc_topology *topology;
3591
3592 topology = hwloc_tma_malloc (tma, sizeof (struct hwloc_topology));
3593 if(!topology)
3594 return -1;
3595
3596 topology->tma = tma;
3597
3598 hwloc_components_init(); /* uses malloc without tma, but won't need it since dup() caller already took a reference */
3599 hwloc_topology_components_init(topology);
3600 hwloc_pci_discovery_init(topology); /* make sure both dup() and load() get sane variables */
3601
3602 /* Setup topology context */
3603 topology->is_loaded = 0;
3604 topology->flags = 0;
3605 topology->is_thissystem = 1;
3606 topology->pid = 0;
3607 topology->userdata = NULL;
3608 topology->topology_abi = HWLOC_TOPOLOGY_ABI;
3609 topology->adopted_shmem_addr = NULL;
3610 topology->adopted_shmem_length = 0;
3611
3612 topology->support.discovery = hwloc_tma_malloc(tma, sizeof(*topology->support.discovery));
3613 topology->support.cpubind = hwloc_tma_malloc(tma, sizeof(*topology->support.cpubind));
3614 topology->support.membind = hwloc_tma_malloc(tma, sizeof(*topology->support.membind));
3615 topology->support.misc = hwloc_tma_malloc(tma, sizeof(*topology->support.misc));
3616
3617 topology->nb_levels_allocated = nblevels; /* enough for default 10 levels = Mach+Pack+Die+NUMA+L3+L2+L1d+L1i+Co+PU */
3618 topology->levels = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->levels));
3619 topology->level_nbobjects = hwloc_tma_calloc(tma, topology->nb_levels_allocated * sizeof(*topology->level_nbobjects));
3620
3621 hwloc__topology_filter_init(topology);
3622
3623 hwloc_internal_distances_init(topology);
3624 hwloc_internal_memattrs_init(topology);
3625 hwloc_internal_cpukinds_init(topology);
3626
3627 topology->userdata_export_cb = NULL;
3628 topology->userdata_import_cb = NULL;
3629 topology->userdata_not_decoded = 0;
3630
3631 /* Make the topology look like something coherent but empty */
3632 hwloc_topology_setup_defaults(topology);
3633
3634 *topologyp = topology;
3635 return 0;
3636 }
3637
3638 int
hwloc_topology_init(struct hwloc_topology ** topologyp)3639 hwloc_topology_init (struct hwloc_topology **topologyp)
3640 {
3641 return hwloc__topology_init(topologyp,
3642 16, /* 16 is enough for default 10 levels = Mach+Pack+Die+NUMA+L3+L2+L1d+L1i+Co+PU */
3643 NULL); /* no TMA for normal topologies, too many allocations to fix */
3644 }
3645
3646 int
hwloc_topology_set_pid(struct hwloc_topology * topology __hwloc_attribute_unused,hwloc_pid_t pid __hwloc_attribute_unused)3647 hwloc_topology_set_pid(struct hwloc_topology *topology __hwloc_attribute_unused,
3648 hwloc_pid_t pid __hwloc_attribute_unused)
3649 {
3650 if (topology->is_loaded) {
3651 errno = EBUSY;
3652 return -1;
3653 }
3654
3655 /* this does *not* change the backend */
3656 #ifdef HWLOC_LINUX_SYS
3657 topology->pid = pid;
3658 return 0;
3659 #else /* HWLOC_LINUX_SYS */
3660 errno = ENOSYS;
3661 return -1;
3662 #endif /* HWLOC_LINUX_SYS */
3663 }
3664
3665 int
hwloc_topology_set_synthetic(struct hwloc_topology * topology,const char * description)3666 hwloc_topology_set_synthetic(struct hwloc_topology *topology, const char *description)
3667 {
3668 if (topology->is_loaded) {
3669 errno = EBUSY;
3670 return -1;
3671 }
3672
3673 return hwloc_disc_component_force_enable(topology,
3674 0 /* api */,
3675 "synthetic",
3676 description, NULL, NULL);
3677 }
3678
3679 int
hwloc_topology_set_xml(struct hwloc_topology * topology,const char * xmlpath)3680 hwloc_topology_set_xml(struct hwloc_topology *topology,
3681 const char *xmlpath)
3682 {
3683 if (topology->is_loaded) {
3684 errno = EBUSY;
3685 return -1;
3686 }
3687
3688 return hwloc_disc_component_force_enable(topology,
3689 0 /* api */,
3690 "xml",
3691 xmlpath, NULL, NULL);
3692 }
3693
3694 int
hwloc_topology_set_xmlbuffer(struct hwloc_topology * topology,const char * xmlbuffer,int size)3695 hwloc_topology_set_xmlbuffer(struct hwloc_topology *topology,
3696 const char *xmlbuffer,
3697 int size)
3698 {
3699 if (topology->is_loaded) {
3700 errno = EBUSY;
3701 return -1;
3702 }
3703
3704 return hwloc_disc_component_force_enable(topology,
3705 0 /* api */,
3706 "xml", NULL,
3707 xmlbuffer, (void*) (uintptr_t) size);
3708 }
3709
3710 int
hwloc_topology_set_flags(struct hwloc_topology * topology,unsigned long flags)3711 hwloc_topology_set_flags (struct hwloc_topology *topology, unsigned long flags)
3712 {
3713 if (topology->is_loaded) {
3714 /* actually harmless */
3715 errno = EBUSY;
3716 return -1;
3717 }
3718
3719 if (flags & ~(HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED|HWLOC_TOPOLOGY_FLAG_IS_THISSYSTEM|HWLOC_TOPOLOGY_FLAG_THISSYSTEM_ALLOWED_RESOURCES|HWLOC_TOPOLOGY_FLAG_IMPORT_SUPPORT)) {
3720 errno = EINVAL;
3721 return -1;
3722 }
3723
3724 topology->flags = flags;
3725 return 0;
3726 }
3727
3728 unsigned long
hwloc_topology_get_flags(struct hwloc_topology * topology)3729 hwloc_topology_get_flags (struct hwloc_topology *topology)
3730 {
3731 return topology->flags;
3732 }
3733
3734 static void
hwloc__topology_filter_init(struct hwloc_topology * topology)3735 hwloc__topology_filter_init(struct hwloc_topology *topology)
3736 {
3737 hwloc_obj_type_t type;
3738 /* Only ignore useless cruft by default */
3739 for(type = HWLOC_OBJ_TYPE_MIN; type < HWLOC_OBJ_TYPE_MAX; type++)
3740 topology->type_filter[type] = HWLOC_TYPE_FILTER_KEEP_ALL;
3741 topology->type_filter[HWLOC_OBJ_L1ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3742 topology->type_filter[HWLOC_OBJ_L2ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3743 topology->type_filter[HWLOC_OBJ_L3ICACHE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3744 topology->type_filter[HWLOC_OBJ_MEMCACHE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3745 topology->type_filter[HWLOC_OBJ_GROUP] = HWLOC_TYPE_FILTER_KEEP_STRUCTURE;
3746 topology->type_filter[HWLOC_OBJ_MISC] = HWLOC_TYPE_FILTER_KEEP_NONE;
3747 topology->type_filter[HWLOC_OBJ_BRIDGE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3748 topology->type_filter[HWLOC_OBJ_PCI_DEVICE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3749 topology->type_filter[HWLOC_OBJ_OS_DEVICE] = HWLOC_TYPE_FILTER_KEEP_NONE;
3750 }
3751
3752 static int
hwloc__topology_set_type_filter(struct hwloc_topology * topology,hwloc_obj_type_t type,enum hwloc_type_filter_e filter)3753 hwloc__topology_set_type_filter(struct hwloc_topology *topology, hwloc_obj_type_t type, enum hwloc_type_filter_e filter)
3754 {
3755 if (type == HWLOC_OBJ_PU || type == HWLOC_OBJ_NUMANODE || type == HWLOC_OBJ_MACHINE) {
3756 if (filter != HWLOC_TYPE_FILTER_KEEP_ALL) {
3757 /* we need the Machine, PU and NUMA levels */
3758 errno = EINVAL;
3759 return -1;
3760 }
3761 } else if (hwloc__obj_type_is_special(type)) {
3762 if (filter == HWLOC_TYPE_FILTER_KEEP_STRUCTURE) {
3763 /* I/O and Misc are outside of the main topology structure, makes no sense. */
3764 errno = EINVAL;
3765 return -1;
3766 }
3767 } else if (type == HWLOC_OBJ_GROUP) {
3768 if (filter == HWLOC_TYPE_FILTER_KEEP_ALL) {
3769 /* Groups are always ignored, at least keep_structure */
3770 errno = EINVAL;
3771 return -1;
3772 }
3773 }
3774
3775 /* "important" just means "all" for non-I/O non-Misc */
3776 if (!hwloc__obj_type_is_special(type) && filter == HWLOC_TYPE_FILTER_KEEP_IMPORTANT)
3777 filter = HWLOC_TYPE_FILTER_KEEP_ALL;
3778
3779 topology->type_filter[type] = filter;
3780 return 0;
3781 }
3782
3783 int
hwloc_topology_set_type_filter(struct hwloc_topology * topology,hwloc_obj_type_t type,enum hwloc_type_filter_e filter)3784 hwloc_topology_set_type_filter(struct hwloc_topology *topology, hwloc_obj_type_t type, enum hwloc_type_filter_e filter)
3785 {
3786 HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0);
3787 if ((unsigned) type >= HWLOC_OBJ_TYPE_MAX) {
3788 errno = EINVAL;
3789 return -1;
3790 }
3791 if (topology->is_loaded) {
3792 errno = EBUSY;
3793 return -1;
3794 }
3795 return hwloc__topology_set_type_filter(topology, type, filter);
3796 }
3797
3798 int
hwloc_topology_set_all_types_filter(struct hwloc_topology * topology,enum hwloc_type_filter_e filter)3799 hwloc_topology_set_all_types_filter(struct hwloc_topology *topology, enum hwloc_type_filter_e filter)
3800 {
3801 hwloc_obj_type_t type;
3802 if (topology->is_loaded) {
3803 errno = EBUSY;
3804 return -1;
3805 }
3806 for(type = HWLOC_OBJ_TYPE_MIN; type < HWLOC_OBJ_TYPE_MAX; type++)
3807 hwloc__topology_set_type_filter(topology, type, filter);
3808 return 0;
3809 }
3810
3811 int
hwloc_topology_set_cache_types_filter(hwloc_topology_t topology,enum hwloc_type_filter_e filter)3812 hwloc_topology_set_cache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter)
3813 {
3814 unsigned i;
3815 for(i=HWLOC_OBJ_L1CACHE; i<HWLOC_OBJ_L3ICACHE; i++)
3816 hwloc_topology_set_type_filter(topology, (hwloc_obj_type_t) i, filter);
3817 return 0;
3818 }
3819
3820 int
hwloc_topology_set_icache_types_filter(hwloc_topology_t topology,enum hwloc_type_filter_e filter)3821 hwloc_topology_set_icache_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter)
3822 {
3823 unsigned i;
3824 for(i=HWLOC_OBJ_L1ICACHE; i<HWLOC_OBJ_L3ICACHE; i++)
3825 hwloc_topology_set_type_filter(topology, (hwloc_obj_type_t) i, filter);
3826 return 0;
3827 }
3828
3829 int
hwloc_topology_set_io_types_filter(hwloc_topology_t topology,enum hwloc_type_filter_e filter)3830 hwloc_topology_set_io_types_filter(hwloc_topology_t topology, enum hwloc_type_filter_e filter)
3831 {
3832 hwloc_topology_set_type_filter(topology, HWLOC_OBJ_BRIDGE, filter);
3833 hwloc_topology_set_type_filter(topology, HWLOC_OBJ_PCI_DEVICE, filter);
3834 hwloc_topology_set_type_filter(topology, HWLOC_OBJ_OS_DEVICE, filter);
3835 return 0;
3836 }
3837
3838 int
hwloc_topology_get_type_filter(struct hwloc_topology * topology,hwloc_obj_type_t type,enum hwloc_type_filter_e * filterp)3839 hwloc_topology_get_type_filter(struct hwloc_topology *topology, hwloc_obj_type_t type, enum hwloc_type_filter_e *filterp)
3840 {
3841 HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0);
3842 if ((unsigned) type >= HWLOC_OBJ_TYPE_MAX) {
3843 errno = EINVAL;
3844 return -1;
3845 }
3846 *filterp = topology->type_filter[type];
3847 return 0;
3848 }
3849
3850 void
hwloc_topology_clear(struct hwloc_topology * topology)3851 hwloc_topology_clear (struct hwloc_topology *topology)
3852 {
3853 /* no need to set to NULL after free() since callers will call setup_defaults() or just destroy the rest of the topology */
3854 unsigned l;
3855 hwloc_internal_cpukinds_destroy(topology);
3856 hwloc_internal_distances_destroy(topology);
3857 hwloc_internal_memattrs_destroy(topology);
3858 hwloc_free_object_and_children(topology->levels[0][0]);
3859 hwloc_bitmap_free(topology->allowed_cpuset);
3860 hwloc_bitmap_free(topology->allowed_nodeset);
3861 for (l=0; l<topology->nb_levels; l++)
3862 free(topology->levels[l]);
3863 for(l=0; l<HWLOC_NR_SLEVELS; l++)
3864 free(topology->slevels[l].objs);
3865 free(topology->machine_memory.page_types);
3866 }
3867
3868 void
hwloc_topology_destroy(struct hwloc_topology * topology)3869 hwloc_topology_destroy (struct hwloc_topology *topology)
3870 {
3871 if (topology->adopted_shmem_addr) {
3872 hwloc__topology_disadopt(topology);
3873 return;
3874 }
3875
3876 hwloc_backends_disable_all(topology);
3877 hwloc_topology_components_fini(topology);
3878 hwloc_components_fini();
3879
3880 hwloc_topology_clear(topology);
3881
3882 free(topology->levels);
3883 free(topology->level_nbobjects);
3884
3885 free(topology->support.discovery);
3886 free(topology->support.cpubind);
3887 free(topology->support.membind);
3888 free(topology->support.misc);
3889 free(topology);
3890 }
3891
3892 int
hwloc_topology_load(struct hwloc_topology * topology)3893 hwloc_topology_load (struct hwloc_topology *topology)
3894 {
3895 struct hwloc_disc_status dstatus;
3896 const char *env;
3897 int err;
3898
3899 if (topology->is_loaded) {
3900 errno = EBUSY;
3901 return -1;
3902 }
3903
3904 /* initialize envvar-related things */
3905 hwloc_internal_distances_prepare(topology);
3906 hwloc_internal_memattrs_prepare(topology);
3907
3908 if (getenv("HWLOC_XML_USERDATA_NOT_DECODED"))
3909 topology->userdata_not_decoded = 1;
3910
3911 /* Ignore variables if HWLOC_COMPONENTS is set. It will be processed later */
3912 if (!getenv("HWLOC_COMPONENTS")) {
3913 /* Only apply variables if we have not changed the backend yet.
3914 * Only the first one will be kept.
3915 * Check for FSROOT first since it's for debugging so likely needs to override everything else.
3916 * Check for XML last (that's the one that may be set system-wide by administrators)
3917 * so that it's only used if other variables are not set,
3918 * to allow users to override easily.
3919 */
3920 if (!topology->backends) {
3921 const char *fsroot_path_env = getenv("HWLOC_FSROOT");
3922 if (fsroot_path_env)
3923 hwloc_disc_component_force_enable(topology,
3924 1 /* env force */,
3925 "linux",
3926 NULL /* backend will getenv again */, NULL, NULL);
3927 }
3928 if (!topology->backends) {
3929 const char *cpuid_path_env = getenv("HWLOC_CPUID_PATH");
3930 if (cpuid_path_env)
3931 hwloc_disc_component_force_enable(topology,
3932 1 /* env force */,
3933 "x86",
3934 NULL /* backend will getenv again */, NULL, NULL);
3935 }
3936 if (!topology->backends) {
3937 const char *synthetic_env = getenv("HWLOC_SYNTHETIC");
3938 if (synthetic_env)
3939 hwloc_disc_component_force_enable(topology,
3940 1 /* env force */,
3941 "synthetic",
3942 synthetic_env, NULL, NULL);
3943 }
3944 if (!topology->backends) {
3945 const char *xmlpath_env = getenv("HWLOC_XMLFILE");
3946 if (xmlpath_env)
3947 hwloc_disc_component_force_enable(topology,
3948 1 /* env force */,
3949 "xml",
3950 xmlpath_env, NULL, NULL);
3951 }
3952 }
3953
3954 dstatus.excluded_phases = 0;
3955 dstatus.flags = 0; /* did nothing yet */
3956
3957 env = getenv("HWLOC_ALLOW");
3958 if (env && !strcmp(env, "all"))
3959 /* don't retrieve the sets of allowed resources */
3960 dstatus.flags |= HWLOC_DISC_STATUS_FLAG_GOT_ALLOWED_RESOURCES;
3961
3962 /* instantiate all possible other backends now */
3963 hwloc_disc_components_enable_others(topology);
3964 /* now that backends are enabled, update the thissystem flag and some callbacks */
3965 hwloc_backends_is_thissystem(topology);
3966 hwloc_backends_find_callbacks(topology);
3967 /*
3968 * Now set binding hooks according to topology->is_thissystem
3969 * and what the native OS backend offers.
3970 */
3971 hwloc_set_binding_hooks(topology);
3972
3973 hwloc_pci_discovery_prepare(topology);
3974
3975 /* actual topology discovery */
3976 err = hwloc_discover(topology, &dstatus);
3977 if (err < 0)
3978 goto out;
3979
3980 hwloc_pci_discovery_exit(topology);
3981
3982 #ifndef HWLOC_DEBUG
3983 if (getenv("HWLOC_DEBUG_CHECK"))
3984 #endif
3985 hwloc_topology_check(topology);
3986
3987 /* Rank cpukinds */
3988 hwloc_internal_cpukinds_rank(topology);
3989
3990 /* Mark distances objs arrays as invalid since we may have removed objects
3991 * from the topology after adding the distances (remove_empty, etc).
3992 * It would be hard to actually verify whether it's needed.
3993 */
3994 hwloc_internal_distances_invalidate_cached_objs(topology);
3995 /* And refresh distances so that multithreaded concurrent distances_get()
3996 * don't refresh() concurrently (disallowed).
3997 */
3998 hwloc_internal_distances_refresh(topology);
3999
4000 /* Same for memattrs */
4001 hwloc_internal_memattrs_need_refresh(topology);
4002 hwloc_internal_memattrs_refresh(topology);
4003
4004 topology->is_loaded = 1;
4005
4006 if (topology->backend_phases & HWLOC_DISC_PHASE_TWEAK) {
4007 dstatus.phase = HWLOC_DISC_PHASE_TWEAK;
4008 hwloc_discover_by_phase(topology, &dstatus, "TWEAK");
4009 }
4010
4011 return 0;
4012
4013 out:
4014 hwloc_pci_discovery_exit(topology);
4015 hwloc_topology_clear(topology);
4016 hwloc_topology_setup_defaults(topology);
4017 hwloc_backends_disable_all(topology);
4018 return -1;
4019 }
4020
4021 /* adjust object cpusets according the given droppedcpuset,
4022 * drop object whose cpuset becomes empty and that have no children,
4023 * and propagate NUMA node removal as nodeset changes in parents.
4024 */
4025 static void
restrict_object_by_cpuset(hwloc_topology_t topology,unsigned long flags,hwloc_obj_t * pobj,hwloc_bitmap_t droppedcpuset,hwloc_bitmap_t droppednodeset)4026 restrict_object_by_cpuset(hwloc_topology_t topology, unsigned long flags, hwloc_obj_t *pobj,
4027 hwloc_bitmap_t droppedcpuset, hwloc_bitmap_t droppednodeset)
4028 {
4029 hwloc_obj_t obj = *pobj, child, *pchild;
4030 int modified = 0;
4031
4032 if (hwloc_bitmap_intersects(obj->complete_cpuset, droppedcpuset)) {
4033 hwloc_bitmap_andnot(obj->cpuset, obj->cpuset, droppedcpuset);
4034 hwloc_bitmap_andnot(obj->complete_cpuset, obj->complete_cpuset, droppedcpuset);
4035 modified = 1;
4036 } else {
4037 if ((flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS)
4038 && hwloc_bitmap_iszero(obj->complete_cpuset)) {
4039 /* we're empty, there's a NUMAnode below us, it'll be removed this time */
4040 modified = 1;
4041 }
4042 /* nodeset cannot intersect unless cpuset intersects or is empty */
4043 if (droppednodeset)
4044 assert(!hwloc_bitmap_intersects(obj->complete_nodeset, droppednodeset)
4045 || hwloc_bitmap_iszero(obj->complete_cpuset));
4046 }
4047 if (droppednodeset) {
4048 hwloc_bitmap_andnot(obj->nodeset, obj->nodeset, droppednodeset);
4049 hwloc_bitmap_andnot(obj->complete_nodeset, obj->complete_nodeset, droppednodeset);
4050 }
4051
4052 if (modified) {
4053 for_each_child_safe(child, obj, pchild)
4054 restrict_object_by_cpuset(topology, flags, pchild, droppedcpuset, droppednodeset);
4055 /* if some hwloc_bitmap_first(child->complete_cpuset) changed, children might need to be reordered */
4056 hwloc__reorder_children(obj);
4057
4058 for_each_memory_child_safe(child, obj, pchild)
4059 restrict_object_by_cpuset(topology, flags, pchild, droppedcpuset, droppednodeset);
4060 /* local NUMA nodes have the same cpusets, no need to reorder them */
4061
4062 /* Nothing to restrict under I/O or Misc */
4063 }
4064
4065 if (!obj->first_child && !obj->memory_first_child /* arity not updated before connect_children() */
4066 && hwloc_bitmap_iszero(obj->cpuset)
4067 && (obj->type != HWLOC_OBJ_NUMANODE || (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS))) {
4068 /* remove object */
4069 hwloc_debug("%s", "\nRemoving object during restrict by cpuset");
4070 hwloc_debug_print_object(0, obj);
4071
4072 if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_IO)) {
4073 hwloc_free_object_siblings_and_children(obj->io_first_child);
4074 obj->io_first_child = NULL;
4075 }
4076 if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_MISC)) {
4077 hwloc_free_object_siblings_and_children(obj->misc_first_child);
4078 obj->misc_first_child = NULL;
4079 }
4080 assert(!obj->first_child);
4081 assert(!obj->memory_first_child);
4082 unlink_and_free_single_object(pobj);
4083 topology->modified = 1;
4084 }
4085 }
4086
4087 /* adjust object nodesets according the given droppednodeset,
4088 * drop object whose nodeset becomes empty and that have no children,
4089 * and propagate PU removal as cpuset changes in parents.
4090 */
4091 static void
restrict_object_by_nodeset(hwloc_topology_t topology,unsigned long flags,hwloc_obj_t * pobj,hwloc_bitmap_t droppedcpuset,hwloc_bitmap_t droppednodeset)4092 restrict_object_by_nodeset(hwloc_topology_t topology, unsigned long flags, hwloc_obj_t *pobj,
4093 hwloc_bitmap_t droppedcpuset, hwloc_bitmap_t droppednodeset)
4094 {
4095 hwloc_obj_t obj = *pobj, child, *pchild;
4096 int modified = 0;
4097
4098 if (hwloc_bitmap_intersects(obj->complete_nodeset, droppednodeset)) {
4099 hwloc_bitmap_andnot(obj->nodeset, obj->nodeset, droppednodeset);
4100 hwloc_bitmap_andnot(obj->complete_nodeset, obj->complete_nodeset, droppednodeset);
4101 modified = 1;
4102 } else {
4103 if ((flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS)
4104 && hwloc_bitmap_iszero(obj->complete_nodeset)) {
4105 /* we're empty, there's a PU below us, it'll be removed this time */
4106 modified = 1;
4107 }
4108 /* cpuset cannot intersect unless nodeset intersects or is empty */
4109 if (droppedcpuset)
4110 assert(!hwloc_bitmap_intersects(obj->complete_cpuset, droppedcpuset)
4111 || hwloc_bitmap_iszero(obj->complete_nodeset));
4112 }
4113 if (droppedcpuset) {
4114 hwloc_bitmap_andnot(obj->cpuset, obj->cpuset, droppedcpuset);
4115 hwloc_bitmap_andnot(obj->complete_cpuset, obj->complete_cpuset, droppedcpuset);
4116 }
4117
4118 if (modified) {
4119 for_each_child_safe(child, obj, pchild)
4120 restrict_object_by_nodeset(topology, flags, pchild, droppedcpuset, droppednodeset);
4121 if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS)
4122 /* cpuset may have changed above where some NUMA nodes were removed.
4123 * if some hwloc_bitmap_first(child->complete_cpuset) changed, children might need to be reordered */
4124 hwloc__reorder_children(obj);
4125
4126 for_each_memory_child_safe(child, obj, pchild)
4127 restrict_object_by_nodeset(topology, flags, pchild, droppedcpuset, droppednodeset);
4128 /* FIXME: we may have to reorder CPU-less groups of NUMA nodes if some of their nodes were removed */
4129
4130 /* Nothing to restrict under I/O or Misc */
4131 }
4132
4133 if (!obj->first_child && !obj->memory_first_child /* arity not updated before connect_children() */
4134 && hwloc_bitmap_iszero(obj->nodeset)
4135 && (obj->type != HWLOC_OBJ_PU || (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS))) {
4136 /* remove object */
4137 hwloc_debug("%s", "\nRemoving object during restrict by nodeset");
4138 hwloc_debug_print_object(0, obj);
4139
4140 if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_IO)) {
4141 hwloc_free_object_siblings_and_children(obj->io_first_child);
4142 obj->io_first_child = NULL;
4143 }
4144 if (!(flags & HWLOC_RESTRICT_FLAG_ADAPT_MISC)) {
4145 hwloc_free_object_siblings_and_children(obj->misc_first_child);
4146 obj->misc_first_child = NULL;
4147 }
4148 assert(!obj->first_child);
4149 assert(!obj->memory_first_child);
4150 unlink_and_free_single_object(pobj);
4151 topology->modified = 1;
4152 }
4153 }
4154
4155 int
hwloc_topology_restrict(struct hwloc_topology * topology,hwloc_const_bitmap_t set,unsigned long flags)4156 hwloc_topology_restrict(struct hwloc_topology *topology, hwloc_const_bitmap_t set, unsigned long flags)
4157 {
4158 hwloc_bitmap_t droppedcpuset, droppednodeset;
4159
4160 if (!topology->is_loaded) {
4161 errno = EINVAL;
4162 return -1;
4163 }
4164 if (topology->adopted_shmem_addr) {
4165 errno = EPERM;
4166 return -1;
4167 }
4168
4169 if (flags & ~(HWLOC_RESTRICT_FLAG_REMOVE_CPULESS
4170 |HWLOC_RESTRICT_FLAG_ADAPT_MISC|HWLOC_RESTRICT_FLAG_ADAPT_IO
4171 |HWLOC_RESTRICT_FLAG_BYNODESET|HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS)) {
4172 errno = EINVAL;
4173 return -1;
4174 }
4175
4176 if (flags & HWLOC_RESTRICT_FLAG_BYNODESET) {
4177 /* cannot use CPULESS with BYNODESET */
4178 if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) {
4179 errno = EINVAL;
4180 return -1;
4181 }
4182 } else {
4183 /* cannot use MEMLESS without BYNODESET */
4184 if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) {
4185 errno = EINVAL;
4186 return -1;
4187 }
4188 }
4189
4190 /* make sure we'll keep something in the topology */
4191 if (((flags & HWLOC_RESTRICT_FLAG_BYNODESET) && !hwloc_bitmap_intersects(set, topology->allowed_nodeset))
4192 || (!(flags & HWLOC_RESTRICT_FLAG_BYNODESET) && !hwloc_bitmap_intersects(set, topology->allowed_cpuset))) {
4193 errno = EINVAL; /* easy failure, just don't touch the topology */
4194 return -1;
4195 }
4196
4197 droppedcpuset = hwloc_bitmap_alloc();
4198 droppednodeset = hwloc_bitmap_alloc();
4199 if (!droppedcpuset || !droppednodeset) {
4200 hwloc_bitmap_free(droppedcpuset);
4201 hwloc_bitmap_free(droppednodeset);
4202 return -1;
4203 }
4204
4205 if (flags & HWLOC_RESTRICT_FLAG_BYNODESET) {
4206 /* nodeset to clear */
4207 hwloc_bitmap_not(droppednodeset, set);
4208 /* cpuset to clear */
4209 if (flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS) {
4210 hwloc_obj_t pu = hwloc_get_obj_by_type(topology, HWLOC_OBJ_PU, 0);
4211 assert(pu);
4212 do {
4213 /* PU will be removed if cpuset gets or was empty */
4214 if (hwloc_bitmap_iszero(pu->cpuset)
4215 || hwloc_bitmap_isincluded(pu->nodeset, droppednodeset))
4216 hwloc_bitmap_set(droppedcpuset, pu->os_index);
4217 pu = pu->next_cousin;
4218 } while (pu);
4219
4220 /* check we're not removing all PUs */
4221 if (hwloc_bitmap_isincluded(topology->allowed_cpuset, droppedcpuset)) {
4222 errno = EINVAL; /* easy failure, just don't touch the topology */
4223 hwloc_bitmap_free(droppedcpuset);
4224 hwloc_bitmap_free(droppednodeset);
4225 return -1;
4226 }
4227 }
4228 /* remove cpuset if empty */
4229 if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_MEMLESS)
4230 || hwloc_bitmap_iszero(droppedcpuset)) {
4231 hwloc_bitmap_free(droppedcpuset);
4232 droppedcpuset = NULL;
4233 }
4234
4235 /* now recurse to filter sets and drop things */
4236 restrict_object_by_nodeset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset);
4237 hwloc_bitmap_andnot(topology->allowed_nodeset, topology->allowed_nodeset, droppednodeset);
4238 if (droppedcpuset)
4239 hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset);
4240
4241 } else {
4242 /* cpuset to clear */
4243 hwloc_bitmap_not(droppedcpuset, set);
4244 /* nodeset to clear */
4245 if (flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS) {
4246 hwloc_obj_t node = hwloc_get_obj_by_type(topology, HWLOC_OBJ_NUMANODE, 0);
4247 assert(node);
4248 do {
4249 /* node will be removed if nodeset gets or was empty */
4250 if (hwloc_bitmap_iszero(node->cpuset)
4251 || hwloc_bitmap_isincluded(node->cpuset, droppedcpuset))
4252 hwloc_bitmap_set(droppednodeset, node->os_index);
4253 node = node->next_cousin;
4254 } while (node);
4255
4256 /* check we're not removing all NUMA nodes */
4257 if (hwloc_bitmap_isincluded(topology->allowed_nodeset, droppednodeset)) {
4258 errno = EINVAL; /* easy failure, just don't touch the topology */
4259 hwloc_bitmap_free(droppedcpuset);
4260 hwloc_bitmap_free(droppednodeset);
4261 return -1;
4262 }
4263 }
4264 /* remove nodeset if empty */
4265 if (!(flags & HWLOC_RESTRICT_FLAG_REMOVE_CPULESS)
4266 || hwloc_bitmap_iszero(droppednodeset)) {
4267 hwloc_bitmap_free(droppednodeset);
4268 droppednodeset = NULL;
4269 }
4270
4271 /* now recurse to filter sets and drop things */
4272 restrict_object_by_cpuset(topology, flags, &topology->levels[0][0], droppedcpuset, droppednodeset);
4273 hwloc_bitmap_andnot(topology->allowed_cpuset, topology->allowed_cpuset, droppedcpuset);
4274 if (droppednodeset)
4275 hwloc_bitmap_andnot(topology->allowed_nodeset, topology->allowed_nodeset, droppednodeset);
4276 }
4277
4278 hwloc_bitmap_free(droppedcpuset);
4279 hwloc_bitmap_free(droppednodeset);
4280
4281 if (hwloc_topology_reconnect(topology, 0) < 0)
4282 goto out;
4283
4284 /* some objects may have disappeared, we need to update distances objs arrays */
4285 hwloc_internal_distances_invalidate_cached_objs(topology);
4286 hwloc_internal_memattrs_need_refresh(topology);
4287
4288 hwloc_filter_levels_keep_structure(topology);
4289 hwloc_propagate_symmetric_subtree(topology, topology->levels[0][0]);
4290 propagate_total_memory(topology->levels[0][0]);
4291 hwloc_internal_cpukinds_restrict(topology);
4292
4293 #ifndef HWLOC_DEBUG
4294 if (getenv("HWLOC_DEBUG_CHECK"))
4295 #endif
4296 hwloc_topology_check(topology);
4297
4298 return 0;
4299
4300 out:
4301 /* unrecoverable failure, re-init the topology */
4302 hwloc_topology_clear(topology);
4303 hwloc_topology_setup_defaults(topology);
4304 return -1;
4305 }
4306
4307 int
hwloc_topology_allow(struct hwloc_topology * topology,hwloc_const_cpuset_t cpuset,hwloc_const_nodeset_t nodeset,unsigned long flags)4308 hwloc_topology_allow(struct hwloc_topology *topology,
4309 hwloc_const_cpuset_t cpuset, hwloc_const_nodeset_t nodeset,
4310 unsigned long flags)
4311 {
4312 if (!topology->is_loaded)
4313 goto einval;
4314
4315 if (topology->adopted_shmem_addr) {
4316 errno = EPERM;
4317 goto error;
4318 }
4319
4320 if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED))
4321 goto einval;
4322
4323 if (flags & ~(HWLOC_ALLOW_FLAG_ALL|HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS|HWLOC_ALLOW_FLAG_CUSTOM))
4324 goto einval;
4325
4326 switch (flags) {
4327 case HWLOC_ALLOW_FLAG_ALL: {
4328 if (cpuset || nodeset)
4329 goto einval;
4330 hwloc_bitmap_copy(topology->allowed_cpuset, hwloc_get_root_obj(topology)->complete_cpuset);
4331 hwloc_bitmap_copy(topology->allowed_nodeset, hwloc_get_root_obj(topology)->complete_nodeset);
4332 break;
4333 }
4334 case HWLOC_ALLOW_FLAG_LOCAL_RESTRICTIONS: {
4335 if (cpuset || nodeset)
4336 goto einval;
4337 if (!topology->is_thissystem)
4338 goto einval;
4339 if (!topology->binding_hooks.get_allowed_resources) {
4340 errno = ENOSYS;
4341 goto error;
4342 }
4343 topology->binding_hooks.get_allowed_resources(topology);
4344 /* make sure the backend returned something sane (Linux cpusets may return offline PUs in some cases) */
4345 hwloc_bitmap_and(topology->allowed_cpuset, topology->allowed_cpuset, hwloc_get_root_obj(topology)->cpuset);
4346 hwloc_bitmap_and(topology->allowed_nodeset, topology->allowed_nodeset, hwloc_get_root_obj(topology)->nodeset);
4347 break;
4348 }
4349 case HWLOC_ALLOW_FLAG_CUSTOM: {
4350 if (cpuset) {
4351 /* keep the intersection with the full topology cpuset, if not empty */
4352 if (!hwloc_bitmap_intersects(hwloc_get_root_obj(topology)->cpuset, cpuset))
4353 goto einval;
4354 hwloc_bitmap_and(topology->allowed_cpuset, hwloc_get_root_obj(topology)->cpuset, cpuset);
4355 }
4356 if (nodeset) {
4357 /* keep the intersection with the full topology nodeset, if not empty */
4358 if (!hwloc_bitmap_intersects(hwloc_get_root_obj(topology)->nodeset, nodeset))
4359 goto einval;
4360 hwloc_bitmap_and(topology->allowed_nodeset, hwloc_get_root_obj(topology)->nodeset, nodeset);
4361 }
4362 break;
4363 }
4364 default:
4365 goto einval;
4366 }
4367
4368 return 0;
4369
4370 einval:
4371 errno = EINVAL;
4372 error:
4373 return -1;
4374 }
4375
4376 int
hwloc_topology_refresh(struct hwloc_topology * topology)4377 hwloc_topology_refresh(struct hwloc_topology *topology)
4378 {
4379 hwloc_internal_cpukinds_rank(topology);
4380 hwloc_internal_distances_refresh(topology);
4381 hwloc_internal_memattrs_refresh(topology);
4382 return 0;
4383 }
4384
4385 int
hwloc_topology_is_thissystem(struct hwloc_topology * topology)4386 hwloc_topology_is_thissystem(struct hwloc_topology *topology)
4387 {
4388 return topology->is_thissystem;
4389 }
4390
4391 int
hwloc_topology_get_depth(struct hwloc_topology * topology)4392 hwloc_topology_get_depth(struct hwloc_topology *topology)
4393 {
4394 return (int) topology->nb_levels;
4395 }
4396
4397 const struct hwloc_topology_support *
hwloc_topology_get_support(struct hwloc_topology * topology)4398 hwloc_topology_get_support(struct hwloc_topology * topology)
4399 {
4400 return &topology->support;
4401 }
4402
hwloc_topology_set_userdata(struct hwloc_topology * topology,const void * userdata)4403 void hwloc_topology_set_userdata(struct hwloc_topology * topology, const void *userdata)
4404 {
4405 topology->userdata = (void *) userdata;
4406 }
4407
hwloc_topology_get_userdata(struct hwloc_topology * topology)4408 void * hwloc_topology_get_userdata(struct hwloc_topology * topology)
4409 {
4410 return topology->userdata;
4411 }
4412
4413 hwloc_const_cpuset_t
hwloc_topology_get_complete_cpuset(hwloc_topology_t topology)4414 hwloc_topology_get_complete_cpuset(hwloc_topology_t topology)
4415 {
4416 return hwloc_get_root_obj(topology)->complete_cpuset;
4417 }
4418
4419 hwloc_const_cpuset_t
hwloc_topology_get_topology_cpuset(hwloc_topology_t topology)4420 hwloc_topology_get_topology_cpuset(hwloc_topology_t topology)
4421 {
4422 return hwloc_get_root_obj(topology)->cpuset;
4423 }
4424
4425 hwloc_const_cpuset_t
hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology)4426 hwloc_topology_get_allowed_cpuset(hwloc_topology_t topology)
4427 {
4428 return topology->allowed_cpuset;
4429 }
4430
4431 hwloc_const_nodeset_t
hwloc_topology_get_complete_nodeset(hwloc_topology_t topology)4432 hwloc_topology_get_complete_nodeset(hwloc_topology_t topology)
4433 {
4434 return hwloc_get_root_obj(topology)->complete_nodeset;
4435 }
4436
4437 hwloc_const_nodeset_t
hwloc_topology_get_topology_nodeset(hwloc_topology_t topology)4438 hwloc_topology_get_topology_nodeset(hwloc_topology_t topology)
4439 {
4440 return hwloc_get_root_obj(topology)->nodeset;
4441 }
4442
4443 hwloc_const_nodeset_t
hwloc_topology_get_allowed_nodeset(hwloc_topology_t topology)4444 hwloc_topology_get_allowed_nodeset(hwloc_topology_t topology)
4445 {
4446 return topology->allowed_nodeset;
4447 }
4448
4449
4450 /****************
4451 * Debug Checks *
4452 ****************/
4453
4454 #ifndef NDEBUG /* assert only enabled if !NDEBUG */
4455
4456 static void
hwloc__check_child_siblings(hwloc_obj_t parent,hwloc_obj_t * array,unsigned arity,unsigned i,hwloc_obj_t child,hwloc_obj_t prev)4457 hwloc__check_child_siblings(hwloc_obj_t parent, hwloc_obj_t *array,
4458 unsigned arity, unsigned i,
4459 hwloc_obj_t child, hwloc_obj_t prev)
4460 {
4461 assert(child->parent == parent);
4462
4463 assert(child->sibling_rank == i);
4464 if (array)
4465 assert(child == array[i]);
4466
4467 if (prev)
4468 assert(prev->next_sibling == child);
4469 assert(child->prev_sibling == prev);
4470
4471 if (!i)
4472 assert(child->prev_sibling == NULL);
4473 else
4474 assert(child->prev_sibling != NULL);
4475
4476 if (i == arity-1)
4477 assert(child->next_sibling == NULL);
4478 else
4479 assert(child->next_sibling != NULL);
4480 }
4481
4482 static void
4483 hwloc__check_object(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t obj);
4484
4485 /* check children between a parent object */
4486 static void
hwloc__check_normal_children(hwloc_topology_t topology,hwloc_bitmap_t gp_indexes,hwloc_obj_t parent)4487 hwloc__check_normal_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent)
4488 {
4489 hwloc_obj_t child, prev;
4490 unsigned j;
4491
4492 if (!parent->arity) {
4493 /* check whether that parent has no children for real */
4494 assert(!parent->children);
4495 assert(!parent->first_child);
4496 assert(!parent->last_child);
4497 return;
4498 }
4499 /* check whether that parent has children for real */
4500 assert(parent->children);
4501 assert(parent->first_child);
4502 assert(parent->last_child);
4503
4504 /* sibling checks */
4505 for(prev = NULL, child = parent->first_child, j = 0;
4506 child;
4507 prev = child, child = child->next_sibling, j++) {
4508 /* normal child */
4509 assert(hwloc__obj_type_is_normal(child->type));
4510 /* check depth */
4511 assert(child->depth > parent->depth);
4512 /* check siblings */
4513 hwloc__check_child_siblings(parent, parent->children, parent->arity, j, child, prev);
4514 /* recurse */
4515 hwloc__check_object(topology, gp_indexes, child);
4516 }
4517 /* check arity */
4518 assert(j == parent->arity);
4519
4520 assert(parent->first_child == parent->children[0]);
4521 assert(parent->last_child == parent->children[parent->arity-1]);
4522
4523 /* no normal children below a PU */
4524 if (parent->type == HWLOC_OBJ_PU)
4525 assert(!parent->arity);
4526 }
4527
4528 static void
hwloc__check_children_cpusets(hwloc_topology_t topology __hwloc_attribute_unused,hwloc_obj_t obj)4529 hwloc__check_children_cpusets(hwloc_topology_t topology __hwloc_attribute_unused, hwloc_obj_t obj)
4530 {
4531 /* we already checked in the caller that objects have either all sets or none */
4532 hwloc_obj_t child;
4533 int prev_first, prev_empty;
4534
4535 if (obj->type == HWLOC_OBJ_PU) {
4536 /* PU cpuset is just itself, with no normal children */
4537 assert(hwloc_bitmap_weight(obj->cpuset) == 1);
4538 assert(hwloc_bitmap_first(obj->cpuset) == (int) obj->os_index);
4539 assert(hwloc_bitmap_weight(obj->complete_cpuset) == 1);
4540 assert(hwloc_bitmap_first(obj->complete_cpuset) == (int) obj->os_index);
4541 if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) {
4542 assert(hwloc_bitmap_isset(topology->allowed_cpuset, (int) obj->os_index));
4543 }
4544 assert(!obj->arity);
4545 } else if (hwloc__obj_type_is_memory(obj->type)) {
4546 /* memory object cpuset is equal to its parent */
4547 assert(hwloc_bitmap_isequal(obj->parent->cpuset, obj->cpuset));
4548 assert(!obj->arity);
4549 } else if (!hwloc__obj_type_is_special(obj->type)) {
4550 hwloc_bitmap_t set;
4551 /* other obj cpuset is an exclusive OR of normal children, except for PUs */
4552 set = hwloc_bitmap_alloc();
4553 for_each_child(child, obj) {
4554 assert(!hwloc_bitmap_intersects(set, child->cpuset));
4555 hwloc_bitmap_or(set, set, child->cpuset);
4556 }
4557 assert(hwloc_bitmap_isequal(set, obj->cpuset));
4558 hwloc_bitmap_free(set);
4559 }
4560
4561 /* check that memory children have same cpuset */
4562 for_each_memory_child(child, obj)
4563 assert(hwloc_bitmap_isequal(obj->cpuset, child->cpuset));
4564
4565 /* check that children complete_cpusets are properly ordered, empty ones may be anywhere
4566 * (can be wrong for main cpuset since removed PUs can break the ordering).
4567 */
4568 prev_first = -1; /* -1 works fine with first comparisons below */
4569 prev_empty = 0; /* no empty cpuset in previous children */
4570 for_each_child(child, obj) {
4571 int first = hwloc_bitmap_first(child->complete_cpuset);
4572 if (first >= 0) {
4573 assert(!prev_empty); /* no objects with CPU after objects without CPU */
4574 assert(prev_first < first);
4575 } else {
4576 prev_empty = 1;
4577 }
4578 prev_first = first;
4579 }
4580 }
4581
4582 static void
hwloc__check_memory_children(hwloc_topology_t topology,hwloc_bitmap_t gp_indexes,hwloc_obj_t parent)4583 hwloc__check_memory_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent)
4584 {
4585 unsigned j;
4586 hwloc_obj_t child, prev;
4587
4588 if (!parent->memory_arity) {
4589 /* check whether that parent has no children for real */
4590 assert(!parent->memory_first_child);
4591 return;
4592 }
4593 /* check whether that parent has children for real */
4594 assert(parent->memory_first_child);
4595
4596 for(prev = NULL, child = parent->memory_first_child, j = 0;
4597 child;
4598 prev = child, child = child->next_sibling, j++) {
4599 assert(hwloc__obj_type_is_memory(child->type));
4600 /* check siblings */
4601 hwloc__check_child_siblings(parent, NULL, parent->memory_arity, j, child, prev);
4602 /* only Memory and Misc children, recurse */
4603 assert(!child->first_child);
4604 assert(!child->io_first_child);
4605 hwloc__check_object(topology, gp_indexes, child);
4606 }
4607 /* check arity */
4608 assert(j == parent->memory_arity);
4609
4610 /* no memory children below a NUMA node */
4611 if (parent->type == HWLOC_OBJ_NUMANODE)
4612 assert(!parent->memory_arity);
4613 }
4614
4615 static void
hwloc__check_io_children(hwloc_topology_t topology,hwloc_bitmap_t gp_indexes,hwloc_obj_t parent)4616 hwloc__check_io_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent)
4617 {
4618 unsigned j;
4619 hwloc_obj_t child, prev;
4620
4621 if (!parent->io_arity) {
4622 /* check whether that parent has no children for real */
4623 assert(!parent->io_first_child);
4624 return;
4625 }
4626 /* check whether that parent has children for real */
4627 assert(parent->io_first_child);
4628
4629 for(prev = NULL, child = parent->io_first_child, j = 0;
4630 child;
4631 prev = child, child = child->next_sibling, j++) {
4632 /* all children must be I/O */
4633 assert(hwloc__obj_type_is_io(child->type));
4634 /* check siblings */
4635 hwloc__check_child_siblings(parent, NULL, parent->io_arity, j, child, prev);
4636 /* only I/O and Misc children, recurse */
4637 assert(!child->first_child);
4638 assert(!child->memory_first_child);
4639 hwloc__check_object(topology, gp_indexes, child);
4640 }
4641 /* check arity */
4642 assert(j == parent->io_arity);
4643 }
4644
4645 static void
hwloc__check_misc_children(hwloc_topology_t topology,hwloc_bitmap_t gp_indexes,hwloc_obj_t parent)4646 hwloc__check_misc_children(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t parent)
4647 {
4648 unsigned j;
4649 hwloc_obj_t child, prev;
4650
4651 if (!parent->misc_arity) {
4652 /* check whether that parent has no children for real */
4653 assert(!parent->misc_first_child);
4654 return;
4655 }
4656 /* check whether that parent has children for real */
4657 assert(parent->misc_first_child);
4658
4659 for(prev = NULL, child = parent->misc_first_child, j = 0;
4660 child;
4661 prev = child, child = child->next_sibling, j++) {
4662 /* all children must be Misc */
4663 assert(child->type == HWLOC_OBJ_MISC);
4664 /* check siblings */
4665 hwloc__check_child_siblings(parent, NULL, parent->misc_arity, j, child, prev);
4666 /* only Misc children, recurse */
4667 assert(!child->first_child);
4668 assert(!child->memory_first_child);
4669 assert(!child->io_first_child);
4670 hwloc__check_object(topology, gp_indexes, child);
4671 }
4672 /* check arity */
4673 assert(j == parent->misc_arity);
4674 }
4675
4676 static void
hwloc__check_object(hwloc_topology_t topology,hwloc_bitmap_t gp_indexes,hwloc_obj_t obj)4677 hwloc__check_object(hwloc_topology_t topology, hwloc_bitmap_t gp_indexes, hwloc_obj_t obj)
4678 {
4679 hwloc_uint64_t total_memory;
4680 hwloc_obj_t child;
4681
4682 assert(!hwloc_bitmap_isset(gp_indexes, obj->gp_index));
4683 hwloc_bitmap_set(gp_indexes, obj->gp_index);
4684
4685 HWLOC_BUILD_ASSERT(HWLOC_OBJ_TYPE_MIN == 0);
4686 assert((unsigned) obj->type < HWLOC_OBJ_TYPE_MAX);
4687
4688 assert(hwloc_filter_check_keep_object(topology, obj));
4689
4690 /* check that sets and depth */
4691 if (hwloc__obj_type_is_special(obj->type)) {
4692 assert(!obj->cpuset);
4693 if (obj->type == HWLOC_OBJ_BRIDGE)
4694 assert(obj->depth == HWLOC_TYPE_DEPTH_BRIDGE);
4695 else if (obj->type == HWLOC_OBJ_PCI_DEVICE)
4696 assert(obj->depth == HWLOC_TYPE_DEPTH_PCI_DEVICE);
4697 else if (obj->type == HWLOC_OBJ_OS_DEVICE)
4698 assert(obj->depth == HWLOC_TYPE_DEPTH_OS_DEVICE);
4699 else if (obj->type == HWLOC_OBJ_MISC)
4700 assert(obj->depth == HWLOC_TYPE_DEPTH_MISC);
4701 } else {
4702 assert(obj->cpuset);
4703 if (obj->type == HWLOC_OBJ_NUMANODE)
4704 assert(obj->depth == HWLOC_TYPE_DEPTH_NUMANODE);
4705 else if (obj->type == HWLOC_OBJ_MEMCACHE)
4706 assert(obj->depth == HWLOC_TYPE_DEPTH_MEMCACHE);
4707 else
4708 assert(obj->depth >= 0);
4709 }
4710
4711 /* group depth cannot be -1 anymore in v2.0+ */
4712 if (obj->type == HWLOC_OBJ_GROUP) {
4713 assert(obj->attr->group.depth != (unsigned) -1);
4714 }
4715
4716 /* there's other cpusets and nodesets if and only if there's a main cpuset */
4717 assert(!!obj->cpuset == !!obj->complete_cpuset);
4718 assert(!!obj->cpuset == !!obj->nodeset);
4719 assert(!!obj->nodeset == !!obj->complete_nodeset);
4720
4721 /* check that complete/inline sets are larger than the main sets */
4722 if (obj->cpuset) {
4723 assert(hwloc_bitmap_isincluded(obj->cpuset, obj->complete_cpuset));
4724 assert(hwloc_bitmap_isincluded(obj->nodeset, obj->complete_nodeset));
4725 }
4726
4727 /* check cache type/depth vs type */
4728 if (hwloc__obj_type_is_cache(obj->type)) {
4729 if (hwloc__obj_type_is_icache(obj->type))
4730 assert(obj->attr->cache.type == HWLOC_OBJ_CACHE_INSTRUCTION);
4731 else if (hwloc__obj_type_is_dcache(obj->type))
4732 assert(obj->attr->cache.type == HWLOC_OBJ_CACHE_DATA
4733 || obj->attr->cache.type == HWLOC_OBJ_CACHE_UNIFIED);
4734 else
4735 assert(0);
4736 assert(hwloc_cache_type_by_depth_type(obj->attr->cache.depth, obj->attr->cache.type) == obj->type);
4737 }
4738
4739 /* check total memory */
4740 total_memory = 0;
4741 if (obj->type == HWLOC_OBJ_NUMANODE)
4742 total_memory += obj->attr->numanode.local_memory;
4743 for_each_child(child, obj) {
4744 total_memory += child->total_memory;
4745 }
4746 for_each_memory_child(child, obj) {
4747 total_memory += child->total_memory;
4748 }
4749 assert(total_memory == obj->total_memory);
4750
4751 /* check children */
4752 hwloc__check_normal_children(topology, gp_indexes, obj);
4753 hwloc__check_memory_children(topology, gp_indexes, obj);
4754 hwloc__check_io_children(topology, gp_indexes, obj);
4755 hwloc__check_misc_children(topology, gp_indexes, obj);
4756 hwloc__check_children_cpusets(topology, obj);
4757 /* nodesets are checked during another recursion with state below */
4758 }
4759
4760 static void
hwloc__check_nodesets(hwloc_topology_t topology,hwloc_obj_t obj,hwloc_bitmap_t parentset)4761 hwloc__check_nodesets(hwloc_topology_t topology, hwloc_obj_t obj, hwloc_bitmap_t parentset)
4762 {
4763 hwloc_obj_t child;
4764 int prev_first;
4765
4766 if (obj->type == HWLOC_OBJ_NUMANODE) {
4767 /* NUMANODE nodeset is just itself, with no memory/normal children */
4768 assert(hwloc_bitmap_weight(obj->nodeset) == 1);
4769 assert(hwloc_bitmap_first(obj->nodeset) == (int) obj->os_index);
4770 assert(hwloc_bitmap_weight(obj->complete_nodeset) == 1);
4771 assert(hwloc_bitmap_first(obj->complete_nodeset) == (int) obj->os_index);
4772 if (!(topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED)) {
4773 assert(hwloc_bitmap_isset(topology->allowed_nodeset, (int) obj->os_index));
4774 }
4775 assert(!obj->arity);
4776 assert(!obj->memory_arity);
4777 assert(hwloc_bitmap_isincluded(obj->nodeset, parentset));
4778 } else {
4779 hwloc_bitmap_t myset;
4780 hwloc_bitmap_t childset;
4781
4782 /* the local nodeset is an exclusive OR of memory children */
4783 myset = hwloc_bitmap_alloc();
4784 for_each_memory_child(child, obj) {
4785 assert(!hwloc_bitmap_intersects(myset, child->nodeset));
4786 hwloc_bitmap_or(myset, myset, child->nodeset);
4787 }
4788 /* the local nodeset cannot intersect with parents' local nodeset */
4789 assert(!hwloc_bitmap_intersects(myset, parentset));
4790 hwloc_bitmap_or(parentset, parentset, myset);
4791 hwloc_bitmap_free(myset);
4792 /* parentset now contains parent+local contribution */
4793
4794 /* for each children, recurse to check/get its contribution */
4795 childset = hwloc_bitmap_alloc();
4796 for_each_child(child, obj) {
4797 hwloc_bitmap_t set = hwloc_bitmap_dup(parentset); /* don't touch parentset, we don't want to propagate the first child contribution to other children */
4798 hwloc__check_nodesets(topology, child, set);
4799 /* extract this child contribution */
4800 hwloc_bitmap_andnot(set, set, parentset);
4801 /* save it */
4802 assert(!hwloc_bitmap_intersects(childset, set));
4803 hwloc_bitmap_or(childset, childset, set);
4804 hwloc_bitmap_free(set);
4805 }
4806 /* combine child contribution into parentset */
4807 assert(!hwloc_bitmap_intersects(parentset, childset));
4808 hwloc_bitmap_or(parentset, parentset, childset);
4809 hwloc_bitmap_free(childset);
4810 /* now check that our nodeset is combination of parent, local and children */
4811 assert(hwloc_bitmap_isequal(obj->nodeset, parentset));
4812 }
4813
4814 /* check that children complete_nodesets are properly ordered, empty ones may be anywhere
4815 * (can be wrong for main nodeset since removed PUs can break the ordering).
4816 */
4817 prev_first = -1; /* -1 works fine with first comparisons below */
4818 for_each_memory_child(child, obj) {
4819 int first = hwloc_bitmap_first(child->complete_nodeset);
4820 assert(prev_first < first);
4821 prev_first = first;
4822 }
4823 }
4824
4825 static void
hwloc__check_level(struct hwloc_topology * topology,int depth,hwloc_obj_t first,hwloc_obj_t last)4826 hwloc__check_level(struct hwloc_topology *topology, int depth,
4827 hwloc_obj_t first, hwloc_obj_t last)
4828 {
4829 unsigned width = hwloc_get_nbobjs_by_depth(topology, depth);
4830 struct hwloc_obj *prev = NULL;
4831 hwloc_obj_t obj;
4832 unsigned j;
4833
4834 /* check each object of the level */
4835 for(j=0; j<width; j++) {
4836 obj = hwloc_get_obj_by_depth(topology, depth, j);
4837 /* check that the object is corrected placed horizontally and vertically */
4838 assert(obj);
4839 assert(obj->depth == depth);
4840 assert(obj->logical_index == j);
4841 /* check that all objects in the level have the same type */
4842 if (prev) {
4843 assert(hwloc_type_cmp(obj, prev) == HWLOC_OBJ_EQUAL);
4844 assert(prev->next_cousin == obj);
4845 }
4846 assert(obj->prev_cousin == prev);
4847
4848 /* check that PUs and NUMA nodes have correct cpuset/nodeset */
4849 if (obj->type == HWLOC_OBJ_NUMANODE) {
4850 assert(hwloc_bitmap_weight(obj->complete_nodeset) == 1);
4851 assert(hwloc_bitmap_first(obj->complete_nodeset) == (int) obj->os_index);
4852 }
4853 prev = obj;
4854 }
4855 if (prev)
4856 assert(prev->next_cousin == NULL);
4857
4858 if (width) {
4859 /* check first object of the level */
4860 obj = hwloc_get_obj_by_depth(topology, depth, 0);
4861 assert(obj);
4862 assert(!obj->prev_cousin);
4863 /* check type */
4864 assert(hwloc_get_depth_type(topology, depth) == obj->type);
4865 assert(depth == hwloc_get_type_depth(topology, obj->type)
4866 || HWLOC_TYPE_DEPTH_MULTIPLE == hwloc_get_type_depth(topology, obj->type));
4867 /* check last object of the level */
4868 obj = hwloc_get_obj_by_depth(topology, depth, width-1);
4869 assert(obj);
4870 assert(!obj->next_cousin);
4871 }
4872
4873 if (depth < 0) {
4874 assert(first == hwloc_get_obj_by_depth(topology, depth, 0));
4875 assert(last == hwloc_get_obj_by_depth(topology, depth, width-1));
4876 } else {
4877 assert(!first);
4878 assert(!last);
4879 }
4880
4881 /* check last+1 object of the level */
4882 obj = hwloc_get_obj_by_depth(topology, depth, width);
4883 assert(!obj);
4884 }
4885
4886 /* check a whole topology structure */
4887 void
hwloc_topology_check(struct hwloc_topology * topology)4888 hwloc_topology_check(struct hwloc_topology *topology)
4889 {
4890 struct hwloc_obj *obj;
4891 hwloc_bitmap_t gp_indexes, set;
4892 hwloc_obj_type_t type;
4893 unsigned i;
4894 int j, depth;
4895
4896 /* make sure we can use ranges to check types */
4897
4898 /* hwloc__obj_type_is_{,d,i}cache() want cache types to be ordered like this */
4899 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L2CACHE == HWLOC_OBJ_L1CACHE + 1);
4900 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L3CACHE == HWLOC_OBJ_L2CACHE + 1);
4901 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L4CACHE == HWLOC_OBJ_L3CACHE + 1);
4902 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L5CACHE == HWLOC_OBJ_L4CACHE + 1);
4903 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L1ICACHE == HWLOC_OBJ_L5CACHE + 1);
4904 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L2ICACHE == HWLOC_OBJ_L1ICACHE + 1);
4905 HWLOC_BUILD_ASSERT(HWLOC_OBJ_L3ICACHE == HWLOC_OBJ_L2ICACHE + 1);
4906
4907 /* hwloc__obj_type_is_normal(), hwloc__obj_type_is_memory(), hwloc__obj_type_is_io(), hwloc__obj_type_is_special()
4908 * and hwloc_reset_normal_type_depths()
4909 * want special types to be ordered like this, after all normal types.
4910 */
4911 HWLOC_BUILD_ASSERT(HWLOC_OBJ_NUMANODE + 1 == HWLOC_OBJ_BRIDGE);
4912 HWLOC_BUILD_ASSERT(HWLOC_OBJ_BRIDGE + 1 == HWLOC_OBJ_PCI_DEVICE);
4913 HWLOC_BUILD_ASSERT(HWLOC_OBJ_PCI_DEVICE + 1 == HWLOC_OBJ_OS_DEVICE);
4914 HWLOC_BUILD_ASSERT(HWLOC_OBJ_OS_DEVICE + 1 == HWLOC_OBJ_MISC);
4915 HWLOC_BUILD_ASSERT(HWLOC_OBJ_MISC + 1 == HWLOC_OBJ_MEMCACHE);
4916 HWLOC_BUILD_ASSERT(HWLOC_OBJ_MEMCACHE + 1 == HWLOC_OBJ_DIE);
4917 HWLOC_BUILD_ASSERT(HWLOC_OBJ_DIE + 1 == HWLOC_OBJ_TYPE_MAX);
4918
4919 /* make sure order and priority arrays have the right size */
4920 HWLOC_BUILD_ASSERT(sizeof(obj_type_order)/sizeof(*obj_type_order) == HWLOC_OBJ_TYPE_MAX);
4921 HWLOC_BUILD_ASSERT(sizeof(obj_order_type)/sizeof(*obj_order_type) == HWLOC_OBJ_TYPE_MAX);
4922 HWLOC_BUILD_ASSERT(sizeof(obj_type_priority)/sizeof(*obj_type_priority) == HWLOC_OBJ_TYPE_MAX);
4923
4924 /* make sure group are not entirely ignored */
4925 assert(topology->type_filter[HWLOC_OBJ_GROUP] != HWLOC_TYPE_FILTER_KEEP_ALL);
4926
4927 /* make sure order arrays are coherent */
4928 for(type=HWLOC_OBJ_TYPE_MIN; type<HWLOC_OBJ_TYPE_MAX; type++)
4929 assert(obj_order_type[obj_type_order[type]] == type);
4930 for(i=HWLOC_OBJ_TYPE_MIN; i<HWLOC_OBJ_TYPE_MAX; i++)
4931 assert(obj_type_order[obj_order_type[i]] == i);
4932
4933 depth = hwloc_topology_get_depth(topology);
4934
4935 assert(!topology->modified);
4936
4937 /* check that first level is Machine.
4938 * Root object cannot be ignored. And Machine can only be merged into PU,
4939 * but there must be a NUMA node below Machine, and it cannot be below PU.
4940 */
4941 assert(hwloc_get_depth_type(topology, 0) == HWLOC_OBJ_MACHINE);
4942
4943 /* check that last level is PU and that it doesn't have memory */
4944 assert(hwloc_get_depth_type(topology, depth-1) == HWLOC_OBJ_PU);
4945 assert(hwloc_get_nbobjs_by_depth(topology, depth-1) > 0);
4946 for(i=0; i<hwloc_get_nbobjs_by_depth(topology, depth-1); i++) {
4947 obj = hwloc_get_obj_by_depth(topology, depth-1, i);
4948 assert(obj);
4949 assert(obj->type == HWLOC_OBJ_PU);
4950 assert(!obj->memory_first_child);
4951 }
4952 /* check that other levels are not PU or Machine */
4953 for(j=1; j<depth-1; j++) {
4954 assert(hwloc_get_depth_type(topology, j) != HWLOC_OBJ_PU);
4955 assert(hwloc_get_depth_type(topology, j) != HWLOC_OBJ_MACHINE);
4956 }
4957
4958 /* check normal levels */
4959 for(j=0; j<depth; j++) {
4960 int d;
4961 type = hwloc_get_depth_type(topology, j);
4962 assert(type != HWLOC_OBJ_NUMANODE);
4963 assert(type != HWLOC_OBJ_MEMCACHE);
4964 assert(type != HWLOC_OBJ_PCI_DEVICE);
4965 assert(type != HWLOC_OBJ_BRIDGE);
4966 assert(type != HWLOC_OBJ_OS_DEVICE);
4967 assert(type != HWLOC_OBJ_MISC);
4968 d = hwloc_get_type_depth(topology, type);
4969 assert(d == j || d == HWLOC_TYPE_DEPTH_MULTIPLE);
4970 }
4971
4972 /* check type depths, even if there's no such level */
4973 for(type=HWLOC_OBJ_TYPE_MIN; type<HWLOC_OBJ_TYPE_MAX; type++) {
4974 int d;
4975 d = hwloc_get_type_depth(topology, type);
4976 if (type == HWLOC_OBJ_NUMANODE) {
4977 assert(d == HWLOC_TYPE_DEPTH_NUMANODE);
4978 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_NUMANODE);
4979 } else if (type == HWLOC_OBJ_MEMCACHE) {
4980 assert(d == HWLOC_TYPE_DEPTH_MEMCACHE);
4981 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_MEMCACHE);
4982 } else if (type == HWLOC_OBJ_BRIDGE) {
4983 assert(d == HWLOC_TYPE_DEPTH_BRIDGE);
4984 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_BRIDGE);
4985 } else if (type == HWLOC_OBJ_PCI_DEVICE) {
4986 assert(d == HWLOC_TYPE_DEPTH_PCI_DEVICE);
4987 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_PCI_DEVICE);
4988 } else if (type == HWLOC_OBJ_OS_DEVICE) {
4989 assert(d == HWLOC_TYPE_DEPTH_OS_DEVICE);
4990 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_OS_DEVICE);
4991 } else if (type == HWLOC_OBJ_MISC) {
4992 assert(d == HWLOC_TYPE_DEPTH_MISC);
4993 assert(hwloc_get_depth_type(topology, d) == HWLOC_OBJ_MISC);
4994 } else {
4995 assert(d >=0 || d == HWLOC_TYPE_DEPTH_UNKNOWN || d == HWLOC_TYPE_DEPTH_MULTIPLE);
4996 }
4997 }
4998
4999 /* top-level specific checks */
5000 assert(hwloc_get_nbobjs_by_depth(topology, 0) == 1);
5001 obj = hwloc_get_root_obj(topology);
5002 assert(obj);
5003 assert(!obj->parent);
5004 assert(obj->cpuset);
5005 assert(!obj->depth);
5006
5007 /* check that allowed sets are larger than the main sets */
5008 if (topology->flags & HWLOC_TOPOLOGY_FLAG_INCLUDE_DISALLOWED) {
5009 assert(hwloc_bitmap_isincluded(topology->allowed_cpuset, obj->cpuset));
5010 assert(hwloc_bitmap_isincluded(topology->allowed_nodeset, obj->nodeset));
5011 } else {
5012 assert(hwloc_bitmap_isequal(topology->allowed_cpuset, obj->cpuset));
5013 assert(hwloc_bitmap_isequal(topology->allowed_nodeset, obj->nodeset));
5014 }
5015
5016 /* check each level */
5017 for(j=0; j<depth; j++)
5018 hwloc__check_level(topology, j, NULL, NULL);
5019 for(j=0; j<HWLOC_NR_SLEVELS; j++)
5020 hwloc__check_level(topology, HWLOC_SLEVEL_TO_DEPTH(j), topology->slevels[j].first, topology->slevels[j].last);
5021
5022 /* recurse and check the tree of children, and type-specific checks */
5023 gp_indexes = hwloc_bitmap_alloc(); /* TODO prealloc to topology->next_gp_index */
5024 hwloc__check_object(topology, gp_indexes, obj);
5025 hwloc_bitmap_free(gp_indexes);
5026
5027 /* recurse and check the nodesets of children */
5028 set = hwloc_bitmap_alloc();
5029 hwloc__check_nodesets(topology, obj, set);
5030 hwloc_bitmap_free(set);
5031 }
5032
5033 #else /* NDEBUG */
5034
5035 void
hwloc_topology_check(struct hwloc_topology * topology __hwloc_attribute_unused)5036 hwloc_topology_check(struct hwloc_topology *topology __hwloc_attribute_unused)
5037 {
5038 }
5039
5040 #endif /* NDEBUG */
5041