]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - net/ipv4/fib_trie.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jbarnes...
[linux-2.6.git] / net / ipv4 / fib_trie.c
index 35851c96bdfb2e6f00eb3681b4d9a090a0163a42..01ef8ba9025cd70c576787d1ec32eb22504f5042 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>
@@ -122,7 +120,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 +162,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 +188,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)
@@ -310,9 +324,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 +344,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 +361,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 +381,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);
@@ -469,14 +513,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;
@@ -486,23 +530,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.
@@ -571,14 +604,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))) {
@@ -595,35 +631,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)) {
 
@@ -638,19 +658,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;
 
@@ -661,10 +672,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;
 }
 
@@ -747,7 +758,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;
                }
 
@@ -792,9 +803,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:
        {
@@ -876,7 +887,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:
        {
@@ -950,7 +961,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;
@@ -974,12 +987,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));
@@ -989,6 +1004,10 @@ static struct node *trie_rebalance(struct trie *t, struct tnode *tn)
                                      (struct node *)tn, wasfull);
 
                tp = node_parent((struct node *) tn);
+               if (!tp)
+                       rcu_assign_pointer(t->trie, (struct node *)tn);
+
+               tnode_free_flush();
                if (!tp)
                        break;
                tn = tp;
@@ -998,7 +1017,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 */
@@ -1083,7 +1105,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;
        }
 
@@ -1119,7 +1141,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;
                }
 
@@ -1146,7 +1168,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;
 }
@@ -1154,7 +1176,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;
@@ -1262,7 +1284,7 @@ static int fn_trie_insert(struct fib_table *tb, struct fib_config *cfg)
 
                        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);
 
@@ -1307,7 +1329,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:
@@ -1338,8 +1360,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)
@@ -1348,17 +1369,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;
@@ -1382,10 +1403,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;
        }
 
@@ -1400,7 +1418,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
@@ -1410,11 +1428,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;
                }
 
@@ -1537,7 +1553,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;
 
@@ -1571,17 +1587,17 @@ static void trie_leaf_remove(struct trie *t, struct leaf *l)
        if (tp) {
                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);
 
-       tnode_free((struct tnode *) l);
+       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;
@@ -1655,14 +1671,14 @@ static int fn_trie_delete(struct fib_table *tb, struct fib_config *cfg)
                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;
@@ -1680,7 +1696,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;
@@ -1688,7 +1704,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);
@@ -1750,7 +1766,7 @@ static struct leaf *trie_firstleaf(struct trie *t)
 static struct leaf *trie_nextleaf(struct leaf *l)
 {
        struct node *c = (struct node *) l;
-       struct tnode *p = node_parent(c);
+       struct tnode *p = node_parent_rcu(c);
 
        if (!p)
                return NULL;    /* trie with just one leaf */
@@ -1762,11 +1778,9 @@ static struct leaf *trie_leafindex(struct trie *t, int index)
 {
        struct leaf *l = trie_firstleaf(t);
 
-       while (index-- > 0) {
+       while (l && index-- > 0)
                l = trie_nextleaf(l);
-               if (!l)
-                       break;
-       }
+
        return l;
 }
 
@@ -1774,14 +1788,14 @@ static struct leaf *trie_leafindex(struct trie *t, int index)
 /*
  * 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 *l, *ll = NULL;
        int found = 0;
 
        for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
-               found += trie_flush_leaf(t, l);
+               found += trie_flush_leaf(l);
 
                if (ll && hlist_empty(&ll->list))
                        trie_leaf_remove(t, ll);
@@ -1795,9 +1809,9 @@ static int fn_trie_flush(struct fib_table *tb)
        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;
@@ -1940,8 +1954,8 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
        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)
 {
        struct leaf *l;
        struct trie *t = (struct trie *) tb->tb_data;
@@ -2008,12 +2022,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));
@@ -2028,9 +2036,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;
 };
@@ -2083,31 +2090,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)
@@ -2118,8 +2120,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;
@@ -2208,61 +2209,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 = {
@@ -2270,70 +2265,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)
@@ -2361,7 +2368,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",
@@ -2390,23 +2397,16 @@ 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 {
@@ -2416,7 +2416,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
                __be32 val = htonl(l->key);
 
                seq_indent(seq, iter->depth);
-               seq_printf(seq, "  |-- %d.%d.%d.%d\n", NIPQUAD(val));
+               seq_printf(seq, "  |-- %pI4\n", &val);
 
                hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
                        struct fib_alias *fa;
@@ -2431,8 +2431,7 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
                                           rtn_type(buf2, sizeof(buf2),
                                                    fa->fa_type));
                                if (fa->fa_tos)
-                                       seq_printf(seq, "tos =%d\n",
-                                                  fa->fa_tos);
+                                       seq_printf(seq, " tos=%d", fa->fa_tos);
                                seq_putc(seq, '\n');
                        }
                }
@@ -2462,6 +2461,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] = {
@@ -2485,7 +2562,6 @@ 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;
        struct leaf_info *li;
        struct hlist_node *node;
@@ -2497,12 +2573,6 @@ 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;
-
        hlist_for_each_entry_rcu(li, node, &l->list, hlist) {
                struct fib_alias *fa;
                __be32 mask, prefix;
@@ -2513,15 +2583,16 @@ static int fib_route_seq_show(struct seq_file *seq, void *v)
                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);
-                       char bf[128];
+                       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,
@@ -2530,14 +2601,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, "");
                }
        }
 
@@ -2545,16 +2617,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 = {