From 61dcbb1d83882a6fa89c0c8218495ac8f2129012 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Thu, 13 Jun 2024 16:30:51 +0200 Subject: [PATCH] Global runtime values separated from config --- conf/conf.c | 68 +++++++++++++++++++++++++++++++------------ conf/conf.h | 22 +++++++++++++- lib/io-loop.h | 2 +- nest/cmds.c | 16 ++++++---- nest/iface.c | 4 ++- nest/proto.c | 27 ++++++++++++----- nest/protocol.h | 2 +- proto/bfd/bfd.c | 6 +++- proto/bgp/packets.c | 9 ++++-- sysdep/unix/domain.c | 3 +- sysdep/unix/io-loop.c | 8 +++-- sysdep/unix/io.c | 19 +++++++----- sysdep/unix/log.c | 8 +++-- sysdep/unix/main.c | 8 +++-- 14 files changed, 146 insertions(+), 56 deletions(-) diff --git a/conf/conf.c b/conf/conf.c index 981db709..0db7ae47 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -105,7 +105,6 @@ config_alloc(const char *name) c->pool = p; c->mem = l; c->file_name = ndup; - c->load_time = current_time(); c->tf_route = c->tf_proto = TM_ISO_SHORT_MS; c->tf_base = c->tf_log = TM_ISO_LONG_MS; c->gr_wait = DEFAULT_GR_WAIT; @@ -150,11 +149,6 @@ config_parse(struct config *c) if (EMPTY_LIST(c->protos)) cf_error("No protocol is specified in the config file"); - /* - if (!c->router_id) - cf_error("Router ID must be configured manually"); - */ - done = 1; cleanup: @@ -244,25 +238,61 @@ config_del_obstacle(struct config *c) ev_send_loop(&main_birdloop, &c->done_event); } +struct global_runtime global_runtime_internal[2] = {{ + .tf_log = { + .fmt1 = "%F %T.%3f", + }, +}}; +struct global_runtime * _Atomic global_runtime = &global_runtime_internal[0]; + static void global_commit(struct config *new, struct config *old) { - if (!old) - return; + /* Updating the global runtime. */ + struct global_runtime *og = atomic_load_explicit(&global_runtime, memory_order_relaxed); + struct global_runtime *ng = &global_runtime_internal[og == &global_runtime_internal[0]]; + ASSERT_DIE(ng != og); - if (!new->router_id) +#define COPY(x) ng->x = new->x; + MACRO_FOREACH(COPY, + tf_route, + tf_proto, + tf_log, + tf_base, + cli_debug, + latency_debug, + latency_limit, + watchdog_warning, + watchdog_timeout, + gr_wait, + hostname + ); +#undef COPY + + ng->load_time = current_time(); + + if (new->router_id) + ng->router_id = new->router_id; + else if (old) + { + /* The startup router ID must be determined after start of device protocol, + * thus if old == NULL then we do nothing */ + ng->router_id = og->router_id; + + if (new->router_id_from) { - new->router_id = old->router_id; - - if (new->router_id_from) - { - u32 id = if_choose_router_id(new->router_id_from, old->router_id); - if (!id) - log(L_WARN "Cannot determine router ID, using old one"); - else - new->router_id = id; - } + u32 id = if_choose_router_id(new->router_id_from, og->router_id); + if (!id) + log(L_WARN "Cannot determine router ID, using old one"); + else + ng->router_id = id; } + } + + atomic_store_explicit(&global_runtime, ng, memory_order_release); + + /* We have to wait until every reader surely doesn't read the old values */ + synchronize_rcu(); } static int diff --git a/conf/conf.h b/conf/conf.h index a4ce225f..1ce5468c 100644 --- a/conf/conf.h +++ b/conf/conf.h @@ -72,9 +72,29 @@ struct config { event done_event; /* Called when obstacle_count reaches zero */ int shutdown; /* This is a pseudo-config for daemon shutdown */ int gr_down; /* This is a pseudo-config for graceful restart */ - btime load_time; /* When we've got this configuration */ }; +struct global_runtime { + struct timeformat tf_route; /* Time format for 'show route' */ + struct timeformat tf_proto; /* Time format for 'show protocol' */ + struct timeformat tf_log; /* Time format for the logfile */ + struct timeformat tf_base; /* Time format for other purposes */ + + u32 gr_wait; /* Graceful restart wait timeout (sec) */ + + u32 router_id; /* Our Router ID */ + const char *hostname; /* Hostname */ + + btime load_time; /* When we reconfigured last time */ + int cli_debug; /* Tracing of CLI connections and commands */ + enum latency_debug_flags latency_debug; + u32 latency_limit; /* Events with longer duration are logged (us) */ + u32 watchdog_warning; /* I/O loop watchdog limit for warning (us) */ + u32 watchdog_timeout; /* Watchdog timeout (in seconds, 0 = disabled) */ +}; + +extern struct global_runtime * _Atomic global_runtime; + /* Please don't use these variables in protocols. Use proto_config->global instead. */ extern struct config *config; /* Currently active configuration */ extern _Thread_local struct config *new_config; /* Configuration being parsed */ diff --git a/lib/io-loop.h b/lib/io-loop.h index 5cad3033..03fe2529 100644 --- a/lib/io-loop.h +++ b/lib/io-loop.h @@ -25,7 +25,7 @@ _Bool task_before_halftime(void); #define MAYBE_DEFER_TASK(target, event, fmt, args...) do { \ if (!task_still_in_limit()) { \ - if (config && (config->latency_debug & DL_SCHEDULING)) \ + if (atomic_load_explicit(&global_runtime, memory_order_relaxed)->latency_debug & DL_SCHEDULING) \ log(L_TRACE "Deferring " fmt, ##args); \ return ev_send(target, event); \ } } while (0) diff --git a/nest/cmds.c b/nest/cmds.c index 6f879635..b3729a5b 100644 --- a/nest/cmds.c +++ b/nest/cmds.c @@ -24,14 +24,18 @@ cmd_show_status(void) { byte tim[TM_DATETIME_BUFFER_SIZE]; + rcu_read_lock(); + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_acquire); + struct timeformat *tf = &gr->tf_base; + cli_msg(-1000, "BIRD " BIRD_VERSION); - tm_format_time(tim, &config->tf_base, current_time()); - cli_msg(-1011, "Router ID is %R", config->router_id); - cli_msg(-1011, "Hostname is %s", config->hostname); + tm_format_time(tim, tf, current_time()); + cli_msg(-1011, "Router ID is %R", gr->router_id); + cli_msg(-1011, "Hostname is %s", gr->hostname); cli_msg(-1011, "Current server time is %s", tim); - tm_format_time(tim, &config->tf_base, boot_time); + tm_format_time(tim, tf, boot_time); cli_msg(-1011, "Last reboot on %s", tim); - tm_format_time(tim, &config->tf_base, config->load_time); + tm_format_time(tim, tf, gr->load_time); cli_msg(-1011, "Last reconfiguration on %s", tim); graceful_restart_show_status(); @@ -42,6 +46,8 @@ cmd_show_status(void) cli_msg(13, "Reconfiguration in progress"); else cli_msg(13, "Daemon is up and running"); + + rcu_read_unlock(); } void diff --git a/nest/iface.c b/nest/iface.c index 1e637479..1059ae06 100644 --- a/nest/iface.c +++ b/nest/iface.c @@ -164,7 +164,9 @@ if_dump_all(void) debug("Known network interfaces:\n"); IFACE_WALK(i) if_dump(i); - debug("Router ID: %08x\n", config->router_id); + rcu_read_lock(); + debug("Router ID: %08x\n", atomic_load_explicit(&global_runtime, memory_order_relaxed)->router_id); + rcu_read_unlock(); } void diff --git a/nest/proto.c b/nest/proto.c index 696e098c..e6613d24 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -1624,11 +1624,15 @@ protos_do_commit(struct config *new, struct config *old, int type) /* Determine router ID for the first time - it has to be here and not in global_commit() because it is postponed after start of device protocol */ - if (!config->router_id) + if ((phase == PROTOCOL_STARTUP_NECESSARY) && !old) { - config->router_id = if_choose_router_id(config->router_id_from, 0); - if (!config->router_id) - die("Cannot determine router ID, please configure it manually"); + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (!gr->router_id) + { + gr->router_id = if_choose_router_id(new->router_id_from, 0); + if (!gr->router_id) + die("Cannot determine router ID, please configure it manually"); + } } /* Commit next round of protocols */ @@ -1781,7 +1785,8 @@ graceful_restart_init(void) graceful_restart_state = GRS_ACTIVE; gr_wait_timer = tm_new_init(proto_pool, graceful_restart_done, NULL, 0, 0); - tm_start(gr_wait_timer, config->gr_wait S); + u32 gr_wait = atomic_load_explicit(&global_runtime, memory_order_relaxed)->gr_wait; + tm_start(gr_wait_timer, gr_wait S); } /** @@ -1795,7 +1800,7 @@ graceful_restart_init(void) * restart wait timer fires (but there are still some locks). */ static void -graceful_restart_done(timer *t UNUSED) +graceful_restart_done(timer *t) { log(L_INFO "Graceful restart done"); graceful_restart_state = GRS_DONE; @@ -1821,6 +1826,8 @@ graceful_restart_done(timer *t UNUSED) } graceful_restart_locks = 0; + + rfree(t); } void @@ -1831,7 +1838,8 @@ graceful_restart_show_status(void) cli_msg(-24, "Graceful restart recovery in progress"); cli_msg(-24, " Waiting for %d channels to recover", graceful_restart_locks); - cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), config->gr_wait); + cli_msg(-24, " Wait timer is %t/%u", tm_remains(gr_wait_timer), + atomic_load_explicit(&global_runtime, memory_order_relaxed)->gr_wait); } /** @@ -2422,7 +2430,10 @@ proto_cmd_show(struct proto *p, uintptr_t verbose, int cnt) buf[0] = 0; if (p->proto->get_status) p->proto->get_status(p, buf); - tm_format_time(tbuf, &config->tf_proto, p->last_state_change); + + rcu_read_lock(); + tm_format_time(tbuf, &atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_proto, p->last_state_change); + rcu_read_unlock(); cli_msg(-1002, "%-10s %-10s %-10s %-6s %-12s %s", p->name, p->proto->name, diff --git a/nest/protocol.h b/nest/protocol.h index 9d2e6292..a4279c5a 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -302,7 +302,7 @@ static inline struct domain_generic *proto_domain(struct proto *p) static inline u32 proto_get_router_id(struct proto_config *pc) { - return pc->router_id ? pc->router_id : pc->global->router_id; + return pc->router_id ?: atomic_load_explicit(&global_runtime, memory_order_relaxed)->router_id; } diff --git a/proto/bfd/bfd.c b/proto/bfd/bfd.c index de3e58b2..e6bf3f39 100644 --- a/proto/bfd/bfd.c +++ b/proto/bfd/bfd.c @@ -1237,7 +1237,11 @@ bfd_show_session(struct bfd_session *s, int details) byte dbuf[BFD_DIAG_BUFFER_SIZE]; byte tbuf[TM_DATETIME_BUFFER_SIZE]; - tm_format_time(tbuf, &config->tf_proto, s->last_state_change); + + rcu_read_lock(); + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + tm_format_time(tbuf, &gr->tf_proto, s->last_state_change); + rcu_read_unlock(); if (!details) { diff --git a/proto/bgp/packets.c b/proto/bgp/packets.c index 7e937aed..16029118 100644 --- a/proto/bgp/packets.c +++ b/proto/bgp/packets.c @@ -283,13 +283,16 @@ bgp_prepare_capabilities(struct bgp_conn *conn) if (p->cf->llgr_mode) caps->llgr_aware = 1; - if (p->cf->enable_hostname && config->hostname) + rcu_read_lock(); + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (p->cf->enable_hostname && gr->hostname) { - size_t length = strlen(config->hostname); + size_t length = strlen(gr->hostname); char *hostname = mb_allocz(p->p.pool, length+1); - memcpy(hostname, config->hostname, length+1); + memcpy(hostname, gr->hostname, length+1); caps->hostname = hostname; } + rcu_read_unlock(); /* Allocate and fill per-AF fields */ BGP_WALK_CHANNELS(p, c) diff --git a/sysdep/unix/domain.c b/sysdep/unix/domain.c index 2d2c0ff4..2817455f 100644 --- a/sysdep/unix/domain.c +++ b/sysdep/unix/domain.c @@ -123,7 +123,8 @@ void do_lock(struct domain_generic *dg, struct domain_generic **lsp) btime lock_begin = current_time(); pthread_mutex_lock(&dg->mutex); btime duration = current_time() - lock_begin; - if (config && (duration > config->watchdog_warning)) + btime wdw = atomic_load_explicit(&global_runtime, memory_order_relaxed)->watchdog_warning; + if (wdw && (duration > wdw)) log(L_WARN "Locking of %s took %d ms", dg->name, (int) (duration TO_MS)); if (dg->prev || dg->locked_by) diff --git a/sysdep/unix/io-loop.c b/sysdep/unix/io-loop.c index ec8aa729..49e9f2db 100644 --- a/sysdep/unix/io-loop.c +++ b/sysdep/unix/io-loop.c @@ -152,9 +152,10 @@ static _Thread_local struct birdloop *birdloop_wakeup_masked; static _Thread_local uint birdloop_wakeup_masked_count; #define LOOP_NAME(loop) domain_name((loop)->time.domain) +#define LATENCY_DEBUG(flags) (atomic_load_explicit(&global_runtime, memory_order_relaxed)->latency_debug & (flags)) -#define LOOP_TRACE(loop, flags, fmt, args...) do { if (config && (config->latency_debug & (flags))) log(L_TRACE "%s (%p): " fmt, LOOP_NAME(loop), (loop), ##args); } while (0) -#define THREAD_TRACE(flags, ...) do { if (config && (config->latency_debug & (flags))) log(L_TRACE "Thread: " __VA_ARGS__); } while (0) +#define LOOP_TRACE(loop, flags, fmt, args...) do { if (LATENCY_DEBUG(flags)) log(L_TRACE "%s (%p): " fmt, LOOP_NAME(loop), (loop), ##args); } while (0) +#define THREAD_TRACE(flags, ...) do { if (LATENCY_DEBUG(flags)) log(L_TRACE "Thread: " __VA_ARGS__); } while (0) #define LOOP_WARN(loop, fmt, args...) log(L_WARN "%s (%p): " fmt, LOOP_NAME(loop), (loop), ##args) @@ -1506,7 +1507,8 @@ birdloop_run(void *_loop) /* Now we can actually do some work */ u64 dif = account_to(&loop->working); - if (dif > this_thread->max_loop_time_ns + config->latency_limit TO_NS) + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (dif > this_thread->max_loop_time_ns + gr->latency_limit TO_NS) LOOP_WARN(loop, "locked %lu us after its scheduled end time", dif NS TO_US); uint repeat, loop_runs = 0; diff --git a/sysdep/unix/io.c b/sysdep/unix/io.c index ab2379aa..7e974ec1 100644 --- a/sysdep/unix/io.c +++ b/sysdep/unix/io.c @@ -2247,7 +2247,8 @@ io_update_time(void) { event_open->duration = last_io_time - event_open->timestamp; - if (event_open->duration > config->latency_limit) + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (event_open->duration > gr->latency_limit) log(L_WARN "Event 0x%p 0x%p took %u.%03u ms", event_open->hook, event_open->data, (uint) (event_open->duration TO_MS), (uint) (event_open->duration % 1000)); @@ -2267,7 +2268,8 @@ io_update_time(void) void io_log_event(void *hook, void *data, uint flag) { - if (config->latency_debug & flag) + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (gr->latency_debug & flag) io_update_time(); struct event_log_entry *en = event_log + event_log_pos; @@ -2281,7 +2283,7 @@ io_log_event(void *hook, void *data, uint flag) event_log_pos++; event_log_pos %= EVENT_LOG_LENGTH; - event_open = (config->latency_debug & flag) ? en : NULL; + event_open = (gr->latency_debug & flag) ? en : NULL; } static inline void @@ -2310,7 +2312,8 @@ void watchdog_sigalrm(int sig UNUSED) { /* Update last_io_time and duration, but skip latency check */ - config->latency_limit = 0xffffffff; + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + gr->latency_limit = 0xffffffff; io_update_time(); debug_safe("Watchdog timer timed out\n"); @@ -2335,9 +2338,10 @@ watchdog_start(void) loop_time = last_io_time; event_log_num = 0; - if (config->watchdog_timeout) + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (gr->watchdog_timeout) { - alarm(config->watchdog_timeout); + alarm(gr->watchdog_timeout); watchdog_active = 1; } } @@ -2354,7 +2358,8 @@ watchdog_stop(void) } btime duration = last_io_time - loop_time; - if (duration > config->watchdog_warning) + struct global_runtime *gr = atomic_load_explicit(&global_runtime, memory_order_relaxed); + if (duration > gr->watchdog_warning) log(L_WARN "I/O loop cycle took %u.%03u ms for %d events", (uint) (duration TO_MS), (uint) (duration % 1000), event_log_num); } diff --git a/sysdep/unix/log.c b/sysdep/unix/log.c index 6046364d..98642e9c 100644 --- a/sysdep/unix/log.c +++ b/sysdep/unix/log.c @@ -284,8 +284,10 @@ log_prepare(log_buffer *buf, int class) buf->pos[LBP_TIMESTAMP] = buf->buf.pos; if (BIT32_TEST(&lf, LBP_TIMESTAMP)) { - const char *fmt = config ? config->tf_log.fmt1 : "%F %T.%3f"; + rcu_read_lock(); + const char *fmt = atomic_load_explicit(&global_runtime, memory_order_acquire)->tf_log.fmt1; int t = tm_format_real_time(buf->buf.pos, buf->buf.end - buf->buf.pos, fmt, current_real_time()); + rcu_read_unlock(); if (t) buf->buf.pos += t; else @@ -307,8 +309,10 @@ log_prepare(log_buffer *buf, int class) else buffer_puts(&buf->buf, "