Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : #ifdef HAVE_CONFIG_H
32 : : #include "config.h"
33 : : #endif
34 : :
35 : : #include <errno.h>
36 : : #include <stdlib.h>
37 : : #include <unistd.h>
38 : : #include <string.h>
39 : : #include <sys/time.h>
40 : : #include <limits.h>
41 : :
42 : : #include "debug.h"
43 : : #include "tapdisk.h"
44 : : #include "scheduler.h"
45 : : #include "tapdisk-log.h"
46 : : #include "timeout-math.h"
47 : :
48 : : #define DBG(_f, _a...) if (0) { tlog_syslog(TLOG_DBG, _f, ##_a); }
49 : : #define BUG_ON(_cond) if (_cond) td_panic()
50 : :
51 : : #define SCHEDULER_MAX_TIMEOUT 600
52 : : #define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
53 : : SCHEDULER_POLL_WRITE_FD | \
54 : : SCHEDULER_POLL_EXCEPT_FD)
55 : :
56 : : #define MIN(a, b) ((a) <= (b) ? (a) : (b))
57 : : #define MAX(a, b) ((a) >= (b) ? (a) : (b))
58 : :
59 : : /**
60 : : * Async-signal safe.
61 : : */
62 : : #define scheduler_for_each_event(s, event) \
63 : : list_for_each_entry(event, &(s)->events, next)
64 : :
65 : : #define scheduler_for_each_event_safe(s, event, tmp) \
66 : : list_for_each_entry_safe(event, tmp, &(s)->events, next)
67 : :
68 : : typedef struct event {
69 : : char mode;
70 : : char dead;
71 : : char pending;
72 : : char masked;
73 : :
74 : : event_id_t id;
75 : :
76 : : int fd;
77 : :
78 : : /**
79 : : * Timeout relative to the time of the registration
80 : : * of the event. Use the special value {(time_t)-1, 0} to indicate infinity.
81 : : */
82 : : struct timeval timeout;
83 : :
84 : : /**
85 : : * Expiration date in seconds after Epoch. Once current time
86 : : * becomes larger than or equal to this value, the event is considered
87 : : * expired and can be run. If event.timeout is set to infinity, this member
88 : : * should not be used.
89 : : *
90 : : */
91 : : struct timeval deadline;
92 : :
93 : : event_cb_t cb;
94 : : void *private;
95 : :
96 : : struct list_head next;
97 : : } event_t;
98 : :
99 : : static void
100 : 31 : scheduler_prepare_events(scheduler_t *s)
101 : : {
102 : : struct timeval diff;
103 : : struct timeval now;
104 : : event_t *event;
105 : :
106 : 31 : FD_ZERO(&s->read_fds);
107 : 31 : FD_ZERO(&s->write_fds);
108 : 31 : FD_ZERO(&s->except_fds);
109 : :
110 : 31 : s->max_fd = -1;
111 : 31 : s->timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
112 : :
113 : 31 : gettimeofday(&now, NULL);
114 : :
115 [ + + ]: 70 : scheduler_for_each_event(s, event) {
116 [ + + ]: 39 : if (event->masked || event->dead)
117 : 6 : continue;
118 : :
119 [ + + ][ + + ]: 33 : if ((event->mode & SCHEDULER_POLL_READ_FD) && event->fd >= 0) {
120 [ - + ][ # # ]: 6 : FD_SET(event->fd, &s->read_fds);
121 : 6 : s->max_fd = MAX(event->fd, s->max_fd);
122 : : }
123 : :
124 [ + + ][ + + ]: 33 : if ((event->mode & SCHEDULER_POLL_WRITE_FD) && event->fd >= 0) {
125 [ - + ][ # # ]: 15 : FD_SET(event->fd, &s->write_fds);
126 : 15 : s->max_fd = MAX(event->fd, s->max_fd);
127 : : }
128 : :
129 [ + + ][ + + ]: 33 : if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) && event->fd >= 0) {
130 [ - + ][ # # ]: 2 : FD_SET(event->fd, &s->except_fds);
131 : 2 : s->max_fd = MAX(event->fd, s->max_fd);
132 : : }
133 : :
134 [ + + ]: 33 : if (event->mode & SCHEDULER_POLL_TIMEOUT
135 [ + + ]: 7 : && !TV_IS_INF(event->timeout)) {
136 [ - + ]: 6 : TV_SUB(event->deadline, now, diff);
137 [ + + ][ + + ]: 6 : if (TV_AFTER(diff, TV_ZERO))
138 [ - + ][ + + ]: 4 : s->timeout = TV_MIN(s->timeout, diff);
139 : : else
140 : 2 : s->timeout = TV_ZERO;
141 : : }
142 : : }
143 : :
144 [ + + ][ + + ]: 31 : s->timeout = TV_MIN(s->timeout, s->max_timeout);
145 : 31 : }
146 : :
147 : : static int
148 : 11 : scheduler_check_fd_events(scheduler_t *s, int nfds)
149 : : {
150 : : event_t *event;
151 : :
152 [ + + ]: 27 : scheduler_for_each_event(s, event) {
153 [ + + ]: 19 : if (!nfds)
154 : : break;
155 : :
156 [ + + ]: 16 : if (event->dead)
157 : 2 : continue;
158 : :
159 [ + + ][ + - ]: 17 : if ((event->mode & SCHEDULER_POLL_READ_FD) &&
160 [ - + ][ # # ]: 3 : FD_ISSET(event->fd, &s->read_fds)) {
161 [ - + ][ # # ]: 3 : FD_CLR(event->fd, &s->read_fds);
162 : 3 : event->pending |= SCHEDULER_POLL_READ_FD;
163 : 3 : --nfds;
164 : : }
165 : :
166 [ + + ][ + - ]: 25 : if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
167 [ - + ][ # # ]: 11 : FD_ISSET(event->fd, &s->write_fds)) {
168 [ - + ][ # # ]: 11 : FD_CLR(event->fd, &s->write_fds);
169 : 11 : event->pending |= SCHEDULER_POLL_WRITE_FD;
170 : 11 : --nfds;
171 : : }
172 : :
173 [ - + ][ # # ]: 14 : if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
174 [ # # ][ # # ]: 0 : FD_ISSET(event->fd, &s->except_fds)) {
175 [ # # ][ # # ]: 0 : FD_CLR(event->fd, &s->except_fds);
176 : 0 : event->pending |= SCHEDULER_POLL_EXCEPT_FD;
177 : 0 : --nfds;
178 : : }
179 : : }
180 : :
181 : 11 : return nfds;
182 : : }
183 : :
184 : : /**
185 : : * Checks all scheduler events whose mode is set to SCHEDULER_POLL_TIMEOUT
186 : : * whether their time out has elapsed, and if so it makes them runnable.
187 : : */
188 : : static void
189 : 17 : scheduler_check_timeouts(scheduler_t *s)
190 : : {
191 : : struct timeval now;
192 : : event_t *event;
193 : :
194 : 17 : gettimeofday(&now, NULL);
195 : :
196 [ + + ]: 46 : scheduler_for_each_event(s, event) {
197 [ + + ][ - + ]: 29 : BUG_ON(event->pending && event->masked);
198 : :
199 [ + + ]: 29 : if (event->dead)
200 : 5 : continue;
201 : :
202 [ + + ]: 24 : if (event->pending)
203 : 15 : continue;
204 : :
205 [ + + ]: 9 : if (!(event->mode & SCHEDULER_POLL_TIMEOUT))
206 : 6 : continue;
207 : :
208 [ + + ]: 3 : if (TV_IS_INF(event->timeout))
209 : 1 : continue;
210 : :
211 [ - + ][ + + ]: 2 : if (TV_BEFORE(now, event->deadline))
212 : 1 : continue;
213 : :
214 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
215 : : }
216 : 17 : }
217 : :
218 : : static int
219 : 16 : scheduler_check_events(scheduler_t *s, int nfds)
220 : : {
221 [ + + ]: 16 : if (nfds)
222 : 11 : nfds = scheduler_check_fd_events(s, nfds);
223 : :
224 : 16 : scheduler_check_timeouts(s);
225 : :
226 : 16 : return nfds;
227 : : }
228 : :
229 : : static void
230 : 18 : scheduler_event_callback(event_t *event, char mode)
231 : : {
232 [ + + ]: 18 : if (event->mode & SCHEDULER_POLL_TIMEOUT
233 [ + - ]: 4 : && !TV_IS_INF(event->timeout)) {
234 : : struct timeval now;
235 : 4 : gettimeofday(&now, NULL);
236 [ - + ]: 4 : TV_ADD(now, event->timeout, event->deadline);
237 : : }
238 : :
239 [ + + ]: 18 : if (!event->masked)
240 : 17 : event->cb(event->id, mode, event->private);
241 : 18 : }
242 : :
243 : : static int
244 : 21 : scheduler_run_events(scheduler_t *s)
245 : : {
246 : : event_t *event;
247 : 21 : int n_dispatched = 0;
248 : :
249 [ + + ]: 48 : scheduler_for_each_event(s, event) {
250 : : char pending;
251 : :
252 [ + + ]: 27 : if (event->dead)
253 : 5 : continue;
254 : :
255 : 22 : pending = event->pending;
256 [ + + ]: 22 : if (pending) {
257 : 16 : event->pending = 0;
258 : : /* NB. must clear before cb */
259 : 16 : scheduler_event_callback(event, pending);
260 : 16 : n_dispatched++;
261 : : }
262 : : }
263 : :
264 : 21 : return n_dispatched;
265 : : }
266 : :
267 : : event_id_t
268 : 64 : scheduler_get_event_uuid(scheduler_t *s) {
269 : :
270 : 64 : bool uuid_found = false;
271 : : event_id_t ret;
272 : : event_t *event;
273 : :
274 [ + + ]: 64 : if (unlikely(s->uuid <= 0)) {
275 : 1 : s->uuid = 1;
276 : 1 : s->uuid_overflow = 1;
277 : : }
278 : :
279 [ + + ]: 64 : if(unlikely(s->uuid_overflow == 1)) {
280 : : do {
281 : 5 : uuid_found = true;
282 [ + + ]: 10 : scheduler_for_each_event(s, event) {
283 [ + + ]: 7 : if(event->id == s->uuid) {
284 : 2 : uuid_found = false;
285 [ - + ]: 2 : if (unlikely(s->uuid == INT_MAX)) {
286 : 0 : s->uuid = 1;
287 : : } else {
288 : 2 : s->uuid++;
289 : : }
290 : : break;
291 : : }
292 : : }
293 : :
294 [ + + ]: 5 : } while(!uuid_found);
295 : : }
296 : :
297 : 64 : ret = s->uuid;
298 [ + + ]: 64 : if (unlikely(s->uuid == INT_MAX)) {
299 : : /* overflowing */
300 : : EPRINTF("scheduler uuid overflow detected");
301 : 1 : s->uuid_overflow = 1;
302 : 1 : s->uuid = 1;
303 : : } else {
304 : 63 : s->uuid += 1;
305 : : }
306 : :
307 : 64 : return ret;
308 : : }
309 : :
310 : : int
311 : 61 : scheduler_register_event(scheduler_t *s, char mode, int fd,
312 : : struct timeval timeout, event_cb_t cb, void *private)
313 : : {
314 : : event_t *event;
315 : : struct timeval now;
316 : :
317 [ + + ]: 61 : if (!cb)
318 : 61 : return -EINVAL;
319 : :
320 [ + + ]: 60 : if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
321 : : return -EINVAL;
322 : :
323 : 59 : event = calloc(1, sizeof(event_t));
324 [ + - ]: 59 : if (!event)
325 : : return -ENOMEM;
326 : :
327 : 59 : gettimeofday(&now, NULL);
328 : :
329 : 59 : INIT_LIST_HEAD(&event->next);
330 : :
331 : 59 : event->mode = mode;
332 : 59 : event->fd = fd;
333 : 59 : event->timeout = timeout;
334 [ + + ]: 59 : if (TV_IS_INF(event->timeout))
335 : : /* initialise it to something meaningful */
336 : 2 : event->deadline = TV_INF;
337 : : else
338 [ - + ]: 57 : TV_ADD(now, timeout, event->deadline);
339 : 59 : event->cb = cb;
340 : 59 : event->private = private;
341 : 59 : event->id = scheduler_get_event_uuid(s);
342 : 59 : event->masked = 0;
343 : :
344 : 59 : list_add_tail(&event->next, &s->events);
345 : :
346 : 59 : return event->id;
347 : : }
348 : :
349 : : void
350 : 0 : scheduler_unregister_event(scheduler_t *s, event_id_t id)
351 : : {
352 : : event_t *event;
353 : :
354 [ + - + - ]: 67 : if (!id)
[ + - + -
+ - ]
[ + - + - ]
[ + - + -
+ - ]
[ + - + - ]
[ + - + -
+ - + - +
- ][ + - +
- + - ][ +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - +
- ][ + - ]
[ + - ][ + - ]
[ + - ][ + -
+ - + - ]
[ + - ]
[ + - + - ]
[ + - + -
+ - + - +
- + - + -
+ - + - ]
[ + - ][ + -
+ - + - +
- + - +
- ][ + - +
- + - ]
[ # # ]
355 : 0 : return;
356 : :
357 [ + - ][ + + ]: 98 : scheduler_for_each_event(s, event)
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ - + ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ - + ]
[ - + ][ # # ]
358 [ + - ][ - + ]: 91 : if (event->id == id) {
[ + - ][ + + ]
[ + - ][ - + ]
[ + - ][ + + ]
[ + - ][ - + ]
[ + - ][ + - ]
[ # # ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + - ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + + ][ + + ]
[ + - ][ - + ]
[ + + ][ + - ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ # # ]
[ # # ][ # # ]
359 : 60 : event->dead = 1;
360 : 0 : break;
361 : : }
362 : : }
363 : :
364 : : void
365 : 0 : scheduler_mask_event(scheduler_t *s, event_id_t id, int masked)
366 : : {
367 : : event_t *event;
368 : :
369 [ + - + - : 3 : if (!id)
+ - ][ # # ]
370 : 0 : return;
371 : :
372 [ + - ][ + - ]: 3 : scheduler_for_each_event(s, event)
[ + - ][ # # ]
373 [ + - ][ + - ]: 3 : if (event->id == id) {
[ + - ][ # # ]
374 : 3 : event->masked = !!masked;
375 : 0 : break;
376 : : }
377 : : }
378 : :
379 : : static void
380 : 61 : scheduler_gc_events(scheduler_t *s)
381 : : {
382 : : event_t *event, *next;
383 : :
384 [ + + ]: 141 : scheduler_for_each_event_safe(s, event, next)
385 [ + + ]: 80 : if (event->dead) {
386 : 139 : list_del(&event->next);
387 : 59 : free(event);
388 : : }
389 : 61 : }
390 : :
391 : : void
392 : 0 : scheduler_set_max_timeout(scheduler_t *s, struct timeval timeout)
393 : : {
394 [ # # ]: 0 : if (!TV_IS_INF(timeout))
395 [ - + ][ - + ]: 2 : s->max_timeout = TV_MIN(s->max_timeout, timeout);
[ - + ][ - + ]
[ # # ][ # # ]
396 : 0 : }
397 : :
398 : : int
399 : 16 : scheduler_wait_for_events(scheduler_t *s)
400 : : {
401 : : int ret;
402 : : struct timeval tv;
403 : :
404 : 16 : s->depth++;
405 : 16 : ret = 0;
406 : :
407 [ - + ][ # # ]: 16 : if (s->depth > 1 && scheduler_run_events(s))
408 : : /* NB. recursive invocations continue with the pending
409 : : * event set. We return as soon as we made some
410 : : * progress. */
411 : : goto out;
412 : :
413 : 16 : scheduler_prepare_events(s);
414 : :
415 : 16 : tv = s->timeout;
416 : :
417 : : DBG("timeout: %ld.%ld, max_timeout: %ld.%ld\n",
418 : : s->timeout.tv_sec, s->timeout.tv_usec, s->max_timeout.tv_sec, s->max_timeout.tv_usec);
419 : :
420 : : do {
421 : 16 : ret = select(s->max_fd + 1, &s->read_fds, &s->write_fds,
422 : : &s->except_fds, &tv);
423 [ - + ]: 16 : if (ret < 0) {
424 : 0 : ret = -errno;
425 [ # # ]: 0 : ASSERT(ret);
426 : : }
427 [ - + ]: 16 : } while (ret == -EINTR);
428 : :
429 [ - + ]: 16 : if (ret < 0) {
430 : 0 : EPRINTF("select failed: %s\n", strerror(-ret));
431 : : goto out;
432 : : }
433 : :
434 : 16 : ret = scheduler_check_events(s, ret);
435 [ - + ]: 16 : BUG_ON(ret);
436 : :
437 : 16 : s->timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
438 : 16 : s->max_timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
439 : :
440 : 16 : scheduler_run_events(s);
441 : :
442 [ + - ]: 16 : if (s->depth == 1)
443 : 16 : scheduler_gc_events(s);
444 : :
445 : : out:
446 : 16 : s->depth--;
447 : :
448 : 16 : return ret;
449 : : }
450 : :
451 : : void
452 : 49 : scheduler_initialize(scheduler_t *s)
453 : : {
454 : : memset(s, 0, sizeof(scheduler_t));
455 : :
456 : 49 : s->uuid = 1;
457 : 49 : s->depth = 0;
458 : 49 : s->uuid_overflow = 0;
459 : :
460 : 49 : FD_ZERO(&s->read_fds);
461 : 49 : FD_ZERO(&s->write_fds);
462 : 49 : FD_ZERO(&s->except_fds);
463 : :
464 : 49 : INIT_LIST_HEAD(&s->events);
465 : 49 : }
466 : :
467 : : int
468 : 5 : scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct timeval timeo)
469 : : {
470 : : event_t *event;
471 : :
472 [ - + ]: 5 : ASSERT(sched);
473 : :
474 [ + + ]: 5 : if (!event_id)
475 : : return -EINVAL;
476 : :
477 [ + + ]: 6 : scheduler_for_each_event(sched, event) {
478 [ + + ]: 5 : if (event->id == event_id) {
479 [ + + ]: 3 : if (!(event->mode & SCHEDULER_POLL_TIMEOUT))
480 : : return -EINVAL;
481 : 2 : event->timeout = timeo;
482 [ + + ]: 2 : if (TV_IS_INF(event->timeout))
483 : 1 : event->deadline = TV_INF;
484 : : else {
485 : : struct timeval now;
486 : 1 : gettimeofday(&now, NULL);
487 [ - + ]: 1 : TV_ADD(now, event->timeout, event->deadline);
488 : : }
489 : : return 0;
490 : : }
491 : : }
492 : :
493 : : return -ENOENT;
494 : : }
495 : :
|