IPv6: addrconf timer race
[safe/jmp/linux-2.6] / net / netlink / genetlink.c
index 66f6ba0..a4b6e14 100644 (file)
@@ -97,25 +97,17 @@ static struct genl_ops *genl_get_cmd(u8 cmd, struct genl_family *family)
 */
 static inline u16 genl_generate_id(void)
 {
-       static u16 id_gen_idx;
-       int overflowed = 0;
+       static u16 id_gen_idx = GENL_MIN_ID;
+       int i;
 
-       do {
-               if (id_gen_idx == 0)
+       for (i = 0; i <= GENL_MAX_ID - GENL_MIN_ID; i++) {
+               if (!genl_family_find_byid(id_gen_idx))
+                       return id_gen_idx;
+               if (++id_gen_idx > GENL_MAX_ID)
                        id_gen_idx = GENL_MIN_ID;
+       }
 
-               if (++id_gen_idx > GENL_MAX_ID) {
-                       if (!overflowed) {
-                               overflowed = 1;
-                               id_gen_idx = 0;
-                               continue;
-                       } else
-                               return 0;
-               }
-
-       } while (genl_family_find_byid(id_gen_idx));
-
-       return id_gen_idx;
+       return 0;
 }
 
 static struct genl_multicast_group notify_grp;
@@ -176,9 +168,10 @@ int genl_register_mc_group(struct genl_family *family,
        if (family->netnsok) {
                struct net *net;
 
+               netlink_table_grab();
                rcu_read_lock();
                for_each_net_rcu(net) {
-                       err = netlink_change_ngroups(net->genl_sock,
+                       err = __netlink_change_ngroups(net->genl_sock,
                                        mc_groups_longs * BITS_PER_LONG);
                        if (err) {
                                /*
@@ -188,10 +181,12 @@ int genl_register_mc_group(struct genl_family *family,
                                 * increased on some sockets which is ok.
                                 */
                                rcu_read_unlock();
+                               netlink_table_ungrab();
                                goto out;
                        }
                }
                rcu_read_unlock();
+               netlink_table_ungrab();
        } else {
                err = netlink_change_ngroups(init_net.genl_sock,
                                             mc_groups_longs * BITS_PER_LONG);
@@ -217,10 +212,12 @@ static void __genl_unregister_mc_group(struct genl_family *family,
        struct net *net;
        BUG_ON(grp->family != family);
 
+       netlink_table_grab();
        rcu_read_lock();
        for_each_net_rcu(net)
-               netlink_clear_multicast_users(net->genl_sock, grp->id);
+               __netlink_clear_multicast_users(net->genl_sock, grp->id);
        rcu_read_unlock();
+       netlink_table_ungrab();
 
        clear_bit(grp->id, mc_groups);
        list_del(&grp->list);
@@ -369,11 +366,6 @@ int genl_register_family(struct genl_family *family)
                goto errout_locked;
        }
 
-       if (genl_family_find_byid(family->id)) {
-               err = -EEXIST;
-               goto errout_locked;
-       }
-
        if (family->id == GENL_ID_GENERATE) {
                u16 newid = genl_generate_id();
 
@@ -383,6 +375,9 @@ int genl_register_family(struct genl_family *family)
                }
 
                family->id = newid;
+       } else if (genl_family_find_byid(family->id)) {
+               err = -EEXIST;
+               goto errout_locked;
        }
 
        if (family->maxattr) {
@@ -686,9 +681,7 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
        int chains_to_skip = cb->args[0];
        int fams_to_skip = cb->args[1];
 
-       for (i = 0; i < GENL_FAM_TAB_SIZE; i++) {
-               if (i < chains_to_skip)
-                       continue;
+       for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) {
                n = 0;
                list_for_each_entry(rt, genl_family_chain(i), family_list) {
                        if (!rt->netnsok && !net_eq(net, &init_net))