fs/hfsplus: use bool instead of int for is_known_namespace() return value
[linux-2.6-block.git] / net / ipv4 / fib_frontend.c
index 23b9b3e86f4cd78987790f3456470318e3b82ece..872494e6e6eb7996185a99a8b05915f861a73ec4 100644 (file)
@@ -52,12 +52,12 @@ static int __net_init fib4_rules_init(struct net *net)
 {
        struct fib_table *local_table, *main_table;
 
-       local_table = fib_trie_table(RT_TABLE_LOCAL);
-       if (local_table == NULL)
+       main_table  = fib_trie_table(RT_TABLE_MAIN, NULL);
+       if (!main_table)
                return -ENOMEM;
 
-       main_table  = fib_trie_table(RT_TABLE_MAIN);
-       if (main_table == NULL)
+       local_table = fib_trie_table(RT_TABLE_LOCAL, main_table);
+       if (!local_table)
                goto fail;
 
        hlist_add_head_rcu(&local_table->tb_hlist,
@@ -67,14 +67,14 @@ static int __net_init fib4_rules_init(struct net *net)
        return 0;
 
 fail:
-       fib_free_table(local_table);
+       fib_free_table(main_table);
        return -ENOMEM;
 }
 #else
 
 struct fib_table *fib_new_table(struct net *net, u32 id)
 {
-       struct fib_table *tb;
+       struct fib_table *tb, *alias = NULL;
        unsigned int h;
 
        if (id == 0)
@@ -83,23 +83,23 @@ struct fib_table *fib_new_table(struct net *net, u32 id)
        if (tb)
                return tb;
 
-       tb = fib_trie_table(id);
+       if (id == RT_TABLE_LOCAL)
+               alias = fib_new_table(net, RT_TABLE_MAIN);
+
+       tb = fib_trie_table(id, alias);
        if (!tb)
                return NULL;
 
        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;
        }
@@ -129,16 +129,62 @@ struct fib_table *fib_get_table(struct net *net, u32 id)
 }
 #endif /* CONFIG_IP_MULTIPLE_TABLES */
 
+static void fib_replace_table(struct net *net, struct fib_table *old,
+                             struct fib_table *new)
+{
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+       switch (new->tb_id) {
+       case RT_TABLE_LOCAL:
+               rcu_assign_pointer(net->ipv4.fib_local, new);
+               break;
+       case RT_TABLE_MAIN:
+               rcu_assign_pointer(net->ipv4.fib_main, new);
+               break;
+       case RT_TABLE_DEFAULT:
+               rcu_assign_pointer(net->ipv4.fib_default, new);
+               break;
+       default:
+               break;
+       }
+
+#endif
+       /* replace the old table in the hlist */
+       hlist_replace_rcu(&old->tb_hlist, &new->tb_hlist);
+}
+
+int fib_unmerge(struct net *net)
+{
+       struct fib_table *old, *new;
+
+       /* attempt to fetch local table if it has been allocated */
+       old = fib_get_table(net, RT_TABLE_LOCAL);
+       if (!old)
+               return 0;
+
+       new = fib_trie_unmerge(old);
+       if (!new)
+               return -ENOMEM;
+
+       /* replace merged table with clean table */
+       if (new != old) {
+               fib_replace_table(net, old, new);
+               fib_free_table(old);
+       }
+
+       return 0;
+}
+
 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);
        }
 
@@ -146,6 +192,19 @@ static void fib_flush(struct net *net)
                rt_cache_flush(net);
 }
 
