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 : :
41 : : #include "debug.h"
42 : : #include "tapdisk.h"
43 : : #include "scheduler.h"
44 : : #include "tapdisk-log.h"
45 : : #include "timeout-math.h"
46 : :
47 : : #define DBG(_f, _a...) if (0) { tlog_syslog(TLOG_DBG, _f, ##_a); }
48 : : #define BUG_ON(_cond) if (_cond) td_panic()
49 : :
50 : : #define SCHEDULER_MAX_TIMEOUT 600
51 : : #define SCHEDULER_POLL_FD (SCHEDULER_POLL_READ_FD | \
52 : : SCHEDULER_POLL_WRITE_FD | \
53 : : SCHEDULER_POLL_EXCEPT_FD)
54 : :
55 : : #define MIN(a, b) ((a) <= (b) ? (a) : (b))
56 : : #define MAX(a, b) ((a) >= (b) ? (a) : (b))
57 : :
58 : : /**
59 : : * Async-signal safe.
60 : : */
61 : : #define scheduler_for_each_event(s, event) \
62 : : list_for_each_entry(event, &(s)->events, next)
63 : :
64 : : #define scheduler_for_each_event_safe(s, event, tmp) \
65 : : list_for_each_entry_safe(event, tmp, &(s)->events, next)
66 : :
67 : : typedef struct event {
68 : : char mode;
69 : : char dead;
70 : : char pending;
71 : : char masked;
72 : :
73 : : event_id_t id;
74 : :
75 : : int fd;
76 : :
77 : : /**
78 : : * Timeout relative to the time of the registration
79 : : * of the event. Use the special value {(time_t)-1, 0} to indicate infinity.
80 : : */
81 : : struct timeval timeout;
82 : :
83 : : /**
84 : : * Expiration date in seconds after Epoch. Once current time
85 : : * becomes larger than or equal to this value, the event is considered
86 : : * expired and can be run. If event.timeout is set to infinity, this member
87 : : * should not be used.
88 : : *
89 : : */
90 : : struct timeval deadline;
91 : :
92 : : event_cb_t cb;
93 : : void *private;
94 : :
95 : : struct list_head next;
96 : : } event_t;
97 : :
98 : : static void
99 : 23 : scheduler_prepare_events(scheduler_t *s)
100 : : {
101 : : struct timeval diff;
102 : : struct timeval now;
103 : : event_t *event;
104 : :
105 : 23 : FD_ZERO(&s->read_fds);
106 : 23 : FD_ZERO(&s->write_fds);
107 : 23 : FD_ZERO(&s->except_fds);
108 : :
109 : 23 : s->max_fd = -1;
110 : 23 : s->timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
111 : :
112 : 23 : gettimeofday(&now, NULL);
113 : :
114 [ + + ]: 46 : scheduler_for_each_event(s, event) {
115 [ + + ]: 23 : if (event->masked || event->dead)
116 : 3 : continue;
117 : :
118 [ + + ][ + + ]: 20 : if ((event->mode & SCHEDULER_POLL_READ_FD) && event->fd >= 0) {
119 : 4 : FD_SET(event->fd, &s->read_fds);
120 : 4 : s->max_fd = MAX(event->fd, s->max_fd);
121 : : }
122 : :
123 [ + + ][ + + ]: 20 : if ((event->mode & SCHEDULER_POLL_WRITE_FD) && event->fd >= 0) {
124 : 4 : FD_SET(event->fd, &s->write_fds);
125 : 4 : s->max_fd = MAX(event->fd, s->max_fd);
126 : : }
127 : :
128 [ + + ][ + + ]: 20 : if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) && event->fd >= 0) {
129 : 2 : FD_SET(event->fd, &s->except_fds);
130 : 2 : s->max_fd = MAX(event->fd, s->max_fd);
131 : : }
132 : :
133 [ + + ]: 20 : if (event->mode & SCHEDULER_POLL_TIMEOUT
134 [ + + ]: 7 : && !TV_IS_INF(event->timeout)) {
135 [ - + ]: 6 : TV_SUB(event->deadline, now, diff);
136 [ + + ][ + + ]: 6 : if (TV_AFTER(diff, TV_ZERO))
137 [ - + ][ + + ]: 4 : s->timeout = TV_MIN(s->timeout, diff);
138 : : else
139 : 2 : s->timeout = TV_ZERO;
140 : : }
141 : : }
142 : :
143 [ + + ][ + + ]: 23 : s->timeout = TV_MIN(s->timeout, s->max_timeout);
144 : 23 : }
145 : :
146 : : static int
147 : 3 : scheduler_check_fd_events(scheduler_t *s, int nfds)
148 : : {
149 : : event_t *event;
150 : :
151 [ + + ]: 6 : scheduler_for_each_event(s, event) {
152 [ + - ]: 3 : if (!nfds)
153 : : break;
154 : :
155 [ - + ]: 3 : if (event->dead)
156 : 0 : continue;
157 : :
158 [ + + ][ + - ]: 3 : if ((event->mode & SCHEDULER_POLL_READ_FD) &&
159 : 1 : FD_ISSET(event->fd, &s->read_fds)) {
160 : 1 : FD_CLR(event->fd, &s->read_fds);
161 : 1 : event->pending |= SCHEDULER_POLL_READ_FD;
162 : 1 : --nfds;
163 : : }
164 : :
165 [ + + ][ + - ]: 3 : if ((event->mode & SCHEDULER_POLL_WRITE_FD) &&
166 : 2 : FD_ISSET(event->fd, &s->write_fds)) {
167 : 2 : FD_CLR(event->fd, &s->write_fds);
168 : 2 : event->pending |= SCHEDULER_POLL_WRITE_FD;
169 : 2 : --nfds;
170 : : }
171 : :
172 [ - + ][ # # ]: 3 : if ((event->mode & SCHEDULER_POLL_EXCEPT_FD) &&
173 : 0 : FD_ISSET(event->fd, &s->except_fds)) {
174 : 0 : FD_CLR(event->fd, &s->except_fds);
175 : 0 : event->pending |= SCHEDULER_POLL_EXCEPT_FD;
176 : 0 : --nfds;
177 : : }
178 : : }
179 : :
180 : 3 : return nfds;
181 : : }
182 : :
183 : : /**
184 : : * Checks all scheduler events whose mode is set to SCHEDULER_POLL_TIMEOUT
185 : : * whether their time out has elapsed, and if so it makes them runnable.
186 : : */
187 : : static void
188 : 9 : scheduler_check_timeouts(scheduler_t *s)
189 : : {
190 : : struct timeval now;
191 : : event_t *event;
192 : :
193 : 9 : gettimeofday(&now, NULL);
194 : :
195 [ + + ]: 22 : scheduler_for_each_event(s, event) {
196 [ + + ][ - + ]: 13 : BUG_ON(event->pending && event->masked);
197 : :
198 [ + + ]: 13 : if (event->dead)
199 : 2 : continue;
200 : :
201 [ + + ]: 11 : if (event->pending)
202 : 4 : continue;
203 : :
204 [ + + ]: 7 : if (!(event->mode & SCHEDULER_POLL_TIMEOUT))
205 : 4 : continue;
206 : :
207 [ + + ]: 3 : if (TV_IS_INF(event->timeout))
208 : 1 : continue;
209 : :
210 [ - + ][ + + ]: 2 : if (TV_BEFORE(now, event->deadline))
211 : 1 : continue;
212 : :
213 : 1 : event->pending = SCHEDULER_POLL_TIMEOUT;
214 : : }
215 : 9 : }
216 : :
217 : : static int
218 : 8 : scheduler_check_events(scheduler_t *s, int nfds)
219 : : {
220 [ + + ]: 8 : if (nfds)
221 : 3 : nfds = scheduler_check_fd_events(s, nfds);
222 : :
223 : 8 : scheduler_check_timeouts(s);
224 : :
225 : 8 : return nfds;
226 : : }
227 : :
228 : : static void
229 : 7 : scheduler_event_callback(event_t *event, char mode)
230 : : {
231 [ + + ]: 7 : if (event->mode & SCHEDULER_POLL_TIMEOUT
232 [ + - ]: 4 : && !TV_IS_INF(event->timeout)) {
233 : : struct timeval now;
234 : 4 : gettimeofday(&now, NULL);
235 [ - + ]: 4 : TV_ADD(now, event->timeout, event->deadline);
236 : : }
237 : :
238 [ + + ]: 7 : if (!event->masked)
239 : 6 : event->cb(event->id, mode, event->private);
240 : 7 : }
241 : :
242 : : static int
243 : 13 : scheduler_run_events(scheduler_t *s)
244 : : {
245 : : event_t *event;
246 : 13 : int n_dispatched = 0;
247 : :
248 [ + + ]: 24 : scheduler_for_each_event(s, event) {
249 : : char pending;
250 : :
251 [ + + ]: 11 : if (event->dead)
252 : 2 : continue;
253 : :
254 : 9 : pending = event->pending;
255 [ + + ]: 9 : if (pending) {
256 : 5 : event->pending = 0;
257 : : /* NB. must clear before cb */
258 : 5 : scheduler_event_callback(event, pending);
259 : 5 : n_dispatched++;
260 : : }
261 : : }
262 : :
263 : 13 : return n_dispatched;
264 : : }
265 : :
266 : : int
267 : 56 : scheduler_get_event_uuid(scheduler_t *s) {
268 : :
269 : : int uuid_found;
270 : : event_t *event;
271 : :
272 [ + + ]: 56 : if(unlikely(s->uuid < 0)) {
273 : 2 : EPRINTF("scheduler uuid overflow detected");
274 : 2 : s->uuid = 1;
275 : 2 : s->uuid_overflow = 1;
276 : : }
277 : :
278 [ + + ]: 56 : if(unlikely(s->uuid_overflow == 1)) {
279 : : do {
280 : 5 : uuid_found = 1;
281 [ + + ]: 10 : scheduler_for_each_event(s, event) {
282 [ + + ]: 7 : if(event->id == s->uuid) {
283 : 2 : uuid_found = 0;
284 : 2 : s->uuid++;
285 [ - + ]: 2 : if(s->uuid < 0)
286 : 0 : s->uuid = 1;
287 : : break;
288 : : }
289 : : }
290 : :
291 [ + + ]: 5 : } while(!uuid_found);
292 : : }
293 : :
294 : 56 : return s->uuid++;
295 : : }
296 : :
297 : : int
298 : 53 : scheduler_register_event(scheduler_t *s, char mode, int fd,
299 : : struct timeval timeout, event_cb_t cb, void *private)
300 : : {
301 : : event_t *event;
302 : : struct timeval now;
303 : :
304 [ + + ]: 53 : if (!cb)
305 : 0 : return -EINVAL;
306 : :
307 [ + + ]: 52 : if (!(mode & SCHEDULER_POLL_TIMEOUT) && !(mode & SCHEDULER_POLL_FD))
308 : : return -EINVAL;
309 : :
310 : 51 : event = calloc(1, sizeof(event_t));
311 [ + - ]: 51 : if (!event)
312 : : return -ENOMEM;
313 : :
314 : 51 : gettimeofday(&now, NULL);
315 : :
316 : 51 : INIT_LIST_HEAD(&event->next);
317 : :
318 : 51 : event->mode = mode;
319 : 51 : event->fd = fd;
320 : 51 : event->timeout = timeout;
321 [ + + ]: 51 : if (TV_IS_INF(event->timeout))
322 : : /* initialise it to something meaningful */
323 : 2 : event->deadline = TV_INF;
324 : : else
325 [ - + ]: 49 : TV_ADD(now, timeout, event->deadline);
326 : 51 : event->cb = cb;
327 : 51 : event->private = private;
328 : 51 : event->id = scheduler_get_event_uuid(s);
329 : 51 : event->masked = 0;
330 : :
331 : 51 : list_add_tail(&event->next, &s->events);
332 : :
333 : 51 : return event->id;
334 : : }
335 : :
336 : : void
337 : 4 : scheduler_unregister_event(scheduler_t *s, event_id_t id)
338 : : {
339 : : event_t *event;
340 : :
341 [ + + ]: 4 : if (!id)
342 : 4 : return;
343 : :
344 [ + - ]: 4 : scheduler_for_each_event(s, event)
345 [ + + ]: 4 : if (event->id == id) {
346 : 3 : event->dead = 1;
347 : 3 : break;
348 : : }
349 : : }
350 : :
351 : : void
352 : 4 : scheduler_mask_event(scheduler_t *s, event_id_t id, int masked)
353 : : {
354 : : event_t *event;
355 : :
356 [ + + ]: 4 : if (!id)
357 : 4 : return;
358 : :
359 [ + - ]: 3 : scheduler_for_each_event(s, event)
360 [ + - ]: 3 : if (event->id == id) {
361 : 3 : event->masked = !!masked;
362 : 3 : break;
363 : : }
364 : : }
365 : :
366 : : static void
367 : 9 : scheduler_gc_events(scheduler_t *s)
368 : : {
369 : : event_t *event, *next;
370 : :
371 [ + + ]: 19 : scheduler_for_each_event_safe(s, event, next)
372 [ + + ]: 10 : if (event->dead) {
373 : 2 : list_del(&event->next);
374 : 2 : free(event);
375 : : }
376 : 9 : }
377 : :
378 : : void
379 : 7 : scheduler_set_max_timeout(scheduler_t *s, struct timeval timeout)
380 : : {
381 [ + + ]: 7 : if (!TV_IS_INF(timeout))
382 [ - + ][ + + ]: 5 : s->max_timeout = TV_MIN(s->max_timeout, timeout);
383 : 7 : }
384 : :
385 : : int
386 : 8 : scheduler_wait_for_events(scheduler_t *s)
387 : : {
388 : : int ret;
389 : : struct timeval tv;
390 : :
391 : 8 : s->depth++;
392 : 8 : ret = 0;
393 : :
394 [ - + ][ # # ]: 8 : if (s->depth > 1 && scheduler_run_events(s))
395 : : /* NB. recursive invocations continue with the pending
396 : : * event set. We return as soon as we made some
397 : : * progress. */
398 : : goto out;
399 : :
400 : 8 : scheduler_prepare_events(s);
401 : :
402 : 8 : tv = s->timeout;
403 : :
404 : : DBG("timeout: %ld.%ld, max_timeout: %ld.%ld\n",
405 : : s->timeout.tv_sec, s->timeout.tv_usec, s->max_timeout.tv_sec, s->max_timeout.tv_usec);
406 : :
407 : : do {
408 : 8 : ret = select(s->max_fd + 1, &s->read_fds, &s->write_fds,
409 : : &s->except_fds, &tv);
410 [ - + ]: 8 : if (ret < 0) {
411 : 0 : ret = -errno;
412 [ # # ]: 0 : ASSERT(ret);
413 : : }
414 [ - + ]: 8 : } while (ret == -EINTR);
415 : :
416 [ - + ]: 8 : if (ret < 0) {
417 : 0 : EPRINTF("select failed: %s\n", strerror(-ret));
418 : 0 : goto out;
419 : : }
420 : :
421 : 8 : ret = scheduler_check_events(s, ret);
422 [ - + ]: 8 : BUG_ON(ret);
423 : :
424 : 8 : s->timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
425 : 8 : s->max_timeout = TV_SECS(SCHEDULER_MAX_TIMEOUT);
426 : :
427 : 8 : scheduler_run_events(s);
428 : :
429 [ + - ]: 8 : if (s->depth == 1)
430 : 8 : scheduler_gc_events(s);
431 : :
432 : : out:
433 : 8 : s->depth--;
434 : :
435 : 8 : return ret;
436 : : }
437 : :
438 : : void
439 : 45 : scheduler_initialize(scheduler_t *s)
440 : : {
441 : 45 : memset(s, 0, sizeof(scheduler_t));
442 : :
443 : 45 : s->uuid = 1;
444 : 45 : s->depth = 0;
445 : 45 : s->uuid_overflow = 0;
446 : :
447 : 45 : FD_ZERO(&s->read_fds);
448 : 45 : FD_ZERO(&s->write_fds);
449 : 45 : FD_ZERO(&s->except_fds);
450 : :
451 : 45 : INIT_LIST_HEAD(&s->events);
452 : 45 : }
453 : :
454 : : int
455 : 5 : scheduler_event_set_timeout(scheduler_t *sched, event_id_t event_id, struct timeval timeo)
456 : : {
457 : : event_t *event;
458 : :
459 [ - + ]: 5 : ASSERT(sched);
460 : :
461 [ + + ]: 5 : if (!event_id)
462 : : return -EINVAL;
463 : :
464 [ + + ]: 6 : scheduler_for_each_event(sched, event) {
465 [ + + ]: 5 : if (event->id == event_id) {
466 [ + + ]: 3 : if (!(event->mode & SCHEDULER_POLL_TIMEOUT))
467 : : return -EINVAL;
468 : 2 : event->timeout = timeo;
469 [ + + ]: 2 : if (TV_IS_INF(event->timeout))
470 : 1 : event->deadline = TV_INF;
471 : : else {
472 : : struct timeval now;
473 : 1 : gettimeofday(&now, NULL);
474 [ - + ]: 1 : TV_ADD(now, event->timeout, event->deadline);
475 : : }
476 : : return 0;
477 : : }
478 : : }
479 : :
480 : : return -ENOENT;
481 : : }
482 : :
|