Merge branch 'master' into for-2.6.35
[safe/jmp/linux-2.6] / net / ipv4 / fib_trie.c
index 1a9231f..c98f115 100644 (file)
@@ -22,8 +22,6 @@
  * IP-address lookup using LC-tries. Stefan Nilsson and Gunnar Karlsson
  * IEEE Journal on Selected Areas in Communications, 17(6):1083-1092, June 1999
  *
- * Version:    $Id: fib_trie.c,v 1.3 2005/06/08 14:20:01 robert Exp $
- *
  *
  * Code from fib_hash has been reused which includes the following header:
  *
@@ -50,7 +48,7 @@
  *             Patrick McHardy <kaber@trash.net>
  */
 
-#define VERSION "0.408"
+#define VERSION "0.409"
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -73,6 +71,7 @@
 #include <linux/netlink.h>
 #include <linux/init.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <net/net_namespace.h>
 #include <net/ip.h>
 #include <net/protocol.h>
@@ -122,7 +121,11 @@ struct tnode {
        unsigned char bits;             /* 2log(KEYLENGTH) bits needed */
        unsigned int full_children;     /* KEYLENGTH bits needed */
        unsigned int empty_children;    /* KEYLENGTH bits needed */
-       struct rcu_head rcu;
+       union {
+               struct rcu_head rcu;
+               struct work_struct work;
+               struct tnode *tnode_free;
+       };
        struct node *child[0];
 };
 
@@ -160,7 +163,16 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct node *n,
 static struct node *resize(struct trie *t, struct tnode *tn);
 static struct tnode *inflate(struct trie *t, struct tnode *tn);
 static struct tnode *halve(struct trie *t, struct tnode *tn);
-static void tnode_free(struct tnode *tn);
+/* tnodes to free after resize(); protected by RTNL */
+static struct tnode *tnode_free_head;
+static size_t tnode_free_size;
+
+/*
+ * synchronize_rcu after call_rcu for that many pages; it should be especially
+ * useful before resizing the root node with PREEMPT_NONE configs; the value was
+ * obtained experimentally, aiming to avoid visible slowdown.
+ */
+static const int sync_pages = 128;
 
 static struct kmem_cache *fn_alias_kmem __read_mostly;
 static struct kmem_cache *trie_leaf_kmem __read_mostly;
@@ -177,10 +189,13 @@ static inline struct tnode *node_parent_rcu(struct node *node)
        return rcu_dereference(ret);
 }
 
+/* Same as rcu_assign_pointer
+ * but that macro() assumes that value is a pointer.
+ */
 static inline void node_set_parent(struct node *node, struct tnode *ptr)
 {
-       rcu_assign_pointer(node->parent,
-                          (unsigned long)ptr | NODE_TYPE(node));
+       smp_wmb();
+       node->parent = (unsigned long)ptr | NODE_TYPE(node);
 }
 
 static inline struct node *tnode_get_child(struct tnode *tn, unsigned int i)
@@ -194,7 +209,9 @@ static inline struct node *tnode_get_child_rcu(struct tnode *tn, unsigned int i)
 {
        struct node *ret = tnode_get_child(tn, i);
 
-       return rcu_dereference(ret);
+       return rcu_dereference_check(ret,
+                                    rcu_read_lock_held() ||
+                                    lockdep_rtnl_is_held());
 }
 
 static inline int tnode_child_length(const struct tnode *tn)
@@ -310,9 +327,8 @@ static inline void check_tnode(const struct tnode *tn)
 
 static const int halve_threshold = 25;
 static const int inflate_threshold = 50;
-static const int halve_threshold_root = 8;
-static const int inflate_threshold_root = 15;
-
+static const int halve_threshold_root = 15;
+static const int inflate_threshold_root = 30;
 
 static void __alias_free_mem(struct rcu_head *head)
 {
@@ -331,6 +347,11 @@ static void __leaf_free_rcu(struct rcu_head *head)
        kmem_cache_free(trie_leaf_kmem, l);
 }
 
+static inline void free_leaf(struct leaf *l)
+{
+       call_rcu_bh(&l->rcu, __leaf_free_rcu);
+}
+
 static void __leaf_info_free_rcu(struct rcu_head *head)
 {
        kfree(container_of(head, struct leaf_info, rcu));
@@ -343,16 +364,16 @@ static inline void free_leaf_info(struct leaf_info *leaf)
 
 static struct tnode *tnode_alloc(size_t size)
 {
-       struct page *pages;
-
        if (size <= PAGE_SIZE)
                return kzalloc(size, GFP_KERNEL);
+       else
+               return __vmalloc(size, GFP_KERNEL | __GFP_ZERO, PAGE_KERNEL);
+}
 
-       pages = alloc_pages(GFP_KERNEL|__GFP_ZERO, get_order(size));
-       if (!pages)
-               return NULL;
-
-       return page_address(pages);
+static void __tnode_vfree(struct work_struct *arg)
+{
+       struct tnode *tn = container_of(arg, struct tnode, work);
+       vfree(tn);
 }
 
 static void __tnode_free_rcu(struct rcu_head *head)
@@ -363,19 +384,45 @@ static void __tnode_free_rcu(struct rcu_head *head)
 
        if (size <= PAGE_SIZE)
                kfree(tn);
-       else
-               free_pages((unsigned long)tn, get_order(size));
+       else {
+               INIT_WORK(&tn->work, __tnode_vfree);
+               schedule_work(&tn->work);
+       }
 }
 
 static inline void tnode_free(struct tnode *tn)
 {
-       if (IS_LEAF(tn)) {
-               struct leaf *l = (struct leaf *) tn;
-               call_rcu_bh(&l->rcu, __leaf_free_rcu);
-       } else
+       if (IS_LEAF(tn))
+               free_leaf((struct leaf *) tn);
+       else
                call_rcu(&tn->rcu, __tnode_free_rcu);
 }
 
