0
0
mirror of https://gitlab.nic.cz/labs/bird.git synced 2025-01-03 07:31:54 +00:00

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.
This commit is contained in:
Maria Matejka 2024-12-23 11:58:05 +01:00
parent 5fd0fd77e2
commit 0fa80d7c79
4 changed files with 48 additions and 32 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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,