]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Nest: Using the brand-new redblack tree to provide an ordered route listing
authorJan Maria Matejka <mq@ucw.cz>
Mon, 3 Dec 2018 09:31:59 +0000 (10:31 +0100)
committerJan Maria Matejka <mq@ucw.cz>
Mon, 3 Dec 2018 09:31:59 +0000 (10:31 +0100)
nest/config.Y
nest/route.h
nest/rt-fib.c
nest/rt-table.c

index 76de67f97e3e5e4c3e4ad19a402ff6a9eb58da26..20a62b7f0a6e970f98c62c1d7a60a9a1fa86685b 100644 (file)
@@ -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");
index 299cbac22b4e5dd73613d9cf13152bc0ac451666..61b30f6e9a847670e4df1cf1ab24f1316135f0f2 100644 (file)
@@ -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_)
 
index 18ccbfc397096b10c3183b1617eb6d0772370b16..e136035751567570ba70b9509c1c331aa29638d6 100644 (file)
@@ -2,6 +2,7 @@
  *     BIRD -- Forwarding Information Base -- Data Structures
  *
  *     (c) 1998--2000 Martin Mares <mj@ucw.cz>
+ *     (c) 2018 Maria Matejka <mq@jmq.cz>
  *
  *     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; h<f->hash_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; h<f->hash_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");
index 5a0ef91434db7c74178d73cf8eec4f7c0901a390..3db7d8a775ff20864b24c997cb862a2266123728 100644 (file)
@@ -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) &&