mirror of
https://gitlab.labs.nic.cz/labs/bird.git
synced 2024-05-11 16:54:54 +00:00
rt_setup to do!
This commit is contained in:
+6
-5
@@ -433,19 +433,20 @@ static inline int rt_cork_check(event *e)
|
||||
|
||||
struct rt_pending_export {
|
||||
struct rt_export_item it;
|
||||
struct rt_pending_export *_Atomic next;
|
||||
struct rt_pending_export *_Atomic next; /* Next export for the same net */
|
||||
struct rt_pending_export *the_other; /* Export interlink for table cleanup */
|
||||
};
|
||||
|
||||
struct rt_net_pending_export {
|
||||
struct rt_pending_export *first, *last;
|
||||
struct rt_pending_export * _Atomic first, * _Atomic last;
|
||||
};
|
||||
|
||||
typedef struct network {
|
||||
struct rte_storage * _Atomic routes; /* Available routes for this network */
|
||||
|
||||
/* Uncleaned pending exports */
|
||||
struct rt_net_pending_export _Atomic all;
|
||||
struct rt_net_pending_export _Atomic best;
|
||||
struct rt_net_pending_export all;
|
||||
struct rt_net_pending_export best;
|
||||
} net;
|
||||
|
||||
struct rte_storage {
|
||||
@@ -671,7 +672,7 @@ void rt_flowspec_link(rtable *src, rtable *dst);
|
||||
void rt_flowspec_unlink(rtable *src, rtable *dst);
|
||||
rtable *rt_setup(pool *, struct rtable_config *);
|
||||
|
||||
struct rt_export_feed *rt_net_feed(rtable *t, const net_addr *a, const struct rt_export_item *first);
|
||||
struct rt_export_feed *rt_net_feed(rtable *t, const net_addr *a, const struct rt_pending_export *first);
|
||||
rte rt_net_best(rtable *t, const net_addr *a);
|
||||
int rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filter);
|
||||
rte *rt_export_merged(struct channel *c, const struct rt_export_feed *feed, linpool *pool, int silent);
|
||||
|
||||
+208
-354
@@ -124,6 +124,7 @@ list routing_tables;
|
||||
list deleted_routing_tables;
|
||||
|
||||
netindex_hash *rt_global_netindex_hash;
|
||||
#define RT_INITIAL_ROUTES_BLOCK_SIZE 128
|
||||
|
||||
struct rt_cork rt_cork;
|
||||
|
||||
@@ -909,7 +910,7 @@ channel_notify_accepted(void *_channel)
|
||||
|
||||
case RT_EXPORT_UPDATE:
|
||||
{
|
||||
struct rt_export_feed *f = rt_net_feed(c->table, u->update->new ? u->update->new->net : u->update->old->net, u->update);
|
||||
struct rt_export_feed *f = rt_net_feed(c->table, u->update->new ? u->update->new->net : u->update->old->net, SKIP_BACK(struct rt_pending_export, it, u->update));
|
||||
rt_notify_accepted(c, f);
|
||||
for (uint i=0; i<f->count_exports; i++)
|
||||
rt_export_processed(&c->out_req, f->exports[i]);
|
||||
@@ -1039,7 +1040,7 @@ channel_notify_merged(void *_channel)
|
||||
|
||||
case RT_EXPORT_UPDATE:
|
||||
{
|
||||
struct rt_export_feed *f = rt_net_feed(c->table, u->update->new ? u->update->new->net : u->update->old->net, u->update);
|
||||
struct rt_export_feed *f = rt_net_feed(c->table, u->update->new ? u->update->new->net : u->update->old->net, SKIP_BACK(struct rt_pending_export, it, u->update));
|
||||
rt_notify_merged(c, f);
|
||||
for (uint i=0; i<f->count_exports; i++)
|
||||
rt_export_processed(&c->out_req, f->exports[i]);
|
||||
@@ -1125,11 +1126,11 @@ channel_notify_basic(void *_channel)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rte_announce_to(struct rt_exporter *e, _Atomic struct rt_net_pending_export *npe, const rte *new, const rte *old)
|
||||
static struct rt_pending_export *
|
||||
rte_announce_to(struct rt_exporter *e, struct rt_net_pending_export *npe, const rte *new, const rte *old)
|
||||
{
|
||||
if (new == old)
|
||||
return;
|
||||
return NULL;
|
||||
|
||||
struct rt_pending_export rpe = {
|
||||
.it = {
|
||||
@@ -1139,16 +1140,16 @@ rte_announce_to(struct rt_exporter *e, _Atomic struct rt_net_pending_export *npe
|
||||
};
|
||||
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, pushed, it, rt_exporter_push(e, &rpe.it));
|
||||
struct rt_net_pending_export nloc = atomic_load_explicit(npe, memory_order_relaxed);
|
||||
|
||||
if (nloc.last)
|
||||
ASSERT_DIE(atomic_exchange_explicit(&nloc.last->next, pushed, memory_order_acq_rel) == NULL);
|
||||
|
||||
nloc.last = pushed;
|
||||
if (!nloc.first)
|
||||
nloc.first = pushed;
|
||||
struct rt_pending_export *last = atomic_load_explicit(&npe->last, memory_order_relaxed);
|
||||
if (last)
|
||||
ASSERT_DIE(atomic_exchange_explicit(&last->next, pushed, memory_order_acq_rel) == NULL);
|
||||
|
||||
atomic_store_explicit(&npe->last, pushed, memory_order_release);
|
||||
if (!atomic_load_explicit(&npe->first, memory_order_relaxed))
|
||||
atomic_store_explicit(&npe->first, pushed, memory_order_release);
|
||||
|
||||
atomic_store_explicit(npe, nloc, memory_order_release);
|
||||
return pushed;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1180,54 +1181,89 @@ rte_announce(struct rtable_private *tab, const struct netindex *i UNUSED, net *n
|
||||
return;
|
||||
}
|
||||
|
||||
rte_announce_to(&tab->export_all, &net->all, new, old);
|
||||
rte_announce_to(&tab->export_best, &net->best, new_best, old_best);
|
||||
struct rt_pending_export *best_rpe, *all_rpe;
|
||||
best_rpe = rte_announce_to(&tab->export_best, &net->best, new_best, old_best);
|
||||
all_rpe = rte_announce_to(&tab->export_all, &net->all, new, old);
|
||||
|
||||
if (best_rpe && all_rpe)
|
||||
{
|
||||
best_rpe->the_other = all_rpe;
|
||||
all_rpe->the_other = best_rpe;
|
||||
}
|
||||
|
||||
rt_check_cork_high(tab);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_cleanup_export(struct lfjour *j, struct lfjour_item *i)
|
||||
static _Bool
|
||||
rt_cleanup_export(struct rtable_private *tab, struct rt_net_pending_export *npe, struct rt_pending_export *rpe)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rtable_private, tab, journal, j);
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, rpe, li, i);
|
||||
|
||||
/* Unlink this export from struct network */
|
||||
ASSERT_DIE(rpe->new || rpe->old);
|
||||
const net_addr *n = rpe->new ?
|
||||
rpe->new->net :
|
||||
rpe->old->net;
|
||||
struct netindex *ni = NET_TO_INDEX(n);
|
||||
ASSERT_DIE(ni->index < atomic_load_explicit(&tab->routes_block_size, memory_order_relaxed));
|
||||
net *routes = atomic_load_explicit(&tab->routes, memory_order_relaxed);
|
||||
net *net = &routes[ni->index];
|
||||
|
||||
ASSERT_DIE(rpe == atomic_load_explicit(&net->first, memory_order_relaxed));
|
||||
struct rt_pending_export *first = atomic_load_explicit(&npe->first, memory_order_relaxed);
|
||||
struct rt_pending_export *last = atomic_load_explicit(&npe->last, memory_order_relaxed);
|
||||
ASSERT_DIE(rpe == first);
|
||||
|
||||
/* Update the first and last pointers */
|
||||
struct rt_pending_export *last = rpe,
|
||||
*next = atomic_load_explicit(&rpe->next, memory_order_relaxed);
|
||||
if (atomic_compare_exchange_strong_explicit(
|
||||
&net->last, &last, NULL,
|
||||
memory_order_acq_rel, memory_order_acquire))
|
||||
ASSERT_DIE(next == NULL);
|
||||
atomic_store_explicit(
|
||||
&npe->first,
|
||||
atomic_load_explicit(&rpe->next, memory_order_relaxed),
|
||||
memory_order_release
|
||||
);
|
||||
|
||||
ASSERT_DIE(atomic_compare_exchange_strong_explicit(
|
||||
&net->first, &rpe, next,
|
||||
memory_order_acq_rel, memory_order_relaxed));
|
||||
if (rpe == last)
|
||||
atomic_store_explicit(&npe->last, NULL, memory_order_release);
|
||||
|
||||
/* Wait for very slow table readers */
|
||||
synchronize_rcu();
|
||||
|
||||
if (rpe->old)
|
||||
/* Cleanups may need to be finalized later */
|
||||
if (rpe->the_other)
|
||||
{
|
||||
ASSERT_DIE(rpe->old->flags & REF_OBSOLETE);
|
||||
hmap_clear(&tab->id_map, rpe->old->id);
|
||||
rte_free(SKIP_BACK(struct rte_storage, rte, rpe->old), tab);
|
||||
rpe->the_other->the_other = NULL;
|
||||
rpe->the_other = NULL;
|
||||
}
|
||||
else if (rpe->it.old && (rpe->it.old->flags & REF_OBSOLETE))
|
||||
{
|
||||
/* When cleaning up the best journal, the old route need not be obsolete */
|
||||
hmap_clear(&tab->id_map, rpe->it.old->id);
|
||||
rte_free(SKIP_BACK(struct rte_storage, rte, rpe->it.old), tab);
|
||||
}
|
||||
|
||||
if (!routes && !next)
|
||||
tab->gc_counter++;
|
||||
return (rpe == last);
|
||||
}
|
||||
|
||||
static struct network *
|
||||
rt_cleanup_export_get_net(struct rtable_private *tab, struct rt_pending_export *rpe)
|
||||
{
|
||||
/* Unlink this export from struct network */
|
||||
ASSERT_DIE(rpe->it.new || rpe->it.old);
|
||||
const net_addr *n = rpe->it.new ?
|
||||
rpe->it.new->net :
|
||||
rpe->it.old->net;
|
||||
struct netindex *ni = NET_TO_INDEX(n);
|
||||
ASSERT_DIE(ni->index < atomic_load_explicit(&tab->routes_block_size, memory_order_relaxed));
|
||||
net *routes = atomic_load_explicit(&tab->routes, memory_order_relaxed);
|
||||
return &routes[ni->index];
|
||||
}
|
||||
|
||||
static void
|
||||
rt_cleanup_export_best(struct lfjour *j, struct lfjour_item *i)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, rpe, it.li, i);
|
||||
SKIP_BACK_DECLARE(struct rtable_private, t, export_best.journal, j);
|
||||
net *net = rt_cleanup_export_get_net(t, rpe);
|
||||
if (rt_cleanup_export(t, &net->best, rpe) &&
|
||||
!atomic_load_explicit(&net->routes, memory_order_relaxed))
|
||||
t->gc_counter++;
|
||||
}
|
||||
|
||||
static void
|
||||
rt_cleanup_export_all(struct lfjour *j, struct lfjour_item *i)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, rpe, it.li, i);
|
||||
SKIP_BACK_DECLARE(struct rtable_private, t, export_all.journal, j);
|
||||
net *net = rt_cleanup_export_get_net(t, rpe);
|
||||
if (rt_cleanup_export(t, &net->all, rpe) &&
|
||||
!atomic_load_explicit(&net->routes, memory_order_relaxed))
|
||||
t->gc_counter++;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1257,9 +1293,8 @@ rt_import_cleared(void *_ih)
|
||||
}
|
||||
|
||||
static void
|
||||
rt_cleanup_done(struct lfjour *j, u64 begin_seq, u64 end_seq)
|
||||
rt_cleanup_done(struct rtable_private *tab, u64 begin_seq, u64 end_seq)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rtable_private, tab, journal, j);
|
||||
ASSERT_DIE(DG_IS_LOCKED(tab->lock.rtable));
|
||||
|
||||
if (~end_seq)
|
||||
@@ -1292,49 +1327,21 @@ rt_cleanup_done(struct lfjour *j, u64 begin_seq, u64 end_seq)
|
||||
rt_kick_prune_timer(tab);
|
||||
}
|
||||
|
||||
#define RT_EXPORT_BULK 1024
|
||||
|
||||
static void
|
||||
rt_export_hook(void *_data)
|
||||
rt_cleanup_done_best(struct lfjour *j, u64 begin_seq, u64 end_seq)
|
||||
{
|
||||
struct rt_export_hook *c = _data;
|
||||
struct lfjour_recipient *r = &c->recipient;
|
||||
|
||||
ASSERT_DIE(atomic_load_explicit(&c->export_state, memory_order_relaxed) == TES_READY);
|
||||
|
||||
/* Process the export */
|
||||
for (uint i=0; i<RT_EXPORT_BULK; i++)
|
||||
{
|
||||
/* Get the next export if exists */
|
||||
struct lfjour_item *li = lfjour_get(r);
|
||||
|
||||
/* Stop exporting if no export is available */
|
||||
if (!li)
|
||||
return;
|
||||
|
||||
/* Process sequence number reset event */
|
||||
if (lfjour_reset_seqno(r))
|
||||
bmap_reset(&c->seq_map, 16);
|
||||
|
||||
/* Process the export */
|
||||
rte_export(c, SKIP_BACK(struct rt_pending_export, li, li));
|
||||
|
||||
/* And release the export */
|
||||
lfjour_release(r);
|
||||
}
|
||||
|
||||
/*
|
||||
* is this actually needed?
|
||||
if (used)
|
||||
RT_LOCKED(tab, t)
|
||||
if (no_next || t->cork_active)
|
||||
rt_export_used(c->table, c->req->name, no_next ? "finished export bulk" : "cork active");
|
||||
*/
|
||||
|
||||
/* Request continuation */
|
||||
rt_send_export_event(c);
|
||||
SKIP_BACK_DECLARE(struct rtable_private, t, export_best.journal, j);
|
||||
rt_cleanup_done(t, begin_seq, end_seq);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_cleanup_done_all(struct lfjour *j, u64 begin_seq, u64 end_seq)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rtable_private, t, export_all.journal, j);
|
||||
rt_cleanup_done(t, begin_seq, end_seq);
|
||||
}
|
||||
|
||||
#define RT_EXPORT_BULK 1024
|
||||
|
||||
static inline int
|
||||
rte_validate(struct channel *ch, rte *e)
|
||||
@@ -1875,7 +1882,7 @@ rte_import(struct rt_import_request *req, const net_addr *n, rte *new, struct rt
|
||||
}
|
||||
|
||||
struct rt_export_feed *
|
||||
rt_net_feed(rtable *t, net_addr *a)
|
||||
rt_net_feed(rtable *t, const net_addr *a, const struct rt_pending_export *first)
|
||||
{
|
||||
RT_READ(t, tr);
|
||||
|
||||
@@ -1885,18 +1892,37 @@ rt_net_feed(rtable *t, net_addr *a)
|
||||
return 0;
|
||||
|
||||
/* Get the feed itself. It may change under our hands tho. */
|
||||
struct rt_pending_export *first = atomic_load_explicit(&n->first, memory_order_acquire);
|
||||
struct rt_pending_export *last = atomic_load_explicit(&n->last, memory_order_acquire);
|
||||
struct rt_pending_export *first_in_net, *last_in_net;
|
||||
first_in_net = atomic_load_explicit(&n->all.first, memory_order_acquire);
|
||||
last_in_net = atomic_load_explicit(&n->all.last, memory_order_acquire);
|
||||
|
||||
/* Export item validity check: we must find it between first_in_net and last_in_net */
|
||||
if (first)
|
||||
{
|
||||
struct rt_pending_export *rpe = first_in_net;
|
||||
while (rpe)
|
||||
if (rpe == first)
|
||||
break;
|
||||
else if (rpe == last_in_net)
|
||||
bug("Called rt_net_feed with an invalid first journal item");
|
||||
else
|
||||
rpe = atomic_load_explicit(&rpe->next, memory_order_acquire);
|
||||
|
||||
if (rpe != first)
|
||||
bug("Called rt_net_feed with an invalid first journal item");
|
||||
}
|
||||
else
|
||||
first = first_in_net;
|
||||
|
||||
/* Count the elements */
|
||||
uint rcnt = rte_feed_count(tr, n);
|
||||
uint ecnt = 0;
|
||||
uint ocnt = 0;
|
||||
for (struct rt_pending_export *rpe = first; rpe;
|
||||
for (const struct rt_pending_export *rpe = first; rpe;
|
||||
rpe = atomic_load_explicit(&rpe->next, memory_order_acquire))
|
||||
{
|
||||
ecnt++;
|
||||
if (rpe->old)
|
||||
if (rpe->it.old)
|
||||
ocnt++;
|
||||
}
|
||||
|
||||
@@ -1926,20 +1952,20 @@ rt_net_feed(rtable *t, net_addr *a)
|
||||
{
|
||||
uint e = 0;
|
||||
uint rpos = rcnt;
|
||||
for (struct rt_pending_export *rpe = first; rpe;
|
||||
for (const struct rt_pending_export *rpe = first; rpe;
|
||||
rpe = atomic_load_explicit(&rpe->next, memory_order_acquire))
|
||||
if (e >= ecnt)
|
||||
RT_READ_RETRY(tr);
|
||||
else
|
||||
{
|
||||
feed->exports[e++] = rpe->seq;
|
||||
feed->exports[e++] = rpe->it.seq;
|
||||
|
||||
/* Copy also obsolete routes */
|
||||
if (rpe->old)
|
||||
if (rpe->it.old)
|
||||
{
|
||||
ASSERT_DIE(rpos < rcnt + ocnt);
|
||||
feed->block[rpos++] = *rpe->old;
|
||||
ea_free_later(ea_ref(rpe->old->attrs));
|
||||
feed->block[rpos++] = *rpe->it.old;
|
||||
ea_free_later(ea_ref(rpe->it.old->attrs));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1948,15 +1974,16 @@ rt_net_feed(rtable *t, net_addr *a)
|
||||
}
|
||||
|
||||
/* Check that it indeed didn't change and the last export is still the same. */
|
||||
if (last != atomic_load_explicit(&n->last, memory_order_acquire) ||
|
||||
first != atomic_load_explicit(&n->first, memory_order_acquire))
|
||||
if (
|
||||
(first_in_net != atomic_load_explicit(&n->all.first, memory_order_acquire))
|
||||
|| (last_in_net != atomic_load_explicit(&n->all.last, memory_order_acquire)))
|
||||
RT_READ_RETRY(tr);
|
||||
|
||||
return feed;
|
||||
}
|
||||
|
||||
rte
|
||||
rt_net_best(rtable *t, net_addr *a)
|
||||
rt_net_best(rtable *t, const net_addr *a)
|
||||
{
|
||||
rte rt = {};
|
||||
|
||||
@@ -1988,34 +2015,6 @@ rt_examine(rtable *t, net_addr *a, struct channel *c, const struct filter *filte
|
||||
return v > 0;
|
||||
}
|
||||
|
||||
static void
|
||||
rt_table_export_done(void *hh)
|
||||
{
|
||||
struct rt_export_hook *hook = hh;
|
||||
struct rt_export_request *req = hook->req;
|
||||
void (*stopped)(struct rt_export_request *) = hook->stopped;
|
||||
rtable *t = hook->tab;
|
||||
|
||||
/* Drop the hook */
|
||||
RT_LOCKED(t, tab)
|
||||
{
|
||||
/* Unlink from the table */
|
||||
if (lfjour_of_recipient(&hook->recipient))
|
||||
lfjour_unregister(&hook->recipient);
|
||||
|
||||
DBG("Export hook %p in table %s finished uc=%u\n", hook, tab->name, tab->use_count);
|
||||
|
||||
/* Free the hook */
|
||||
rp_free(hook->pool);
|
||||
}
|
||||
|
||||
/* Inform the stopper */
|
||||
CALL(stopped, req);
|
||||
|
||||
/* Unlock the table */
|
||||
rt_unlock_table(t);
|
||||
}
|
||||
|
||||
static inline void
|
||||
rt_set_import_state(struct rt_import_hook *hook, u8 state)
|
||||
{
|
||||
@@ -2025,24 +2024,6 @@ rt_set_import_state(struct rt_import_hook *hook, u8 state)
|
||||
CALL(hook->req->log_state_change, hook->req, state);
|
||||
}
|
||||
|
||||
u8
|
||||
rt_set_export_state(struct rt_export_hook *hook, u32 expected_mask, u8 state)
|
||||
{
|
||||
hook->last_state_change = current_time();
|
||||
u8 old = atomic_exchange_explicit(&hook->export_state, state, memory_order_release);
|
||||
if (!((1 << old) & expected_mask))
|
||||
bug("Unexpected export state change from %s to %s, expected mask %02x",
|
||||
rt_export_state_name(old),
|
||||
rt_export_state_name(state),
|
||||
expected_mask
|
||||
);
|
||||
|
||||
if (old != state)
|
||||
CALL(hook->req->log_state_change, hook->req, state);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
void
|
||||
rt_request_import(rtable *t, struct rt_import_request *req)
|
||||
{
|
||||
@@ -2087,164 +2068,6 @@ rt_stop_import(struct rt_import_request *req, void (*stopped)(struct rt_import_r
|
||||
}
|
||||
}
|
||||
|
||||
static void rt_table_export_start_feed(struct rtable_private *tab, struct rt_export_hook *hook);
|
||||
static void
|
||||
rt_table_export_uncork(void *_hook)
|
||||
{
|
||||
ASSERT_DIE(birdloop_inside(&main_birdloop));
|
||||
|
||||
struct rt_export_hook *hook = _hook;
|
||||
struct birdloop *loop = hook->req->list->loop;
|
||||
|
||||
if (loop != &main_birdloop)
|
||||
birdloop_enter(loop);
|
||||
|
||||
u8 state;
|
||||
RT_LOCKED(hook->tab, tab)
|
||||
switch (state = atomic_load_explicit(&hook->export_state, memory_order_relaxed))
|
||||
{
|
||||
case TES_HUNGRY:
|
||||
rt_table_export_start_feed(tab, hook);
|
||||
break;
|
||||
case TES_STOP:
|
||||
hook->event->hook = rt_table_export_done;
|
||||
rt_send_export_event(hook);
|
||||
break;
|
||||
default:
|
||||
bug("Uncorking a table export in a strange state: %u", state);
|
||||
}
|
||||
|
||||
if (loop != &main_birdloop)
|
||||
birdloop_leave(loop);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_table_export_start_locked(struct rtable_private *tab, struct rt_export_request *req)
|
||||
{
|
||||
rt_lock_table(tab);
|
||||
|
||||
pool *p = rp_new(req->pool, req->pool->domain, "Export hook");
|
||||
struct rt_export_hook *hook = req->hook = mb_allocz(p, sizeof(struct rt_export_hook));
|
||||
hook->req = req;
|
||||
hook->tab = RT_PUB(tab);
|
||||
hook->pool = p;
|
||||
atomic_store_explicit(&hook->export_state, TES_DOWN, memory_order_release);
|
||||
hook->event = ev_new_init(p, rt_table_export_uncork, hook);
|
||||
|
||||
if (rt_cork_check(hook->event))
|
||||
rt_set_export_state(hook, BIT32_ALL(TES_DOWN), TES_HUNGRY);
|
||||
else
|
||||
rt_table_export_start_feed(tab, hook);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_table_export_start_feed(struct rtable_private *tab, struct rt_export_hook *hook)
|
||||
{
|
||||
struct rt_export_request *req = hook->req;
|
||||
|
||||
/* stats zeroed by mb_allocz */
|
||||
switch (req->prefilter.mode)
|
||||
{
|
||||
case TE_ADDR_IN:
|
||||
case TE_ADDR_NONE:
|
||||
case TE_ADDR_TRIE:
|
||||
case TE_ADDR_HOOK:
|
||||
hook->feed_index = 0;
|
||||
hook->event->hook = rt_feed_by_fib;
|
||||
break;
|
||||
|
||||
case TE_ADDR_EQUAL:
|
||||
hook->event->hook = rt_feed_equal;
|
||||
break;
|
||||
|
||||
case TE_ADDR_FOR:
|
||||
hook->event->hook = rt_feed_for;
|
||||
break;
|
||||
|
||||
default:
|
||||
bug("Requested an unknown export address mode");
|
||||
}
|
||||
|
||||
DBG("New export hook %p req %p in table %s uc=%u\n", hook, req, tab->name, tab->use_count);
|
||||
|
||||
hook->recipient = (struct lfjour_recipient) {
|
||||
.event = hook->event,
|
||||
.target = req->list,
|
||||
};
|
||||
lfjour_register(&tab->journal, &hook->recipient);
|
||||
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, rpe, li, atomic_load_explicit(&hook->recipient.last, memory_order_relaxed));
|
||||
req_trace(req, D_STATES, "Export initialized, last export %p (%lu)", rpe, rpe ? rpe->seq : 0);
|
||||
|
||||
bmap_init(&hook->seq_map, hook->pool, 16);
|
||||
|
||||
/* Regular export */
|
||||
rt_set_export_state(hook, BIT32_ALL(TES_DOWN, TES_HUNGRY), TES_FEEDING);
|
||||
rt_send_export_event(hook);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void
|
||||
rt_table_export_start(struct rt_exporter *re, struct rt_export_request *req)
|
||||
{
|
||||
RT_LOCKED(SKIP_BACK(rtable, priv.exporter, re), tab)
|
||||
rt_table_export_start_locked(tab, req);
|
||||
}
|
||||
#endif
|
||||
|
||||
void rt_request_export(rtable *t, struct rt_export_request *req)
|
||||
{
|
||||
RT_LOCKED(t, tab)
|
||||
rt_table_export_start_locked(tab, req); /* Is locked inside */
|
||||
}
|
||||
|
||||
static void
|
||||
rt_stop_export_locked(struct rtable_private *tab, struct rt_export_hook *hook)
|
||||
{
|
||||
struct rt_export_request *req = hook->req;
|
||||
|
||||
/* Update export state, get old */
|
||||
switch (rt_set_export_state(hook, BIT32_ALL(TES_HUNGRY, TES_FEEDING, TES_READY), TES_STOP))
|
||||
{
|
||||
case TES_STOP:
|
||||
rt_trace(tab, D_EVENTS, "Stopping export hook %s already requested", req->name);
|
||||
return;
|
||||
|
||||
case TES_HUNGRY:
|
||||
rt_trace(tab, D_EVENTS, "Stopping export hook %s must wait for uncorking", req->name);
|
||||
return;
|
||||
|
||||
case TES_FEEDING:
|
||||
break;
|
||||
}
|
||||
|
||||
rt_trace(tab, D_EVENTS, "Stopping export hook %s right now", req->name);
|
||||
|
||||
/* Reset the event as the stopped event */
|
||||
ASSERT_DIE(birdloop_inside(req->list->loop));
|
||||
hook->event->hook = rt_table_export_done;
|
||||
|
||||
/* Run the stopped event */
|
||||
rt_send_export_event(hook);
|
||||
}
|
||||
|
||||
void
|
||||
rt_stop_export(struct rt_export_request *req, void (*stopped)(struct rt_export_request *))
|
||||
{
|
||||
ASSERT_DIE(birdloop_inside(req->list->loop));
|
||||
struct rt_export_hook *hook = req->hook;
|
||||
ASSERT_DIE(hook);
|
||||
|
||||
RT_LOCKED(hook->tab, t)
|
||||
{
|
||||
/* Set the stopped callback */
|
||||
hook->stopped = stopped;
|
||||
|
||||
/* Do the rest */
|
||||
rt_stop_export_locked(t, hook);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rt_refresh_begin - start a refresh cycle
|
||||
@@ -2421,6 +2244,8 @@ rt_dump_hooks(rtable *tp)
|
||||
ih->last_state_change, ih->import_state, ih->stopped);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* FIXME: I'm very lazy to write this now */
|
||||
WALK_TLIST(lfjour_recipient, r, &tab->journal.recipients)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_export_hook, eh, recipient, r);
|
||||
@@ -2430,6 +2255,7 @@ rt_dump_hooks(rtable *tp)
|
||||
eh, eh->req, eh->refeed_pending, eh->last_state_change,
|
||||
atomic_load_explicit(&eh->export_state, memory_order_relaxed));
|
||||
}
|
||||
#endif
|
||||
debug("\n");
|
||||
|
||||
}
|
||||
@@ -2513,43 +2339,72 @@ struct rt_flowspec_link {
|
||||
rtable *dst;
|
||||
u32 uc;
|
||||
struct rt_export_request req;
|
||||
event event;
|
||||
};
|
||||
|
||||
#include "lib/tlists.h"
|
||||
|
||||
|
||||
static void
|
||||
rt_flowspec_export_one(struct rt_export_request *req, const net_addr *net, struct rt_pending_export *first)
|
||||
rt_flowspec_export(void *_link)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_flowspec_link, ln, req, req);
|
||||
struct rt_flowspec_link *ln = _link;
|
||||
rtable *dst_pub = ln->dst;
|
||||
ASSUME(rt_is_flow(dst_pub));
|
||||
|
||||
RT_LOCKED(dst_pub, dst)
|
||||
RT_EXPORT_WALK(&ln->req, u)
|
||||
{
|
||||
const net_addr *n = NULL;
|
||||
switch (u->kind)
|
||||
{
|
||||
case RT_EXPORT_STOP:
|
||||
bug("Main table export stopped");
|
||||
|
||||
/* No need to inspect it further if recalculation is already scheduled */
|
||||
if ((dst->nhu_state == NHU_SCHEDULED) || (dst->nhu_state == NHU_DIRTY)
|
||||
|| !trie_match_net(dst->flowspec_trie, net))
|
||||
{
|
||||
rpe_mark_seen_all(req->hook, first, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
case RT_EXPORT_FEED:
|
||||
if (u->feed->count_routes)
|
||||
n = u->feed->block[0].net;
|
||||
break;
|
||||
|
||||
/* This net may affect some flowspecs, check the actual change */
|
||||
const rte *o = RTE_VALID_OR_NULL(first->old_best);
|
||||
const rte *new_best = first->new_best;
|
||||
case RT_EXPORT_UPDATE:
|
||||
n = (u->update->new ?: u->update->old)->net;
|
||||
break;
|
||||
}
|
||||
|
||||
RPE_WALK(first, rpe, NULL)
|
||||
{
|
||||
rpe_mark_seen(req->hook, rpe);
|
||||
new_best = rpe->new_best;
|
||||
}
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
/* Yes, something has actually changed. Schedule the update. */
|
||||
if (o != RTE_VALID_OR_NULL(new_best))
|
||||
rt_schedule_nhu(dst);
|
||||
RT_LOCKED(dst_pub, dst)
|
||||
{
|
||||
/* No need to inspect it further if recalculation is already scheduled */
|
||||
if ((dst->nhu_state == NHU_SCHEDULED) || (dst->nhu_state == NHU_DIRTY))
|
||||
break;
|
||||
|
||||
/* Irrelevant prefix */
|
||||
if (!trie_match_net(dst->flowspec_trie, n))
|
||||
break;
|
||||
|
||||
/* Conflate following updates */
|
||||
const rte *old = RTE_VALID_OR_NULL(u->update->old);
|
||||
const rte *new = u->update->new;
|
||||
for (
|
||||
SKIP_BACK_DECLARE(struct rt_pending_export, rpe, it, u->update);
|
||||
rpe = atomic_load_explicit(&rpe->next, memory_order_acquire) ;)
|
||||
{
|
||||
ASSERT_DIE(new == rpe->it.old);
|
||||
new = rpe->it.new;
|
||||
rt_export_processed(&ln->req, rpe->it.seq);
|
||||
}
|
||||
|
||||
/* Ignore idempotent */
|
||||
if ((old == new) || old && new && rte_same(old, new))
|
||||
break;
|
||||
|
||||
/* Actually, schedule NHU */
|
||||
rt_schedule_nhu(dst);
|
||||
}
|
||||
|
||||
if (!task_still_in_limit())
|
||||
return ev_send_loop(dst_pub->loop, &ln->event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2560,25 +2415,19 @@ rt_flowspec_dump_req(struct rt_export_request *req)
|
||||
debug(" Flowspec link for table %s (%p)\n", ln->dst->name, req);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_flowspec_log_state_change(struct rt_export_request *req, u8 state)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_flowspec_link, ln, req, req);
|
||||
rt_trace(ln->dst, D_STATES, "Flowspec link from %s export state changed to %s",
|
||||
ln->src->name, rt_export_state_name(state));
|
||||
}
|
||||
|
||||
static struct rt_flowspec_link *
|
||||
rt_flowspec_find_link(struct rtable_private *src, rtable *dst)
|
||||
{
|
||||
WALK_TLIST(rt_flowspec_link, ln, &src->flowspec_links)
|
||||
if (ln->dst == dst && ln->req.hook)
|
||||
switch (atomic_load_explicit(&ln->req.hook->export_state, memory_order_acquire))
|
||||
if (ln->dst == dst)
|
||||
switch (rt_export_get_state(&ln->req))
|
||||
{
|
||||
case TES_HUNGRY:
|
||||
case TES_FEEDING:
|
||||
case TES_READY:
|
||||
return ln;
|
||||
|
||||
default:
|
||||
bug("Unexpected flowspec link state");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -2606,16 +2455,21 @@ rt_flowspec_link(rtable *src_pub, rtable *dst_pub)
|
||||
ln->dst = dst_pub;
|
||||
ln->req = (struct rt_export_request) {
|
||||
.name = mb_sprintf(p, "%s.flowspec.notifier", dst_pub->name),
|
||||
.list = birdloop_event_list(dst_pub->loop),
|
||||
.r = {
|
||||
.event = &ln->event,
|
||||
.target = birdloop_event_list(dst_pub->loop),
|
||||
},
|
||||
.pool = p,
|
||||
.trace_routes = src->config->debug,
|
||||
.dump_req = rt_flowspec_dump_req,
|
||||
.log_state_change = rt_flowspec_log_state_change,
|
||||
.export_one = rt_flowspec_export_one,
|
||||
.dump = rt_flowspec_dump_req,
|
||||
};
|
||||
ln->event = (event) {
|
||||
.hook = rt_flowspec_export,
|
||||
.data = ln,
|
||||
};
|
||||
rt_flowspec_link_add_tail(&src->flowspec_links, ln);
|
||||
|
||||
rt_table_export_start_locked(src, &ln->req);
|
||||
rt_export_subscribe(&src->export_best, &ln->req);
|
||||
|
||||
lock_dst = 1;
|
||||
}
|
||||
@@ -2629,21 +2483,13 @@ rt_flowspec_link(rtable *src_pub, rtable *dst_pub)
|
||||
birdloop_leave(dst_pub->loop);
|
||||
}
|
||||
|
||||
static void
|
||||
rt_flowspec_link_stopped(struct rt_export_request *req)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rt_flowspec_link, ln, req, req);
|
||||
rtable *dst = ln->dst;
|
||||
|
||||
mb_free(ln);
|
||||
rt_unlock_table(dst);
|
||||
}
|
||||
|
||||
void
|
||||
rt_flowspec_unlink(rtable *src, rtable *dst)
|
||||
{
|
||||
birdloop_enter(dst->loop);
|
||||
|
||||
_Bool unlock_dst = 0;
|
||||
|
||||
struct rt_flowspec_link *ln;
|
||||
RT_LOCKED(src, t)
|
||||
{
|
||||
@@ -2654,11 +2500,16 @@ rt_flowspec_unlink(rtable *src, rtable *dst)
|
||||
if (!--ln->uc)
|
||||
{
|
||||
rt_flowspec_link_rem_node(&t->flowspec_links, ln);
|
||||
ln->req.hook->stopped = rt_flowspec_link_stopped;
|
||||
rt_stop_export_locked(t, ln->req.hook);
|
||||
rt_export_unsubscribe(&ln->req);
|
||||
ev_postpone(&ln->event);
|
||||
mb_free(ln);
|
||||
unlock_dst = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlock_dst)
|
||||
rt_unlock_table(dst);
|
||||
|
||||
birdloop_leave(dst->loop);
|
||||
}
|
||||
|
||||
@@ -2696,19 +2547,22 @@ rt_free(resource *_r)
|
||||
}
|
||||
|
||||
static void
|
||||
rt_res_dump(resource *_r, unsigned indent)
|
||||
rt_res_dump(resource *_r, unsigned indent UNUSED)
|
||||
{
|
||||
SKIP_BACK_DECLARE(struct rtable_private, r, r, _r);
|
||||
|
||||
debug("name \"%s\", addr_type=%s, rt_count=%u, use_count=%d\n",
|
||||
r->name, net_label[r->addr_type], r->rt_count, r->use_count);
|
||||
|
||||
#if 0
|
||||
/* TODO: rethink this completely */
|
||||
/* TODO: move this to lfjour */
|
||||
char x[32];
|
||||
bsprintf(x, "%%%dspending export %%p\n", indent + 2);
|
||||
|
||||
WALK_TLIST(lfjour_block, n, &r->journal.pending)
|
||||
debug(x, "", n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct resclass rt_class = {
|
||||
|
||||
Reference in New Issue
Block a user