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 "support/queues.h" | ||
25 | |||
26 | #include "support/debug.h" | ||
27 | #include "apis/dlb_errors.h" | ||
28 | |||
29 | #include <string.h> | ||
30 | |||
31 | enum { NOBODY = 0 }; | ||
32 | |||
33 | /*** queue_lewi_reqs_t ***********************************************************/ | ||
34 | |||
35 | 12 | void queue_lewi_reqs_init(queue_lewi_reqs_t *queue) { | |
36 | 12 | memset(queue, 0, sizeof(*queue)); | |
37 | 12 | } | |
38 | |||
39 | 1206 | unsigned int queue_lewi_reqs_size(const queue_lewi_reqs_t *queue) { | |
40 | 1206 | return queue->head; | |
41 | } | ||
42 | |||
43 | /* Remove entry with given <pid> and pack list, return previous value if exists */ | ||
44 | 378 | unsigned int queue_lewi_reqs_remove(queue_lewi_reqs_t *queue, pid_t pid) { | |
45 | |||
46 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 377 times.
|
378 | if (unlikely(pid == 0)) return 0; |
47 |
2/2✓ Branch 0 taken 36 times.
✓ Branch 1 taken 341 times.
|
377 | if (queue->head == 0) return 0; |
48 | |||
49 | /* The queue does not need to be ordered. Find element to remove and swap | ||
50 | * last position */ | ||
51 |
2/2✓ Branch 0 taken 12702 times.
✓ Branch 1 taken 43 times.
|
12745 | for (unsigned int i=0; i < queue->head; ++i) { |
52 | 12702 | lewi_request_t *request = &queue->queue[i]; | |
53 |
2/2✓ Branch 0 taken 298 times.
✓ Branch 1 taken 12404 times.
|
12702 | if (request->pid == pid) { |
54 | // return value | ||
55 | 298 | int howmany = request->howmany; | |
56 | |||
57 |
2/2✓ Branch 0 taken 204 times.
✓ Branch 1 taken 94 times.
|
298 | if (i+1 < queue->head) { |
58 | // this is not the last element, copy last one here | ||
59 | 204 | *request = queue->queue[queue->head-1]; | |
60 | } | ||
61 | 298 | --queue->head; | |
62 | |||
63 | 298 | return howmany; | |
64 | } | ||
65 | } | ||
66 | |||
67 | 43 | return 0; | |
68 | } | ||
69 | |||
70 | /* Push entry with given <pid>, add value if already present */ | ||
71 | 924 | int queue_lewi_reqs_push(queue_lewi_reqs_t *queue, pid_t pid, unsigned int howmany) { | |
72 | |||
73 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 923 times.
|
924 | if (unlikely(pid == 0)) return DLB_NOUPDT; |
74 | |||
75 | /* Remove entry if value is reset to 0 */ | ||
76 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 922 times.
|
923 | if (howmany == 0) { |
77 | 1 | queue_lewi_reqs_remove(queue, pid); | |
78 | 1 | return DLB_SUCCESS; | |
79 | } | ||
80 | |||
81 | /* Find entry, if exists */ | ||
82 | unsigned int i; | ||
83 |
2/2✓ Branch 0 taken 33242 times.
✓ Branch 1 taken 367 times.
|
33609 | for (i=0; i<queue->head; ++i) { |
84 |
2/2✓ Branch 0 taken 555 times.
✓ Branch 1 taken 32687 times.
|
33242 | if (queue->queue[i].pid == pid) { |
85 | /* Found, just update value */ | ||
86 | 555 | queue->queue[i].howmany += howmany; | |
87 | 555 | return DLB_SUCCESS; | |
88 | } | ||
89 | } | ||
90 | |||
91 | /* No existing entry and 'head' points to the last position, which is reserved */ | ||
92 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 366 times.
|
367 | if (queue->head == QUEUE_LEWI_REQS_SIZE-1) { |
93 | 1 | return DLB_ERR_REQST; | |
94 | } | ||
95 | |||
96 | /* 'i' (and 'head') points the new spot. Add entry. */ | ||
97 | 366 | queue->queue[i] = (const lewi_request_t) { | |
98 | .pid = pid, | ||
99 | .howmany = howmany, | ||
100 | }; | ||
101 | 366 | ++queue->head; | |
102 | 366 | return DLB_SUCCESS; | |
103 | } | ||
104 | |||
105 | /* Remove entries with no values (howmany == 0) */ | ||
106 | 100 | static void queue_lewi_reqs_pack(queue_lewi_reqs_t *queue) { | |
107 | unsigned int i, i_write; | ||
108 |
2/2✓ Branch 0 taken 119 times.
✓ Branch 1 taken 100 times.
|
219 | for (i=0, i_write=0; i<queue->head; ++i) { |
109 |
2/2✓ Branch 0 taken 88 times.
✓ Branch 1 taken 31 times.
|
119 | if (queue->queue[i].howmany > 0) { |
110 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 87 times.
|
88 | if (i != i_write) { |
111 | 1 | queue->queue[i_write] = queue->queue[i]; | |
112 | } | ||
113 | 88 | ++i_write; | |
114 | } | ||
115 | } | ||
116 | 100 | queue->head = i_write; | |
117 | 100 | } | |
118 | |||
119 | /* Sort in descending order by number of requests */ | ||
120 | 22 | static int sort_queue_lewi_reqs(const void *elem1, const void *elem2) { | |
121 | 22 | return ((lewi_request_t*)elem2)->howmany - ((lewi_request_t*)elem1)->howmany; | |
122 | } | ||
123 | |||
124 | /* Pop ncpus from the queue in an equal distribution, return array of accepted requests | ||
125 | * by argument, and number of CPUs not assigned */ | ||
126 | 101 | unsigned int queue_lewi_reqs_pop_ncpus(queue_lewi_reqs_t *queue, unsigned int ncpus, | |
127 | lewi_request_t *requests, unsigned int *nreqs, unsigned int maxreqs) { | ||
128 | |||
129 | 101 | unsigned int queue_size = queue_lewi_reqs_size(queue); | |
130 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 100 times.
|
101 | if (queue_size == 0) { |
131 | 1 | *nreqs = 0; | |
132 | 1 | return ncpus; | |
133 | } | ||
134 | |||
135 | /* For evenly distribute CPU removal, we need to sort the queue by number | ||
136 | * of requests per process */ | ||
137 | 100 | qsort(queue->queue, queue_size, sizeof(queue->queue[0]), sort_queue_lewi_reqs); | |
138 | |||
139 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 100 times.
|
100 | ensure(queue->queue[queue->head-1].howmany > 0, "Empty spots in the queue"); |
140 | |||
141 | unsigned int i, j; | ||
142 |
4/6✓ Branch 0 taken 119 times.
✓ Branch 1 taken 100 times.
✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 119 times.
✗ Branch 5 not taken.
|
219 | for (i = queue->head, j = 0; i-- > 0 && ncpus > 0 && j < maxreqs; ) { |
143 | 119 | lewi_request_t *request = &queue->queue[i]; | |
144 | /* For every iteration, we compute the CPUs to be popped by computing | ||
145 | * the minimum between an even distribution and the current request | ||
146 | * value. */ | ||
147 | 119 | unsigned int ncpus_popped = min_uint(ncpus/(i+1), request->howmany); | |
148 |
2/2✓ Branch 0 taken 117 times.
✓ Branch 1 taken 2 times.
|
119 | if (ncpus_popped > 0) { |
149 | /* Assign number of CPUs to output array */ | ||
150 | 117 | requests[j++] = (const lewi_request_t) { | |
151 | 117 | .pid = request->pid, | |
152 | .howmany = ncpus_popped, | ||
153 | }; | ||
154 | |||
155 | /* Remove the CPUs from the queue */ | ||
156 | 117 | request->howmany -= ncpus_popped; | |
157 | |||
158 | /* Update number of CPUs to be popped */ | ||
159 | 117 | ncpus -= ncpus_popped; | |
160 | } | ||
161 | } | ||
162 | |||
163 | /* Update output array size */ | ||
164 | 100 | *nreqs = j; | |
165 | |||
166 | /* Pack queue, remove elements if howmany reached 0 */ | ||
167 | 100 | queue_lewi_reqs_pack(queue); | |
168 | |||
169 | 100 | return ncpus; | |
170 | } | ||
171 | |||
172 | /* Return the value of requests of the given pid */ | ||
173 | 313 | unsigned int queue_lewi_reqs_get(queue_lewi_reqs_t *queue, pid_t pid) { | |
174 | |||
175 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 313 times.
|
313 | if (unlikely(pid == 0)) return 0; |
176 | |||
177 |
2/2✓ Branch 0 taken 32725 times.
✓ Branch 1 taken 22 times.
|
32747 | for (unsigned int i = 0; i < queue->head; ++i) { |
178 |
2/2✓ Branch 0 taken 291 times.
✓ Branch 1 taken 32434 times.
|
32725 | if (queue->queue[i].pid == pid) { |
179 | 291 | return queue->queue[i].howmany; | |
180 | } | ||
181 | } | ||
182 | |||
183 | 22 | return 0; | |
184 | } | ||
185 | |||
186 | |||
187 | /*** queue_proc_reqs_t ***********************************************************/ | ||
188 | |||
189 | 3 | void queue_proc_reqs_init(queue_proc_reqs_t *queue) { | |
190 | 3 | memset(queue, 0, sizeof(*queue)); | |
191 | 3 | } | |
192 | |||
193 | 16406 | unsigned int queue_proc_reqs_size(const queue_proc_reqs_t *queue) { | |
194 | 16406 | return queue->head >= queue->tail | |
195 | 8208 | ? queue->head - queue->tail | |
196 |
2/2✓ Branch 0 taken 8208 times.
✓ Branch 1 taken 8198 times.
|
16406 | : QUEUE_PROC_REQS_SIZE - queue->tail + queue->head; |
197 | } | ||
198 | |||
199 | 8192 | process_request_t* queue_proc_reqs_front(queue_proc_reqs_t *queue) { | |
200 | 8192 | return &queue->queue[queue->tail]; | |
201 | } | ||
202 | |||
203 | 12297 | process_request_t* queue_proc_reqs_back(queue_proc_reqs_t *queue) { | |
204 |
2/2✓ Branch 0 taken 4102 times.
✓ Branch 1 taken 8195 times.
|
12297 | unsigned int prev_head = queue->head > 0 ? queue->head-1 : QUEUE_PROC_REQS_SIZE-1; |
205 | 12297 | return &queue->queue[prev_head]; | |
206 | } | ||
207 | |||
208 | 3 | void queue_proc_reqs_remove(queue_proc_reqs_t *queue, pid_t pid) { | |
209 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
|
3 | if (unlikely(!pid)) return; |
210 | |||
211 | unsigned int i; | ||
212 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 3 times.
|
14 | for (i=queue->tail; i!=queue->head; i=(i+1)%QUEUE_PROC_REQS_SIZE) { |
213 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 7 times.
|
11 | if (queue->queue[i].pid == pid) { |
214 | /* Clear request */ | ||
215 | 4 | memset(&queue->queue[i], 0, sizeof(process_request_t)); | |
216 | } | ||
217 | /* Advance tail if it points to invalid elements */ | ||
218 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 6 times.
|
11 | if (queue->queue[i].pid == NOBODY |
219 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
|
5 | && i == queue->tail) { |
220 | 1 | queue->tail = (queue->tail+1) % QUEUE_PROC_REQS_SIZE; | |
221 | } | ||
222 | } | ||
223 | } | ||
224 | |||
225 | 12298 | int queue_proc_reqs_push(queue_proc_reqs_t *queue, pid_t pid, | |
226 | unsigned int howmany, const cpu_set_t *allowed) { | ||
227 | /* Remove entry if value is reset to 0 */ | ||
228 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12297 times.
|
12298 | if (howmany == 0) { |
229 | 1 | queue_proc_reqs_remove(queue, pid); | |
230 | 1 | return DLB_SUCCESS; | |
231 | } | ||
232 | |||
233 | /* If the last request belongs to the same pid, just increment value */ | ||
234 | 12297 | process_request_t *last_req = queue_proc_reqs_back(queue); | |
235 |
2/2✓ Branch 0 taken 8191 times.
✓ Branch 1 taken 4106 times.
|
12297 | if (last_req->pid == pid) { |
236 | 8191 | last_req->howmany += howmany; | |
237 | 8191 | return DLB_NOTED; | |
238 | } | ||
239 | |||
240 | /* If the queue is full, return error code */ | ||
241 | 4106 | unsigned int next_head = (queue->head + 1) % QUEUE_PROC_REQS_SIZE; | |
242 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4105 times.
|
4106 | if (__builtin_expect((next_head == queue->tail), 0)) { |
243 | 1 | return DLB_ERR_REQST; | |
244 | } | ||
245 | |||
246 | /* Otherwise, push new entry */ | ||
247 | 4105 | queue->queue[queue->head].pid = pid; | |
248 | 4105 | queue->queue[queue->head].howmany = howmany; | |
249 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4101 times.
|
4105 | if (allowed) { |
250 | 4 | memcpy(&queue->queue[queue->head].allowed, allowed, sizeof(*allowed)); | |
251 | } else { | ||
252 | 4101 | memset(&queue->queue[queue->head].allowed,0xff, sizeof(*allowed)); | |
253 | } | ||
254 | 4105 | queue->head = next_head; | |
255 | |||
256 | 4105 | return DLB_NOTED; | |
257 | } | ||
258 | |||
259 | ✗ | void queue_proc_reqs_pop(queue_proc_reqs_t *queue, process_request_t *request) { | |
260 | /* If front is valid, extract request */ | ||
261 | ✗ | process_request_t *front = queue_proc_reqs_front(queue); | |
262 | ✗ | if (front->pid != NOBODY) { | |
263 | ✗ | if (request != NULL) { | |
264 | /* Copy request if argument is valid */ | ||
265 | ✗ | memcpy(request, front, sizeof(process_request_t)); | |
266 | } | ||
267 | /* Clear request */ | ||
268 | ✗ | memset(front, 0, sizeof(process_request_t)); | |
269 | } | ||
270 | |||
271 | /* Advance tail as long as it points to invalid elements */ | ||
272 | ✗ | while(queue->head != queue->tail | |
273 | ✗ | && queue->queue[queue->tail].pid == NOBODY) { | |
274 | ✗ | queue->tail = (queue->tail + 1) % QUEUE_PROC_REQS_SIZE; | |
275 | } | ||
276 | } | ||
277 | |||
278 | 45166 | void queue_proc_reqs_get(queue_proc_reqs_t *queue, pid_t *pid, int cpuid) { | |
279 | 45166 | *pid = NOBODY; | |
280 | 45166 | unsigned int pointer = queue->tail; | |
281 |
4/4✓ Branch 0 taken 90226 times.
✓ Branch 1 taken 109 times.
✓ Branch 2 taken 45169 times.
✓ Branch 3 taken 45057 times.
|
90335 | while(pointer != queue->head && *pid == NOBODY) { |
282 | 45169 | process_request_t *request = &queue->queue[pointer]; | |
283 |
2/2✓ Branch 0 taken 45166 times.
✓ Branch 1 taken 3 times.
|
45169 | if (request->pid != NOBODY |
284 |
5/6✓ Branch 0 taken 45166 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 45162 times.
✓ Branch 3 taken 4 times.
✓ Branch 4 taken 45162 times.
✓ Branch 5 taken 4 times.
|
45166 | && CPU_ISSET(cpuid, &request->allowed)) { |
285 | /* request is valid */ | ||
286 | 45162 | *pid = request->pid; | |
287 |
2/2✓ Branch 0 taken 4101 times.
✓ Branch 1 taken 41061 times.
|
45162 | if (--request->howmany == 0) { |
288 | 4101 | request->pid = NOBODY; | |
289 | 4101 | CPU_ZERO(&request->allowed); | |
290 | } | ||
291 | } | ||
292 | |||
293 | /* Advance tail if possible */ | ||
294 |
2/2✓ Branch 0 taken 45164 times.
✓ Branch 1 taken 5 times.
|
45169 | if (pointer == queue->tail |
295 |
2/2✓ Branch 0 taken 4099 times.
✓ Branch 1 taken 41065 times.
|
45164 | && request->pid == NOBODY) { |
296 | 4099 | queue->tail = (queue->tail + 1) % QUEUE_PROC_REQS_SIZE; | |
297 | } | ||
298 | |||
299 | /* Advance pointer */ | ||
300 | 45169 | pointer = (pointer + 1) % QUEUE_PROC_REQS_SIZE; | |
301 | } | ||
302 | |||
303 | /* Advance tail as long as it points to invalid elements */ | ||
304 | 45166 | while(queue->head != queue->tail | |
305 |
4/4✓ Branch 0 taken 45165 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 45160 times.
|
45171 | && queue->queue[queue->tail].pid == NOBODY) { |
306 | 5 | queue->tail = (queue->tail + 1) % QUEUE_PROC_REQS_SIZE; | |
307 | } | ||
308 | 45166 | } | |
309 | |||
310 | /*********************************************************************************/ | ||
311 | |||
312 | /*** queue_pids_t ****************************************************************/ | ||
313 | |||
314 | 1 | void queue_pids_init(queue_pids_t *queue) { | |
315 | 1 | memset(queue, 0, sizeof(*queue)); | |
316 | 1 | } | |
317 | |||
318 | 26 | unsigned int queue_pids_size(const queue_pids_t *queue) { | |
319 | 26 | return queue->head >= queue->tail | |
320 | 20 | ? queue->head - queue->tail | |
321 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 6 times.
|
26 | : QUEUE_PIDS_SIZE - queue->tail + queue->head; |
322 | } | ||
323 | |||
324 | 5 | void queue_pids_remove(queue_pids_t *queue, pid_t pid) { | |
325 | unsigned int i; | ||
326 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 5 times.
|
25 | for (i=queue->tail; i!=queue->head; i=(i+1)%QUEUE_PIDS_SIZE) { |
327 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 11 times.
|
20 | if (queue->queue[i] == pid) { |
328 | 9 | queue->queue[i] = NOBODY; | |
329 | } | ||
330 | /* Advance tail if it points to invalid elements */ | ||
331 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 8 times.
|
20 | if (queue->queue[i] == NOBODY |
332 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 3 times.
|
12 | && i == queue->tail) { |
333 | 9 | queue->tail = (queue->tail+1) % QUEUE_PIDS_SIZE; | |
334 | } | ||
335 | } | ||
336 | 5 | } | |
337 | |||
338 | 13 | int queue_pids_push(queue_pids_t *queue, pid_t pid) { | |
339 | 13 | unsigned int next_head = (queue->head + 1) % QUEUE_PIDS_SIZE; | |
340 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
|
13 | if (__builtin_expect((next_head == queue->tail), 0)) { |
341 | 1 | return DLB_ERR_REQST; | |
342 | } | ||
343 | 12 | queue->queue[queue->head] = pid; | |
344 | 12 | queue->head = next_head; | |
345 | 12 | return DLB_NOTED; | |
346 | } | ||
347 | |||
348 | 7 | void queue_pids_pop(queue_pids_t *queue, pid_t *pid) { | |
349 | 7 | *pid = NOBODY; | |
350 |
4/4✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 6 times.
|
14 | while(queue->head != queue->tail && *pid == NOBODY) { |
351 | 7 | *pid = queue->queue[queue->tail]; | |
352 | 7 | queue->queue[queue->tail] = NOBODY; | |
353 | 7 | queue->tail = (queue->tail + 1) % QUEUE_PIDS_SIZE; | |
354 | } | ||
355 | 7 | } | |
356 | /*********************************************************************************/ | ||
357 |