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