GCC Code Coverage Report


Directory: src/
File: src/LB_comm/shmem_lewi_async.c
Date: 2025-11-21 10:34:40
Exec Total Coverage
Lines: 301 306 98.4%
Functions: 20 21 95.2%
Branches: 148 176 84.1%

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 #include "LB_comm/shmem_lewi_async.h"
21
22 #include "LB_comm/shmem.h"
23 #include "apis/dlb_errors.h"
24 #include "support/atomic.h"
25 #include "support/debug.h"
26 #include "support/mask_utils.h"
27 #include "support/queues.h"
28 #include "support/tracing.h"
29 #include "support/types.h"
30
31 #include <inttypes.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34
35 typedef struct DLB_ALIGN_CACHE lewi_process_t {
36 pid_t pid;
37 unsigned int initial_ncpus;
38 unsigned int current_ncpus;
39 } lewi_process_t;
40
41 typedef struct lewi_async_shdata {
42 unsigned int idle_cpus;
43 unsigned int attached_nprocs;
44 queue_lewi_reqs_t requests; /* queue of requests */
45 unsigned int max_processes; /* list capacity */
46 unsigned int proc_list_head; /* list upper-bound */
47 lewi_process_t processes[]; /* per-process lewi data */
48 } lewi_async_shdata_t;
49
50 enum { NOBODY = 0 };
51 enum { SHMEM_LEWI_ASYNC_VERSION = 3 };
52
53 static lewi_async_shdata_t *shdata = NULL;
54 static shmem_handler_t *shm_handler = NULL;
55 static const char *shmem_name = "lewi_async";
56 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
57 static int subprocesses_attached = 0;
58 static unsigned int max_processes = 0;
59 static lewi_process_t *my_process = NULL;
60
61
62 static void lend_ncpus_to_shmem(unsigned int ncpus, lewi_request_t *requests,
63 unsigned int *nreqs, unsigned int maxreqs);
64 static int reclaim_from_shmem(lewi_process_t *process, unsigned int ncpus,
65 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs);
66
67 /*********************************************************************************/
68 /* Shared memory management */
69 /*********************************************************************************/
70
71 static void cleanup_shmem(void *shdata_ptr, int pid) {
72 lewi_async_shdata_t *shared_data = shdata_ptr;
73 if (shared_data->attached_nprocs > 0) {
74 --shared_data->attached_nprocs;
75 }
76 }
77
78 18 static bool is_shmem_empty(void) {
79
3/4
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 6 times.
18 return shdata && shdata->attached_nprocs == 0;
80 }
81
82 34 static void close_shmem(void) {
83 34 pthread_mutex_lock(&mutex);
84 {
85
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 16 times.
34 if (--subprocesses_attached == 0) {
86 18 shmem_finalize(shm_handler, is_shmem_empty);
87 18 shm_handler = NULL;
88 18 shdata = NULL;
89 }
90 }
91 34 pthread_mutex_unlock(&mutex);
92 34 }
93
94 312 static lewi_process_t* get_process(pid_t pid) {
95
1/2
✓ Branch 0 taken 312 times.
✗ Branch 1 not taken.
312 if (shdata != NULL) {
96 /* Check first if pid is this process */
97
2/2
✓ Branch 0 taken 295 times.
✓ Branch 1 taken 17 times.
312 if (my_process != NULL
98
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 161 times.
295 && my_process->pid == pid) {
99 134 return my_process;
100 }
101
102 /* Iterate otherwise */
103
2/2
✓ Branch 0 taken 268 times.
✓ Branch 1 taken 5 times.
273 for (unsigned int p = 0; p < shdata->proc_list_head; ++p) {
104
2/2
✓ Branch 0 taken 173 times.
✓ Branch 1 taken 95 times.
268 if (shdata->processes[p].pid == pid) {
105 173 return &shdata->processes[p];
106 }
107 }
108 }
109 5 return NULL;
110 }
111
112 2 bool shmem_lewi_async__exists(void) {
113 2 return shm_handler != NULL;
114 }
115
116 1 int shmem_lewi_async__version(void) {
117 1 return SHMEM_LEWI_ASYNC_VERSION;
118 }
119
120 19 size_t shmem_lewi_async__size(void) {
121 // max_processes contains a value once shmem is initialized,
122 // otherwise return default size
123 19 return sizeof(lewi_async_shdata_t) + sizeof(lewi_process_t) * (
124
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 1 times.
19 max_processes > 0 ? max_processes : (unsigned)mu_get_system_size());
125 }
126
127
128 /*********************************************************************************/
129 /* Queue management */
130 /*********************************************************************************/
131
132 20 void shmem_lewi_async__remove_requests(pid_t pid) {
133
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19 times.
20 if (unlikely(shm_handler == NULL)) return;
134 19 shmem_lock(shm_handler);
135 {
136 19 queue_lewi_reqs_remove(&shdata->requests, pid);
137 }
138 19 shmem_unlock(shm_handler);
139 }
140
141 /* Only for testing purposes */
142 57 unsigned int shmem_lewi_async__get_num_requests(pid_t pid) {
143
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 57 times.
57 if (unlikely(shm_handler == NULL)) return 0;
144 unsigned int num_requests;
145
146 57 shmem_lock(shm_handler);
147 {
148 57 num_requests = queue_lewi_reqs_get(&shdata->requests, pid);
149 }
150 57 shmem_unlock(shm_handler);
151
152 57 return num_requests;
153 }
154
155
156 /*********************************************************************************/
157 /* Init */
158 /*********************************************************************************/
159
160 34 static void open_shmem(const char *shmem_key, int shmem_size_multiplier) {
161 34 pthread_mutex_lock(&mutex);
162 {
163
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 16 times.
34 if (shm_handler == NULL) {
164 18 max_processes = mu_get_system_size() * shmem_size_multiplier;
165 36 shm_handler = shmem_init((void**)&shdata,
166 18 &(const shmem_props_t) {
167 18 .size = shmem_lewi_async__size(),
168 .name = shmem_name,
169 .key = shmem_key,
170 .version = SHMEM_LEWI_ASYNC_VERSION,
171 .cleanup_fn = cleanup_shmem,
172 });
173 18 subprocesses_attached = 1;
174 } else {
175 16 ++subprocesses_attached;
176 }
177 }
178 34 pthread_mutex_unlock(&mutex);
179 34 }
180
181 34 int shmem_lewi_async__init(pid_t pid, unsigned int ncpus,
182 const char *shmem_key, int shmem_size_multiplier) {
183
184 34 int error = DLB_SUCCESS;
185
186
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 34 times.
34 verbose(VB_SHMEM, "Initializing LeWI_async shared memory");
187
188 // Shared memory creation
189 34 open_shmem(shmem_key, shmem_size_multiplier);
190
191 34 shmem_lock(shm_handler);
192 {
193
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 22 times.
34 if (++shdata->attached_nprocs == 1) {
194 // first attached process, initialize common structures
195 12 shdata->max_processes = max_processes;
196 12 shdata->idle_cpus = 0;
197 12 queue_lewi_reqs_init(&shdata->requests);
198 } else {
199
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if (shdata->max_processes != max_processes) {
200 1 error = DLB_ERR_INIT;
201 }
202 }
203
204
2/2
✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1 times.
34 if (error == DLB_SUCCESS) {
205 // Iterate the processes array to find a free spot
206 33 lewi_process_t *process = NULL;
207
2/2
✓ Branch 0 taken 174 times.
✓ Branch 1 taken 5 times.
179 for (unsigned int p = 0; p < max_processes; ++p) {
208
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 146 times.
174 if (shdata->processes[p].pid == NOBODY) {
209 28 process = &shdata->processes[p];
210 /* save the highest upper bound to iterate faster */
211 28 shdata->proc_list_head = max_int(shdata->proc_list_head, p+1);
212 28 break;
213 }
214 }
215
216 // Assign this process initial data
217
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 5 times.
33 if (process != NULL) {
218 28 *process = (const lewi_process_t) {
219 .pid = pid,
220 .initial_ncpus = ncpus,
221 .current_ncpus = ncpus,
222 };
223 28 my_process = process;
224 } else {
225 5 error = DLB_ERR_NOMEM;
226 }
227 }
228
229
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
34 if (error < DLB_SUCCESS) {
230 6 --shdata->attached_nprocs;
231 }
232 }
233 34 shmem_unlock(shm_handler);
234
235
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 33 times.
34 if (error == DLB_ERR_INIT) {
236 1 warning("Cannot attach to LeWI async shmem because existing size differ."
237 " Existing shmem size: %d, expected: %d."
238 " Check for DLB_ARGS consistency among processes or clean up shared memory.",
239 1 shdata->max_processes, max_processes);
240 }
241
242
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
34 if (error < DLB_SUCCESS) {
243 // The shared memory contents are untouched, but the counter needs to
244 // be decremented, and the shared memory deleted if needed
245 6 close_shmem();
246 }
247
248 34 return error;
249 }
250
251
252 /*********************************************************************************/
253 /* Finalize */
254 /*********************************************************************************/
255
256 34 static int reset_process(lewi_process_t *process, lewi_request_t *requests,
257 unsigned int *nreqs, unsigned int maxreqs, unsigned int *prev_requested) {
258
259 34 *nreqs = 0;
260 34 int error = DLB_NOUPDT;
261
262 // Clear requests
263 34 *prev_requested = queue_lewi_reqs_remove(&shdata->requests, process->pid);
264
265 // Lend excess CPUs
266
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 31 times.
34 if (process->current_ncpus > process->initial_ncpus) {
267
268 /* Compute CPUs to lend and update process info */
269 3 unsigned int ncpus_to_lend =
270 3 process->current_ncpus - process->initial_ncpus;
271 3 process->current_ncpus = process->initial_ncpus;
272
273 /* Update output variable. Excess CPUs count as previously requested */
274 3 *prev_requested += ncpus_to_lend;
275
276 /* Update shmem, resolve requests if possible */
277 3 lend_ncpus_to_shmem(ncpus_to_lend, requests, nreqs, maxreqs);
278
279 3 error = DLB_SUCCESS;
280 }
281
282 // Borrow or Reclaim
283
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
31 else if (process->current_ncpus < process->initial_ncpus) {
284
285 7 unsigned int ncpus_to_reclaim =
286 7 process->initial_ncpus - process->current_ncpus;
287
288 // Borrow first
289 7 unsigned int ncpus_to_borrow = min_uint(shdata->idle_cpus,
290 ncpus_to_reclaim);
291
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if (ncpus_to_borrow > 0) {
292 4 shdata->idle_cpus -= ncpus_to_borrow;
293 4 process->current_ncpus += ncpus_to_borrow;
294 4 ncpus_to_reclaim -= ncpus_to_borrow;
295 4 error = DLB_SUCCESS;
296 }
297
298 // Reclaim later
299
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 3 times.
7 if (ncpus_to_reclaim > 0) {
300 4 error = reclaim_from_shmem(process, ncpus_to_reclaim, requests,
301 nreqs, maxreqs);
302 }
303 }
304
305 34 return error;
306 }
307
308 28 void shmem_lewi_async__finalize(pid_t pid, unsigned int *new_ncpus,
309 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs) {
310
311 28 *nreqs = 0;
312
313
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (shm_handler == NULL) return;
314
315 28 lewi_process_t *process = get_process(pid);
316
317 28 shmem_lock(shm_handler);
318 {
319
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 if (process != NULL) {
320 28 *new_ncpus = process->initial_ncpus;
321
322 // Resolve requests and CPUs out of place, ignore previously requested
323 unsigned int prev_requested;
324 28 reset_process(process, requests, nreqs, maxreqs, &prev_requested);
325
326 // Remove process data
327 28 *process = (const lewi_process_t) {};
328
329 // Clear local pointer
330 28 my_process = NULL;
331
332 // Decrement process counter
333 28 --shdata->attached_nprocs;
334 }
335 }
336 28 shmem_unlock(shm_handler);
337
338 28 close_shmem();
339 }
340
341
342 /*********************************************************************************/
343 /* Lend */
344 /*********************************************************************************/
345
346 /* Lend ncpus. Check whether they can resolve some request, add to shmem otherwise */
347 70 static void lend_ncpus_to_shmem(unsigned int ncpus, lewi_request_t *requests,
348 unsigned int *nreqs, unsigned int maxreqs) {
349
350
2/2
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 45 times.
70 if (queue_lewi_reqs_size(&shdata->requests) == 0) {
351 /* queue is empty */
352 25 shdata->idle_cpus += ncpus;
353 25 *nreqs = 0;
354 } else {
355
356 /* Resolve as many requests as possible, the remainder goes to the shmem */
357 45 unsigned int not_needed_cpus = queue_lewi_reqs_pop_ncpus(&shdata->requests,
358 ncpus, requests, nreqs, maxreqs);
359 45 shdata->idle_cpus += not_needed_cpus;
360
361 /* Update shmem with the resolved requests, and add current values */
362
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 45 times.
99 for (unsigned int i = 0; i < *nreqs; ++i) {
363 54 lewi_process_t *target = get_process(requests[i].pid);
364 54 target->current_ncpus += requests[i].howmany;
365 /* the request is updated to call the appropriate set_num_threads */
366 54 requests[i].howmany = target->current_ncpus;
367 }
368 }
369 70 }
370
371 /* Lend ncpus. Return new_ncpus and pending CPU requests. */
372 42 int shmem_lewi_async__lend_cpus(pid_t pid, unsigned int ncpus,
373 unsigned int *new_ncpus, lewi_request_t *requests, unsigned int *nreqs,
374 unsigned int maxreqs, unsigned int *prev_requested) {
375
376
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 41 times.
42 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
377
378 41 lewi_process_t *process = get_process(pid);
379
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 40 times.
41 if (process == NULL) return DLB_ERR_NOPROC;
380
381 40 *new_ncpus = 0;
382 40 *nreqs = 0;
383 40 *prev_requested = 0;
384
385
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 39 times.
40 if (unlikely(ncpus == 0)) return DLB_NOUPDT;
386
387 int error;
388 unsigned int idle_cpus;
389 39 shmem_lock(shm_handler);
390 {
391 /* Not enough CPUs to lend */
392
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 38 times.
39 if (ncpus > process->current_ncpus) {
393 1 error = DLB_ERR_PERM;
394 }
395 /* Lend */
396 else {
397 /* CPUs previously requested is the sum of the previous petitions
398 * in the queue (which are removed at this point) and the excess of
399 * CPUs that the process lends but does not own */
400 38 unsigned int ncpus_lent_not_owned =
401 38 process->current_ncpus > process->initial_ncpus
402 1 ? min_uint(process->current_ncpus - process->initial_ncpus, ncpus)
403
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 37 times.
38 : 0;
404 38 unsigned int ncpus_in_queue = queue_lewi_reqs_remove(&shdata->requests, pid);
405 38 *prev_requested = ncpus_lent_not_owned + ncpus_in_queue;
406
407 /* Update process info and output variable */
408 38 process->current_ncpus -= ncpus;
409 38 *new_ncpus = process->current_ncpus;
410
411 /* Update shmem, resolve requests if possible */
412 38 lend_ncpus_to_shmem(ncpus, requests, nreqs, maxreqs);
413
414 38 error = DLB_SUCCESS;
415 }
416
417 39 idle_cpus = shdata->idle_cpus;
418 }
419 39 shmem_unlock(shm_handler);
420
421
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 1 times.
39 if (error == DLB_SUCCESS) {
422 add_event(IDLE_CPUS_EVENT, idle_cpus);
423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38 times.
38 verbose(VB_SHMEM, "Lending %u CPUs (idle: %u, triggered requests: %u)",
424 ncpus, idle_cpus, *nreqs);
425 } else {
426
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 verbose(VB_SHMEM, "Lend failed");
427 }
428
429 39 return error;
430 }
431
432 /* Lend all possible CPUs, keep new_ncpus. Return pending CPU requests. */
433 33 int shmem_lewi_async__lend_keep_cpus(pid_t pid, unsigned int new_ncpus,
434 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs,
435 unsigned int *prev_requested) {
436
437
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
438
439 32 lewi_process_t *process = get_process(pid);
440
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 31 times.
32 if (process == NULL) return DLB_ERR_NOPROC;
441
442 31 *nreqs = 0;
443 31 *prev_requested = 0;
444
445 int error;
446 unsigned int idle_cpus;
447 unsigned int lent_cpus;
448 31 shmem_lock(shm_handler);
449 {
450 /* Not enough CPUs to lend */
451
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30 times.
31 if (new_ncpus > process->current_ncpus) {
452 1 error = DLB_ERR_PERM;
453 }
454
455 /* No-op */
456
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
30 else if (new_ncpus == process->current_ncpus) {
457 1 error = DLB_NOUPDT;
458 }
459
460 /* Lend */
461 else {
462 /* Compute CPUs to lend */
463 29 lent_cpus = process->current_ncpus - new_ncpus;
464
465 /* CPUs previously requested is the sum of the previous petitions
466 * in the queue (which are removed at this point) and the excess of
467 * CPUs that the process lends but does not own */
468 29 unsigned int ncpus_lent_not_owned =
469 29 process->current_ncpus > process->initial_ncpus
470 10 ? min_uint(process->current_ncpus - process->initial_ncpus, lent_cpus)
471
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 19 times.
29 : 0;
472 29 unsigned int ncpus_in_queue = queue_lewi_reqs_remove(&shdata->requests, pid);
473 29 *prev_requested = ncpus_lent_not_owned + ncpus_in_queue;
474
475 /* Compute CPUs to lend and update process info */
476 29 lent_cpus = process->current_ncpus - new_ncpus;
477 29 process->current_ncpus = new_ncpus;
478
479 /* Update shmem, resolve requests if possible */
480 29 lend_ncpus_to_shmem(lent_cpus, requests, nreqs, maxreqs);
481
482 29 error = DLB_SUCCESS;
483 }
484
485 31 idle_cpus = shdata->idle_cpus;
486 }
487 31 shmem_unlock(shm_handler);
488
489
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 2 times.
31 if (error == DLB_SUCCESS) {
490 add_event(IDLE_CPUS_EVENT, idle_cpus);
491
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 29 times.
29 verbose(VB_SHMEM, "Lending %u CPUs (idle: %u, triggered requests: %u)",
492 lent_cpus, idle_cpus, *nreqs);
493 } else {
494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 verbose(VB_SHMEM, "Lend failed");
495 }
496
497 31 return error;
498 }
499
500
501 /*********************************************************************************/
502 /* Reclaim */
503 /*********************************************************************************/
504
505 /* Helper function to reclaim all CPUs from a process */
506 48 static int reclaim_from_shmem(lewi_process_t *process, unsigned int ncpus,
507 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs) {
508
509 /* These conditions are actually checked before invoking the function */
510
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 ensure(process != NULL, "illegal process in %s", __func__);
511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 ensure(process->initial_ncpus >= process->current_ncpus + ncpus,
512 "cannot reclaim in %s", __func__);
513
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 ensure(shdata->idle_cpus == 0, "reclaiming while idle CPUs > 0");
514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 ensure(ncpus > 0, "Reclaiming 0 CPUs");
515
516 48 int error = DLB_SUCCESS;
517
518 // find victims to steal CPUs from
519
520 /* Construct a queue with the CPU surplus of each target process */
521 48 queue_lewi_reqs_t surplus = {};
522
2/2
✓ Branch 0 taken 138 times.
✓ Branch 1 taken 48 times.
186 for (unsigned int p = 0; p < shdata->proc_list_head; ++p) {
523 138 lewi_process_t *target = &shdata->processes[p];
524
1/2
✓ Branch 0 taken 138 times.
✗ Branch 1 not taken.
138 if (target->pid != NOBODY
525
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 85 times.
138 && target->current_ncpus > target->initial_ncpus) {
526 53 queue_lewi_reqs_push(&surplus, target->pid,
527 53 target->current_ncpus - target->initial_ncpus);
528 }
529 }
530
531 /* Pop CPUs evenly */
532 48 unsigned int remaining_ncpus = queue_lewi_reqs_pop_ncpus(&surplus, ncpus,
533 requests, nreqs, maxreqs);
534
1/2
✓ Branch 0 taken 48 times.
✗ Branch 1 not taken.
48 if (remaining_ncpus == 0) {
535 /* Update shmem with the victims, subtract current values */
536
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 48 times.
101 for (unsigned int i = 0; i < *nreqs; ++i) {
537 53 lewi_process_t *target = get_process(requests[i].pid);
538 53 target->current_ncpus -= requests[i].howmany;
539
540 /* Add requests for reclaimed CPUs */
541 53 queue_lewi_reqs_push(&shdata->requests, requests[i].pid,
542 53 requests[i].howmany);
543
544 /* the request is updated to call the appropriate set_num_threads */
545 53 requests[i].howmany = target->current_ncpus;
546 }
547
548 48 process->current_ncpus += ncpus;
549 } else {
550 /* This should be either an error or an impossibility to fill
551 * the requests array, due to a low capacity. */
552 error = DLB_ERR_REQST;
553 }
554
555 48 return error;
556 }
557
558 /* Reclaim initial number of CPUs */
559 32 int shmem_lewi_async__reclaim(pid_t pid, unsigned int *new_ncpus,
560 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs,
561 unsigned int prev_requested) {
562
563
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 31 times.
32 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
564
565 31 lewi_process_t *process = get_process(pid);
566
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 30 times.
31 if (process == NULL) return DLB_ERR_NOPROC;
567
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 verbose(VB_SHMEM, "Reclaiming initial CPUs...");
569
570 30 *nreqs = 0;
571
572 30 int error = DLB_NOUPDT;
573 unsigned int idle_cpus;
574 30 shmem_lock(shm_handler);
575 {
576
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
30 if (process->current_ncpus < process->initial_ncpus) {
577
578 28 unsigned int ncpus_to_reclaim =
579 28 process->initial_ncpus - process->current_ncpus;
580
581 // Borrow first
582 28 unsigned int ncpus_to_borrow = min_uint(shdata->idle_cpus,
583 ncpus_to_reclaim);
584
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 16 times.
28 if (ncpus_to_borrow > 0) {
585 12 shdata->idle_cpus -= ncpus_to_borrow;
586 12 process->current_ncpus += ncpus_to_borrow;
587 12 ncpus_to_reclaim -= ncpus_to_borrow;
588 12 error = DLB_SUCCESS;
589 }
590
591 // Reclaim later
592
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 11 times.
28 if (ncpus_to_reclaim > 0) {
593 17 error = reclaim_from_shmem(process, ncpus_to_reclaim, requests,
594 nreqs, maxreqs);
595 }
596
597 // Attend previous requests
598
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 15 times.
28 if (prev_requested > 0) {
599 // Try first to resolve them with the available CPUs
600 unsigned int ncpus_from_prev_requests =
601 13 min_uint(shdata->idle_cpus, prev_requested);
602
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 9 times.
13 if (ncpus_from_prev_requests > 0) {
603 4 shdata->idle_cpus -= ncpus_from_prev_requests;
604 4 process->current_ncpus += ncpus_from_prev_requests;
605 4 prev_requested -= ncpus_from_prev_requests;
606 }
607
608 // If we still have previous requests, add them to the queue
609
1/2
✓ Branch 0 taken 13 times.
✗ Branch 1 not taken.
13 if (prev_requested > 0) {
610 13 queue_lewi_reqs_push(&shdata->requests, pid, prev_requested);
611 }
612 }
613 }
614
615 // Update output variable
616 30 idle_cpus = shdata->idle_cpus;
617 30 *new_ncpus = process->current_ncpus;
618 }
619 30 shmem_unlock(shm_handler);
620
621
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 2 times.
30 if (error == DLB_SUCCESS) {
622 add_event(IDLE_CPUS_EVENT, idle_cpus);
623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 verbose(VB_SHMEM, "Using %u CPUs... Idle: %u", *new_ncpus, idle_cpus);
624 }
625
626 30 return error;
627 }
628
629
630 /*********************************************************************************/
631 /* Acquire */
632 /*********************************************************************************/
633
634 59 int shmem_lewi_async__acquire_cpus(pid_t pid, unsigned int ncpus,
635 unsigned int *new_ncpus, lewi_request_t *requests, unsigned int *nreqs,
636 unsigned int maxreqs) {
637
638
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 58 times.
59 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
639
640
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 57 times.
58 if (ncpus == 0) return DLB_NOUPDT;
641
642 57 lewi_process_t *process = get_process(pid);
643
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 56 times.
57 if (process == NULL) return DLB_ERR_NOPROC;
644
645 56 *nreqs = 0;
646
647 56 int error = DLB_NOUPDT;
648 int idle_cpus;
649 56 shmem_lock(shm_handler);
650 {
651 // Borrow first
652 56 unsigned int ncpus_to_borrow = min_uint(shdata->idle_cpus, ncpus);
653
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 45 times.
56 if (ncpus_to_borrow > 0) {
654 11 shdata->idle_cpus -= ncpus_to_borrow;
655 11 process->current_ncpus += ncpus_to_borrow;
656 11 ncpus -= ncpus_to_borrow;
657 11 error = DLB_SUCCESS;
658 }
659
660 // Reclaim later
661 56 unsigned int ncpus_to_reclaim = min_uint(ncpus,
662
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 process->initial_ncpus > process->current_ncpus ?
663 28 process->initial_ncpus - process->current_ncpus : 0);
664
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 29 times.
56 if (ncpus_to_reclaim > 0) {
665 27 error = reclaim_from_shmem(process, ncpus_to_reclaim, requests,
666 nreqs, maxreqs);
667
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if (error == DLB_SUCCESS) {
668 27 ncpus -= ncpus_to_reclaim;
669 }
670 }
671
672 // Add request for the rest
673
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 30 times.
56 if (ncpus > 0) {
674 26 queue_lewi_reqs_push(&shdata->requests, pid, ncpus);
675 26 error = DLB_NOTED;
676 }
677
678 56 *new_ncpus = process->current_ncpus;
679 56 idle_cpus = shdata->idle_cpus;
680 }
681 56 shmem_unlock(shm_handler);
682
683
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 26 times.
56 if (error == DLB_SUCCESS) {
684 add_event(IDLE_CPUS_EVENT, idle_cpus);
685
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 verbose(VB_SHMEM, "Borrowing CPUs... New: %d, Idle: %d", *new_ncpus, idle_cpus);
686 }
687
688 56 return error;
689 }
690
691
692 /*********************************************************************************/
693 /* Borrow */
694 /*********************************************************************************/
695
696 7 int shmem_lewi_async__borrow_cpus(pid_t pid, unsigned int ncpus,
697 unsigned int *new_ncpus) {
698
699
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
7 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
700
701 6 lewi_process_t *process = get_process(pid);
702
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5 times.
6 if (process == NULL) return DLB_ERR_NOPROC;
703
704 int error;
705 int idle_cpus;
706
707 5 shmem_lock(shm_handler);
708 {
709 // Borrow as many as possible
710 5 unsigned int ncpus_to_borrow = min_uint(shdata->idle_cpus, ncpus);
711
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (ncpus_to_borrow > 0) {
712 3 shdata->idle_cpus -= ncpus_to_borrow;
713 3 process->current_ncpus += ncpus_to_borrow;
714 3 error = DLB_SUCCESS;
715 } else {
716 // No idle CPUs to borrow
717 2 error = DLB_NOUPDT;
718 }
719
720 5 *new_ncpus = process->current_ncpus;
721 5 idle_cpus = shdata->idle_cpus;
722 }
723 5 shmem_unlock(shm_handler);
724
725
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
5 if (error == DLB_SUCCESS) {
726 add_event(IDLE_CPUS_EVENT, idle_cpus);
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 verbose(VB_SHMEM, "Borrowing CPUs... New: %d, Idle: %d", *new_ncpus, idle_cpus);
728 }
729
730 5 return error;
731 }
732
733
734 /*********************************************************************************/
735 /* Reset (Lend or Reclaim) */
736 /*********************************************************************************/
737
738 10 int shmem_lewi_async__reset(pid_t pid, unsigned int *new_ncpus,
739 lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs,
740 unsigned int *prev_requested) {
741
742
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (unlikely(shm_handler == NULL)) return DLB_ERR_NOSHMEM;
743
744 10 lewi_process_t *process = get_process(pid);
745
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 if (process == NULL) return DLB_ERR_NOPROC;
746
747
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 verbose(VB_SHMEM, "Resetting");
748
749 10 *nreqs = 0;
750 10 *prev_requested = 0;
751
752 10 int error = DLB_NOUPDT;
753 unsigned int idle_cpus;
754 10 shmem_lock(shm_handler);
755 {
756
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 if (process->initial_ncpus != process->current_ncpus) {
757 6 error = reset_process(process, requests, nreqs, maxreqs, prev_requested);
758 }
759
760 10 idle_cpus = shdata->idle_cpus;
761 10 *new_ncpus = process->current_ncpus;
762 }
763 10 shmem_unlock(shm_handler);
764
765
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
10 if (error == DLB_SUCCESS) {
766 add_event(IDLE_CPUS_EVENT, idle_cpus);
767
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 verbose(VB_SHMEM, "Using %u CPUs... Idle: %u", *new_ncpus, idle_cpus);
768 }
769
770 10 return error;
771 }
772