| 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_policies/lewi_mask.h" | ||
| 21 | |||
| 22 | #include "LB_core/spd.h" | ||
| 23 | #include "LB_comm/shmem_cpuinfo.h" | ||
| 24 | #include "LB_comm/shmem_async.h" | ||
| 25 | #include "apis/dlb_errors.h" | ||
| 26 | #include "support/debug.h" | ||
| 27 | #include "support/mask_utils.h" | ||
| 28 | #include "support/small_array.h" | ||
| 29 | #include "support/types.h" | ||
| 30 | |||
| 31 | #include <sched.h> | ||
| 32 | #include <stdlib.h> | ||
| 33 | #include <string.h> | ||
| 34 | #include <pthread.h> | ||
| 35 | |||
| 36 | /* array_cpuid_t */ | ||
| 37 | #define ARRAY_T cpuid_t | ||
| 38 | #include "support/array_template.h" | ||
| 39 | |||
| 40 | /* array_cpuinfo_task_t */ | ||
| 41 | #define ARRAY_T cpuinfo_task_t | ||
| 42 | #define ARRAY_KEY_T pid_t | ||
| 43 | #include "support/array_template.h" | ||
| 44 | |||
| 45 | |||
| 46 | /* Node size will be the same for all processes in the node, | ||
| 47 | * it is safe to be out of the shared memory */ | ||
| 48 | static int node_size = -1; | ||
| 49 | |||
| 50 | /* LeWI_mask data is private for each process */ | ||
| 51 | typedef struct LeWI_mask_info { | ||
| 52 | int64_t last_borrow; | ||
| 53 | int max_parallelism; | ||
| 54 | array_cpuid_t cpus_priority_array; | ||
| 55 | cpu_set_t pending_reclaimed_cpus; /* CPUs that become reclaimed after an MPI */ | ||
| 56 | cpu_set_t in_mpi_cpus; /* CPUs inside an MPI call */ | ||
| 57 | pthread_mutex_t mutex; /* Mutex to protect lewi_info */ | ||
| 58 | } lewi_info_t; | ||
| 59 | |||
| 60 | |||
| 61 | /* Compute the common elements between a cpuid array a cpu_set: | ||
| 62 | * cpuid_t *result = cpuid_t *op1 AND cpu_set_t *op2 | ||
| 63 | * (cpu_set_t* may be NULL, meaning neutral value) | ||
| 64 | */ | ||
| 65 | 123 | static inline void cpu_array_and(array_cpuid_t *result, | |
| 66 | const array_cpuid_t *op1, const cpu_set_t *op2) { | ||
| 67 |
2/2✓ Branch 0 taken 984 times.
✓ Branch 1 taken 123 times.
|
1107 | for (unsigned int i = 0; i < op1->count; ++i) { |
| 68 |
7/8✓ Branch 0 taken 240 times.
✓ Branch 1 taken 744 times.
✓ Branch 2 taken 240 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 158 times.
✓ Branch 5 taken 82 times.
✓ Branch 6 taken 158 times.
✓ Branch 7 taken 82 times.
|
984 | if (op2 == NULL || CPU_ISSET(op1->items[i], op2)) { |
| 69 | 902 | array_cpuid_t_push(result, op1->items[i]); | |
| 70 | } | ||
| 71 | } | ||
| 72 | 123 | } | |
| 73 | |||
| 74 | /* Construct a cpuid array from a cpu_set_t */ | ||
| 75 | 12 | static inline void cpu_array_from_cpuset(array_cpuid_t *result, | |
| 76 | const cpu_set_t *cpu_set) { | ||
| 77 | 12 | for (int cpuid = mu_get_first_cpu(cpu_set); | |
| 78 |
3/4✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 24 times.
✗ Branch 3 not taken.
|
36 | cpuid >= 0 && cpuid != DLB_CPUID_INVALID; |
| 79 | 24 | cpuid = mu_get_next_cpu(cpu_set, cpuid)) { | |
| 80 | 24 | array_cpuid_t_push(result, cpuid); | |
| 81 | } | ||
| 82 | 12 | } | |
| 83 | |||
| 84 | |||
| 85 | /* Construct a priority list of CPUs considering the topology and the affinity mask */ | ||
| 86 | 40 | static void lewi_mask_UpdateOwnershipInfo(const subprocess_descriptor_t *spd, | |
| 87 | const cpu_set_t *process_mask) { | ||
| 88 | |||
| 89 | 40 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 90 | 40 | lewi_affinity_t lewi_affinity = spd->options.lewi_affinity; | |
| 91 | |||
| 92 | cpu_set_t affinity_mask; | ||
| 93 | 40 | mu_get_nodes_intersecting_with_cpuset(&affinity_mask, process_mask); | |
| 94 | |||
| 95 | /* Reset current priority array */ | ||
| 96 | 40 | array_cpuid_t *cpus_priority_array = &lewi_info->cpus_priority_array; | |
| 97 | 40 | array_cpuid_t_clear(cpus_priority_array); | |
| 98 | |||
| 99 | /* First, construct a list of potentially available CPUs | ||
| 100 | * (owned + nearby, depending on the affinity policy) */ | ||
| 101 | cpu_set_t system_mask; | ||
| 102 | 40 | mu_get_system_mask(&system_mask); | |
| 103 | 40 | for (int cpuid = mu_get_first_cpu(&system_mask); | |
| 104 |
3/4✓ Branch 0 taken 584 times.
✓ Branch 1 taken 40 times.
✓ Branch 2 taken 584 times.
✗ Branch 3 not taken.
|
624 | cpuid >= 0 && cpuid != DLB_CPUID_INVALID; |
| 105 | 584 | cpuid = mu_get_next_cpu(&system_mask, cpuid)) { | |
| 106 |
5/6✓ Branch 0 taken 584 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 192 times.
✓ Branch 3 taken 392 times.
✓ Branch 4 taken 192 times.
✓ Branch 5 taken 392 times.
|
584 | if (CPU_ISSET(cpuid, process_mask)) { |
| 107 | 192 | array_cpuid_t_push(cpus_priority_array, cpuid); | |
| 108 | } else { | ||
| 109 |
1/5✓ Branch 0 taken 392 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
392 | switch (lewi_affinity) { |
| 110 | 392 | case LEWI_AFFINITY_AUTO: | |
| 111 | case LEWI_AFFINITY_MASK: | ||
| 112 | case LEWI_AFFINITY_NEARBY_FIRST: | ||
| 113 | 392 | array_cpuid_t_push(cpus_priority_array, cpuid); | |
| 114 | 392 | break; | |
| 115 | ✗ | case LEWI_AFFINITY_NEARBY_ONLY: | |
| 116 | ✗ | if (CPU_ISSET(cpuid, &affinity_mask)) { | |
| 117 | ✗ | array_cpuid_t_push(cpus_priority_array, cpuid); | |
| 118 | } | ||
| 119 | ✗ | break; | |
| 120 | ✗ | case LEWI_AFFINITY_SPREAD_IFEMPTY: | |
| 121 | // This case cannot be pre-computed | ||
| 122 | ✗ | break; | |
| 123 | ✗ | case LEWI_AFFINITY_NONE: | |
| 124 | /* LEWI_AFFINITY_NONE should force LeWI without mask support */ | ||
| 125 | ✗ | fatal("Unhandled LEWI_AFFINITY_NONE in LeWI mask. " | |
| 126 | "Please report bug"); | ||
| 127 | } | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /* Sort available CPUs according to the affinity */ | ||
| 132 | cpu_set_t affinity[3]; | ||
| 133 | 40 | memcpy(&affinity[0], process_mask, sizeof(cpu_set_t)); | |
| 134 | 40 | memcpy(&affinity[1], &affinity_mask, sizeof(cpu_set_t)); | |
| 135 | 40 | CPU_ZERO(&affinity[2]); | |
| 136 | 40 | qsort_r(cpus_priority_array->items, cpus_priority_array->count, | |
| 137 | sizeof(cpuid_t), mu_cmp_cpuids_by_affinity, &affinity); | ||
| 138 | 40 | } | |
| 139 | |||
| 140 | |||
| 141 | /*********************************************************************************/ | ||
| 142 | /* Thread-local pointers for temporary storage during LeWI calls */ | ||
| 143 | /*********************************************************************************/ | ||
| 144 | |||
| 145 | /* Array of eligible CPUs. Usually a subset of the CPUs priority array. */ | ||
| 146 | static __thread array_cpuid_t *_cpu_subset = NULL; | ||
| 147 | |||
| 148 | static pthread_key_t cpu_subset_key; | ||
| 149 | static pthread_once_t cpu_subset_once = PTHREAD_ONCE_INIT; | ||
| 150 | |||
| 151 | 17 | static void cpu_subset_destructor(void *p) { | |
| 152 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | ensure(p == _cpu_subset, "pthread key does not match _cpu_subset"); |
| 153 | 17 | array_cpuid_t_destroy(_cpu_subset); | |
| 154 | 17 | free(_cpu_subset); | |
| 155 | 17 | _cpu_subset = NULL; | |
| 156 | 17 | } | |
| 157 | |||
| 158 | 10 | static void make_cpu_subset_key(void) { | |
| 159 | /* Associate key with destructor, all worker threads will call it on pthread_exit */ | ||
| 160 | 10 | pthread_key_create(&cpu_subset_key, cpu_subset_destructor); | |
| 161 | 10 | } | |
| 162 | |||
| 163 | 135 | static inline array_cpuid_t* get_cpu_subset(const subprocess_descriptor_t *spd) { | |
| 164 | /* Thread already has an allocated array, return it */ | ||
| 165 |
2/2✓ Branch 0 taken 118 times.
✓ Branch 1 taken 17 times.
|
135 | if (likely(_cpu_subset != NULL)) { |
| 166 | 118 | array_cpuid_t_clear(_cpu_subset); | |
| 167 | 118 | return _cpu_subset; | |
| 168 | } | ||
| 169 | |||
| 170 | /* Otherwise, allocate */ | ||
| 171 | 17 | _cpu_subset = malloc(sizeof(array_cpuid_t)); | |
| 172 | 17 | array_cpuid_t_init(_cpu_subset, node_size); | |
| 173 | |||
| 174 | /* Associate pointer to key to clean up array on thread destruction */ | ||
| 175 | 17 | pthread_once(&cpu_subset_once, make_cpu_subset_key); | |
| 176 | 17 | pthread_setspecific(cpu_subset_key, _cpu_subset); | |
| 177 | |||
| 178 | 17 | return _cpu_subset; | |
| 179 | } | ||
| 180 | |||
| 181 | /* Array of cpuinfo tasks. For enabling or disabling CPUs. */ | ||
| 182 | static __thread array_cpuinfo_task_t *_tasks = NULL; | ||
| 183 | |||
| 184 | static pthread_key_t tasks_key; | ||
| 185 | static pthread_once_t tasks_once = PTHREAD_ONCE_INIT; | ||
| 186 | |||
| 187 | 40 | static void tasks_destructor(void *p) { | |
| 188 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
|
40 | ensure(p == _tasks, "pthread key does not match _tasks"); |
| 189 | 40 | array_cpuinfo_task_t_destroy(_tasks); | |
| 190 | 40 | free(_tasks); | |
| 191 | 40 | _tasks = NULL; | |
| 192 | 40 | } | |
| 193 | |||
| 194 | 15 | static void make_tasks_key(void) { | |
| 195 | /* Associate key with destructor, all worker threads will call it on pthread_exit */ | ||
| 196 | 15 | pthread_key_create(&tasks_key, tasks_destructor); | |
| 197 | 15 | } | |
| 198 | |||
| 199 | 397 | static inline array_cpuinfo_task_t* get_tasks(const subprocess_descriptor_t *spd) { | |
| 200 | /* Thread already has an allocated array, return it */ | ||
| 201 |
2/2✓ Branch 0 taken 357 times.
✓ Branch 1 taken 40 times.
|
397 | if (likely(_tasks != NULL)) { |
| 202 | 357 | array_cpuinfo_task_t_clear(_tasks); | |
| 203 | 357 | return _tasks; | |
| 204 | } | ||
| 205 | |||
| 206 | /* Otherwise, allocate */ | ||
| 207 | 40 | _tasks = malloc(sizeof(array_cpuinfo_task_t)); | |
| 208 | 40 | array_cpuinfo_task_t_init(_tasks, node_size*2); | |
| 209 | |||
| 210 | /* Associate pointer to key to clean up structure on thread destruction */ | ||
| 211 | 40 | pthread_once(&tasks_once, make_tasks_key); | |
| 212 | 40 | pthread_setspecific(tasks_key, _tasks); | |
| 213 | |||
| 214 | 40 | return _tasks; | |
| 215 | } | ||
| 216 | |||
| 217 | |||
| 218 | /*********************************************************************************/ | ||
| 219 | /* Resolve cpuinfo tasks */ | ||
| 220 | /*********************************************************************************/ | ||
| 221 | |||
| 222 | 290 | static void resolve_cpuinfo_tasks(const subprocess_descriptor_t *restrict spd, | |
| 223 | array_cpuinfo_task_t *restrict tasks) { | ||
| 224 | |||
| 225 | 290 | size_t tasks_count = tasks->count; | |
| 226 | |||
| 227 | /* We don't need it strictly sorted, but if there are 3 or more tasks | ||
| 228 | * we need to group them by pid */ | ||
| 229 |
2/2✓ Branch 0 taken 31 times.
✓ Branch 1 taken 259 times.
|
290 | if (tasks_count > 2 ) { |
| 230 | 31 | array_cpuinfo_task_t_sort(tasks); | |
| 231 | } | ||
| 232 | |||
| 233 | /* Iterate tasks by group of PIDs */ | ||
| 234 |
2/2✓ Branch 0 taken 210 times.
✓ Branch 1 taken 290 times.
|
500 | for (size_t i=0; i<tasks_count; ) { |
| 235 | |||
| 236 | /* count how many times this PID is repeated */ | ||
| 237 | 210 | size_t j = i+1; | |
| 238 | 210 | while (j < tasks_count | |
| 239 |
4/4✓ Branch 0 taken 148 times.
✓ Branch 1 taken 178 times.
✓ Branch 2 taken 116 times.
✓ Branch 3 taken 32 times.
|
326 | && tasks->items[j].pid == tasks->items[i].pid) { |
| 240 | 116 | ++j; | |
| 241 | } | ||
| 242 | 210 | size_t num_tasks = j - i; | |
| 243 | |||
| 244 |
2/2✓ Branch 0 taken 139 times.
✓ Branch 1 taken 71 times.
|
210 | if (num_tasks == 1) { |
| 245 | /* resolve single task */ | ||
| 246 | 139 | const cpuinfo_task_t *task = &tasks->items[i]; | |
| 247 |
2/2✓ Branch 0 taken 115 times.
✓ Branch 1 taken 24 times.
|
139 | if (task->pid == spd->id) { |
| 248 |
2/2✓ Branch 0 taken 98 times.
✓ Branch 1 taken 17 times.
|
115 | if (task->action == ENABLE_CPU) { |
| 249 |
2/2✓ Branch 0 taken 56 times.
✓ Branch 1 taken 42 times.
|
98 | verbose(VB_MICROLB, "Enabling CPU %d", task->cpuid); |
| 250 | 98 | enable_cpu(&spd->pm, task->cpuid); | |
| 251 | } | ||
| 252 |
1/2✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
|
17 | else if (task->action == DISABLE_CPU) { |
| 253 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 11 times.
|
17 | verbose(VB_MICROLB, "Disabling CPU %d", task->cpuid); |
| 254 | 17 | disable_cpu(&spd->pm, task->cpuid); | |
| 255 | } | ||
| 256 | } | ||
| 257 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 7 times.
|
24 | else if (spd->options.mode == MODE_ASYNC) { |
| 258 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
|
17 | if (task->action == ENABLE_CPU) { |
| 259 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 6 times.
|
8 | verbose(VB_MICROLB, "Requesting process %d to enable CPU %d", |
| 260 | task->pid, task->cpuid); | ||
| 261 | 8 | shmem_async_enable_cpu(task->pid, task->cpuid); | |
| 262 | } | ||
| 263 |
1/2✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
|
9 | else if (task->action == DISABLE_CPU) { |
| 264 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
|
9 | verbose(VB_MICROLB, "Requesting process %d to disable CPU %d", |
| 265 | task->pid, task->cpuid); | ||
| 266 | 9 | shmem_async_disable_cpu(task->pid, task->cpuid); | |
| 267 | 9 | shmem_cpuinfo__return_async_cpu(task->pid, task->cpuid); | |
| 268 | } | ||
| 269 | } | ||
| 270 | } else { | ||
| 271 | /* group tasks */ | ||
| 272 | 71 | cpu_set_t cpus_to_enable = {}; | |
| 273 | 71 | cpu_set_t cpus_to_disable = {}; | |
| 274 |
2/2✓ Branch 0 taken 187 times.
✓ Branch 1 taken 71 times.
|
258 | for (size_t k = i; k < i+num_tasks; ++k) { |
| 275 | 187 | const cpuinfo_task_t *task = &tasks->items[k]; | |
| 276 |
2/2✓ Branch 0 taken 143 times.
✓ Branch 1 taken 44 times.
|
187 | if (task->action == ENABLE_CPU) { |
| 277 |
1/2✓ Branch 0 taken 143 times.
✗ Branch 1 not taken.
|
143 | CPU_SET(task->cpuid, &cpus_to_enable); |
| 278 | } | ||
| 279 |
1/2✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
|
44 | else if (task->action == DISABLE_CPU) { |
| 280 |
1/2✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
|
44 | CPU_SET(task->cpuid, &cpus_to_disable); |
| 281 | } | ||
| 282 | } | ||
| 283 | |||
| 284 | /* resolve group of tasks for the same PID */ | ||
| 285 | 71 | const cpuinfo_task_t *task = &tasks->items[i]; | |
| 286 |
2/2✓ Branch 0 taken 51 times.
✓ Branch 1 taken 20 times.
|
71 | if (task->pid == spd->id) { |
| 287 |
2/2✓ Branch 1 taken 48 times.
✓ Branch 2 taken 3 times.
|
51 | if (CPU_COUNT(&cpus_to_enable) > 0) { |
| 288 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 21 times.
|
48 | verbose(VB_MICROLB, "Enabling CPUs %s", mu_to_str(&cpus_to_enable)); |
| 289 | 48 | enable_cpu_set(&spd->pm, &cpus_to_enable); | |
| 290 | } | ||
| 291 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 48 times.
|
51 | if (CPU_COUNT(&cpus_to_disable) > 0) { |
| 292 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
|
3 | verbose(VB_MICROLB, "Disabling CPUs %s", mu_to_str(&cpus_to_disable)); |
| 293 | 3 | disable_cpu_set(&spd->pm, &cpus_to_disable); | |
| 294 | } | ||
| 295 | } | ||
| 296 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 9 times.
|
20 | else if (spd->options.mode == MODE_ASYNC) { |
| 297 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 7 times.
|
11 | if (CPU_COUNT(&cpus_to_enable) > 0) { |
| 298 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | verbose(VB_MICROLB, "Requesting process %d to enable CPUs %s", |
| 299 | task->pid, mu_to_str(&cpus_to_enable)); | ||
| 300 | 4 | shmem_async_enable_cpu_set(task->pid, &cpus_to_enable); | |
| 301 | } | ||
| 302 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 4 times.
|
11 | if (CPU_COUNT(&cpus_to_disable) > 0) { |
| 303 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
|
7 | verbose(VB_MICROLB, "Requesting process %d to disable CPUs %s", |
| 304 | task->pid, mu_to_str(&cpus_to_disable)); | ||
| 305 | 7 | shmem_async_disable_cpu_set(task->pid, &cpus_to_disable); | |
| 306 | 7 | shmem_cpuinfo__return_async_cpu_mask(task->pid, &cpus_to_disable); | |
| 307 | } | ||
| 308 | } | ||
| 309 | } | ||
| 310 | |||
| 311 | 210 | i += num_tasks; | |
| 312 | } | ||
| 313 | 290 | } | |
| 314 | |||
| 315 | |||
| 316 | /*********************************************************************************/ | ||
| 317 | /* Init / Finalize */ | ||
| 318 | /*********************************************************************************/ | ||
| 319 | |||
| 320 | 40 | int lewi_mask_Init(subprocess_descriptor_t *spd) { | |
| 321 | /* Value is always updated to allow testing different node sizes */ | ||
| 322 | 40 | node_size = mu_get_system_size(); | |
| 323 | |||
| 324 | /* Allocate and initialize private structure */ | ||
| 325 | 40 | spd->lewi_info = malloc(sizeof(lewi_info_t)); | |
| 326 | 40 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 327 | 40 | *lewi_info = (const lewi_info_t) { | |
| 328 | 40 | .max_parallelism = spd->options.lewi_max_parallelism, | |
| 329 | .mutex = PTHREAD_MUTEX_INITIALIZER, | ||
| 330 | }; | ||
| 331 | 40 | array_cpuid_t_init(&lewi_info->cpus_priority_array, node_size); | |
| 332 | 40 | lewi_mask_UpdateOwnershipInfo(spd, &spd->process_mask); | |
| 333 | |||
| 334 | /* Enable request queues only in async mode */ | ||
| 335 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 24 times.
|
40 | if (spd->options.mode == MODE_ASYNC) { |
| 336 | 16 | shmem_cpuinfo__enable_request_queues(); | |
| 337 | } | ||
| 338 | |||
| 339 | 40 | return DLB_SUCCESS; | |
| 340 | } | ||
| 341 | |||
| 342 | 40 | int lewi_mask_Finalize(subprocess_descriptor_t *spd) { | |
| 343 | /* De-register subprocess from the shared memory */ | ||
| 344 | 40 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 345 | 40 | int error = shmem_cpuinfo__deregister(spd->id, tasks); | |
| 346 |
1/2✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
|
40 | if (error == DLB_SUCCESS) { |
| 347 | 40 | resolve_cpuinfo_tasks(spd, tasks); | |
| 348 | } | ||
| 349 | |||
| 350 | /* Deallocate main thread arrays (worker threads do it on thread_exit) */ | ||
| 351 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 23 times.
|
40 | if (_cpu_subset != NULL) cpu_subset_destructor(_cpu_subset); |
| 352 |
1/2✓ Branch 0 taken 40 times.
✗ Branch 1 not taken.
|
40 | if (_tasks != NULL) tasks_destructor(_tasks); |
| 353 | |||
| 354 | /* Deallocate private structure */ | ||
| 355 | 40 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 356 | 40 | array_cpuid_t_destroy(&lewi_info->cpus_priority_array); | |
| 357 | 40 | free(lewi_info); | |
| 358 | 40 | lewi_info = NULL; | |
| 359 | |||
| 360 | 40 | return (error >= 0) ? DLB_SUCCESS : error; | |
| 361 | } | ||
| 362 | |||
| 363 | |||
| 364 | /*********************************************************************************/ | ||
| 365 | /* LeWI Modes (enable/disable, max_parallelism, ...) */ | ||
| 366 | /*********************************************************************************/ | ||
| 367 | |||
| 368 | 5 | int lewi_mask_EnableDLB(const subprocess_descriptor_t *spd) { | |
| 369 | /* Reset value of last_borrow */ | ||
| 370 | 5 | ((lewi_info_t*)spd->lewi_info)->last_borrow = 0; | |
| 371 | 5 | return DLB_SUCCESS; | |
| 372 | } | ||
| 373 | |||
| 374 | 5 | int lewi_mask_DisableDLB(const subprocess_descriptor_t *spd) { | |
| 375 | 5 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 376 | 5 | int error = shmem_cpuinfo__reset(spd->id, tasks); | |
| 377 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | if (error == DLB_SUCCESS) { |
| 378 | 5 | resolve_cpuinfo_tasks(spd, tasks); | |
| 379 | } | ||
| 380 | 5 | return error; | |
| 381 | } | ||
| 382 | |||
| 383 | 4 | int lewi_mask_SetMaxParallelism(const subprocess_descriptor_t *spd, int max) { | |
| 384 | 4 | int error = DLB_SUCCESS; | |
| 385 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (max > 0) { |
| 386 | 4 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 387 | 4 | lewi_info->max_parallelism = max; | |
| 388 | 4 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 389 | 4 | error = shmem_cpuinfo__update_max_parallelism(spd->id, max, tasks); | |
| 390 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (error == DLB_SUCCESS) { |
| 391 | 4 | resolve_cpuinfo_tasks(spd, tasks); | |
| 392 | } | ||
| 393 | } | ||
| 394 | 4 | return error; | |
| 395 | } | ||
| 396 | |||
| 397 | 4 | int lewi_mask_UnsetMaxParallelism(const subprocess_descriptor_t *spd) { | |
| 398 | 4 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 399 | 4 | lewi_info->max_parallelism = 0; | |
| 400 | 4 | return DLB_SUCCESS; | |
| 401 | } | ||
| 402 | |||
| 403 | |||
| 404 | /*********************************************************************************/ | ||
| 405 | /* MPI */ | ||
| 406 | /*********************************************************************************/ | ||
| 407 | |||
| 408 | /* Obtain thread mask and remove first core if keep_cpu_on_blocking_call */ | ||
| 409 | 28 | static inline void get_mask_for_blocking_call( | |
| 410 | cpu_set_t *cpu_set, bool keep_cpu_on_blocking_call) { | ||
| 411 | |||
| 412 | /* Obtain current thread's affinity mask */ | ||
| 413 | 28 | pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), cpu_set); | |
| 414 | |||
| 415 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 24 times.
|
28 | if (keep_cpu_on_blocking_call) { |
| 416 | /* Remove the first core */ | ||
| 417 | 4 | int first_cpuid = mu_get_first_cpu(cpu_set); | |
| 418 | 4 | const mu_cpuset_t *first_core = mu_get_core_mask(first_cpuid); | |
| 419 | 4 | mu_subtract(cpu_set, cpu_set, first_core->set); | |
| 420 | } | ||
| 421 | 28 | } | |
| 422 | |||
| 423 | /* Lend the CPUs of the thread encountering the blocking call */ | ||
| 424 | 14 | int lewi_mask_IntoBlockingCall(const subprocess_descriptor_t *spd) { | |
| 425 | |||
| 426 | 14 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 427 | |||
| 428 | /* Obtain affinity mask to lend */ | ||
| 429 | cpu_set_t cpu_set; | ||
| 430 | 14 | get_mask_for_blocking_call(&cpu_set, | |
| 431 | 14 | spd->options.lewi_keep_cpu_on_blocking_call); | |
| 432 | |||
| 433 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
|
14 | if (unlikely(mu_intersects(&lewi_info->in_mpi_cpus, &cpu_set))) { |
| 434 | #ifdef DEBUG_VERSION | ||
| 435 | ✗ | fatal("Some CPU in %s already into blocking call", mu_to_str(&cpu_set)); | |
| 436 | #else | ||
| 437 | /* This scenario is not expected to occur; if it does, simply ignore. */ | ||
| 438 | return DLB_NOUPDT; | ||
| 439 | #endif | ||
| 440 | } | ||
| 441 | |||
| 442 | 14 | int error = DLB_NOUPDT; | |
| 443 | |||
| 444 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
|
14 | if (mu_count(&cpu_set) > 0) { |
| 445 | |||
| 446 | /* Add cpu_set to in_mpi_cpus */ | ||
| 447 | 12 | mu_or(&lewi_info->in_mpi_cpus, &lewi_info->in_mpi_cpus, &cpu_set); | |
| 448 | |||
| 449 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | verbose(VB_MICROLB, "In blocking call, lending %s", mu_to_str(&cpu_set)); |
| 450 | |||
| 451 | /* Finally, lend mask */ | ||
| 452 | 12 | error = lewi_mask_LendCpuMask(spd, &cpu_set); | |
| 453 | } | ||
| 454 | 14 | return error; | |
| 455 | } | ||
| 456 | |||
| 457 | /* Reclaim the CPUs that were lent when encountering the blocking call. | ||
| 458 | * The thread must have not change its affinity mask since then. */ | ||
| 459 | 14 | int lewi_mask_OutOfBlockingCall(const subprocess_descriptor_t *spd) { | |
| 460 | |||
| 461 | 14 | int error = DLB_NOUPDT; | |
| 462 | 14 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 463 | |||
| 464 | /* Obtain affinity mask to lend */ | ||
| 465 | cpu_set_t cpu_set; | ||
| 466 | 14 | get_mask_for_blocking_call(&cpu_set, | |
| 467 | 14 | spd->options.lewi_keep_cpu_on_blocking_call); | |
| 468 | |||
| 469 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
|
14 | if (unlikely(!mu_is_subset(&lewi_info->in_mpi_cpus, &cpu_set))) { |
| 470 | #ifdef DEBUG_VERSION | ||
| 471 | ✗ | fatal("Some CPU in %s is not into blocking call", mu_to_str(&cpu_set)); | |
| 472 | #else | ||
| 473 | /* This scenario is not expected to occur; if it does, simply ignore. */ | ||
| 474 | return DLB_NOUPDT; | ||
| 475 | #endif | ||
| 476 | } | ||
| 477 | |||
| 478 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 2 times.
|
14 | if (mu_count(&lewi_info->in_mpi_cpus) > 0) { |
| 479 | |||
| 480 | /* Clear cpu_set from in_mpi_cpus */ | ||
| 481 | 12 | mu_subtract(&lewi_info->in_mpi_cpus, &lewi_info->in_mpi_cpus, &cpu_set); | |
| 482 | |||
| 483 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | verbose(VB_MICROLB, "Out of blocking call, acquiring %s", mu_to_str(&cpu_set)); |
| 484 | |||
| 485 | /* If there are owned CPUs to reclaim: */ | ||
| 486 | cpu_set_t owned_cpus; | ||
| 487 | 12 | mu_and(&owned_cpus, &cpu_set, &spd->process_mask); | |
| 488 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
|
12 | if (mu_count(&owned_cpus) > 0) { |
| 489 | |||
| 490 | /* Construct a CPU array from owned_cpus */ | ||
| 491 | 6 | array_cpuid_t *cpu_subset = get_cpu_subset(spd); | |
| 492 | 6 | cpu_array_from_cpuset(cpu_subset, &owned_cpus); | |
| 493 | |||
| 494 | /* Acquire CPUs */ | ||
| 495 | 6 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 496 | 6 | error = shmem_cpuinfo__acquire_from_cpu_subset(spd->id, cpu_subset, tasks); | |
| 497 | |||
| 498 | /* Resolve tasks: Even if callbacks weren't called during IntoBlockingCall, | ||
| 499 | * other LeWI actions may have been called after it so we cannot ignore | ||
| 500 | * pending actions */ | ||
| 501 |
3/4✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
|
6 | if (error == DLB_SUCCESS || error == DLB_NOTED) { |
| 502 | 6 | resolve_cpuinfo_tasks(spd, tasks); | |
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | /* If there are ONLY non-owned CPUs to reclaim: | ||
| 507 | * (if some owned CPU was already reclaimed, we forget about non-owned) */ | ||
| 508 | cpu_set_t non_owned_cpus; | ||
| 509 | 12 | mu_subtract(&non_owned_cpus, &cpu_set, &spd->process_mask); | |
| 510 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
|
12 | if (mu_count(&non_owned_cpus) > 0 |
| 511 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | && mu_count(&owned_cpus) == 0) { |
| 512 | |||
| 513 | /* Remove CPUs that are disabled in the shmem */ | ||
| 514 | 6 | for (int cpuid = mu_get_first_cpu(&non_owned_cpus); | |
| 515 |
3/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
12 | cpuid >= 0 && cpuid != DLB_CPUID_INVALID; |
| 516 | 6 | cpuid = mu_get_next_cpu(&non_owned_cpus, cpuid)) { | |
| 517 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4 times.
|
6 | if (!shmem_cpuinfo__is_cpu_enabled(cpuid)) { |
| 518 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | CPU_CLR(cpuid, &non_owned_cpus); |
| 519 | } | ||
| 520 | } | ||
| 521 | |||
| 522 | /* Construct a CPU array from non_owned_cpus */ | ||
| 523 | 6 | array_cpuid_t *cpu_subset = get_cpu_subset(spd); | |
| 524 | 6 | cpu_array_from_cpuset(cpu_subset, &non_owned_cpus); | |
| 525 | |||
| 526 | /* Borrow CPUs, but also ignoring the enable callbacks */ | ||
| 527 | 6 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 528 | 6 | error = shmem_cpuinfo__borrow_from_cpu_subset(spd->id, cpu_subset, tasks); | |
| 529 | |||
| 530 | /* A non-success error means that not all CPUs could be borrowed */ | ||
| 531 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
|
6 | if (error != DLB_SUCCESS) { |
| 532 | /* Annotate the CPU as pending to bypass the shared memory for the next query */ | ||
| 533 | 4 | mu_or(&lewi_info->pending_reclaimed_cpus, &lewi_info->pending_reclaimed_cpus, | |
| 534 | &non_owned_cpus); | ||
| 535 | |||
| 536 | /* We lost the CPU while in MPI. | ||
| 537 | * In async mode, we can just disable the CPU. | ||
| 538 | * In polling mode, the process needs to call Return */ | ||
| 539 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
|
4 | if (spd->options.mode == MODE_ASYNC) { |
| 540 | 2 | disable_cpu_set(&spd->pm, &non_owned_cpus); | |
| 541 | } | ||
| 542 | } | ||
| 543 | } | ||
| 544 | } | ||
| 545 | |||
| 546 | 14 | return error; | |
| 547 | } | ||
| 548 | |||
| 549 | |||
| 550 | /*********************************************************************************/ | ||
| 551 | /* Lend */ | ||
| 552 | /*********************************************************************************/ | ||
| 553 | |||
| 554 | 2 | int lewi_mask_Lend(const subprocess_descriptor_t *spd) { | |
| 555 | cpu_set_t mask; | ||
| 556 | 2 | mu_get_system_mask(&mask); | |
| 557 | 2 | return lewi_mask_LendCpuMask(spd, &mask); | |
| 558 | } | ||
| 559 | |||
| 560 | 83 | int lewi_mask_LendCpu(const subprocess_descriptor_t *spd, int cpuid) { | |
| 561 | 83 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 562 | 83 | int error = shmem_cpuinfo__lend_cpu(spd->id, cpuid, tasks); | |
| 563 | |||
| 564 |
1/2✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
|
83 | if (error == DLB_SUCCESS) { |
| 565 |
2/2✓ Branch 0 taken 44 times.
✓ Branch 1 taken 39 times.
|
83 | if (spd->options.mode == MODE_ASYNC) { |
| 566 | 44 | resolve_cpuinfo_tasks(spd, tasks); | |
| 567 | } | ||
| 568 | |||
| 569 | /* Clear possible pending reclaimed CPUs */ | ||
| 570 | 83 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 571 |
1/2✓ Branch 0 taken 83 times.
✗ Branch 1 not taken.
|
83 | CPU_CLR(cpuid, &lewi_info->pending_reclaimed_cpus); |
| 572 | } | ||
| 573 | 83 | return error; | |
| 574 | } | ||
| 575 | |||
| 576 | 51 | int lewi_mask_LendCpuMask(const subprocess_descriptor_t *spd, const cpu_set_t *mask) { | |
| 577 | 51 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 578 | 51 | int error = shmem_cpuinfo__lend_cpu_mask(spd->id, mask, tasks); | |
| 579 |
1/2✓ Branch 0 taken 51 times.
✗ Branch 1 not taken.
|
51 | if (error == DLB_SUCCESS) { |
| 580 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 25 times.
|
51 | if (spd->options.mode == MODE_ASYNC) { |
| 581 | 26 | resolve_cpuinfo_tasks(spd, tasks); | |
| 582 | } | ||
| 583 | |||
| 584 | /* Clear possible pending reclaimed CPUs */ | ||
| 585 | 51 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 586 | 51 | mu_subtract(&lewi_info->pending_reclaimed_cpus, | |
| 587 | 51 | &lewi_info->pending_reclaimed_cpus, mask); | |
| 588 | } | ||
| 589 | 51 | return error; | |
| 590 | } | ||
| 591 | |||
| 592 | |||
| 593 | /*********************************************************************************/ | ||
| 594 | /* Reclaim */ | ||
| 595 | /*********************************************************************************/ | ||
| 596 | |||
| 597 | 6 | int lewi_mask_Reclaim(const subprocess_descriptor_t *spd) { | |
| 598 | /* Even though shmem_cpuinfo__reclaim_all exists, | ||
| 599 | * iterating the mask should be more efficient */ | ||
| 600 | 6 | return lewi_mask_ReclaimCpuMask(spd, &spd->process_mask); | |
| 601 | } | ||
| 602 | |||
| 603 | 8 | int lewi_mask_ReclaimCpu(const subprocess_descriptor_t *spd, int cpuid) { | |
| 604 | 8 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 605 | 8 | int error = shmem_cpuinfo__reclaim_cpu(spd->id, cpuid, tasks); | |
| 606 |
4/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 1 times.
|
8 | if (error == DLB_SUCCESS || error == DLB_NOTED) { |
| 607 | 7 | resolve_cpuinfo_tasks(spd, tasks); | |
| 608 | } | ||
| 609 | 8 | return error; | |
| 610 | } | ||
| 611 | |||
| 612 | ✗ | int lewi_mask_ReclaimCpus(const subprocess_descriptor_t *spd, int ncpus) { | |
| 613 | ✗ | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 614 | ✗ | int error = shmem_cpuinfo__reclaim_cpus(spd->id, ncpus, tasks); | |
| 615 | ✗ | if (error == DLB_SUCCESS || error == DLB_NOTED) { | |
| 616 | ✗ | resolve_cpuinfo_tasks(spd, tasks); | |
| 617 | } | ||
| 618 | ✗ | return error; | |
| 619 | } | ||
| 620 | |||
| 621 | 17 | int lewi_mask_ReclaimCpuMask(const subprocess_descriptor_t *spd, const cpu_set_t *mask) { | |
| 622 | 17 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 623 | 17 | int error = shmem_cpuinfo__reclaim_cpu_mask(spd->id, mask, tasks); | |
| 624 | 17 | resolve_cpuinfo_tasks(spd, tasks); | |
| 625 | 17 | return error; | |
| 626 | } | ||
| 627 | |||
| 628 | |||
| 629 | /*********************************************************************************/ | ||
| 630 | /* Acquire */ | ||
| 631 | /*********************************************************************************/ | ||
| 632 | |||
| 633 | 29 | int lewi_mask_AcquireCpu(const subprocess_descriptor_t *spd, int cpuid) { | |
| 634 | 29 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 635 | 29 | int error = shmem_cpuinfo__acquire_cpu(spd->id, cpuid, tasks); | |
| 636 |
4/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 3 times.
|
29 | if (error == DLB_SUCCESS || error == DLB_NOTED) { |
| 637 | 26 | resolve_cpuinfo_tasks(spd, tasks); | |
| 638 | } | ||
| 639 | 29 | return error; | |
| 640 | } | ||
| 641 | |||
| 642 | 65 | int lewi_mask_AcquireCpus(const subprocess_descriptor_t *spd, int ncpus) { | |
| 643 | 65 | int error = DLB_NOUPDT; | |
| 644 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 56 times.
|
65 | if (ncpus == 0) { |
| 645 | /* AcquireCPUs(0) has a special meaning of removing any previous request */ | ||
| 646 | 9 | shmem_cpuinfo__remove_requests(spd->id); | |
| 647 | 9 | error = DLB_SUCCESS; | |
| 648 |
1/2✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
|
56 | } else if (ncpus > 0) { |
| 649 | 56 | error = lewi_mask_AcquireCpusInMask(spd, ncpus, NULL); | |
| 650 | } | ||
| 651 | 65 | return error; | |
| 652 | } | ||
| 653 | |||
| 654 | 22 | int lewi_mask_AcquireCpuMask(const subprocess_descriptor_t *spd, const cpu_set_t *mask) { | |
| 655 | 22 | return lewi_mask_AcquireCpusInMask(spd, 0, mask); | |
| 656 | } | ||
| 657 | |||
| 658 | 93 | int lewi_mask_AcquireCpusInMask(const subprocess_descriptor_t *spd, int ncpus, | |
| 659 | const cpu_set_t *mask) { | ||
| 660 | 93 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 661 | 93 | bool async = spd->options.mode == MODE_ASYNC; | |
| 662 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 57 times.
|
93 | int64_t *last_borrow = async ? NULL : &lewi_info->last_borrow; |
| 663 | |||
| 664 | /* Construct a CPU array based on cpus_priority_array and mask (if present) */ | ||
| 665 | 93 | array_cpuid_t *cpu_subset = get_cpu_subset(spd); | |
| 666 | 93 | cpu_array_and(cpu_subset, &lewi_info->cpus_priority_array, mask); | |
| 667 | |||
| 668 | /* Provide a number of requested CPUs only if needed */ | ||
| 669 |
2/2✓ Branch 0 taken 71 times.
✓ Branch 1 taken 22 times.
|
93 | int *requested_ncpus = ncpus > 0 ? &ncpus : NULL; |
| 670 | |||
| 671 | 93 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 672 | 93 | int error = shmem_cpuinfo__acquire_ncpus_from_cpu_subset(spd->id, | |
| 673 | 93 | requested_ncpus, cpu_subset, spd->options.lewi_affinity, | |
| 674 | lewi_info->max_parallelism, last_borrow, tasks); | ||
| 675 | |||
| 676 |
2/2✓ Branch 0 taken 73 times.
✓ Branch 1 taken 20 times.
|
93 | if (error != DLB_NOUPDT) { |
| 677 | 73 | resolve_cpuinfo_tasks(spd, tasks); | |
| 678 | } | ||
| 679 | 93 | return error; | |
| 680 | } | ||
| 681 | |||
| 682 | |||
| 683 | /*********************************************************************************/ | ||
| 684 | /* Borrow */ | ||
| 685 | /*********************************************************************************/ | ||
| 686 | |||
| 687 | 8 | int lewi_mask_Borrow(const subprocess_descriptor_t *spd) { | |
| 688 | cpu_set_t system_mask; | ||
| 689 | 8 | mu_get_system_mask(&system_mask); | |
| 690 | 8 | return lewi_mask_BorrowCpusInMask(spd, 0, &system_mask); | |
| 691 | } | ||
| 692 | |||
| 693 | 10 | int lewi_mask_BorrowCpu(const subprocess_descriptor_t *spd, int cpuid) { | |
| 694 | 10 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 695 | 10 | int error = shmem_cpuinfo__borrow_cpu(spd->id, cpuid, tasks); | |
| 696 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
10 | if (error == DLB_SUCCESS) { |
| 697 | 10 | resolve_cpuinfo_tasks(spd, tasks); | |
| 698 | } | ||
| 699 | 10 | return error; | |
| 700 | } | ||
| 701 | |||
| 702 | 14 | int lewi_mask_BorrowCpus(const subprocess_descriptor_t *spd, int ncpus) { | |
| 703 | 14 | return lewi_mask_BorrowCpusInMask(spd, ncpus, NULL); | |
| 704 | } | ||
| 705 | |||
| 706 | ✗ | int lewi_mask_BorrowCpuMask(const subprocess_descriptor_t *spd, const cpu_set_t *mask) { | |
| 707 | ✗ | return lewi_mask_BorrowCpusInMask(spd, 0, mask); | |
| 708 | } | ||
| 709 | |||
| 710 | 30 | int lewi_mask_BorrowCpusInMask(const subprocess_descriptor_t *spd, int ncpus, const cpu_set_t *mask) { | |
| 711 | 30 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 712 | 30 | bool async = spd->options.mode == MODE_ASYNC; | |
| 713 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 15 times.
|
30 | int64_t *last_borrow = async ? NULL : &lewi_info->last_borrow; |
| 714 | |||
| 715 | /* Construct a CPU array based on cpus_priority_array and mask (if present) */ | ||
| 716 | 30 | array_cpuid_t *cpu_subset = get_cpu_subset(spd); | |
| 717 | 30 | cpu_array_and(cpu_subset, &lewi_info->cpus_priority_array, mask); | |
| 718 | |||
| 719 | /* Provide a number of requested CPUs only if needed */ | ||
| 720 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 8 times.
|
30 | int *requested_ncpus = ncpus > 0 ? &ncpus : NULL; |
| 721 | |||
| 722 | 30 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 723 | 30 | int error = shmem_cpuinfo__borrow_ncpus_from_cpu_subset(spd->id, | |
| 724 | 30 | requested_ncpus, cpu_subset, spd->options.lewi_affinity, | |
| 725 | lewi_info->max_parallelism, last_borrow, tasks); | ||
| 726 | |||
| 727 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 12 times.
|
30 | if (error == DLB_SUCCESS) { |
| 728 | 18 | resolve_cpuinfo_tasks(spd, tasks); | |
| 729 | } | ||
| 730 | |||
| 731 | 30 | return error; | |
| 732 | } | ||
| 733 | |||
| 734 | |||
| 735 | /*********************************************************************************/ | ||
| 736 | /* Return */ | ||
| 737 | /*********************************************************************************/ | ||
| 738 | |||
| 739 | 1 | int lewi_mask_Return(const subprocess_descriptor_t *spd) { | |
| 740 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (spd->options.mode == MODE_ASYNC) { |
| 741 | // Return should not be called in async mode | ||
| 742 | ✗ | return DLB_ERR_NOCOMP; | |
| 743 | } | ||
| 744 | |||
| 745 | 1 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 746 | 1 | int error = shmem_cpuinfo__return_all(spd->id, tasks); | |
| 747 | 1 | resolve_cpuinfo_tasks(spd, tasks); | |
| 748 | |||
| 749 | /* Check possible pending reclaimed CPUs */ | ||
| 750 | 1 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 751 | 1 | int cpuid = mu_get_first_cpu(&lewi_info->pending_reclaimed_cpus); | |
| 752 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (cpuid >= 0) { |
| 753 | do { | ||
| 754 | ✗ | disable_cpu(&spd->pm, cpuid); | |
| 755 | ✗ | cpuid = mu_get_next_cpu(&lewi_info->pending_reclaimed_cpus, cpuid); | |
| 756 | ✗ | } while (cpuid >= 0 && cpuid != DLB_CPUID_INVALID); | |
| 757 | ✗ | CPU_ZERO(&lewi_info->pending_reclaimed_cpus); | |
| 758 | ✗ | error = DLB_SUCCESS; | |
| 759 | } | ||
| 760 | |||
| 761 | 1 | return error; | |
| 762 | } | ||
| 763 | |||
| 764 | 21 | int lewi_mask_ReturnCpu(const subprocess_descriptor_t *spd, int cpuid) { | |
| 765 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 14 times.
|
21 | if (spd->options.mode == MODE_ASYNC) { |
| 766 | // Return should not be called in async mode | ||
| 767 | 7 | return DLB_ERR_NOCOMP; | |
| 768 | } | ||
| 769 | |||
| 770 | 14 | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 771 | 14 | int error = shmem_cpuinfo__return_cpu(spd->id, cpuid, tasks); | |
| 772 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
14 | if (error == DLB_SUCCESS || error == DLB_ERR_REQST) { |
| 773 | 13 | resolve_cpuinfo_tasks(spd, tasks); | |
| 774 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | } else if (error == DLB_ERR_PERM) { |
| 775 | /* Check possible pending reclaimed CPUs */ | ||
| 776 | 1 | lewi_info_t *lewi_info = spd->lewi_info; | |
| 777 |
3/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
|
1 | if (CPU_ISSET(cpuid, &lewi_info->pending_reclaimed_cpus)) { |
| 778 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | CPU_CLR(cpuid, &lewi_info->pending_reclaimed_cpus); |
| 779 | 1 | disable_cpu(&spd->pm, cpuid); | |
| 780 | 1 | error = DLB_SUCCESS; | |
| 781 | } | ||
| 782 | } | ||
| 783 | 14 | return error; | |
| 784 | } | ||
| 785 | |||
| 786 | ✗ | int lewi_mask_ReturnCpuMask(const subprocess_descriptor_t *spd, const cpu_set_t *mask) { | |
| 787 | ✗ | if (spd->options.mode == MODE_ASYNC) { | |
| 788 | // Return should not be called in async mode | ||
| 789 | ✗ | return DLB_ERR_NOCOMP; | |
| 790 | } | ||
| 791 | |||
| 792 | ✗ | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 793 | ✗ | int error = shmem_cpuinfo__return_cpu_mask(spd->id, mask, tasks); | |
| 794 | ✗ | resolve_cpuinfo_tasks(spd, tasks); | |
| 795 | |||
| 796 | /* Check possible pending reclaimed CPUs */ | ||
| 797 | ✗ | lewi_info_t *lewi_info = spd->lewi_info; | |
| 798 | ✗ | if (CPU_COUNT(&lewi_info->pending_reclaimed_cpus) > 0) { | |
| 799 | cpu_set_t cpus_to_return; | ||
| 800 | ✗ | CPU_AND(&cpus_to_return, &lewi_info->pending_reclaimed_cpus, mask); | |
| 801 | ✗ | for (int cpuid = mu_get_first_cpu(&cpus_to_return); | |
| 802 | ✗ | cpuid >= 0 && cpuid != DLB_CPUID_INVALID; | |
| 803 | ✗ | cpuid = mu_get_next_cpu(&cpus_to_return, cpuid)) { | |
| 804 | ✗ | disable_cpu(&spd->pm, cpuid); | |
| 805 | } | ||
| 806 | ✗ | mu_subtract(&lewi_info->pending_reclaimed_cpus, | |
| 807 | ✗ | &lewi_info->pending_reclaimed_cpus, &cpus_to_return); | |
| 808 | ✗ | error = DLB_SUCCESS; | |
| 809 | } | ||
| 810 | |||
| 811 | ✗ | return error; | |
| 812 | } | ||
| 813 | |||
| 814 | |||
| 815 | // Others | ||
| 816 | |||
| 817 | 32 | int lewi_mask_CheckCpuAvailability(const subprocess_descriptor_t *spd, int cpuid) { | |
| 818 | 32 | return shmem_cpuinfo__check_cpu_availability(spd->id, cpuid); | |
| 819 | } | ||
| 820 | |||
| 821 | ✗ | int lewi_mask_UpdateOwnership(const subprocess_descriptor_t *spd, | |
| 822 | const cpu_set_t *process_mask) { | ||
| 823 | /* Update priority array */ | ||
| 824 | ✗ | lewi_mask_UpdateOwnershipInfo(spd, process_mask); | |
| 825 | |||
| 826 | /* Update cpuinfo data and return reclaimed CPUs */ | ||
| 827 | ✗ | array_cpuinfo_task_t *tasks = get_tasks(spd); | |
| 828 | ✗ | shmem_cpuinfo__update_ownership(spd->id, process_mask, tasks); | |
| 829 | ✗ | resolve_cpuinfo_tasks(spd, tasks); | |
| 830 | |||
| 831 | /* Check possible pending reclaimed CPUs */ | ||
| 832 | ✗ | lewi_info_t *lewi_info = spd->lewi_info; | |
| 833 | ✗ | int cpuid = mu_get_first_cpu(&lewi_info->pending_reclaimed_cpus); | |
| 834 | ✗ | if (cpuid >= 0) { | |
| 835 | do { | ||
| 836 | ✗ | disable_cpu(&spd->pm, cpuid); | |
| 837 | ✗ | cpuid = mu_get_next_cpu(&lewi_info->pending_reclaimed_cpus, cpuid); | |
| 838 | ✗ | } while (cpuid >= 0 && cpuid != DLB_CPUID_INVALID); | |
| 839 | ✗ | CPU_ZERO(&lewi_info->pending_reclaimed_cpus); | |
| 840 | } | ||
| 841 | |||
| 842 | ✗ | return DLB_SUCCESS; | |
| 843 | } | ||
| 844 |