[IPV4] fib_trie: fix warning from rcu_assign_poinger
[sfrench/cifs-2.6.git] / net / ipv4 / fib_trie.c
index 2ea94ebe19f83f144e25c40492bb9cf2269e8a20..f6cdc012eec559a28a176cd00569fee0bf171a08 100644 (file)
@@ -177,10 +177,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)
@@ -447,7 +450,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++;
@@ -1206,20 +1208,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,7 +1258,7 @@ 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);
@@ -1248,20 +1275,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))
@@ -1306,7 +1324,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,
@@ -1545,49 +1562,23 @@ 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;
-
-       pr_debug("entering trie_leaf_remove(%p)\n", n);
+       struct tnode *tp = node_parent((struct node *) l);
 
-       /* 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));
        } else
                rcu_assign_pointer(t->trie, NULL);
 
-       return 1;
+       tnode_free((struct tnode *) l);
 }
 
 /*
@@ -1628,9 +1619,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,7 +1655,7 @@ 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);
@@ -1747,7 +1737,6 @@ static struct leaf *leaf_walk_rcu(struct tnode *p, struct node *c)
        return NULL; /* Root of trie */
 }
 
-
 static struct leaf *trie_firstleaf(struct trie *t)
 {
        struct tnode *n = (struct tnode *) rcu_dereference(t->trie);
@@ -1772,25 +1761,36 @@ static struct leaf *trie_nextleaf(struct leaf *l)
        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)
 {
        struct trie *t = (struct trie *) tb->tb_data;
-       struct leaf *ll = NULL, *l = NULL;
+       struct leaf *l, *ll = NULL;
        int found = 0;
 
        for (l = trie_firstleaf(t); l; l = trie_nextleaf(l)) {
                found += trie_flush_leaf(t, 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;
@@ -1875,10 +1875,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 */
@@ -1899,16 +1898,15 @@ static int fn_trie_dump_fa(t_key key, int plen, struct list_head *fah,
                                  plen,
                                  fa->fa_tos,
                                  fa->fa_info, NLM_F_MULTI) < 0) {
-                       cb->args[4] = i;
+                       cb->args[5] = i;
                        return -1;
                }
                i++;
        }
-       cb->args[4] = i;
+       cb->args[5] = i;
        return skb->len;
 }
 
-
 static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
                        struct sk_buff *skb, struct netlink_callback *cb)
 {
@@ -1916,7 +1914,7 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
        struct hlist_node *node;
        int i, s_i;
 
-       s_i = cb->args[3];
+       s_i = cb->args[4];
        i = 0;
 
        /* rcu_read_lock is hold by caller */
@@ -1927,51 +1925,61 @@ static int fn_trie_dump_leaf(struct leaf *l, struct fib_table *tb,
                }
 
                if (i > s_i)
-                       cb->args[4] = 0;
+                       cb->args[5] = 0;
 
                if (list_empty(&li->falh))
                        continue;
 
                if (fn_trie_dump_fa(l->key, li->plen, &li->falh, tb, skb, cb) < 0) {
-                       cb->args[3] = i;
+                       cb->args[4] = i;
                        return -1;
                }
                i++;
        }
 
-       cb->args[3] = i;
+       cb->args[4] = i;
        return skb->len;
 }
 
-
-
 static int fn_trie_dump(struct fib_table *tb, struct sk_buff *skb,
                        struct netlink_callback *cb)
 {
        struct leaf *l;
        struct trie *t = (struct trie *) tb->tb_data;
-       int h = 0;
-       int s_h = cb->args[2];
+       t_key key = cb->args[2];
+       int count = cb->args[3];
 
        rcu_read_lock();
-       for (h = 0, l = trie_firstleaf(t); l != NULL; h++, l = trie_nextleaf(l)) {
-               if (h < s_h)
-                       continue;
-
-               if (h > s_h) {
-                       cb->args[3] = 0;
-                       cb->args[4] = 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);
+       }
 
+       while (l) {
+               cb->args[2] = l->key;
                if (fn_trie_dump_leaf(l, tb, skb, cb) < 0) {
+                       cb->args[3] = count;
                        rcu_read_unlock();
-                       cb->args[2] = h;
                        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] = h;
        return skb->len;
 }
 
@@ -2406,7 +2414,6 @@ static int fib_trie_seq_show(struct seq_file *seq, void *v)
                struct leaf *l = (struct leaf *) n;
                struct leaf_info *li;
                struct hlist_node *node;
-
                __be32 val = htonl(l->key);
 
                seq_indent(seq, iter->depth);
@@ -2425,8 +2432,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');
                        }
                }
@@ -2456,6 +2462,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(iter->p.net, 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] = {
@@ -2479,7 +2563,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;
@@ -2491,19 +2574,10 @@ 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;
 
-               if (!li)
-                       continue;
-
                mask = inet_make_mask(li->plen);
                prefix = htonl(l->key);
 
@@ -2542,16 +2616,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 = {