diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-09-11 23:03:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-09-14 20:02:50 -0400 |
commit | d136f1bd366fdb7e747ca7e0218171e7a00a98a5 (patch) | |
tree | cee39b3249c36aba4b765cae6d9d3579c9f10a2d /net/netlink/genetlink.c | |
parent | 8be8057e72d7d319f8e97b26e16de8021fe63988 (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.c | 5 |
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); |