+static void tnode_free_safe(struct tnode *tn)
+{
+       BUG_ON(IS_LEAF(tn));
+       tn->tnode_free = tnode_free_head;
+       tnode_free_head = tn;
+       tnode_free_size += sizeof(struct tnode) +
+                          (sizeof(struct node *) << tn->bits);
+}
+
+static void tnode_free_flush(void)
+{
+       struct tnode *tn;
+
+       while ((tn = tnode_free_head)) {
+               tnode_free_head = tn->tnode_free;
+               tn->tnode_free = NULL;
+               tnode_free(tn);
+       }
+
+       if (tnode_free_size >= PAGE_SIZE * sync_pages) {
+               tnode_free_size = 0;
+               synchronize_rcu();
+       }
+}
+
 static struct leaf *leaf_new(void)
 {
        struct leaf *l = kmem_cache_alloc(trie_leaf_kmem, GFP_KERNEL);
@@ -447,7 +494,6 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct node *n,
 
        BUG_ON(i >= 1<<tn->bits);
 
-
        /* update emptyChildren */
        if (n == NULL && chi != NULL)
                tn->empty_children++;
@@ -470,14 +516,14 @@ static void tnode_put_child_reorg(struct tnode *tn, int i, struct node *n,
        rcu_assign_pointer(tn->child[i], n);
 }
 
+#define MAX_WORK 10
 static struct node *resize(struct trie *t, struct tnode *tn)
 {
        int i;
-       int err = 0;
        struct tnode *old_tn;
        int inflate_threshold_use;
        int halve_threshold_use;
-       int max_resize;
+       int max_work;
 
        if (!tn)
                return NULL;
@@ -487,23 +533,12 @@ static struct node *resize(struct trie *t, struct tnode *tn)
 
        /* No children */
        if (tn->empty_children == tnode_child_length(tn)) {
-               tnode_free(tn);
+               tnode_free_safe(tn);
                return NULL;
        }
        /* One child */
        if (tn->empty_children == tnode_child_length(tn) - 1)
-               for (i = 0; i < tnode_child_length(tn); i++) {
-                       struct node *n;
-
-                       n = tn->child[i];
-                       if (!n)
-                               continue;
-
-                       /* compress one level */
-                       node_set_parent(n, NULL);
-                       tnode_free(tn);
-                       return n;
-               }
+               goto one_child;
        /*
         * Double as long as the resulting node has a number of
         * nonempty nodes that are above the threshold.
@@ -572,14 +607,17 @@ static struct node *resize(struct trie *t, struct tnode *tn)
 
        /* Keep root node larger  */
 
-       if (!tn->parent)
+       if (!node_parent((struct node*) tn)) {
                inflate_threshold_use = inflate_threshold_root;
-       else
+               halve_threshold_use = halve_threshold_root;
+       }
+       else {
                inflate_threshold_use = inflate_threshold;
+               halve_threshold_use = halve_threshold;
+       }
 
-       err = 0;
-       max_resize = 10;
-       while ((tn->full_children > 0 &&  max_resize-- &&
+       max_work = MAX_WORK;
+       while ((tn->full_children > 0 &&  max_work-- &&
                50 * (tn->full_children + tnode_child_length(tn)
                      - tn->empty_children)
                >= inflate_threshold_use * tnode_child_length(tn))) {
@@ -596,35 +634,19 @@ static struct node *resize(struct trie *t, struct tnode *tn)
                }
        }
 
-       if (max_resize < 0) {
-               if (!tn->parent)
-                       pr_warning("Fix inflate_threshold_root."
-                                  " Now=%d size=%d bits\n",
-                                  inflate_threshold_root, tn->bits);
-               else
-                       pr_warning("Fix inflate_threshold."
-                                  " Now=%d size=%d bits\n",
-                                  inflate_threshold, tn->bits);
-       }
-
        check_tnode(tn);
 
+       /* Return if at least one inflate is run */
+       if( max_work != MAX_WORK)
+               return (struct node *) tn;
+
        /*
         * Halve as long as the number of empty children in this
         * node is above threshold.
         */
 
-
-       /* Keep root node larger  */
-
-       if (!tn->parent)
-               halve_threshold_use = halve_threshold_root;
-       else
-               halve_threshold_use = halve_threshold;
-
-       err = 0;
-       max_resize = 10;
-       while (tn->bits > 1 &&  max_resize-- &&
+       max_work = MAX_WORK;
+       while (tn->bits > 1 &&  max_work-- &&
               100 * (tnode_child_length(tn) - tn->empty_children) <
               halve_threshold_use * tnode_child_length(tn)) {
 
@@ -639,19 +661,10 @@ static struct node *resize(struct trie *t, struct tnode *tn)
                }
        }
 
-       if (max_resize < 0) {
-               if (!tn->parent)
-                       pr_warning("Fix halve_threshold_root."
-                                  " Now=%d size=%d bits\n",
-                                  halve_threshold_root, tn->bits);
-               else
-                       pr_warning("Fix halve_threshold."
-                                  " Now=%d size=%d bits\n",
-                                  halve_threshold, tn->bits);
-       }
 
        /* Only one child remains */
-       if (tn->empty_children == tnode_child_length(tn) - 1)
+       if (tn->empty_children == tnode_child_length(tn) - 1) {
+one_child:
                for (i = 0; i < tnode_child_length(tn); i++) {
                        struct node *n;
 
@@ -662,10 +675,10 @@ static struct node *resize(struct trie *t, struct tnode *tn)
                        /* compress one level */
 
                        node_set_parent(n, NULL);
-                       tnode_free(tn);
+                       tnode_free_safe(tn);
                        return n;
                }
-
+       }
        return (struct node *) tn;
 }
 
@@ -748,7 +761,7 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn)
                        put_child(t, tn, 2*i, inode->child[0]);
                        put_child(t, tn, 2*i+1, inode->child[1]);
 
-                       tnode_free(inode);
+                       tnode_free_safe(inode);
                        continue;
                }
 
@@ -793,9 +806,9 @@ static struct tnode *inflate(struct trie *t, struct tnode *tn)
                put_child(t, tn, 2*i, resize(t, left));
                put_child(t, tn, 2*i+1, resize(t, right));
 
-               tnode_free(inode);
+               tnode_free_safe(inode);
        }
-       tnode_free(oldtnode);
+       tnode_free_safe(oldtnode);
        return tn;
 nomem:
        {
@@ -877,7 +890,7 @@ static struct tnode *halve(struct trie *t, struct tnode *tn)
                put_child(t, newBinNode, 1, right);
                put_child(t, tn, i/2, resize(t, newBinNode));
        }
