]> git.ipfire.org Git - thirdparty/bird.git/blame - proto/bfd/io.c
BFD work in progress.
[thirdparty/bird.git] / proto / bfd / io.c
CommitLineData
6a8d3f1c
OZ
1/*
2 * BIRD -- I/O and event loop
3 *
4 * Can be freely distributed and used under the terms of the GNU GPL.
5 */
6
7#include <stdio.h>
8#include <stdlib.h>
9#include <unistd.h>
10#include <errno.h>
11#include <fcntl.h>
12#include <poll.h>
13#include <pthread.h>
14#include <time.h>
15#include <sys/time.h>
16
17#include "nest/bird.h"
18#include "proto/bfd/io.h"
bf139664
OZ
19
20#include "lib/buffer.h"
21#include "lib/heap.h"
6a8d3f1c
OZ
22#include "lib/lists.h"
23#include "lib/resource.h"
24#include "lib/event.h"
25#include "lib/socket.h"
26
bf139664
OZ
27
28struct birdloop
29{
30 pool *pool;
6a8d3f1c
OZ
31 pthread_t thread;
32 pthread_mutex_t mutex;
bf139664 33
6a8d3f1c
OZ
34 btime last_time;
35 btime real_time;
bf139664
OZ
36 u8 use_monotonic_clock;
37
6a8d3f1c
OZ
38 u8 poll_active;
39 u8 wakeup_masked;
40 int wakeup_fds[2];
41
bf139664
OZ
42 BUFFER(timer2 *) timers;
43 list event_list;
44 list sock_list;
45 uint sock_num;
46
47 BUFFER(sock *) poll_sk;
48 BUFFER(struct pollfd) poll_fd;
49 u8 poll_changed;
50 u8 close_scheduled;
bf139664
OZ
51};
52
53
6a8d3f1c
OZ
54
55static pthread_key_t current_loop_key;
56
57static inline struct birdloop *
58birdloop_current(void)
59{
60 return pthread_getspecific(current_loop_key);
61}
62
63static inline void
64birdloop_set_current(struct birdloop *loop)
65{
66 pthread_setspecific(current_loop_key, loop);
67}
68
69static inline void
70birdloop_init_current(void)
71{
72 pthread_key_create(&current_loop_key, NULL);
73}
74
75
76
bf139664
OZ
77static void times_update_alt(struct birdloop *loop);
78
6a8d3f1c 79static void
bf139664
OZ
80times_init(struct birdloop *loop)
81{
82 struct timespec ts;
83 int rv;
84
85 rv = clock_gettime(CLOCK_MONOTONIC, &ts);
86 if (rv < 0)
87 {
88 // log(L_WARN "Monotonic clock is missing");
89
90 loop->use_monotonic_clock = 0;
91 loop->last_time = 0;
92 loop->real_time = 0;
93 times_update_alt(loop);
94 return;
95 }
96
97 /*
6a8d3f1c 98 if ((ts.tv_sec < 0) || (((s64) ts.tv_sec) > ((s64) 1 << 40)))
bf139664
OZ
99 log(L_WARN "Monotonic clock is crazy");
100 */
101
102 loop->use_monotonic_clock = 1;
6a8d3f1c 103 loop->last_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
bf139664
OZ
104 loop->real_time = 0;
105}
106
107static void
108times_update_pri(struct birdloop *loop)
109{
110 struct timespec ts;
111 int rv;
112
113 rv = clock_gettime(CLOCK_MONOTONIC, &ts);
114 if (rv < 0)
115 die("clock_gettime: %m");
116
6a8d3f1c 117 btime new_time = (ts.tv_sec S) + (ts.tv_nsec / 1000);
bf139664
OZ
118
119 /*
120 if (new_time < loop->last_time)
121 log(L_ERR "Monotonic clock is broken");
122 */
123
124 loop->last_time = new_time;
125 loop->real_time = 0;
126}
127
128static void
129times_update_alt(struct birdloop *loop)
130{
131 struct timeval tv;
132 int rv;
133
134 rv = gettimeofday(&tv, NULL);
135 if (rv < 0)
136 die("gettimeofday: %m");
137
6a8d3f1c
OZ
138 btime new_time = (tv.tv_sec S) + tv.tv_usec;
139 btime delta = new_time - loop->real_time;
bf139664
OZ
140
141 if ((delta < 0) || (delta > (60 S)))
142 {
143 /*
144 if (loop->real_time)
145 log(L_WARN "Time jump, delta %d us", (int) delta);
146 */
147
148 delta = 100 MS;
149 }
150
151 loop->last_time += delta;
152 loop->real_time = new_time;
153}
154
155static void
156times_update(struct birdloop *loop)
157{
158 if (loop->use_monotonic_clock)
159 times_update_pri(loop);
160 else
161 times_update_alt(loop);
162}
163
6a8d3f1c
OZ
164btime
165current_time(void)
166{
167 return birdloop_current()->last_time;
168}
169
bf139664
OZ
170
171
172static void
173pipe_new(int *pfds)
174{
6a8d3f1c 175 int rv = pipe(pfds);
bf139664
OZ
176 if (rv < 0)
177 die("pipe: %m");
178
179 if (fcntl(pfds[0], F_SETFL, O_NONBLOCK) < 0)
180 die("fcntl(O_NONBLOCK): %m");
181
182 if (fcntl(pfds[1], F_SETFL, O_NONBLOCK) < 0)
183 die("fcntl(O_NONBLOCK): %m");
184}
185
6a8d3f1c
OZ
186void
187pipe_drain(int fd)
bf139664
OZ
188{
189 char buf[64];
190 int rv;
191
192 try:
6a8d3f1c 193 rv = read(fd, buf, 64);
bf139664
OZ
194 if (rv < 0)
195 {
196 if (errno == EINTR)
197 goto try;
198 if (errno == EAGAIN)
199 return;
200 die("wakeup read: %m");
201 }
202 if (rv == 64)
203 goto try;
204}
205
6a8d3f1c
OZ
206void
207pipe_kick(int fd)
bf139664
OZ
208{
209 u64 v = 1;
210 int rv;
211
212 try:
6a8d3f1c 213 rv = write(fd, &v, sizeof(u64));
bf139664
OZ
214 if (rv < 0)
215 {
216 if (errno == EINTR)
217 goto try;
218 if (errno == EAGAIN)
219 return;
220 die("wakeup write: %m");
221 }
222}
223
6a8d3f1c
OZ
224static inline void
225wakeup_init(struct birdloop *loop)
226{
227 pipe_new(loop->wakeup_fds);
228}
bf139664 229
6a8d3f1c
OZ
230static inline void
231wakeup_drain(struct birdloop *loop)
232{
233 pipe_drain(loop->wakeup_fds[0]);
234}
bf139664 235
6a8d3f1c
OZ
236static inline void
237wakeup_do_kick(struct birdloop *loop)
238{
239 pipe_kick(loop->wakeup_fds[1]);
240}
bf139664 241
6a8d3f1c
OZ
242static inline void
243wakeup_kick(struct birdloop *loop)
244{
245 if (!loop->wakeup_masked)
246 wakeup_do_kick(loop);
247 else
248 loop->wakeup_masked = 2;
249}
bf139664 250
6a8d3f1c
OZ
251
252
253static inline uint
254events_waiting(struct birdloop *loop)
255{
256 return !EMPTY_LIST(loop->event_list);
257}
258
259static inline void
bf139664
OZ
260events_init(struct birdloop *loop)
261{
6a8d3f1c 262 init_list(&loop->event_list);
bf139664
OZ
263}
264
265static void
266events_fire(struct birdloop *loop)
267{
268 times_update(loop);
269 ev_run_list(&loop->event_list);
270}
271
272void
273ev2_schedule(event *e)
274{
6a8d3f1c
OZ
275 struct birdloop *loop = birdloop_current();
276
bf139664
OZ
277 if (loop->poll_active && EMPTY_LIST(loop->event_list))
278 wakeup_kick(loop);
279
280 if (e->n.next)
281 rem_node(&e->n);
282
283 add_tail(&loop->event_list, &e->n);
284}
285
286
6a8d3f1c 287
bf139664
OZ
288#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
289#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
290 heap[a]->index = (a), heap[b]->index = (b))
291
292
293static inline uint timers_count(struct birdloop *loop)
294{ return loop->timers.used - 1; }
295
296static inline timer2 *timers_first(struct birdloop *loop)
297{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
298
299
300static void
301tm2_free(resource *r)
302{
303 timer2 *t = (timer2 *) r;
304
305 tm2_stop(t);
306}
307
308static void
309tm2_dump(resource *r)
310{
311 timer2 *t = (timer2 *) r;
312
313 debug("(code %p, data %p, ", t->hook, t->data);
314 if (t->randomize)
315 debug("rand %d, ", t->randomize);
316 if (t->recurrent)
317 debug("recur %d, ", t->recurrent);
318 if (t->expires)
6a8d3f1c 319 debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
bf139664
OZ
320 else
321 debug("inactive)\n");
322}
323
6a8d3f1c 324
bf139664
OZ
325static struct resclass tm2_class = {
326 "Timer",
6a8d3f1c 327 sizeof(timer2),
bf139664
OZ
328 tm2_free,
329 tm2_dump,
330 NULL,
331 NULL
332};
333
334timer2 *
335tm2_new(pool *p)
336{
337 timer2 *t = ralloc(p, &tm2_class);
338 t->index = -1;
339 return t;
340}
341
342void
6a8d3f1c 343tm2_set(timer2 *t, btime when)
bf139664 344{
6a8d3f1c 345 struct birdloop *loop = birdloop_current();
bf139664
OZ
346 uint tc = timers_count(loop);
347
348 if (!t->expires)
349 {
350 t->index = ++tc;
351 t->expires = when;
352 BUFFER_PUSH(loop->timers) = t;
353 HEAP_INSERT(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP);
354 }
355 else if (t->expires < when)
356 {
357 t->expires = when;
358 HEAP_INCREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
359 }
360 else if (t->expires > when)
361 {
362 t->expires = when;
363 HEAP_DECREASE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
364 }
365
366 if (loop->poll_active && (t->index == 1))
367 wakeup_kick(loop);
368}
369
6a8d3f1c
OZ
370void
371tm2_start(timer2 *t, btime after)
372{
373 tm2_set(t, current_time() + MAX(after, 0));
374}
375
bf139664
OZ
376void
377tm2_stop(timer2 *t)
378{
379 if (!t->expires)
380 return;
381
6a8d3f1c
OZ
382 struct birdloop *loop = birdloop_current();
383 uint tc = timers_count(loop);
384
bf139664
OZ
385 HEAP_DELETE(loop->timers.data, tc, timer2 *, TIMER_LESS, TIMER_SWAP, t->index);
386 BUFFER_POP(loop->timers);
387
388 t->index = -1;
389 t->expires = 0;
390}
391
392static void
393timers_init(struct birdloop *loop)
394{
395 BUFFER_INIT(loop->timers, loop->pool, 4);
396 BUFFER_PUSH(loop->timers) = NULL;
397}
398
399static void
400timers_fire(struct birdloop *loop)
401{
6a8d3f1c 402 btime base_time;
bf139664
OZ
403 timer2 *t;
404
405 times_update(loop);
406 base_time = loop->last_time;
407
408 while (t = timers_first(loop))
409 {
410 if (t->expires > base_time)
411 return;
412
413 if (t->recurrent)
414 {
6a8d3f1c 415 btime when = t->expires + t->recurrent;
bf139664 416
6a8d3f1c
OZ
417 if (when <= loop->last_time)
418 when = loop->last_time + t->recurrent;
bf139664 419
6a8d3f1c
OZ
420 if (t->randomize)
421 when += random() % (t->randomize + 1);
bf139664 422
6a8d3f1c 423 tm2_set(t, when);
bf139664
OZ
424 }
425 else
426 tm2_stop(t);
427
428 t->hook(t);
429 }
430}
431
432
6a8d3f1c 433
bf139664
OZ
434static void
435sockets_init(struct birdloop *loop)
436{
6a8d3f1c
OZ
437 init_list(&loop->sock_list);
438 loop->sock_num = 0;
bf139664
OZ
439
440 BUFFER_INIT(loop->poll_sk, loop->pool, 4);
441 BUFFER_INIT(loop->poll_fd, loop->pool, 4);
6a8d3f1c 442 loop->poll_changed = 1; /* add wakeup fd */
bf139664
OZ
443}
444
445static void
446sockets_add(struct birdloop *loop, sock *s)
447{
448 add_tail(&loop->sock_list, &s->n);
449 loop->sock_num++;
450
451 s->index = -1;
452 loop->poll_changed = 1;
453
454 if (loop->poll_active)
455 wakeup_kick(loop);
456}
457
458void
459sk_start(sock *s)
460{
6a8d3f1c
OZ
461 struct birdloop *loop = birdloop_current();
462
463 sockets_add(loop, s);
bf139664
OZ
464}
465
466static void
467sockets_remove(struct birdloop *loop, sock *s)
468{
469 rem_node(&s->n);
470 loop->sock_num--;
471
472 if (s->index >= 0)
6a8d3f1c 473 loop->poll_sk.data[s->index] = NULL;
bf139664
OZ
474
475 s->index = -1;
476 loop->poll_changed = 1;
477
478 /* Wakeup moved to sk_stop() */
479}
480
481void
482sk_stop(sock *s)
483{
6a8d3f1c
OZ
484 struct birdloop *loop = birdloop_current();
485
486 sockets_remove(loop, s);
bf139664
OZ
487
488 if (loop->poll_active)
489 {
490 loop->close_scheduled = 1;
491 wakeup_kick(loop);
492 }
493 else
494 close(s->fd);
495
496 s->fd = -1;
497}
498
499static inline uint sk_want_events(sock *s)
500{ return (s->rx_hook ? POLLIN : 0) | ((s->ttx != s->tpos) ? POLLOUT : 0); }
501
502static void
503sockets_update(struct birdloop *loop, sock *s)
504{
505 if (s->index >= 0)
6a8d3f1c 506 loop->poll_fd.data[s->index].events = sk_want_events(s);
bf139664
OZ
507}
508
509static void
510sockets_prepare(struct birdloop *loop)
511{
512 BUFFER_SET(loop->poll_sk, loop->sock_num + 1);
513 BUFFER_SET(loop->poll_fd, loop->sock_num + 1);
514
515 struct pollfd *pfd = loop->poll_fd.data;
516 sock **psk = loop->poll_sk.data;
517 int i = 0;
518 node *n;
519
6a8d3f1c 520 WALK_LIST(n, loop->sock_list)
bf139664
OZ
521 {
522 sock *s = SKIP_BACK(sock, n, n);
523
524 ASSERT(i < loop->sock_num);
525
526 s->index = i;
527 *psk = s;
528 pfd->fd = s->fd;
529 pfd->events = sk_want_events(s);
530 pfd->revents = 0;
531
532 pfd++;
533 psk++;
534 i++;
535 }
536
537 ASSERT(i == loop->sock_num);
538
539 /* Add internal wakeup fd */
540 *psk = NULL;
541 pfd->fd = loop->wakeup_fds[0];
542 pfd->events = POLLIN;
543 pfd->revents = 0;
544
545 loop->poll_changed = 0;
546}
547
548static void
549sockets_close_fds(struct birdloop *loop)
550{
551 struct pollfd *pfd = loop->poll_fd.data;
552 sock **psk = loop->poll_sk.data;
553 int poll_num = loop->poll_fd.used - 1;
554
555 int i;
556 for (i = 0; i < poll_num; i++)
557 if (psk[i] == NULL)
558 close(pfd[i].fd);
559
560 loop->close_scheduled = 0;
561}
562
6a8d3f1c
OZ
563int sk_read(sock *s);
564int sk_write(sock *s);
bf139664
OZ
565
566static void
567sockets_fire(struct birdloop *loop)
568{
569 struct pollfd *pfd = loop->poll_fd.data;
570 sock **psk = loop->poll_sk.data;
571 int poll_num = loop->poll_fd.used - 1;
572
573 times_update(loop);
574
575 /* Last fd is internal wakeup fd */
576 if (pfd[loop->sock_num].revents & POLLIN)
577 wakeup_drain(loop);
578
579 int i;
580 for (i = 0; i < poll_num; pfd++, psk++, i++)
581 {
582 int e = 1;
583
584 if (! pfd->revents)
585 continue;
586
587 if (pfd->revents & POLLNVAL)
588 die("poll: invalid fd %d", pfd->fd);
589
590 if (pfd->revents & POLLIN)
591 while (e && *psk && (*psk)->rx_hook)
592 e = sk_read(*psk);
593
594 e = 1;
595 if (pfd->revents & POLLOUT)
596 while (e && *psk)
597 e = sk_write(*psk);
598 }
599}
600
601
6a8d3f1c
OZ
602
603static void * birdloop_main(void *arg);
604
bf139664
OZ
605struct birdloop *
606birdloop_new(pool *p)
607{
6a8d3f1c
OZ
608 /* FIXME: this init should be elsewhere and thread-safe */
609 static int init = 0;
610 if (!init)
611 { birdloop_init_current(); init = 1; }
612
bf139664 613 struct birdloop *loop = mb_allocz(p, sizeof(struct birdloop));
6a8d3f1c
OZ
614 loop->pool = p;
615 pthread_mutex_init(&loop->mutex, NULL);
bf139664
OZ
616
617 times_init(loop);
618 wakeup_init(loop);
619
620 events_init(loop);
621 timers_init(loop);
622 sockets_init(loop);
623
6a8d3f1c
OZ
624
625 int rv = pthread_create(&loop->thread, NULL, birdloop_main, loop);
626 if (rv < 0)
627 die("pthread_create(): %m");
628
bf139664
OZ
629 return loop;
630}
631
632void
633birdloop_enter(struct birdloop *loop)
634{
6a8d3f1c
OZ
635 /* TODO: these functions could save and restore old context */
636 pthread_mutex_lock(&loop->mutex);
637 birdloop_set_current(loop);
bf139664
OZ
638}
639
640void
641birdloop_leave(struct birdloop *loop)
642{
6a8d3f1c
OZ
643 /* TODO: these functions could save and restore old context */
644 birdloop_set_current(NULL);
645 pthread_mutex_unlock(&loop->mutex);
bf139664
OZ
646}
647
6a8d3f1c
OZ
648void
649birdloop_mask_wakeups(struct birdloop *loop)
650{
651 pthread_mutex_lock(&loop->mutex);
652 loop->wakeup_masked = 1;
653 pthread_mutex_unlock(&loop->mutex);
654}
bf139664
OZ
655
656void
6a8d3f1c 657birdloop_unmask_wakeups(struct birdloop *loop)
bf139664 658{
6a8d3f1c
OZ
659 pthread_mutex_lock(&loop->mutex);
660 if (loop->wakeup_masked == 2)
661 wakeup_do_kick(loop);
662 loop->wakeup_masked = 0;
663 pthread_mutex_unlock(&loop->mutex);
664}
665
666static void *
667birdloop_main(void *arg)
668{
669 struct birdloop *loop = arg;
bf139664 670 timer2 *t;
6a8d3f1c 671 int rv, timeout;
bf139664 672
6a8d3f1c
OZ
673 birdloop_set_current(loop);
674
675 pthread_mutex_lock(&loop->mutex);
bf139664
OZ
676 while (1)
677 {
678 events_fire(loop);
679 timers_fire(loop);
680
681 times_update(loop);
682 if (events_waiting(loop))
683 timeout = 0;
684 else if (t = timers_first(loop))
685 timeout = (tm2_remains(t) TO_MS) + 1;
686 else
687 timeout = -1;
688
689 if (loop->poll_changed)
690 sockets_prepare(loop);
691
692 loop->poll_active = 1;
6a8d3f1c 693 pthread_mutex_unlock(&loop->mutex);
bf139664
OZ
694
695 try:
696 rv = poll(loop->poll_fd.data, loop->poll_fd.used, timeout);
697 if (rv < 0)
698 {
699 if (errno == EINTR || errno == EAGAIN)
700 goto try;
701 die("poll: %m");
702 }
703
6a8d3f1c 704 pthread_mutex_lock(&loop->mutex);
bf139664
OZ
705 loop->poll_active = 0;
706
707 if (loop->close_scheduled)
708 sockets_close_fds(loop);
709
710 if (rv)
711 sockets_fire(loop);
712
713 timers_fire(loop);
714 }
6a8d3f1c
OZ
715
716 return NULL;
bf139664
OZ
717}
718
719
720