mirror of
https://gitlab.nic.cz/labs/bird.git
synced 2024-11-09 20:58:44 +00:00
Exporter routine refactoring to allow for nicer table locking
This commit is contained in:
parent
66ccdc2a0c
commit
636bc44e61
@ -702,7 +702,7 @@ r_args:
|
||||
}
|
||||
| r_args IMPORT TABLE channel_arg {
|
||||
if (!($4->in_keep & RIK_PREFILTER)) cf_error("No import table in channel %s.%s", $4->proto->name, $4->name);
|
||||
rt_show_add_exporter($$, &$4->table->exporter, "import")->prefilter = $4;
|
||||
rt_show_add_exporter($$, &$4->table->exporter.e, "import")->prefilter = $4;
|
||||
$$->tables_defined_by = RSD_TDB_DIRECT;
|
||||
}
|
||||
| r_args EXPORT TABLE channel_arg {
|
||||
|
12
nest/proto.c
12
nest/proto.c
@ -376,7 +376,7 @@ channel_dump_roa_req(struct rt_export_request *req)
|
||||
{
|
||||
struct roa_subscription *s = SKIP_BACK(struct roa_subscription, req, req);
|
||||
struct channel *c = s->c;
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, req->hook->table);
|
||||
rtable *tab = SKIP_BACK(rtable, exporter.e, req->hook->table);
|
||||
|
||||
debug(" Channel %s.%s ROA %s change notifier from table %s request %p\n",
|
||||
c->proto->name, c->name,
|
||||
@ -394,7 +394,7 @@ channel_roa_is_subscribed(struct channel *c, rtable *tab, int dir)
|
||||
node *n;
|
||||
|
||||
WALK_LIST2(s, n, c->roa_subscriptions, roa_node)
|
||||
if ((s->req.hook->table == &tab->exporter) && (s->t.hook == hook))
|
||||
if ((s->req.hook->table == &tab->exporter.e) && (s->t.hook == hook))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
@ -422,7 +422,7 @@ channel_roa_subscribe(struct channel *c, rtable *tab, int dir)
|
||||
};
|
||||
|
||||
add_tail(&c->roa_subscriptions, &s->roa_node);
|
||||
rt_request_export(&tab->exporter, &s->req);
|
||||
rt_request_export(tab, &s->req);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -575,7 +575,7 @@ channel_start_export(struct channel *c)
|
||||
}
|
||||
|
||||
DBG("%s.%s: Channel start export req=%p\n", c->proto->name, c->name, &c->out_req);
|
||||
rt_request_export(&c->table->exporter, &c->out_req);
|
||||
rt_request_export(c->table, &c->out_req);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -629,7 +629,7 @@ channel_export_stopped(struct rt_export_request *req)
|
||||
{
|
||||
c->refeeding = 1;
|
||||
c->refeed_pending = 0;
|
||||
rt_request_export(&c->table->exporter, req);
|
||||
rt_request_export(c->table, req);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -673,7 +673,7 @@ channel_schedule_reload(struct channel *c)
|
||||
{
|
||||
ASSERT(c->in_req.hook);
|
||||
|
||||
rt_request_export(&c->table->exporter, &c->reload_req);
|
||||
rt_request_export(c->table, &c->reload_req);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -301,7 +301,7 @@ rt_show_cont(struct rt_show_data *d)
|
||||
if (d->tables_defined_by & RSD_TDB_SET)
|
||||
rt_show_table(d);
|
||||
|
||||
rt_request_export(d->tab->table, &d->req);
|
||||
rt_request_export_other(d->tab->table, &d->req);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -356,7 +356,7 @@ rt_show_add_exporter(struct rt_show_data *d, struct rt_exporter *t, const char *
|
||||
struct rt_show_data_rtable *
|
||||
rt_show_add_table(struct rt_show_data *d, struct rtable *t)
|
||||
{
|
||||
struct rt_show_data_rtable *rsdr = rt_show_add_exporter(d, &t->exporter, t->name);
|
||||
struct rt_show_data_rtable *rsdr = rt_show_add_exporter(d, &t->exporter.e, t->name);
|
||||
|
||||
struct proto_config *krt = t->config->krt_attached;
|
||||
if (krt)
|
||||
@ -419,11 +419,11 @@ rt_show_prepare_tables(struct rt_show_data *d)
|
||||
if (d->export_mode)
|
||||
{
|
||||
if (!tab->export_channel && d->export_channel &&
|
||||
(tab->table == &d->export_channel->table->exporter))
|
||||
(tab->table == &d->export_channel->table->exporter.e))
|
||||
tab->export_channel = d->export_channel;
|
||||
|
||||
if (!tab->export_channel && d->export_protocol)
|
||||
tab->export_channel = proto_find_channel_by_table(d->export_protocol, SKIP_BACK(rtable, exporter, tab->table));
|
||||
tab->export_channel = proto_find_channel_by_table(d->export_protocol, SKIP_BACK(rtable, exporter.e, tab->table));
|
||||
|
||||
if (!tab->export_channel)
|
||||
{
|
||||
|
286
nest/rt-table.c
286
nest/rt-table.c
@ -138,13 +138,13 @@ static void rt_feed_by_fib(void *);
|
||||
static void rt_feed_by_trie(void *);
|
||||
static void rt_feed_equal(void *);
|
||||
static void rt_feed_for(void *);
|
||||
static uint rt_feed_net(struct rt_export_hook *c, net *n);
|
||||
static uint rt_feed_net(struct rt_table_export_hook *c, net *n);
|
||||
static void rt_check_cork_low(rtable *tab);
|
||||
static void rt_check_cork_high(rtable *tab);
|
||||
static void rt_cork_release_hook(void *);
|
||||
|
||||
static inline void rt_export_used(struct rt_exporter *);
|
||||
static void rt_export_cleanup(rtable *tab);
|
||||
static void rt_export_used(struct rt_table_exporter *);
|
||||
static void rt_export_cleanup(rtable *);
|
||||
|
||||
static int rte_same(rte *x, rte *y);
|
||||
|
||||
@ -1162,9 +1162,10 @@ rpe_next(struct rt_pending_export *rpe, struct rte_src *src)
|
||||
}
|
||||
|
||||
static struct rt_pending_export * rt_next_export_fast(struct rt_pending_export *last);
|
||||
static void
|
||||
rte_export(struct rt_export_hook *hook, struct rt_pending_export *rpe)
|
||||
static int
|
||||
rte_export(struct rt_table_export_hook *th, struct rt_pending_export *rpe)
|
||||
{
|
||||
struct rt_export_hook *hook = &th->h;
|
||||
if (bmap_test(&hook->seq_map, rpe->seq))
|
||||
goto ignore; /* Seen already */
|
||||
|
||||
@ -1216,15 +1217,16 @@ rte_export(struct rt_export_hook *hook, struct rt_pending_export *rpe)
|
||||
|
||||
ignore:
|
||||
/* Get the next export if exists */
|
||||
hook->rpe_next = rt_next_export_fast(rpe);
|
||||
th->rpe_next = rt_next_export_fast(rpe);
|
||||
|
||||
/* The last block may be available to free */
|
||||
if (PAGE_HEAD(hook->rpe_next) != PAGE_HEAD(rpe))
|
||||
CALL(hook->table->used, hook->table);
|
||||
int used = (PAGE_HEAD(th->rpe_next) != PAGE_HEAD(rpe));
|
||||
|
||||
/* Releasing this export for cleanup routine */
|
||||
DBG("store hook=%p last_export=%p seq=%lu\n", hook, rpe, rpe->seq);
|
||||
atomic_store_explicit(&hook->last_export, rpe, memory_order_release);
|
||||
atomic_store_explicit(&th->last_export, rpe, memory_order_release);
|
||||
|
||||
return used;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1273,7 +1275,7 @@ rte_announce(rtable *tab, net *net, struct rte_storage *new, struct rte_storage
|
||||
if (old_best_valid)
|
||||
old_best->rte.sender->stats.pref--;
|
||||
|
||||
if (EMPTY_LIST(tab->exporter.hooks) && EMPTY_LIST(tab->exporter.pending))
|
||||
if (EMPTY_LIST(tab->exporter.e.hooks) && EMPTY_LIST(tab->exporter.pending))
|
||||
{
|
||||
/* No export hook and no pending exports to cleanup. We may free the route immediately. */
|
||||
if (!old)
|
||||
@ -1382,7 +1384,7 @@ rt_next_export_fast(struct rt_pending_export *last)
|
||||
}
|
||||
|
||||
static struct rt_pending_export *
|
||||
rt_next_export(struct rt_export_hook *hook, struct rt_exporter *tab)
|
||||
rt_next_export(struct rt_table_export_hook *hook, struct rt_table_exporter *tab)
|
||||
{
|
||||
/* As the table is locked, it is safe to reload the last export pointer */
|
||||
struct rt_pending_export *last = atomic_load_explicit(&hook->last_export, memory_order_acquire);
|
||||
@ -1399,7 +1401,7 @@ rt_next_export(struct rt_export_hook *hook, struct rt_exporter *tab)
|
||||
static inline void
|
||||
rt_send_export_event(struct rt_export_hook *hook)
|
||||
{
|
||||
ev_send(hook->req->list, hook->event);
|
||||
ev_send(hook->req->list, &hook->event);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1411,7 +1413,7 @@ rt_announce_exports(timer *tm)
|
||||
return;
|
||||
|
||||
struct rt_export_hook *c; node *n;
|
||||
WALK_LIST2(c, n, tab->exporter.hooks, n)
|
||||
WALK_LIST2(c, n, tab->exporter.e.hooks, n)
|
||||
{
|
||||
if (atomic_load_explicit(&c->export_state, memory_order_acquire) != TES_READY)
|
||||
continue;
|
||||
@ -1443,7 +1445,7 @@ rt_import_announce_exports(void *_hook)
|
||||
}
|
||||
|
||||
static struct rt_pending_export *
|
||||
rt_last_export(struct rt_exporter *tab)
|
||||
rt_last_export(struct rt_table_exporter *tab)
|
||||
{
|
||||
struct rt_pending_export *rpe = NULL;
|
||||
|
||||
@ -1463,31 +1465,33 @@ rt_last_export(struct rt_exporter *tab)
|
||||
static void
|
||||
rt_export_hook(void *_data)
|
||||
{
|
||||
struct rt_export_hook *c = _data;
|
||||
struct rt_table_export_hook *c = _data;
|
||||
|
||||
ASSERT_DIE(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_READY);
|
||||
ASSERT_DIE(atomic_load_explicit(&c->h.export_state, memory_order_relaxed) == TES_READY);
|
||||
|
||||
if (!c->rpe_next)
|
||||
{
|
||||
c->rpe_next = rt_next_export(c, c->table);
|
||||
|
||||
if (!c->rpe_next)
|
||||
{
|
||||
CALL(c->table->used, c->table);
|
||||
return;
|
||||
}
|
||||
return rt_export_used(c->table);
|
||||
}
|
||||
|
||||
int used = 0;
|
||||
|
||||
/* Process the export */
|
||||
for (uint i=0; i<RT_EXPORT_BULK; i++)
|
||||
{
|
||||
rte_export(c, c->rpe_next);
|
||||
used += rte_export(c, c->rpe_next);
|
||||
|
||||
if (!c->rpe_next)
|
||||
break;
|
||||
}
|
||||
|
||||
rt_send_export_event(c);
|
||||
if (used)
|
||||
rt_export_used(c->table);
|
||||
|
||||
rt_send_export_event(&c->h);
|
||||
}
|
||||
|
||||
|
||||
@ -1909,28 +1913,27 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte
|
||||
}
|
||||
|
||||
static void
|
||||
rt_table_export_done(struct rt_export_hook *hook)
|
||||
rt_table_export_done(void *hh)
|
||||
{
|
||||
struct rt_exporter *re = hook->table;
|
||||
struct rtable *tab = SKIP_BACK(struct rtable, exporter, re);
|
||||
struct rt_table_export_hook *hook = hh;
|
||||
struct rtable *tab = SKIP_BACK(struct rtable, exporter, hook->table);
|
||||
|
||||
DBG("Export hook %p in table %s finished uc=%u\n", hook, tab->name, tab->use_count);
|
||||
|
||||
/* Free the hook before unlocking the table */
|
||||
rfree(hook->pool);
|
||||
/* Drop pending exports */
|
||||
rt_export_used(&tab->exporter);
|
||||
|
||||
/* Do the common code */
|
||||
rt_export_stopped(&hook->h);
|
||||
|
||||
/* Unlock the table; this may free it */
|
||||
rt_unlock_table(tab);
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
rt_export_stopped(void *data)
|
||||
{
|
||||
struct rt_export_hook *hook = data;
|
||||
struct rt_exporter *tab = hook->table;
|
||||
|
||||
/* Drop pending exports */
|
||||
CALL(tab->used, tab);
|
||||
|
||||
/* Unlist */
|
||||
rem_node(&hook->n);
|
||||
@ -1938,8 +1941,8 @@ rt_export_stopped(void *data)
|
||||
/* Report the channel as stopped. */
|
||||
CALL(hook->stopped, hook->req);
|
||||
|
||||
/* Reporting the hook as finished. */
|
||||
CALL(tab->done, hook);
|
||||
/* Free the hook itself together with its pool */
|
||||
rfree(hook->pool);
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -1991,15 +1994,16 @@ rt_stop_import(struct rt_import_request *req, void (*stopped)(struct rt_import_r
|
||||
hook->stopped = stopped;
|
||||
}
|
||||
|
||||
static struct rt_export_hook *
|
||||
static void
|
||||
rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
||||
{
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, re);
|
||||
rtable *tab = SKIP_BACK(rtable, exporter.e, re);
|
||||
rt_lock_table(tab);
|
||||
|
||||
pool *p = rp_new(tab->rp, "Export hook");
|
||||
struct rt_export_hook *hook = mb_allocz(p, sizeof(struct rt_export_hook));
|
||||
hook->pool = p;
|
||||
req->hook = rt_alloc_export(re, sizeof(struct rt_table_export_hook));
|
||||
req->hook->req = req;
|
||||
|
||||
struct rt_table_export_hook *hook = SKIP_BACK(struct rt_table_export_hook, h, req->hook);
|
||||
|
||||
/* stats zeroed by mb_allocz */
|
||||
switch (req->addr_mode)
|
||||
@ -2007,24 +2011,24 @@ rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
||||
case TE_ADDR_IN:
|
||||
if (tab->trie && net_val_match(tab->addr_type, NB_IP))
|
||||
{
|
||||
hook->walk_state = mb_allocz(p, sizeof (struct f_trie_walk_state));
|
||||
hook->walk_state = mb_allocz(hook->h.pool, sizeof (struct f_trie_walk_state));
|
||||
hook->walk_lock = rt_lock_trie(tab);
|
||||
trie_walk_init(hook->walk_state, tab->trie, req->addr);
|
||||
hook->event = ev_new_init(p, rt_feed_by_trie, hook);
|
||||
hook->h.event.hook = rt_feed_by_trie;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
case TE_ADDR_NONE:
|
||||
FIB_ITERATE_INIT(&hook->feed_fit, &tab->fib);
|
||||
hook->event = ev_new_init(p, rt_feed_by_fib, hook);
|
||||
hook->h.event.hook = rt_feed_by_fib;
|
||||
break;
|
||||
|
||||
case TE_ADDR_EQUAL:
|
||||
hook->event = ev_new_init(p, rt_feed_equal, hook);
|
||||
hook->h.event.hook = rt_feed_equal;
|
||||
break;
|
||||
|
||||
case TE_ADDR_FOR:
|
||||
hook->event = ev_new_init(p, rt_feed_for, hook);
|
||||
hook->h.event.hook = rt_feed_for;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -2033,23 +2037,43 @@ rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
||||
|
||||
DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count);
|
||||
|
||||
struct rt_pending_export *rpe = rt_last_export(hook->table);
|
||||
DBG("store hook=%p last_export=%p seq=%lu\n", hook, rpe, rpe ? rpe->seq : 0);
|
||||
atomic_store_explicit(&hook->last_export, rpe, memory_order_relaxed);
|
||||
|
||||
rt_init_export(re, req->hook);
|
||||
}
|
||||
|
||||
void rt_request_export(rtable *tab, struct rt_export_request *req)
|
||||
{
|
||||
rt_table_export_start(&tab->exporter.e, req);
|
||||
}
|
||||
|
||||
void
|
||||
rt_request_export_other(struct rt_exporter *re, struct rt_export_request *req)
|
||||
{
|
||||
return re->class->start(re, req);
|
||||
}
|
||||
|
||||
struct rt_export_hook *
|
||||
rt_alloc_export(struct rt_exporter *re, uint size)
|
||||
{
|
||||
pool *p = rp_new(re->rp, "Export hook");
|
||||
struct rt_export_hook *hook = mb_allocz(p, size);
|
||||
|
||||
hook->pool = p;
|
||||
hook->table = re;
|
||||
|
||||
return hook;
|
||||
}
|
||||
|
||||
void
|
||||
rt_request_export(struct rt_exporter *re, struct rt_export_request *req)
|
||||
rt_init_export(struct rt_exporter *re, struct rt_export_hook *hook)
|
||||
{
|
||||
struct rt_export_hook *hook = req->hook = re->start(re, req);
|
||||
|
||||
hook->req = req;
|
||||
hook->table = re;
|
||||
hook->event.data = hook;
|
||||
|
||||
bmap_init(&hook->seq_map, hook->pool, 1024);
|
||||
|
||||
struct rt_pending_export *rpe = rt_last_export(hook->table);
|
||||
DBG("store hook=%p last_export=%p seq=%lu\n", hook, rpe, rpe ? rpe->seq : 0);
|
||||
atomic_store_explicit(&hook->last_export, rpe, memory_order_relaxed);
|
||||
|
||||
hook->n = (node) {};
|
||||
add_tail(&re->hooks, &hook->n);
|
||||
|
||||
@ -2059,14 +2083,15 @@ rt_request_export(struct rt_exporter *re, struct rt_export_request *req)
|
||||
}
|
||||
|
||||
static void
|
||||
rt_table_export_stop(struct rt_export_hook *hook)
|
||||
rt_table_export_stop(struct rt_export_hook *hh)
|
||||
{
|
||||
struct rt_table_export_hook *hook = SKIP_BACK(struct rt_table_export_hook, h, hh);
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, hook->table);
|
||||
|
||||
if (atomic_load_explicit(&hook->export_state, memory_order_relaxed) != TES_FEEDING)
|
||||
if (atomic_load_explicit(&hh->export_state, memory_order_relaxed) != TES_FEEDING)
|
||||
return;
|
||||
|
||||
switch (hook->req->addr_mode)
|
||||
switch (hh->req->addr_mode)
|
||||
{
|
||||
case TE_ADDR_IN:
|
||||
if (hook->walk_lock)
|
||||
@ -2087,17 +2112,18 @@ rt_table_export_stop(struct rt_export_hook *hook)
|
||||
void
|
||||
rt_stop_export(struct rt_export_request *req, void (*stopped)(struct rt_export_request *))
|
||||
{
|
||||
ASSERT_DIE(birdloop_inside(req->list->loop));
|
||||
ASSERT_DIE(req->hook);
|
||||
struct rt_export_hook *hook = req->hook;
|
||||
|
||||
/* Cancel the feeder event */
|
||||
ev_postpone(hook->event);
|
||||
ev_postpone(&hook->event);
|
||||
|
||||
/* Stop feeding from the exporter */
|
||||
CALL(hook->table->stop, hook);
|
||||
CALL(hook->table->class->stop, hook);
|
||||
|
||||
/* Reset the event as the stopped event */
|
||||
hook->event->hook = rt_export_stopped;
|
||||
hook->event.hook = hook->table->class->done;
|
||||
hook->stopped = stopped;
|
||||
|
||||
/* Update export state */
|
||||
@ -2253,13 +2279,14 @@ rt_dump_hooks(rtable *tab)
|
||||
ih->last_state_change, ih->import_state, ih->stopped);
|
||||
}
|
||||
|
||||
struct rt_export_hook *eh;
|
||||
WALK_LIST(eh, tab->exporter.hooks)
|
||||
struct rt_table_export_hook *eh;
|
||||
WALK_LIST(eh, tab->exporter.e.hooks)
|
||||
{
|
||||
eh->req->dump_req(eh->req);
|
||||
eh->h.req->dump_req(eh->h.req);
|
||||
debug(" Export hook %p requested by %p:"
|
||||
" refeed_pending=%u last_state_change=%t export_state=%u\n",
|
||||
eh, eh->req, eh->refeed_pending, eh->last_state_change, atomic_load_explicit(&eh->export_state, memory_order_relaxed));
|
||||
eh, eh->h.req, eh->refeed_pending, eh->h.last_state_change,
|
||||
atomic_load_explicit(&eh->h.export_state, memory_order_relaxed));
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
@ -2303,7 +2330,7 @@ rt_schedule_prune(rtable *tab)
|
||||
}
|
||||
|
||||
static void
|
||||
rt_export_used(struct rt_exporter *e)
|
||||
rt_export_used(struct rt_table_exporter *e)
|
||||
{
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, e);
|
||||
|
||||
@ -2426,15 +2453,15 @@ rt_flowspec_dump_req(struct rt_export_request *req)
|
||||
static struct rt_flowspec_link *
|
||||
rt_flowspec_find_link(rtable *src, rtable *dst)
|
||||
{
|
||||
struct rt_export_hook *hook; node *n;
|
||||
WALK_LIST2(hook, n, src->exporter.hooks, n)
|
||||
switch (atomic_load_explicit(&hook->export_state, memory_order_acquire))
|
||||
struct rt_table_export_hook *hook; node *n;
|
||||
WALK_LIST2(hook, n, src->exporter.e.hooks, h.n)
|
||||
switch (atomic_load_explicit(&hook->h.export_state, memory_order_acquire))
|
||||
{
|
||||
case TES_FEEDING:
|
||||
case TES_READY:
|
||||
if (hook->req->export_one == rt_flowspec_export_one)
|
||||
if (hook->h.req->export_one == rt_flowspec_export_one)
|
||||
{
|
||||
struct rt_flowspec_link *ln = SKIP_BACK(struct rt_flowspec_link, req, hook->req);
|
||||
struct rt_flowspec_link *ln = SKIP_BACK(struct rt_flowspec_link, req, hook->h.req);
|
||||
if (ln->dst == dst)
|
||||
return ln;
|
||||
}
|
||||
@ -2467,7 +2494,7 @@ rt_flowspec_link(rtable *src, rtable *dst)
|
||||
.export_one = rt_flowspec_export_one,
|
||||
};
|
||||
|
||||
rt_request_export(&src->exporter, &ln->req);
|
||||
rt_request_export(src, &ln->req);
|
||||
}
|
||||
|
||||
ln->uc++;
|
||||
@ -2544,6 +2571,18 @@ static struct resclass rt_class = {
|
||||
.memsize = NULL,
|
||||
};
|
||||
|
||||
static const struct rt_exporter_class rt_table_exporter_class = {
|
||||
.start = rt_table_export_start,
|
||||
.stop = rt_table_export_stop,
|
||||
.done = rt_table_export_done,
|
||||
};
|
||||
|
||||
void
|
||||
rt_exporter_init(struct rt_exporter *e)
|
||||
{
|
||||
init_list(&e->hooks);
|
||||
}
|
||||
|
||||
rtable *
|
||||
rt_setup(pool *pp, struct rtable_config *cf)
|
||||
{
|
||||
@ -2568,17 +2607,6 @@ rt_setup(pool *pp, struct rtable_config *cf)
|
||||
t->fib.init = net_init_with_trie;
|
||||
}
|
||||
|
||||
t->exporter = (struct rt_exporter) {
|
||||
.addr_type = t->addr_type,
|
||||
.start = rt_table_export_start,
|
||||
.stop = rt_table_export_stop,
|
||||
.done = rt_table_export_done,
|
||||
.used = rt_export_used,
|
||||
};
|
||||
|
||||
init_list(&t->exporter.hooks);
|
||||
init_list(&t->exporter.pending);
|
||||
|
||||
init_list(&t->imports);
|
||||
|
||||
hmap_init(&t->id_map, p, 1024);
|
||||
@ -2587,9 +2615,21 @@ rt_setup(pool *pp, struct rtable_config *cf)
|
||||
t->rt_event = ev_new_init(p, rt_event, t);
|
||||
t->uncork_event = ev_new_init(p, rt_uncork_event, t);
|
||||
t->prune_timer = tm_new_init(p, rt_prune_timer, t, 0, 0);
|
||||
t->exporter.export_timer = tm_new_init(p, rt_announce_exports, t, 0, 0);
|
||||
t->last_rt_change = t->gc_time = current_time();
|
||||
t->exporter.next_seq = 1;
|
||||
|
||||
t->exporter = (struct rt_table_exporter) {
|
||||
.e = {
|
||||
.class = &rt_table_exporter_class,
|
||||
.addr_type = t->addr_type,
|
||||
.rp = t->rp,
|
||||
},
|
||||
.export_timer = tm_new_init(p, rt_announce_exports, t, 0, 0),
|
||||
.next_seq = 1,
|
||||
};
|
||||
|
||||
rt_exporter_init(&t->exporter.e);
|
||||
|
||||
init_list(&t->exporter.pending);
|
||||
|
||||
t->cork_threshold = cf->cork_threshold;
|
||||
|
||||
@ -2781,7 +2821,7 @@ again:
|
||||
}
|
||||
|
||||
/* In some cases, we may want to directly proceed to export cleanup */
|
||||
if (EMPTY_LIST(tab->exporter.hooks) && flushed_channels)
|
||||
if (EMPTY_LIST(tab->exporter.e.hooks) && flushed_channels)
|
||||
rt_export_cleanup(tab);
|
||||
}
|
||||
|
||||
@ -2795,11 +2835,11 @@ rt_export_cleanup(rtable *tab)
|
||||
struct rt_pending_export *first = tab->exporter.first;
|
||||
int want_prune = 0;
|
||||
|
||||
struct rt_export_hook *eh;
|
||||
struct rt_table_export_hook *eh;
|
||||
node *n;
|
||||
WALK_LIST2(eh, n, tab->exporter.hooks, n)
|
||||
WALK_LIST2(eh, n, tab->exporter.e.hooks, h.n)
|
||||
{
|
||||
switch (atomic_load_explicit(&eh->export_state, memory_order_acquire))
|
||||
switch (atomic_load_explicit(&eh->h.export_state, memory_order_acquire))
|
||||
{
|
||||
case TES_DOWN:
|
||||
continue;
|
||||
@ -2832,9 +2872,9 @@ rt_export_cleanup(rtable *tab)
|
||||
tab->exporter.first ? tab->exporter.first->seq : 0,
|
||||
min_seq);
|
||||
|
||||
WALK_LIST2(eh, n, tab->exporter.hooks, n)
|
||||
WALK_LIST2(eh, n, tab->exporter.e.hooks, h.n)
|
||||
{
|
||||
if (atomic_load_explicit(&eh->export_state, memory_order_acquire) != TES_READY)
|
||||
if (atomic_load_explicit(&eh->h.export_state, memory_order_acquire) != TES_READY)
|
||||
continue;
|
||||
|
||||
struct rt_pending_export *last = atomic_load_explicit(&eh->last_export, memory_order_acquire);
|
||||
@ -2908,13 +2948,13 @@ rt_export_cleanup(rtable *tab)
|
||||
rt_trace(tab, D_EVENTS, "Resetting export seq");
|
||||
|
||||
node *n;
|
||||
WALK_LIST2(eh, n, tab->exporter.hooks, n)
|
||||
WALK_LIST2(eh, n, tab->exporter.e.hooks, h.n)
|
||||
{
|
||||
if (atomic_load_explicit(&eh->export_state, memory_order_acquire) != TES_READY)
|
||||
if (atomic_load_explicit(&eh->h.export_state, memory_order_acquire) != TES_READY)
|
||||
continue;
|
||||
|
||||
ASSERT_DIE(atomic_load_explicit(&eh->last_export, memory_order_acquire) == NULL);
|
||||
bmap_reset(&eh->seq_map, 1024);
|
||||
bmap_reset(&eh->h.seq_map, 1024);
|
||||
}
|
||||
|
||||
tab->exporter.next_seq = 1;
|
||||
@ -3667,10 +3707,10 @@ rt_reconfigure(rtable *tab, struct rtable_config *new, struct rtable_config *old
|
||||
if (tab->hostcache)
|
||||
tab->hostcache->req.trace_routes = new->debug;
|
||||
|
||||
struct rt_export_hook *hook; node *n;
|
||||
WALK_LIST2(hook, n, tab->exporter.hooks, n)
|
||||
if (hook->req->export_one == rt_flowspec_export_one)
|
||||
hook->req->trace_routes = new->debug;
|
||||
struct rt_table_export_hook *hook; node *n;
|
||||
WALK_LIST2(hook, n, tab->exporter.e.hooks, h.n)
|
||||
if (hook->h.req->export_one == rt_flowspec_export_one)
|
||||
hook->h.req->trace_routes = new->debug;
|
||||
|
||||
tab->cork_threshold = new->cork_threshold;
|
||||
|
||||
@ -3745,7 +3785,7 @@ rt_commit(struct config *new, struct config *old)
|
||||
static void
|
||||
rt_feed_done(struct rt_export_hook *c)
|
||||
{
|
||||
c->event->hook = rt_export_hook;
|
||||
c->event.hook = rt_export_hook;
|
||||
|
||||
rt_set_export_state(c, TES_READY);
|
||||
|
||||
@ -3764,12 +3804,12 @@ rt_feed_done(struct rt_export_hook *c)
|
||||
static void
|
||||
rt_feed_by_fib(void *data)
|
||||
{
|
||||
struct rt_export_hook *c = data;
|
||||
struct rt_table_export_hook *c = data;
|
||||
|
||||
struct fib_iterator *fit = &c->feed_fit;
|
||||
int max_feed = 256;
|
||||
|
||||
ASSERT(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT(atomic_load_explicit(&c->h.export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, c->table);
|
||||
|
||||
@ -3778,25 +3818,25 @@ rt_feed_by_fib(void *data)
|
||||
if (max_feed <= 0)
|
||||
{
|
||||
FIB_ITERATE_PUT(fit);
|
||||
rt_send_export_event(c);
|
||||
rt_send_export_event(&c->h);
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_load_explicit(&c->export_state, memory_order_acquire) != TES_FEEDING)
|
||||
if (atomic_load_explicit(&c->h.export_state, memory_order_acquire) != TES_FEEDING)
|
||||
return;
|
||||
|
||||
if ((c->req->addr_mode == TE_ADDR_NONE) || net_in_netX(n->n.addr, c->req->addr))
|
||||
if ((c->h.req->addr_mode == TE_ADDR_NONE) || net_in_netX(n->n.addr, c->h.req->addr))
|
||||
max_feed -= rt_feed_net(c, n);
|
||||
}
|
||||
FIB_ITERATE_END;
|
||||
|
||||
rt_feed_done(c);
|
||||
rt_feed_done(&c->h);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_feed_by_trie(void *data)
|
||||
{
|
||||
struct rt_export_hook *c = data;
|
||||
struct rt_table_export_hook *c = data;
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, c->table);
|
||||
|
||||
ASSERT_DIE(c->walk_state);
|
||||
@ -3804,7 +3844,7 @@ rt_feed_by_trie(void *data)
|
||||
|
||||
int max_feed = 256;
|
||||
|
||||
ASSERT(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT(atomic_load_explicit(&c->h.export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
|
||||
net_addr addr;
|
||||
while (trie_walk_next(ws, &addr))
|
||||
@ -3816,7 +3856,7 @@ rt_feed_by_trie(void *data)
|
||||
if ((max_feed -= rt_feed_net(c, n)) <= 0)
|
||||
return;
|
||||
|
||||
if (atomic_load_explicit(&c->export_state, memory_order_acquire) != TES_FEEDING)
|
||||
if (atomic_load_explicit(&c->h.export_state, memory_order_acquire) != TES_FEEDING)
|
||||
return;
|
||||
}
|
||||
|
||||
@ -3826,65 +3866,65 @@ rt_feed_by_trie(void *data)
|
||||
mb_free(c->walk_state);
|
||||
c->walk_state = NULL;
|
||||
|
||||
rt_feed_done(c);
|
||||
rt_feed_done(&c->h);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_feed_equal(void *data)
|
||||
{
|
||||
struct rt_export_hook *c = data;
|
||||
struct rt_table_export_hook *c = data;
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, c->table);
|
||||
|
||||
ASSERT_DIE(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT_DIE(c->req->addr_mode == TE_ADDR_EQUAL);
|
||||
ASSERT_DIE(atomic_load_explicit(&c->h.export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT_DIE(c->h.req->addr_mode == TE_ADDR_EQUAL);
|
||||
|
||||
net *n = net_find(tab, c->req->addr);
|
||||
net *n = net_find(tab, c->h.req->addr);
|
||||
if (n)
|
||||
rt_feed_net(c, n);
|
||||
|
||||
rt_feed_done(c);
|
||||
rt_feed_done(&c->h);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_feed_for(void *data)
|
||||
{
|
||||
struct rt_export_hook *c = data;
|
||||
struct rt_table_export_hook *c = data;
|
||||
rtable *tab = SKIP_BACK(rtable, exporter, c->table);
|
||||
|
||||
ASSERT_DIE(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT_DIE(c->req->addr_mode == TE_ADDR_FOR);
|
||||
ASSERT_DIE(atomic_load_explicit(&c->h.export_state, memory_order_relaxed) == TES_FEEDING);
|
||||
ASSERT_DIE(c->h.req->addr_mode == TE_ADDR_FOR);
|
||||
|
||||
net *n = net_route(tab, c->req->addr);
|
||||
net *n = net_route(tab, c->h.req->addr);
|
||||
if (n)
|
||||
rt_feed_net(c, n);
|
||||
|
||||
rt_feed_done(c);
|
||||
rt_feed_done(&c->h);
|
||||
}
|
||||
|
||||
static uint
|
||||
rt_feed_net(struct rt_export_hook *c, net *n)
|
||||
rt_feed_net(struct rt_table_export_hook *c, net *n)
|
||||
{
|
||||
uint count = 0;
|
||||
|
||||
if (c->req->export_bulk)
|
||||
if (c->h.req->export_bulk)
|
||||
{
|
||||
count = rte_feed_count(n);
|
||||
if (count)
|
||||
{
|
||||
rte **feed = alloca(count * sizeof(rte *));
|
||||
rte_feed_obtain(n, feed, count);
|
||||
c->req->export_bulk(c->req, n->n.addr, NULL, feed, count);
|
||||
c->h.req->export_bulk(c->h.req, n->n.addr, NULL, feed, count);
|
||||
}
|
||||
}
|
||||
|
||||
else if (n->routes)
|
||||
{
|
||||
struct rt_pending_export rpe = { .new = n->routes, .new_best = n->routes };
|
||||
c->req->export_one(c->req, n->n.addr, &rpe);
|
||||
c->h.req->export_one(c->h.req, n->n.addr, &rpe);
|
||||
count = 1;
|
||||
}
|
||||
|
||||
rpe_mark_seen_all(c, n->first, NULL);
|
||||
rpe_mark_seen_all(&c->h, n->first, NULL);
|
||||
return count;
|
||||
}
|
||||
|
||||
@ -4072,7 +4112,7 @@ rt_init_hostcache(rtable *tab)
|
||||
.export_one = hc_notify_export_one,
|
||||
};
|
||||
|
||||
rt_request_export(&tab->exporter, &hc->req);
|
||||
rt_request_export(tab, &hc->req);
|
||||
|
||||
tab->hostcache = hc;
|
||||
}
|
||||
|
65
nest/rt.h
65
nest/rt.h
@ -67,16 +67,23 @@ struct rtable_config {
|
||||
|
||||
struct rt_export_hook;
|
||||
struct rt_export_request;
|
||||
struct rt_exporter;
|
||||
|
||||
struct rt_exporter_class {
|
||||
void (*start)(struct rt_exporter *, struct rt_export_request *);
|
||||
void (*stop)(struct rt_export_hook *);
|
||||
void (*done)(void *_rt_export_hook);
|
||||
};
|
||||
|
||||
struct rt_exporter {
|
||||
const struct rt_exporter_class *class;
|
||||
pool *rp;
|
||||
list hooks; /* Registered route export hooks */
|
||||
uint addr_type; /* Type of address data exported (NET_*) */
|
||||
};
|
||||
|
||||
struct rt_export_hook *(*start)(struct rt_exporter *, struct rt_export_request *);
|
||||
void (*stop)(struct rt_export_hook *);
|
||||
void (*done)(struct rt_export_hook *);
|
||||
void (*used)(struct rt_exporter *);
|
||||
|
||||
struct rt_table_exporter {
|
||||
struct rt_exporter e;
|
||||
list pending; /* List of packed struct rt_pending_export */
|
||||
struct timer *export_timer;
|
||||
|
||||
@ -97,7 +104,7 @@ typedef struct rtable {
|
||||
u32 rt_count; /* Number of routes in the table */
|
||||
|
||||
list imports; /* Registered route importers */
|
||||
struct rt_exporter exporter; /* Exporter API structure */
|
||||
struct rt_table_exporter exporter; /* Exporter API structure */
|
||||
|
||||
struct hmap id_map;
|
||||
struct hostcache *hostcache;
|
||||
@ -266,29 +273,39 @@ struct rt_export_hook {
|
||||
u32 withdraws_received; /* Number of route withdraws received */
|
||||
} stats;
|
||||
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
|
||||
_Atomic u8 export_state; /* Route export state (TES_*, see below) */
|
||||
struct event event; /* Event running all the export operations */
|
||||
|
||||
struct bmap seq_map; /* Keep track which exports were already procesed */
|
||||
|
||||
void (*stopped)(struct rt_export_request *); /* Stored callback when export is stopped */
|
||||
};
|
||||
|
||||
struct rt_table_export_hook {
|
||||
union {
|
||||
struct rt_export_hook h;
|
||||
struct { /* Overriding the parent structure beginning */
|
||||
node _n;
|
||||
struct rt_table_exporter *table;
|
||||
};
|
||||
};
|
||||
|
||||
union {
|
||||
struct fib_iterator feed_fit; /* Routing table iterator used during feeding */
|
||||
struct {
|
||||
struct f_trie_walk_state *walk_state; /* Iterator over networks in trie */
|
||||
struct f_trie *walk_lock; /* Locked trie for walking */
|
||||
};
|
||||
u32 hash_iter; /* Iterator over hash */
|
||||
};
|
||||
|
||||
struct bmap seq_map; /* Keep track which exports were already procesed */
|
||||
|
||||
struct rt_pending_export * _Atomic last_export;/* Last export processed */
|
||||
struct rt_pending_export *_Atomic last_export;/* Last export processed */
|
||||
struct rt_pending_export *rpe_next; /* Next pending export to process */
|
||||
|
||||
btime last_state_change; /* Time of last state transition */
|
||||
|
||||
u8 refeed_pending; /* Refeeding and another refeed is scheduled */
|
||||
_Atomic u8 export_state; /* Route export state (TES_*, see below) */
|
||||
u8 feed_type; /* Which feeding method is used (TFT_*, see below) */
|
||||
|
||||
struct event *event; /* Event running all the export operations */
|
||||
|
||||
void (*stopped)(struct rt_export_request *); /* Stored callback when export is stopped */
|
||||
};
|
||||
|
||||
#define TIS_DOWN 0
|
||||
@ -317,7 +334,8 @@ struct rt_export_hook {
|
||||
#define TFT_HASH 3
|
||||
|
||||
void rt_request_import(rtable *tab, struct rt_import_request *req);
|
||||
void rt_request_export(struct rt_exporter *tab, struct rt_export_request *req);
|
||||
void rt_request_export(rtable *tab, struct rt_export_request *req);
|
||||
void rt_request_export_other(struct rt_exporter *tab, struct rt_export_request *req);
|
||||
|
||||
void rt_export_once(struct rt_exporter *tab, struct rt_export_request *req);
|
||||
|
||||
@ -334,6 +352,10 @@ void rt_set_export_state(struct rt_export_hook *hook, u8 state);
|
||||
|
||||
void rte_import(struct rt_import_request *req, const net_addr *net, rte *new, struct rte_src *src);
|
||||
|
||||
/*
|
||||
* For table export processing
|
||||
*/
|
||||
|
||||
/* Get next rpe. If src is given, it must match. */
|
||||
struct rt_pending_export *rpe_next(struct rt_pending_export *rpe, struct rte_src *src);
|
||||
|
||||
@ -350,6 +372,15 @@ void rpe_mark_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
|
||||
/* Get pending export seen status */
|
||||
int rpe_get_seen(struct rt_export_hook *hook, struct rt_pending_export *rpe);
|
||||
|
||||
/*
|
||||
* For rt_export_hook and rt_exporter inheritance
|
||||
*/
|
||||
|
||||
void rt_init_export(struct rt_exporter *re, struct rt_export_hook *hook);
|
||||
struct rt_export_hook *rt_alloc_export(struct rt_exporter *re, uint size);
|
||||
void rt_export_stopped(void *data);
|
||||
void rt_exporter_init(struct rt_exporter *re);
|
||||
|
||||
/* Types of route announcement, also used as flags */
|
||||
#define RA_UNDEF 0 /* Undefined RA type */
|
||||
#define RA_OPTIMAL 1 /* Announcement of optimal route change */
|
||||
|
@ -1867,25 +1867,30 @@ bgp_free_pending_tx(struct bgp_channel *c)
|
||||
* Prefix hash table exporter
|
||||
*/
|
||||
|
||||
struct bgp_out_export_hook {
|
||||
struct rt_export_hook h;
|
||||
u32 hash_iter; /* Iterator over hash */
|
||||
};
|
||||
|
||||
static void
|
||||
bgp_out_table_feed(void *data)
|
||||
{
|
||||
struct rt_export_hook *hook = data;
|
||||
struct bgp_channel *bc = SKIP_BACK(struct bgp_channel, prefix_exporter, hook->table);
|
||||
struct bgp_out_export_hook *hook = data;
|
||||
struct bgp_channel *bc = SKIP_BACK(struct bgp_channel, prefix_exporter, hook->h.table);
|
||||
struct bgp_pending_tx *c = bc->ptx;
|
||||
|
||||
int max = 512;
|
||||
|
||||
const net_addr *neq = (hook->req->addr_mode == TE_ADDR_EQUAL) ? hook->req->addr : NULL;
|
||||
const net_addr *neq = (hook->h.req->addr_mode == TE_ADDR_EQUAL) ? hook->h.req->addr : NULL;
|
||||
const net_addr *cand = NULL;
|
||||
|
||||
do {
|
||||
HASH_WALK_ITER(c->prefix_hash, PXH, n, hook->hash_iter)
|
||||
{
|
||||
switch (hook->req->addr_mode)
|
||||
switch (hook->h.req->addr_mode)
|
||||
{
|
||||
case TE_ADDR_IN:
|
||||
if (!net_in_netX(n->net, hook->req->addr))
|
||||
if (!net_in_netX(n->net, hook->h.req->addr))
|
||||
continue;
|
||||
/* fall through */
|
||||
case TE_ADDR_NONE:
|
||||
@ -1897,7 +1902,7 @@ bgp_out_table_feed(void *data)
|
||||
case TE_ADDR_FOR:
|
||||
if (!neq)
|
||||
{
|
||||
if (net_in_netX(hook->req->addr, n->net) && (!cand || (n->net->length > cand->length)))
|
||||
if (net_in_netX(hook->h.req->addr, n->net) && (!cand || (n->net->length > cand->length)))
|
||||
cand = n->net;
|
||||
continue;
|
||||
}
|
||||
@ -1942,13 +1947,13 @@ bgp_out_table_feed(void *data)
|
||||
.new = &es, .new_best = &es,
|
||||
};
|
||||
|
||||
if (hook->req->export_bulk)
|
||||
if (hook->h.req->export_bulk)
|
||||
{
|
||||
rte *feed = &es.rte;
|
||||
hook->req->export_bulk(hook->req, n->net, &rpe, &feed, 1);
|
||||
hook->h.req->export_bulk(hook->h.req, n->net, &rpe, &feed, 1);
|
||||
}
|
||||
else if (hook->req->export_one)
|
||||
hook->req->export_one(hook->req, n->net, &rpe);
|
||||
else if (hook->h.req->export_one)
|
||||
hook->h.req->export_one(hook->h.req, n->net, &rpe);
|
||||
else
|
||||
bug("No export method in export request");
|
||||
}
|
||||
@ -1959,43 +1964,40 @@ bgp_out_table_feed(void *data)
|
||||
} while (neq);
|
||||
|
||||
if (hook->hash_iter)
|
||||
ev_schedule_work(hook->event);
|
||||
ev_schedule_work(&hook->h.event);
|
||||
else
|
||||
rt_set_export_state(hook, TES_READY);
|
||||
}
|
||||
|
||||
static struct rt_export_hook *
|
||||
bgp_out_table_export_start(struct rt_exporter *re, struct rt_export_request *req UNUSED)
|
||||
{
|
||||
struct bgp_channel *bc = SKIP_BACK(struct bgp_channel, prefix_exporter, re);
|
||||
pool *p = rp_new(bc->c.proto->pool, "Export hook");
|
||||
struct rt_export_hook *hook = mb_allocz(p, sizeof(struct rt_export_hook));
|
||||
hook->pool = p;
|
||||
hook->event = ev_new_init(p, bgp_out_table_feed, hook);
|
||||
hook->feed_type = TFT_HASH;
|
||||
|
||||
return hook;
|
||||
rt_set_export_state(&hook->h, TES_READY);
|
||||
}
|
||||
|
||||
static void
|
||||
bgp_out_table_export_done(struct rt_export_hook *hook)
|
||||
bgp_out_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
||||
{
|
||||
rfree(hook->pool);
|
||||
req->hook = rt_alloc_export(re, sizeof(struct bgp_out_export_hook));
|
||||
req->hook->req = req;
|
||||
|
||||
struct bgp_out_export_hook *hook = SKIP_BACK(struct bgp_out_export_hook, h, req->hook);
|
||||
|
||||
hook->h.event.hook = bgp_out_table_feed;
|
||||
rt_init_export(re, req->hook);
|
||||
}
|
||||
|
||||
static const struct rt_exporter_class bgp_out_table_export_class = {
|
||||
.start = bgp_out_table_export_start,
|
||||
.done = rt_export_stopped,
|
||||
};
|
||||
|
||||
void
|
||||
bgp_setup_out_table(struct bgp_channel *c)
|
||||
{
|
||||
ASSERT_DIE(c->c.out_table == NULL);
|
||||
|
||||
c->prefix_exporter = (struct rt_exporter) {
|
||||
.class = &bgp_out_table_export_class,
|
||||
.addr_type = c->c.table->addr_type,
|
||||
.start = bgp_out_table_export_start,
|
||||
.done = bgp_out_table_export_done,
|
||||
.rp = c->c.proto->pool,
|
||||
};
|
||||
|
||||
init_list(&c->prefix_exporter.hooks);
|
||||
init_list(&c->prefix_exporter.pending);
|
||||
rt_exporter_init(&c->prefix_exporter);
|
||||
|
||||
c->c.out_table = &c->prefix_exporter;
|
||||
}
|
||||
|
@ -847,7 +847,7 @@ bgp_graceful_restart_feed(struct bgp_channel *c)
|
||||
.export_one = bgp_graceful_restart_drop_export,
|
||||
};
|
||||
|
||||
rt_request_export(&c->c.table->exporter, &c->stale_feed);
|
||||
rt_request_export(c->c.table, &c->stale_feed);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user