diff --git a/lib/Makefile b/lib/Makefile index f620f484..5c106e2a 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,4 +1,4 @@ -src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c lockfree.c mac.c md5.c mempool.c net.c netindex.c patmatch.c printf.c rcu.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c +src := a-path.c a-set.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c defer.c event.c flowspec.c idm.c ip.c lists.c lockfree.c mac.c md5.c mempool.c net.c netindex.c patmatch.c printf.c rcu.c resource.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/defer.c b/lib/defer.c new file mode 100644 index 00000000..3d51534c --- /dev/null +++ b/lib/defer.c @@ -0,0 +1,40 @@ +/* + * BIRD -- Deferring calls to the end of the task + * + * (c) 2024 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include "lib/defer.h" + +_Thread_local struct deferred local_deferred = {}; + +static void +defer_execute(void *_ld) +{ + ASSERT_DIE(_ld == &local_deferred); + + /* Run */ + for (struct deferred_call *call = local_deferred.first; call; call = call->next) + call->hook(call); + + /* Cleanup */ + local_deferred.first = NULL; + local_deferred.last = &local_deferred.first; + + lp_flush(local_deferred.lp); +} + +void +defer_init(linpool *lp) +{ + local_deferred = (struct deferred) { + .e = { + .hook = defer_execute, + .data = &local_deferred, + }, + .lp = lp, + .last = &local_deferred.first, + }; +} diff --git a/lib/defer.h b/lib/defer.h new file mode 100644 index 00000000..b53d1efa --- /dev/null +++ b/lib/defer.h @@ -0,0 +1,48 @@ +/* + * BIRD -- Deferring calls to the end of the task + * + * (c) 2024 Maria Matejka + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +/** + * There are cases when we need to just run something multiple times after the + * current task ends, mostly for cleanup reasons, and it doesn't need the + * full-blown event list overhead. Therefore, one just can use this tool + * instead. */ + +#include "lib/birdlib.h" +#include "lib/event.h" +#include "lib/resource.h" +#include "lib/io-loop.h" + +struct deferred_call { + struct deferred_call *next; + void (*hook)(struct deferred_call *); +}; + +struct deferred { + event e; + linpool *lp; + struct deferred_call *first, **last; +}; + +extern _Thread_local struct deferred local_deferred; +void defer_init(linpool *lp); + +static inline void defer_call(struct deferred_call *call, size_t actual_size) { + /* Reallocate the call to the appropriate linpool */ + ASSERT_DIE(actual_size < 128); + struct deferred_call *a = lp_alloc(local_deferred.lp, actual_size); + memcpy(a, call, actual_size); + + /* If first, send the actual event to the local thread */ + if (local_deferred.last == &local_deferred.first) + ev_send_this_thread(&local_deferred.e); + + /* Add to list */ + a->next = NULL; + *local_deferred.last = a; + local_deferred.last = &a->next; +} diff --git a/lib/lockfree.c b/lib/lockfree.c index 11d98715..ff205f95 100644 --- a/lib/lockfree.c +++ b/lib/lockfree.c @@ -12,16 +12,10 @@ #define LOCAL_DEBUG -_Thread_local struct lfuc_unlock_queue *lfuc_unlock_queue; - -void lfuc_unlock_deferred(void *_q) +void lfuc_unlock_deferred(struct deferred_call *dc) { - struct lfuc_unlock_queue *q = _q; - for (u32 i = 0; i < q->pos; i++) - lfuc_unlock_immediately(q->block[i].c, q->block[i].el, q->block[i].ev); - - free_page(q); - lfuc_unlock_queue = NULL; + struct lfuc_unlock_queue_item *luqi = SKIP_BACK(struct lfuc_unlock_queue_item, dc, dc); + lfuc_unlock_immediately(luqi->c, luqi->el, luqi->ev); } #if 0 diff --git a/lib/lockfree.h b/lib/lockfree.h index 15b45010..0553aac1 100644 --- a/lib/lockfree.h +++ b/lib/lockfree.h @@ -10,6 +10,7 @@ #ifndef _BIRD_LOCKFREE_H_ #define _BIRD_LOCKFREE_H_ +#include "lib/defer.h" #include "lib/event.h" #include "lib/rcu.h" #include "lib/settle.h" @@ -108,46 +109,25 @@ static inline void lfuc_unlock_immediately(struct lfuc *c, event_list *el, event // return uc - LFUC_IN_PROGRESS - 1; } -extern _Thread_local struct lfuc_unlock_queue { - event e; - u32 pos; - struct lfuc_unlock_queue_block { - struct lfuc *c; - event_list *el; - event *ev; - } block[0]; -} *lfuc_unlock_queue; +struct lfuc_unlock_queue_item { + struct deferred_call dc; + struct lfuc *c; + event_list *el; + event *ev; +}; -void lfuc_unlock_deferred(void *queue); +void lfuc_unlock_deferred(struct deferred_call *dc); static inline void lfuc_unlock(struct lfuc *c, event_list *el, event *ev) { - static u32 queue_items = 0; - if (queue_items == 0) - { - ASSERT_DIE((u64) page_size > sizeof(struct lfuc_unlock_queue) + sizeof(struct lfuc_unlock_queue_block)); - queue_items = (page_size - OFFSETOF(struct lfuc_unlock_queue, block)) - / sizeof lfuc_unlock_queue->block[0]; - } - - if (!lfuc_unlock_queue || (lfuc_unlock_queue->pos >= queue_items)) - { - lfuc_unlock_queue = alloc_page(); - *lfuc_unlock_queue = (struct lfuc_unlock_queue) { - .e = { - .hook = lfuc_unlock_deferred, - .data = lfuc_unlock_queue, - }, - }; - - ev_send_this_thread(&lfuc_unlock_queue->e); - } - - lfuc_unlock_queue->block[lfuc_unlock_queue->pos++] = (struct lfuc_unlock_queue_block) { + struct lfuc_unlock_queue_item luqi = { + .dc.hook = lfuc_unlock_deferred, .c = c, .el = el, .ev = ev, }; + + defer_call(&luqi.dc, sizeof luqi); } /** diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c index 76865a85..fd0d9a25 100644 --- a/sysdep/unix/io-loop.c +++ b/sysdep/unix/io-loop.c @@ -17,6 +17,7 @@ #include "nest/bird.h" #include "lib/buffer.h" +#include "lib/defer.h" #include "lib/lists.h" #include "lib/locking.h" #include "lib/resource.h" @@ -794,6 +795,8 @@ bird_thread_main(void *arg) tmp_init(thr->pool, birdloop_domain(thr->meta)); init_list(&thr->loops); + defer_init(lp_new(thr->pool)); + thr->sock_changed = 1; struct pfd pfd; @@ -1377,6 +1380,8 @@ birdloop_init(void) birdloop_enter_locked(&main_birdloop); this_birdloop = &main_birdloop; this_thread = &main_thread; + + defer_init(lp_new(&root_pool)); } static void