aboutsummaryrefslogtreecommitdiffstats
path: root/net/netlink/genetlink.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-09-11 23:03:15 -0400
committerDavid S. Miller <davem@davemloft.net>2009-09-14 20:02:50 -0400
commitd136f1bd366fdb7e747ca7e0218171e7a00a98a5 (patch)
treecee39b3249c36aba4b765cae6d9d3579c9f10a2d /net/netlink/genetlink.c
parent8be8057e72d7d319f8e97b26e16de8021fe63988 (diff)
genetlink: fix netns vs. netlink table locking
Since my commits introducing netns awareness into genetlink we can get this problem: BUG: scheduling while atomic: modprobe/1178/0x00000002 2 locks held by modprobe/1178: #0: (genl_mutex){+.+.+.}, at: [<ffffffff8135ee1a>] genl_register_mc_grou #1: (rcu_read_lock){.+.+..}, at: [<ffffffff8135eeb5>] genl_register_mc_g Pid: 1178, comm: modprobe Not tainted 2.6.31-rc8-wl-34789-g95cb731-dirty # Call Trace: [<ffffffff8103e285>] __schedule_bug+0x85/0x90 [<ffffffff81403138>] schedule+0x108/0x588 [<ffffffff8135b131>] netlink_table_grab+0xa1/0xf0 [<ffffffff8135c3a7>] netlink_change_ngroups+0x47/0x100 [<ffffffff8135ef0f>] genl_register_mc_group+0x12f/0x290 because I overlooked that netlink_table_grab() will schedule, thinking it was just the rwlock. However, in the contention case, that isn't actually true. Fix this by letting the code grab the netlink table lock first and then the RCU for netns protection. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/netlink/genetlink.c')
-rw-r--r--net/netlink/genetlink.c5
1 files changed, 4 insertions, 1 deletions
diff --git a/net/netlink/genetlink.c b/net/netlink/genetlink.c
index 66f6ba0bab11..566941e03363 100644
--- a/net/netlink/genetlink.c
+++ b/net/netlink/genetlink.c
@@ -176,9 +176,10 @@ int genl_register_mc_group(struct genl_family *family,
176 if (family->netnsok) { 176 if (family->netnsok) {
177 struct net *net; 177 struct net *net;
178 178
179 netlink_table_grab();
179 rcu_read_lock(); 180 rcu_read_lock();
180 for_each_net_rcu(net) { 181 for_each_net_rcu(net) {
181 err = netlink_change_ngroups(net->genl_sock, 182 err = __netlink_change_ngroups(net->genl_sock,
182 mc_groups_longs * BITS_PER_LONG); 183 mc_groups_longs * BITS_PER_LONG);
183 if (err) { 184 if (err) {
184 /* 185 /*
@@ -188,10 +189,12 @@ int genl_register_mc_group(struct genl_family *family,
188 * increased on some sockets which is ok. 189 * increased on some sockets which is ok.
189 */ 190 */
190 rcu_read_unlock(); 191 rcu_read_unlock();
192 netlink_table_ungrab();
191 goto out; 193 goto out;
192 } 194 }
193 } 195 }
194 rcu_read_unlock(); 196 rcu_read_unlock();
197 netlink_table_ungrab();
195 } else { 198 } else {
196 err = netlink_change_ngroups(init_net.genl_sock, 199 err = netlink_change_ngroups(init_net.genl_sock,
197 mc_groups_longs * BITS_PER_LONG); 200 mc_groups_longs * BITS_PER_LONG);