1 /*
2 * Copyright © 2013-2020 Inria. All rights reserved.
3 * See COPYING in top-level directory.
4 */
5
6 #include "private/autogen/config.h"
7 #include "private/private.h"
8 #include "private/misc.h"
9
hwloc_topology_diff_destroy(hwloc_topology_diff_t diff)10 int hwloc_topology_diff_destroy(hwloc_topology_diff_t diff)
11 {
12 hwloc_topology_diff_t next;
13 while (diff) {
14 next = diff->generic.next;
15 switch (diff->generic.type) {
16 default:
17 break;
18 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR:
19 switch (diff->obj_attr.diff.generic.type) {
20 default:
21 break;
22 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME:
23 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO:
24 free(diff->obj_attr.diff.string.name);
25 free(diff->obj_attr.diff.string.oldvalue);
26 free(diff->obj_attr.diff.string.newvalue);
27 break;
28 }
29 break;
30 }
31 free(diff);
32 diff = next;
33 }
34 return 0;
35 }
36
37 /************************
38 * Computing diffs
39 */
40
hwloc_append_diff(hwloc_topology_diff_t newdiff,hwloc_topology_diff_t * firstdiffp,hwloc_topology_diff_t * lastdiffp)41 static void hwloc_append_diff(hwloc_topology_diff_t newdiff,
42 hwloc_topology_diff_t *firstdiffp,
43 hwloc_topology_diff_t *lastdiffp)
44 {
45 if (*firstdiffp)
46 (*lastdiffp)->generic.next = newdiff;
47 else
48 *firstdiffp = newdiff;
49 *lastdiffp = newdiff;
50 newdiff->generic.next = NULL;
51 }
52
hwloc_append_diff_too_complex(hwloc_obj_t obj1,hwloc_topology_diff_t * firstdiffp,hwloc_topology_diff_t * lastdiffp)53 static int hwloc_append_diff_too_complex(hwloc_obj_t obj1,
54 hwloc_topology_diff_t *firstdiffp,
55 hwloc_topology_diff_t *lastdiffp)
56 {
57 hwloc_topology_diff_t newdiff;
58 newdiff = malloc(sizeof(*newdiff));
59 if (!newdiff)
60 return -1;
61
62 newdiff->too_complex.type = HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX;
63 newdiff->too_complex.obj_depth = obj1->depth;
64 newdiff->too_complex.obj_index = obj1->logical_index;
65 hwloc_append_diff(newdiff, firstdiffp, lastdiffp);
66 return 0;
67 }
68
hwloc_append_diff_obj_attr_string(hwloc_obj_t obj,hwloc_topology_diff_obj_attr_type_t type,const char * name,const char * oldvalue,const char * newvalue,hwloc_topology_diff_t * firstdiffp,hwloc_topology_diff_t * lastdiffp)69 static int hwloc_append_diff_obj_attr_string(hwloc_obj_t obj,
70 hwloc_topology_diff_obj_attr_type_t type,
71 const char *name,
72 const char *oldvalue,
73 const char *newvalue,
74 hwloc_topology_diff_t *firstdiffp,
75 hwloc_topology_diff_t *lastdiffp)
76 {
77 hwloc_topology_diff_t newdiff;
78 newdiff = malloc(sizeof(*newdiff));
79 if (!newdiff)
80 return -1;
81
82 newdiff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR;
83 newdiff->obj_attr.obj_depth = obj->depth;
84 newdiff->obj_attr.obj_index = obj->logical_index;
85 newdiff->obj_attr.diff.string.type = type;
86 newdiff->obj_attr.diff.string.name = name ? strdup(name) : NULL;
87 newdiff->obj_attr.diff.string.oldvalue = oldvalue ? strdup(oldvalue) : NULL;
88 newdiff->obj_attr.diff.string.newvalue = newvalue ? strdup(newvalue) : NULL;
89 hwloc_append_diff(newdiff, firstdiffp, lastdiffp);
90 return 0;
91 }
92
hwloc_append_diff_obj_attr_uint64(hwloc_obj_t obj,hwloc_topology_diff_obj_attr_type_t type,hwloc_uint64_t idx,hwloc_uint64_t oldvalue,hwloc_uint64_t newvalue,hwloc_topology_diff_t * firstdiffp,hwloc_topology_diff_t * lastdiffp)93 static int hwloc_append_diff_obj_attr_uint64(hwloc_obj_t obj,
94 hwloc_topology_diff_obj_attr_type_t type,
95 hwloc_uint64_t idx,
96 hwloc_uint64_t oldvalue,
97 hwloc_uint64_t newvalue,
98 hwloc_topology_diff_t *firstdiffp,
99 hwloc_topology_diff_t *lastdiffp)
100 {
101 hwloc_topology_diff_t newdiff;
102 newdiff = malloc(sizeof(*newdiff));
103 if (!newdiff)
104 return -1;
105
106 newdiff->obj_attr.type = HWLOC_TOPOLOGY_DIFF_OBJ_ATTR;
107 newdiff->obj_attr.obj_depth = obj->depth;
108 newdiff->obj_attr.obj_index = obj->logical_index;
109 newdiff->obj_attr.diff.uint64.type = type;
110 newdiff->obj_attr.diff.uint64.index = idx;
111 newdiff->obj_attr.diff.uint64.oldvalue = oldvalue;
112 newdiff->obj_attr.diff.uint64.newvalue = newvalue;
113 hwloc_append_diff(newdiff, firstdiffp, lastdiffp);
114 return 0;
115 }
116
117 static int
hwloc_diff_trees(hwloc_topology_t topo1,hwloc_obj_t obj1,hwloc_topology_t topo2,hwloc_obj_t obj2,unsigned flags,hwloc_topology_diff_t * firstdiffp,hwloc_topology_diff_t * lastdiffp)118 hwloc_diff_trees(hwloc_topology_t topo1, hwloc_obj_t obj1,
119 hwloc_topology_t topo2, hwloc_obj_t obj2,
120 unsigned flags,
121 hwloc_topology_diff_t *firstdiffp, hwloc_topology_diff_t *lastdiffp)
122 {
123 unsigned i;
124 int err;
125 hwloc_obj_t child1, child2;
126
127 if (obj1->depth != obj2->depth)
128 goto out_too_complex;
129
130 if (obj1->type != obj2->type)
131 goto out_too_complex;
132 if ((!obj1->subtype) != (!obj2->subtype)
133 || (obj1->subtype && strcmp(obj1->subtype, obj2->subtype)))
134 goto out_too_complex;
135
136 if (obj1->os_index != obj2->os_index)
137 /* we could allow different os_index for non-PU non-NUMAnode objects
138 * but it's likely useless anyway */
139 goto out_too_complex;
140
141 #define _SETS_DIFFERENT(_set1, _set2) \
142 ( ( !(_set1) != !(_set2) ) \
143 || ( (_set1) && !hwloc_bitmap_isequal(_set1, _set2) ) )
144 #define SETS_DIFFERENT(_set, _obj1, _obj2) _SETS_DIFFERENT((_obj1)->_set, (_obj2)->_set)
145 if (SETS_DIFFERENT(cpuset, obj1, obj2)
146 || SETS_DIFFERENT(complete_cpuset, obj1, obj2)
147 || SETS_DIFFERENT(nodeset, obj1, obj2)
148 || SETS_DIFFERENT(complete_nodeset, obj1, obj2))
149 goto out_too_complex;
150
151 /* no need to check logical_index, sibling_rank, symmetric_subtree,
152 * the parents did it */
153
154 /* gp_index don't have to be strictly identical */
155
156 if ((!obj1->name) != (!obj2->name)
157 || (obj1->name && strcmp(obj1->name, obj2->name))) {
158 err = hwloc_append_diff_obj_attr_string(obj1,
159 HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME,
160 NULL,
161 obj1->name,
162 obj2->name,
163 firstdiffp, lastdiffp);
164 if (err < 0)
165 return err;
166 }
167
168 /* type-specific attrs */
169 switch (obj1->type) {
170 default:
171 break;
172 case HWLOC_OBJ_NUMANODE:
173 if (obj1->attr->numanode.local_memory != obj2->attr->numanode.local_memory) {
174 err = hwloc_append_diff_obj_attr_uint64(obj1,
175 HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE,
176 0,
177 obj1->attr->numanode.local_memory,
178 obj2->attr->numanode.local_memory,
179 firstdiffp, lastdiffp);
180 if (err < 0)
181 return err;
182 }
183 /* ignore memory page_types */
184 break;
185 case HWLOC_OBJ_L1CACHE:
186 case HWLOC_OBJ_L2CACHE:
187 case HWLOC_OBJ_L3CACHE:
188 case HWLOC_OBJ_L4CACHE:
189 case HWLOC_OBJ_L5CACHE:
190 case HWLOC_OBJ_L1ICACHE:
191 case HWLOC_OBJ_L2ICACHE:
192 case HWLOC_OBJ_L3ICACHE:
193 if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->cache)))
194 goto out_too_complex;
195 break;
196 case HWLOC_OBJ_GROUP:
197 if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->group)))
198 goto out_too_complex;
199 break;
200 case HWLOC_OBJ_PCI_DEVICE:
201 if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->pcidev)))
202 goto out_too_complex;
203 break;
204 case HWLOC_OBJ_BRIDGE:
205 if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->bridge)))
206 goto out_too_complex;
207 break;
208 case HWLOC_OBJ_OS_DEVICE:
209 if (memcmp(obj1->attr, obj2->attr, sizeof(obj1->attr->osdev)))
210 goto out_too_complex;
211 break;
212 }
213
214 /* infos */
215 if (obj1->infos_count != obj2->infos_count)
216 goto out_too_complex;
217 for(i=0; i<obj1->infos_count; i++) {
218 struct hwloc_info_s *info1 = &obj1->infos[i], *info2 = &obj2->infos[i];
219 if (strcmp(info1->name, info2->name))
220 goto out_too_complex;
221 if (strcmp(obj1->infos[i].value, obj2->infos[i].value)) {
222 err = hwloc_append_diff_obj_attr_string(obj1,
223 HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO,
224 info1->name,
225 info1->value,
226 info2->value,
227 firstdiffp, lastdiffp);
228 if (err < 0)
229 return err;
230 }
231 }
232
233 /* ignore userdata */
234
235 /* children */
236 for(child1 = obj1->first_child, child2 = obj2->first_child;
237 child1 != NULL && child2 != NULL;
238 child1 = child1->next_sibling, child2 = child2->next_sibling) {
239 err = hwloc_diff_trees(topo1, child1,
240 topo2, child2,
241 flags,
242 firstdiffp, lastdiffp);
243 if (err < 0)
244 return err;
245 }
246 if (child1 || child2)
247 goto out_too_complex;
248
249 /* memory children */
250 for(child1 = obj1->memory_first_child, child2 = obj2->memory_first_child;
251 child1 != NULL && child2 != NULL;
252 child1 = child1->next_sibling, child2 = child2->next_sibling) {
253 err = hwloc_diff_trees(topo1, child1,
254 topo2, child2,
255 flags,
256 firstdiffp, lastdiffp);
257 if (err < 0)
258 return err;
259 }
260 if (child1 || child2)
261 goto out_too_complex;
262
263 /* I/O children */
264 for(child1 = obj1->io_first_child, child2 = obj2->io_first_child;
265 child1 != NULL && child2 != NULL;
266 child1 = child1->next_sibling, child2 = child2->next_sibling) {
267 err = hwloc_diff_trees(topo1, child1,
268 topo2, child2,
269 flags,
270 firstdiffp, lastdiffp);
271 if (err < 0)
272 return err;
273 }
274 if (child1 || child2)
275 goto out_too_complex;
276
277 /* misc children */
278 for(child1 = obj1->misc_first_child, child2 = obj2->misc_first_child;
279 child1 != NULL && child2 != NULL;
280 child1 = child1->next_sibling, child2 = child2->next_sibling) {
281 err = hwloc_diff_trees(topo1, child1,
282 topo2, child2,
283 flags,
284 firstdiffp, lastdiffp);
285 if (err < 0)
286 return err;
287 }
288 if (child1 || child2)
289 goto out_too_complex;
290
291 return 0;
292
293 out_too_complex:
294 hwloc_append_diff_too_complex(obj1, firstdiffp, lastdiffp);
295 return 0;
296 }
297
hwloc_topology_diff_build(hwloc_topology_t topo1,hwloc_topology_t topo2,unsigned long flags,hwloc_topology_diff_t * diffp)298 int hwloc_topology_diff_build(hwloc_topology_t topo1,
299 hwloc_topology_t topo2,
300 unsigned long flags,
301 hwloc_topology_diff_t *diffp)
302 {
303 hwloc_topology_diff_t lastdiff, tmpdiff;
304 struct hwloc_internal_distances_s *dist1, *dist2;
305 unsigned i;
306 int err;
307
308 if (!topo1->is_loaded || !topo2->is_loaded) {
309 errno = EINVAL;
310 return -1;
311 }
312
313 if (flags != 0) {
314 errno = EINVAL;
315 return -1;
316 }
317
318 *diffp = NULL;
319 err = hwloc_diff_trees(topo1, hwloc_get_root_obj(topo1),
320 topo2, hwloc_get_root_obj(topo2),
321 flags,
322 diffp, &lastdiff);
323 if (!err) {
324 tmpdiff = *diffp;
325 while (tmpdiff) {
326 if (tmpdiff->generic.type == HWLOC_TOPOLOGY_DIFF_TOO_COMPLEX) {
327 err = 1;
328 break;
329 }
330 tmpdiff = tmpdiff->generic.next;
331 }
332 }
333
334 if (!err) {
335 if (SETS_DIFFERENT(allowed_cpuset, topo1, topo2)
336 || SETS_DIFFERENT(allowed_nodeset, topo1, topo2))
337 goto roottoocomplex;
338 }
339
340 if (!err) {
341 /* distances */
342 hwloc_internal_distances_refresh(topo1);
343 hwloc_internal_distances_refresh(topo2);
344 dist1 = topo1->first_dist;
345 dist2 = topo2->first_dist;
346 while (dist1 || dist2) {
347 if (!!dist1 != !!dist2)
348 goto roottoocomplex;
349 if (dist1->unique_type != dist2->unique_type
350 || dist1->different_types || dist2->different_types /* too lazy to support this case */
351 || dist1->nbobjs != dist2->nbobjs
352 || dist1->kind != dist2->kind
353 || memcmp(dist1->values, dist2->values, dist1->nbobjs * dist1->nbobjs * sizeof(*dist1->values)))
354 goto roottoocomplex;
355 for(i=0; i<dist1->nbobjs; i++)
356 /* gp_index isn't enforced above. so compare logical_index instead, which is enforced. requires distances refresh() above */
357 if (dist1->objs[i]->logical_index != dist2->objs[i]->logical_index)
358 goto roottoocomplex;
359 dist1 = dist1->next;
360 dist2 = dist2->next;
361 }
362 }
363
364 if (!err) {
365 /* memattrs */
366 hwloc_internal_memattrs_refresh(topo1);
367 hwloc_internal_memattrs_refresh(topo2);
368 if (topo1->nr_memattrs != topo2->nr_memattrs)
369 goto roottoocomplex;
370 for(i=0; i<topo1->nr_memattrs; i++) {
371 struct hwloc_internal_memattr_s *imattr1 = &topo1->memattrs[i], *imattr2 = &topo2->memattrs[i];
372 unsigned j;
373 if (strcmp(imattr1->name, imattr2->name)
374 || imattr1->flags != imattr2->flags
375 || imattr1->nr_targets != imattr2->nr_targets)
376 goto roottoocomplex;
377 if (i == HWLOC_MEMATTR_ID_CAPACITY
378 || i == HWLOC_MEMATTR_ID_LOCALITY)
379 /* no need to check virtual attributes, there were refreshed from other topology attributes, checked above */
380 continue;
381 for(j=0; j<imattr1->nr_targets; j++) {
382 struct hwloc_internal_memattr_target_s *imtg1 = &imattr1->targets[j], *imtg2 = &imattr2->targets[j];
383 if (imtg1->type != imtg2->type)
384 goto roottoocomplex;
385 if (imtg1->obj->logical_index != imtg2->obj->logical_index)
386 goto roottoocomplex;
387 if (imattr1->flags & HWLOC_MEMATTR_FLAG_NEED_INITIATOR) {
388 unsigned k;
389 for(k=0; k<imtg1->nr_initiators; k++) {
390 struct hwloc_internal_memattr_initiator_s *imi1 = &imtg1->initiators[k], *imi2 = &imtg2->initiators[k];
391 if (imi1->value != imi2->value
392 || imi1->initiator.type != imi2->initiator.type)
393 goto roottoocomplex;
394 if (imi1->initiator.type == HWLOC_LOCATION_TYPE_CPUSET) {
395 if (!hwloc_bitmap_isequal(imi1->initiator.location.cpuset, imi2->initiator.location.cpuset))
396 goto roottoocomplex;
397 } else if (imi1->initiator.type == HWLOC_LOCATION_TYPE_OBJECT) {
398 if (imi1->initiator.location.object.type != imi2->initiator.location.object.type)
399 goto roottoocomplex;
400 if (imi1->initiator.location.object.obj->logical_index != imi2->initiator.location.object.obj->logical_index)
401 goto roottoocomplex;
402 } else {
403 assert(0);
404 }
405 }
406 } else {
407 if (imtg1->noinitiator_value != imtg2->noinitiator_value)
408 goto roottoocomplex;
409 }
410 }
411 }
412 }
413
414 return err;
415
416 roottoocomplex:
417 hwloc_append_diff_too_complex(hwloc_get_root_obj(topo1), diffp, &lastdiff);
418 return 1;
419 }
420
421 /********************
422 * Applying diffs
423 */
424
425 static int
hwloc_apply_diff_one(hwloc_topology_t topology,hwloc_topology_diff_t diff,unsigned long flags)426 hwloc_apply_diff_one(hwloc_topology_t topology,
427 hwloc_topology_diff_t diff,
428 unsigned long flags)
429 {
430 int reverse = !!(flags & HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE);
431
432 switch (diff->generic.type) {
433 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR: {
434 struct hwloc_topology_diff_obj_attr_s *obj_attr = &diff->obj_attr;
435 hwloc_obj_t obj = hwloc_get_obj_by_depth(topology, obj_attr->obj_depth, obj_attr->obj_index);
436 if (!obj)
437 return -1;
438
439 switch (obj_attr->diff.generic.type) {
440 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_SIZE: {
441 hwloc_obj_t tmpobj;
442 hwloc_uint64_t oldvalue = reverse ? obj_attr->diff.uint64.newvalue : obj_attr->diff.uint64.oldvalue;
443 hwloc_uint64_t newvalue = reverse ? obj_attr->diff.uint64.oldvalue : obj_attr->diff.uint64.newvalue;
444 hwloc_uint64_t valuediff = newvalue - oldvalue;
445 if (obj->type != HWLOC_OBJ_NUMANODE)
446 return -1;
447 if (obj->attr->numanode.local_memory != oldvalue)
448 return -1;
449 obj->attr->numanode.local_memory = newvalue;
450 tmpobj = obj;
451 while (tmpobj) {
452 tmpobj->total_memory += valuediff;
453 tmpobj = tmpobj->parent;
454 }
455 break;
456 }
457 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_NAME: {
458 const char *oldvalue = reverse ? obj_attr->diff.string.newvalue : obj_attr->diff.string.oldvalue;
459 const char *newvalue = reverse ? obj_attr->diff.string.oldvalue : obj_attr->diff.string.newvalue;
460 if (!obj->name || strcmp(obj->name, oldvalue))
461 return -1;
462 free(obj->name);
463 obj->name = strdup(newvalue);
464 break;
465 }
466 case HWLOC_TOPOLOGY_DIFF_OBJ_ATTR_INFO: {
467 const char *name = obj_attr->diff.string.name;
468 const char *oldvalue = reverse ? obj_attr->diff.string.newvalue : obj_attr->diff.string.oldvalue;
469 const char *newvalue = reverse ? obj_attr->diff.string.oldvalue : obj_attr->diff.string.newvalue;
470 unsigned i;
471 int found = 0;
472 for(i=0; i<obj->infos_count; i++) {
473 struct hwloc_info_s *info = &obj->infos[i];
474 if (!strcmp(info->name, name)
475 && !strcmp(info->value, oldvalue)) {
476 free(info->value);
477 info->value = strdup(newvalue);
478 found = 1;
479 break;
480 }
481 }
482 if (!found)
483 return -1;
484 break;
485 }
486 default:
487 return -1;
488 }
489
490 break;
491 }
492 default:
493 return -1;
494 }
495
496 return 0;
497 }
498
hwloc_topology_diff_apply(hwloc_topology_t topology,hwloc_topology_diff_t diff,unsigned long flags)499 int hwloc_topology_diff_apply(hwloc_topology_t topology,
500 hwloc_topology_diff_t diff,
501 unsigned long flags)
502 {
503 hwloc_topology_diff_t tmpdiff, tmpdiff2;
504 int err, nr;
505
506 if (!topology->is_loaded) {
507 errno = EINVAL;
508 return -1;
509 }
510 if (topology->adopted_shmem_addr) {
511 errno = EPERM;
512 return -1;
513 }
514
515 if (flags & ~HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE) {
516 errno = EINVAL;
517 return -1;
518 }
519
520 tmpdiff = diff;
521 nr = 0;
522 while (tmpdiff) {
523 nr++;
524 err = hwloc_apply_diff_one(topology, tmpdiff, flags);
525 if (err < 0)
526 goto cancel;
527 tmpdiff = tmpdiff->generic.next;
528 }
529 return 0;
530
531 cancel:
532 tmpdiff2 = tmpdiff;
533 tmpdiff = diff;
534 while (tmpdiff != tmpdiff2) {
535 hwloc_apply_diff_one(topology, tmpdiff, flags ^ HWLOC_TOPOLOGY_DIFF_APPLY_REVERSE);
536 tmpdiff = tmpdiff->generic.next;
537 }
538 errno = EINVAL;
539 return -nr; /* return the index (starting at 1) of the first element that couldn't be applied */
540 }
541