0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2024-12-31 14:11:54 +00:00

IO: added a specific loop pickup group for BFD; to be done better in future

This commit is contained in:
Maria Matejka 2023-04-17 11:37:29 +02:00
parent 61e64d6a41
commit 787fb56da3
7 changed files with 128 additions and 90 deletions

View File

@ -17,7 +17,7 @@
extern struct birdloop main_birdloop;
/* Start a new birdloop owned by given pool and domain */
struct birdloop *birdloop_new(pool *p, uint order, const char *name);
struct birdloop *birdloop_new(pool *p, uint order, const char *name, btime max_latency);
/* Stop the loop. At the end, the @stopped callback is called unlocked in tail
* position to finish cleanup. Run birdloop_free() from that callback to free

View File

@ -1220,7 +1220,7 @@ proto_start(struct proto *p)
p->gr_recovery = 1;
if (p->cf->loop_order != DOMAIN_ORDER(the_bird))
p->loop = birdloop_new(p->pool, p->cf->loop_order, p->pool->name);
p->loop = birdloop_new(p->pool, p->cf->loop_order, p->pool->name, p->cf->loop_max_latency);
p->iface_sub.target = proto_event_list(p);

View File

@ -106,6 +106,7 @@ struct proto_config {
u32 debug, mrtdump; /* Debugging bitfields, both use D_* constants */
u32 router_id; /* Protocol specific router ID */
uint loop_order; /* Launch a birdloop on this locking level; use DOMAIN_ORDER(the_bird) for mainloop */
btime loop_max_latency; /* Request this specific maximum latency of loop; zero to default */
list channels; /* List of channel configs (struct channel_config) */
struct iface *vrf; /* Related VRF instance, NULL if global */

View File

@ -2907,7 +2907,7 @@ rt_setup(pool *pp, struct rtable_config *cf)
}
/* Start the service thread */
t->loop = birdloop_new(p, DOMAIN_ORDER(service), mb_sprintf(p, "Routing table %s", t->name));
t->loop = birdloop_new(p, DOMAIN_ORDER(service), mb_sprintf(p, "Routing table %s", t->name), 0);
birdloop_enter(t->loop);
birdloop_flag_set_handler(t->loop, &t->fh);
birdloop_leave(t->loop);

View File