+void fib_flush_external(struct net *net)
+{
+       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)
+                       fib_table_flush_external(tb);
+       }
+}
+
 /*
  * Find address type as if only "dev" was present in the system. If
  * on_dev is NULL then all interfaces are taken into consideration.
@@ -427,7 +486,7 @@ static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
                        for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next)
                                if (strcmp(ifa->ifa_label, devname) == 0)
                                        break;
-                       if (ifa == NULL)
+                       if (!ifa)
                                return -ENODEV;
                        cfg->fc_prefsrc = ifa->ifa_local;
                }
@@ -455,7 +514,7 @@ static int rtentry_to_fib_config(struct net *net, int cmd, struct rtentry *rt,
                int len = 0;
 
                mx = kzalloc(3 * nla_total_size(4), GFP_KERNEL);
-               if (mx == NULL)
+               if (!mx)
                        return -ENOMEM;
 
                if (rt->rt_flags & RTF_MTU)
@@ -617,7 +676,7 @@ static int inet_rtm_delroute(struct sk_buff *skb, struct nlmsghdr *nlh)
                goto errout;
 
        tb = fib_get_table(net, cfg.fc_table);
-       if (tb == NULL) {
+       if (!tb) {
                err = -ESRCH;
                goto errout;
        }
@@ -639,7 +698,7 @@ static int inet_rtm_newroute(struct sk_buff *skb, struct nlmsghdr *nlh)
                goto errout;
 
        tb = fib_new_table(net, cfg.fc_table);
-       if (tb == NULL) {
+       if (!tb) {
                err = -ENOBUFS;
                goto errout;
        }
@@ -665,10 +724,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 +743,8 @@ next:
                }
        }
 out:
+       rcu_read_unlock();
+
        cb->args[1] = e;
        cb->args[0] = h;
 
@@ -716,7 +779,7 @@ static void fib_magic(int cmd, int type, __be32 dst, int dst_len, struct in_ifad
        else
                tb = fib_new_table(net, RT_TABLE_LOCAL);
 
-       if (tb == NULL)
+       if (!tb)
                return;
 
        cfg.fc_table = tb->tb_id;
@@ -743,7 +806,7 @@ void fib_add_ifaddr(struct in_ifaddr *ifa)
 
        if (ifa->ifa_flags & IFA_F_SECONDARY) {
                prim = inet_ifa_byprefix(in_dev, prefix, mask);
-               if (prim == NULL) {
+               if (!prim) {
                        pr_warn("%s: bug: prim == NULL\n", __func__);
                        return;
                }
@@ -797,7 +860,7 @@ void fib_del_ifaddr(struct in_ifaddr *ifa, struct in_ifaddr *iprim)
 
        if (ifa->ifa_flags & IFA_F_SECONDARY) {
                prim = inet_ifa_byprefix(in_dev, any, ifa->ifa_mask);
-               if (prim == NULL) {
+               if (!prim) {
                        pr_warn("%s: bug: prim == NULL\n", __func__);
                        return;
                }
@@ -967,7 +1030,7 @@ static void nl_fib_input(struct sk_buff *skb)
                return;
 
        skb = netlink_skb_clone(skb, GFP_KERNEL);
-       if (skb == NULL)
+       if (!skb)
                return;
        nlh = nlmsg_hdr(skb);
 
@@ -988,7 +1051,7 @@ static int __net_init nl_fib_lookup_init(struct net *net)
        };
 
        sk = netlink_kernel_create(net, NETLINK_FIB_LOOKUP, &cfg);
-       if (sk == NULL)
+       if (!sk)
                return -EAFNOSUPPORT;
        net->ipv4.fibnl = sk;
        return 0;
@@ -1026,7 +1089,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
        case NETDEV_DOWN:
                fib_del_ifaddr(ifa, NULL);
                atomic_inc(&net->ipv4.dev_addr_genid);
-               if (ifa->ifa_dev->ifa_list == NULL) {
+               if (!ifa->ifa_dev->ifa_list) {
                        /* Last address was deleted from this interface.
                         * Disable IP.
                         */
@@ -1094,7 +1157,7 @@ static int __net_init ip_fib_net_init(struct net *net)
        size = max_t(size_t, size, L1_CACHE_BYTES);
 
        net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL);
-       if (net->ipv4.fib_table_hash == NULL)
+       if (!net->ipv4.fib_table_hash)
                return -ENOMEM;
 
        err = fib4_rules_init(net);
@@ -1113,20 +1176,25 @@ static void ip_fib_net_exit(struct net *net)
 
        rtnl_lock();
 #ifdef CONFIG_IP_MULTIPLE_TABLES
-       fib4_rules_exit(net);
+       RCU_INIT_POINTER(net->ipv4.fib_local, NULL);
+       RCU_INIT_POINTER(net->ipv4.fib_main, NULL);
+       RCU_INIT_POINTER(net->ipv4.fib_default, NULL);
 #endif
        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;
 
-               head = &net->ipv4.fib_table_hash[i];
                hlist_for_each_entry_safe(tb, tmp, head, tb_hlist) {
                        hlist_del(&tb->tb_hlist);
                        fib_table_flush(tb);
                        fib_free_table(tb);
                }
        }
+
+#ifdef CONFIG_IP_MULTIPLE_TABLES
+       fib4_rules_exit(net);
+#endif
        rtnl_unlock();
        kfree(net->ipv4.fib_table_hash);
 }