GCC Code Coverage Report


Directory: src/
File: src/support/mask_utils.c
Date: 2026-09-15 07:37:49
Exec Total Coverage
Lines: 694 715 97.1%
Functions: 67 67 100.0%
Branches: 414 494 83.8%

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 1320 static inline void mu_cpuset_from_glibc_sched_affinity(mu_cpuset_t *mu_cpuset,
67 const cpu_set_t *cpu_set) {
68 5280 *mu_cpuset = (const mu_cpuset_t) {
69 1320 .set = CPU_ALLOC(mu_cpuset_setsize),
70 .alloc_size = mu_cpuset_alloc_size,
71 1320 .count = CPU_COUNT_S(mu_cpuset_alloc_size, cpu_set),
72 1320 .first_cpuid = mu_get_first_cpu(cpu_set),
73 1320 .last_cpuid = mu_get_last_cpu(cpu_set),
74 };
75 1320 memcpy(mu_cpuset->set, cpu_set, mu_cpuset_alloc_size);
76 1320 }
77
78 #ifdef HWLOC_LIB
79 292 static inline void mu_cpuset_from_hwloc_bitmap(mu_cpuset_t *mu_cpuset,
80 hwloc_const_bitmap_t bitmap, hwloc_topology_t topology) {
81 292 *mu_cpuset = (const mu_cpuset_t) {
82 292 .set = CPU_ALLOC(mu_cpuset_setsize),
83 .alloc_size = mu_cpuset_alloc_size,
84 292 .count = hwloc_bitmap_weight(bitmap),
85 292 .first_cpuid = hwloc_bitmap_first(bitmap),
86 292 .last_cpuid = hwloc_bitmap_last(bitmap),
87 };
88 292 hwloc_cpuset_to_glibc_sched_affinity(topology, bitmap, mu_cpuset->set,
89 mu_cpuset_alloc_size);
90 292 }
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 88 static void init_mu_struct(void) {
116 88 sys = (const mu_system_loc_t) {};
117 88 }
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 72 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 58 times.
✓ Branch 1 taken 14 times.
72 if (mu_initialized) {
127 58 mu_finalize();
128 } else {
129 14 init_mu_struct();
130 }
131
132 /*** System ***/
133 72 sys.num_cpus = mu_get_last_cpu(sys_mask) + 1;
134 72 mu_cpuset_setsize = sys.num_cpus;
135 72 mu_cpuset_num_ulongs = (mu_cpuset_setsize + CPUS_PER_ULONG - 1) / CPUS_PER_ULONG;
136 72 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
137 72 sys.sys_mask = (const mu_cpuset_t) {
138 72 .set = CPU_ALLOC(mu_cpuset_setsize),
139 72 .count = CPU_COUNT_S(mu_cpuset_alloc_size, sys_mask),
140 .first_cpuid = 0,
141 72 .last_cpuid = mu_cpuset_setsize - 1,
142 };
143 72 memcpy(sys.sys_mask.set, sys_mask, mu_cpuset_alloc_size);
144
145 /*** Cores ***/
146 72 sys.num_cores = num_cores;
147 72 sys.core_masks_by_coreid = malloc(sys.num_cores * sizeof(mu_cpuset_t));
148 72 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
149
2/2
✓ Branch 0 taken 1238 times.
✓ Branch 1 taken 72 times.
1310 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
150 1238 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
151 1238 mu_cpuset_from_glibc_sched_affinity(core_cpuset, &core_masks[core_id]);
152 1238 for (int cpuid = core_cpuset->first_cpuid;
153
4/4
✓ Branch 0 taken 1326 times.
✓ Branch 1 taken 1237 times.
✓ Branch 2 taken 1325 times.
✓ Branch 3 taken 1 times.
2563 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
154 1325 cpuid = mu_get_next_cpu(core_cpuset->set, cpuid)) {
155 /* Save reference to another array indexed by cpuid */
156 1325 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
157 }
158 }
159
160 /*** NUMA Nodes ***/
161 72 sys.num_nodes = num_nodes;
162 72 sys.node_masks = malloc(sys.num_nodes * sizeof(mu_cpuset_t));
163
2/2
✓ Branch 0 taken 77 times.
✓ Branch 1 taken 72 times.
149 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
164 77 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_masks[node_id]);
165 }
166
167 72 mu_initialized = true;
168 72 }
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 60 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 60 CPU_ZERO(&sys_mask);
179
2/2
✓ Branch 0 taken 1230 times.
✓ Branch 1 taken 60 times.
1290 for (unsigned int cpuid = 0; cpuid < num_cpus; ++cpuid) {
180
1/2
✓ Branch 0 taken 1230 times.
✗ Branch 1 not taken.
1230 CPU_SET(cpuid, &sys_mask);
181 }
182
183 /*** Cores ***/
184 60 unsigned int cpus_per_core = num_cpus / num_cores;
185 60 cpu_set_t *core_masks = calloc(num_cores, sizeof(cpu_set_t));
186
2/2
✓ Branch 0 taken 1190 times.
✓ Branch 1 taken 60 times.
1250 for (unsigned int core_id = 0; core_id < num_cores; ++core_id) {
187 1190 for (unsigned int cpuid = core_id * cpus_per_core;
188
2/2
✓ Branch 0 taken 1230 times.
✓ Branch 1 taken 1190 times.
2420 cpuid < (core_id+1) * cpus_per_core; ++cpuid) {
189
1/2
✓ Branch 0 taken 1230 times.
✗ Branch 1 not taken.
1230 CPU_SET(cpuid, &core_masks[core_id]);
190 }
191 }
192
193 /*** NUMA Nodes ***/
194 60 unsigned int cpus_per_node = num_cpus / num_nodes;
195 60 cpu_set_t *node_masks = calloc(num_nodes, sizeof(cpu_set_t));
196
2/2
✓ Branch 0 taken 63 times.
✓ Branch 1 taken 60 times.
123 for (unsigned int node_id = 0; node_id < num_nodes; ++node_id) {
197 63 for (unsigned int cpuid = node_id * cpus_per_node;
198
2/2
✓ Branch 0 taken 1230 times.
✓ Branch 1 taken 63 times.
1293 cpuid < (node_id+1) * cpus_per_node; ++cpuid) {
199
1/2
✓ Branch 0 taken 1230 times.
✗ Branch 1 not taken.
1230 CPU_SET(cpuid, &node_masks[node_id]);
200 }
201 }
202
203 60 init_system_masks(&sys_mask, core_masks, num_cores, node_masks, num_nodes);
204 60 free(core_masks);
205 60 free(node_masks);
206 60 }
207
208 73 static int parse_hwloc(void) {
209 #ifdef HWLOC_LIB
210 /* Check runtime library compatibility */
211 73 unsigned int hwloc_version = hwloc_get_api_version();
212
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 73 times.
73 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 73 hwloc_topology_init(&topology);
219 73 hwloc_topology_load(topology);
220
221 /*** System ***/
222 73 hwloc_obj_t machine = hwloc_get_obj_by_type(topology, HWLOC_OBJ_MACHINE, 0);
223 73 sys.num_cpus = hwloc_bitmap_last(machine->cpuset) + 1;
224 73 mu_cpuset_setsize = sys.num_cpus;
225 #if HWLOC_API_VERSION >= 0x00020100
226 73 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 73 mu_cpuset_alloc_size = mu_cpuset_num_ulongs * sizeof(unsigned long);
231 73 mu_cpuset_from_hwloc_bitmap(&sys.sys_mask, machine->cpuset, topology);
232
233 /*** Cores ***/
234 73 hwloc_obj_type_t core = HWLOC_OBJ_CORE;
235 73 sys.num_cores = hwloc_get_nbobjs_by_type(topology, core);
236 73 sys.core_masks_by_coreid = calloc(sys.num_cores, sizeof(mu_cpuset_t));
237 73 sys.core_masks_by_cpuid = calloc(sys.num_cpus, sizeof(mu_cpuset_t*));
238
2/2
✓ Branch 0 taken 146 times.
✓ Branch 1 taken 73 times.
219 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
239 146 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, core, core_id);
240 146 mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
241 146 mu_cpuset_from_hwloc_bitmap(core_cpuset, obj->cpuset, topology);
242 146 for (int cpuid = core_cpuset->first_cpuid;
243
3/4
✓ Branch 0 taken 146 times.
✓ Branch 1 taken 146 times.
✓ Branch 2 taken 146 times.
✗ Branch 3 not taken.
292 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
244 146 cpuid = hwloc_bitmap_next(obj->cpuset, cpuid)) {
245 /* Save reference to another array indexed by cpuid */
246 146 sys.core_masks_by_cpuid[cpuid] = core_cpuset;
247 }
248 }
249
250 /*** NUMA Nodes ***/
251 73 hwloc_obj_type_t node = HWLOC_OBJ_NODE;
252 73 sys.num_nodes = hwloc_get_nbobjs_by_type(topology, node);
253 73 sys.node_masks = calloc(sys.num_nodes, sizeof(mu_cpuset_t));
254
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 73 times.
146 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
255 73 hwloc_obj_t obj = hwloc_get_obj_by_type(topology, node, node_id);
256 73 mu_cpuset_from_hwloc_bitmap(&sys.node_masks[node_id],
257 73 obj->cpuset, topology);
258 }
259
260 73 hwloc_topology_destroy(topology);
261
262 73 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 int prev_num_nodes = num_nodes;
482 1 num_nodes = max_int(num_nodes, node_id + 1);
483 1 mu_cpuset_t *p = realloc(sys.node_masks, num_nodes*sizeof(mu_cpuset_t));
484
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 fatal_cond(!p, "realloc failed");
485 1 sys.node_masks = p;
486
487 /* Everytime we reallocate, we need to initialize
488 * [prev_size..new_size-1] with empty elements */
489
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 for (int i = prev_num_nodes; i < node_id; ++i) {
490 mu_cpuset_t *node_cpuset = &sys.node_masks[i];
491 mu_cpuset_from_glibc_sched_affinity(node_cpuset, &empty_mask);
492 }
493
494 /* New value */
495 1 mu_cpuset_from_glibc_sched_affinity(&sys.node_masks[node_id], &node_mask);
496 }
497 }
498 }
499 1 closedir(dir);
500 }
501 1 sys.num_nodes = num_nodes;
502
503 /* Fallback if some info could not be parsed */
504
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (sys.sys_mask.count == 0) {
505 int nproc_onln = sysconf(_SC_NPROCESSORS_ONLN);
506 fatal_cond(nproc_onln <= 0, "Cannot obtain system size. Contact us at "
507 PACKAGE_BUGREPORT " or configure DLB with HWLOC support.");
508 init_system(nproc_onln, nproc_onln, 1);
509 }
510 1 }
511
512
513 /*********************************************************************************/
514 /* Mask utils public functions */
515 /*********************************************************************************/
516
517 183 void mu_init( void ) {
518
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 110 times.
183 if ( !mu_initialized ) {
519 73 init_mu_struct();
520
521 #if defined IS_BGQ_MACHINE
522 enum { BGQ_NUM_CPUS = 64 };
523 enum { BGQ_NUM_CORES = 16 };
524 enum { BGQ_NUM_NODES = 1 };
525 init_system(BGQ_NUM_CPUS, BGQ_NUM_CORES, BGQ_NUM_NODES);
526 #else
527 /* Try to parse HW info from HWLOC first */
528
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 73 times.
73 if (parse_hwloc() != 0) {
529 /* Fallback to system files if needed */
530 parse_system_files();
531 }
532
533 73 mu_initialized = true;
534 #endif
535 }
536 183 }
537
538 /* This function used to be declared as destructor but it may be dangerous
539 * with the OpenMP / DLB finalization at destruction time. */
540 104 void mu_finalize( void ) {
541
542 104 CPU_FREE(sys.sys_mask.set);
543
544 /* Nodes */
545
2/2
✓ Branch 0 taken 107 times.
✓ Branch 1 taken 104 times.
211 for (unsigned int i = 0; i < sys.num_nodes; ++i) {
546 107 CPU_FREE(sys.node_masks[i].set);
547 }
548 104 free(sys.node_masks);
549
550 /* Cores per core id */
551
2/2
✓ Branch 0 taken 910 times.
✓ Branch 1 taken 104 times.
1014 for (unsigned int i = 0; i < sys.num_cores; ++i) {
552 910 CPU_FREE(sys.core_masks_by_coreid[i].set);
553 }
554 104 free(sys.core_masks_by_coreid);
555
556 /* Cores per CPU id (just references) */
557 104 free(sys.core_masks_by_cpuid);
558
559 104 sys = (const mu_system_loc_t) {};
560 104 mu_initialized = false;
561 104 mu_cpuset_setsize = CPU_SETSIZE;
562 104 mu_cpuset_alloc_size = CPU_ALLOC_SIZE(CPU_SETSIZE);
563 104 mu_cpuset_num_ulongs = CPU_ALLOC_SIZE(CPU_SETSIZE) / sizeof(unsigned long);
564 104 }
565
566 54 int mu_get_system_count( void ) {
567
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 51 times.
54 if (unlikely(!mu_initialized)) mu_init();
568 54 return sys.sys_mask.count;
569 }
570
571 15728 int mu_get_system_size( void ) {
572
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 15702 times.
15728 if (unlikely(!mu_initialized)) mu_init();
573 15728 return sys.sys_mask.last_cpuid + 1;
574 }
575
576 165 void mu_get_system_mask(cpu_set_t *mask) {
577
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 164 times.
165 if (unlikely(!mu_initialized)) mu_init();
578 165 CPU_ZERO(mask);
579 165 memcpy(mask, sys.sys_mask.set, mu_cpuset_alloc_size);
580 165 }
581
582 12 int mu_get_system_hwthreads_per_core(void) {
583
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (unlikely(!mu_initialized)) mu_init();
584
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 fatal_cond(sys.core_masks_by_coreid[0].count == 0,
585 "Number of hardware threads per core is 0. Please report bug at " PACKAGE_BUGREPORT);
586 12 return sys.core_masks_by_coreid[0].count;
587 }
588
589 12 int mu_get_system_num_nodes(void) {
590 12 return sys.num_nodes;
591 }
592
593 7 int mu_get_system_cores_per_node(void) {
594 7 return mu_count_cores(sys.node_masks[0].set);
595 }
596
597 8 void mu_get_system_description(print_buffer_t *buffer) {
598
599 #define TAB " "
600
601 /* Should be enough to hold up a list of > 1024 CPUs */
602 enum { LINE_BUFFER_SIZE = 8096 };
603 char line[LINE_BUFFER_SIZE];
604 char *l;
605
606 8 printbuffer_init(buffer);
607
608 // CPUs: N (C cores x T threads)
609 8 l = line;
610 8 l += sprintf(line, TAB"CPUs: %d", mu_get_system_size());
611
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4 times.
8 if (mu_system_has_smt()) {
612 4 sprintf(l, " (%d cores x %d threads)",
613 mu_get_num_cores(), mu_get_system_hwthreads_per_core());
614 } else {
615 4 sprintf(l, " (%d cores)", mu_get_num_cores());
616 }
617 8 printbuffer_append(buffer, line);
618
619 // NUMA nodes: N (C cores per NUMA node)
620 8 l = line;
621 8 l += sprintf(line, TAB"NUMA nodes: %d", mu_get_system_num_nodes());
622
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 5 times.
8 if (sys.num_nodes > 1) {
623 3 sprintf(l, " (%d cores per NUMA node)", mu_get_system_cores_per_node());
624 }
625 8 printbuffer_append(buffer, line);
626
627 // System mask
628 8 printbuffer_append_no_newline(buffer, TAB"System mask: ");
629 8 printbuffer_append(buffer, mu_to_str(sys.sys_mask.set));
630
631 // NUMA node masks
632 8 printbuffer_append_no_newline(buffer, TAB"NUMA node masks: ");
633
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) {
634 13 printbuffer_append_no_newline(buffer, mu_to_str(sys.node_masks[node_id].set));
635
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
13 if (node_id + 1 < sys.num_nodes) {
636 5 printbuffer_append_no_newline(buffer, ", ");
637 }
638 }
639 8 printbuffer_append(buffer, "");
640
641 // Core masks
642 8 printbuffer_append_no_newline(buffer, TAB"Core masks: ");
643
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) {
644 358 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
645
1/2
✓ Branch 0 taken 358 times.
✗ Branch 1 not taken.
358 if (core_cpuset->count > 0) {
646 358 printbuffer_append_no_newline(buffer, mu_to_str(core_cpuset->set));
647
2/2
✓ Branch 0 taken 350 times.
✓ Branch 1 taken 8 times.
358 if (core_id + 1 < sys.num_cores) {
648 350 printbuffer_append_no_newline(buffer, ", ");
649 }
650 }
651 }
652 8 printbuffer_append(buffer, "");
653 8 }
654
655 97 bool mu_system_has_smt(void) {
656
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 97 times.
97 if (unlikely(!mu_initialized)) mu_init();
657 97 return sys.core_masks_by_coreid[0].count > 1;
658 }
659
660 9 int mu_get_num_cores(void) {
661
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (unlikely(!mu_initialized)) mu_init();
662
663 /* Not likely, but sys.num_cores may not be the number of valid cores */
664 9 int num_cores = 0;
665
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) {
666
1/2
✓ Branch 0 taken 360 times.
✗ Branch 1 not taken.
360 if (sys.core_masks_by_coreid[core_id].count > 0) {
667 360 ++num_cores;
668 }
669 }
670
671 9 return num_cores;
672 }
673
674 14782 int mu_get_core_id(int cpuid) {
675
676
4/4
✓ Branch 0 taken 14764 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 14761 times.
14782 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return -1;
677
678
2/2
✓ Branch 0 taken 294284 times.
✓ Branch 1 taken 1 times.
294285 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
679
5/6
✓ Branch 0 taken 294284 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14760 times.
✓ Branch 3 taken 279524 times.
✓ Branch 4 taken 14760 times.
✓ Branch 5 taken 279524 times.
294284 if (CPU_ISSET_S(cpuid, mu_cpuset_alloc_size,
680 sys.core_masks_by_coreid[core_id].set)) {
681 14760 return core_id;
682 }
683 }
684
685 1 return -1;
686 }
687
688 712 const mu_cpuset_t* mu_get_core_mask(int cpuid) {
689
690
3/4
✓ Branch 0 taken 712 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 710 times.
712 if (cpuid < 0 || (unsigned)cpuid >= sys.num_cpus) return NULL;
691
692 710 return sys.core_masks_by_cpuid[cpuid];
693 }
694
695 44 const mu_cpuset_t* mu_get_core_mask_by_coreid(int core_id) {
696
697
2/4
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
44 if (core_id < 0 || (unsigned)core_id >= sys.num_cores) return NULL;
698
699 44 return &sys.core_masks_by_coreid[core_id];
700 }
701
702 /* Return Mask of full NUMA nodes covering at least 1 CPU of cpuset:
703 * e.g.:
704 * node0: [0-3]
705 * node1: [4-7]
706 * cpuset: [1-7]
707 * returns [0-7]
708 */
709 46 void mu_get_nodes_intersecting_with_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
710
711 46 CPU_ZERO(node_set);
712
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 46 times.
94 for (unsigned int i=0; i<sys.num_nodes; ++i) {
713 cpu_set_t intxn;
714
2/2
✓ Branch 0 taken 50 times.
✓ Branch 1 taken 48 times.
98 CPU_AND_S(mu_cpuset_alloc_size, &intxn, sys.node_masks[i].set, cpuset);
715
2/2
✓ Branch 1 taken 43 times.
✓ Branch 2 taken 5 times.
48 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
716
2/2
✓ Branch 0 taken 45 times.
✓ Branch 1 taken 43 times.
88 CPU_OR_S(mu_cpuset_alloc_size, node_set, node_set, sys.node_masks[i].set);
717 }
718 }
719 46 }
720
721 /* Return Mask of full NUMA nodes containing all CPUs in cpuset:
722 * e.g.:
723 * node0: [0-3]
724 * node1: [4-7]
725 * cpuset: [1-7]
726 * returns [4-7]
727 */
728 2 void mu_get_nodes_subset_of_cpuset(cpu_set_t *node_set, const cpu_set_t *cpuset) {
729
730 2 CPU_ZERO(node_set);
731
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (unsigned int i=0; i<sys.num_nodes; ++i) {
732
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (mu_is_subset(sys.node_masks[i].set, cpuset)) {
733
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);
734 }
735 }
736 2 }
737
738 /* Return Mask of cores covering at least 1 CPU of cpuset:
739 * e.g.:
740 * node0: [0-1]
741 * node1: [2-3]
742 * cpuset: [1-3]
743 * returns [0-3]
744 */
745 5 void mu_get_cores_intersecting_with_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
746 5 CPU_ZERO(core_set);
747
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) {
748 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
749 cpu_set_t intxn;
750
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);
751
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 11 times.
20 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
752
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);
753 }
754 }
755 5 }
756
757 /* Return Mask of cores containing all CPUs in cpuset:
758 * e.g.:
759 * core0: [0-1]
760 * core1: [2-3]
761 * cpuset: [1-3]
762 * returns [2-3]
763 */
764 5 void mu_get_cores_subset_of_cpuset(cpu_set_t *core_set, const cpu_set_t *cpuset) {
765 5 CPU_ZERO(core_set);
766
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) {
767 20 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
768
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 14 times.
20 if (mu_is_subset(core_cpuset->set, cpuset)) {
769
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);
770 }
771 }
772 5 }
773
774 /* Return the next enabled CPU in mask which pertains to the next core after
775 * prev_cpu, or -1 if not found. */
776 44 int mu_get_cpu_next_core(const cpu_set_t *mask, int prev_cpu) {
777
778
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 43 times.
44 if (unlikely(prev_cpu < -1)) return -1;
779
780 43 int prev_core = mu_get_core_id(prev_cpu);
781 43 int next_cpu = mu_get_next_cpu(mask, prev_cpu);
782 43 int next_core = mu_get_core_id(next_cpu);
783
784 43 while (next_cpu != -1
785
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) {
786 32 next_cpu = mu_get_next_cpu(mask, next_cpu);
787 32 next_core = mu_get_core_id(next_cpu);
788 }
789
790
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;
791 }
792
793 /* We define as "complete" those cores that all the CPUs defined by
794 * sys.core_masks_by_coreid are enabled. */
795
796 /* Return the number of complete cores in the mask.
797 * e.g.:
798 * core0: [0-1]
799 * core1: [2-3]
800 * core2: [4-5]
801 * cpuset: [0-4]
802 * returns 2
803 */
804 33 int mu_count_cores(const cpu_set_t *mask) {
805
806 33 int cores_count = 0;
807
808
2/2
✓ Branch 0 taken 450 times.
✓ Branch 1 taken 33 times.
483 for (unsigned int coreid = 0; coreid < sys.num_cores; coreid++) {
809 // Check if we have the complete set of CPUs form the core
810
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)) {
811 246 cores_count++;
812 }
813 }
814
815 33 return cores_count;
816 }
817
818 /* Return the number of complete cores in the mask.
819 * e.g.:
820 * core0: [0-1]
821 * core1: [2-3]
822 * core2: [4-5]
823 * cpuset: [0-4]
824 * returns 3
825 */
826 26 int mu_count_cores_intersecting_with_cpuset(const cpu_set_t *mask) {
827
828 26 int cores_count = 0;
829
830
2/2
✓ Branch 0 taken 1628 times.
✓ Branch 1 taken 26 times.
1654 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
831 1628 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
832 cpu_set_t intxn;
833
2/2
✓ Branch 0 taken 3164 times.
✓ Branch 1 taken 1628 times.
4792 CPU_AND_S(mu_cpuset_alloc_size, &intxn, core_cpuset->set, mask);
834
2/2
✓ Branch 1 taken 816 times.
✓ Branch 2 taken 812 times.
1628 if (CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0) {
835 816 cores_count++;
836 }
837 }
838
839 26 return cores_count;
840 }
841
842 /* Return the id of the last complete core in the mask if any, otherwise return -1.
843 * e.g.:
844 * core0: [0-1]
845 * core1: [2-3]
846 * core2: [4-5]
847 * cpuset: [0-3]
848 * returns 1 (node1)
849 */
850 24 int mu_get_last_coreid(const cpu_set_t *mask){
851
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 2 times.
170 for (int coreid = sys.num_cores-1; coreid >= 0 ; coreid--) {
852 // Check if we have the complete set of CPUs form the core
853
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)) {
854 22 return coreid;
855 }
856 }
857
858 2 return -1;
859 }
860
861 /* Disables the CPUs of the last complete core in the mask and returns its
862 * coreid if any, otherwise return -1.
863 * e.g.:
864 * core0: [0-1]
865 * core1: [2-3]
866 * core2: [4-5]
867 * cpuset: [2-5]
868 * returns 2 (node2)
869 * updated cpuset: [2-3]
870 */
871 22 int mu_take_last_coreid(cpu_set_t *mask) {
872 22 int last_coreid = mu_get_last_coreid(mask);
873
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 20 times.
22 if (last_coreid == -1) return -1;
874 20 mu_xor(mask, mask, sys.core_masks_by_coreid[last_coreid].set);
875 20 return last_coreid;
876 }
877
878 /* Enables all the CPUs of the core
879 * e.g.:
880 * core0: [0-1]
881 * core1: [2-3]
882 * core2: [4-5]
883 * cpuset: []
884 * coreid: 1
885 * updated cpuset: [2-3]
886 */
887 2 void mu_set_core(cpu_set_t *mask, int coreid){
888 2 mu_or(mask, mask, sys.core_masks_by_coreid[coreid].set);
889 2 }
890
891 /* Disables all the CPUs of the core
892 * e.g.:
893 * core0: [0-1]
894 * core1: [2-3]
895 * core2: [4-5]
896 * cpuset: [0-5]
897 * coreid: 1
898 * updated cpuset: [0-1,4-5]
899 */
900 2 void mu_unset_core(cpu_set_t *mask, int coreid){
901 2 mu_subtract(mask, mask, sys.core_masks_by_coreid[coreid].set);
902 2 }
903
904 /* Basic mask utils functions that do not need to read system's topology,
905 * i.e., mostly mask operations */
906
907 2 void mu_zero(cpu_set_t *result) {
908 2 CPU_ZERO_S(mu_cpuset_alloc_size, result);
909 2 }
910
911 191 void mu_and(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
912
2/2
✓ Branch 0 taken 384 times.
✓ Branch 1 taken 191 times.
575 CPU_AND_S(mu_cpuset_alloc_size, result, mask1, mask2);
913 191 }
914
915 224 void mu_or(cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
916
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 224 times.
644 CPU_OR_S(mu_cpuset_alloc_size, result, mask1, mask2);
917 224 }
918
919 20 void mu_xor (cpu_set_t *result, const cpu_set_t *mask1, const cpu_set_t *mask2) {
920
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
40 CPU_XOR_S(mu_cpuset_alloc_size, result, mask1, mask2);
921 20 }
922
923 4 bool mu_equal(const cpu_set_t *mask1, const cpu_set_t *mask2) {
924 4 return CPU_EQUAL_S(mu_cpuset_alloc_size, mask1, mask2) != 0;
925 }
926
927 /* Returns true is all bits in subset are set in superset */
928 780 bool mu_is_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
929 // The condition is true if the intersection is identical to subset
930 cpu_set_t intxn;
931
2/2
✓ Branch 0 taken 1001 times.
✓ Branch 1 taken 780 times.
1781 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
932 780 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
933 }
934
935 /* Returns true is all bits in superset are set in subset */
936 9 bool mu_is_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
937 // The condition is true if the intersection is identical to subset
938 cpu_set_t intxn;
939
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 9 times.
153 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
940 9 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset);
941 }
942
943 /* Returns true is all bits in subset are set in superset and they're not equal */
944 19 bool mu_is_proper_subset(const cpu_set_t *subset, const cpu_set_t *superset) {
945 cpu_set_t intxn;
946
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 19 times.
173 CPU_AND_S(mu_cpuset_alloc_size, &intxn, subset, superset);
947 19 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
948
4/4
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 3 times.
19 && !CPU_EQUAL_S(mu_cpuset_alloc_size, subset, superset);
949 }
950
951 /* Returns true is all bits in superset are set in subset and they're not equal */
952 22 bool mu_is_proper_superset(const cpu_set_t *superset, const cpu_set_t *subset) {
953 cpu_set_t intxn;
954
2/2
✓ Branch 0 taken 157 times.
✓ Branch 1 taken 22 times.
179 CPU_AND_S(mu_cpuset_alloc_size, &intxn, superset, subset);
955 22 return CPU_EQUAL_S(mu_cpuset_alloc_size, &intxn, subset)
956
4/4
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 3 times.
22 && !CPU_EQUAL_S(mu_cpuset_alloc_size, superset, subset);
957 }
958
959 /* Return true if any bit is present in both sets */
960 25 bool mu_intersects(const cpu_set_t *mask1, const cpu_set_t *mask2) {
961 cpu_set_t intxn;
962
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 25 times.
187 CPU_AND_S(mu_cpuset_alloc_size, &intxn, mask1, mask2);
963 25 return CPU_COUNT_S(mu_cpuset_alloc_size, &intxn) > 0;
964 }
965
966 /* Return the number of bits set in mask */
967 106 int mu_count(const cpu_set_t *mask) {
968 106 return CPU_COUNT_S(mu_cpuset_alloc_size, mask);
969 }
970
971 /* Return the minuend after subtracting the bits in subtrahend */
972 1088 void mu_subtract(cpu_set_t *result, const cpu_set_t *minuend, const cpu_set_t *subtrahend) {
973 cpu_set_t xor;
974
2/2
✓ Branch 0 taken 1476 times.
✓ Branch 1 taken 1088 times.
2564 CPU_XOR_S(mu_cpuset_alloc_size, &xor, minuend, subtrahend);
975
2/2
✓ Branch 0 taken 1476 times.
✓ Branch 1 taken 1088 times.
2564 CPU_AND_S(mu_cpuset_alloc_size, result, minuend, &xor);
976 1088 }
977
978 /* Return the one and only enabled CPU in mask, or -1 if count != 1 */
979 8 int mu_get_single_cpu(const cpu_set_t *mask) {
980
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 5 times.
8 if (CPU_COUNT_S(mu_cpuset_alloc_size, mask) == 1) {
981 3 return mu_get_first_cpu(mask);
982 }
983 5 return -1;
984 }
985
986 /* some of the following functions have been inspired by:
987 * https://github.com/open-mpi/hwloc/blob/master/hwloc/bitmap.c */
988
989 /* Return the first enabled CPU in mask, or -1 if mask is empty */
990 5626 int mu_get_first_cpu(const cpu_set_t *mask) {
991
992
2/2
✓ Branch 0 taken 7597 times.
✓ Branch 1 taken 51 times.
7648 for (unsigned int i = 0; i < mu_cpuset_num_ulongs; ++i) {
993 7597 unsigned long bits = mask->__bits[i];
994
2/2
✓ Branch 0 taken 5575 times.
✓ Branch 1 taken 2022 times.
7597 if (bits) {
995 5575 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
996 }
997 }
998
999 51 return -1;
1000 }
1001
1002 /* Return the last enabled CPU in mask, or -1 if mask is empty */
1003 1638 int mu_get_last_cpu(const cpu_set_t *mask) {
1004
1005
2/2
✓ Branch 0 taken 3486 times.
✓ Branch 1 taken 3 times.
3489 for (unsigned int i = mu_cpuset_num_ulongs; i-- > 0; ) {
1006 3486 unsigned long bits = mask->__bits[i];
1007
2/2
✓ Branch 0 taken 1635 times.
✓ Branch 1 taken 1851 times.
3486 if (bits) {
1008 /* glibc does not provide a fls function, there are more optimal
1009 * solutions, but this function is not that critical */
1010 1635 int cpuid = CPUS_PER_ULONG * i;
1011
2/2
✓ Branch 0 taken 42014 times.
✓ Branch 1 taken 1635 times.
43649 while (bits >>= 1) {
1012 42014 ++cpuid;
1013 }
1014 1635 return cpuid;
1015 }
1016 }
1017
1018 3 return -1;
1019 }
1020
1021 /* Return the next enabled CPU in mask after prev, or -1 if not found */
1022 8851 int mu_get_next_cpu(const cpu_set_t *mask, int prev) {
1023
1024
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8850 times.
8851 if (unlikely(prev < -1)) return -1;
1025
1026 8850 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1027
2/2
✓ Branch 0 taken 11345 times.
✓ Branch 1 taken 3702 times.
15047 i < mu_cpuset_num_ulongs; ++i) {
1028 11345 unsigned long bits = mask->__bits[i];
1029
1030 /* mask bitmap only if previous cpu belong to current iteration */
1031
4/4
✓ Branch 0 taken 11343 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 8673 times.
✓ Branch 3 taken 2670 times.
11345 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1032 8673 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1033 }
1034
1035
2/2
✓ Branch 0 taken 5148 times.
✓ Branch 1 taken 6197 times.
11345 if (bits) {
1036 5148 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1037 }
1038 }
1039
1040 3702 return -1;
1041 }
1042
1043 /* Return the next unset CPU in mask after prev, or -1 if not found */
1044 1750 int mu_get_next_unset(const cpu_set_t *mask, int prev) {
1045
1046
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1749 times.
1750 if (unlikely(prev < -1)) return -1;
1047
1048 1749 for (unsigned int i = (prev + 1) / CPUS_PER_ULONG;
1049
2/2
✓ Branch 0 taken 1846 times.
✓ Branch 1 taken 58 times.
1904 i < mu_cpuset_num_ulongs; ++i) {
1050 1846 unsigned long bits = ~(mask->__bits[i]);
1051
1052 /* mask bitmap only if previous cpu belong to current iteration */
1053
4/4
✓ Branch 0 taken 1845 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1723 times.
✓ Branch 3 taken 122 times.
1846 if (prev >= 0 && (unsigned)prev / CPUS_PER_ULONG == i) {
1054 1723 bits &= ULONG_MAX << (prev % CPUS_PER_ULONG + 1);
1055 }
1056
1057
2/2
✓ Branch 0 taken 1691 times.
✓ Branch 1 taken 155 times.
1846 if (bits) {
1058 1691 return ffsl(bits) - 1 + CPUS_PER_ULONG * i;
1059 }
1060 }
1061
1062 58 return -1;
1063 }
1064
1065 // mu_to_str and mu_parse_mask functions are used by DLB utilities
1066 // We export their dynamic symbols to avoid code duplication,
1067 // although they do not belong to the public API
1068 DLB_EXPORT_SYMBOL
1069 1691 const char* mu_to_str( const cpu_set_t *mask ) {
1070
1071 static __thread char buffer[CPU_SETSIZE*4];
1072 1691 char *b = buffer;
1073 1691 *(b++) = '[';
1074 1691 bool entry_made = false;
1075
2/2
✓ Branch 1 taken 1736 times.
✓ Branch 2 taken 1691 times.
3427 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1076 1736 cpuid = mu_get_next_cpu(mask, cpuid)) {
1077
1078 /* Find interval distance */
1079 1736 int next_unset = mu_get_next_unset(mask, cpuid);
1080 3417 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1081
2/2
✓ Branch 0 taken 1681 times.
✓ Branch 1 taken 55 times.
1736 : mu_get_last_cpu(mask) - cpuid;
1082
1083 /* Add ',' separator for subsequent entries */
1084
2/2
✓ Branch 0 taken 76 times.
✓ Branch 1 taken 1660 times.
1736 if (entry_made) {
1085 76 *(b++) = ',';
1086 } else {
1087 1660 entry_made = true;
1088 }
1089
1090 /* Write element, pair or range */
1091
2/2
✓ Branch 0 taken 1288 times.
✓ Branch 1 taken 448 times.
1736 if (distance == 0) {
1092 1288 b += sprintf(b, "%d", cpuid);
1093
2/2
✓ Branch 0 taken 233 times.
✓ Branch 1 taken 215 times.
448 } else if (distance == 1) {
1094 233 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1095 233 ++cpuid;
1096 } else {
1097 215 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1098 215 cpuid += distance;
1099 }
1100 }
1101 1691 *(b++) = ']';
1102 1691 *b = '\0';
1103
1104 1691 return buffer;
1105 }
1106
1107 39 static void parse_64_bits_mask(cpu_set_t *mask, unsigned int offset, const char *str, int base) {
1108 39 unsigned long long number = strtoull(str, NULL, base);
1109 39 unsigned int i = offset;
1110
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) {
1111
2/2
✓ Branch 0 taken 66 times.
✓ Branch 1 taken 278 times.
344 if (number & 1) {
1112
1/2
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
66 CPU_SET(i, mask);
1113 }
1114 344 ++i;
1115 344 number = number >> 1;
1116 }
1117 39 }
1118
1119 DLB_EXPORT_SYMBOL
1120 266 void mu_parse_mask( const char *str, cpu_set_t *mask ) {
1121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 266 times.
269 if (!str) return;
1122
1123 266 size_t str_len = strnlen(str, CPU_SETSIZE+1);
1124
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 265 times.
266 if (str_len > CPU_SETSIZE) return;
1125
1126 265 CPU_ZERO( mask );
1127
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 263 times.
265 if (str_len == 0) return;
1128
1129 regex_t regex_bitmask;
1130 regex_t regex_hexmask;
1131 regex_t regex_range;
1132 regex_t old_regex_bitmask;
1133
1134 /* Compile regular expressions */
1135
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
263 if ( regcomp(&regex_bitmask, "^0[bB][0-1]+$", REG_EXTENDED|REG_NOSUB) ) {
1136 fatal0( "Could not compile regex");
1137 }
1138
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
263 if ( regcomp(&regex_hexmask, "^0[xX][0-9,a-f,A-F]+$", REG_EXTENDED|REG_NOSUB) ) {
1139 fatal0( "Could not compile regex");
1140 }
1141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
263 if ( regcomp(&regex_range, "^[0-9,-]+$", REG_EXTENDED|REG_NOSUB) ) {
1142 fatal0( "Could not compile regex");
1143 }
1144
1145 /***** Deprecated *****/
1146
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
263 if ( regcomp(&old_regex_bitmask, "^[0-1][0-1]+[bB]$", REG_EXTENDED|REG_NOSUB) ) {
1147 fatal0( "Could not compile regex");
1148 }
1149 /* Regular expression matches OLD bitmask, e.g.: 11110011b */
1150
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 260 times.
263 if ( !regexec(&old_regex_bitmask, str, 0, NULL, 0) ) {
1151 3 warning("The binary form xxxxb is deprecated, please use 0bxxxx.");
1152 // Parse
1153
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 3 times.
22 for (unsigned int i=0; i<str_len; i++) {
1154
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 ) {
1155
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 CPU_SET( i, mask );
1156 }
1157 }
1158 }
1159 /**********************/
1160
1161 /* Regular expression matches bitmask, e.g.: 0b11100001 */
1162
2/2
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 251 times.
260 else if ( !regexec(&regex_bitmask, str, 0, NULL, 0) ) {
1163 /* Ignore '0b' */
1164 9 str += 2;
1165
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
9 if (strlen(str) <= 64) {
1166 6 parse_64_bits_mask(mask, 0, str, 2);
1167 } else {
1168 /* parse in chunks of 64 bits */
1169 3 char *str_copy = strdup(str);
1170 char *start_ptr;
1171 3 char *end_ptr = str_copy + strlen(str_copy);
1172 3 unsigned int offset = 0;
1173 do {
1174
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 2 times.
12 start_ptr = strlen(str_copy) < 64 ? str_copy : end_ptr - 64;
1175 12 parse_64_bits_mask(mask, offset, start_ptr, 2);
1176 12 offset += 64;
1177 12 end_ptr = start_ptr;
1178 12 *end_ptr = '\0';
1179
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
12 } while (strlen(str_copy) > 0);
1180 3 free(str_copy);
1181 }
1182 }
1183
1184 /* Regular expression matches hexmask, e.g.: 0xE1 */
1185
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 241 times.
251 else if ( !regexec(&regex_hexmask, str, 0, NULL, 0) ) {
1186 /* Ignore '0x' */
1187 10 str += 2;
1188
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 3 times.
10 if (strlen(str) <= 16) {
1189 7 parse_64_bits_mask(mask, 0, str, 16);
1190 } else {
1191 /* parse in chunks of 64 bits (16 hex digits) */
1192 3 char *str_copy = strdup(str);
1193 char *start_ptr;
1194 3 char *end_ptr = str_copy + strlen(str_copy);
1195 3 unsigned int offset = 0;
1196 do {
1197
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 start_ptr = strlen(str_copy) < 16 ? str_copy : end_ptr - 16;
1198 14 parse_64_bits_mask(mask, offset, start_ptr, 16);
1199 14 offset += 64;
1200 14 end_ptr = start_ptr;
1201 14 *end_ptr = '\0';
1202
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
14 } while (strlen(str_copy) > 0);
1203 3 free(str_copy);
1204 }
1205 }
1206
1207 /* Regular expression matches range, e.g.: 0,5-7 */
1208
1/2
✓ Branch 1 taken 241 times.
✗ Branch 2 not taken.
241 else if ( !regexec(&regex_range, str, 0, NULL, 0) ) {
1209 // Parse
1210 241 const char *ptr = str;
1211 char *endptr;
1212
2/2
✓ Branch 0 taken 360 times.
✓ Branch 1 taken 241 times.
601 while ( ptr < str+strlen(str) ) {
1213 // Discard junk at the left
1214
2/2
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 337 times.
360 if ( !isdigit(*ptr) ) { ptr++; continue; }
1215
1216 337 unsigned long start_ = strtoul( ptr, &endptr, 10 );
1217 337 unsigned long start = start_ < mu_cpuset_setsize ? start_ : mu_cpuset_setsize;
1218 337 ptr = endptr;
1219
1220 // Single element
1221
5/6
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 73 times.
✓ Branch 2 taken 95 times.
✓ Branch 3 taken 169 times.
✓ Branch 4 taken 168 times.
✗ Branch 5 not taken.
337 if ( (*ptr == ',' || *ptr == '\0') && start < mu_cpuset_setsize ) {
1222
1/2
✓ Branch 0 taken 168 times.
✗ Branch 1 not taken.
168 CPU_SET( start, mask );
1223 168 ptr++;
1224 168 continue;
1225 }
1226 // Range
1227
1/2
✓ Branch 0 taken 169 times.
✗ Branch 1 not taken.
169 else if ( *ptr == '-' ) {
1228 // Discard '-' and possible junk
1229 169 ptr++;
1230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 169 times.
169 if ( !isdigit(*ptr) ) { ptr++; continue; }
1231
1232 169 unsigned long end_ = strtoul( ptr, &endptr, 10 );
1233 169 unsigned long end = end_ < mu_cpuset_setsize ? end_ : mu_cpuset_setsize;
1234 169 ptr = endptr;
1235
1236 // Valid range
1237
1/2
✓ Branch 0 taken 169 times.
✗ Branch 1 not taken.
169 if ( end > start ) {
1238
4/4
✓ Branch 0 taken 2360 times.
✓ Branch 1 taken 167 times.
✓ Branch 2 taken 2358 times.
✓ Branch 3 taken 2 times.
2527 for ( unsigned long i=start; i<=end && i<mu_cpuset_setsize; i++ ) {
1239
1/2
✓ Branch 0 taken 2358 times.
✗ Branch 1 not taken.
2358 CPU_SET( i, mask );
1240 }
1241 }
1242 169 continue;
1243 }
1244 // Unexpected token
1245 else { }
1246 }
1247 }
1248 /* Regular expression does not match */
1249 else { }
1250
1251 263 regfree(&regex_bitmask);
1252 263 regfree(&regex_hexmask);
1253 263 regfree(&regex_range);
1254 263 regfree(&old_regex_bitmask);
1255
1256
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 263 times.
263 if ( CPU_COUNT(mask) == 0 ) {
1257 warning( "Parsed mask \"%s\" does not seem to be a valid mask\n", str );
1258 }
1259 }
1260
1261 /* Equivalent to mu_to_str, but generate quoted string in str up to namelen-1 bytes */
1262 8 void mu_get_quoted_mask(const cpu_set_t *mask, char *str, size_t namelen) {
1263
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
8 if (namelen < 2)
1264 return;
1265
1266 8 char *b = str;
1267 8 *(b++) = '"';
1268 8 size_t bytes = 1;
1269 8 bool entry_made = false;
1270
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
16 for (int cpuid = mu_get_first_cpu(mask); cpuid >= 0;
1271 8 cpuid = mu_get_next_cpu(mask, cpuid)) {
1272
1273 /* Find interval distance */
1274 8 int next_unset = mu_get_next_unset(mask, cpuid);
1275 14 int distance = next_unset > 0 ? next_unset - 1 - cpuid
1276
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
8 : mu_get_last_cpu(mask) - cpuid;
1277
1278 /* Add ',' separator for subsequent entries */
1279
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 7 times.
8 if (entry_made) {
1280
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (bytes+1 < namelen) {
1281 1 *(b++) = ',';
1282 1 ++bytes;
1283 }
1284 } else {
1285 7 entry_made = true;
1286 }
1287
1288 /* Write element, pair or range */
1289
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 3 times.
8 if (distance == 0) {
1290 5 int len = snprintf(NULL, 0, "%d", cpuid);
1291
1/2
✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
5 if (bytes+len < namelen) {
1292 5 b += sprintf(b, "%d", cpuid);
1293 5 bytes += len;
1294 }
1295
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 } else if (distance == 1) {
1296 1 int len = snprintf(NULL, 0, "%d,%d", cpuid, cpuid+1);
1297
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (bytes+len < namelen) {
1298 1 b += sprintf(b, "%d,%d", cpuid, cpuid+1);
1299 1 bytes += len;
1300 1 ++cpuid;
1301 }
1302 } else {
1303 2 int len = snprintf(NULL, 0, "%d-%d", cpuid, cpuid+distance);
1304
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (bytes+len < namelen) {
1305 2 b += sprintf(b, "%d-%d", cpuid, cpuid+distance);
1306 2 bytes += len;
1307 2 cpuid += distance;
1308 }
1309 }
1310 }
1311
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
8 if (bytes+1 < namelen) {
1312 8 *(b++) = '"';
1313 8 ++bytes;
1314 }
1315 8 *b = '\0';
1316 }
1317
1318 1 char * mu_parse_to_slurm_format(const cpu_set_t *mask) {
1319 1 char *str = malloc((mu_cpuset_setsize >> 2) + 3);
1320 if (str < 0)
1321 return NULL;
1322 1 unsigned int offset = 2;
1323 1 unsigned long long val = 0;
1324 1 const int threshold = 4;
1325 1 sprintf(str, "0x");
1326
2/2
✓ Branch 1 taken 130 times.
✓ Branch 2 taken 1 times.
131 for (int cpuid = mu_get_last_cpu(mask); cpuid >= 0; --cpuid) {
1327
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)) {
1328 9 val |= 1 << (cpuid % threshold);
1329 }
1330
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) {
1331 32 sprintf(str+offset, "%llx", val);
1332 32 val = 0;
1333 32 offset++;
1334 }
1335 }
1336 1 sprintf(str+offset, "%llx", val);
1337 1 return str;
1338 }
1339
1340 4 bool mu_equivalent_masks(const char *str1, const char *str2) {
1341 cpu_set_t mask1, mask2;
1342 4 mu_parse_mask(str1, &mask1);
1343 4 mu_parse_mask(str2, &mask2);
1344 4 return CPU_EQUAL(&mask1, &mask2);
1345 }
1346
1347
1348 3892 static int cmp_cpuids(cpuid_t cpuid1, cpuid_t cpuid2) {
1349 3892 int cpu1_core_id = mu_get_core_id(cpuid1);
1350 3892 int cpu2_core_id = mu_get_core_id(cpuid2);
1351
2/2
✓ Branch 0 taken 480 times.
✓ Branch 1 taken 3412 times.
3892 if (cpu1_core_id == cpu2_core_id) {
1352 480 return cpuid1 - cpuid2;
1353 } else {
1354 3412 return cpu1_core_id - cpu2_core_id;
1355 }
1356 }
1357
1358 /* Compare CPUs so that:
1359 * - owned CPUs first, in ascending order
1360 * - non-owned later, starting from the first owned, then ascending
1361 * e.g.: system: [0-7], owned: [3-5]
1362 * cpu_list = {4,5,6,7,0,1,2,3}
1363 */
1364 1543 int mu_cmp_cpuids_by_ownership(const void *cpuid1, const void *cpuid2, void *mask) {
1365 /* Expand arguments */
1366 1543 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1367 1543 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1368 1543 cpu_set_t *process_mask = mask;
1369
1370
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)) {
1371
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)) {
1372 /* both CPUs are owned: ascending order */
1373 529 return cmp_cpuids(_cpuid1, _cpuid2);
1374 } else {
1375 /* cpuid2 is NOT owned and cpuid1 IS */
1376 208 return -1;
1377 }
1378 } else {
1379
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)) {
1380 /* cpuid2 IS owned and cpuid1 is NOT */
1381 223 return 1;
1382 } else {
1383 /* none is owned */
1384 583 int first_cpu = mu_get_first_cpu(process_mask);
1385 583 int first_core = mu_get_core_id(first_cpu);
1386 583 int cpu1_core_id = mu_get_core_id(_cpuid1);
1387 583 int cpu2_core_id = mu_get_core_id(_cpuid2);
1388
2/2
✓ Branch 0 taken 351 times.
✓ Branch 1 taken 232 times.
583 if ((cpu1_core_id > first_core
1389
2/2
✓ Branch 0 taken 47 times.
✓ Branch 1 taken 304 times.
351 && cpu2_core_id > first_core)
1390
2/2
✓ Branch 0 taken 232 times.
✓ Branch 1 taken 47 times.
279 || (cpu1_core_id < first_core
1391
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 58 times.
232 && cpu2_core_id < first_core)) {
1392 /* Both CPUs are either before or after the process mask */
1393 478 return cmp_cpuids(_cpuid1, _cpuid2);
1394 } else {
1395 /* Compare with respect to process mask */
1396 105 return cmp_cpuids(first_cpu, _cpuid1);
1397 }
1398 }
1399 }
1400 }
1401
1402 /* Compare CPUs so that:
1403 * - CPUs are sorted according to the affinity array:
1404 * * affinity: array of cpu_set_t, each position represents a level
1405 * in the affinity, the last position is an empty cpu set.
1406 * (PRE: each affinity level is a superset of the previous level mask)
1407 * - Sorted by affinity level in ascending order
1408 * - non-owned later, starting from the first owned, then ascending
1409 * e.g.: affinity: {{6-7}, {4-7}, {0-7}, {}}
1410 * sorted_cpu_list = {6,7,4,5,0,1,2,3}
1411 */
1412 3643 int mu_cmp_cpuids_by_affinity(const void *cpuid1, const void *cpuid2, void *affinity) {
1413 /* Expand arguments */
1414 3643 cpuid_t _cpuid1 = *(cpuid_t*)cpuid1;
1415 3643 cpuid_t _cpuid2 = *(cpuid_t*)cpuid2;
1416 3643 cpu_set_t *_affinity = affinity;
1417
1418 /* Find affinity level of each CPU */
1419 3643 int cpu1_level = 0;
1420 3643 int cpu2_level = 0;
1421 3643 cpu_set_t *mask = _affinity;
1422
2/2
✓ Branch 1 taken 7951 times.
✓ Branch 2 taken 3643 times.
11594 while(CPU_COUNT(mask) > 0) {
1423
5/6
✓ Branch 0 taken 7951 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5296 times.
✓ Branch 3 taken 2655 times.
✓ Branch 4 taken 2655 times.
✓ Branch 5 taken 5296 times.
7951 if (!CPU_ISSET(_cpuid1, mask)) {
1424 2655 ++cpu1_level;
1425 }
1426
5/6
✓ Branch 0 taken 7951 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 5225 times.
✓ Branch 3 taken 2726 times.
✓ Branch 4 taken 2726 times.
✓ Branch 5 taken 5225 times.
7951 if (!CPU_ISSET(_cpuid2, mask)) {
1427 2726 ++cpu2_level;
1428 }
1429 7951 ++mask;
1430 }
1431
1432 /* If levels differ, sort levels in ascending order */
1433
2/2
✓ Branch 0 taken 863 times.
✓ Branch 1 taken 2780 times.
3643 if (cpu1_level != cpu2_level) {
1434 863 return cpu1_level - cpu2_level;
1435 }
1436
1437 /* If both are level 0, sort in ascending order */
1438
2/2
✓ Branch 0 taken 1098 times.
✓ Branch 1 taken 1682 times.
2780 if (cpu1_level == 0) {
1439 1098 return cmp_cpuids(_cpuid1, _cpuid2);
1440 }
1441
1442 /* If both are level 1, sort from the first CPU in level 0 */
1443 /* e.g.: level0: [2,3], level1: [0,7] -> [4,5,6,7,0,1] */
1444
2/2
✓ Branch 0 taken 1401 times.
✓ Branch 1 taken 281 times.
1682 if (cpu1_level == 1) {
1445 1401 cpu_set_t *level0_mask = _affinity;
1446 1401 int first_cpu = mu_get_first_cpu(level0_mask);
1447 1401 int first_core = mu_get_core_id(first_cpu);
1448 1401 int cpu1_core_id = mu_get_core_id(_cpuid1);
1449 1401 int cpu2_core_id = mu_get_core_id(_cpuid2);
1450
2/2
✓ Branch 0 taken 999 times.
✓ Branch 1 taken 402 times.
1401 if ((cpu1_core_id > first_core
1451
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 983 times.
999 && cpu2_core_id > first_core)
1452
2/2
✓ Branch 0 taken 402 times.
✓ Branch 1 taken 16 times.
418 || (cpu1_core_id < first_core
1453
2/2
✓ Branch 0 taken 294 times.
✓ Branch 1 taken 108 times.
402 && cpu2_core_id < first_core)) {
1454 /* Both CPUs are either before or after the process mask */
1455 1277 return cmp_cpuids(_cpuid1, _cpuid2);
1456 } else {
1457 /* Compare with respect to process mask */
1458 124 return cmp_cpuids(first_cpu, _cpuid1);
1459 }
1460 }
1461
1462 /* TODO: compute numa distance */
1463 /* Levels 2+, sort in ascending order */
1464 281 return cmp_cpuids(_cpuid1, _cpuid2);
1465 }
1466
1467
1468 /*********************************************************************************/
1469 /* Mask utils testing functions */
1470 /*********************************************************************************/
1471
1472 73 static void print_sys_info(void) {
1473
1474
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 25 times.
73 verbose(VB_AFFINITY, "System mask: %s", mu_to_str(sys.sys_mask.set));
1475
1476
2/2
✓ Branch 0 taken 78 times.
✓ Branch 1 taken 73 times.
151 for (unsigned int node_id = 0; node_id < sys.num_nodes; ++node_id) {
1477
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 65 times.
78 verbose(VB_AFFINITY, "Node %d mask: %s",
1478 node_id, mu_to_str(sys.node_masks[node_id].set));
1479 }
1480
1481
2/2
✓ Branch 0 taken 1240 times.
✓ Branch 1 taken 73 times.
1313 for (unsigned int core_id = 0; core_id < sys.num_cores; ++core_id) {
1482 1240 const mu_cpuset_t *core_cpuset = &sys.core_masks_by_coreid[core_id];
1483
1/2
✓ Branch 0 taken 1240 times.
✗ Branch 1 not taken.
1240 if (core_cpuset->count > 0) {
1484
2/2
✓ Branch 0 taken 420 times.
✓ Branch 1 taken 820 times.
1240 verbose(VB_AFFINITY, "Core %d mask: %s",
1485 core_id, mu_to_str(core_cpuset->set));
1486 }
1487 }
1488
1489 73 for (int cpuid = sys.sys_mask.first_cpuid;
1490
4/4
✓ Branch 0 taken 1466 times.
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 1465 times.
✓ Branch 3 taken 1 times.
1538 cpuid >= 0 && cpuid != DLB_CPUID_INVALID;
1491 1465 cpuid = mu_get_next_cpu(sys.sys_mask.set, cpuid)) {
1492 1465 const mu_cpuset_t *core_cpuset = sys.core_masks_by_cpuid[cpuid];
1493
3/4
✓ Branch 0 taken 1327 times.
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 1327 times.
✗ Branch 3 not taken.
1465 if (core_cpuset && core_cpuset->count > 0) {
1494
2/2
✓ Branch 0 taken 447 times.
✓ Branch 1 taken 880 times.
1327 verbose(VB_AFFINITY, "CPU %d core mask: %s",
1495 cpuid, mu_to_str(core_cpuset->set));
1496 }
1497 }
1498 73 }
1499
1500 2 bool mu_testing_is_initialized(void) {
1501 2 return mu_initialized;
1502 }
1503
1504 57 void mu_testing_set_sys_size(int size) {
1505 57 init_system(size, size, 1);
1506 57 print_sys_info();
1507 57 }
1508
1509 3 void mu_testing_set_sys(unsigned int num_cpus, unsigned int num_cores,
1510 unsigned int num_nodes) {
1511 3 init_system(num_cpus, num_cores, num_nodes);
1512 3 print_sys_info();
1513 3 }
1514
1515 12 void mu_testing_set_sys_masks(const cpu_set_t *sys_mask,
1516 const cpu_set_t *core_masks, unsigned int num_cores,
1517 const cpu_set_t *node_masks, unsigned int num_nodes) {
1518 12 init_system_masks(sys_mask, core_masks, num_cores, node_masks, num_nodes);
1519 12 print_sys_info();
1520 12 }
1521
1522 1 void mu_testing_init_nohwloc(void) {
1523 1 init_mu_struct();
1524 1 parse_system_files();
1525 1 mu_initialized = true;
1526 1 print_sys_info();
1527 1 }
1528