GCC Code Coverage Report


Directory: src/
File: src/LB_comm/shmem_procinfo.c
Date: 2025-11-21 10:34:40
Exec Total Coverage
Lines: 560 725 77.2%
Functions: 29 40 72.5%
Branches: 395 566 69.8%

Line Branch Exec Source
1 /*********************************************************************************/
2 /* Copyright 2009-2021 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 "LB_comm/shmem_procinfo.h"
25
26 #include "LB_comm/shmem.h"
27 #include "LB_numThreads/numThreads.h"
28 #include "apis/dlb_errors.h"
29 #include "support/atomic.h"
30 #include "support/debug.h"
31 #include "support/types.h"
32 #include "support/mytime.h"
33 #include "support/mask_utils.h"
34 #include "LB_core/spd.h"
35
36 #include <sched.h>
37 #include <unistd.h>
38 #include <string.h>
39 #include <pthread.h>
40 #include <sys/types.h>
41 #include <sys/resource.h>
42
43 enum { NOBODY = 0 };
44 enum { SYNC_POLL_DELAY = 10000 }; /* 10^4 ns = 10 ms */
45 enum { SYNC_POLL_TIMEOUT = 1000000000 }; /* 10^9 ns = 1s */
46
47
48 typedef struct DLB_ALIGN_CACHE pinfo_t {
49 pid_t pid;
50 bool dirty;
51 bool preregistered;
52 cpu_set_t current_process_mask;
53 cpu_set_t future_process_mask;
54 cpu_set_t stolen_cpus;
55 unsigned int active_cpus;
56 // Cpu Usage fields:
57 double cpu_usage;
58 double cpu_avg_usage;
59 #ifdef DLB_LOAD_AVERAGE
60 // Load average fields:
61 float load[3]; // 1min, 5min, 15mins
62 struct timespec last_ltime; // Last time that Load was updated
63 #endif
64 } pinfo_t;
65
66 typedef struct procinfo_flags {
67 bool initialized:1;
68 bool allow_cpu_sharing:1; // efectively, disables DROM functionalities
69 bool cpu_sharing_unknown:1; // set when flag is not not during initialization
70 } procinfo_flags_t;
71
72 typedef struct {
73 procinfo_flags_t flags;
74 struct timespec initial_time;
75 cpu_set_t free_mask; // Contains the CPUs in the system not owned
76 int max_processes; // process_info capacity
77 int num_processes; // process_info upper bound
78 pinfo_t process_info[];
79 } shdata_t;
80
81 enum { SHMEM_PROCINFO_VERSION = 10 };
82
83 static shmem_handler_t *shm_handler = NULL;
84 static shdata_t *shdata = NULL;
85 static int max_cpus;
86 static int max_processes = 0;
87 //static struct timespec last_ttime; // Total time
88 //static struct timespec last_utime; // Useful time (user+system)
89 static const char *shmem_name = "procinfo";
90 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
91 static int subprocesses_attached = 0;
92 static pinfo_t *my_pinfo = NULL;
93
94 static int set_new_mask(pinfo_t *process, const cpu_set_t *mask, bool sync,
95 bool return_stolen, cpu_set_t *free_cpu_mask);
96 static void close_shmem(void);
97
98 13 static pid_t get_parent_pid(pid_t pid) {
99 13 pid_t parent_pid = 0;
100 enum { BUF_LEN = 128 };
101 char buf[BUF_LEN];
102 enum { PID_FILE_MAX_LEN = 32 };
103 char pid_status_filename[PID_FILE_MAX_LEN];
104 13 snprintf(pid_status_filename, PID_FILE_MAX_LEN, "/proc/%d/status", pid);
105 13 FILE *fd = fopen(pid_status_filename, "r");
106
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 6 times.
13 if (fd != NULL) {
107
2/2
✓ Branch 1 taken 420 times.
✓ Branch 2 taken 7 times.
427 while(fgets(buf, BUF_LEN, fd) != NULL) {
108 420 char *substring = strstr(buf, "PPid:");
109
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 413 times.
420 if (substring != NULL) {
110 7 substring += strlen("PPid:");
111 7 long ppid = strtol(substring, NULL, 10);
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7 times.
7 if (ppid > 0) {
113 parent_pid = ppid;
114 break;
115 }
116 }
117 }
118 7 fclose(fd);
119 }
120 13 return parent_pid;
121 }
122
123 227 static pinfo_t* get_process(pid_t pid) {
124
1/2
✓ Branch 0 taken 227 times.
✗ Branch 1 not taken.
227 if (shdata) {
125 /* Check first if pid is this process */
126
2/2
✓ Branch 0 taken 152 times.
✓ Branch 1 taken 75 times.
227 if (my_pinfo != NULL
127
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 87 times.
152 && my_pinfo->pid == pid) {
128 65 return my_pinfo;
129 }
130
131 /* Iterate otherwise */
132 162 int num_processes = shdata->num_processes;
133
2/2
✓ Branch 0 taken 367 times.
✓ Branch 1 taken 13 times.
380 for (int p = 0; p < num_processes; p++) {
134
2/2
✓ Branch 0 taken 149 times.
✓ Branch 1 taken 218 times.
367 if (shdata->process_info[p].pid == pid) {
135 149 return &shdata->process_info[p];
136 }
137 }
138
139 /* Try parent PID */
140 13 pid_t parent_pid = get_parent_pid(pid);
141
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 if (parent_pid > 0) {
142 return get_process(parent_pid);
143 }
144 }
145 13 return NULL;
146 }
147
148 /*********************************************************************************/
149 /* Init / Register */
150 /*********************************************************************************/
151
152 /* This function may be called from shmem_init to cleanup pid */
153 1 static void cleanup_shmem(void *shdata_ptr, int pid) {
154 1 bool shmem_empty = true;
155 1 shdata_t *shared_data = shdata_ptr;
156 1 int num_processes = shdata->num_processes;
157
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 for (int p = 0; p < num_processes; p++) {
158 1 pinfo_t *process = &shared_data->process_info[p];
159
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (process->pid == pid) {
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (process->dirty) {
161 CPU_OR(&shared_data->free_mask, &shared_data->free_mask,
162 &process->future_process_mask);
163 } else {
164
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
17 CPU_OR(&shared_data->free_mask, &shared_data->free_mask,
165 &process->current_process_mask);
166 }
167 1 *process = (const pinfo_t){0};
168 } else if (process->pid > 0) {
169 shmem_empty = false;
170 }
171 }
172
173 /* If there are no registered processes, make sure shmem is reset */
174
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (shmem_empty) {
175 1 memset(shared_data, 0, shmem_procinfo__size());
176 }
177 1 }
178
179 97 static bool is_shmem_empty(void) {
180
181 97 int num_processes = shdata->num_processes;
182
2/2
✓ Branch 0 taken 143 times.
✓ Branch 1 taken 75 times.
218 for (int p = 0; p < num_processes; p++) {
183
2/2
✓ Branch 0 taken 22 times.
✓ Branch 1 taken 121 times.
143 if (shdata->process_info[p].pid != NOBODY) {
184 22 return false;
185 }
186 }
187 75 return true;
188 }
189
190 175 static void open_shmem(const char *shmem_key, int shmem_size_multiplier) {
191 175 pthread_mutex_lock(&mutex);
192 {
193
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 78 times.
175 if (shm_handler == NULL) {
194 // We assume no more processes than CPUs
195 97 max_cpus = mu_get_system_size();
196 97 max_processes = mu_get_system_size() * shmem_size_multiplier;
197
198 194 shm_handler = shmem_init((void**)&shdata,
199 97 &(const shmem_props_t) {
200 97 .size = shmem_procinfo__size(),
201 .name = shmem_name,
202 .key = shmem_key,
203 .version = SHMEM_PROCINFO_VERSION,
204 .cleanup_fn = cleanup_shmem,
205 });
206 97 subprocesses_attached = 1;
207 } else {
208 78 ++subprocesses_attached;
209 }
210 }
211 175 pthread_mutex_unlock(&mutex);
212 175 }
213
214 // Register a new set of CPUs. Remove them from the free_mask and assign them to new_owner if ok
215 149 static int register_mask(pinfo_t *new_owner, const cpu_set_t *mask) {
216 // Return if empty mask
217
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 115 times.
149 if (CPU_COUNT(mask) == 0) return DLB_SUCCESS;
218
219 // Return if sharing is allowed and, thus, we don't need to check CPU overlapping
220
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 105 times.
115 if (shdata->flags.allow_cpu_sharing) {
221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 verbose(VB_DROM, "Process %d registering shared mask %s", new_owner->pid, mu_to_str(mask));
222 10 return DLB_SUCCESS;
223 }
224
225
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 92 times.
105 verbose(VB_DROM, "Process %d registering mask %s", new_owner->pid, mu_to_str(mask));
226 105 int error = DLB_SUCCESS;
227
2/2
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 5 times.
105 if (mu_is_subset(mask, &shdata->free_mask)) {
228 100 mu_subtract(&shdata->free_mask, &shdata->free_mask, mask);
229
2/2
✓ Branch 0 taken 1600 times.
✓ Branch 1 taken 100 times.
1700 CPU_OR(&new_owner->future_process_mask, &new_owner->future_process_mask, mask);
230 100 mu_subtract(&new_owner->stolen_cpus, &new_owner->stolen_cpus, mask);
231 100 new_owner->dirty = true;
232 } else {
233 cpu_set_t wrong_cpus;
234 5 mu_subtract(&wrong_cpus, mask, &shdata->free_mask);
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 verbose(VB_SHMEM, "Error registering CPUs: %s, already belong to other processes",
236 mu_to_str(&wrong_cpus));
237 5 error = DLB_ERR_PERM;
238 }
239 105 return error;
240 }
241
242 131 static int shmem_procinfo__init_(pid_t pid, pid_t preinit_pid, const cpu_set_t *process_mask,
243 cpu_set_t *new_process_mask, const char *shmem_key, int shmem_size_multiplier,
244 bool allow_cpu_sharing) {
245
246 131 int error = DLB_SUCCESS;
247
248 // Shared memory creation
249 131 open_shmem(shmem_key, shmem_size_multiplier);
250
251 131 pinfo_t *process = NULL;
252 131 shmem_lock(shm_handler);
253 {
254 // Initialize some values if this is the 1st process attached to the shmem
255
2/2
✓ Branch 0 taken 49 times.
✓ Branch 1 taken 82 times.
131 if (!shdata->flags.initialized) {
256 49 shdata->flags = (const procinfo_flags_t) {
257 .initialized = true,
258 .allow_cpu_sharing = allow_cpu_sharing,
259 };
260 49 get_time(&shdata->initial_time);
261 49 mu_get_system_mask(&shdata->free_mask);
262 49 shdata->max_processes = max_processes;
263 49 shdata->num_processes = 0;
264 } else {
265
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 81 times.
82 if (shdata->max_processes != max_processes) {
266 1 error = DLB_ERR_INIT;
267 1 goto shmem_procinfo__init_error;
268 }
269
270
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 73 times.
81 if (shdata->flags.cpu_sharing_unknown) {
271 /* Already initialized but cpu_sharing unknown, probably due to
272 * initiazing the shared memory via shmem_procinfo_ext__init */
273 8 shdata->flags.allow_cpu_sharing = allow_cpu_sharing;
274 8 shdata->flags.cpu_sharing_unknown = false;
275
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 70 times.
73 } else if (shdata->flags.allow_cpu_sharing != allow_cpu_sharing) {
276 // For now we require all processes registering the procinfo
277 // to have the same value in 'allow_cpu_sharing'
278 3 error = DLB_ERR_NOCOMP;
279 3 goto shmem_procinfo__init_error;
280 }
281 }
282
283 // Iterate the processes array to find the two potential pointers:
284 127 pinfo_t *empty_spot = NULL;
285 127 pinfo_t *preinit_process = NULL;
286 127 int num_processes = shdata->num_processes;
287
2/2
✓ Branch 0 taken 298 times.
✓ Branch 1 taken 116 times.
414 for (int p = 0; p < num_processes; ++p) {
288
2/2
✓ Branch 0 taken 297 times.
✓ Branch 1 taken 1 times.
298 if (empty_spot == NULL
289
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 287 times.
297 && shdata->process_info[p].pid == NOBODY) {
290 /* First empty spot */
291 10 empty_spot = &shdata->process_info[p];
292 }
293
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 254 times.
288 else if (preinit_pid != 0
294
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 10 times.
34 && preinit_process == NULL
295
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 && shdata->process_info[p].pid == preinit_pid) {
296 /* This is the preregistered process */
297 20 preinit_process = &shdata->process_info[p];
298
2/2
✓ Branch 0 taken 19 times.
✓ Branch 1 taken 1 times.
20 if (preinit_process->preregistered) {
299 19 error = DLB_NOTED;
300 } else {
301 // The process matches the preinit_pid but it's
302 // not flagged as preregistered
303 1 error = DLB_ERR_INIT;
304 1 goto shmem_procinfo__init_error;
305 }
306 }
307
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 267 times.
268 else if (shdata->process_info[p].pid == pid) {
308 /* This process is already registered */
309 1 error = DLB_ERR_INIT;
310 1 goto shmem_procinfo__init_error;
311 }
312
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 285 times.
296 if (empty_spot
313
6/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 8 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 1 times.
11 && ((preinit_pid && preinit_process) || !preinit_pid)) {
314 /* Everything needed is found */
315 break;
316 }
317 }
318
319 // If no empty spot, get last position but not increase yet
320 // num_processes, since the empty spot may not be used
321 125 bool empty_spot_is_last = false;
322
4/4
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 107 times.
✓ Branch 3 taken 10 times.
125 if (num_processes < max_processes && empty_spot == NULL) {
323 107 empty_spot = &shdata->process_info[num_processes];
324 107 empty_spot_is_last = true;
325 }
326
327 // If it's a new process, initialize structure
328
4/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 105 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
224 if (preinit_pid == 0 || preinit_process == NULL) {
329
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 99 times.
106 if (empty_spot == NULL) {
330 7 error = DLB_ERR_NOMEM;
331 7 goto shmem_procinfo__init_error;
332 }
333
334 99 process = empty_spot;
335 99 *process = (const pinfo_t) {.pid = pid};
336 99 error = register_mask(process, process_mask);
337
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 3 times.
99 if (error == DLB_SUCCESS) {
338 96 memcpy(&process->current_process_mask, process_mask, sizeof(cpu_set_t));
339 96 memcpy(&process->future_process_mask, process_mask, sizeof(cpu_set_t));
340 96 process->dirty = false; /* register_mask sets dirty flag, undo */
341 } else {
342 // Revert process registration if mask registration failed
343 3 process->pid = NOBODY;
344 }
345 }
346
347 // Pre-registered process, check if the spot is inherited or initialize a new one
348
2/4
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
✗ Branch 3 not taken.
19 else if(preinit_process && error == DLB_NOTED) {
349 38 cpu_set_t *preinit_mask = preinit_process->dirty
350 ? &preinit_process->future_process_mask
351
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 18 times.
19 : &preinit_process->current_process_mask;
352
353 // A: Simple inheritance, mask is not provided, or masks are equal
354
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 2 times.
19 if (process_mask == NULL
355
2/2
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 2 times.
17 || CPU_COUNT(process_mask) == 0
356
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 10 times.
15 || CPU_EQUAL(process_mask, preinit_mask)) {
357 9 process = preinit_process;
358 9 process->pid = pid;
359 9 process->preregistered = false;
360 }
361
362 // B: Inheritance + expansion
363
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 7 times.
10 else if (mu_is_proper_superset(process_mask, preinit_mask)) {
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (preinit_process->dirty) {
365 // This case does not allow a dirty process
366 error = DLB_ERR_PDIRTY;
367 } else {
368 // Register the new CPUs in a placeholder process
369 pinfo_t new_process;
370 cpu_set_t new_cpus;
371 3 CPU_ZERO(&new_cpus);
372 3 mu_subtract(&new_cpus, process_mask, preinit_mask);
373 3 int register_error = register_mask(&new_process, &new_cpus);
374
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (register_error == DLB_SUCCESS) {
375 // Inherit and merge CPUs
376 2 process = preinit_process;
377 2 process->pid = pid;
378 2 process->preregistered = false;
379 2 memcpy(&process->current_process_mask, process_mask, sizeof(cpu_set_t));
380 2 memcpy(&process->future_process_mask, process_mask, sizeof(cpu_set_t));
381 } else {
382 1 error = DLB_ERR_PERM;
383 }
384 }
385 }
386
387 // C: New spot, subset
388
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 1 times.
7 else if (mu_is_proper_subset(process_mask, preinit_mask)) {
389
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if (preinit_process->dirty) {
390 // This case does not allow a dirty process
391 error = DLB_ERR_PDIRTY;
392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 } else if (empty_spot == NULL) {
393 error = DLB_ERR_NOMEM;
394 } else {
395 /* Initialize new spot with inherited CPUs */
396 cpu_set_t inherited_cpus;
397
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 6 times.
102 CPU_AND(&inherited_cpus, preinit_mask, process_mask);
398 6 process = empty_spot;
399 6 *process = (const pinfo_t) {.pid = pid};
400 6 memcpy(&process->current_process_mask, &inherited_cpus, sizeof(cpu_set_t));
401 6 memcpy(&process->future_process_mask, &inherited_cpus, sizeof(cpu_set_t));
402 /* Remove inherited CPUs from preregistered process */
403 6 mu_subtract(&preinit_process->current_process_mask,
404 6 &preinit_process->current_process_mask, &inherited_cpus);
405 6 mu_subtract(&preinit_process->future_process_mask,
406 6 &preinit_process->future_process_mask, &inherited_cpus);
407 }
408 }
409
410 // D: New spot + expansion
411 else {
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (preinit_process->dirty) {
413 // This case does not allow a dirty process
414 error = DLB_ERR_PDIRTY;
415
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 } else if (empty_spot == NULL) {
416 error = DLB_ERR_NOMEM;
417 } else {
418 // Register the new CPUs in a placeholder process
419 pinfo_t new_process;
420 cpu_set_t new_cpus;
421 1 CPU_ZERO(&new_cpus);
422 1 mu_subtract(&new_cpus, process_mask, preinit_mask);
423 1 int register_error = register_mask(&new_process, &new_cpus);
424
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (register_error == DLB_SUCCESS) {
425 /* Initialize new spot with the mask provided */
426 1 process = empty_spot;
427 1 process->pid = pid;
428 1 process->preregistered = false;
429 1 memcpy(&process->current_process_mask, process_mask, sizeof(cpu_set_t));
430 1 memcpy(&process->future_process_mask, process_mask, sizeof(cpu_set_t));
431 /* Remove inherited CPUs from preregistered process */
432 1 mu_subtract(&preinit_process->current_process_mask,
433 1 &preinit_process->current_process_mask, process_mask);
434 1 mu_subtract(&preinit_process->future_process_mask,
435 1 &preinit_process->future_process_mask, process_mask);
436 } else {
437 error = DLB_ERR_PERM;
438 }
439 }
440 }
441
442
3/4
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
19 if (error == DLB_NOTED && new_process_mask) {
443 // If the process has correctly inherit all/some of the CPUs,
444 // update the output mask with the appropriate CPU mask
445 // we cannot resolve the dirty flag yet
446 18 memcpy(new_process_mask,
447
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 process->dirty ? &process->future_process_mask
448 : &process->current_process_mask,
449 sizeof(cpu_set_t));
450 }
451 } // end of pre-registered process
452
453
2/2
✓ Branch 0 taken 117 times.
✓ Branch 1 taken 1 times.
118 if (process) {
454 // Save pointer for faster access
455 117 my_pinfo = process;
456 }
457
458
4/4
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 106 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 101 times.
118 if (process == empty_spot && empty_spot_is_last) {
459 // increase shared memory upper bound
460 101 ++shdata->num_processes;
461 }
462
463 17 shmem_procinfo__init_error:
464 /* labels are required to belong to a statment, a null one is enough */
465 ;
466 }
467 131 shmem_unlock(shm_handler);
468
469
6/6
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 117 times.
131 if (error == DLB_ERR_NOMEM || error == DLB_ERR_PERM || error == DLB_ERR_NOCOMP) {
470 14 warn_error(error);
471 }
472
473
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 114 times.
131 if (error < DLB_SUCCESS) {
474 // The shared memory contents are untouched, but the counter needs to
475 // be decremented, and the shared memory deleted if needed
476 17 close_shmem();
477 }
478
479 131 return error;
480 }
481
482 113 int shmem_procinfo__init(pid_t pid, pid_t preinit_pid, const cpu_set_t *process_mask,
483 cpu_set_t *new_process_mask, const char *shmem_key, int shmem_size_multiplier) {
484
485 113 return shmem_procinfo__init_(pid, preinit_pid, process_mask, new_process_mask,
486 shmem_key, shmem_size_multiplier, false);
487 }
488
489 18 int shmem_procinfo__init_with_cpu_sharing(pid_t pid, pid_t preinit_pid,
490 const cpu_set_t *process_mask, const char *shmem_key, int shmem_size_multiplier) {
491
492 18 return shmem_procinfo__init_(pid, preinit_pid, process_mask, NULL,
493 shmem_key, shmem_size_multiplier, true);
494 }
495
496 43 int shmem_procinfo_ext__init(const char *shmem_key, int shmem_size_multiplier) {
497 // Shared memory creation
498 43 open_shmem(shmem_key, shmem_size_multiplier);
499
500 43 shmem_lock(shm_handler);
501 {
502 // Initialize some values if this is the 1st process attached to the shmem
503
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 15 times.
43 if (!shdata->flags.initialized) {
504 28 get_time(&shdata->initial_time);
505 28 mu_get_system_mask(&shdata->free_mask);
506 28 shdata->flags = (const procinfo_flags_t) {
507 .initialized = true,
508 .cpu_sharing_unknown = true,
509 };
510 28 shdata->max_processes = max_processes;
511 28 shdata->num_processes = 0;
512 }
513 }
514 43 shmem_unlock(shm_handler);
515
516 43 return DLB_SUCCESS;
517 }
518
519 28 int shmem_procinfo_ext__preinit(pid_t pid, const cpu_set_t *mask, dlb_drom_flags_t flags) {
520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
521
522 28 bool steal = flags & DLB_STEAL_CPUS;
523 28 bool sync = flags & DLB_SYNC_QUERY;
524 28 bool return_stolen = flags & DLB_RETURN_STOLEN;
525 28 int error = DLB_SUCCESS;
526 28 pinfo_t *process = NULL;
527 28 shmem_lock(shm_handler);
528 {
529
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
44 for (int p = 0; p < max_processes; p++) {
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
44 if (shdata->process_info[p].pid == pid) {
531 // PID already registered
532 error = DLB_ERR_INIT;
533 break;
534
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 16 times.
44 } else if (shdata->process_info[p].pid == NOBODY) {
535 28 process = &shdata->process_info[p];
536 28 *process = (const pinfo_t){
537 .pid = pid,
538 .preregistered = true};
539
540 // Register process mask into the system
541
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 13 times.
28 if (!steal) {
542 // If no stealing, register as usual
543 15 error = register_mask(process, mask);
544 } else {
545 // Otherwise, steal CPUs if necessary
546 13 error = set_new_mask(process, mask, sync, return_stolen, NULL);
547 }
548
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 26 times.
28 if (error) {
549 // Release shared memory spot
550 2 process->pid = NOBODY;
551 2 break;
552 }
553
554 // Set process initial values
555 26 memcpy(&process->current_process_mask, mask, sizeof(cpu_set_t));
556 26 memcpy(&process->future_process_mask, mask, sizeof(cpu_set_t));
557 26 process->dirty = false;
558
559 // Increase num_processes if needed
560
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 ensure ( p <= shdata->num_processes,
561 "Found free spot beyond num_processes in %s. Please report to %s.",
562 __func__, PACKAGE_BUGREPORT );
563
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 10 times.
26 if (p == shdata->num_processes) {
564 16 ++shdata->num_processes;
565 }
566
567 26 break;
568 }
569 }
570 }
571 28 shmem_unlock(shm_handler);
572
573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (error == DLB_ERR_INIT) {
574 verbose(VB_SHMEM, "Process %d already registered", pid);
575
3/4
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 27 times.
28 } else if (error == DLB_ERR_PERM || error == DLB_ERR_NOCOMP) {
576 1 warn_error(DLB_ERR_PERM);
577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
27 } else if (process == NULL) {
578 error = DLB_ERR_NOMEM;
579 warn_error(DLB_ERR_NOMEM);
580 }
581
582 28 return error;
583 }
584
585
586 /*********************************************************************************/
587 /* Finalize / Unregister */
588 /*********************************************************************************/
589
590 // Unregister CPUs. Add them to the free_mask or give them back to their owner
591 156 static int unregister_mask(pinfo_t *owner, const cpu_set_t *mask, bool return_stolen) {
592 // Return if empty mask
593
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 123 times.
156 if (CPU_COUNT(mask) == 0) return DLB_SUCCESS;
594
595 // Return if sharing is allowed and, thus, we don't need keep track of free_mask
596 // nor stolen CPUs
597
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 113 times.
123 if (shdata->flags.allow_cpu_sharing) {
598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 verbose(VB_DROM, "Process %d unregistering shared mask %s", owner->pid, mu_to_str(mask));
599 10 return DLB_SUCCESS;
600 }
601
602
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 100 times.
113 verbose(VB_DROM, "Process %d unregistering mask %s", owner->pid, mu_to_str(mask));
603
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 105 times.
113 if (return_stolen) {
604 // Look if each CPU belongs to some other process
605 int c, p;
606 8 int num_processes = shdata->num_processes;
607
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 8 times.
52 for (c = 0; c < max_cpus; c++) {
608
5/6
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 27 times.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 27 times.
44 if (CPU_ISSET(c, mask)) {
609
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 for (p = 0; p < num_processes; p++) {
610 27 pinfo_t *process = &shdata->process_info[p];
611
6/8
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 27 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 10 times.
✓ Branch 6 taken 17 times.
✓ Branch 7 taken 10 times.
27 if (process->pid != NOBODY && CPU_ISSET(c, &process->stolen_cpus)) {
612 // give it back to the process
613
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 CPU_SET(c, &process->future_process_mask);
614
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 CPU_CLR(c, &process->stolen_cpus);
615 17 process->dirty = true;
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 verbose(VB_DROM, "Giving back CPU %d to process %d", c, process->pid);
617 17 break;
618 }
619 }
620 // if we didn't find the owner, add it to the free_mask
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (p == num_processes) {
622 CPU_SET(c, &shdata->free_mask);
623 }
624 // remove CPU from owner
625
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 CPU_CLR(c, &owner->future_process_mask);
626 17 owner->dirty = true;
627 }
628 }
629 } else {
630 // Add mask to free_mask and remove them from owner
631
2/2
✓ Branch 0 taken 1680 times.
✓ Branch 1 taken 105 times.
1785 CPU_OR(&shdata->free_mask, &shdata->free_mask, mask);
632 105 mu_subtract(&owner->future_process_mask, &owner->future_process_mask, mask);
633 105 owner->dirty = true;
634 }
635 113 return DLB_SUCCESS;
636 }
637
638 175 static void close_shmem(void) {
639 175 pthread_mutex_lock(&mutex);
640 {
641
2/2
✓ Branch 0 taken 97 times.
✓ Branch 1 taken 78 times.
175 if (--subprocesses_attached == 0) {
642 97 shmem_finalize(shm_handler, is_shmem_empty);
643 97 shm_handler = NULL;
644 97 shdata = NULL;
645 }
646 }
647 175 pthread_mutex_unlock(&mutex);
648 175 }
649
650 127 int shmem_procinfo__finalize(pid_t pid, bool return_stolen, const char *shmem_key,
651 int shmem_size_multiplier) {
652
653 127 int error = DLB_SUCCESS;
654 127 bool shmem_reopened = false;
655
656
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 121 times.
127 if (shm_handler == NULL) {
657 /* procinfo_finalize may be called to finalize existing process
658 * even if the file descriptor is not opened. (DLB_PreInit + forc-exec case) */
659
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5 times.
6 if (shmem_exists(shmem_name, shmem_key)) {
660 1 open_shmem(shmem_key, shmem_size_multiplier);
661 1 shmem_reopened = true;
662 } else {
663 5 return DLB_ERR_NOSHMEM;
664 }
665 }
666
667 // Check that pid exists
668 122 pinfo_t *process = get_process(pid);
669
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 115 times.
122 if (unlikely(process == NULL)) {
670 7 error = DLB_ERR_NOPROC;
671 }
672
673 122 shmem_lock(shm_handler);
674 {
675
2/2
✓ Branch 0 taken 115 times.
✓ Branch 1 taken 7 times.
122 if (process) {
676 // Unregister our process mask, or future mask if we are dirty
677
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 114 times.
115 if (process->dirty) {
678 1 unregister_mask(process, &process->future_process_mask, return_stolen);
679 } else {
680 114 unregister_mask(process, &process->current_process_mask, return_stolen);
681 }
682
683 // Clear process fields
684 115 *process = (const pinfo_t){0};
685
686 // Clear local pointer
687 115 my_pinfo = NULL;
688 }
689 }
690 122 shmem_unlock(shm_handler);
691
692 // Close shared memory only if pid was succesfully removed or if shmem was reopened
693
3/4
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 115 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
122 if (process || shmem_reopened) {
694 115 close_shmem();
695 }
696
697 122 return error;
698 }
699
700 43 int shmem_procinfo_ext__finalize(void) {
701 // Protect double finalization
702
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if (shm_handler == NULL) {
703 return DLB_ERR_NOSHMEM;
704 }
705
706 // Shared memory destruction
707 43 close_shmem();
708
709 43 return DLB_SUCCESS;
710 }
711
712 12 int shmem_procinfo_ext__postfinalize(pid_t pid, bool return_stolen) {
713
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
12 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
714
715 12 int error = DLB_SUCCESS;
716 12 shmem_lock(shm_handler);
717 {
718 12 pinfo_t *process = get_process(pid);
719
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 11 times.
12 if (process == NULL) {
720
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 verbose(VB_DROM, "Cannot finalize process %d", pid);
721 1 error = DLB_ERR_NOPROC;
722 } else {
723 // Unregister process mask, or future mask if dirty
724
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (process->dirty) {
725 unregister_mask(process, &process->future_process_mask, return_stolen);
726 } else {
727 11 unregister_mask(process, &process->current_process_mask, return_stolen);
728 }
729
730 // Clear process fields
731 11 *process = (const pinfo_t){0};
732 }
733 }
734 12 shmem_unlock(shm_handler);
735 12 return error;
736 }
737
738 5 int shmem_procinfo_ext__recover_stolen_cpus(int pid) {
739
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
740
741 int error;
742 5 shmem_lock(shm_handler);
743 {
744 5 pinfo_t *process = get_process(pid);
745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (process == NULL) {
746 verbose(VB_DROM, "Cannot find process %d", pid);
747 error = DLB_ERR_NOPROC;
748
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
5 } else if (CPU_COUNT(&process->stolen_cpus) == 0) {
749 4 error = DLB_NOUPDT;
750 } else {
751 // Recover all stolen CPUs only if the CPU is set in the free_mask
752 cpu_set_t recovered_cpus;
753
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 1 times.
17 CPU_AND(&recovered_cpus, &process->stolen_cpus, &shdata->free_mask);
754 1 error = register_mask(process, &recovered_cpus);
755
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (error == DLB_SUCCESS) {
756 1 mu_subtract(&process->stolen_cpus, &process->stolen_cpus, &recovered_cpus);
757 }
758 }
759 }
760 5 shmem_unlock(shm_handler);
761 5 return error;
762 }
763
764
765 /*********************************************************************************/
766 /* Get / Set Process mask */
767 /*********************************************************************************/
768
769 /* PRE: shm_handler and my_pinfo are not NULL */
770 11 static int shmem_procinfo__getprocessmask_self(cpu_set_t *mask) {
771 int error;
772 11 pinfo_t *process = my_pinfo;
773 11 shmem_lock(shm_handler);
774 {
775 /* If current process is dirty, update mask and return the new one */
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (process->dirty) {
777 memcpy(mask, &process->future_process_mask, sizeof(cpu_set_t));
778 memcpy(&process->current_process_mask, &process->future_process_mask,
779 sizeof(cpu_set_t));
780 process->dirty = false;
781 error = DLB_NOTED;
782 } else {
783 11 memcpy(mask, &process->current_process_mask, sizeof(cpu_set_t));
784 11 error = DLB_SUCCESS;
785 }
786 }
787 11 shmem_unlock(shm_handler);
788 11 return error;
789 }
790
791 42 int shmem_procinfo__getprocessmask(pid_t pid, cpu_set_t *mask, dlb_drom_flags_t flags) {
792
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 36 times.
42 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
793
794 /* Specific case, if pid is 0 the target is the current process */
795
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 33 times.
36 if (pid == 0) {
796
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if (my_pinfo != NULL) {
797 2 return shmem_procinfo__getprocessmask_self(mask);
798 } else {
799 1 return DLB_ERR_NOPROC;
800 }
801 }
802
803 /* Specific case, if pid is current process */
804
4/4
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 19 times.
33 if (my_pinfo != NULL && my_pinfo->pid == pid) {
805 9 return shmem_procinfo__getprocessmask_self(mask);
806 }
807
808 24 int error = DLB_SUCCESS;
809 24 bool done = false;
810 pinfo_t *process;
811
812 24 shmem_lock(shm_handler);
813 {
814 // Find process
815 24 process = get_process(pid);
816
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 20 times.
24 if (process == NULL) {
817
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 verbose(VB_DROM, "Getting mask: cannot find process with pid %d", pid);
818 4 error = DLB_ERR_NOPROC;
819 }
820
821
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
24 if (!error) {
822
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if (!process->dirty) {
823 // Get current mask if not dirty
824 20 memcpy(mask, &process->current_process_mask, sizeof(cpu_set_t));
825 20 done = true;
826 } else if (!(flags & DLB_SYNC_QUERY)) {
827 // Get future mask if query is non-blocking
828 memcpy(mask, &process->future_process_mask, sizeof(cpu_set_t));
829 done = true;
830 }
831 }
832 }
833 24 shmem_unlock(shm_handler);
834
835
3/4
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 20 times.
24 if (!error && !done) {
836 // process is valid, but it's dirty so we need to poll
837 int64_t elapsed;
838 struct timespec start, now;
839 get_time_coarse(&start);
840 while(true) {
841
842 // Delay
843 usleep(SYNC_POLL_DELAY);
844
845 // Polling
846 shmem_lock(shm_handler);
847 {
848 if (!process->dirty) {
849 memcpy(mask, &process->current_process_mask, sizeof(cpu_set_t));
850 done = true;
851 }
852 }
853 shmem_unlock(shm_handler);
854
855 // Break if done
856 if (done) break;
857
858 // Break if timeout
859 get_time_coarse(&now);
860 elapsed = timespec_diff(&start, &now);
861 if (elapsed > SYNC_POLL_TIMEOUT) {
862 error = DLB_ERR_TIMEOUT;
863 break;
864 }
865 }
866 }
867
868 24 return error;
869 }
870
871 /* PRE: shm_handler and my_pinfo are not NULL */
872 5 static int shmem_procinfo__setprocessmask_self(const cpu_set_t *mask, dlb_drom_flags_t flags,
873 cpu_set_t *free_cpu_mask) {
874 int error;
875 5 bool return_stolen = flags & DLB_RETURN_STOLEN;
876 5 bool skip_auto_update = flags & DLB_NO_SYNC;
877 5 pinfo_t *process = my_pinfo;
878 5 shmem_lock(shm_handler);
879 {
880
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
5 if (!process->dirty || CPU_EQUAL(mask, &process->future_process_mask)) {
881 5 error = set_new_mask(process, mask, false /* sync */, return_stolen, free_cpu_mask);
882 } else {
883 error = DLB_ERR_PDIRTY;
884 }
885
886 /* Update current mask now */
887
4/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
5 if (error == DLB_SUCCESS && !skip_auto_update) {
888 3 memcpy(&process->current_process_mask, &process->future_process_mask,
889 sizeof(cpu_set_t));
890 3 process->dirty = false;
891 }
892 }
893 5 shmem_unlock(shm_handler);
894
895
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (error == DLB_ERR_PDIRTY) {
896 verbose(VB_DROM, "Setting mask: current process is already dirty");
897
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 } else if (error == DLB_ERR_PERM) {
898 verbose(VB_DROM, "Setting mask: cannot steal mask %s", mu_to_str(mask));
899
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
5 } else if (error == DLB_ERR_NOCOMP) {
900
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 verbose(VB_DROM, "Setting mask: cannot steal mask if shmem has been intialized with CPU sharing");
901 }
902
903 5 return error;
904 }
905
906 28 int shmem_procinfo__setprocessmask(pid_t pid, const cpu_set_t *mask, dlb_drom_flags_t flags,
907 cpu_set_t *free_cpu_mask) {
908
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 23 times.
28 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
909
910 /* Specific case, if pid is 0 the target is the current process */
911
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 19 times.
23 if (pid == 0) {
912
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if (my_pinfo != NULL) {
913 2 return shmem_procinfo__setprocessmask_self(mask, flags, free_cpu_mask);
914 } else {
915 2 return DLB_ERR_NOPROC;
916 }
917 }
918
919 /* Specific case, if pid is current process */
920
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 11 times.
19 if (my_pinfo != NULL && my_pinfo->pid == pid) {
921 3 return shmem_procinfo__setprocessmask_self(mask, flags, free_cpu_mask);
922 }
923
924 16 bool sync = flags & DLB_SYNC_QUERY;
925 16 bool return_stolen = flags & DLB_RETURN_STOLEN;
926 16 int error = DLB_SUCCESS;
927 pinfo_t *process;
928 16 shmem_lock(shm_handler);
929 {
930 // Find process
931 16 process = get_process(pid);
932
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (process == NULL) {
933 error = DLB_ERR_NOPROC;
934 }
935
936 // Process already dirty
937
3/4
✓ Branch 0 taken 16 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 15 times.
16 if (!error && process->dirty) {
938 1 error = DLB_ERR_PDIRTY;
939 }
940
941 // Set new mask if everything ok
942
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 1 times.
16 error = error ? error : set_new_mask(process, mask, sync, return_stolen, free_cpu_mask);
943 }
944 16 shmem_unlock(shm_handler);
945
946 // Polling until dirty is cleared
947
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 14 times.
16 if (!error && sync) {
948 bool done = false;
949 struct timespec start, now;
950 get_time_coarse(&start);
951 do {
952
953 // Delay
954 usleep(SYNC_POLL_DELAY);
955
956 // Poll
957 shmem_lock(shm_handler);
958 {
959 if (process->pid != pid) {
960 // process no longer valid
961 error = DLB_ERR_NOPROC;
962 done = true;
963 }
964
965 if (!process->dirty) {
966 done = true;
967 }
968 }
969 shmem_unlock(shm_handler);
970
971 // Check timeout
972 if (!done) {
973 get_time_coarse(&now);
974 if (timespec_diff(&start, &now) > SYNC_POLL_TIMEOUT) {
975 error = DLB_ERR_TIMEOUT;
976 }
977 }
978 } while (!done && error == DLB_SUCCESS);
979 }
980
981
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
16 if (error == DLB_ERR_NOPROC) {
982 verbose(VB_DROM, "Setting mask: cannot find process with pid %d", pid);
983
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 15 times.
16 } else if (error == DLB_ERR_PDIRTY) {
984
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 verbose(VB_DROM, "Setting mask: process %d is already dirty", pid);
985
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 } else if (error == DLB_ERR_PERM) {
986
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 verbose(VB_DROM, "Setting mask: cannot steal mask %s", mu_to_str(mask));
987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 } else if (error == DLB_ERR_NOCOMP) {
988 verbose(VB_DROM, "Setting mask: cannot steal mask if shmem has been intialized with CPU sharing");
989 }
990
991 16 return error;
992 }
993
994
995 /*********************************************************************************/
996 /* Generic Getters */
997 /*********************************************************************************/
998
999 49 int shmem_procinfo__polldrom(pid_t pid, int *new_cpus, cpu_set_t *new_mask) {
1000 int error;
1001
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 48 times.
49 if (shm_handler == NULL) {
1002 1 error = DLB_ERR_NOSHMEM;
1003 } else {
1004 48 pinfo_t *process = get_process(pid);
1005
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 47 times.
48 if (!process) {
1006 1 error = DLB_ERR_NOPROC;
1007
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 36 times.
47 } else if (!process->dirty) {
1008 11 error = DLB_NOUPDT;
1009 } else {
1010 36 shmem_lock(shm_handler);
1011 {
1012 // Update output parameters
1013 36 memcpy(new_mask, &process->future_process_mask, sizeof(cpu_set_t));
1014
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 35 times.
36 if (new_cpus != NULL) *new_cpus = CPU_COUNT(&process->future_process_mask);
1015
1016 // Upate local info
1017 36 memcpy(&process->current_process_mask, &process->future_process_mask,
1018 sizeof(cpu_set_t));
1019 36 process->dirty = false;
1020 }
1021 36 shmem_unlock(shm_handler);
1022 36 error = DLB_SUCCESS;
1023 }
1024 }
1025 49 return error;
1026 }
1027
1028 5 int shmem_procinfo__getpidlist(pid_t *pidlist, int *nelems, int max_len) {
1029 5 *nelems = 0;
1030
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
1031 5 shmem_lock(shm_handler);
1032 {
1033 5 int num_processes = shdata->num_processes;
1034
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 3 times.
5 for (int p = 0; p < num_processes; p++) {
1035 2 pid_t pid = shdata->process_info[p].pid;
1036
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (pid != NOBODY) {
1037 2 pidlist[(*nelems)++] = pid;
1038 }
1039
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (*nelems == max_len) {
1040 2 break;
1041 }
1042 }
1043 }
1044 5 shmem_unlock(shm_handler);
1045 5 return DLB_SUCCESS;
1046 }
1047
1048
1049 /*********************************************************************************/
1050 /* Statistics */
1051 /*********************************************************************************/
1052
1053 double shmem_procinfo__getcpuusage(pid_t pid) {
1054 if (shm_handler == NULL) return -1.0;
1055
1056 double cpu_usage = -1.0;
1057 shmem_lock(shm_handler);
1058 {
1059 pinfo_t *process = get_process(pid);
1060 if (process) {
1061 cpu_usage = process->cpu_usage;
1062 }
1063 }
1064 shmem_unlock(shm_handler);
1065
1066 return cpu_usage;
1067 }
1068
1069 double shmem_procinfo__getcpuavgusage(pid_t pid) {
1070 if (shm_handler == NULL) return -1.0;
1071
1072 double cpu_avg_usage = -1.0;
1073 shmem_lock(shm_handler);
1074 {
1075 pinfo_t *process = get_process(pid);
1076 if (process) {
1077 cpu_avg_usage = process->cpu_avg_usage;
1078 }
1079 }
1080 shmem_unlock(shm_handler);
1081
1082 return cpu_avg_usage;
1083 }
1084
1085 void shmem_procinfo__getcpuusage_list(double *usagelist, int *nelems, int max_len) {
1086 *nelems = 0;
1087 if (shm_handler == NULL) return;
1088 shmem_lock(shm_handler);
1089 {
1090 int num_processes = shdata->num_processes;
1091 for (int p = 0; p < num_processes; p++) {
1092 if (shdata->process_info[p].pid != NOBODY) {
1093 usagelist[(*nelems)++] = shdata->process_info[p].cpu_usage;
1094 }
1095 if (*nelems == max_len) {
1096 break;
1097 }
1098 }
1099 }
1100 shmem_unlock(shm_handler);
1101 }
1102
1103 void shmem_procinfo__getcpuavgusage_list(double *avgusagelist, int *nelems, int max_len) {
1104 *nelems = 0;
1105 if (shm_handler == NULL) return;
1106 shmem_lock(shm_handler);
1107 {
1108 int num_processes = shdata->num_processes;
1109 for (int p = 0; p < num_processes; p++) {
1110 if (shdata->process_info[p].pid != NOBODY) {
1111 avgusagelist[(*nelems)++] = shdata->process_info[p].cpu_avg_usage;
1112 }
1113 if (*nelems == max_len) {
1114 break;
1115 }
1116 }
1117 }
1118 shmem_unlock(shm_handler);
1119 }
1120
1121 double shmem_procinfo__getnodeusage(void) {
1122 if (shm_handler == NULL) return -1.0;
1123
1124 double cpu_usage = 0.0;
1125 shmem_lock(shm_handler);
1126 {
1127 int num_processes = shdata->num_processes;
1128 for (int p = 0; p < num_processes; p++) {
1129 if (shdata->process_info[p].pid != NOBODY) {
1130 cpu_usage += shdata->process_info[p].cpu_usage;
1131 }
1132 }
1133 }
1134 shmem_unlock(shm_handler);
1135
1136 return cpu_usage;
1137 }
1138
1139 double shmem_procinfo__getnodeavgusage(void) {
1140 if (shm_handler == NULL) return -1.0;
1141
1142 double cpu_avg_usage = 0.0;
1143 shmem_lock(shm_handler);
1144 {
1145 int num_processes = shdata->num_processes;
1146 for (int p = 0; p < num_processes; p++) {
1147 if (shdata->process_info[p].pid != NOBODY) {
1148 cpu_avg_usage += shdata->process_info[p].cpu_avg_usage;
1149 }
1150 }
1151 }
1152 shmem_unlock(shm_handler);
1153
1154 return cpu_avg_usage;
1155 }
1156
1157 int shmem_procinfo__getactivecpus(pid_t pid) {
1158 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
1159
1160 int active_cpus = -1;
1161 shmem_lock(shm_handler);
1162 {
1163 pinfo_t *process = get_process(pid);
1164 if (process) {
1165 active_cpus = process->active_cpus;
1166 }
1167 }
1168 shmem_unlock(shm_handler);
1169 return active_cpus;
1170 }
1171
1172 void shmem_procinfo__getactivecpus_list(pid_t *cpuslist, int *nelems, int max_len) {
1173 *nelems = 0;
1174 if (shm_handler == NULL) return;
1175 shmem_lock(shm_handler);
1176 {
1177 int num_processes = shdata->num_processes;
1178 for (int p = 0; p < num_processes; p++) {
1179 if (shdata->process_info[p].pid != NOBODY) {
1180 cpuslist[(*nelems)++] = shdata->process_info[p].active_cpus;
1181 }
1182 if (*nelems == max_len) {
1183 break;
1184 }
1185 }
1186 }
1187 shmem_unlock(shm_handler);
1188 }
1189
1190 int shmem_procinfo__getloadavg(pid_t pid, double *load) {
1191 if (shm_handler == NULL) return DLB_ERR_NOSHMEM;
1192 int error = DLB_ERR_UNKNOWN;
1193 #ifdef DLB_LOAD_AVERAGE
1194 shmem_lock(shm_handler);
1195 {
1196 pinfo_t *process = get_process(pid);
1197 if (process) {
1198 load[0] = process->load[0];
1199 load[1] = process->load[1];
1200 load[2] = process->load[2];
1201 error = 0;
1202 }
1203 }
1204 shmem_unlock(shm_handler);
1205 #endif
1206 return error;
1207 }
1208
1209
1210 int shmem_procinfo__setcpuavgusage(pid_t pid, double new_avg_usage) {
1211
1212 if (shm_handler == NULL) return -1.0;
1213
1214 shmem_lock(shm_handler);
1215 {
1216 pinfo_t *process = get_process(pid);
1217 if (process) {
1218 process->cpu_avg_usage = new_avg_usage;
1219 }
1220 }
1221 shmem_unlock(shm_handler);
1222
1223 return DLB_SUCCESS;
1224 }
1225
1226 int shmem_procinfo__setcpuusage(pid_t pid,int index, double new_avg_usage) {
1227 if (shm_handler == NULL) return -1.0;
1228
1229 shmem_lock(shm_handler);
1230 {
1231 pinfo_t *process = get_process(pid);
1232 if (process) {
1233 process->cpu_avg_usage = new_avg_usage;
1234 }
1235 }
1236 shmem_unlock(shm_handler);
1237
1238 return DLB_SUCCESS;
1239 }
1240
1241
1242 /*********************************************************************************/
1243 /* Misc */
1244 /*********************************************************************************/
1245
1246 9 void shmem_procinfo__print_info(const char *shmem_key, int shmem_size_multiplier) {
1247
1248 /* If the shmem is not opened, obtain a temporary fd */
1249 9 bool temporary_shmem = shm_handler == NULL;
1250
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 if (temporary_shmem) {
1251 4 shmem_procinfo_ext__init(shmem_key, shmem_size_multiplier);
1252 }
1253
1254 /* Make a full copy of the shared memory */
1255 9 shdata_t *shdata_copy = malloc(shmem_procinfo__size());
1256 9 shmem_lock(shm_handler);
1257 {
1258 9 memcpy(shdata_copy, shdata, shmem_procinfo__size());
1259 }
1260 9 shmem_unlock(shm_handler);
1261
1262 /* Close shmem if needed */
1263
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 5 times.
9 if (temporary_shmem) {
1264 4 shmem_procinfo_ext__finalize();
1265 }
1266
1267 /* Find the max number of characters per column */
1268 9 pid_t max_pid = 111; /* 3 digits for 'PID' */
1269 9 int max_current = 4; /* 'Mask' */
1270 9 int max_future = 6; /* 'Future' */
1271 9 int max_stolen = 6; /* 'Stolen' */
1272 9 int num_processes = shdata_copy->num_processes;
1273
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 for (int p = 0; p < num_processes; ++p) {
1274 8 pinfo_t *process = &shdata_copy->process_info[p];
1275
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if (process->pid != NOBODY) {
1276 int len;
1277 /* pid */
1278 7 max_pid = process->pid > max_pid ? process->pid : max_pid;
1279 /* current_mask */
1280 7 len = strlen(mu_to_str(&process->current_process_mask));
1281 7 max_current = len > max_current ? len : max_current;
1282 /* future_mask */
1283 7 len = strlen(mu_to_str(&process->future_process_mask));
1284 7 max_future = len > max_future ? len : max_future;
1285 /* stolen_mask */
1286 7 len = strlen(mu_to_str(&process->stolen_cpus));
1287 7 max_stolen = len > max_stolen ? len : max_stolen;
1288 }
1289 }
1290 9 int max_pid_digits = snprintf(NULL, 0, "%d", max_pid);
1291
1292 /* Initialize buffer */
1293 print_buffer_t buffer;
1294 9 printbuffer_init(&buffer);
1295
1296 /* Set up line buffer */
1297 enum { MAX_LINE_LEN = 512 };
1298 char line[MAX_LINE_LEN];
1299
1300
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 for (int p = 0; p < num_processes; p++) {
1301 8 pinfo_t *process = &shdata_copy->process_info[p];
1302
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if (process->pid != NOBODY) {
1303 const char *mask_str;
1304
1305 /* Copy current mask */
1306 7 mask_str = mu_to_str(&process->current_process_mask);
1307 7 char *current = malloc((strlen(mask_str)+1)*sizeof(char));
1308 7 strcpy(current, mask_str);
1309
1310 /* Copy future mask */
1311 7 mask_str = mu_to_str(&process->future_process_mask);
1312 7 char *future = malloc((strlen(mask_str)+1)*sizeof(char));
1313 7 strcpy(future, mask_str);
1314
1315 /* Copy stolen mask */
1316 7 mask_str = mu_to_str(&process->stolen_cpus);
1317 7 char *stolen = malloc((strlen(mask_str)+1)*sizeof(char));
1318 7 strcpy(stolen, mask_str);
1319
1320 /* Append line to buffer */
1321 7 snprintf(line, MAX_LINE_LEN,
1322 " | %*d | %*s | %*s | %*s | %6d |",
1323 max_pid_digits, process->pid,
1324 max_current, current,
1325 max_future, future,
1326 max_stolen, stolen,
1327 7 process->dirty);
1328 7 printbuffer_append(&buffer, line);
1329
1330 7 free(current);
1331 7 free(future);
1332 7 free(stolen);
1333 }
1334 }
1335
1336
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
9 if (buffer.addr[0] != '\0' ) {
1337 /* Construct header */
1338 5 snprintf(line, MAX_LINE_LEN,
1339 " | %*s | %*s | %*s | %*s | Dirty? |",
1340 max_pid_digits, "PID",
1341 max_current, "Mask",
1342 max_future, "Future",
1343 max_stolen, "Stolen");
1344
1345 /* Print header + buffer */
1346 5 info0("=== Processes Masks ===\n"
1347 "%s\n"
1348 "%s", line, buffer.addr);
1349 }
1350 9 printbuffer_destroy(&buffer);
1351 9 free(shdata_copy);
1352 9 }
1353
1354 11 bool shmem_procinfo__exists(void) {
1355 11 return shm_handler != NULL;
1356 }
1357
1358 1 int shmem_procinfo__version(void) {
1359 1 return SHMEM_PROCINFO_VERSION;
1360 }
1361
1362 117 size_t shmem_procinfo__size(void) {
1363 // max_processes contains a value once shmem is initialized,
1364 // otherwise return default size
1365 117 return sizeof(shdata_t) + sizeof(pinfo_t) * (
1366
2/2
✓ Branch 0 taken 116 times.
✓ Branch 1 taken 1 times.
117 max_processes > 0 ? max_processes : mu_get_system_size());
1367 }
1368
1369
1370 /*** Helper functions, the shm lock must have been acquired beforehand ***/
1371
1372
1373 // FIXME Update statistics temporarily disabled
1374 #if 0
1375 static void update_process_loads(void) {
1376 // Get the active CPUs
1377 cpu_set_t mask;
1378 memcpy(&mask, &global_spd.active_mask, sizeof(cpu_set_t));
1379 shdata->process_info[my_process].active_cpus = CPU_COUNT(&mask);
1380
1381 // Compute elapsed total time
1382 struct timespec current_ttime;
1383 int64_t elapsed_ttime_since_last;
1384 int64_t elapsed_ttime_since_init;;
1385 get_time_coarse(&current_ttime);
1386 elapsed_ttime_since_last = timespec_diff(&last_ttime, &current_ttime);
1387 elapsed_ttime_since_init = timespec_diff(&shdata->initial_time, &current_ttime );
1388
1389 // Compute elapsed useful time (user+system)
1390 struct rusage usage;
1391 struct timespec current_utime;
1392 int64_t elapsed_utime_since_last;
1393 int64_t elapsed_utime_since_init;
1394 getrusage(RUSAGE_SELF, &usage);
1395 add_tv_to_ts(&usage.ru_utime, &usage.ru_stime, &current_utime);
1396 elapsed_utime_since_last = timespec_diff(&last_utime, &current_utime);
1397 elapsed_utime_since_init = to_nsecs(&current_utime);
1398
1399 // Update times for next update
1400 last_ttime = current_ttime;
1401 last_utime = current_utime;
1402
1403 // Compute usage
1404 shdata->process_info[my_process].cpu_usage = 100 *
1405 (double)elapsed_utime_since_last / (double)elapsed_ttime_since_last;
1406
1407 // Compute avg usage
1408 shdata->process_info[my_process].cpu_avg_usage = 100 *
1409 (double)elapsed_utime_since_init / (double)elapsed_ttime_since_init;
1410
1411 #ifdef DLB_LOAD_AVERAGE
1412 // Do not update the Load Average if the elapsed is less that a threshold
1413 if (elapsed_ttime > UPDATE_LOADAVG_MIN_THRESHOLD) {
1414 shdata->process_info[my_process].last_ltime = current_ttime;
1415 // WIP
1416 }
1417 #endif
1418 }
1419 #endif
1420
1421
1422 // Steal every CPU in mask from other processes
1423 63 static int steal_mask(pinfo_t* new_owner, const cpu_set_t *mask, bool sync, bool dry_run) {
1424 // Return if empty mask
1425
2/2
✓ Branch 1 taken 38 times.
✓ Branch 2 taken 25 times.
63 if (CPU_COUNT(mask) == 0) return DLB_SUCCESS;
1426
1427 25 int error = DLB_SUCCESS;
1428 cpu_set_t cpus_left_to_steal;
1429 25 memcpy(&cpus_left_to_steal, mask, sizeof(cpu_set_t));
1430
1431 // Iterate per process, steal in batch
1432 25 int num_processes = shdata->num_processes;
1433
1/2
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
43 for (int p = 0; p < num_processes; ++p) {
1434 43 pinfo_t *victim = &shdata->process_info[p];
1435
3/4
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 35 times.
✗ Branch 3 not taken.
43 if (victim != new_owner && victim->pid != NOBODY) {
1436 cpu_set_t target_cpus;
1437
2/2
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 35 times.
595 CPU_AND(&target_cpus, &victim->current_process_mask, mask);
1438
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 if (CPU_COUNT(&target_cpus) > 0) {
1439 // victim contains target CPUs
1440
2/2
✓ Branch 0 taken 34 times.
✓ Branch 1 taken 1 times.
35 if (!victim->dirty) {
1441 // Steal target_cpus from victim
1442
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 17 times.
34 if (!dry_run) {
1443 17 victim->dirty = true;
1444 17 mu_subtract(&victim->future_process_mask,
1445 17 &victim->current_process_mask, &target_cpus);
1446
2/2
✓ Branch 0 taken 272 times.
✓ Branch 1 taken 17 times.
289 CPU_OR(&victim->stolen_cpus, &victim->stolen_cpus, &target_cpus);
1447
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 verbose(VB_DROM, "CPUs %s have been removed from process %d",
1448 mu_to_str(mask), victim->pid);
1449 }
1450 34 mu_subtract(&cpus_left_to_steal, &cpus_left_to_steal, &target_cpus);
1451
2/2
✓ Branch 1 taken 24 times.
✓ Branch 2 taken 10 times.
34 if (CPU_COUNT(&cpus_left_to_steal) == 0)
1452 25 break;
1453 } else {
1454 1 error = DLB_ERR_PERM;
1455 1 break;
1456 }
1457 }
1458 }
1459 }
1460
1461
3/4
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
25 if (unlikely(!error && CPU_COUNT(&cpus_left_to_steal) > 0)) {
1462 warning("Could not find candidate for stealing mask %s. Please report to "
1463 PACKAGE_BUGREPORT, mu_to_str(mask));
1464 error = DLB_ERR_PERM;
1465 }
1466
1467
6/6
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 18 times.
✓ Branch 4 taken 3 times.
✓ Branch 5 taken 3 times.
25 if (!error && sync && !dry_run) {
1468 // Relase lock and poll until victims update their masks or timeout
1469 3 shmem_unlock(shm_handler);
1470
1471 3 bool done = false;
1472 struct timespec start, now;
1473 3 get_time_coarse(&start);
1474 do {
1475 // Delay
1476 101 usleep(SYNC_POLL_DELAY);
1477
1478 // Poll
1479 101 shmem_lock(shm_handler);
1480 {
1481 cpu_set_t all_current_masks;
1482 101 CPU_ZERO(&all_current_masks);
1483 101 num_processes = shdata->num_processes;
1484
2/2
✓ Branch 0 taken 303 times.
✓ Branch 1 taken 101 times.
404 for (int p = 0; p < num_processes; ++p) {
1485 303 pinfo_t *victim = &shdata->process_info[p];
1486
3/4
✓ Branch 0 taken 202 times.
✓ Branch 1 taken 101 times.
✓ Branch 2 taken 202 times.
✗ Branch 3 not taken.
303 if (victim != new_owner && victim->pid != NOBODY) {
1487 // Accumulate updated masks of other processes
1488
2/2
✓ Branch 0 taken 3232 times.
✓ Branch 1 taken 202 times.
3434 CPU_OR(&all_current_masks, &all_current_masks,
1489 &victim->current_process_mask);
1490 }
1491 }
1492
1493 // Polling is complete when no current_mask of any process
1494 // contains any CPU from the mask we are stealing
1495 cpu_set_t common_cpus;
1496
2/2
✓ Branch 0 taken 1616 times.
✓ Branch 1 taken 101 times.
1717 CPU_AND(&common_cpus, &all_current_masks, mask);
1497 101 done = CPU_COUNT(&common_cpus) == 0;
1498 }
1499 101 shmem_unlock(shm_handler);
1500
1501 // Check timeout
1502
2/2
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 2 times.
101 if (!done) {
1503 99 get_time_coarse(&now);
1504
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 98 times.
99 if (timespec_diff(&start, &now) > SYNC_POLL_TIMEOUT) {
1505 1 error = DLB_ERR_TIMEOUT;
1506 }
1507 }
1508
4/4
✓ Branch 0 taken 99 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 98 times.
✓ Branch 3 taken 1 times.
101 } while (!done && error == DLB_SUCCESS);
1509
1510 3 shmem_lock(shm_handler);
1511 }
1512
1513
4/4
✓ Branch 0 taken 23 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 12 times.
25 if (!error && !dry_run) {
1514 /* Assign stolen CPUs to the new owner */
1515
2/2
✓ Branch 0 taken 176 times.
✓ Branch 1 taken 11 times.
187 CPU_OR(&new_owner->future_process_mask, &new_owner->future_process_mask, mask);
1516 11 mu_subtract(&new_owner->stolen_cpus, &new_owner->stolen_cpus, mask);
1517 11 new_owner->dirty = true;
1518 }
1519
1520
4/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 23 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
25 if (error && !dry_run) {
1521 // Some CPUs could not be stolen, roll everything back
1522 1 num_processes = shdata->num_processes;
1523
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 1 times.
4 for (int p = 0; p < num_processes; ++p) {
1524 3 pinfo_t *victim = &shdata->process_info[p];
1525
3/4
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
3 if (victim != new_owner && victim->pid != NOBODY) {
1526 cpu_set_t cpus_to_return;
1527
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34 CPU_AND(&cpus_to_return, &victim->stolen_cpus, mask);
1528
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 if (CPU_COUNT(&cpus_to_return) > 0) {
1529 // warning: we may return some CPU to a wrong process
1530 // if more than one contains that CPU as stolen
1531
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34 CPU_OR(&victim->future_process_mask, &victim->future_process_mask,
1532 &cpus_to_return);
1533 2 mu_subtract(&victim->stolen_cpus, &victim->stolen_cpus, &cpus_to_return);
1534 2 victim->dirty = !CPU_EQUAL(
1535 &victim->current_process_mask, &victim->future_process_mask);
1536 }
1537 }
1538 }
1539 }
1540
1541 25 return error;
1542 }
1543
1544
1545 /* Set new mask for process, stealing if necessary
1546 * - If the CPU is SET and unused -> register
1547 * - If the CPU is SET, used and not owned -> steal
1548 * - If the CPU is UNSET and owned by the process -> unregister
1549 */
1550 33 static int set_new_mask(pinfo_t *process, const cpu_set_t *mask, bool sync,
1551 bool return_stolen, cpu_set_t *free_cpu_mask) {
1552 // this function cannot be used if allowing CPU sharing
1553
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if (shdata->flags.allow_cpu_sharing) return DLB_ERR_NOCOMP;
1554
1555 cpu_set_t cpus_to_acquire;
1556 cpu_set_t cpus_to_steal;
1557 cpu_set_t cpus_to_free;
1558 32 CPU_ZERO(&cpus_to_acquire);
1559 32 CPU_ZERO(&cpus_to_steal);
1560 32 CPU_ZERO(&cpus_to_free);
1561
1562 int c;
1563
2/2
✓ Branch 0 taken 204 times.
✓ Branch 1 taken 32 times.
236 for (c = 0; c < max_cpus; c++) {
1564
5/6
✓ Branch 0 taken 204 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 120 times.
✓ Branch 4 taken 84 times.
✓ Branch 5 taken 120 times.
204 if (CPU_ISSET(c, mask)) {
1565
5/6
✓ Branch 0 taken 84 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 66 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 66 times.
84 if (CPU_ISSET(c, &shdata->free_mask)) {
1566 // CPU is not being used
1567
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 CPU_SET(c, &cpus_to_acquire);
1568 } else {
1569
5/6
✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 42 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 24 times.
✓ Branch 5 taken 42 times.
66 if (!CPU_ISSET(c, &process->current_process_mask)) {
1570 // CPU is being used by other process
1571
1/2
✓ Branch 0 taken 24 times.
✗ Branch 1 not taken.
24 CPU_SET(c, &cpus_to_steal);
1572 }
1573 }
1574 } else {
1575
5/6
✓ Branch 0 taken 120 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 11 times.
✓ Branch 3 taken 109 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 109 times.
120 if (CPU_ISSET(c, &process->current_process_mask)) {
1576 // CPU no longer used by this process
1577
1/2
✓ Branch 0 taken 11 times.
✗ Branch 1 not taken.
11 CPU_SET(c, &cpus_to_free);
1578 }
1579 }
1580 }
1581
1582 /* Run first a dry run to check if CPUs can be stolen */
1583 32 int error = steal_mask(process, &cpus_to_steal, sync, /* dry_run */ true);
1584
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1 times.
32 error = error ? error : steal_mask(process, &cpus_to_steal, sync, /* dry_run */ false);
1585
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 error = error ? error : register_mask(process, &cpus_to_acquire);
1586
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 error = error ? error : unregister_mask(process, &cpus_to_free, return_stolen);
1587
4/4
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 23 times.
32 if (error == DLB_SUCCESS && free_cpu_mask != NULL) {
1588 7 memcpy(free_cpu_mask, &cpus_to_free, sizeof(cpu_set_t));
1589 }
1590
1591 32 return error;
1592 }
1593