From 0fa80d7c79428e5370740a2eba5605b65131ebd6 Mon Sep 17 00:00:00 2001 From: Maria Matejka Date: Mon, 23 Dec 2024 11:58:05 +0100 Subject: [PATCH] Kernel: feed only once during startup There was an inefficiency in the initial scan state machine, causing routes to be fed several times instead of just once. Now the export startup is postponed until first krt_scan() finishes and we actually can do the pruning with full information. --- nest/proto.c | 4 ++- nest/protocol.h | 2 ++ sysdep/unix/krt.c | 69 ++++++++++++++++++++++++++++------------------- sysdep/unix/krt.h | 5 ++-- 4 files changed, 48 insertions(+), 32 deletions(-) diff --git a/nest/proto.c b/nest/proto.c index 678697d6..6fa74e9f 100644 --- a/nest/proto.c +++ b/nest/proto.c @@ -676,9 +676,11 @@ void channel_notify_basic(void *); void channel_notify_accepted(void *); void channel_notify_merged(void *); -static void +void channel_start_export(struct channel *c) { + ASSERT_DIE(birdloop_inside(c->proto->loop)); + if (rt_export_get_state(&c->out_req) != TES_DOWN) bug("%s.%s: Attempted to start channel's already started export", c->proto->name, c->name); diff --git a/nest/protocol.h b/nest/protocol.h index cf7ecb89..2bfa1628 100644 --- a/nest/protocol.h +++ b/nest/protocol.h @@ -747,6 +747,8 @@ int proto_configure_channel(struct proto *p, struct channel **c, struct channel_ void channel_set_state(struct channel *c, uint state); +void channel_start_export(struct channel *c); + void channel_add_obstacle(struct channel *c); void channel_del_obstacle(struct channel *c); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index 34882b88..1658dd6f 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -342,6 +342,8 @@ krt_learn_async(struct krt_proto *p, rte *e, int new) /* Hook defined in nest/rt-table.c ... to be refactored away later */ rte *krt_export_net(struct channel *c, const net_addr *a, linpool *lp); +static void krt_rt_notify(struct proto *P, struct channel *ch, const net_addr *net, rte *new, const rte *old); + static int krt_same_dest(rte *k, rte *e) { @@ -361,6 +363,11 @@ krt_same_dest(rte *k, rte *e) void krt_got_route(struct krt_proto *p, rte *e, s8 src) { + /* If we happen to get an asynchronous route notification + * before initialization, we wait for the scan. */ + if (p->sync_state == KPS_INIT) + return; + rte *new = NULL; e->pflags = 0; @@ -391,10 +398,6 @@ krt_got_route(struct krt_proto *p, rte *e, s8 src) /* The rest is for KRT_SRC_BIRD (or KRT_SRC_UNKNOWN) */ - /* We wait for the initial feed to have correct installed state */ - if (!p->ready) - goto ignore; - /* Get the exported version */ new = krt_export_net(p->p.main_channel, e->net, krt_filter_lp); @@ -423,10 +426,6 @@ aseen: krt_trace_in(p, e, "already seen"); goto done; -ignore: - krt_trace_in(p, e, "ignored"); - goto done; - update: krt_trace_in(p, new, "updating"); krt_replace_rte(p, e->net, new, e); @@ -447,12 +446,21 @@ krt_init_scan(struct krt_proto *p) { switch (p->sync_state) { + case KPS_INIT: + /* Allow exports now */ + p->p.rt_notify = krt_rt_notify; + channel_start_export(p->p.main_channel); + rt_refresh_begin(&p->p.main_channel->in_req); + p->sync_state = KPS_FIRST_SCAN; + return 1; + case KPS_IDLE: rt_refresh_begin(&p->p.main_channel->in_req); bmap_reset(&p->seen_map, 1024); p->sync_state = KPS_SCANNING; return 1; + case KPS_FIRST_SCAN: case KPS_SCANNING: bug("Kernel scan double-init"); @@ -470,14 +478,17 @@ krt_prune(struct krt_proto *p) { switch (p->sync_state) { + case KPS_INIT: case KPS_IDLE: bug("Kernel scan prune without scan"); case KPS_SCANNING: + channel_request_full_refeed(p->p.main_channel); + /* fall through */ + case KPS_FIRST_SCAN: p->sync_state = KPS_PRUNING; KRT_TRACE(p, D_EVENTS, "Pruning table %s", p->p.main_channel->table->name); rt_refresh_end(&p->p.main_channel->in_req); - channel_request_full_refeed(p->p.main_channel); break; case KPS_PRUNING: @@ -549,7 +560,7 @@ krt_scan_all(timer *t UNUSED) krt_do_scan(NULL); WALK_LIST2(p, n, krt_proto_list, krt_node) - if (p->sync_state == KPS_SCANNING) + if ((p->sync_state == KPS_SCANNING) || (p->sync_state == KPS_FIRST_SCAN)) krt_prune(p); } @@ -644,6 +655,9 @@ krt_scan_timer_kick(struct krt_proto *p) static int krt_preexport(struct channel *C, rte *e) { + /* The export should not start before proper sync */ + ASSERT_DIE(SKIP_BACK(struct krt_proto, p, C->proto)->sync_state != KPS_INIT); + if (e->src->owner == &C->proto->sources) #ifdef CONFIG_SINGLE_ROUTE return 1; @@ -659,15 +673,6 @@ krt_preexport(struct channel *C, rte *e) return -1; } - /* Before first scan we don't touch the routes */ - if (!SKIP_BACK(struct krt_proto, p, C->proto)->ready) - { - if (C->debug & D_ROUTES) - log(L_TRACE "%s.%s not ready yet to accept route for %N", - C->proto->name, C->name, e->net); - return -1; - } - return 0; } @@ -685,18 +690,24 @@ krt_rt_notify(struct proto *P, struct channel *ch, const net_addr *net, switch (p->sync_state) { + case KPS_INIT: + bug("Routes in init state should have been rejected by preexport."); + case KPS_IDLE: case KPS_PRUNING: if (new && bmap_test(&p->seen_map, new->id)) + { if (ch->debug & D_ROUTES) { /* Already installed and seen in the kernel dump */ log(L_TRACE "%s.%s: %N already in kernel", P->name, ch->name, net); - return; } + return; + } /* fall through */ + case KPS_FIRST_SCAN: case KPS_SCANNING: /* Actually replace the route */ krt_replace_rte(p, net, new, old); @@ -732,7 +743,6 @@ krt_reload_routes(struct channel *C, struct rt_feeding_request *rfr) if (KRT_CF->learn) { - p->reload = 1; krt_scan_timer_kick(p); } @@ -749,15 +759,18 @@ krt_export_fed(struct channel *C) { struct krt_proto *p = (void *) C->proto; - p->ready = 1; - p->initialized = 1; - switch (p->sync_state) { + case KPS_INIT: + bug("KRT export started before scan"); + case KPS_IDLE: krt_scan_timer_kick(p); break; + case KPS_FIRST_SCAN: + bug("KRT export done before first scan"); + case KPS_SCANNING: break; @@ -831,7 +844,8 @@ krt_init(struct proto_config *CF) p->p.main_channel = proto_add_channel(&p->p, proto_cf_main_channel(CF)); p->p.preexport = krt_preexport; - p->p.rt_notify = krt_rt_notify; + /* Not setting rt_notify here to not start exports, must wait for the first scan + * and then we can start exports manually */ p->p.iface_sub.if_notify = krt_if_notify; p->p.reload_routes = krt_reload_routes; p->p.export_fed = krt_export_fed; @@ -887,7 +901,7 @@ krt_shutdown(struct proto *P) return PS_FLUSH; /* FIXME we should flush routes even when persist during reconfiguration */ - if (p->initialized && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN)) + if ((p->sync_state != KPS_INIT) && !KRT_CF->persist && (P->down_code != PDC_CMD_GR_DOWN)) { struct rt_export_feeder req = (struct rt_export_feeder) { @@ -922,8 +936,7 @@ krt_shutdown(struct proto *P) static void krt_cleanup(struct krt_proto *p) { - p->ready = 0; - p->initialized = 0; + p->sync_state = KPS_INIT; krt_sys_shutdown(p); rem_node(&p->krt_node); diff --git a/sysdep/unix/krt.h b/sysdep/unix/krt.h index 394e7401..14be715f 100644 --- a/sysdep/unix/krt.h +++ b/sysdep/unix/krt.h @@ -59,10 +59,9 @@ struct krt_proto { struct bmap seen_map; /* Routes seen during last periodic scan */ node krt_node; /* Node in krt_proto_list */ byte af; /* Kernel address family (AF_*) */ - byte ready; /* Initial feed has been finished */ - byte initialized; /* First scan has been finished */ - byte reload; /* Next scan is doing reload */ PACKED enum krt_prune_state { + KPS_INIT, + KPS_FIRST_SCAN, KPS_IDLE, KPS_SCANNING, KPS_PRUNING,