| 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 |