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