Commit 16cff36a authored by Maria Matejka's avatar Maria Matejka
Browse files

Threads and loops hopefully finished

parent 2deb9de0
Pipeline #106526 failed with stages
in 54 minutes and 16 seconds
......@@ -61,6 +61,7 @@ Reply codes of BIRD command-line interface
1023 Show Babel interfaces
1024 Show Babel neighbors
1025 Show Babel entries
1026 Show threads
8000 Reply too long
8001 Route not found
......
......@@ -261,7 +261,7 @@ ev_send(event_list *l, event *e)
memory_order_acq_rel, memory_order_acquire));
edlog(l, e, next, 4, EDL_SEND);
birdloop_ping(l->loop);
if (l->loop) birdloop_ping(l->loop);
}
void io_log_event(void *hook, void *data);
......
......@@ -14,6 +14,7 @@ struct domain_generic;
/* Here define the global lock order; first to last. */
struct lock_order {
struct domain_generic *the_bird;
struct domain_generic *control;
struct domain_generic *proto;
struct domain_generic *service;
struct domain_generic *rtable;
......@@ -34,6 +35,9 @@ 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_NAME(type, d) domain_name((d).type)
const char *domain_name(struct domain_generic *);
#define DOMAIN_NULL(type) (DOMAIN(type)) {}
#define LOCK_DOMAIN(type, d) do_lock(((d).type), &(locking_stack.type))
......
......@@ -2856,7 +2856,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 tahle %s", t->name));
t->loop = birdloop_new(p, DOMAIN_ORDER(service), mb_sprintf(p, "Routing table %s", t->name));
birdloop_enter(t->loop);
birdloop_flag_set_handler(t->loop, &t->fh);
birdloop_leave(t->loop);
......
......@@ -145,6 +145,11 @@ CF_CLI_HELP(GRACEFUL, restart, [[Shut the daemon down for graceful restart]])
CF_CLI(GRACEFUL RESTART,,, [[Shut the daemon down for graceful restart]])
{ cmd_graceful_restart(); } ;
CF_CLI(SHOW THREADS,,, [[Write out thread information]])
{ cmd_show_threads(0); } ;
CF_CLI(SHOW THREADS ALL,,, [[Write out thread and IO loop information]])
{ cmd_show_threads(1); } ;
cfg_name:
/* empty */ { $$ = NULL; }
......
......@@ -70,6 +70,12 @@ domain_free(struct domain_generic *dg)
xfree(dg);
}
const char *
domain_name(struct domain_generic *dg)
{
return dg->name;
}
uint dg_order(struct domain_generic *dg)
{
return dg->order;
......
......@@ -18,6 +18,7 @@
#include "lib/buffer.h"
#include "lib/lists.h"
#include "lib/locking.h"
#include "lib/resource.h"
#include "lib/event.h"
#include "lib/timer.h"
......@@ -26,9 +27,36 @@
#include "lib/io-loop.h"
#include "sysdep/unix/io-loop.h"
#include "conf/conf.h"
#include "nest/cli.h"
#define THREAD_STACK_SIZE 65536 /* To be lowered in near future */
/*
* Nanosecond time for accounting purposes
*
* A fixed point on startup is set as zero, all other values are relative to that.
* Caution: this overflows after like 500 years or so. If you plan to run
* BIRD for such a long time, please implement some means of overflow prevention.
*/
static struct timespec ns_begin;
static void ns_init(void)
{
if (clock_gettime(CLOCK_MONOTONIC, &ns_begin))
bug("clock_gettime: %m");
}
static u64 ns_now(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts))
bug("clock_gettime: %m");
return (u64) (ts.tv_sec - ns_begin.tv_sec) * 1000000000 + ts.tv_nsec - ns_begin.tv_nsec;
}
/*
* Current thread context
*/
......@@ -90,21 +118,6 @@ birdloop_process_flags(struct birdloop *loop)
return !!flags;
}
static int
birdloop_run_events(struct birdloop *loop)
{
btime begin = current_time();
while (current_time() - begin < 5 MS)
{
if (!ev_run_list(&loop->event_list))
return 0;
times_update();
}
return 1;
}
/*
* Wakeup code for birdloop
*/
......@@ -242,7 +255,8 @@ sockets_add(struct birdloop *loop, sock *s)
loop->sock_num++;
s->index = -1;
loop->poll_changed = 1;
if (loop->thread)
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
birdloop_ping(loop);
}
......@@ -263,7 +277,9 @@ sockets_remove(struct birdloop *loop, sock *s)
/* Decouple the socket from the loop at all. */
rem_node(&s->n);
loop->sock_num--;
loop->poll_changed = 1;
if (loop->thread)
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
s->index = -1;
/* Close the filedescriptor. If it ever gets into the poll(), it just returns
......@@ -306,7 +322,6 @@ sockets_prepare(struct birdloop *loop, struct pollfd *pfd, struct pollfd *end)
pfd++;
}
loop->poll_changed = 0;
return pfd;
}
......@@ -342,7 +357,7 @@ sockets_fire(struct birdloop *loop)
if (rev & POLLOUT)
{
loop->poll_changed = 1;
atomic_store_explicit(&loop->thread->poll_changed, 1, memory_order_release);
while (e = sk_write(s))
;
}
......@@ -358,10 +373,12 @@ static DOMAIN(resource) birdloop_domain;
static list birdloop_pickup;
static list bird_thread_pickup;
static _Thread_local struct bird_thread *this_thread;
static void *
bird_thread_main(void *arg)
{
struct bird_thread *thr = arg;
struct bird_thread *thr = this_thread = arg;
rcu_thread_start(&thr->rcu);
synchronize_rcu();
......@@ -369,7 +386,7 @@ bird_thread_main(void *arg)
tmp_init(thr->pool);
init_list(&thr->loops);
int refresh_sockets = 1;
u32 refresh_sockets = 1;
struct pollfd *pfd, *end;
......@@ -387,10 +404,11 @@ bird_thread_main(void *arg)
UNLOCK_DOMAIN(resource, birdloop_domain);
add_tail(&thr->loops, &loop->n);
refresh_sockets = 1;
birdloop_enter(loop);
loop->thread = thr;
if (!EMPTY_LIST(loop->sock_list))
refresh_sockets = 1;
birdloop_leave(loop);
/* If there are more loops to be picked up, wakeup the next thread */
......@@ -407,21 +425,33 @@ bird_thread_main(void *arg)
WALK_LIST2(loop, nn, thr->loops, n)
{
birdloop_enter(loop);
u64 after_enter = ns_now();
timer *t;
times_update();
timers_fire(&loop->time, 0);
if (birdloop_process_flags(loop) + birdloop_run_events(loop))
int again = birdloop_process_flags(loop) + ev_run_list(&loop->event_list);
#if 0
if (loop->n.next->next)
__builtin_prefetch(SKIP_BACK(struct birdloop, n, loop->n.next)->time.domain);
#endif
if (again)
timeout = MIN(0, timeout);
else if (t = timers_first(&loop->time))
timeout = MIN(((tm_remains(t) TO_MS) + 1), timeout);
if (loop->poll_changed)
refresh_sockets = 1;
u64 before_leave = ns_now();
loop->total_time_spent_ns += (before_leave - after_enter);
birdloop_leave(loop);
ev_run_list(&thr->priority_events);
}
refresh_sockets += atomic_exchange_explicit(&thr->poll_changed, 0, memory_order_acq_rel);
if (!refresh_sockets && ((timeout < 0) || (timeout > 5000)))
flush_local_pages();
......@@ -462,19 +492,23 @@ poll_retry:;
/* Drain wakeup fd */
if (thr->pfd[0].revents & POLLIN)
{
ASSERT_DIE(rv > 0);
rv--;
wakeup_drain(thr);
}
atomic_exchange_explicit(&thr->ping_sent, 0, memory_order_acq_rel);
if (!rv && !atomic_exchange_explicit(&thr->run_cleanup, 0, memory_order_acq_rel))
continue;
/* Process stops and regular sockets */
node *nxt;
WALK_LIST2_DELSAFE(loop, nn, nxt, thr->loops, n)
{
birdloop_enter(loop);
if (loop->poll_changed)
refresh_sockets = 1;
if (loop->stopped)
{
/* Flush remaining events */
......@@ -509,13 +543,14 @@ bird_thread_start(pool *pp)
{
pool *p = rp_new(pp, "Thread");
struct bird_thread *thr = mb_alloc(p, sizeof(*thr));
struct bird_thread *thr = mb_allocz(p, sizeof(*thr));
thr->pool = p;
thr->pfd = mb_alloc(p, sizeof(struct pollfd) * (thr->pfd_max = 16));
atomic_store_explicit(&thr->ping_sent, 0, memory_order_relaxed);
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);
......@@ -547,6 +582,102 @@ bird_thread_stop(struct bird_thread *thr)
rfree(thr->pool);
}
DEFINE_DOMAIN(control);
struct bird_thread_show_data {
cli *cli;
pool *pool;
DOMAIN(control) lock;
uint total;
uint done;
u8 show_loops;
};
static void
bird_thread_show_cli_cont(struct cli *c UNUSED)
{
/* Explicitly do nothing to prevent CLI from trying to parse another command. */
}
static int
bird_thread_show_cli_cleanup(struct cli *c UNUSED)
{
return 1; /* Defer the cleanup until the writeout is finished. */
}
static void
bird_thread_show(void *data)
{
struct bird_thread_show_data *tsd = data;
LOCK_DOMAIN(control, tsd->lock);
if (tsd->show_loops)
cli_printf(tsd->cli, -1026, "Thread %p", this_thread);
u64 total_time_ns = 0;
struct birdloop *loop;
WALK_LIST(loop, this_thread->loops)
{
if (tsd->show_loops)
cli_printf(tsd->cli, -1026, " Loop %s time: %t", domain_name(loop->time.domain), loop->total_time_spent_ns NS);
total_time_ns += loop->total_time_spent_ns;
}
tsd->done++;
int last = (tsd->done == tsd->total);
if (last)
{
tsd->cli->cont = NULL;
tsd->cli->cleanup = NULL;
}
if (tsd->show_loops)
cli_printf(tsd->cli, (last ? 1 : -1) * 1026, " Total time: %t", total_time_ns NS);
else
cli_printf(tsd->cli, (last ? 1 : -1) * 1026, "Thread %p time %t", this_thread, total_time_ns NS);
UNLOCK_DOMAIN(control, tsd->lock);
if (last)
{
the_bird_lock();
cli_write_trigger(tsd->cli);
DOMAIN_FREE(control, tsd->lock);
rfree(tsd->pool);
the_bird_unlock();
}
}
void
cmd_show_threads(int show_loops)
{
pool *p = rp_new(&root_pool, "Show Threads");
struct bird_thread_show_data *tsd = mb_allocz(p, sizeof(struct bird_thread_show_data));
tsd->lock = DOMAIN_NEW(control, "Show Threads");
tsd->cli = this_cli;
tsd->pool = p;
tsd->show_loops = 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)
{
tsd->total++;
ev_send(&thr->priority_events, ev_new_init(p, bird_thread_show, tsd));
wakeup_do_kick(thr);
}
UNLOCK_DOMAIN(resource, birdloop_domain);
UNLOCK_DOMAIN(control, tsd->lock);
}
/*
* Birdloop
......@@ -560,11 +691,13 @@ static void birdloop_enter_locked(struct birdloop *loop);
void
birdloop_init(void)
{
ns_init();
birdloop_domain = DOMAIN_NEW(resource, "Loop Pickup");
init_list(&birdloop_pickup);
init_list(&bird_thread_pickup);
#define THREAD_PREFORK_COUNT 1
#define THREAD_PREFORK_COUNT 6
for (int i=0; i<THREAD_PREFORK_COUNT; i++)
bird_thread_start(&root_pool);
......@@ -613,7 +746,10 @@ birdloop_do_stop(struct birdloop *loop, void (*stopped)(void *data), void *data)
loop->stopped = stopped;
loop->stop_data = data;
if (loop->thread)
{
atomic_store_explicit(&loop->thread->run_cleanup, 1, memory_order_release);
wakeup_do_kick(loop->thread);
}
}
void
......
......@@ -31,7 +31,6 @@ struct birdloop
int sock_num;
uint ping_pending;
uint poll_changed;
uint links;
......@@ -45,6 +44,8 @@ struct birdloop
struct bird_thread *thread;
struct pollfd *pfd;
u64 total_time_spent_ns;
};
struct bird_thread
......@@ -55,7 +56,11 @@ struct bird_thread
uint pfd_max;
_Atomic u32 ping_sent;
_Atomic u32 run_cleanup;
_Atomic u32 poll_changed;
struct pipe wakeup;
event_list priority_events;
pthread_t thread_id;
pthread_attr_t thread_attr;
......@@ -66,5 +71,4 @@ struct bird_thread
pool *pool;
};
#endif
......@@ -32,6 +32,7 @@ void cmd_reconfig_undo(void);
void cmd_reconfig_status(void);
void cmd_shutdown(void);
void cmd_graceful_restart(void);
void cmd_show_threads(int);
#define UNIX_DEFAULT_CONFIGURE_TIMEOUT 300
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment