From: Jan Maria Matejka Date: Mon, 3 Dec 2018 09:31:59 +0000 (+0100) Subject: Nest: Using the brand-new redblack tree to provide an ordered route listing X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4067ad1aa495882db1074fb5b488246b11f2230b;p=thirdparty%2Fbird.git Nest: Using the brand-new redblack tree to provide an ordered route listing --- diff --git a/nest/config.Y b/nest/config.Y index 76de67f97..20a62b7f0 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -71,7 +71,7 @@ CF_KEYWORDS(RECEIVE, LIMIT, ACTION, WARN, BLOCK, RESTART, DISABLE, KEEP, FILTERE CF_KEYWORDS(PASSWORD, FROM, PASSIVE, TO, ID, EVENTS, PACKETS, PROTOCOLS, INTERFACES) CF_KEYWORDS(ALGORITHM, KEYED, HMAC, MD5, SHA1, SHA256, SHA384, SHA512) CF_KEYWORDS(PRIMARY, STATS, COUNT, BY, FOR, COMMANDS, PREEXPORT, NOEXPORT, GENERATE) -CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED) +CF_KEYWORDS(BGP, PASSWORDS, DESCRIPTION, SORTED, ORDERED) CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CLASS, DSCP) CF_KEYWORDS(TIMEFORMAT, ISO, SHORT, LONG, ROUTE, PROTOCOL, BASE, LOG, S, MS, US) CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS, STATISTICS) @@ -558,6 +558,10 @@ r_args: rt_show_add_table($$, t->table); $$->tables_defined_by = RSD_TDB_ALL; } + | r_args ORDERED { + $$ = $1; + $$->fit.flags |= FIF_ORDERED; + } | r_args FILTER filter { $$ = $1; if ($$->filter != FILTER_ACCEPT) cf_error("Filter specified twice"); diff --git a/nest/route.h b/nest/route.h index 299cbac22..61b30f6e9 100644 --- a/nest/route.h +++ b/nest/route.h @@ -10,6 +10,7 @@ #define _BIRD_ROUTE_H_ #include "lib/lists.h" +#include "lib/redblack.h" #include "lib/resource.h" #include "lib/net.h" @@ -37,13 +38,17 @@ struct fib_node { struct fib_node *next; /* Next in hash chain */ struct fib_iterator *readers; /* List of readers of this node */ byte flags; /* User-defined, will be removed */ + REDBLACK_NODE(struct fib_node, rb); /* Node in search tree */ net_addr addr[0]; }; +#define FIF_ORDERED 1 + struct fib_iterator { /* See lib/slists.h for an explanation */ struct fib_iterator *prev, *next; /* Must be synced with struct fib_node! */ byte efef; /* 0xff to distinguish between iterator and node */ - byte pad[3]; + byte flags; /* FIF_* */ + byte pad[2]; struct fib_node *node; /* Or NULL if freshly merged */ uint hash; }; @@ -53,6 +58,7 @@ typedef void (*fib_init_fn)(void *); struct fib { pool *fib_pool; /* Pool holding all our data */ slab *fib_slab; /* Slab holding all fib nodes */ + struct fib_node *tree_root; /* Tree to hold lexicographically sorted entries */ struct fib_node **hash_table; /* Node hash table */ uint hash_size; /* Number of hash table entries (a power of two) */ uint hash_order; /* Binary logarithm of hash_size */ @@ -73,7 +79,7 @@ static inline struct fib_node * fib_user_to_node(struct fib *f, void *e) void fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offset, uint hash_order, fib_init_fn init); void *fib_find(struct fib *, const net_addr *); /* Find or return NULL if doesn't exist */ -void *fib_get_chain(struct fib *f, const net_addr *a); /* Find first node in linked list from hash table */ +void *fib_get_chain(struct fib *f, const net_addr *a) DEPRECATED; /* Find first node in linked list from hash table */ void *fib_get(struct fib *, const net_addr *); /* Find or create new if nonexistent */ void *fib_route(struct fib *, const net_addr *); /* Longest-match routing lookup */ void fib_delete(struct fib *, void *); /* Remove fib entry */ @@ -98,21 +104,27 @@ void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uin #define FIB_ITERATE_INIT(it, fib) fit_init(it, fib) #define FIB_ITERATE_START(fib, it, type, z) do { \ + struct fib_iterator *it_ = it; \ struct fib_node *fn_ = fit_get(fib, it); \ uint count_ = (fib)->hash_size; \ - uint hpos_ = (it)->hash; \ + uint hpos_ = (it_)->hash; \ type *z; \ for(;;) { \ if (!fn_) \ { \ - if (++hpos_ >= count_) \ + if ((it_->flags & FIF_ORDERED) || \ + (++hpos_ >= count_)) \ break; \ fn_ = (fib)->hash_table[hpos_]; \ continue; \ } \ z = fib_node_to_user(fib, fn_); -#define FIB_ITERATE_END fn_ = fn_->next; } } while(0) +#define FIB_ITERATE_END \ + fn_ = (it_->flags & FIF_ORDERED) ? \ + REDBLACK_NEXT(struct fib_node, rb, fn_) : \ + fn_->next; \ + } } while(0) #define FIB_ITERATE_PUT(it) fit_put(it, fn_) diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 18ccbfc39..e13603575 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -2,6 +2,7 @@ * BIRD -- Forwarding Information Base -- Data Structures * * (c) 1998--2000 Martin Mares + * (c) 2018 Maria Matejka * * Can be freely distributed and used under the terms of the GNU GPL. */ @@ -111,6 +112,8 @@ fib_init(struct fib *f, pool *p, uint addr_type, uint node_size, uint node_offse { uint addr_length = net_addr_length[addr_type]; + f->tree_root = NULL; + if (!hash_order) hash_order = HASH_DEF_ORDER; f->fib_pool = p; @@ -172,6 +175,7 @@ fib_rehash(struct fib *f, int step) #define CAST(t) (const net_addr_##t *) #define CAST2(t) (net_addr_##t *) +#define FIB_KEY(e) ((e)->addr) #define FIB_HASH(f,a,t) (net_hash_##t(CAST(t) a) >> f->hash_shift) #define FIB_FIND(f,a,t) \ @@ -194,6 +198,8 @@ fib_rehash(struct fib *f, int step) net_copy_##t(CAST2(t) e->addr, CAST(t) a); \ e->next = *ee; \ *ee = e; \ + REDBLACK_INSERT(struct fib_node, rb, CAST(t) FIB_KEY, \ + net_compare_##t, f->tree_root, e); \ }) @@ -363,41 +369,6 @@ fib_route(struct fib *f, const net_addr *n) } } - -static inline void -fib_merge_readers(struct fib_iterator *i, struct fib_node *to) -{ - if (to) - { - struct fib_iterator *j = to->readers; - if (!j) - { - /* Fast path */ - to->readers = i; - i->prev = (struct fib_iterator *) to; - } - else - { - /* Really merging */ - while (j->next) - j = j->next; - j->next = i; - i->prev = j; - } - while (i && i->node) - { - i->node = NULL; - i = i->next; - } - } - else /* No more nodes */ - while (i) - { - i->prev = NULL; - i = i->next; - } -} - /** * fib_delete - delete a FIB node * @f: FIB to delete from @@ -417,33 +388,54 @@ fib_delete(struct fib *f, void *E) while (*ee) { - if (*ee == e) + if (*ee != e) { - *ee = e->next; - if (it = e->readers) + ee = &((*ee)->next); + continue; + } + + *ee = e->next; + +#define UNDEF ((void *) 1) + struct fib_node *onext = UNDEF, *hnext = UNDEF; + while (it = e->readers) + { + e->readers = it->next; + if (it->flags & FIF_ORDERED) + { + if (onext == UNDEF) + onext = REDBLACK_NEXT(struct fib_node, rb, e); + fit_put(it, onext); + } + else { - struct fib_node *l = e->next; - while (!l) - { - h++; - if (h >= f->hash_size) - break; - else - l = f->hash_table[h]; + if (hnext == UNDEF) + { + hnext = e->next; + while (!hnext) + { + h++; + if (h >= f->hash_size) + break; + else + hnext = f->hash_table[h]; + } } - fib_merge_readers(it, l); + fit_put(it, hnext); } + } +#undef UNDEF - if (f->fib_slab) - sl_free(f->fib_slab, E); - else - mb_free(E); + if (f->fib_slab) + sl_free(f->fib_slab, E); + else + mb_free(E); - if (f->entries-- < f->entries_min) - fib_rehash(f, -HASH_LO_STEP); - return; - } - ee = &((*ee)->next); + REDBLACK_DELETE(struct fib_node, rb, f->tree_root, e); + + if (f->entries-- < f->entries_min) + fib_rehash(f, -HASH_LO_STEP); + return; } bug("fib_delete() called for invalid node"); } @@ -469,16 +461,18 @@ fit_init(struct fib_iterator *i, struct fib *f) struct fib_node *n; i->efef = 0xff; - for(h=0; hhash_size; h++) - if (n = f->hash_table[h]) - { - i->prev = (struct fib_iterator *) n; - if (i->next = n->readers) - i->next->prev = i; - n->readers = i; - i->node = n; - return; - } + if (i->flags & FIF_ORDERED) + { + if (n = REDBLACK_FIRST(struct fib_node, rb, f->tree_root)) + return fit_put(i, n); + } + else + { + for(h=0; hhash_size; h++) + if (n = f->hash_table[h]) + return fit_put(i, n); + } + /* The fib is empty, nothing to do */ i->prev = i->next = NULL; i->node = NULL; @@ -496,14 +490,8 @@ fit_get(struct fib *f, struct fib_iterator *i) i->hash = ~0 - 1; return NULL; } - if (!(n = i->node)) - { - /* No node info available, we are a victim of merging. Try harder. */ - j = i; - while (j->efef == 0xff) - j = j->prev; - n = (struct fib_node *) j; - } + ASSERT(i->node); + n = i->node; j = i->prev; if (k = i->next) k->prev = j; @@ -528,12 +516,20 @@ fit_put(struct fib_iterator *i, struct fib_node *n) void fit_put_next(struct fib *f, struct fib_iterator *i, struct fib_node *n, uint hpos) { - if (n = n->next) - goto found; + if (i->flags & FIF_ORDERED) + { + if (n = REDBLACK_NEXT(struct fib_node, rb, n)) + goto found; + } + else + { + if (n = n->next) + goto found; - while (++hpos < f->hash_size) - if (n = f->hash_table[hpos]) - goto found; + while (++hpos < f->hash_size) + if (n = f->hash_table[hpos]) + goto found; + } /* We are at the end */ i->prev = i->next = NULL; @@ -576,8 +572,6 @@ fib_check(struct fib *f) bug("fib_check: iterator->prev mismatch"); j0 = j; if (!j->node) - nulls++; - else if (nulls) bug("fib_check: iterator nullified"); else if (j->node != n) bug("fib_check: iterator->node mismatch"); diff --git a/nest/rt-table.c b/nest/rt-table.c index 5a0ef9143..3db7d8a77 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -101,6 +101,7 @@ net_route_ip6_sadr(rtable *t, net_addr_ip6_sadr *n) match with the smallest matching src prefix. */ for (fn = fib_get_chain(&t->fib, (net_addr *) n); fn; fn = fn->next) { + /* TODO: This may be more effectively done by searching in the redblack tree. */ net_addr_ip6_sadr *a = (void *) fn->addr; if (net_equal_dst_ip6_sadr(n, a) &&