-       tnode_free(oldtnode);
+       tnode_free_safe(oldtnode);
        return tn;
 nomem:
        {
@@ -951,7 +964,9 @@ fib_find_node(struct trie *t, u32 key)
        struct node *n;
 
        pos = 0;
-       n = rcu_dereference(t->trie);
+       n = rcu_dereference_check(t->trie,
+                                 rcu_read_lock_held() ||
+                                 lockdep_rtnl_is_held());
 
        while (n != NULL &&  NODE_TYPE(n) == T_TNODE) {
                tn = (struct tnode *) n;
@@ -975,12 +990,14 @@ fib_find_node(struct trie *t, u32 key)
        return NULL;
 }
 
-static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
+static void trie_rebalance(struct trie *t, struct tnode *tn)
 {
        int wasfull;
-       t_key cindex, key = tn->key;
+       t_key cindex, key;
        struct tnode *tp;
 
+       key = tn->key;
+
        while (tn != NULL && (tp = node_parent((struct node *)tn)) != NULL) {
                cindex = tkey_extract_bits(key, tp->pos, tp->bits);
                wasfull = tnode_full(tp, tnode_get_child(tp, cindex));
@@ -991,6 +1008,10 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
 
                tp = node_parent((struct node *) tn);
                if (!tp)
+                       rcu_assign_pointer(t->trie, (struct node *)tn);
+
+               tnode_free_flush();
+               if (!tp)
                        break;
                tn = tp;
        }
@@ -999,7 +1020,10 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
        if (IS_TNODE(tn))
                tn = (struct tnode *)resize(t, (struct tnode *)tn);
 
-       return (struct node *)tn;
+       rcu_assign_pointer(t->trie, (struct node *)tn);
+       tnode_free_flush();
+
+       return;
 }
 
 /* only used from updater-side */
@@ -1084,7 +1108,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
        li = leaf_info_new(plen);
 
        if (!li) {
-               tnode_free((struct tnode *) l);
+               free_leaf(l);
                return NULL;
        }
 
@@ -1120,7 +1144,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
 
                if (!tn) {
                        free_leaf_info(li);
-                       tnode_free((struct tnode *) l);
+                       free_leaf(l);
                        return NULL;
                }
 
@@ -1147,7 +1171,7 @@ static struct list_head *fib_insert_node(struct trie *t, u32 key, int plen)
 
        /* Rebalance the trie */
 
-       rcu_assign_pointer(t->trie, trie_rebalance(t, tp));
+       trie_rebalance(t, tp);
 done:
        return fa_head;
 }
@@ -1155,7 +1179,7 @@ done:
 /*
  * Caller must hold RTNL.
  */
-static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        struct fib_alias *fa, *new_fa;
@@ -1206,20 +1230,45 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
         * and we need to allocate a new one of those as well.
         */
 
-       if (fa && fa->fa_info->fib_priority == fi->fib_priority) {
-               struct fib_alias *fa_orig;
+       if (fa && fa->fa_tos == tos &&
+           fa->fa_info->fib_priority == fi->fib_priority) {
+               struct fib_alias *fa_first, *fa_match;
 
                err = -EEXIST;
                if (cfg->fc_nlflags & NLM_F_EXCL)
                        goto out;
 
+               /* We have 2 goals:
+                * 1. Find exact match for type, scope, fib_info to avoid
+                * duplicate routes
+                * 2. Find next 'fa' (or head), NLM_F_APPEND inserts before it
+                */
+               fa_match = NULL;
+               fa_first = fa;
+               fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
+               list_for_each_entry_continue(fa, fa_head, fa_list) {
+                       if (fa->fa_tos != tos)
+                               break;
+                       if (fa->fa_info->fib_priority != fi->fib_priority)
+                               break;
+                       if (fa->fa_type == cfg->fc_type &&
+                           fa->fa_scope == cfg->fc_scope &&
+                           fa->fa_info == fi) {
+                               fa_match = fa;
+                               break;
+                       }
+               }
+
                if (cfg->fc_nlflags & NLM_F_REPLACE) {
                        struct fib_info *fi_drop;
                        u8 state;
 
-                       if (fi->fib_treeref > 1)
+                       fa = fa_first;
+                       if (fa_match) {
+                               if (fa == fa_match)
+                                       err = 0;
                                goto out;
-
+                       }
                        err = -ENOBUFS;
                        new_fa = kmem_cache_alloc(fn_alias_kmem, GFP_KERNEL);
                        if (new_fa == NULL)
@@ -1231,14 +1280,14 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
                        new_fa->fa_type = cfg->fc_type;
                        new_fa->fa_scope = cfg->fc_scope;
                        state = fa->fa_state;
-                       new_fa->fa_state &= ~FA_S_ACCESSED;
+                       new_fa->fa_state = state & ~FA_S_ACCESSED;
 
                        list_replace_rcu(&fa->fa_list, &new_fa->fa_list);
                        alias_free_mem_rcu(fa);
 
                        fib_release_info(fi_drop);
                        if (state & FA_S_ACCESSED)
-                               rt_cache_flush(-1);
+                               rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
                        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
                                tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
 
@@ -1248,20 +1297,11 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
                 * uses the same scope, type, and nexthop
                 * information.
                 */
-               fa_orig = fa;
-               list_for_each_entry(fa, fa_orig->fa_list.prev, fa_list) {
-                       if (fa->fa_tos != tos)
-                               break;
-                       if (fa->fa_info->fib_priority != fi->fib_priority)
-                               break;
-                       if (fa->fa_type == cfg->fc_type &&
-                           fa->fa_scope == cfg->fc_scope &&
-                           fa->fa_info == fi)
-                               goto out;
-               }
+               if (fa_match)
+                       goto out;
 
                if (!(cfg->fc_nlflags & NLM_F_APPEND))
-                       fa = fa_orig;
+                       fa = fa_first;
        }
        err = -ENOENT;
        if (!(cfg->fc_nlflags & NLM_F_CREATE))
@@ -1292,7 +1332,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
        list_add_tail_rcu(&new_fa->fa_list,
                          (fa ? &fa->fa_list : fa_head));
 
-       rt_cache_flush(-1);
+       rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
        rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
                  &cfg->fc_nlinfo, 0);
 succeeded:
@@ -1306,7 +1346,6 @@ err:
        return err;
 }
 
