GCC Code Coverage Report


Directory: src/
File: src/support/mask_utils.c
Date: 2026-02-23 15:13:19
Exec Total Coverage
Lines: 689 708 97.3%
Functions: 66 66 100.0%
Branches: 411 490 83.9%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2024 Barcelona Supercomputing Center */
3 /* */
4 /* This file is part of the DLB library. */
5 /* */
6 /* DLB is free software: you can redistribute it and/or modify */
7 /* it under the terms of the GNU Lesser General Public License as published by */
8 /* the Free Software Foundation, either version 3 of the License, or */
9 /* (at your option) any later version. */
10 /* */
11 /* DLB is distributed in the hope that it will be useful, */
12 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
13 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
14 /* GNU Lesser General Public License for more details. */
15 /* */
16 /* You should have received a copy of the GNU Lesser General Public License */
17 /* along with DLB. If not, see <https://www.gnu.org/licenses/>. */
18 /*********************************************************************************/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include "support/mask_utils.h"
25
26 #include "support/debug.h"
27 #include "support/dlb_common.h"
28
29 #ifdef HWLOC_LIB
30 #include <hwloc.h>
31 #include <hwloc/bitmap.h>
32 #include <hwloc/glibc-sched.h>
33 #endif
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <dirent.h>
37
38 #include <sched.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <limits.h>
43 #include <ctype.h>
44 #include <sys/types.h>
45 #include <regex.h>
46
47 #ifdef IS_BGQ_MACHINE
48 static void parse_mask_from_file(const char *filename, cpu_set_t *mask)
49 __attribute__((unused));
50 static int parse_hwloc(void) __attribute__((unused));
51 static void parse_system_files(void) __attribute__((unused));
52 #endif
53
54
55 /*********************************************************************************/
56 /* mu_cpuset_t: custom cpuset type for mask utils */
57 /*********************************************************************************/
58
59 /* Initial values to accomodate up to CPU_SETSIZE CPUs.
60 * Later, they are reduced according to the machine specification */
61 static unsigned int mu_cpuset_setsize = CPU_SETSIZE;
62 static size_t mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
63 static size_t mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
64
65
66 1168 static inline void mu_cpuset_from_glibc_sched_affinity(mu_cpuset_t *mu_cpuset,
67 const cpu_set_t *cpu_set) {
68 4672 *mu_cpuset = (const mu_cpuset_t) {
69 1168 .set = CPU_ALLOC(mu_cpuset_setsize),
70 .alloc_size = mu_cpuset_alloc_size,
71 1168 .count = CPU_COUNT_S(mu_cpuset_alloc_size, cpu_set),
72 1168 .first_cpuid = mu_get_first_cpu(cpu_set),
73 1168 .last_cpuid = mu_get_last_cpu(cpu_set),
74 };
75 1168 memcpy(mu_cpuset->set, cpu_set, mu_cpuset_alloc_size);
76 1168 }
77
78 #ifdef HWLOC_LIB
79 280 static inline void mu_cpuset_from_hwloc_bitmap(mu_cpuset_t *mu_cpuset,
80 hwloc_const_bitmap_t bitmap, hwloc_topology_t topology) {
81 280 *mu_cpuset = (const mu_cpuset_t) {
82 280 .set = CPU_ALLOC(mu_cpuset_setsize),
83 .alloc_size = mu_cpuset_alloc_size,
84 280 .count = hwloc_bitmap_weight(bitmap),
85 280 .first_cpuid = hwloc_bitmap_first(bitmap),
86 280 .last_cpuid = hwloc_bitmap_last(bitmap),
87 };
88 280 hwloc_cpuset_to_glibc_sched_affinity(topology, bitmap, mu_cpuset->set,
89 mu_cpuset_alloc_size);
90 280 }
91 #endif
92
93
94 /*********************************************************************************/
95 /* Mask utils system info */
96 /*********************************************************************************/
97
98 /* mask_utils main structure with system info */
99 typedef struct {
100 unsigned int num_nodes;
101 unsigned int num_cores;
102 unsigned int num_cpus;
103 mu_cpuset_t sys_mask;
104 mu_cpuset_t* node_masks;
105 mu_cpuset_t* core_masks_by_coreid;
106 mu_cpuset_t** core_masks_by_cpuid;
107 } mu_system_loc_t;
108
109 enum { BITS_PER_BYTE = 8 };
110 enum { CPUS_PER_ULONG = sizeof(unsigned long) * BITS_PER_BYTE };
111
112 static mu_system_loc_t sys = {0};
113 static bool mu_initialized = false;
114
115 80 static void init_mu_struct(void) {
116 80 sys = (const mu_system_loc_t) {};
117 80 }
118
119 /* This function (re-)initializes 'sys' with the given cpu sets.
120 * It is used for specific set-ups, fallback, or testing purposes */
121 66 static void init_system_masks(const cpu_set_t *sys_mask,
122 const cpu_set_t *core_masks, unsigned int num_cores,
123 const cpu_set_t *node_masks, unsigned int num_nodes) {
124
125 /* De-allocate structures if already initialized */
126
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 9 times.
66 if (mu_initialized) {
127 57 mu_finalize();
128 } else {
129 9 init_mu_struct();
130 }
131
132 /*** System ***/
133 66 sys.num_cpus = mu_get_last_cpu(sys_mask) + 1;
134 66 mu_cpuset_setsize = sys.num_cpus;
135 66 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
136 66 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
137 66 sys.sys_mask = (const mu_cpuset_t) {
138 66 .set = CPU_ALLOC(mu_cpuset_setsize),
139 66 .count = CPU_COUNT_S(mu_cpuset_alloc_size, sys_mask),
140 .first_cpuid = 0,
141 66 .last_cpuid = mu_cpuset_setsize - 1,
142 };
143 66 memcpy(sys.sys_mask.set, sys_mask, mu_cpuset_alloc_size);
144
145 /*** Cores ***/
146 66 sys.num_cores = num_cores;
147 66 sys.core_masks_by_coreid = malloc(sys.num_cores * sizeof(mu_cpuset_t));
148 66 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
149
2/2
✓ Branch 0 taken 1092 times.
✓ Branch 1 taken 66 times.
1158 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
150 1092 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
151 1092 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_masks[core_id]);
152 1092 for (int cpuid = core_cpuset->first_cpuid;
153
4/4
✓ Branch 0 taken 1180 times.
✓ Branch 1 taken 1091 times.
✓ Branch 2 taken 1179 times.
✓ Branch 3 taken 1 times.
2271 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
154 1179 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
155 /* Save reference to another array indexed by cpuid */
156 1179 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
157 }
158 }
159
160 /*** NUMA Nodes ***/
161 66 sys.num_nodes = num_nodes;
162 66 sys.node_masks = malloc(sys.num_nodes * sizeof(mu_cpuset_t));
163
2/2
✓ Branch 0 taken 71 times.
✓ Branch 1 taken 66 times.
137 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
164 71 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_masks[node_id]);
165 }
166
167 66 mu_initialized = true;
168 66 }
169
170
171 /* This function (re-)initializes 'sys' given an overall number of resources.
172 * It is used for specific set-ups, fallback, or testing purposes */
173 54 static void init_system(unsigned int num_cpus, unsigned int num_cores,
174 unsigned int num_nodes) {
175
176 /*** System ***/
177 cpu_set_t sys_mask;
178 54 CPU_ZERO(&sys_mask);
179
2/2
✓ Branch 0 taken 1084 times.
✓ Branch 1 taken 54 times.
1138 for (unsigned int cpuid = 0; cpuid < num_cpus; ++cpuid) {
180
1/2
✓ Branch 0 taken 1084 times.
✗ Branch 1 not taken.
1084 CPU_SET(cpuid, &sys_mask);
181 }
182
183 /*** Cores ***/
184 54 unsigned int cpus_per_core = num_cpus / num_cores;
185 54 cpu_set_t *core_masks = calloc(num_cores, sizeof(cpu_set_t));
186
2/2
✓ Branch 0 taken 1044 times.
✓ Branch 1 taken 54 times.
1098 for (unsigned int core_id = 0; core_id < num_cores; ++core_id) {
187 1044 for (unsigned int cpuid = core_id * cpus_per_core;
188
2/2
✓ Branch 0 taken 1084 times.
✓ Branch 1 taken 1044 times.
2128 cpuid < (core_id+1) * cpus_per_core; ++cpuid) {
189
1/2
✓ Branch 0 taken 1084 times.
✗ Branch 1 not taken.
1084 CPU_SET(cpuid, &core_masks[core_id]);
190 }
191 }
192
193 /*** NUMA Nodes ***/
194 54 unsigned int cpus_per_node = num_cpus / num_nodes;
195 54 cpu_set_t *node_masks = calloc(num_nodes, sizeof(cpu_set_t));
196
2/2
✓ Branch 0 taken 57 times.
✓ Branch 1 taken 54 times.
111 for (unsigned int node_id = 0; node_id < num_nodes; ++node_id) {
197 57 for (unsigned int cpuid = node_id * cpus_per_node;
198
2/2
✓ Branch 0 taken 1084 times.
✓ Branch 1 taken 57 times.
1141 cpuid < (node_id+1) * cpus_per_node; ++cpuid) {
199
1/2
✓ Branch 0 taken 1084 times.
✗ Branch 1 not taken.
1084 CPU_SET(cpuid, &node_masks[node_id]);
200 }
201 }
202
203 54 init_system_masks(&sys_mask, core_masks, num_cores, node_masks, num_nodes);
204 54 free(core_masks);
205 54 free(node_masks);
206 54 }
207
208 70 static int parse_hwloc(void) {
209 #ifdef HWLOC_LIB
210 /* Check runtime library compatibility */
211 70 unsigned int hwloc_version = hwloc_get_api_version();
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 70 times.
70 if (hwloc_version >> 16 != HWLOC_API_VERSION >> 16) {
213 warning("Detected incompatible HWLOC runtime library");
214 return -1;
215 }
216
217 hwloc_topology_t topology;
218 70 hwloc_topology_init(&topology);
219 70 hwloc_topology_load(topology);
220
221 /*** System ***/
222 70 hwloc_obj_t machine = hwloc_get_obj_by_type(topology, HWLOC_OBJ_MACHINE, 0);
223 70 sys.num_cpus = hwloc_bitmap_last(machine->cpuset) + 1;
224 70 mu_cpuset_setsize = sys.num_cpus;
225 #if HWLOC_API_VERSION >= 0x00020100
226 70 mu_cpuset_num_ulongs = hwloc_bitmap_nr_ulongs(machine->cpuset);
227 #else
228 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
229 #endif
230 70 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
231 70 mu_cpuset_from_hwloc_bitmap(&sys.sys_mask, machine->cpuset, topology);
232
233 /*** Cores ***/
234 70 hwloc_obj_type_t core = HWLOC_OBJ_CORE;
235 70 sys.num_cores = hwloc_get_nbobjs_by_type(topology, core);
236 70 sys.core_masks_by_coreid = calloc(sys.num_cores, sizeof(mu_cpuset_t));
237 70 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
238
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 70 times.
210 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
239 140 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, core, core_id);
240 140 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
241 140 mu_cpuset_from_hwloc_bitmap(core_cpuset, obj->cpuset, topology);
242 140 for (int cpuid = core_cpuset->first_cpuid;
243
3/4
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 140 times.
✓ Branch 2 taken 140 times.
✗ Branch 3 not taken.
280 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
244 140 cpuid = hwloc_bitmap_next(obj->cpuset, cpuid)) {
245 /* Save reference to another array indexed by cpuid */
246 140 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
247 }
248 }
249
250 /*** NUMA Nodes ***/
251 70 hwloc_obj_type_t node = HWLOC_OBJ_NODE;
252 70 sys.num_nodes = hwloc_get_nbobjs_by_type(topology, node);
253 70 sys.node_masks = calloc(sys.num_nodes, sizeof(mu_cpuset_t));
254
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 70 times.
140 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
255 70 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, node, node_id);
256 70 mu_cpuset_from_hwloc_bitmap(&sys.node_masks[node_id],
257 70 obj->cpuset, topology);
258 }
259
260 70 hwloc_topology_destroy(topology);
261
262 70 return 0;
263 #else
264 return -1;
265 #endif
266 }
267
268 4 static void parse_mask_from_file(const char *filename, cpu_set_t *mask) {
269
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (access(filename, F_OK) == 0) {
270 enum { BUF_LEN = CPU_SETSIZE*7 };
271 char buf[BUF_LEN];
272 4 FILE *fd = fopen(filename, "r");
273
274
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
4 if (!fgets(buf, BUF_LEN, fd)) {
275 fatal("cannot read %s\n", filename);
276 }
277 4 fclose(fd);
278
279 4 size_t len = strlen(buf);
280
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (buf[len - 1] == '\n')
281 4 buf[len - 1] = '\0';
282
283 4 mu_parse_mask(buf, mask);
284 }
285 4 }
286
287 2 static int parse_int_from_file(const char *filename) {
288 2 int value = -1;
289
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (access(filename, F_OK) == 0) {
290 enum { BUF_LEN = 16 };
291 char buf[BUF_LEN];
292 2 FILE *fd = fopen(filename, "r");
293
294
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!fgets(buf, BUF_LEN, fd)) {
295 fatal("cannot read %s\n", filename);
296 }
297 2 fclose(fd);
298
299 2 value = strtol(buf, NULL, 10);
300 }
301 2 return value;
302 }
303
304 1 static int cmp_mu_cpuset(const void *a, const void *b) {
305 1 const mu_cpuset_t *set_a = a;
306 1 const mu_cpuset_t *set_b = b;
307
308
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (set_a->count == 0) return 1;
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (set_b->count == 0) return -1;
310 1 return set_a->first_cpuid - set_b->first_cpuid;
311 }
312
313 #define PATH_SYSTEM_MASK "/sys/devices/system/cpu/present"
314 #define PATH_SYSTEM_CPUS "/sys/devices/system/cpu"
315 #define PATH_SYSTEM_NODE "/sys/devices/system/node"
316 1 static void parse_system_files(void) {
317 /*** System ***/
318 cpu_set_t system_mask;
319 1 parse_mask_from_file(PATH_SYSTEM_MASK, &system_mask);
320 1 sys.num_cpus = mu_get_last_cpu(&system_mask) + 1;
321 1 mu_cpuset_setsize = sys.num_cpus;
322 1 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
323 1 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
324 1 mu_cpuset_from_glibc_sched_affinity(&sys.sys_mask, &system_mask);
325
326 /*** Cores ***/
327
328 /* This array contains references to core_masks. It is initialized to 0
329 * to account for possible missing CPU ids */
330 1 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
331
332 /* The number of cores is not known beforehand, and the number indicates
333 * the capacity to hold the last core id, rather than the valid cores */
334 1 int num_cores = 0;
335
336 /* Only if core_id info is not reliable */
337 1 bool core_masks_array_needs_reordering = false;
338
339 cpu_set_t empty_mask;
340 1 CPU_ZERO(&empty_mask);
341
342 1 DIR *dir = opendir(PATH_SYSTEM_CPUS);
343
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir) {
344 struct dirent *d;
345
2/2
✓ Branch 1 taken 20 times.
✓ Branch 2 taken 1 times.
21 while ((d = readdir(dir))) {
346
3/4
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 10 times.
20 if (d && d->d_type == DT_DIR
347
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 6 times.
10 && strncmp(d->d_name, "cpu", 3) == 0
348
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 && isdigit(d->d_name[3]) ) {
349
350 enum { SYSPATH_MAX = 64 };
351 char filename[SYSPATH_MAX];
352
353 /* Get CPU id */
354 2 int cpu_id = strtol(d->d_name+3, NULL, 10);
355
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 fatal_cond(cpu_id < 0, "Error parsing cpu_id");
356
357 /* Get core id */
358 2 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
359 2 "/%.8s/topology/core_id", d->d_name);
360 2 int core_id = parse_int_from_file(filename);
361
362 /* Get core CPUs list */
363 cpu_set_t core_mask;
364 2 CPU_ZERO(&core_mask);
365 2 snprintf(filename, SYSPATH_MAX, PATH_SYSTEM_CPUS
366 2 "/%.8s/topology/thread_siblings_list", d->d_name);
367 2 parse_mask_from_file(filename, &core_mask);
368
369 /* Reallocate core_masks_by_coreid as needed */
370
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (core_id >= num_cores) {
371 /* Realloc, saving prev_num_cores */
372 1 int prev_num_cores = num_cores;
373 1 num_cores = core_id + 1;
374 1 void *p = realloc(sys.core_masks_by_coreid,
375 num_cores * sizeof(mu_cpuset_t));
376
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed in %s", __func__);
377 1 sys.core_masks_by_coreid = p;
378
379 /* Everytime we reallocate, we need to initialize
380 * [prev_size..new_size] with empty elements */
381
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int i = prev_num_cores; i < num_cores; ++i) {
382 1 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[i];
383 1 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &empty_mask);
384 }
385 }
386
387 /* Save core mask */
388 2 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
389
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (core_cpuset->count == 0) {
390 /* Save core mask */
391 1 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_mask);
392
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 } else if (CPU_EQUAL_S(mu_cpuset_alloc_size, core_cpuset->set, &core_mask)) {
393 /* Core mask already saved */
394 } else {
395 /* If we get here it means that different masks have same
396 * core_id, so we cannot trust its value to index masks.
397 * Use whatever core_id to save the mask and then reorder array
398 * with virtual core_ids */
399
400 /* Find first unused core_id and whether this mask is also
401 * present with another core id */
402 1 int new_coreid = -1;
403 1 bool already_present = false;
404
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int c = 0; c < num_cores; ++c) {
405 1 mu_cpuset_t *core = &sys.core_masks_by_coreid[c];
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (core->count == 0) {
407 if (new_coreid == -1) {
408 new_coreid = c;
409 }
410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (CPU_EQUAL_S(mu_cpuset_alloc_size, core->set, &core_mask)) {
411 already_present = true;
412 break;
413 }
414 }
415
416 /* Continue to next CPU if the core mask has already been registered */
417
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (already_present) {
418 continue;
419 }
420
421 /* Relloacate if we didn't find en empty spot nor the same mask */
422
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (new_coreid == -1) {
423 1 new_coreid = num_cores++;
424 1 void *p = realloc(sys.core_masks_by_coreid,
425 num_cores * sizeof(mu_cpuset_t));
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed in %s", __func__);
427 1 sys.core_masks_by_coreid = p;
428 }
429
430 /* Finally save core mask */
431 1 mu_cpuset_t *new_core = &sys.core_masks_by_coreid[new_coreid];
432 1 mu_cpuset_from_glibc_sched_affinity(new_core, &core_mask);
433
434 /* Set boolean to do post-processing */
435 1 core_masks_array_needs_reordering = true;
436 }
437 }
438 }
439 1 closedir(dir);
440 }
441 1 sys.num_cores = num_cores;
442
443 /* Only if replacing core_id by virtual core id */
444
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (core_masks_array_needs_reordering) {
445 1 qsort(sys.core_masks_by_coreid, num_cores, sizeof(mu_cpuset_t), cmp_mu_cpuset);
446 }
447
448 /* Add core mask references to array indexed by CPU id */
449
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
450 2 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
451 2 for (int cpuid = core_cpuset->first_cpuid;
452
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
453 2 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
454 2 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
455 }
456 }
457
458 /*** NUMA Nodes ***/
459 1 int num_nodes = 0;
460 1 dir = opendir(PATH_SYSTEM_NODE);
461
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (dir) {
462 struct dirent *d;
463
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
12 while ((d = readdir(dir))) {
464
3/4
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 7 times.
11 if (d && d->d_type == DT_DIR
465
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 && strncmp(d->d_name, "node", 4) == 0
466
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 && isdigit(d->d_name[4]) ) {
467
468 /* Get node id */
469 1 int node_id = strtol(d->d_name+4, NULL, 10);
470
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 fatal_cond(node_id < 0 || node_id > 1024, "Error parsing node_id");
471
472 /* Get node CPUs list */
473 cpu_set_t node_mask;
474 1 CPU_ZERO(&node_mask);
475 char filename[64];
476 1 snprintf(filename, 64, PATH_SYSTEM_NODE "/%.10s/cpulist", d->d_name);
477 1 parse_mask_from_file(filename, &node_mask);
478
479 /* Save node mask */
480
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if (CPU_COUNT(&node_mask) > 0) {
481 1 num_nodes = max_int(num_nodes, node_id + 1);
482 1 mu_cpuset_t *p = realloc(sys.node_masks, num_nodes*sizeof(mu_cpuset_t));
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed");
484 1 sys.node_masks = p;
485 1 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_mask);
486 }
487 }
488 }
489 1 closedir(dir);
490 }
491 1 sys.num_nodes = num_nodes;
492
493 /* Fallback if some info could not be parsed */
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (sys.sys_mask.count == 0) {
495 int nproc_onln = sysconf(_SC_NPROCESSORS_ONLN);
496 fatal_cond(nproc_onln <= 0, "Cannot obtain system size. Contact us at "
497 PACKAGE_BUGREPORT " or configure DLB with HWLOC support.");
498 init_system(nproc_onln, nproc_onln, 1);
499 }
500 1 }
501
502
503 /*********************************************************************************/
504 /* Mask utils public functions */
505 /*********************************************************************************/
506
507 148 void mu_init( void ) {
508
2/2
✓ Branch 0 taken 70 times.
✓ Branch 1 taken 78 times.
148 if ( !mu_initialized ) {
509 70 init_mu_struct();
510
511 #if defined IS_BGQ_MACHINE
512 enum { BGQ_NUM_CPUS = 64 };
513 enum { BGQ_NUM_CORES = 16 };
514 enum { BGQ_NUM_NODES = 1 };
515 init_system(BGQ_NUM_CPUS, BGQ_NUM_CORES, BGQ_NUM_NODES);
516 #else
517 /* Try to parse HW info from HWLOC first */
518
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 70 times.
70 if (parse_hwloc() != 0) {
519 /* Fallback to system files if needed */
520 parse_system_files();
521 }
522
523 70 mu_initialized = true;
524 #endif
525 }
526 148 }
527
528 /* This function used to be declared as destructor but it may be dangerous
529 * with the OpenMP / DLB finalization at destruction time. */
530 103 void mu_finalize( void ) {
531
532 103 CPU_FREE(sys.sys_mask.set);
533
534 /* Nodes */
535
2/2
✓ Branch 0 taken 106 times.
✓ Branch 1 taken 103 times.
209 for (unsigned int i = 0; i < sys.num_nodes; ++i) {
536 106 CPU_FREE(sys.node_masks[i].set);
537 }
538 103 free(sys.node_masks);
539
540 /* Cores per core id */
541
2/2
✓ Branch 0 taken 908 times.
✓ Branch 1 taken 103 times.
1011 for (unsigned int i = 0; i < sys.num_cores; ++i) {
542 908 CPU_FREE(sys.core_masks_by_coreid[i].set);
543 }
544 103 free(sys.core_masks_by_coreid);
545
546 /* Cores per CPU id (just references) */
547 103 free(sys.core_masks_by_cpuid);
548
549 103 sys = (const mu_system_loc_t) {};
550 103 mu_initialized = false;
551 103 mu_cpuset_setsize = CPU_SETSIZE;
552 103 mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
553 103 mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
554 103 }
555
556 7201 int mu_get_system_size( void ) {
557
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 7175 times.
7201 if (unlikely(!mu_initialized)) mu_init();
558 7201 return sys.sys_mask.last_cpuid + 1;
559 }
560
561 141 void mu_get_system_mask(cpu_set_t *mask) {
562
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 139 times.
141 if (unlikely(!mu_initialized)) mu_init();
563 141 CPU_ZERO(mask);
564 141 memcpy(mask, sys.sys_mask.set, mu_cpuset_alloc_size);
565 141 }
566
567 12 int mu_get_system_hwthreads_per_core(void) {
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (unlikely(!mu_initialized)) mu_init();
569
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 fatal_cond(sys.core_masks_by_coreid[0].count == 0,
570 "Number of hardware threads per core is 0. Please report bug at " PACKAGE_BUGREPORT);
571 12 return sys.core_masks_by_coreid[0].count;
572 }
573
574 12 int mu_get_system_num_nodes(void) {
575 12 return sys.num_nodes;
576 }
577
578 7 int mu_get_system_cores_per_node(void) {
579 7 return mu_count_cores(sys.node_masks[0].set);
580 }
581
582 8 void mu_get_system_description(print_buffer_t *buffer) {
583
584 #define TAB " "
585
586 /* Should be enough to hold up a list of > 1024 CPUs */
587 enum { LINE_BUFFER_SIZE = 8096 };
588 char line[LINE_BUFFER_SIZE];
589 char *l;
590
591 8 printbuffer_init(buffer);
592
593 // CPUs: N (C cores x T threads)
594 8 l = line;
595 8 l += sprintf(line, TAB"CPUs: %d", mu_get_system_size());
596
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
8 if (mu_system_has_smt()) {
597 4 sprintf(l, " (%d cores x %d threads)",
598 mu_get_num_cores(), mu_get_system_hwthreads_per_core());
599 } else {
600 4 sprintf(l, " (%d cores)", mu_get_num_cores());
601 }
602 8 printbuffer_append(buffer, line);
603
604 // NUMA nodes: N (C cores per NUMA node)
605 8 l = line;
606 8 l += sprintf(line, TAB"NUMA nodes: %d", mu_get_system_num_nodes());
607
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (sys.num_nodes > 1) {
608 3 sprintf(l, " (%d cores per NUMA node)", mu_get_system_cores_per_node());
609 }
610 8 printbuffer_append(buffer, line);
611
612 // System mask
613 8 printbuffer_append_no_newline(buffer, TAB"System mask: ");
614 8 printbuffer_append(buffer, mu_to_str(sys.sys_mask.set));
615
616 // NUMA node masks
617 8 printbuffer_append_no_newline(buffer, TAB"NUMA node masks: ");
618
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
619 13 printbuffer_append_no_newline(buffer, mu_to_str(sys.node_masks[node_id].set));
620
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 if (node_id + 1 < sys.num_nodes) {
621 5 printbuffer_append_no_newline(buffer, ", ");
622 }
623 }
624 8 printbuffer_append(buffer, "");
625
626 // Core masks
627 8 printbuffer_append_no_newline(buffer, TAB"Core masks: ");
628
2/2
✓ Branch 0 taken 358 times.
✓ Branch 1 taken 8 times.
366 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
629 358 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
630
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 if (core_cpuset->count > 0) {
631 358 printbuffer_append_no_newline(buffer, mu_to_str(core_cpuset->set));
632
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 8 times.
358 if (core_id + 1 < sys.num_cores) {
633 350 printbuffer_append_no_newline(buffer, ", ");
634 }
635 }
636 }
637 8 printbuffer_append(buffer, "");
638 8 }
639
640 85 bool mu_system_has_smt(void) {
641
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
85 if (unlikely(!mu_initialized)) mu_init();
642 85 return sys.core_masks_by_coreid[0].count > 1;
643 }
644
645 9 int mu_get_num_cores(void) {
646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (unlikely(!mu_initialized)) mu_init();
647
648 /* Not likely, but sys.num_cores may not be the number of valid cores */
649 9 int num_cores = 0;
650
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 9 times.
369 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
651
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 if (sys.core_masks_by_coreid[core_id].count > 0) {
652 360 ++num_cores;
653 }
654 }
655
656 9 return num_cores;
657 }
658
659 11788 int mu_get_core_id(int cpuid) {
660
661
4/4
✓ Branch 0 taken 11770 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 11767 times.
11788 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return -1;
662
663
2/2
✓ Branch 0 taken 121844 times.
✓ Branch 1 taken 1 times.
121845 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
664
5/6
✓ Branch 0 taken 121844 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11766 times.
✓ Branch 3 taken 110078 times.
✓ Branch 4 taken 11766 times.
✓ Branch 5 taken 110078 times.
121844 if (CPU_ISSET_S(cpuid, mu_cpuset_alloc_size,
665 sys.core_masks_by_coreid[core_id].set)) {
666 11766 return core_id;
667 }
668 }
669
670 1 return -1;
671 }
672
673 372 const mu_cpuset_t* mu_get_core_mask(int cpuid) {
674
675
3/4
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 370 times.
372 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return NULL;
676
677 370 return sys.core_masks_by_cpuid[cpuid];
678 }
679
680 43 const mu_cpuset_t* mu_get_core_mask_by_coreid(int core_id) {
681
682
2/4
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 43 times.
43 if (core_id < 0 || (unsigned)core_id >= sys.num_cores) return NULL;
683
684 43 return &sys.core_masks_by_coreid[core_id];
685 }
686
687 /* Return Mask of full NUMA nodes covering at least 1 CPU of cpuset:
688 * e.g.:
689 * node0: [0-3]
690 * node1: [4-7]
691 * cpuset: [1-7]
692 * returns [0-7]
693 */
694 42 void mu_get_nodes_intersecting_with_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
695
696 42 CPU_ZERO(node_set);
697
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 42 times.
86 for (unsigned int i=0; i<sys.num_nodes; ++i) {
698 cpu_set_t intxn;
699
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 44 times.
88 CPU_AND_S(mu_cpuset_alloc_size, &intxn, sys.node_masks[i].set, cpuset);
700
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 5 times.
44 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
701
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 39 times.
78 CPU_OR_S(mu_cpuset_alloc_size, node_set, node_set, sys.node_masks[i].set);
702 }
703 }
704 42 }
705
706 /* Return Mask of full NUMA nodes containing all CPUs in cpuset:
707 * e.g.:
708 * node0: [0-3]
709 * node1: [4-7]
710 * cpuset: [1-7]
711 * returns [4-7]
712 */
713 2 void mu_get_nodes_subset_of_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
714
715 2 CPU_ZERO(node_set);
716
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (unsigned int i=0; i<sys.num_nodes; ++i) {
717
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (mu_is_subset(sys.node_masks[i].set, cpuset)) {
718
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 CPU_OR_S(mu_cpuset_alloc_size, node_set, node_set, sys.node_masks[i].set);
719 }
720 }
721 2 }
722
723 /* Return Mask of cores covering at least 1 CPU of cpuset:
724 * e.g.:
725 * node0: [0-1]
726 * node1: [2-3]
727 * cpuset: [1-3]
728 * returns [0-3]
729 */
730 5 void mu_get_cores_intersecting_with_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
731 5 CPU_ZERO(core_set);
732
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
25 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
733 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
734 cpu_set_t intxn;
735
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_AND_S(mu_cpuset_alloc_size, &intxn, core_cpuset->set, cpuset);
736
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
20 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
737
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 CPU_OR_S(mu_cpuset_alloc_size, core_set, core_set, core_cpuset->set);
738 }
739 }
740 5 }
741
742 /* Return Mask of cores containing all CPUs in cpuset:
743 * e.g.:
744 * core0: [0-1]
745 * core1: [2-3]
746 * cpuset: [1-3]
747 * returns [2-3]
748 */
749 5 void mu_get_cores_subset_of_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
750 5 CPU_ZERO(core_set);
751
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
25 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
752 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
753
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if (mu_is_subset(core_cpuset->set, cpuset)) {
754
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 CPU_OR_S(mu_cpuset_alloc_size, core_set, core_set, core_cpuset->set);
755 }
756 }
757 5 }
758
759 /* Return the next enabled CPU in mask which pertains to the next core after
760 * prev_cpu, or -1 if not found. */
761 44 int mu_get_cpu_next_core(const cpu_set_t *mask, int prev_cpu) {
762
763
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 43 times.
44 if (unlikely(prev_cpu < -1)) return -1;
764
765 43 int prev_core = mu_get_core_id(prev_cpu);
766 43 int next_cpu = mu_get_next_cpu(mask, prev_cpu);
767 43 int next_core = mu_get_core_id(next_cpu);
768
769 43 while (next_cpu != -1
770
4/4
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 27 times.
75 && next_core <= prev_core) {
771 32 next_cpu = mu_get_next_cpu(mask, next_cpu);
772 32 next_core = mu_get_core_id(next_cpu);
773 }
774
775
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
43 return (next_cpu == -1 || (unsigned)next_cpu >= sys.num_cpus) ? -1 : next_cpu;
776 }
777
778 /* We define as "complete" those cores that all the CPUs defined by
779 * sys.core_masks_by_coreid are enabled. */
780
781 /* Return the number of complete cores in the mask.
782 * e.g.:
783 * core0: [0-1]
784 * core1: [2-3]
785 * core2: [4-5]
786 * cpuset: [0-4]
787 * returns 2
788 */
789 33 int mu_count_cores(const cpu_set_t *mask) {
790
791 33 int cores_count = 0;
792
793
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 33 times.
483 for (unsigned int coreid = 0; coreid < sys.num_cores; coreid++) {
794 // Check if we have the complete set of CPUs form the core
795
2/2
✓ Branch 1 taken 246 times.
✓ Branch 2 taken 204 times.
450 if (mu_is_subset(sys.core_masks_by_coreid[coreid].set, mask)) {
796 246 cores_count++;
797 }
798 }
799
800 33 return cores_count;
801 }
802
803 /* Return the number of complete cores in the mask.
804 * e.g.:
805 * core0: [0-1]
806 * core1: [2-3]
807 * core2: [4-5]
808 * cpuset: [0-4]
809 * returns 3
810 */
811 6 int mu_count_cores_intersecting_with_cpuset(const cpu_set_t *mask) {
812
813 6 int cores_count = 0;
814
815
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 6 times.
66 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
816 60 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
817 cpu_set_t intxn;
818
2/2
✓ Branch 0 taken 60 times.
✓ Branch 1 taken 60 times.
120 CPU_AND_S(mu_cpuset_alloc_size, &intxn, core_cpuset->set, mask);
819
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 28 times.
60 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
820 32 cores_count++;
821 }
822 }
823
824 6 return cores_count;
825 }
826
827 /* Return the id of the last complete core in the mask if any, otherwise return -1.
828 * e.g.:
829 * core0: [0-1]
830 * core1: [2-3]
831 * core2: [4-5]
832 * cpuset: [0-3]
833 * returns 1 (node1)
834 */
835 24 int mu_get_last_coreid(const cpu_set_t *mask){
836
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2 times.
170 for (int coreid = sys.num_cores-1; coreid >= 0 ; coreid--) {
837 // Check if we have the complete set of CPUs form the core
838
2/2
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 146 times.
168 if (mu_is_subset(sys.core_masks_by_coreid[coreid].set, mask)) {
839 22 return coreid;
840 }
841 }
842
843 2 return -1;
844 }
845
846 /* Disables the CPUs of the last complete core in the mask and returns its
847 * coreid if any, otherwise return -1.
848 * e.g.:
849 * core0: [0-1]
850 * core1: [2-3]
851 * core2: [4-5]
852 * cpuset: [2-5]
853 * returns 2 (node2)
854 * updated cpuset: [2-3]
855 */
856 22 int mu_take_last_coreid(cpu_set_t *mask) {
857 22 int last_coreid = mu_get_last_coreid(mask);
858
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (last_coreid == -1) return -1;
859 20 mu_xor(mask, mask, sys.core_masks_by_coreid[last_coreid].set);
860 20 return last_coreid;
861 }
862
863 /* Enables all the CPUs of the core
864 * e.g.:
865 * core0: [0-1]
866 * core1: [2-3]
867 * core2: [4-5]
868 * cpuset: []
869 * coreid: 1
870 * updated cpuset: [2-3]
871 */
872 2 void mu_set_core(cpu_set_t *mask, int coreid){
873 2 mu_or(mask, mask, sys.core_masks_by_coreid[coreid].set);
874 2 }
875
876 /* Disables all the CPUs of the core
877 * e.g.:
878 * core0: [0-1]
879 * core1: [2-3]
880 * core2: [4-5]
881 * cpuset: [0-5]
882 * coreid: 1
883 * updated cpuset: [0-1,4-5]
884 */
885 2 void mu_unset_core(cpu_set_t *mask, int coreid){
886 2 mu_subtract(mask, mask, sys.core_masks_by_coreid[coreid].set);
887 2 }
888
889 /* Basic mask utils functions that do not need to read system's topology,
890 * i.e., mostly mask operations */
891
892 2 void mu_zero(cpu_set_t *result) {
893 2 CPU_ZERO_S(mu_cpuset_alloc_size, result);
894 2 }
895
896 13 void mu_and(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
897
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 13 times.
41 CPU_AND_S(mu_cpuset_alloc_size, result, mask1, mask2);
898 13 }
899
900 43 void mu_or(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
901
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 43 times.
101 CPU_OR_S(mu_cpuset_alloc_size, result, mask1, mask2);
902 43 }
903
904 20 void mu_xor (cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
905
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_XOR_S(mu_cpuset_alloc_size, result, mask1, mask2);
906 20 }
907
908 4 bool mu_equal(const cpu_set_t *mask1, const cpu_set_t *mask2) {
909 4 return CPU_EQUAL_S(mu_cpuset_alloc_size, mask1, mask2) != 0;
910 }
911
912 /* Returns true is all bits in subset are set in superset */
913 754 bool mu_is_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
914 // The condition is true if the intersection is identical to subset
915 cpu_set_t intxn;
916
2/2
✓ Branch 0 taken 967 times.
✓ Branch 1 taken 754 times.
1721 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
917 754 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
918 }
919
920 /* Returns true is all bits in superset are set in subset */
921 9 bool mu_is_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
922 // The condition is true if the intersection is identical to subset
923 cpu_set_t intxn;
924
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9 times.
153 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
925 9 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
926 }
927
928 /* Returns true is all bits in subset are set in superset and they're not equal */
929 16 bool mu_is_proper_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
930 cpu_set_t intxn;
931
2/2
✓ Branch 0 taken 151 times.
✓ Branch 1 taken 16 times.
167 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
932 16 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
933
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 3 times.
16 && !CPU_EQUAL_S(mu_cpuset_alloc_size, subset, superset);
934 }
935
936 /* Returns true is all bits in superset are set in subset and they're not equal */
937 19 bool mu_is_proper_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
938 cpu_set_t intxn;
939
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 19 times.
173 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
940 19 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
941
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
19 && !CPU_EQUAL_S(mu_cpuset_alloc_size, superset, subset);
942 }
943
944 /* Return true if any bit is present in both sets */
945 23 bool mu_intersects(const cpu_set_t *mask1, const cpu_set_t *mask2) {
946 cpu_set_t intxn;
947
2/2
✓ Branch 0 taken 158 times.
✓ Branch 1 taken 23 times.
181 CPU_AND_S(mu_cpuset_alloc_size, &intxn, mask1, mask2);
948 23 return CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0;
949 }
950
951 /* Return the number of bits set in mask */
952 97 int mu_count(const cpu_set_t *mask) {
953 97 return CPU_COUNT_S(mu_cpuset_alloc_size, mask);
954 }
955
956 /* Return the minuend after subtracting the bits in subtrahend */
957 659 void mu_subtract(cpu_set_t *result, const cpu_set_t *minuend, const cpu_set_t *subtrahend) {
958 cpu_set_t xor;
959
2/2
✓ Branch 0 taken 719 times.
✓ Branch 1 taken 659 times.
1378 CPU_XOR_S(mu_cpuset_alloc_size, &xor, minuend, subtrahend);
960
2/2
✓ Branch 0 taken 719 times.
✓ Branch 1 taken 659 times.
1378 CPU_AND_S(mu_cpuset_alloc_size, result, minuend, &xor);
961 659 }
962
963 /* Return the one and only enabled CPU in mask, or -1 if count != 1 */
964 8 int mu_get_single_cpu(const cpu_set_t *mask) {
965
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
8 if (CPU_COUNT_S(mu_cpuset_alloc_size, mask) == 1) {
966 3 return mu_get_first_cpu(mask);
967 }
968 5 return -1;
969 }
970
971 /* some of the following functions have been inspired by:
972 * https://github.com/open-mpi/hwloc/blob/master/hwloc/bitmap.c */
973
974 /* Return the first enabled CPU in mask, or -1 if mask is empty */
975 4877 int mu_get_first_cpu(const cpu_set_t *mask) {
976
977
2/2
✓ Branch 0 taken 6515 times.
✓ Branch 1 taken 46 times.
6561 for (unsigned int i = 0; i < mu_cpuset_num_ulongs; ++i) {
978 6515 unsigned long bits = mask->__bits[i];
979
2/2
✓ Branch 0 taken 4831 times.
✓ Branch 1 taken 1684 times.
6515 if (bits) {
980 4831 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
981 }
982 }
983
984 46 return -1;
985 }
986
987 /* Return the last enabled CPU in mask, or -1 if mask is empty */
988 1273 int mu_get_last_cpu(const cpu_set_t *mask) {
989
990
2/2
✓ Branch 0 taken 2792 times.
✓ Branch 1 taken 3 times.
2795 for (unsigned int i = mu_cpuset_num_ulongs; i-- > 0; ) {
991 2792 unsigned long bits = mask->__bits[i];
992
2/2
✓ Branch 0 taken 1270 times.
✓ Branch 1 taken 1522 times.
2792 if (bits) {
993 /* glibc does not provide a fls function, there are more optimal
994 * solutions, but this function is not that critical */
995 1270 int cpuid = CPUS_PER_ULONG * i;
996
2/2
✓ Branch 0 taken 29270 times.
✓ Branch 1 taken 1270 times.
30540 while (bits >>= 1) {
997 29270 ++cpuid;
998 }
999 1270 return cpuid;
1000 }
1001 }
1002
1003 3 return -1;
1004 }
1005
1006 /* Return the next enabled CPU in mask after prev, or -1 if not found */
1007 7533 int mu_get_next_cpu(const cpu_set_t *mask, int prev) {
1008
1009
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7532 times.
7533 if (unlikely(prev < -1)) return -1;
1010
1011 7532 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1012
2/2
✓ Branch 0 taken 9972 times.
✓ Branch 1 taken 3343 times.
13315 i < mu_cpuset_num_ulongs; ++i) {
1013 9972 unsigned long bits = mask->__bits[i];
1014
1015 /* mask bitmap only if previous cpu belong to current iteration */
1016
4/4
✓ Branch 0 taken 9970 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7436 times.
✓ Branch 3 taken 2534 times.
9972 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1017 7436 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1018 }
1019
1020
2/2
✓ Branch 0 taken 4189 times.
✓ Branch 1 taken 5783 times.
9972 if (bits) {
1021 4189 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1022 }
1023 }
1024
1025 3343 return -1;
1026 }
1027
1028 /* Return the next unset CPU in mask after prev, or -1 if not found */
1029 1613 int mu_get_next_unset(const cpu_set_t *mask, int prev) {
1030
1031
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1612 times.
1613 if (unlikely(prev < -1)) return -1;
1032
1033 1612 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1034
2/2
✓ Branch 0 taken 1670 times.
✓ Branch 1 taken 27 times.
1697 i < mu_cpuset_num_ulongs; ++i) {
1035 1670 unsigned long bits = ~(mask->__bits[i]);
1036
1037 /* mask bitmap only if previous cpu belong to current iteration */
1038
4/4
✓ Branch 0 taken 1669 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1586 times.
✓ Branch 3 taken 83 times.
1670 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1039 1586 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1040 }
1041
1042
2/2
✓ Branch 0 taken 1585 times.
✓ Branch 1 taken 85 times.
1670 if (bits) {
1043 1585 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1044 }
1045 }
1046
1047 27 return -1;
1048 }
1049
1050 // mu_to_str and mu_parse_mask functions are used by DLB utilities
1051 // We export their dynamic symbols to avoid code duplication,
1052 // although they do not belong to the public API
1053 DLB_EXPORT_SYMBOL
1054 1553 const char* mu_to_str( const cpu_set_t *mask ) {
1055
1056 static __thread char buffer[CPU_SETSIZE*4];
1057 1553 char *b = buffer;
1058 1553 *(b++) = '[';
1059 1553 bool entry_made = false;
1060
2/2
✓ Branch 1 taken 1599 times.
✓ Branch 2 taken 1553 times.
3152 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1061 1599 cpuid = mu_get_next_cpu(mask, cpuid)) {
1062
1063 /* Find interval distance */
1064 1599 int next_unset = mu_get_next_unset(mask, cpuid);
1065 3174 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1066
2/2
✓ Branch 0 taken 1575 times.
✓ Branch 1 taken 24 times.
1599 : mu_get_last_cpu(mask) - cpuid;
1067
1068 /* Add ',' separator for subsequent entries */
1069
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 1527 times.
1599 if (entry_made) {
1070 72 *(b++) = ',';
1071 } else {
1072 1527 entry_made = true;
1073 }
1074
1075 /* Write element, pair or range */
1076
2/2
✓ Branch 0 taken 1258 times.
✓ Branch 1 taken 341 times.
1599 if (distance == 0) {
1077 1258 b += sprintf(b, "%d", cpuid);
1078
2/2
✓ Branch 0 taken 198 times.
✓ Branch 1 taken 143 times.
341 } else if (distance == 1) {
1079 198 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1080 198 ++cpuid;
1081 } else {
1082 143 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1083 143 cpuid += distance;
1084 }
1085 }
1086 1553 *(b++) = ']';
1087 1553 *b = '\0';
1088
1089 1553 return buffer;
1090 }
1091
1092 39 static void parse_64_bits_mask(cpu_set_t *mask, unsigned int offset, const char *str, int base) {
1093 39 unsigned long long number = strtoull(str, NULL, base);
1094 39 unsigned int i = offset;
1095
3/4
✓ Branch 0 taken 344 times.
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 344 times.
✗ Branch 3 not taken.
383 while (number > 0 && i < mu_cpuset_setsize) {
1096
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 278 times.
344 if (number & 1) {
1097
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 CPU_SET(i, mask);
1098 }
1099 344 ++i;
1100 344 number = number >> 1;
1101 }
1102 39 }
1103
1104 DLB_EXPORT_SYMBOL
1105 233 void mu_parse_mask( const char *str, cpu_set_t *mask ) {
1106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 233 times.
236 if (!str) return;
1107
1108 233 size_t str_len = strnlen(str, CPU_SETSIZE+1);
1109
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 232 times.
233 if (str_len > CPU_SETSIZE) return;
1110
1111 232 CPU_ZERO( mask );
1112
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 230 times.
232 if (str_len == 0) return;
1113
1114 regex_t regex_bitmask;
1115 regex_t regex_hexmask;
1116 regex_t regex_range;
1117 regex_t old_regex_bitmask;
1118
1119 /* Compile regular expressions */
1120
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 230 times.
230 if ( regcomp(&regex_bitmask, "^0[bB][0-1]+$", REG_EXTENDED|REG_NOSUB) ) {
1121 fatal0( "Could not compile regex");
1122 }
1123
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 230 times.
230 if ( regcomp(&regex_hexmask, "^0[xX][0-9,a-f,A-F]+$", REG_EXTENDED|REG_NOSUB) ) {
1124 fatal0( "Could not compile regex");
1125 }
1126
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 230 times.
230 if ( regcomp(&regex_range, "^[0-9,-]+$", REG_EXTENDED|REG_NOSUB) ) {
1127 fatal0( "Could not compile regex");
1128 }
1129
1130 /***** Deprecated *****/
1131
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 230 times.
230 if ( regcomp(&old_regex_bitmask, "^[0-1][0-1]+[bB]$", REG_EXTENDED|REG_NOSUB) ) {
1132 fatal0( "Could not compile regex");
1133 }
1134 /* Regular expression matches OLD bitmask, e.g.: 11110011b */
1135
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 227 times.
230 if ( !regexec(&old_regex_bitmask, str, 0, NULL, 0) ) {
1136 3 warning("The binary form xxxxb is deprecated, please use 0bxxxx.");
1137 // Parse
1138
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 for (unsigned int i=0; i<str_len; i++) {
1139
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
19 if ( str[i] == '1' && i < mu_cpuset_setsize ) {
1140
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 CPU_SET( i, mask );
1141 }
1142 }
1143 }
1144 /**********************/
1145
1146 /* Regular expression matches bitmask, e.g.: 0b11100001 */
1147
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 218 times.
227 else if ( !regexec(&regex_bitmask, str, 0, NULL, 0) ) {
1148 /* Ignore '0b' */
1149 9 str += 2;
1150
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strlen(str) <= 64) {
1151 6 parse_64_bits_mask(mask, 0, str, 2);
1152 } else {
1153 /* parse in chunks of 64 bits */
1154 3 char *str_copy = strdup(str);
1155 char *start_ptr;
1156 3 char *end_ptr = str_copy + strlen(str_copy);
1157 3 unsigned int offset = 0;
1158 do {
1159
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 start_ptr = strlen(str_copy) < 64 ? str_copy : end_ptr - 64;
1160 12 parse_64_bits_mask(mask, offset, start_ptr, 2);
1161 12 offset += 64;
1162 12 end_ptr = start_ptr;
1163 12 *end_ptr = '\0';
1164
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 } while (strlen(str_copy) > 0);
1165 3 free(str_copy);
1166 }
1167 }
1168
1169 /* Regular expression matches hexmask, e.g.: 0xE1 */
1170
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 208 times.
218 else if ( !regexec(&regex_hexmask, str, 0, NULL, 0) ) {
1171 /* Ignore '0x' */
1172 10 str += 2;
1173
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 if (strlen(str) <= 16) {
1174 7 parse_64_bits_mask(mask, 0, str, 16);
1175 } else {
1176 /* parse in chunks of 64 bits (16 hex digits) */
1177 3 char *str_copy = strdup(str);
1178 char *start_ptr;
1179 3 char *end_ptr = str_copy + strlen(str_copy);
1180 3 unsigned int offset = 0;
1181 do {
1182
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 start_ptr = strlen(str_copy) < 16 ? str_copy : end_ptr - 16;
1183 14 parse_64_bits_mask(mask, offset, start_ptr, 16);
1184 14 offset += 64;
1185 14 end_ptr = start_ptr;
1186 14 *end_ptr = '\0';
1187
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 } while (strlen(str_copy) > 0);
1188 3 free(str_copy);
1189 }
1190 }
1191
1192 /* Regular expression matches range, e.g.: 0,5-7 */
1193
1/2
✓ Branch 1 taken 208 times.
✗ Branch 2 not taken.
208 else if ( !regexec(&regex_range, str, 0, NULL, 0) ) {
1194 // Parse
1195 208 const char *ptr = str;
1196 char *endptr;
1197
2/2
✓ Branch 0 taken 322 times.
✓ Branch 1 taken 208 times.
530 while ( ptr < str+strlen(str) ) {
1198 // Discard junk at the left
1199
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 301 times.
322 if ( !isdigit(*ptr) ) { ptr++; continue; }
1200
1201 301 unsigned long start_ = strtoul( ptr, &endptr, 10 );
1202 301 unsigned long start = start_ < mu_cpuset_setsize ? start_ : mu_cpuset_setsize;
1203 301 ptr = endptr;
1204
1205 // Single element
1206
5/6
✓ Branch 0 taken 229 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 143 times.
✓ Branch 4 taken 158 times.
✗ Branch 5 not taken.
301 if ( (*ptr == ',' || *ptr == '\0') && start < mu_cpuset_setsize ) {
1207
1/2
✓ Branch 0 taken 158 times.
✗ Branch 1 not taken.
158 CPU_SET( start, mask );
1208 158 ptr++;
1209 158 continue;
1210 }
1211 // Range
1212
1/2
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
143 else if ( *ptr == '-' ) {
1213 // Discard '-' and possible junk
1214 143 ptr++;
1215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 143 times.
143 if ( !isdigit(*ptr) ) { ptr++; continue; }
1216
1217 143 unsigned long end_ = strtoul( ptr, &endptr, 10 );
1218 143 unsigned long end = end_ < mu_cpuset_setsize ? end_ : mu_cpuset_setsize;
1219 143 ptr = endptr;
1220
1221 // Valid range
1222
1/2
✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
143 if ( end > start ) {
1223
4/4
✓ Branch 0 taken 1926 times.
✓ Branch 1 taken 142 times.
✓ Branch 2 taken 1925 times.
✓ Branch 3 taken 1 times.
2068 for ( unsigned long i=start; i<=end && i<mu_cpuset_setsize; i++ ) {
1224
1/2
✓ Branch 0 taken 1925 times.
✗ Branch 1 not taken.
1925 CPU_SET( i, mask );
1225 }
1226 }
1227 143 continue;
1228 }
1229 // Unexpected token
1230 else { }
1231 }
1232 }
1233 /* Regular expression does not match */
1234 else { }
1235
1236 230 regfree(&regex_bitmask);
1237 230 regfree(&regex_hexmask);
1238 230 regfree(&regex_range);
1239 230 regfree(&old_regex_bitmask);
1240
1241
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 230 times.
230 if ( CPU_COUNT(mask) == 0 ) {
1242 warning( "Parsed mask \"%s\" does not seem to be a valid mask\n", str );
1243 }
1244 }
1245
1246 /* Equivalent to mu_to_str, but generate quoted string in str up to namelen-1 bytes */
1247 8 void mu_get_quoted_mask(const cpu_set_t *mask, char *str, size_t namelen) {
1248
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (namelen < 2)
1249 return;
1250
1251 8 char *b = str;
1252 8 *(b++) = '"';
1253 8 size_t bytes = 1;
1254 8 bool entry_made = false;
1255
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
16 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1256 8 cpuid = mu_get_next_cpu(mask, cpuid)) {
1257
1258 /* Find interval distance */
1259 8 int next_unset = mu_get_next_unset(mask, cpuid);
1260 14 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1261
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 : mu_get_last_cpu(mask) - cpuid;
1262
1263 /* Add ',' separator for subsequent entries */
1264
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (entry_made) {
1265
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (bytes+1 < namelen) {
1266 1 *(b++) = ',';
1267 1 ++bytes;
1268 }
1269 } else {
1270 7 entry_made = true;
1271 }
1272
1273 /* Write element, pair or range */
1274
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (distance == 0) {
1275 3 int len = snprintf(NULL, 0, "%d", cpuid);
1276
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1277 3 b += sprintf(b, "%d", cpuid);
1278 3 bytes += len;
1279 }
1280
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 } else if (distance == 1) {
1281 3 int len = snprintf(NULL, 0, "%d,%d", cpuid, cpuid+1);
1282
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (bytes+len < namelen) {
1283 3 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1284 3 bytes += len;
1285 3 ++cpuid;
1286 }
1287 } else {
1288 2 int len = snprintf(NULL, 0, "%d-%d", cpuid, cpuid+distance);
1289
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bytes+len < namelen) {
1290 2 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1291 2 bytes += len;
1292 2 cpuid += distance;
1293 }
1294 }
1295 }
1296
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (bytes+1 < namelen) {
1297 8 *(b++) = '"';
1298 8 ++bytes;
1299 }
1300 8 *b = '\0';
1301 }
1302
1303 1 char * mu_parse_to_slurm_format(const cpu_set_t *mask) {
1304 1 char *str = malloc((mu_cpuset_setsize >> 2) + 3);
1305 if (str < 0)
1306 return NULL;
1307 1 unsigned int offset = 2;
1308 1 unsigned long long val = 0;
1309 1 const int threshold = 4;
1310 1 sprintf(str, "0x");
1311
2/2
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 1 times.
131 for (int cpuid = mu_get_last_cpu(mask); cpuid >= 0; --cpuid) {
1312
5/6
✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 121 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 121 times.
130 if(CPU_ISSET(cpuid, mask)) {
1313 9 val |= 1 << (cpuid % threshold);
1314 }
1315
4/4
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 32 times.
✓ Branch 3 taken 97 times.
130 if (cpuid > 0 && cpuid % threshold == 0) {
1316 32 sprintf(str+offset, "%llx", val);
1317 32 val = 0;
1318 32 offset++;
1319 }
1320 }
1321 1 sprintf(str+offset, "%llx", val);
1322 1 return str;
1323 }
1324
1325 4 bool mu_equivalent_masks(const char *str1, const char *str2) {
1326 cpu_set_t mask1, mask2;
1327 4 mu_parse_mask(str1, &mask1);
1328 4 mu_parse_mask(str2, &mask2);
1329 4 return CPU_EQUAL(&mask1, &mask2);
1330 }
1331
1332
1333 3120 static int cmp_cpuids(cpuid_t cpuid1, cpuid_t cpuid2) {
1334 3120 int cpu1_core_id = mu_get_core_id(cpuid1);
1335 3120 int cpu2_core_id = mu_get_core_id(cpuid2);
1336
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 2640 times.
3120 if (cpu1_core_id == cpu2_core_id) {
1337 480 return cpuid1 - cpuid2;
1338 } else {
1339 2640 return cpu1_core_id - cpu2_core_id;
1340 }
1341 }
1342
1343 /* Compare CPUs so that:
1344 * - owned CPUs first, in ascending order
1345 * - non-owned later, starting from the first owned, then ascending
1346 * e.g.: system: [0-7], owned: [3-5]
1347 * cpu_list = {4,5,6,7,0,1,2,3}
1348 */
1349 1543 int mu_cmp_cpuids_by_ownership(const void *cpuid1, const void *cpuid2, void *mask) {
1350 /* Expand arguments */
1351 1543 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1352 1543 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1353 1543 cpu_set_t *process_mask = mask;
1354
1355
5/6
✓ Branch 0 taken 1543 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 737 times.
✓ Branch 3 taken 806 times.
✓ Branch 4 taken 737 times.
✓ Branch 5 taken 806 times.
1543 if (CPU_ISSET(_cpuid1, process_mask)) {
1356
5/6
✓ Branch 0 taken 737 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 529 times.
✓ Branch 3 taken 208 times.
✓ Branch 4 taken 529 times.
✓ Branch 5 taken 208 times.
737 if (CPU_ISSET(_cpuid2, process_mask)) {
1357 /* both CPUs are owned: ascending order */
1358 529 return cmp_cpuids(_cpuid1, _cpuid2);
1359 } else {
1360 /* cpuid2 is NOT owned and cpuid1 IS */
1361 208 return -1;
1362 }
1363 } else {
1364
5/6
✓ Branch 0 taken 806 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 223 times.
✓ Branch 3 taken 583 times.
✓ Branch 4 taken 223 times.
✓ Branch 5 taken 583 times.
806 if (CPU_ISSET(_cpuid2, process_mask)) {
1365 /* cpuid2 IS owned and cpuid1 is NOT */
1366 223 return 1;
1367 } else {
1368 /* none is owned */
1369 583 int first_cpu = mu_get_first_cpu(process_mask);
1370 583 int first_core = mu_get_core_id(first_cpu);
1371 583 int cpu1_core_id = mu_get_core_id(_cpuid1);
1372 583 int cpu2_core_id = mu_get_core_id(_cpuid2);
1373
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 232 times.
583 if ((cpu1_core_id > first_core
1374
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 304 times.
351 && cpu2_core_id > first_core)
1375
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 47 times.
279 || (cpu1_core_id < first_core
1376
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 58 times.
232 && cpu2_core_id < first_core)) {
1377 /* Both CPUs are either before or after the process mask */
1378 478 return cmp_cpuids(_cpuid1, _cpuid2);
1379 } else {
1380 /* Compare with respect to process mask */
1381 105 return cmp_cpuids(first_cpu, _cpuid1);
1382 }
1383 }
1384 }
1385 }
1386
1387 /* Compare CPUs so that:
1388 * - CPUs are sorted according to the affinity array:
1389 * * affinity: array of cpu_set_t, each position represents a level
1390 * in the affinity, the last position is an empty cpu set.
1391 * (PRE: each affinity level is a superset of the previous level mask)
1392 * - Sorted by affinity level in ascending order
1393 * - non-owned later, starting from the first owned, then ascending
1394 * e.g.: affinity: {{6-7}, {4-7}, {0-7}, {}}
1395 * sorted_cpu_list = {6,7,4,5,0,1,2,3}
1396 */
1397 2739 int mu_cmp_cpuids_by_affinity(const void *cpuid1, const void *cpuid2, void *affinity) {
1398 /* Expand arguments */
1399 2739 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1400 2739 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1401 2739 cpu_set_t *_affinity = affinity;
1402
1403 /* Find affinity level of each CPU */
1404 2739 int cpu1_level = 0;
1405 2739 int cpu2_level = 0;
1406 2739 cpu_set_t *mask = _affinity;
1407
2/2
✓ Branch 1 taken 6143 times.
✓ Branch 2 taken 2739 times.
8882 while(CPU_COUNT(mask) > 0) {
1408
5/6
✓ Branch 0 taken 6143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3940 times.
✓ Branch 3 taken 2203 times.
✓ Branch 4 taken 2203 times.
✓ Branch 5 taken 3940 times.
6143 if (!CPU_ISSET(_cpuid1, mask)) {
1409 2203 ++cpu1_level;
1410 }
1411
5/6
✓ Branch 0 taken 6143 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3869 times.
✓ Branch 3 taken 2274 times.
✓ Branch 4 taken 2274 times.
✓ Branch 5 taken 3869 times.
6143 if (!CPU_ISSET(_cpuid2, mask)) {
1412 2274 ++cpu2_level;
1413 }
1414 6143 ++mask;
1415 }
1416
1417 /* If levels differ, sort levels in ascending order */
1418
2/2
✓ Branch 0 taken 731 times.
✓ Branch 1 taken 2008 times.
2739 if (cpu1_level != cpu2_level) {
1419 731 return cpu1_level - cpu2_level;
1420 }
1421
1422 /* If both are level 0, sort in ascending order */
1423
2/2
✓ Branch 0 taken 712 times.
✓ Branch 1 taken 1296 times.
2008 if (cpu1_level == 0) {
1424 712 return cmp_cpuids(_cpuid1, _cpuid2);
1425 }
1426
1427 /* If both are level 1, sort from the first CPU in level 0 */
1428 /* e.g.: level0: [2,3], level1: [0,7] -> [4,5,6,7,0,1] */
1429
2/2
✓ Branch 0 taken 1015 times.
✓ Branch 1 taken 281 times.
1296 if (cpu1_level == 1) {
1430 1015 cpu_set_t *level0_mask = _affinity;
1431 1015 int first_cpu = mu_get_first_cpu(level0_mask);
1432 1015 int first_core = mu_get_core_id(first_cpu);
1433 1015 int cpu1_core_id = mu_get_core_id(_cpuid1);
1434 1015 int cpu2_core_id = mu_get_core_id(_cpuid2);
1435
2/2
✓ Branch 0 taken 806 times.
✓ Branch 1 taken 209 times.
1015 if ((cpu1_core_id > first_core
1436
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 790 times.
806 && cpu2_core_id > first_core)
1437
2/2
✓ Branch 0 taken 209 times.
✓ Branch 1 taken 16 times.
225 || (cpu1_core_id < first_core
1438
2/2
✓ Branch 0 taken 101 times.
✓ Branch 1 taken 108 times.
209 && cpu2_core_id < first_core)) {
1439 /* Both CPUs are either before or after the process mask */
1440 891 return cmp_cpuids(_cpuid1, _cpuid2);
1441 } else {
1442 /* Compare with respect to process mask */
1443 124 return cmp_cpuids(first_cpu, _cpuid1);
1444 }
1445 }
1446
1447 /* TODO: compute numa distance */
1448 /* Levels 2+, sort in ascending order */
1449 281 return cmp_cpuids(_cpuid1, _cpuid2);
1450 }
1451
1452
1453 /*********************************************************************************/
1454 /* Mask utils testing functions */
1455 /*********************************************************************************/
1456
1457 67 static void print_sys_info(void) {
1458
1459
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 25 times.
67 verbose(VB_AFFINITY, "System mask: %s", mu_to_str(sys.sys_mask.set));
1460
1461
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 67 times.
139 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
1462
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 59 times.
72 verbose(VB_AFFINITY, "Node %d mask: %s",
1463 node_id, mu_to_str(sys.node_masks[node_id].set));
1464 }
1465
1466
2/2
✓ Branch 0 taken 1094 times.
✓ Branch 1 taken 67 times.
1161 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
1467 1094 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
1468
1/2
✓ Branch 0 taken 1094 times.
✗ Branch 1 not taken.
1094 if (core_cpuset->count > 0) {
1469
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 674 times.
1094 verbose(VB_AFFINITY, "Core %d mask: %s",
1470 core_id, mu_to_str(core_cpuset->set));
1471 }
1472 }
1473
1474 67 for (int cpuid = sys.sys_mask.first_cpuid;
1475
4/4
✓ Branch 0 taken 1320 times.
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 1319 times.
✓ Branch 3 taken 1 times.
1386 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
1476 1319 cpuid = mu_get_next_cpu(sys.sys_mask.set, cpuid)) {
1477 1319 const mu_cpuset_t *core_cpuset = sys.core_masks_by_cpuid[cpuid];
1478
3/4
✓ Branch 0 taken 1181 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 1181 times.
✗ Branch 3 not taken.
1319 if (core_cpuset && core_cpuset->count > 0) {
1479
2/2
✓ Branch 0 taken 447 times.
✓ Branch 1 taken 734 times.
1181 verbose(VB_AFFINITY, "CPU %d core mask: %s",
1480 cpuid, mu_to_str(core_cpuset->set));
1481 }
1482 }
1483 67 }
1484
1485 2 bool mu_testing_is_initialized(void) {
1486 2 return mu_initialized;
1487 }
1488
1489 51 void mu_testing_set_sys_size(int size) {
1490 51 init_system(size, size, 1);
1491 51 print_sys_info();
1492 51 }
1493
1494 3 void mu_testing_set_sys(unsigned int num_cpus, unsigned int num_cores,
1495 unsigned int num_nodes) {
1496 3 init_system(num_cpus, num_cores, num_nodes);
1497 3 print_sys_info();
1498 3 }
1499
1500 12 void mu_testing_set_sys_masks(const cpu_set_t *sys_mask,
1501 const cpu_set_t *core_masks, unsigned int num_cores,
1502 const cpu_set_t *node_masks, unsigned int num_nodes) {
1503 12 init_system_masks(sys_mask, core_masks, num_cores, node_masks, num_nodes);
1504 12 print_sys_info();
1505 12 }
1506
1507 1 void mu_testing_init_nohwloc(void) {
1508 1 init_mu_struct();
1509 1 parse_system_files();
1510 1 mu_initialized = true;
1511 1 print_sys_info();
1512 1 }
1513