diff --git a/nest/route.h b/nest/route.h index 27aaec14..55689b5f 100644 --- a/nest/route.h +++ b/nest/route.h @@ -447,7 +447,8 @@ typedef struct rta { ip_addr from; /* Advertising router */ u32 igp_metric; /* IGP metric to next hop (for iBGP routes) */ u16 cached:1; /* Are attributes cached? */ - u16 source:7; /* Route source (RTS_...) */ + u16 obsolete:1; /* This rta is going to be freed */ + u16 source:6; /* Route source (RTS_...) */ u16 scope:4; /* Route scope (SCOPE_... -- see ip.h) */ u16 dest:4; /* Route destination type (RTD_...) */ word pref; @@ -666,9 +667,16 @@ static inline size_t rta_size(const rta *a) { return sizeof(rta) + sizeof(u32)*a #define RTA_MAX_SIZE (sizeof(rta) + sizeof(u32)*MPLS_MAX_LABEL_STACK) rta *rta_lookup(rta *); /* Get rta equivalent to this one, uc++ */ static inline int rta_is_cached(rta *r) { return r->cached; } -static inline rta *rta_clone(rta *r) { r->uc++; return r; } -void rta__free(rta *r); -static inline void rta_free(rta *r) { if (r && !--r->uc) rta__free(r); } +static inline rta *rta_clone(rta *r) +{ + if (r->obsolete) + return rta_lookup(r); + + r->uc++; + return r; +} +void rta_unlink(rta *r); +static inline void rta_free(rta *r) { if (r && !--r->uc) rta_unlink(r); } rta *rta_do_cow(rta *o, linpool *lp); static inline rta * rta_cow(rta *r, linpool *lp) { return rta_is_cached(r) ? rta_do_cow(r, lp) : r; } void rta_dump(rta *); diff --git a/nest/rt-attr.c b/nest/rt-attr.c index 78fb58d6..08920032 100644 --- a/nest/rt-attr.c +++ b/nest/rt-attr.c @@ -51,12 +51,14 @@ #include "nest/cli.h" #include "nest/attrs.h" #include "lib/alloca.h" +#include "lib/gc.h" #include "lib/hash.h" #include "lib/idm.h" #include "lib/resource.h" #include "lib/string.h" #include +#include const adata null_adata; /* adata of length 0 */ @@ -1080,6 +1082,16 @@ static uint rta_cache_size = 32; static uint rta_cache_limit; static uint rta_cache_mask; static rta **rta_hash_table; +static pthread_mutex_t rta_hash_mutex = PTHREAD_MUTEX_INITIALIZER; + +struct rta_gc_chain { + node n; + u64 round; + rta *chain; +}; + +static _Thread_local struct rta_gc_chain *rta_gc_current_chain = NULL; +static list rta_gc_chain_list; static void rta_alloc_hash(void) @@ -1136,6 +1148,7 @@ rta_copy(rta *o) rta *r = sl_alloc(rta_slab(o)); memcpy(r, o, rta_size(o)); + r->obsolete = 0; r->uc = 1; r->nh.next = nexthop_copy(o->nh.next); r->eattrs = ea_list_copy(o->eattrs); @@ -1192,15 +1205,30 @@ rta_lookup(rta *o) rta *r; uint h; + if (o->cached && o->obsolete) + { + pthread_mutex_lock(&rta_hash_mutex); + ASSERT(o->uc == 0); + h = o->hash_key; + goto copy; + } + ASSERT(!o->cached); if (o->eattrs) ea_normalize(o->eattrs); h = rta_hash(o); + + pthread_mutex_lock(&rta_hash_mutex); + for(r=rta_hash_table[h & rta_cache_mask]; r; r=r->next) if (r->hash_key == h && rta_same(r, o)) - return rta_clone(r); + { + r = rta_clone(r); + goto done; + } +copy: r = rta_copy(o); r->hash_key = h; r->cached = 1; @@ -1210,17 +1238,36 @@ rta_lookup(rta *o) if (++rta_cache_count > rta_cache_limit) rta_rehash(); +done: + pthread_mutex_unlock(&rta_hash_mutex); return r; } void -rta__free(rta *a) +rta_unlink(rta *a) { + pthread_mutex_lock(&rta_hash_mutex); ASSERT(rta_cache_count && a->cached); + ASSERT(rta_gc_current_chain); + + a->obsolete = 1; rta_cache_count--; + *a->pprev = a->next; if (a->next) a->next->pprev = a->pprev; + + a->pprev = NULL; + a->next = rta_gc_current_chain->chain; + rta_gc_current_chain->chain = a; + + pthread_mutex_unlock(&rta_hash_mutex); +} + +static void +rta_do_free(rta *a) +{ + ASSERT(a->obsolete); rt_unlock_hostentry(a->hostentry); if (a->nh.next) nexthop_free(a->nh.next); @@ -1229,6 +1276,67 @@ rta__free(rta *a) sl_free(rta_slab(a), a); } +static void +rta_gc_enter(u64 round, struct gc_callback_set *gcs UNUSED) +{ + pthread_mutex_lock(&rta_hash_mutex); + ASSERT(rta_gc_current_chain == NULL); + rta_gc_current_chain = mb_alloc(rta_pool, sizeof(struct rta_gc_chain)); + *rta_gc_current_chain = (struct rta_gc_chain) { .round = round }; + + add_tail(&rta_gc_chain_list, &rta_gc_current_chain->n); + + pthread_mutex_unlock(&rta_hash_mutex); +} + +static void +rta_gc_exit(u64 round UNUSED, struct gc_callback_set *gcs UNUSED) +{ + if (!rta_gc_current_chain->chain) + { + pthread_mutex_lock(&rta_hash_mutex); + rem_node(&rta_gc_current_chain->n); + mb_free(rta_gc_current_chain); + pthread_mutex_unlock(&rta_hash_mutex); + } + + rta_gc_current_chain = NULL; +} + +static void +rta_gc_cleanup(u64 round, struct gc_callback_set *gcs UNUSED) +{ + pthread_mutex_lock(&rta_hash_mutex); + node *n = HEAD(rta_gc_chain_list); + if (!NODE_VALID(n)) + goto done; + + struct rta_gc_chain *rgc = SKIP_BACK(struct rta_gc_chain, n, n); + if (rgc->round > round) + goto done; + + ASSERT(rgc->round == round); + rem_node(n); + + rta *a, *nxt = rgc->chain; + while (a = nxt) + { + nxt = a->next; + rta_do_free(a); + } + + mb_free(rgc); + +done: + pthread_mutex_unlock(&rta_hash_mutex); +} + +static struct gc_callback_set rta_gc_callback_set = { + .enter = rta_gc_enter, + .exit = rta_gc_exit, + .cleanup = rta_gc_cleanup, +}; + rta * rta_do_cow(rta *o, linpool *lp) { @@ -1336,8 +1444,12 @@ rta_init(void) nexthop_slab_[2] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*2); nexthop_slab_[3] = sl_new(rta_pool, sizeof(struct nexthop) + sizeof(u32)*MPLS_MAX_LABEL_STACK); + init_list(&rta_gc_chain_list); + gc_register(&rta_gc_callback_set); + rta_alloc_hash(); rte_src_init(); + } /*