]> Git Repo - linux.git/blobdiff - net/ipv4/fib_frontend.c
fib_trie: Make fib_table rcu safe
[linux.git] / net / ipv4 / fib_frontend.c
index 57be71dd6a9e0163dceefd564bf71036c12dc9ba..220c4b4af4cf78d8c8911e95abf1ae62261208dc 100644 (file)
@@ -89,17 +89,14 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
 
        switch (id) {
        case RT_TABLE_LOCAL:
-               net->ipv4.fib_local = tb;
+               rcu_assign_pointer(net->ipv4.fib_local, tb);
                break;
-
        case RT_TABLE_MAIN:
-               net->ipv4.fib_main = tb;
+               rcu_assign_pointer(net->ipv4.fib_main, tb);
                break;
-
        case RT_TABLE_DEFAULT:
-               net->ipv4.fib_default = tb;
+               rcu_assign_pointer(net->ipv4.fib_default, tb);
                break;
-
        default:
                break;
        }
@@ -132,13 +129,14 @@ struct fib_table *fib_get_table(struct net *net, u32 id)
 static void fib_flush(struct net *net)
 {
        int flushed = 0;
-       struct fib_table *tb;
-       struct hlist_head *head;
        unsigned int h;
 
        for (h = 0; h < FIB_TABLE_HASHSZ; h++) {
-               head = &net->ipv4.fib_table_hash[h];
-               hlist_for_each_entry(tb, head, tb_hlist)
+               struct hlist_head *head = &net->ipv4.fib_table_hash[h];
+               struct hlist_node *tmp;
+               struct fib_table *tb;
+
+               hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
                        flushed += fib_table_flush(tb);
        }
 
@@ -665,10 +663,12 @@ static int inet_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
        s_h = cb->args[0];
        s_e = cb->args[1];
 
+       rcu_read_lock();
+
        for (h = s_h; h < FIB_TABLE_HASHSZ; h++, s_e = 0) {
                e = 0;
                head = &net->ipv4.fib_table_hash[h];
-               hlist_for_each_entry(tb, head, tb_hlist) {
+               hlist_for_each_entry_rcu(tb, head, tb_hlist) {
                        if (e < s_e)
                                goto next;
                        if (dumped)
@@ -682,6 +682,8 @@ next:
                }
        }
 out:
+       rcu_read_unlock();
+
        cb->args[1] = e;
        cb->args[0] = h;
 
@@ -1117,14 +1119,34 @@ static void ip_fib_net_exit(struct net *net)
 
        rtnl_lock();
        for (i = 0; i < FIB_TABLE_HASHSZ; i++) {
-               struct fib_table *tb;
-               struct hlist_head *head;
+               struct hlist_head *head = &net->ipv4.fib_table_hash[i];
                struct hlist_node *tmp;
+               struct fib_table *tb;
+
+               /* this is done in two passes as flushing the table could
+                * cause it to be reallocated in order to accommodate new
+                * tnodes at the root as the table shrinks.
+                */
+               hlist_for_each_entry_safe(tb, tmp, head, tb_hlist)
+                       fib_table_flush(tb);
 
-               head = &net->ipv4.fib_table_hash[i];
                hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) {
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+                       switch (tb->tb_id) {
+                       case RT_TABLE_LOCAL:
+                               RCU_INIT_POINTER(net->ipv4.fib_local, NULL);
+                               break;
+                       case RT_TABLE_MAIN:
+                               RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
+                               break;
+                       case RT_TABLE_DEFAULT:
+                               RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
+                               break;
+                       default:
+                               break;
+                       }
+#endif
                        hlist_del(&tb->tb_hlist);
-                       fib_table_flush(tb);
                        fib_free_table(tb);
                }
        }
This page took 0.034074 seconds and 4 git commands to generate.