summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile2
-rw-r--r--lib/birdlib.h8
-rw-r--r--lib/coro.h31
-rw-r--r--lib/event.c193
-rw-r--r--lib/event.h71
-rw-r--r--lib/event_test.c7
-rw-r--r--lib/flowspec_test.c11
-rw-r--r--lib/hash.h6
-rw-r--r--lib/hash_test.c6
-rw-r--r--lib/io-loop.h57
-rw-r--r--lib/lists.c24
-rw-r--r--lib/lists.h13
-rw-r--r--lib/locking.h66
-rw-r--r--lib/mempool.c21
-rw-r--r--lib/rcu.c79
-rw-r--r--lib/rcu.h55
-rw-r--r--lib/resource.c133
-rw-r--r--lib/resource.h22
-rw-r--r--lib/slab.c19
-rw-r--r--lib/socket.h4
-rw-r--r--lib/timer.c115
-rw-r--r--lib/timer.h41
22 files changed, 781 insertions, 203 deletions
diff --git a/lib/Makefile b/lib/Makefile
index 4378a7bd..98c5db3c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,4 +1,4 @@
-src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
+src := bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c rcu.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)
diff --git a/lib/birdlib.h b/lib/birdlib.h
index 431b7c0d..385bf75c 100644
--- a/lib/birdlib.h
+++ b/lib/birdlib.h
@@ -9,6 +9,7 @@
#ifndef _BIRD_BIRDLIB_H_
#define _BIRD_BIRDLIB_H_
+#include "sysdep/config.h"
#include "lib/alloca.h"
/* Ugly structure offset handling macros */
@@ -16,7 +17,7 @@
struct align_probe { char x; long int y; };
#define OFFSETOF(s, i) ((size_t) &((s *)0)->i)
-#define SKIP_BACK(s, i, p) ((s *)((char *)p - OFFSETOF(s, i)))
+#define SKIP_BACK(s, i, p) ({ s *_ptr = ((s *)((char *)p - OFFSETOF(s, i))); ASSERT_DIE(&_ptr->i == p); _ptr; })
#define BIRD_ALIGN(s, a) (((s)+a-1)&~(a-1))
#define CPU_STRUCT_ALIGN (sizeof(struct align_probe))
@@ -70,6 +71,7 @@ static inline int u64_cmp(u64 i1, u64 i2)
/* Macros for gcc attributes */
#define NORET __attribute__((noreturn))
+#define USE_RESULT __atribute__((warn_unused_result))
#define UNUSED __attribute__((unused))
#define PACKED __attribute__((packed))
#define NONNULL(...) __attribute__((nonnull((__VA_ARGS__))))
@@ -77,10 +79,6 @@ static inline int u64_cmp(u64 i1, u64 i2)
#define STATIC_ASSERT(EXP) _Static_assert(EXP, #EXP)
#define STATIC_ASSERT_MSG(EXP,MSG) _Static_assert(EXP, MSG)
-#ifndef HAVE_THREAD_LOCAL
-#define _Thread_local
-#endif
-
/* Microsecond time */
typedef s64 btime;
diff --git a/lib/coro.h b/lib/coro.h
new file mode 100644
index 00000000..b36f1d2c
--- /dev/null
+++ b/lib/coro.h
@@ -0,0 +1,31 @@
+/*
+ * BIRD Coroutines
+ *
+ * (c) 2017 Martin Mares <mj@ucw.cz>
+ * (c) 2020-2021 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_CORO_H_
+#define _BIRD_CORO_H_
+
+#include "lib/resource.h"
+
+/* A completely opaque coroutine handle. */
+struct coroutine;
+
+/* Coroutines are independent threads bound to pools.
+ * You request a coroutine by calling coro_run().
+ * It is forbidden to free a running coroutine from outside.
+ * The running coroutine must free itself by rfree() before returning.
+ */
+struct coroutine *coro_run(pool *, void (*entry)(void *), void *data);
+
+/* Get self. */
+extern _Thread_local struct coroutine *this_coro;
+
+/* Just wait for a little while. Not intended for general use; use events if possible. */
+void coro_yield(void);
+
+#endif
diff --git a/lib/event.c b/lib/event.c
index 273447e0..5031f314 100644
--- a/lib/event.c
+++ b/lib/event.c
@@ -19,8 +19,14 @@
* events in them and explicitly ask to run them.
*/
+#undef LOCAL_DEBUG
+
#include "nest/bird.h"
#include "lib/event.h"
+#include "lib/locking.h"
+#include "lib/io-loop.h"
+
+extern _Thread_local struct coroutine *this_coro;
event_list global_event_list;
event_list global_work_list;
@@ -28,11 +34,16 @@ event_list global_work_list;
inline void
ev_postpone(event *e)
{
+ event_list *el = e->list;
+ if (!el)
+ return;
+
+ ASSERT_DIE(birdloop_inside(el->loop));
+
+ LOCK_DOMAIN(event, el->lock);
if (ev_active(e))
- {
- rem_node(&e->n);
- e->n.next = NULL;
- }
+ rem_node(&e->n);
+ UNLOCK_DOMAIN(event, el->lock);
}
static void
@@ -95,40 +106,50 @@ ev_run(event *e)
* list @l which can be run by calling ev_run_list().
*/
inline void
-ev_enqueue(event_list *l, event *e)
+ev_send(event_list *l, event *e)
{
- ev_postpone(e);
- add_tail(l, &e->n);
-}
+ DBG("ev_send(%p, %p)\n", l, e);
+ ASSERT_DIE(e->hook);
+ ASSERT_DIE(!e->list || (e->list == l) || (e->list->loop == l->loop));
-/**
- * ev_schedule - schedule an event
- * @e: an event
- *
- * This function schedules an event by enqueueing it to a system-wide
- * event list which is run by the platform dependent code whenever
- * appropriate.
- */
-void
-ev_schedule(event *e)
-{
- ev_enqueue(&global_event_list, e);
-}
+ e->list = l;
-/**
- * ev_schedule_work - schedule a work-event.
- * @e: an event
- *
- * This function schedules an event by enqueueing it to a system-wide work-event
- * list which is run by the platform dependent code whenever appropriate. This
- * is designated for work-events instead of regular events. They are executed
- * less often in order to not clog I/O loop.
- */
-void
-ev_schedule_work(event *e)
-{
- if (!ev_active(e))
- add_tail(&global_work_list, &e->n);
+ struct event_cork *ec = e->cork;
+
+ uint ping = 0;
+
+ if (ec)
+ {
+ LOCK_DOMAIN(cork, ec->lock);
+ LOCK_DOMAIN(event, l->lock);
+
+ if (!enlisted(&e->n))
+ if (ec->count)
+ add_tail(&ec->events, &e->n);
+ else
+ {
+ add_tail(&l->events, &e->n);
+ ping = 1;
+ }
+
+ UNLOCK_DOMAIN(event, l->lock);
+ UNLOCK_DOMAIN(cork, ec->lock);
+ }
+ else
+ {
+ LOCK_DOMAIN(event, l->lock);
+
+ if (!enlisted(&e->n))
+ {
+ add_tail(&l->events, &e->n);
+ ping = 1;
+ }
+
+ UNLOCK_DOMAIN(event, l->lock);
+ }
+
+ if (ping)
+ birdloop_ping(l->loop);
}
void io_log_event(void *hook, void *data);
@@ -142,35 +163,64 @@ void io_log_event(void *hook, void *data);
int
ev_run_list(event_list *l)
{
+ const _Bool legacy = LEGACY_EVENT_LIST(l);
+
+ if (legacy)
+ ASSERT_THE_BIRD_LOCKED;
+
node *n;
- list tmp_list;
+ list tmp_list;
init_list(&tmp_list);
- add_tail_list(&tmp_list, l);
- init_list(l);
+
+ /* Move the event list contents to a local list to avoid executing repeatedly added events */
+ LOCK_DOMAIN(event, l->lock);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+
WALK_LIST_FIRST(n, tmp_list)
{
event *e = SKIP_BACK(event, n, n);
- /* This is ugly hack, we want to log just events executed from the main I/O loop */
- if ((l == &global_event_list) || (l == &global_work_list))
+ if (legacy)
+ {
+ /* The legacy way of event execution */
io_log_event(e->hook, e->data);
-
- ev_run(e);
+ ev_postpone(e);
+ e->hook(e->data);
+ }
+ else
+ {
+ // io_log_event(e->hook, e->data); /* TODO: add support for event logging in other io loops */
+ ASSERT_DIE(e->list == l);
+ LOCK_DOMAIN(event, l->lock);
+ rem_node(&e->n);
+ UNLOCK_DOMAIN(event, l->lock);
+ e->hook(e->data);
+ }
}
- return !EMPTY_LIST(*l);
+ LOCK_DOMAIN(event, l->lock);
+ int repeat = ! EMPTY_LIST(l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+ return repeat;
}
int
ev_run_list_limited(event_list *l, uint limit)
{
+ ASSERT_DIE(LEGACY_EVENT_LIST(l));
+ ASSERT_THE_BIRD_LOCKED;
+
node *n;
list tmp_list;
+ LOCK_DOMAIN(event, l->lock);
init_list(&tmp_list);
- add_tail_list(&tmp_list, l);
- init_list(l);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ UNLOCK_DOMAIN(event, l->lock);
WALK_LIST_FIRST(n, tmp_list)
{
@@ -179,21 +229,60 @@ ev_run_list_limited(event_list *l, uint limit)
if (!limit)
break;
- /* This is ugly hack, we want to log just events executed from the main I/O loop */
- if ((l == &global_event_list) || (l == &global_work_list))
- io_log_event(e->hook, e->data);
+ io_log_event(e->hook, e->data);
ev_run(e);
limit--;
}
+ LOCK_DOMAIN(event, l->lock);
if (!EMPTY_LIST(tmp_list))
{
/* Attach new items after the unprocessed old items */
- add_tail_list(&tmp_list, l);
- init_list(l);
- add_tail_list(l, &tmp_list);
+ add_tail_list(&tmp_list, &l->events);
+ init_list(&l->events);
+ add_tail_list(&l->events, &tmp_list);
+ }
+
+ int repeat = ! EMPTY_LIST(l->events);
+ UNLOCK_DOMAIN(event, l->lock);
+
+ return repeat;
+}
+
+void ev_cork(struct event_cork *ec)
+{
+ LOCK_DOMAIN(cork, ec->lock);
+ ec->count++;
+ UNLOCK_DOMAIN(cork, ec->lock);
+}
+
+void ev_uncork(struct event_cork *ec)
+{
+ LOCK_DOMAIN(cork, ec->lock);
+
+ if (--ec->count)
+ {
+ UNLOCK_DOMAIN(cork, ec->lock);
+ return;
}
- return !EMPTY_LIST(*l);
+ node *n;
+ WALK_LIST_FIRST(n, ec->events)
+ {
+ event *e = SKIP_BACK(event, n, n);
+ event_list *el = e->list;
+
+ rem_node(&e->n);
+
+ LOCK_DOMAIN(event, el->lock);
+ add_tail(&el->events, &e->n);
+ UNLOCK_DOMAIN(event, el->lock);
+
+ birdloop_ping(el->loop);
+ }
+
+ UNLOCK_DOMAIN(cork, ec->lock);
+
+ birdloop_ping(&main_birdloop);
}
diff --git a/lib/event.h b/lib/event.h
index 5f3b78d8..cd85bf78 100644
--- a/lib/event.h
+++ b/lib/event.h
@@ -10,33 +10,92 @@
#define _BIRD_EVENT_H_
#include "lib/resource.h"
+#include "lib/locking.h"
+
+#include <stdatomic.h>
+
+DEFINE_DOMAIN(event);
+DEFINE_DOMAIN(cork);
typedef struct event {
resource r;
void (*hook)(void *);
void *data;
node n; /* Internal link */
+ struct event_list *list; /* List where this event is put in */
+ struct event_cork *cork; /* Event execution limiter */
+ node cork_node;
} event;
-typedef list event_list;
+typedef struct event_list {
+ list events;
+ pool *pool;
+ struct birdloop *loop;
+ DOMAIN(event) lock;
+} event_list;
+
+struct event_cork {
+ DOMAIN(cork) lock;
+ u32 count;
+ list events;
+};
extern event_list global_event_list;
extern event_list global_work_list;
event *ev_new(pool *);
void ev_run(event *);
-#define ev_init_list(el) init_list(el)
-void ev_enqueue(event_list *, event *);
-void ev_schedule(event *);
-void ev_schedule_work(event *);
+
+static inline void ev_init_list(event_list *el, struct birdloop *loop, const char *name)
+{
+ init_list(&el->events);
+ el->loop = loop;
+ el->lock = DOMAIN_NEW(event, name);
+}
+
+static inline void ev_init_cork(struct event_cork *ec, const char *name)
+{
+ init_list(&ec->events);
+ ec->lock = DOMAIN_NEW(cork, name);
+ ec->count = 0;
+};
+
+void ev_send(event_list *, event *);
+#define ev_send_loop(l, e) ev_send(birdloop_event_list((l)), (e))
+
+#define ev_schedule(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_event_list, (e)); })
+#define ev_schedule_work(e) ({ ASSERT_THE_BIRD_LOCKED; if (!ev_active((e))) ev_send(&global_work_list, (e)); })
+
void ev_postpone(event *);
int ev_run_list(event_list *);
int ev_run_list_limited(event_list *, uint);
+#define LEGACY_EVENT_LIST(l) (((l) == &global_event_list) || ((l) == &global_work_list))
+
+void ev_cork(struct event_cork *);
+void ev_uncork(struct event_cork *);
+
+static inline u32 ev_corked(struct event_cork *ec)
+{
+ if (!ec)
+ return 0;
+
+ LOCK_DOMAIN(cork, ec->lock);
+ u32 out = ec->count;
+ UNLOCK_DOMAIN(cork, ec->lock);
+ return out;
+}
+
+_Bool birdloop_inside(struct birdloop *loop);
+
static inline int
ev_active(event *e)
{
- return e->n.next != NULL;
+ if (e->list == NULL)
+ return 0;
+
+ ASSERT_DIE(birdloop_inside(e->list->loop));
+ return enlisted(&e->n);
}
static inline event*
diff --git a/lib/event_test.c b/lib/event_test.c
index e1215bba..f8bb303b 100644
--- a/lib/event_test.c
+++ b/lib/event_test.c
@@ -48,14 +48,17 @@ init_event_check_points(void)
event_check_points[i] = 0;
}
+void resource_sys_init(void);
+
static int
t_ev_run_list(void)
{
int i;
+ resource_sys_init();
resource_init();
+ birdloop_init();
olock_init();
- timer_init();
io_init();
rt_init();
if_init();
@@ -82,7 +85,9 @@ main(int argc, char *argv[])
{
bt_init(argc, argv);
+ the_bird_lock();
bt_test_suite(t_ev_run_list, "Schedule and run 3 events in right order.");
+ the_bird_unlock();
return bt_exit_value();
}
diff --git a/lib/flowspec_test.c b/lib/flowspec_test.c
index ed4afe51..518b8fc7 100644
--- a/lib/flowspec_test.c
+++ b/lib/flowspec_test.c
@@ -8,6 +8,7 @@
#include "test/birdtest.h"
#include "lib/flowspec.h"
+#include "lib/io-loop.h"
#define NET_ADDR_FLOW4_(what,prefix,pxlen,data_) \
do \
@@ -446,8 +447,6 @@ t_validation6(void)
static int
t_builder4(void)
{
- resource_init();
-
struct flow_builder *fb = flow_builder_init(&root_pool);
linpool *lp = lp_new_default(&root_pool);
@@ -529,7 +528,6 @@ t_builder6(void)
{
net_addr_ip6 ip;
- resource_init();
linpool *lp = lp_new_default(&root_pool);
struct flow_builder *fb = flow_builder_init(&root_pool);
fb->ipv6 = 1;
@@ -666,10 +664,16 @@ t_formatting6(void)
return 1;
}
+void resource_sys_init(void);
+
int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ resource_sys_init();
+ resource_init();
+ the_bird_lock();
+ birdloop_init();
bt_test_suite(t_read_length, "Testing get NLRI length");
bt_test_suite(t_write_length, "Testing set NLRI length");
@@ -685,5 +689,6 @@ main(int argc, char *argv[])
bt_test_suite(t_formatting4, "Formatting Flow Specification (IPv4) into text representation");
bt_test_suite(t_formatting6, "Formatting Flow Specification (IPv6) into text representation");
+ the_bird_unlock();
return bt_exit_value();
}
diff --git a/lib/hash.h b/lib/hash.h
index ea4ca6dd..8febb33f 100644
--- a/lib/hash.h
+++ b/lib/hash.h
@@ -215,6 +215,12 @@ mem_hash_mix(u64 *h, const void *p, uint s)
*h = *h * multiplier + pp[i];
}
+static inline void
+mem_hash_mix_num(u64 *h, u64 val)
+{
+ mem_hash_mix(h, &val, sizeof(val));
+}
+
static inline uint
mem_hash_value(u64 *h)
{
diff --git a/lib/hash_test.c b/lib/hash_test.c
index 59beb7c0..7ef54662 100644
--- a/lib/hash_test.c
+++ b/lib/hash_test.c
@@ -9,7 +9,9 @@
#undef LOCAL_DEBUG
#include "test/birdtest.h"
+#include "test/bt-utils.h"
+#include "lib/io-loop.h"
#include "lib/hash.h"
struct test_node {
@@ -61,8 +63,7 @@ dump_nodes(void)
static void
init_hash_(uint order)
{
- resource_init();
- my_pool = rp_new(&root_pool, "Test pool");
+ my_pool = rp_new(&root_pool, &main_birdloop, "Test pool");
HASH_INIT(hash, my_pool, order);
@@ -290,6 +291,7 @@ int
main(int argc, char *argv[])
{
bt_init(argc, argv);
+ bt_bird_init();
bt_test_suite(t_insert_find, "HASH_INSERT and HASH_FIND");
bt_test_suite(t_insert_find_random, "HASH_INSERT pseudo-random keys and HASH_FIND");
diff --git a/lib/io-loop.h b/lib/io-loop.h
new file mode 100644
index 00000000..386a31d5
--- /dev/null
+++ b/lib/io-loop.h
@@ -0,0 +1,57 @@
+/*
+ * BIRD -- I/O and event loop
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_IO_LOOP_H_
+#define _BIRD_IO_LOOP_H_
+
+#include "nest/bird.h"
+#include "lib/lists.h"
+#include "lib/locking.h"
+#include "lib/resource.h"
+#include "lib/event.h"
+#include "lib/socket.h"
+
+void sk_start(sock *s);
+void sk_stop(sock *s);
+void sk_reloop(sock *s, struct birdloop *loop);
+
+extern struct birdloop main_birdloop;
+
+/* Start a new birdloop owned by given pool and domain.
+ * The loop allocates its internal pool for local allocations
+ * which is freed when the loop itself is stopped. */
+struct birdloop *birdloop_new(pool *p, uint order, const char *name);
+
+/* Stop the loop. At the end, the @stopped callback is called with locked
+ * parent to finish cleanup. The loop then frees itself together with its pool. */
+void birdloop_stop(struct birdloop *loop, void (*stopped)(void *data), void *data);
+void birdloop_stop_self(struct birdloop *loop, void (*stopped)(void *data), void *data);
+
+/* Get birdloop's event list */
+event_list *birdloop_event_list(struct birdloop *loop);
+
+/* Get birdloop's time heap */
+struct timeloop *birdloop_time_loop(struct birdloop *loop);
+
+/* Get birdloop's resource pool */
+pool *birdloop_pool(struct birdloop *loop);
+
+/* Enter and exit the birdloop */
+void birdloop_enter(struct birdloop *loop);
+void birdloop_leave(struct birdloop *loop);
+
+_Bool birdloop_inside(struct birdloop *loop);
+
+void birdloop_mask_wakeups(struct birdloop *loop);
+void birdloop_unmask_wakeups(struct birdloop *loop);
+
+void birdloop_link(struct birdloop *loop);
+void birdloop_unlink(struct birdloop *loop);
+
+void birdloop_ping(struct birdloop *loop);
+
+void birdloop_init(void);
+#endif /* _BIRD_IO_LOOP_H_ */
diff --git a/lib/lists.c b/lib/lists.c
index 200576cf..dc2e4cbb 100644
--- a/lib/lists.c
+++ b/lib/lists.c
@@ -26,7 +26,7 @@
#define _BIRD_LISTS_C_
-#include "nest/bird.h"
+#include "lib/birdlib.h"
#include "lib/lists.h"
LIST_INLINE int
@@ -35,11 +35,12 @@ check_list(list *l, node *n)
if (!l)
{
ASSERT_DIE(n);
- ASSERT_DIE(n->prev);
- do { n = n->prev; } while (n->prev);
+ node *nn = n;
+ while (nn->prev)
+ nn = nn->prev;
- l = SKIP_BACK(list, head_node, n);
+ l = SKIP_BACK(list, head_node, nn);
}
int seen = 0;
@@ -60,7 +61,7 @@ check_list(list *l, node *n)
}
ASSERT_DIE(cur == &(l->tail_node));
- ASSERT_DIE(!n || (seen == 1));
+ ASSERT_DIE(!n || (seen == 1) || (n == &l->head_node) || (n == &l->tail_node));
return 1;
}
@@ -109,6 +110,15 @@ add_head(list *l, node *n)
l->head = n;
}
+LIST_INLINE void
+self_link(node *n)
+{
+ ASSUME(n->prev == NULL);
+ ASSUME(n->next == NULL);
+
+ n->prev = n->next = n;
+}
+
/**
* insert_node - insert a node to a list
* @n: a new list node
@@ -120,7 +130,7 @@ add_head(list *l, node *n)
LIST_INLINE void
insert_node(node *n, node *after)
{
- EXPENSIVE_CHECK(check_list(l, after));
+ EXPENSIVE_CHECK(check_list(NULL, after));
ASSUME(n->prev == NULL);
ASSUME(n->next == NULL);
@@ -141,7 +151,7 @@ insert_node(node *n, node *after)
LIST_INLINE void
rem_node(node *n)
{
- EXPENSIVE_CHECK(check_list(NULL, n));
+ EXPENSIVE_CHECK((n == n->prev) && (n == n->next) || check_list(NULL, n));
node *z = n->prev;
node *x = n->next;
diff --git a/lib/lists.h b/lib/lists.h
index 479f4ed1..dc49ec8a 100644
--- a/lib/lists.h
+++ b/lib/lists.h
@@ -68,6 +68,18 @@ typedef union list { /* In fact two overlayed nodes */
#define EMPTY_LIST(list) (!(list).head->next)
+static inline _Bool
+enlisted(node *n)
+{
+ switch ((!!n->next) + (!!n->prev))
+ {
+ case 0: return 0;
+ case 2: return 1;
+ case 1: bug("Garbled event list node");
+ }
+
+ bug("Maths is broken. And you should see a new heaven and a new earth: for the first heaven and the first earth had been passed away.");
+}
#ifndef _BIRD_LISTS_C_
#define LIST_INLINE static inline
@@ -78,6 +90,7 @@ typedef union list { /* In fact two overlayed nodes */
#define LIST_INLINE
void add_tail(list *, node *);
void add_head(list *, node *);
+void self_link(node *);
void rem_node(node *);
void add_tail_list(list *, list *);
void init_list(list *);
diff --git a/lib/locking.h b/lib/locking.h
new file mode 100644
index 00000000..1a8bdcd4
--- /dev/null
+++ b/lib/locking.h
@@ -0,0 +1,66 @@
+/*
+ * BIRD Library -- Locking
+ *
+ * (c) 2020--2021 Maria Matejka <mq@jmq.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#ifndef _BIRD_LOCKING_H_
+#define _BIRD_LOCKING_H_
+
+struct domain_generic;
+
+/* Here define the global lock order; first to last. */
+struct lock_order {
+ struct domain_generic *the_bird;
+ struct domain_generic *proto;
+ struct domain_generic *rtable;
+ struct domain_generic *attrs;
+ struct domain_generic *cork;
+ struct domain_generic *event;
+ struct domain_generic *resource;
+};
+
+extern _Thread_local struct lock_order locking_stack;
+extern _Thread_local struct domain_generic **last_locked;
+
+#define DOMAIN(type) struct domain__##type
+#define DEFINE_DOMAIN(type) DOMAIN(type) { struct domain_generic *type; }
+#define DOMAIN_ORDER(type) OFFSETOF(struct lock_order, type)
+
+#define DOMAIN_NEW(type, name) (DOMAIN(type)) { .type = domain_new(name, DOMAIN_ORDER(type)) }
+struct domain_generic *domain_new(const char *name, uint order);
+
+#define DOMAIN_FREE(type, d) domain_free((d).type)
+void domain_free(struct domain_generic *);
+
+#define DOMAIN_NULL(type) (DOMAIN(type)) {}
+
+#define LOCK_DOMAIN(type, d) do_lock(((d).type), &(locking_stack.type))
+#define UNLOCK_DOMAIN(type, d) do_unlock(((d).type), &(locking_stack.type))
+
+#define DOMAIN_IS_LOCKED(type, d) (((d).type) == (locking_stack.type))
+#define DG_IS_LOCKED(d) ((d) == *(DG_LSP(d)))
+
+/* Internal for locking */
+void do_lock(struct domain_generic *dg, struct domain_generic **lsp);
+void do_unlock(struct domain_generic *dg, struct domain_generic **lsp);
+
+uint dg_order(struct domain_generic *dg);
+
+#define DG_LSP(d) ((struct domain_generic **) (((void *) &locking_stack) + dg_order(d)))
+#define DG_LOCK(d) do_lock(d, DG_LSP(d))
+#define DG_UNLOCK(d) do_unlock(d, DG_LSP(d))
+
+/* Use with care. To be removed in near future. */
+DEFINE_DOMAIN(the_bird);
+extern DOMAIN(the_bird) the_bird_domain;
+
+#define the_bird_lock() LOCK_DOMAIN(the_bird, the_bird_domain)
+#define the_bird_unlock() UNLOCK_DOMAIN(the_bird, the_bird_domain)
+#define the_bird_locked() DOMAIN_IS_LOCKED(the_bird, the_bird_domain)
+
+#define ASSERT_THE_BIRD_LOCKED ({ if (!the_bird_locked()) bug("The BIRD lock must be locked here: %s:%d", __FILE__, __LINE__); })
+
+#endif
diff --git a/lib/mempool.c b/lib/mempool.c
index 90d7c774..6d157e5c 100644
--- a/lib/mempool.c
+++ b/lib/mempool.c
@@ -37,9 +37,10 @@ const int lp_chunk_size = sizeof(struct lp_chunk);
struct linpool {
resource r;
byte *ptr, *end;
+ pool *p;
struct lp_chunk *first, *current; /* Normal (reusable) chunks */
struct lp_chunk *first_large; /* Large chunks */
- uint chunk_size, threshold, total, total_large;
+ uint chunk_size, threshold, total:31, use_pages:1, total_large;
};
static void lp_free(resource *);
@@ -69,6 +70,13 @@ linpool
*lp_new(pool *p, uint blk)
{
linpool *m = ralloc(p, &lp_class);
+ m->p = p;
+ if (!blk)
+ {
+ m->use_pages = 1;
+ blk = page_size - lp_chunk_size;
+ }
+
m->chunk_size = blk;
m->threshold = 3*blk/4;
return m;
@@ -121,7 +129,11 @@ lp_alloc(linpool *m, uint size)
else
{
/* Need to allocate a new chunk */
- c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
+ if (m->use_pages)
+ c = alloc_page();
+ else
+ c = xmalloc(sizeof(struct lp_chunk) + m->chunk_size);
+
m->total += m->chunk_size;
c->next = NULL;
c->size = m->chunk_size;
@@ -258,7 +270,10 @@ lp_free(resource *r)
for(d=m->first; d; d = c)
{
c = d->next;
- xfree(d);
+ if (m->use_pages)
+ free_page(d);
+ else
+ xfree(d);
}
for(d=m->first_large; d; d = c)
{
diff --git a/lib/rcu.c b/lib/rcu.c
new file mode 100644
index 00000000..69f3442f
--- /dev/null
+++ b/lib/rcu.c
@@ -0,0 +1,79 @@
+/*
+ * BIRD Library -- Read-Copy-Update Basic Operations
+ *
+ * (c) 2021 Maria Matejka <mq@jmq.cz>
+ * (c) 2021 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ * Note: all the relevant patents shall be expired.
+ *
+ * Using the Supplementary Material for User-Level Implementations of Read-Copy-Update
+ * by Matthieu Desnoyers, Paul E. McKenney, Alan S. Stern, Michel R. Dagenais and Jonathan Walpole
+ * obtained from https://www.efficios.com/pub/rcu/urcu-supp-accepted.pdf
+ */
+
+#include "lib/rcu.h"
+#include "lib/coro.h"
+#include "lib/locking.h"
+
+_Atomic uint rcu_gp_ctl = RCU_NEST_CNT;
+_Thread_local struct rcu_coro *this_rcu_coro = NULL;
+
+static list rcu_coro_list;
+
+static struct rcu_coro main_rcu_coro;
+
+DEFINE_DOMAIN(resource);
+static DOMAIN(resource) rcu_domain;
+
+static int
+rcu_gp_ongoing(_Atomic uint *ctl)
+{
+ uint val = atomic_load(ctl);
+ return (val & RCU_NEST_CNT) && ((val ^ rcu_gp_ctl) & RCU_GP_PHASE);
+}
+
+static void
+update_counter_and_wait(void)
+{
+ atomic_fetch_xor(&rcu_gp_ctl, RCU_GP_PHASE);
+ struct rcu_coro *rc;
+ WALK_LIST(rc, rcu_coro_list)
+ while (rcu_gp_ongoing(&rc->ctl))
+ coro_yield();
+}
+
+void
+synchronize_rcu(void)
+{
+ LOCK_DOMAIN(resource, rcu_domain);
+ update_counter_and_wait();
+ update_counter_and_wait();
+ UNLOCK_DOMAIN(resource, rcu_domain);
+}
+
+void
+rcu_coro_start(struct rcu_coro *rc)
+{
+ LOCK_DOMAIN(resource, rcu_domain);
+ add_tail(&rcu_coro_list, &rc->n);
+ this_rcu_coro = rc;
+ UNLOCK_DOMAIN(resource, rcu_domain);
+}
+
+void
+rcu_coro_stop(struct rcu_coro *rc)
+{
+ LOCK_DOMAIN(resource, rcu_domain);
+ this_rcu_coro = NULL;
+ rem_node(&rc->n);
+ UNLOCK_DOMAIN(resource, rcu_domain);
+}
+
+void
+rcu_init(void)
+{
+ rcu_domain = DOMAIN_NEW(resource, "Read-Copy-Update");
+ init_list(&rcu_coro_list);
+ rcu_coro_start(&main_rcu_coro);
+}
diff --git a/lib/rcu.h b/lib/rcu.h
new file mode 100644
index 00000000..ac8fc9ce
--- /dev/null
+++ b/lib/rcu.h
@@ -0,0 +1,55 @@
+/*
+ * BIRD Library -- Read-Copy-Update Basic Operations
+ *
+ * (c) 2021 Maria Matejka <mq@jmq.cz>
+ * (c) 2021 CZ.NIC z.s.p.o.
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ * Note: all the relevant patents shall be expired.
+ */
+
+#ifndef _BIRD_RCU_H_
+#define _BIRD_RCU_H_
+
+#include "lib/birdlib.h"
+#include "lib/lists.h"
+#include <stdatomic.h>
+
+#define RCU_GP_PHASE 0x100000
+#define RCU_NEST_MASK 0x0fffff
+#define RCU_NEST_CNT 0x000001
+
+extern _Atomic uint rcu_gp_ctl;
+
+struct rcu_coro {
+ node n;
+ _Atomic uint ctl;
+};
+
+extern _Thread_local struct rcu_coro *this_rcu_coro;
+
+static inline void rcu_read_lock(void)
+{
+ uint cmp = atomic_load_explicit(&this_rcu_coro->ctl, memory_order_acquire);
+
+ if (cmp & RCU_NEST_MASK)
+ atomic_store_explicit(&this_rcu_coro->ctl, cmp + RCU_NEST_CNT, memory_order_relaxed);
+ else
+ atomic_store(&this_rcu_coro->ctl, atomic_load_explicit(&rcu_gp_ctl, memory_order_acquire));
+}
+
+static inline void rcu_read_unlock(void)
+{
+ atomic_fetch_sub(&this_rcu_coro->ctl, RCU_NEST_CNT);
+}
+
+void synchronize_rcu(void);
+
+/* Registering and unregistering a coroutine. To be called from coroutine implementation */
+void rcu_coro_start(struct rcu_coro *);
+void rcu_coro_stop(struct rcu_coro *);
+
+/* Run this from resource init */
+void rcu_init(void);
+
+#endif
diff --git a/lib/resource.c b/lib/resource.c
index 5d4c7780..9a4c7717 100644
--- a/lib/resource.c
+++ b/lib/resource.c
@@ -14,6 +14,8 @@
#include "nest/bird.h"
#include "lib/resource.h"
#include "lib/string.h"
+#include "lib/rcu.h"
+#include "lib/io-loop.h"
/**
* DOC: Resource pools
@@ -29,12 +31,6 @@
* is freed upon shutdown of the module.
*/
-struct pool {
- resource r;
- list inside;
- const char *name;
-};
-
static void pool_dump(resource *);
static void pool_free(resource *);
static resource *pool_lookup(resource *, unsigned long);
@@ -56,26 +52,39 @@ static int indent;
/**
* rp_new - create a resource pool
* @p: parent pool
+ * @l: loop to assign
* @name: pool name (to be included in debugging dumps)
*
* rp_new() creates a new resource pool inside the specified
* parent pool.
*/
pool *
-rp_new(pool *p, const char *name)
+rp_new(pool *p, struct birdloop *loop, const char *name)
{
+ ASSERT_DIE(birdloop_inside(p->loop));
+ ASSERT_DIE(birdloop_inside(loop));
+
pool *z = ralloc(p, &pool_class);
+ z->loop = loop;
z->name = name;
init_list(&z->inside);
return z;
}
+_Thread_local static pool *pool_parent = NULL;
+
static void
pool_free(resource *P)
{
+ ASSERT_DIE(pool_parent);
+
pool *p = (pool *) P;
- resource *r, *rr;
+ ASSERT_DIE(birdloop_inside(p->loop));
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+ resource *r, *rr;
r = HEAD(p->inside);
while (rr = (resource *) r->n.next)
{
@@ -83,14 +92,25 @@ pool_free(resource *P)
xfree(r);
r = rr;
}
+
+ pool_parent = parent;
+}
+
+void
+rp_free(pool *p, pool *parent)
+{
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = parent;
+ rfree(p);
+ ASSERT_DIE(pool_parent == parent);
+ pool_parent = NULL;
}
static void
-pool_dump(resource *P)
+pool_dump_locked(pool *p)
{
- pool *p = (pool *) P;
resource *r;
-
+
debug("%s\n", p->name);
indent += 3;
WALK_LIST(r, p->inside)
@@ -98,10 +118,47 @@ pool_dump(resource *P)
indent -= 3;
}
-static struct resmem
-pool_memsize(resource *P)
+static void
+pool_dump(resource *P)
{
pool *p = (pool *) P;
+
+ if (p->loop != pool_parent->loop)
+ birdloop_enter(p->loop);
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+
+ pool_dump_locked(p);
+
+ pool_parent = parent;
+
+ if (p->loop != pool_parent->loop)
+ birdloop_leave(p->loop);
+}
+
+void
+rp_dump(pool *p)
+{
+ int inside = birdloop_inside(p->loop);
+ if (!inside)
+ birdloop_enter(p->loop);
+
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = p;
+
+ pool_dump_locked(p);
+
+ ASSERT_DIE(pool_parent == p);
+ pool_parent = NULL;
+
+ if (!inside)
+ birdloop_leave(p->loop);
+}
+
+static struct resmem
+pool_memsize_locked(pool *p)
+{
resource *r;
struct resmem sum = {
.effective = 0,
@@ -118,6 +175,46 @@ pool_memsize(resource *P)
return sum;
}
+static struct resmem
+pool_memsize(resource *P)
+{
+ pool *p = (pool *) P;
+
+ pool *parent = pool_parent;
+ pool_parent = p;
+
+ if (p->loop != parent->loop)
+ birdloop_enter(p->loop);
+
+ struct resmem sum = pool_memsize_locked(p);
+
+ if (p->loop != parent->loop)
+ birdloop_leave(p->loop);
+
+ pool_parent = parent;
+
+ return sum;
+}
+
+struct resmem
+rp_memsize(pool *p)
+{
+ int inside = birdloop_inside(p->loop);
+ if (!inside)
+ birdloop_enter(p->loop);
+
+ ASSERT_DIE(pool_parent == NULL);
+ pool_parent = p;
+ struct resmem sum = pool_memsize_locked(p);
+ ASSERT_DIE(pool_parent == p);
+ pool_parent = NULL;
+
+ if (!inside)
+ birdloop_leave(p->loop);
+
+ return sum;
+}
+
static resource *
pool_lookup(resource *P, unsigned long a)
{
@@ -228,12 +325,15 @@ rmemsize(void *res)
void *
ralloc(pool *p, struct resclass *c)
{
+ ASSERT_DIE(p);
+ ASSERT_DIE(birdloop_inside(p->loop));
+
resource *r = xmalloc(c->size);
bzero(r, c->size);
r->class = c;
- if (p)
- add_tail(&p->inside, &r->n);
+ add_tail(&p->inside, &r->n);
+
return r;
}
@@ -270,6 +370,8 @@ rlookup(unsigned long a)
void
resource_init(void)
{
+ rcu_init();
+
root_pool.r.class = &pool_class;
root_pool.name = "Root";
init_list(&root_pool.inside);
@@ -440,7 +542,6 @@ mb_free(void *m)
}
-
#define STEP_UP(x) ((x) + (x)/2 + 4)
void
diff --git a/lib/resource.h b/lib/resource.h
index 9ec41ed8..b80ff8c6 100644
--- a/lib/resource.h
+++ b/lib/resource.h
@@ -40,10 +40,16 @@ struct resclass {
/* Generic resource manipulation */
-typedef struct pool pool;
+typedef struct pool {
+ resource r;
+ list inside;
+ struct pool_pages *pages;
+ struct birdloop *loop;
+ const char *name;
+} pool;
void resource_init(void);
-pool *rp_new(pool *, const char *); /* Create new pool */
+
void rfree(void *); /* Free single resource */
void rdump(void *); /* Dump to debug output */
struct resmem rmemsize(void *res); /* Return size of memory used by the resource */
@@ -52,6 +58,11 @@ void rmove(void *, pool *); /* Move to a different pool */
void *ralloc(pool *, struct resclass *);
+pool *rp_new(pool *, struct birdloop *loop, const char *); /* Create new pool */
+void rp_free(pool *p, pool *parent); /* Free parent pool */
+struct resmem rp_memsize(pool *p); /* Return size of memory used by the pool */
+void rp_dump(pool *p); /* Dump pool to debug output */
+
extern pool root_pool;
/* Normal memory blocks */
@@ -82,7 +93,7 @@ void lp_restore(linpool *m, lp_state *p); /* Restore state */
extern const int lp_chunk_size;
#define LP_GAS 1024
#define LP_GOOD_SIZE(x) (((x + LP_GAS - 1) & (~(LP_GAS - 1))) - lp_chunk_size)
-#define lp_new_default(p) lp_new(p, LP_GOOD_SIZE(LP_GAS*4))
+#define lp_new_default(p) lp_new(p, 0)
/* Slabs */
@@ -100,11 +111,12 @@ void sl_free(slab *, void *);
void buffer_realloc(void **buf, unsigned *size, unsigned need, unsigned item_size);
+extern long page_size;
+
/* Allocator of whole pages; for use in slabs and other high-level allocators. */
-u64 get_page_size(void);
void *alloc_page(void);
void free_page(void *);
-extern uint pages_kept;
+#define PAGE_HEAD(x) ((void *) (((intptr_t) (x)) & ~(page_size-1)))
#ifdef HAVE_LIBDMALLOC
/*
diff --git a/lib/slab.c b/lib/slab.c
index 6cab6b7b..c3947162 100644
--- a/lib/slab.c
+++ b/lib/slab.c
@@ -155,6 +155,7 @@ slab_memsize(resource *r)
struct slab {
resource r;
+ pool *p;
uint obj_size, head_size, head_bitfield_len;
uint objs_per_slab, num_empty_heads, data_size;
list empty_heads, partial_heads, full_heads;
@@ -180,7 +181,7 @@ struct sl_alignment { /* Magic structure for testing of alignment */
int x[0];
};
-#define SL_GET_HEAD(x) ((struct sl_head *) (((uintptr_t) (x)) & ~(get_page_size()-1)))
+#define SL_GET_HEAD(x) ((struct sl_head *) PAGE_HEAD(x))
/**
* sl_new - create a new Slab
@@ -194,6 +195,7 @@ slab *
sl_new(pool *p, uint size)
{
slab *s = ralloc(p, &sl_class);
+ s->p = p;
uint align = sizeof(struct sl_alignment);
if (align < sizeof(int))
align = sizeof(int);
@@ -202,7 +204,6 @@ sl_new(pool *p, uint size)
s->obj_size = size;
s->head_size = sizeof(struct sl_head);
- u64 page_size = get_page_size();
do {
s->objs_per_slab = (page_size - s->head_size) / size;
@@ -272,6 +273,9 @@ no_partial:
goto okay;
}
h = alloc_page();
+#ifdef POISON
+ memset(h, 0xba, page_size);
+#endif
ASSERT_DIE(SL_GET_HEAD(h) == h);
memset(h, 0, s->head_size);
add_head(&s->partial_heads, &h->n);
@@ -327,7 +331,12 @@ sl_free(slab *s, void *oo)
{
rem_node(&h->n);
if (s->num_empty_heads >= MAX_EMPTY_HEADS)
+ {
+#ifdef POISON
+ memset(h, 0xde, page_size);
+#endif
free_page(h);
+ }
else
{
add_head(&s->empty_heads, &h->n);
@@ -391,7 +400,7 @@ slab_memsize(resource *r)
return (struct resmem) {
.effective = eff,
- .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * get_page_size() - eff,
+ .overhead = ALLOC_OVERHEAD + sizeof(struct slab) + heads * page_size - eff,
};
}
@@ -402,10 +411,10 @@ slab_lookup(resource *r, unsigned long a)
struct sl_head *h;
WALK_LIST(h, s->partial_heads)
- if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
+ if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
WALK_LIST(h, s->full_heads)
- if ((unsigned long) h < a && (unsigned long) h + get_page_size() < a)
+ if ((unsigned long) h < a && (unsigned long) h + page_size < a)
return r;
return NULL;
}
diff --git a/lib/socket.h b/lib/socket.h
index 96fedeeb..ff07660f 100644
--- a/lib/socket.h
+++ b/lib/socket.h
@@ -12,6 +12,7 @@
#include <errno.h>
#include "lib/resource.h"
+#include "lib/event.h"
#ifdef HAVE_LIBSSH
#define LIBSSH_LEGACY_0_4
#include <libssh/libssh.h>
@@ -56,6 +57,7 @@ typedef struct birdsock {
uint fast_rx; /* RX has higher priority in event loop */
uint rbsize;
int (*rx_hook)(struct birdsock *, uint size); /* NULL=receiving turned off, returns 1 to clear rx buffer */
+ struct event_cork *cork; /* Cork to temporarily stop receiving data */
byte *tbuf, *tpos; /* NULL=allocate automatically */
byte *ttx; /* Internal */
@@ -79,6 +81,7 @@ typedef struct birdsock {
const char *password; /* Password for MD5 authentication */
const char *err; /* Error message */
struct ssh_sock *ssh; /* Used in SK_SSH */
+ struct event reloop; /* Reloop event */
} sock;
sock *sock_new(pool *); /* Allocate new socket */
@@ -128,6 +131,7 @@ extern int sk_priority_control; /* Suggested priority for control traffic, shou
#define SKF_TRUNCATED 0x200 /* Received packet was truncated, set by IO layer */
#define SKF_HDRINCL 0x400 /* Used internally */
#define SKF_PKTINFO 0x800 /* Used internally */
+#define SKF_PASSIVE_THREAD 0x1000 /* Child sockets used in thread, do not add to main loop */
/*
* Socket types SA SP DA DP IF TTL SendTo (?=may, -=must not, *=must)
diff --git a/lib/timer.c b/lib/timer.c
index 381163d0..eb7ea690 100644
--- a/lib/timer.c
+++ b/lib/timer.c
@@ -32,61 +32,18 @@
#include "nest/bird.h"
+#include "lib/coro.h"
#include "lib/heap.h"
#include "lib/resource.h"
#include "lib/timer.h"
-
-struct timeloop main_timeloop;
-
-
-#ifdef USE_PTHREADS
-
#include <pthread.h>
-/* Data accessed and modified from proto/bfd/io.c */
-pthread_key_t current_time_key;
-
-static inline struct timeloop *
-timeloop_current(void)
-{
- return pthread_getspecific(current_time_key);
-}
-
-static inline void
-timeloop_init_current(void)
-{
- pthread_key_create(&current_time_key, NULL);
- pthread_setspecific(current_time_key, &main_timeloop);
-}
+_Atomic btime last_time;
+_Atomic btime real_time;
void wakeup_kick_current(void);
-#else
-
-/* Just use main timelooop */
-static inline struct timeloop * timeloop_current(void) { return &main_timeloop; }
-static inline void timeloop_init_current(void) { }
-
-#endif
-
-btime
-current_time(void)
-{
- return timeloop_current()->last_time;
-}
-
-btime
-current_real_time(void)
-{
- struct timeloop *loop = timeloop_current();
-
- if (!loop->real_time)
- times_update_real_time(loop);
-
- return loop->real_time;
-}
-
#define TIMER_LESS(a,b) ((a)->expires < (b)->expires)
#define TIMER_SWAP(heap,a,b,t) (t = heap[a], heap[a] = heap[b], heap[b] = t, \
@@ -112,7 +69,7 @@ tm_dump(resource *r)
if (t->recurrent)
debug("recur %d, ", t->recurrent);
if (t->expires)
- debug("expires in %d ms)\n", (t->expires - current_time()) TO_MS);
+ debug("in loop %p expires in %d ms)\n", t->loop, (t->expires - current_time()) TO_MS);
else
debug("inactive)\n");
}
@@ -135,41 +92,40 @@ tm_new(pool *p)
return t;
}
-void
-tm_set(timer *t, btime when)
+static void
+tm_set_in_tl(timer *t, btime when, struct timeloop *local_timeloop)
{
- struct timeloop *loop = timeloop_current();
- uint tc = timers_count(loop);
+ uint tc = timers_count(local_timeloop);
if (!t->expires)
{
t->index = ++tc;
t->expires = when;
- BUFFER_PUSH(loop->timers) = t;
- HEAP_INSERT(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
+ BUFFER_PUSH(local_timeloop->timers) = t;
+ HEAP_INSERT(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP);
}
else if (t->expires < when)
{
t->expires = when;
- HEAP_INCREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
+ HEAP_INCREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
}
else if (t->expires > when)
{
t->expires = when;
- HEAP_DECREASE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
+ HEAP_DECREASE(local_timeloop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
}
-#ifdef CONFIG_BFD
- /* Hack to notify BFD loops */
- if ((loop != &main_timeloop) && (t->index == 1))
- wakeup_kick_current();
-#endif
+ t->loop = local_timeloop;
+
+ if ((t->index == 1) && (local_timeloop->coro != this_coro))
+ birdloop_ping(local_timeloop->loop);
}
void
-tm_start(timer *t, btime after)
+tm_set_in(timer *t, btime when, struct birdloop *loop)
{
- tm_set(t, current_time() + MAX(after, 0));
+ ASSERT_DIE(birdloop_inside(loop));
+ tm_set_in_tl(t, when, birdloop_time_loop(loop));
}
void
@@ -178,20 +134,22 @@ tm_stop(timer *t)
if (!t->expires)
return;
- struct timeloop *loop = timeloop_current();
- uint tc = timers_count(loop);
+ TLOCK_TIMER_ASSERT(t->loop);
- HEAP_DELETE(loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
- BUFFER_POP(loop->timers);
+ uint tc = timers_count(t->loop);
+
+ HEAP_DELETE(t->loop->timers.data, tc, timer *, TIMER_LESS, TIMER_SWAP, t->index);
+ BUFFER_POP(t->loop->timers);
t->index = -1;
t->expires = 0;
+ t->loop = NULL;
}
void
timers_init(struct timeloop *loop, pool *p)
{
- times_init(loop);
+ TLOCK_TIMER_ASSERT(loop);
BUFFER_INIT(loop->timers, p, 4);
BUFFER_PUSH(loop->timers) = NULL;
@@ -200,13 +158,15 @@ timers_init(struct timeloop *loop, pool *p)
void io_log_event(void *hook, void *data);
void
-timers_fire(struct timeloop *loop)
+timers_fire(struct timeloop *loop, int io_log)
{
+ TLOCK_TIMER_ASSERT(loop);
+
btime base_time;
timer *t;
- times_update(loop);
- base_time = loop->last_time;
+ times_update();
+ base_time = current_time();
while (t = timers_first(loop))
{
@@ -217,32 +177,25 @@ timers_fire(struct timeloop *loop)
{
btime when = t->expires + t->recurrent;
- if (when <= loop->last_time)
- when = loop->last_time + t->recurrent;
+ if (when <= base_time)
+ when = base_time + t->recurrent;
if (t->randomize)
when += random() % (t->randomize + 1);
- tm_set(t, when);
+ tm_set_in_tl(t, when, loop);
}
else
tm_stop(t);
/* This is ugly hack, we want to log just timers executed from the main I/O loop */
- if (loop == &main_timeloop)
+ if (io_log)
io_log_event(t->hook, t->data);
t->hook(t);
}
}
-void
-timer_init(void)
-{
- timers_init(&main_timeloop, &root_pool);
- timeloop_init_current();
-}
-
/**
* tm_parse_time - parse a date and time
diff --git a/lib/timer.h b/lib/timer.h
index c5ea430c..04544ace 100644
--- a/lib/timer.h
+++ b/lib/timer.h
@@ -12,8 +12,14 @@
#include "nest/bird.h"
#include "lib/buffer.h"
+#include "lib/io-loop.h"
+#include "lib/locking.h"
#include "lib/resource.h"
+#include <stdatomic.h>
+
+extern _Atomic btime last_time;
+extern _Atomic btime real_time;
typedef struct timer
{
@@ -25,36 +31,43 @@ typedef struct timer
uint randomize; /* Amount of randomization */
uint recurrent; /* Timer recurrence */
+ struct timeloop *loop; /* Loop where the timer is active */
+
int index;
} timer;
struct timeloop
{
BUFFER_(timer *) timers;
- btime last_time;
- btime real_time;
+ struct domain_generic *domain;
+ struct birdloop *loop;
+ struct coroutine *coro;
};
+#define TLOCK_TIMER_ASSERT(loop) ASSERT_DIE((loop)->domain && DG_IS_LOCKED((loop)->domain))
+#define TLOCK_LOCAL_ASSERT(loop) ASSERT_DIE(!(loop)->domain || DG_IS_LOCKED((loop)->domain))
+
static inline uint timers_count(struct timeloop *loop)
-{ return loop->timers.used - 1; }
+{ TLOCK_TIMER_ASSERT(loop); return loop->timers.used - 1; }
static inline timer *timers_first(struct timeloop *loop)
-{ return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
+{ TLOCK_TIMER_ASSERT(loop); return (loop->timers.used > 1) ? loop->timers.data[1] : NULL; }
-extern struct timeloop main_timeloop;
-
-btime current_time(void);
-btime current_real_time(void);
+#define current_time() atomic_load_explicit(&last_time, memory_order_acquire)
+#define current_real_time() atomic_load_explicit(&real_time, memory_order_acquire)
//#define now (current_time() TO_S)
//#define now_real (current_real_time() TO_S)
extern btime boot_time;
timer *tm_new(pool *p);
-void tm_set(timer *t, btime when);
-void tm_start(timer *t, btime after);
+#define tm_set(t, when) tm_set_in((t), (when), &main_birdloop)
+#define tm_start(t, after) tm_start_in((t), (after), &main_birdloop)
void tm_stop(timer *t);
+void tm_set_in(timer *t, btime when, struct birdloop *loop);
+#define tm_start_in(t, after, loop) tm_set_in((t), (current_time() + MAX_((after), 0)), loop)
+
static inline int
tm_active(timer *t)
{
@@ -94,15 +107,11 @@ tm_start_max(timer *t, btime after)
}
/* In sysdep code */
-void times_init(struct timeloop *loop);
-void times_update(struct timeloop *loop);
-void times_update_real_time(struct timeloop *loop);
+void times_update(void);
/* For I/O loop */
void timers_init(struct timeloop *loop, pool *p);
-void timers_fire(struct timeloop *loop);
-
-void timer_init(void);
+void timers_fire(struct timeloop *loop, int io_log);
struct timeformat {