-
 /* should be called with rcu_read_lock */
 static int check_leaf(struct trie *t, struct leaf *l,
                      t_key key,  const struct flowi *flp,
@@ -1324,8 +1363,7 @@ static int check_leaf(struct trie *t, struct leaf *l,
                if (l->key != (key & ntohl(mask)))
                        continue;
 
-               err = fib_semantic_match(&li->falh, flp, res,
-                                        htonl(l->key), mask, plen);
+               err = fib_semantic_match(&li->falh, flp, res, plen);
 
 #ifdef CONFIG_IP_FIB_TRIE_STATS
                if (err <= 0)
@@ -1334,17 +1372,17 @@ static int check_leaf(struct trie *t, struct leaf *l,
                        t->stats.semantic_match_miss++;
 #endif
                if (err <= 0)
-                       return plen;
+                       return err;
        }
 
-       return -1;
+       return 1;
 }
 
-static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp,
-                         struct fib_result *res)
+int fib_table_lookup(struct fib_table *tb, const struct flowi *flp,
+                    struct fib_result *res)
 {
        struct trie *t = (struct trie *) tb->tb_data;
-       int plen, ret = 0;
+       int ret;
        struct node *n;
        struct tnode *pn;
        int pos, bits;
@@ -1368,10 +1406,7 @@ static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp,
 
        /* Just a leaf? */
        if (IS_LEAF(n)) {
-               plen = check_leaf(t, (struct leaf *)n, key, flp, res);
-               if (plen < 0)
-                       goto failed;
-               ret = 0;
+               ret = check_leaf(t, (struct leaf *)n, key, flp, res);
                goto found;
        }
 
@@ -1386,7 +1421,7 @@ static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp,
                        cindex = tkey_extract_bits(mask_pfx(key, current_prefix_length),
                                                   pos, bits);
 
-               n = tnode_get_child(pn, cindex);
+               n = tnode_get_child_rcu(pn, cindex);
 
                if (n == NULL) {
 #ifdef CONFIG_IP_FIB_TRIE_STATS
@@ -1396,11 +1431,9 @@ static int fn_trie_lookup(struct fib_table *tb, const struct flowi *flp,
                }
 
                if (IS_LEAF(n)) {
-                       plen = check_leaf(t, (struct leaf *)n, key, flp, res);
-                       if (plen < 0)
+                       ret = check_leaf(t, (struct leaf *)n, key, flp, res);
+                       if (ret > 0)
                                goto backtrace;
-
-                       ret = 0;
                        goto found;
                }
 
@@ -1523,7 +1556,7 @@ backtrace:
                if (chopped_off <= pn->bits) {
                        cindex &= ~(1 << (chopped_off-1));
                } else {
-                       struct tnode *parent = node_parent((struct node *) pn);
+                       struct tnode *parent = node_parent_rcu((struct node *) pn);
                        if (!parent)
                                goto failed;
 
@@ -1545,55 +1578,29 @@ found:
        return ret;
 }
 
-/* only called from updater side */
-static int trie_leaf_remove(struct trie *t, t_key key)
+/*
+ * Remove the leaf and return parent.
+ */
+static void trie_leaf_remove(struct trie *t, struct leaf *l)
 {
-       t_key cindex;
-       struct tnode *tp = NULL;
-       struct node *n = t->trie;
-       struct leaf *l;
+       struct tnode *tp = node_parent((struct node *) l);
 
-       pr_debug("entering trie_leaf_remove(%p)\n", n);
-
-       /* Note that in the case skipped bits, those bits are *not* checked!
-        * When we finish this, we will have NULL or a T_LEAF, and the
-        * T_LEAF may or may not match our key.
-        */
-
-       while (n != NULL && IS_TNODE(n)) {
-               struct tnode *tn = (struct tnode *) n;
-               check_tnode(tn);
-               n = tnode_get_child(tn, tkey_extract_bits(key,
-                                                         tn->pos, tn->bits));
-
-               BUG_ON(n && node_parent(n) != tn);
-       }
-       l = (struct leaf *) n;
-
-       if (!n || !tkey_equals(l->key, key))
-               return 0;
-
-       /*
-        * Key found.
-        * Remove the leaf and rebalance the tree
-        */
-       tp = node_parent(n);
-       tnode_free((struct tnode *) n);
+       pr_debug("entering trie_leaf_remove(%p)\n", l);
 
        if (tp) {
-               cindex = tkey_extract_bits(key, tp->pos, tp->bits);
+               t_key cindex = tkey_extract_bits(l->key, tp->pos, tp->bits);
                put_child(t, (struct tnode *)tp, cindex, NULL);
-               rcu_assign_pointer(t->trie, trie_rebalance(t, tp));
+               trie_rebalance(t, tp);
        } else
                rcu_assign_pointer(t->trie, NULL);
 
-       return 1;
+       free_leaf(l);
 }
 
 /*
  * Caller must hold RTNL.
  */
-static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
+int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        u32 key, mask;
@@ -1628,9 +1635,8 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
        pr_debug("Deleting %08x/%d tos=%d t=%p\n", key, plen, tos, t);
 
        fa_to_delete = NULL;
-       fa_head = fa->fa_list.prev;
-
-       list_for_each_entry(fa, fa_head, fa_list) {
+       fa = list_entry(fa->fa_list.prev, struct fib_alias, fa_list);
+       list_for_each_entry_continue(fa, fa_head, fa_list) {
                struct fib_info *fi = fa->fa_info;
 
                if (fa->fa_tos != tos)
@@ -1665,17 +1671,17 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
        }
 
        if (hlist_empty(&l->list))
-               trie_leaf_remove(t, key);
+               trie_leaf_remove(t, l);
 
        if (fa->fa_state & FA_S_ACCESSED)
-               rt_cache_flush(-1);
+               rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
 
        fib_release_info(fa->fa_info);
        alias_free_mem_rcu(fa);
        return 0;
 }
 
-static int trie_flush_list(struct trie *t, struct list_head *head)
+static int trie_flush_list(struct list_head *head)
 {
        struct fib_alias *fa, *fa_node;
        int found = 0;
@@ -1693,7 +1699,7 @@ static int trie_flush_list(struct trie *t, struct list_head *head)
        return found;
 }
 
-static int trie_flush_leaf(struct trie *t, struct leaf *l)
+static int trie_flush_leaf(struct leaf *l)
 {
        int found = 0;
        struct hlist_head *lih = &l->list;
@@ -1701,7 +1707,7 @@ static int trie_flush_leaf(struct trie *t, struct leaf *l)
        struct leaf_info *li = NULL;
 
        hlist_for_each_entry_safe(li, node, tmp, lih, hlist) {
-               found += trie_flush_list(t, &li->falh);
+               found += trie_flush_list(&li->falh);
 
                if (list_empty(&li->falh)) {
                        hlist_del_rcu(&li->hlist);
@@ -1711,93 +1717,104 @@ static int trie_flush_leaf(struct trie *t, struct leaf *l)
        return found;
 }
 
-/* rcu_read_lock needs to be hold by caller from readside */
-
-static struct leaf *nextleaf(struct trie *t, struct leaf *thisleaf)
+/*
+ * Scan for the next right leaf starting at node p->child[idx]
+ * Since we have back pointer, no recursion necessary.
+ */
+static struct leaf *leaf_walk_rcu(struct tnode *p, struct node *c)
 {
-       struct node *c = (struct node *) thisleaf;
-       struct tnode *p;
-       int idx;
-       struct node *trie = rcu_dereference(t->trie);
-
-       if (c == NULL) {
-               if (trie == NULL)
-                       return NULL;
-
-               if (IS_LEAF(trie))          /* trie w. just a leaf */
-                       return (struct leaf *) trie;
-
-               p = (struct tnode *)trie;  /* Start */
-       } else
-               p = node_parent_rcu(c);
-
-       while (p) {
-               int pos, last;
+       do {
+               t_key idx;
 
-               /*  Find the next child of the parent */
                if (c)
-                       pos = 1 + tkey_extract_bits(c->key, p->pos, p->bits);
+                       idx = tkey_extract_bits(c->key, p->pos, p->bits) + 1;
                else
-                       pos = 0;
-
-               last = 1 << p->bits;
-               for (idx = pos; idx < last ; idx++) {
-                       c = rcu_dereference(p->child[idx]);
+                       idx = 0;
 
+               while (idx < 1u << p->bits) {
+                       c = tnode_get_child_rcu(p, idx++);
                        if (!c)
                                continue;
 
-                       /* Decend if tnode */
-                       while (IS_TNODE(c)) {
-                               p = (struct tnode *) c;
-                               idx = 0;
-
-                               /* Rightmost non-NULL branch */
-                               if (p && IS_TNODE(p))
-                                       while (!(c = rcu_dereference(p->child[idx]))
-                                              && idx < (1<<p->bits)) idx++;
-
-                               /* Done with this tnode? */
-                               if (idx >= (1 << p->bits) || !c)
-                                       goto up;
+                       if (IS_LEAF(c)) {
+                               prefetch(p->child[idx]);
+                               return (struct leaf *) c;
                        }
-                       return (struct leaf *) c;
+
+                       /* Rescan start scanning in new node */
+                       p = (struct tnode *) c;
+                       idx = 0;
                }
-up:
-               /* No more children go up one step  */
+
+               /* Node empty, walk back up to parent */
                c = (struct node *) p;
-               p = node_parent_rcu(c);
-       }
-       return NULL; /* Ready. Root of trie */
+       } while ( (p = node_parent_rcu(c)) != NULL);
+
+       return NULL; /* Root of trie */
+}
+
+static struct leaf *trie_firstleaf(struct trie *t)
+{
+       struct tnode *n = (struct tnode *) rcu_dereference(t->trie);
+
+       if (!n)
+               return NULL;
+
+       if (IS_LEAF(n))          /* trie is just a leaf */
+               return (struct leaf *) n;
+
+       return leaf_walk_rcu(n, NULL);
+}
+
+static struct leaf *trie_nextleaf(struct leaf *l)
+{
+       struct node *c = (struct node *) l;
+       struct tnode *p = node_parent_rcu(c);
+
+       if (!p)
+               return NULL;    /* trie with just one leaf */
+
+       return leaf_walk_rcu(p, c);
+}
+
+static struct leaf *trie_leafindex(struct trie *t, int index)
+{
+       struct leaf *l = trie_firstleaf(t);
+
+       while (l && index-- > 0)
+               l = trie_nextleaf(l);
+
+       return l;
 }
 
+
 /*
  * Caller must hold RTNL.
  */
-static int fn_trie_flush(struct fib_table *tb)
+int fib_table_flush(struct fib_table *tb)
 {
        struct trie *t = (struct trie *) tb->tb_data;
-       struct leaf *ll = NULL, *l = NULL;
-       int found = 0, h;
+       struct leaf *l, *ll = NULL;
+       int found = 0;
 
-       for (h = 0; (l = nextleaf(t, l)) != NULL; h++) {
-               found += trie_flush_leaf(t, l);
+       for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
+               found += trie_flush_leaf(l);
 
                if (ll && hlist_empty(&ll->list))
-                       trie_leaf_remove(t, ll->key);
+                       trie_leaf_remove(t, ll);
                ll = l;
        }
 
        if (ll && hlist_empty(&ll->list))
-               trie_leaf_remove(t, ll->key);
+               trie_leaf_remove(t, ll);
 
        pr_debug("trie_flush found=%d\n", found);
        return found;
 }
 
-static void fn_trie_select_default(struct fib_table *tb,
-                                  const struct flowi *flp,
-                                  struct fib_result *res)
+void fib_table_select_default(struct fib_table *tb,
+                             const struct flowi *flp,
+                             struct fib_result *res)
 {
        struct trie *t = (struct trie *) tb->tb_data;
        int order, last_idx;
@@ -1874,10 +1891,9 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
 {
        int i, s_i;
        struct fib_alias *fa;
-
        __be32 xkey = htonl(key);
 
-       s_i = cb->args[4];
+       s_i = cb->args[5];
        i = 0;
 
        /* rcu_read_lock is hold by caller */
@@ -1887,7 +1903,6 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
                        i++;
                        continue;
                }
-               BUG_ON(!fa->fa_info);
 
                if (fib_dump_info(skb, NETLINK_CB(cb->skb).pid,
                                  cb->nlh->nlmsg_seq,
@@ -1898,76 +1913,90 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
                                  xkey,
                                  plen,
                                  fa->fa_tos,
-                                 fa->fa_info, 0) < 0) {
-                       cb->args[4] = i;
+                                 fa->fa_info, NLM_F_MULTI) < 0) {
+                       cb->args[5] = i;
                        return -1;
                }
                i++;
        }
-       cb->args[4] = i;
+       cb->args[5] = i;
        return skb->len;
 }
 
-static int fn_trie_dump_plen(struct trie *t, int plen, struct fib_table *tb,
-                            struct sk_buff *skb, struct netlink_callback *cb)
+static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
+                       struct sk_buff *skb, struct netlink_callback *cb)
 {
-       int h, s_h;
-       struct list_head *fa_head;
-       struct leaf *l = NULL;
+       struct leaf_info *li;
+       struct hlist_node *node;
+       int i, s_i;
 
-       s_h = cb->args[3];
+       s_i = cb->args[4];
+       i = 0;
 
-       for (h = 0; (l = nextleaf(t, l)) != NULL; h++) {
-               if (h < s_h)
+       /* rcu_read_lock is hold by caller */
+       hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
+               if (i < s_i) {
+                       i++;
                        continue;
-               if (h > s_h)
-                       memset(&cb->args[4], 0,
-                              sizeof(cb->args) - 4*sizeof(cb->args[0]));
-
-               fa_head = get_fa_head(l, plen);
+               }
 
-               if (!fa_head)
-                       continue;
+               if (i > s_i)
+                       cb->args[5] = 0;
 
-               if (list_empty(fa_head))
+               if (list_empty(&li->falh))
                        continue;
 
-               if (fn_trie_dump_fa(l->key, plen, fa_head, tb, skb, cb) < 0) {
-                       cb->args[3] = h;
+               if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) {
+                       cb->args[4] = i;
                        return -1;
                }
+               i++;
        }
-       cb->args[3] = h;
+
+       cb->args[4] = i;
        return skb->len;
 }
 
-static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb,
-                       struct netlink_callback *cb)
+int fib_table_dump(struct fib_table *tb, struct sk_buff *skb,
+                  struct netlink_callback *cb)
 {
-       int m, s_m;
+       struct leaf *l;
        struct trie *t = (struct trie *) tb->tb_data;
-
-       s_m = cb->args[2];
+       t_key key = cb->args[2];
+       int count = cb->args[3];
 
        rcu_read_lock();
-       for (m = 0; m <= 32; m++) {
-               if (m < s_m)
-                       continue;
-               if (m > s_m)
-                       memset(&cb->args[3], 0,
-                               sizeof(cb->args) - 3*sizeof(cb->args[0]));
+       /* Dump starting at last key.
+        * Note: 0.0.0.0/0 (ie default) is first key.
+        */
+       if (count == 0)
+               l = trie_firstleaf(t);
+       else {
+               /* Normally, continue from last key, but if that is missing
+                * fallback to using slow rescan
+                */
+               l = fib_find_node(t, key);
+               if (!l)
+                       l = trie_leafindex(t, count);
+       }
 
-               if (fn_trie_dump_plen(t, 32-m, tb, skb, cb) < 0) {
-                       cb->args[2] = m;
-                       goto out;
+       while (l) {
+               cb->args[2] = l->key;
+               if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) {
+                       cb->args[3] = count;
+                       rcu_read_unlock();
+                       return -1;
                }
+
+               ++count;
+               l = trie_nextleaf(l);
+               memset(&cb->args[4], 0,
+                      sizeof(cb->args) - 4*sizeof(cb->args[0]));
        }
+       cb->args[3] = count;
        rcu_read_unlock();
-       cb->args[2] = m;
+
        return skb->len;
-out:
-       rcu_read_unlock();
-       return -1;
 }
 
 void __init fib_hash_init(void)
@@ -1996,12 +2025,6 @@ struct fib_table *fib_hash_table(u32 id)
 
        tb->tb_id = id;
        tb->tb_default = -1;
-       tb->tb_lookup = fn_trie_lookup;
-       tb->tb_insert = fn_trie_insert;
-       tb->tb_delete = fn_trie_delete;
-       tb->tb_flush = fn_trie_flush;
-       tb->tb_select_default = fn_trie_select_default;
-       tb->tb_dump = fn_trie_dump;
 
        t = (struct trie *) tb->tb_data;
        memset(t, 0, sizeof(*t));
@@ -2016,9 +2039,8 @@ struct fib_table *fib_hash_table(u32 id)
 /* Depth first Trie walk iterator */
 struct fib_trie_iter {
        struct seq_net_private p;
-       struct trie *trie_local, *trie_main;
+       struct fib_table *tb;
        struct tnode *tnode;
-       struct trie *trie;
        unsigned index;
        unsigned depth;
 };
@@ -2071,31 +2093,26 @@ rescan:
 static struct node *fib_trie_get_first(struct fib_trie_iter *iter,
                                       struct trie *t)
 {
-       struct node *n ;
+       struct node *n;
 
        if (!t)
                return NULL;
 
        n = rcu_dereference(t->trie);
-
-       if (!iter)
+       if (!n)
                return NULL;
 
-       if (n) {
-               if (IS_TNODE(n)) {
-                       iter->tnode = (struct tnode *) n;
-                       iter->trie = t;
-                       iter->index = 0;
-                       iter->depth = 1;
-               } else {
-                       iter->tnode = NULL;
-                       iter->trie  = t;
-                       iter->index = 0;
-                       iter->depth = 0;
-               }
-               return n;
+       if (IS_TNODE(n)) {
+               iter->tnode = (struct tnode *) n;
+               iter->index = 0;
+               iter->depth = 1;
+       } else {
+               iter->tnode = NULL;
+               iter->index = 0;
+               iter->depth = 0;
        }
-       return NULL;
+
+       return n;
 }
 
 static void trie_collect_stats(struct trie *t, struct trie_stat *s)
@@ -2106,8 +2123,7 @@ static void trie_collect_stats(struct trie *t, struct trie_stat *s)
        memset(s, 0, sizeof(*s));
 
        rcu_read_lock();
-       for (n = fib_trie_get_first(&iter, t); n;
-            n = fib_trie_get_next(&iter)) {
+       for (n = fib_trie_get_first(&iter, t); n; n = fib_trie_get_next(&iter)) {
                if (IS_LEAF(n)) {
                        struct leaf *l = (struct leaf *)n;
                        struct leaf_info *li;
@@ -2196,61 +2212,55 @@ static void trie_show_usage(struct seq_file *seq,
 }
 #endif /*  CONFIG_IP_FIB_TRIE_STATS */
 
-static void fib_trie_show(struct seq_file *seq, const char *name,
-                         struct trie *trie)
+static void fib_table_print(struct seq_file *seq, struct fib_table *tb)
 {
-       struct trie_stat stat;
-
-       trie_collect_stats(trie, &stat);
-       seq_printf(seq, "%s:\n", name);
-       trie_show_stats(seq, &stat);
-#ifdef CONFIG_IP_FIB_TRIE_STATS
-       trie_show_usage(seq, &trie->stats);
-#endif
+       if (tb->tb_id == RT_TABLE_LOCAL)
+               seq_puts(seq, "Local:\n");
+       else if (tb->tb_id == RT_TABLE_MAIN)
+               seq_puts(seq, "Main:\n");
+       else
+               seq_printf(seq, "Id %d:\n", tb->tb_id);
 }
 
+
 static int fib_triestat_seq_show(struct seq_file *seq, void *v)
 {
        struct net *net = (struct net *)seq->private;
-       struct fib_table *tb;
+       unsigned int h;
 
        seq_printf(seq,
                   "Basic info: size of leaf:"
                   " %Zd bytes, size of tnode: %Zd bytes.\n",
                   sizeof(struct leaf), sizeof(struct tnode));
 
-       tb = fib_get_table(net, RT_TABLE_LOCAL);
-       if (tb)
-               fib_trie_show(seq, "Local", (struct trie *) tb->tb_data);
+       for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
+               struct hlist_head *head = &net->ipv4.fib_table_hash[h];
+               struct hlist_node *node;
+               struct fib_table *tb;
 
-       tb = fib_get_table(net, RT_TABLE_MAIN);
-       if (tb)
-               fib_trie_show(seq, "Main", (struct trie *) tb->tb_data);
+               hlist_for_each_entry_rcu(tb, node, head, tb_hlist) {
+                       struct trie *t = (struct trie *) tb->tb_data;
+                       struct trie_stat stat;
 
-       return 0;
-}
+                       if (!t)
+                               continue;
 
-static int fib_triestat_seq_open(struct inode *inode, struct file *file)
-{
-       int err;
-       struct net *net;
-
-       net = get_proc_net(inode);
-       if (net == NULL)
-               return -ENXIO;
-       err = single_open(file, fib_triestat_seq_show, net);
-       if (err < 0) {
-               put_net(net);
-               return err;
+                       fib_table_print(seq, tb);
+
+                       trie_collect_stats(t, &stat);
+                       trie_show_stats(seq, &stat);
+#ifdef CONFIG_IP_FIB_TRIE_STATS
+                       trie_show_usage(seq, &t->stats);
+#endif
+               }
        }
+
        return 0;
 }
 
-static int fib_triestat_seq_release(struct inode *ino, struct file *f)
+static int fib_triestat_seq_open(struct inode *inode, struct file *file)
 {
-       struct seq_file *seq = f->private_data;
-       put_net(seq->private);
-       return single_release(ino, f);
+       return single_open_net(inode, file, fib_triestat_seq_show);
 }
 
 static const struct file_operations fib_triestat_fops = {
@@ -2258,70 +2268,82 @@ static const struct file_operations fib_triestat_fops = {
        .open   = fib_triestat_seq_open,
        .read   = seq_read,
        .llseek = seq_lseek,
-       .release = fib_triestat_seq_release,
+       .release = single_release_net,
 };
 
-static struct node *fib_trie_get_idx(struct fib_trie_iter *iter,
-                                     loff_t pos)
+static struct node *fib_trie_get_idx(struct seq_file *seq, loff_t pos)
 {
+       struct fib_trie_iter *iter = seq->private;
+       struct net *net = seq_file_net(seq);
        loff_t idx = 0;
-       struct node *n;
+       unsigned int h;
 
-       for (n = fib_trie_get_first(iter, iter->trie_local);
-            n; ++idx, n = fib_trie_get_next(iter)) {
-               if (pos == idx)
-                       return n;
-       }
+       for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
+               struct hlist_head *head = &net->ipv4.fib_table_hash[h];
+               struct hlist_node *node;
+               struct fib_table *tb;
 
-       for (n = fib_trie_get_first(iter, iter->trie_main);
-            n; ++idx, n = fib_trie_get_next(iter)) {
-               if (pos == idx)
-                       return n;
+               hlist_for_each_entry_rcu(tb, node, head, tb_hlist) {
+                       struct node *n;
+
+                       for (n = fib_trie_get_first(iter,
+                                                   (struct trie *) tb->tb_data);
+                            n; n = fib_trie_get_next(iter))
+                               if (pos == idx++) {
+                                       iter->tb = tb;
+                                       return n;
+                               }
+               }
        }
+
        return NULL;
 }
 
 static void *fib_trie_seq_start(struct seq_file *seq, loff_t *pos)
        __acquires(RCU)
 {
-       struct fib_trie_iter *iter = seq->private;
-       struct fib_table *tb;
-
-       if (!iter->trie_local) {
-               tb = fib_get_table(iter->p.net, RT_TABLE_LOCAL);
-               if (tb)
-                       iter->trie_local = (struct trie *) tb->tb_data;
-       }
-       if (!iter->trie_main) {
-               tb = fib_get_table(iter->p.net, RT_TABLE_MAIN);
-               if (tb)
-                       iter->trie_main = (struct trie *) tb->tb_data;
-       }
        rcu_read_lock();
-       if (*pos == 0)
-               return SEQ_START_TOKEN;
-       return fib_trie_get_idx(iter, *pos - 1);
+       return fib_trie_get_idx(seq, *pos);
 }
 
 static void *fib_trie_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct fib_trie_iter *iter = seq->private;
-       void *l = v;
+       struct net *net = seq_file_net(seq);
+       struct fib_table *tb = iter->tb;
+       struct hlist_node *tb_node;
+       unsigned int h;
+       struct node *n;
 
        ++*pos;
-       if (v == SEQ_START_TOKEN)
-               return fib_trie_get_idx(iter, 0);
-
-       v = fib_trie_get_next(iter);
-       BUG_ON(v == l);
-       if (v)
-               return v;
+       /* next node in same table */
+       n = fib_trie_get_next(iter);
+       if (n)
+               return n;
 
-       /* continue scan in next trie */
-       if (iter->trie == iter->trie_local)
-               return fib_trie_get_first(iter, iter->trie_main);
+       /* walk rest of this hash chain */
+       h = tb->tb_id & (FIB_TABLE_HASHSZ - 1);
+       while ( (tb_node = rcu_dereference(tb->tb_hlist.next)) ) {
+               tb = hlist_entry(tb_node, struct fib_table, tb_hlist);
+               n = fib_trie_get_first(iter, (struct trie *) tb->tb_data);
+               if (n)
+                       goto found;
+       }
 
+       /* new hash chain */
+       while (++h < FIB_TABLE_HASHSZ) {
+               struct hlist_head *head = &net->ipv4.fib_table_hash[h];
+               hlist_for_each_entry_rcu(tb, tb_node, head, tb_hlist) {
+                       n = fib_trie_get_first(iter, (struct trie *) tb->tb_data);
+                       if (n)
+                               goto found;
+               }
+       }
        return NULL;
+
+found:
+       iter->tb = tb;
+       return n;
 }
 
 static void fib_trie_seq_stop(struct seq_file *seq, void *v)
@@ -2349,7 +2371,7 @@ static inline const char *rtn_scope(char *buf, size_t len, enum rt_scope_t s)
        }
 }
 
-static const char *rtn_type_names[__RTN_MAX] = {
+static const char *const rtn_type_names[__RTN_MAX] = {
        [RTN_UNSPEC] = "UNSPEC",
        [RTN_UNICAST] = "UNICAST",
        [RTN_LOCAL] = "LOCAL",
@@ -2378,52 +2400,42 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
        const struct fib_trie_iter *iter = seq->private;
        struct node *n = v;
 
-       if (v == SEQ_START_TOKEN)
-               return 0;
-
-       if (!node_parent_rcu(n)) {
-               if (iter->trie == iter->trie_local)
-                       seq_puts(seq, "<local>:\n");
-               else
-                       seq_puts(seq, "<main>:\n");
-       }
+       if (!node_parent_rcu(n))
+               fib_table_print(seq, iter->tb);
 
        if (IS_TNODE(n)) {
                struct tnode *tn = (struct tnode *) n;
                __be32 prf = htonl(mask_pfx(tn->key, tn->pos));
 
                seq_indent(seq, iter->depth-1);
-               seq_printf(seq, "  +-- %d.%d.%d.%d/%d %d %d %d\n",
-                          NIPQUAD(prf), tn->pos, tn->bits, tn->full_children,
+               seq_printf(seq, "  +-- %pI4/%d %d %d %d\n",
+                          &prf, tn->pos, tn->bits, tn->full_children,
                           tn->empty_children);
 
        } else {
                struct leaf *l = (struct leaf *) n;
-               int i;
+               struct leaf_info *li;
+               struct hlist_node *node;
                __be32 val = htonl(l->key);
 
                seq_indent(seq, iter->depth);
-               seq_printf(seq, "  |-- %d.%d.%d.%d\n", NIPQUAD(val));
-               for (i = 32; i >= 0; i--) {
-                       struct leaf_info *li = find_leaf_info(l, i);
-
-                       if (li) {
-                               struct fib_alias *fa;
-
-                               list_for_each_entry_rcu(fa, &li->falh, fa_list) {
-                                       char buf1[32], buf2[32];
-
-                                       seq_indent(seq, iter->depth+1);
-                                       seq_printf(seq, "  /%d %s %s", i,
-                                                  rtn_scope(buf1, sizeof(buf1),
-                                                            fa->fa_scope),
-                                                  rtn_type(buf2, sizeof(buf2),
-                                                            fa->fa_type));
-                                       if (fa->fa_tos)
-                                               seq_printf(seq, "tos =%d\n",
-                                                          fa->fa_tos);
-                                       seq_putc(seq, '\n');
-                               }
+               seq_printf(seq, "  |-- %pI4\n", &val);
+
+               hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
+                       struct fib_alias *fa;
+
+                       list_for_each_entry_rcu(fa, &li->falh, fa_list) {
+                               char buf1[32], buf2[32];
+
+                               seq_indent(seq, iter->depth+1);
+                               seq_printf(seq, "  /%d %s %s", li->plen,
+                                          rtn_scope(buf1, sizeof(buf1),
+                                                    fa->fa_scope),
+                                          rtn_type(buf2, sizeof(buf2),
+                                                   fa->fa_type));
+                               if (fa->fa_tos)
+                                       seq_printf(seq, " tos=%d", fa->fa_tos);
+                               seq_putc(seq, '\n');
                        }
                }
        }
@@ -2452,6 +2464,84 @@ static const struct file_operations fib_trie_fops = {
        .release = seq_release_net,
 };
 
+struct fib_route_iter {
+       struct seq_net_private p;
+       struct trie *main_trie;
+       loff_t  pos;
+       t_key   key;
+};
+
+static struct leaf *fib_route_get_idx(struct fib_route_iter *iter, loff_t pos)
+{
+       struct leaf *l = NULL;
+       struct trie *t = iter->main_trie;
+
+       /* use cache location of last found key */
+       if (iter->pos > 0 && pos >= iter->pos && (l = fib_find_node(t, iter->key)))
+               pos -= iter->pos;
+       else {
+               iter->pos = 0;
+               l = trie_firstleaf(t);
+       }
+
+       while (l && pos-- > 0) {
+               iter->pos++;
+               l = trie_nextleaf(l);
+       }
+
+       if (l)
+               iter->key = pos;        /* remember it */
+       else
+               iter->pos = 0;          /* forget it */
+
+       return l;
+}
+
+static void *fib_route_seq_start(struct seq_file *seq, loff_t *pos)
+       __acquires(RCU)
+{
+       struct fib_route_iter *iter = seq->private;
+       struct fib_table *tb;
+
+       rcu_read_lock();
+       tb = fib_get_table(seq_file_net(seq), RT_TABLE_MAIN);
+       if (!tb)
+               return NULL;
+
+       iter->main_trie = (struct trie *) tb->tb_data;
+       if (*pos == 0)
+               return SEQ_START_TOKEN;
+       else
+               return fib_route_get_idx(iter, *pos - 1);
+}
+
+static void *fib_route_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+       struct fib_route_iter *iter = seq->private;
+       struct leaf *l = v;
+
+       ++*pos;
+       if (v == SEQ_START_TOKEN) {
+               iter->pos = 0;
+               l = trie_firstleaf(iter->main_trie);
+       } else {
+               iter->pos++;
+               l = trie_nextleaf(l);
+       }
+
+       if (l)
+               iter->key = l->key;
+       else
+               iter->pos = 0;
+       return l;
+}
+
+static void fib_route_seq_stop(struct seq_file *seq, void *v)
+       __releases(RCU)
+{
+       rcu_read_unlock();
+}
+
 static unsigned fib_flag_trans(int type, __be32 mask, const struct fib_info *fi)
 {
        static unsigned type2flags[RTN_MAX + 1] = {
@@ -2475,10 +2565,9 @@ static unsigned fib_flag_trans(int type, __be32 mask, const struct fib_info *fi)
  */
 static int fib_route_seq_show(struct seq_file *seq, void *v)
 {
-       const struct fib_trie_iter *iter = seq->private;
        struct leaf *l = v;
-       int i;
-       char bf[128];
+       struct leaf_info *li;
+       struct hlist_node *node;
 
        if (v == SEQ_START_TOKEN) {
                seq_printf(seq, "%-127s\n", "Iface\tDestination\tGateway "
@@ -2487,34 +2576,26 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
                return 0;
        }
 
-       if (iter->trie == iter->trie_local)
-               return 0;
-
-       if (IS_TNODE(l))
-               return 0;
-
-       for (i = 32; i >= 0; i--) {
-               struct leaf_info *li = find_leaf_info(l, i);
+       hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
                struct fib_alias *fa;
                __be32 mask, prefix;
 
-               if (!li)
-                       continue;
-
                mask = inet_make_mask(li->plen);
                prefix = htonl(l->key);
 
                list_for_each_entry_rcu(fa, &li->falh, fa_list) {
                        const struct fib_info *fi = fa->fa_info;
                        unsigned flags = fib_flag_trans(fa->fa_type, mask, fi);
+                       int len;
 
                        if (fa->fa_type == RTN_BROADCAST
                            || fa->fa_type == RTN_MULTICAST)
                                continue;
 
                        if (fi)
-                               snprintf(bf, sizeof(bf),
-                                        "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+                               seq_printf(seq,
+                                        "%s\t%08X\t%08X\t%04X\t%d\t%u\t"
+                                        "%d\t%08X\t%d\t%u\t%u%n",
                                         fi->fib_dev ? fi->fib_dev->name : "*",
                                         prefix,
                                         fi->fib_nh->nh_gw, flags, 0, 0,
@@ -2523,14 +2604,15 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
                                         (fi->fib_advmss ?
                                          fi->fib_advmss + 40 : 0),
                                         fi->fib_window,
-                                        fi->fib_rtt >> 3);
+                                        fi->fib_rtt >> 3, &len);
                        else
-                               snprintf(bf, sizeof(bf),
-                                        "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
+                               seq_printf(seq,
+                                        "*\t%08X\t%08X\t%04X\t%d\t%u\t"
+                                        "%d\t%08X\t%d\t%u\t%u%n",
                                         prefix, 0, flags, 0, 0, 0,
-                                        mask, 0, 0, 0);
+                                        mask, 0, 0, 0, &len);
 
-                       seq_printf(seq, "%-127s\n", bf);
+                       seq_printf(seq, "%*s\n", 127 - len, "");
                }
        }
 
@@ -2538,16 +2620,16 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
 }
 
 static const struct seq_operations fib_route_seq_ops = {
-       .start  = fib_trie_seq_start,
-       .next   = fib_trie_seq_next,
-       .stop   = fib_trie_seq_stop,
+       .start  = fib_route_seq_start,
+       .next   = fib_route_seq_next,
+       .stop   = fib_route_seq_stop,
        .show   = fib_route_seq_show,
 };
 
 static int fib_route_seq_open(struct inode *inode, struct file *file)
 {
        return seq_open_net(inode, file, &fib_route_seq_ops,
-                           sizeof(struct fib_trie_iter));
+                           sizeof(struct fib_route_iter));
 }
 
 static const struct file_operations fib_route_fops = {