@ -38,6 +38,7 @@ bfd_proto_start: proto_start BFD
{
this_proto = proto_config_new(&proto_bfd, $1);
this_proto->loop_order = DOMAIN_ORDER(proto);
this_proto->loop_max_latency = 10 MS_;
init_list(&BFD_CFG->patt_list);
init_list(&BFD_CFG->neigh_list);
BFD_CFG->accept_ipv4 = BFD_CFG->accept_ipv6 = 1;

View File

@ -31,7 +31,7 @@
#define THREAD_STACK_SIZE 65536 /* To be lowered in near future */
static struct birdloop *birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup);
static struct birdloop *birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup, struct birdloop_pickup_group *group);
/*
* Nanosecond time for accounting purposes
@ -497,14 +497,26 @@ sockets_fire(struct birdloop *loop)
*/
DEFINE_DOMAIN(resource);
static DOMAIN(resource) birdloop_domain;
static list birdloop_pickup;
static list bird_thread_pickup;
struct birdloop_pickup_group {
DOMAIN(resource) domain;
list loops;
list threads;
btime max_latency;
} pickup_groups[2] = {
{
/* all zeroes */
},
{
/* FIXME: make this dynamic, now it copies the loop_max_latency value from proto/bfd/config.Y */
.max_latency = 10 MS,
},
};
static _Thread_local struct bird_thread *this_thread;
static void
birdloop_set_thread(struct birdloop *loop, struct bird_thread *thr)
birdloop_set_thread(struct birdloop *loop, struct bird_thread *thr, struct birdloop_pickup_group *group)
{
struct bird_thread *old = loop->thread;
ASSERT_DIE(!thr != !old);
@ -530,9 +542,9 @@ birdloop_set_thread(struct birdloop *loop, struct bird_thread *thr)
{
old->loop_count--;
LOCK_DOMAIN(resource, birdloop_domain);
add_tail(&birdloop_pickup, &loop->n);
UNLOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
add_tail(&group->loops, &loop->n);
UNLOCK_DOMAIN(resource, group->domain);
}
/* Finished */
@ -543,54 +555,54 @@ birdloop_set_thread(struct birdloop *loop, struct bird_thread *thr)
}
static struct birdloop *
birdloop_take(void)
birdloop_take(struct birdloop_pickup_group *group)
{
struct birdloop *loop = NULL;
LOCK_DOMAIN(resource, birdloop_domain);
if (!EMPTY_LIST(birdloop_pickup))
LOCK_DOMAIN(resource, group->domain);
if (!EMPTY_LIST(group->loops))
{
/* Take the first loop from the pickup list and unlock */
loop = SKIP_BACK(struct birdloop, n, HEAD(birdloop_pickup));
loop = SKIP_BACK(struct birdloop, n, HEAD(group->loops));
rem_node(&loop->n);
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(resource, group->domain);
birdloop_set_thread(loop, this_thread);
birdloop_set_thread(loop, this_thread, group);
/* This thread goes to the end of the pickup list */
LOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
rem_node(&this_thread->n);
add_tail(&bird_thread_pickup, &this_thread->n);
add_tail(&group->threads, &this_thread->n);
/* If there are more loops to be picked up, wakeup the next thread in order */
if (!EMPTY_LIST(birdloop_pickup))
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
if (!EMPTY_LIST(group->loops))
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(group->threads)));
}
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(resource, group->domain);
return loop;
}
static void
birdloop_drop(struct birdloop *loop)
birdloop_drop(struct birdloop *loop, struct birdloop_pickup_group *group)
{
/* Remove loop from this thread's list */
rem_node(&loop->n);
/* Unset loop's thread */
if (birdloop_inside(loop))
birdloop_set_thread(loop, NULL);
birdloop_set_thread(loop, NULL, group);
else
{
birdloop_enter(loop);
birdloop_set_thread(loop, NULL);
birdloop_set_thread(loop, NULL, group);
birdloop_leave(loop);
}
/* Put loop into pickup list */
LOCK_DOMAIN(resource, birdloop_domain);
add_tail(&birdloop_pickup, &loop->n);
UNLOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
add_tail(&group->loops, &loop->n);
UNLOCK_DOMAIN(resource, group->domain);
}
static int
@ -615,7 +627,7 @@ bird_thread_main(void *arg)
tmp_init(thr->pool);
init_list(&thr->loops);
thr->meta = birdloop_new_internal(thr->pool, DOMAIN_ORDER(meta), "Thread Meta", 0);
thr->meta = birdloop_new_internal(thr->pool, DOMAIN_ORDER(meta), "Thread Meta", 0, thr->group);
thr->meta->thread = thr;
birdloop_enter(thr->meta);
@ -632,7 +644,7 @@ bird_thread_main(void *arg)
int timeout;
/* Pickup new loops */
struct birdloop *loop = birdloop_take();
struct birdloop *loop = birdloop_take(thr->group);
if (loop)
{
birdloop_enter(loop);
@ -738,23 +750,23 @@ bird_thread_cleanup(void *_thr)
}
static struct bird_thread *
bird_thread_start(btime max_latency)
bird_thread_start(struct birdloop_pickup_group *group)
{
ASSERT_DIE(birdloop_inside(&main_birdloop));
ASSERT_DIE(DOMAIN_IS_LOCKED(resource, group->domain));
pool *p = rp_new(&root_pool, "Thread");
struct bird_thread *thr = mb_allocz(p, sizeof(*thr));
thr->pool = p;
thr->cleanup_event = (event) { .hook = bird_thread_cleanup, .data = thr, };
thr->max_latency_ns = max_latency TO_NS;
thr->group = group;
thr->max_latency_ns = (group->max_latency ?: 5 S) TO_NS;
wakeup_init(thr);
ev_init_list(&thr->priority_events, NULL, "Thread direct event list");
LOCK_DOMAIN(resource, birdloop_domain);
add_tail(&bird_thread_pickup, &thr->n);
UNLOCK_DOMAIN(resource, birdloop_domain);
add_tail(&group->threads, &thr->n);
int e = 0;
@ -782,8 +794,9 @@ static uint thread_dropper_goal;
static void
bird_thread_shutdown(void * _ UNUSED)
{
LOCK_DOMAIN(resource, birdloop_domain);
int dif = list_length(&bird_thread_pickup) - thread_dropper_goal;
struct birdloop_pickup_group *group = this_thread->group;
LOCK_DOMAIN(resource, group->domain);
int dif = list_length(&group->threads) - thread_dropper_goal;
struct birdloop *tdl_stop = NULL;
if (dif > 0)
@ -794,7 +807,7 @@ bird_thread_shutdown(void * _ UNUSED)
thread_dropper = NULL;
}
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(resource, group->domain);
DBG("Thread pickup size differs from dropper goal by %d%s\n", dif, tdl_stop ? ", stopping" : "");
@ -807,18 +820,18 @@ bird_thread_shutdown(void * _ UNUSED)
struct bird_thread *thr = this_thread;
/* Leave the thread-picker list to get no more loops */
LOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
rem_node(&thr->n);
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(resource, group->domain);
/* Drop loops including the thread dropper itself */
while (!EMPTY_LIST(thr->loops))
birdloop_drop(HEAD(thr->loops));
birdloop_drop(HEAD(thr->loops), group);
/* Let others know about new loops */
if (!EMPTY_LIST(birdloop_pickup))
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
UNLOCK_DOMAIN(resource, birdloop_domain);
if (!EMPTY_LIST(group->loops))
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(group->threads)));
UNLOCK_DOMAIN(resource, group->domain);
/* Leave the thread-dropper loop as we aren't going to return. */
birdloop_leave(thread_dropper);
@ -855,26 +868,30 @@ bird_thread_commit(struct config *new, struct config *old UNUSED)
while (1)
{
LOCK_DOMAIN(resource, birdloop_domain);
int dif = list_length(&bird_thread_pickup) - (thread_dropper_goal = new->thread_count);
struct birdloop_pickup_group *group = &pickup_groups[0];
LOCK_DOMAIN(resource, group->domain);
int dif = list_length(&group->threads) - (thread_dropper_goal = new->thread_count);
_Bool thread_dropper_running = !!thread_dropper;
UNLOCK_DOMAIN(resource, birdloop_domain);
if (dif < 0)
{
bird_thread_start(5 S);
bird_thread_start(group);
UNLOCK_DOMAIN(resource, group->domain);
continue;
}
UNLOCK_DOMAIN(resource, group->domain);
if ((dif > 0) && !thread_dropper_running)
{
struct birdloop *tdl = birdloop_new(&root_pool, DOMAIN_ORDER(control), "Thread dropper");
struct birdloop *tdl = birdloop_new(&root_pool, DOMAIN_ORDER(control), "Thread dropper", group->max_latency);
event *tde = ev_new_init(tdl->pool, bird_thread_shutdown, NULL);
LOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
thread_dropper = tdl;
thread_dropper_event = tde;
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(resource, group->domain);
ev_send_loop(thread_dropper, thread_dropper_event);
}
@ -945,26 +962,31 @@ bird_thread_show(void *data)
{
the_bird_lock();
LOCK_DOMAIN(resource, birdloop_domain);
if (!EMPTY_LIST(birdloop_pickup))
if (tsd->show_loops)
{
cli_printf(tsd->cli, -1026, "Unassigned loops");
WALK_LIST(loop, birdloop_pickup)
cli_printf(tsd->cli, -1026, " Loop %s time: %t", domain_name(loop->time.domain), loop->total_time_spent_ns NS);
}
else
{
uint count = 0;
u64 total_time_ns = 0;
WALK_LIST(loop, birdloop_pickup)
for (int i=0; i<2; i++)
{
struct birdloop_pickup_group *group = &pickup_groups[i];
LOCK_DOMAIN(resource, group->domain);
if (!EMPTY_LIST(group->loops))
if (tsd->show_loops)
{
count++;
total_time_ns += loop->total_time_spent_ns;
cli_printf(tsd->cli, -1026, "Unassigned loops");
WALK_LIST(loop, group->loops)
cli_printf(tsd->cli, -1026, " Loop %s time: %t", domain_name(loop->time.domain), loop->total_time_spent_ns NS);
}
cli_printf(tsd->cli, -1026, "Unassigned loops: %d, total time %t", count, total_time_ns NS);
}
UNLOCK_DOMAIN(resource, birdloop_domain);
else
{
uint count = 0;
u64 total_time_ns = 0;
WALK_LIST(loop, group->loops)
{
count++;
total_time_ns += loop->total_time_spent_ns;
}
cli_printf(tsd->cli, -1026, "Unassigned loops: %d, total time %t", count, total_time_ns NS);
}
UNLOCK_DOMAIN(resource, group->domain);
}
cli_write_trigger(tsd->cli);
DOMAIN_FREE(control, tsd->lock);
@ -989,19 +1011,24 @@ cmd_show_threads(int show_loops)
this_cli->cont = bird_thread_show_cli_cont;
this_cli->cleanup = bird_thread_show_cli_cleanup;
LOCK_DOMAIN(control, tsd->lock);
LOCK_DOMAIN(resource, birdloop_domain);
struct bird_thread *thr;
WALK_LIST(thr, bird_thread_pickup)
for (int i=0; i<2; i++)
{
tsd->total++;
ev_send(&thr->priority_events, ev_new_init(p, bird_thread_show, tsd));
wakeup_do_kick(thr);
}
struct birdloop_pickup_group *group = &pickup_groups[i];
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(control, tsd->lock);
LOCK_DOMAIN(control, tsd->lock);
LOCK_DOMAIN(resource, group->domain);
struct bird_thread *thr;
WALK_LIST(thr, group->threads)
{
tsd->total++;
ev_send(&thr->priority_events, ev_new_init(p, bird_thread_show, tsd));
wakeup_do_kick(thr);
}
UNLOCK_DOMAIN(resource, group->domain);
UNLOCK_DOMAIN(control, tsd->lock);
}
}
/*
@ -1018,9 +1045,14 @@ birdloop_init(void)
{
ns_init();
birdloop_domain = DOMAIN_NEW(resource, "Loop Pickup");
init_list(&birdloop_pickup);
init_list(&bird_thread_pickup);
for (int i=0; i<2; i++)
{
struct birdloop_pickup_group *group = &pickup_groups[i];
group->domain = DOMAIN_NEW(resource, "Loop Pickup");
init_list(&group->loops);
init_list(&group->threads);
}
wakeup_init(main_birdloop.thread);
@ -1146,7 +1178,7 @@ birdloop_run_timer(timer *tm)
}
static struct birdloop *
birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup)
birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup, struct birdloop_pickup_group *group)
{
struct domain_generic *dg = domain_new(name, order);
@ -1170,10 +1202,13 @@ birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup
if (request_pickup)
{
LOCK_DOMAIN(resource, birdloop_domain);
add_tail(&birdloop_pickup, &loop->n);
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(bird_thread_pickup)));
UNLOCK_DOMAIN(resource, birdloop_domain);
LOCK_DOMAIN(resource, group->domain);
add_tail(&group->loops, &loop->n);
if (EMPTY_LIST(group->threads))
bird_thread_start(group);
wakeup_do_kick(SKIP_BACK(struct bird_thread, n, HEAD(group->threads)));
UNLOCK_DOMAIN(resource, group->domain);
}
else
loop->n.next = loop->n.prev = &loop->n;
@ -1184,9 +1219,9 @@ birdloop_new_internal(pool *pp, uint order, const char *name, int request_pickup
}
struct birdloop *
birdloop_new(pool *pp, uint order, const char *name)
birdloop_new(pool *pp, uint order, const char *name, btime max_latency)
{
return birdloop_new_internal(pp, order, name, 1);
return birdloop_new_internal(pp, order, name, 1, max_latency ? &pickup_groups[1] : &pickup_groups[0]);
}
static void

View File

@ -78,6 +78,7 @@ struct bird_thread
struct rcu_thread rcu;
list loops;
struct birdloop_pickup_group *group;
pool *pool;
struct pfd *pfd;