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