diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2007-09-18 17:29:21 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-10-10 19:53:00 -0400 |
commit | 79010420cc3f78eab911598bfdd29c4b06a83e1f (patch) | |
tree | a9031164d7944f8aa90a455d297780b241f3d865 /net/mac80211/ieee80211_iface.c | |
parent | ea49c359f36d5b40bf033c45a08332cb73777aa2 (diff) |
[PATCH] mac80211: fix virtual interface locking
Florian Lohoff noticed a bug in mac80211: when bringing the
master interface down while other virtual interfaces are up
we call dev_close() under a spinlock which is not allowed.
This patch removes the sub_if_lock used by mac80211 in favour
of using an RCU list. All list manipulations are already done
under rtnl so are well protected against each other, and the
read-side locks we took in the RX and TX code are already in
RCU read-side critical sections.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Cc: Florian Lohoff <flo@rfc822.org>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: Michal Piotrowski <michal.k.k.piotrowski@gmail.com>
Cc: Satyam Sharma <satyam@infradead.org>
Signed-off-by: Michael Wu <flamingice@sourmilk.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/ieee80211_iface.c')
-rw-r--r-- | net/mac80211/ieee80211_iface.c | 31 |
1 files changed, 14 insertions, 17 deletions
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index 4590205fdf4b..2ba24ef319da 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c | |||
@@ -79,16 +79,15 @@ int ieee80211_if_add(struct net_device *dev, const char *name, | |||
79 | ieee80211_debugfs_add_netdev(sdata); | 79 | ieee80211_debugfs_add_netdev(sdata); |
80 | ieee80211_if_set_type(ndev, type); | 80 | ieee80211_if_set_type(ndev, type); |
81 | 81 | ||
82 | write_lock_bh(&local->sub_if_lock); | 82 | /* we're under RTNL so all this is fine */ |
83 | if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) { | 83 | if (unlikely(local->reg_state == IEEE80211_DEV_UNREGISTERED)) { |
84 | write_unlock_bh(&local->sub_if_lock); | ||
85 | __ieee80211_if_del(local, sdata); | 84 | __ieee80211_if_del(local, sdata); |
86 | return -ENODEV; | 85 | return -ENODEV; |
87 | } | 86 | } |
88 | list_add(&sdata->list, &local->sub_if_list); | 87 | list_add_tail_rcu(&sdata->list, &local->interfaces); |
88 | |||
89 | if (new_dev) | 89 | if (new_dev) |
90 | *new_dev = ndev; | 90 | *new_dev = ndev; |
91 | write_unlock_bh(&local->sub_if_lock); | ||
92 | 91 | ||
93 | return 0; | 92 | return 0; |
94 | 93 | ||
@@ -226,22 +225,22 @@ void ieee80211_if_reinit(struct net_device *dev) | |||
226 | /* Remove all virtual interfaces that use this BSS | 225 | /* Remove all virtual interfaces that use this BSS |
227 | * as their sdata->bss */ | 226 | * as their sdata->bss */ |
228 | struct ieee80211_sub_if_data *tsdata, *n; | 227 | struct ieee80211_sub_if_data *tsdata, *n; |
229 | LIST_HEAD(tmp_list); | ||
230 | 228 | ||
231 | write_lock_bh(&local->sub_if_lock); | 229 | list_for_each_entry_safe(tsdata, n, &local->interfaces, list) { |
232 | list_for_each_entry_safe(tsdata, n, &local->sub_if_list, list) { | ||
233 | if (tsdata != sdata && tsdata->bss == &sdata->u.ap) { | 230 | if (tsdata != sdata && tsdata->bss == &sdata->u.ap) { |
234 | printk(KERN_DEBUG "%s: removing virtual " | 231 | printk(KERN_DEBUG "%s: removing virtual " |
235 | "interface %s because its BSS interface" | 232 | "interface %s because its BSS interface" |
236 | " is being removed\n", | 233 | " is being removed\n", |
237 | sdata->dev->name, tsdata->dev->name); | 234 | sdata->dev->name, tsdata->dev->name); |
238 | list_move_tail(&tsdata->list, &tmp_list); | 235 | list_del_rcu(&tsdata->list); |
236 | /* | ||
237 | * We have lots of time and can afford | ||
238 | * to sync for each interface | ||
239 | */ | ||
240 | synchronize_rcu(); | ||
241 | __ieee80211_if_del(local, tsdata); | ||
239 | } | 242 | } |
240 | } | 243 | } |
241 | write_unlock_bh(&local->sub_if_lock); | ||
242 | |||
243 | list_for_each_entry_safe(tsdata, n, &tmp_list, list) | ||
244 | __ieee80211_if_del(local, tsdata); | ||
245 | 244 | ||
246 | kfree(sdata->u.ap.beacon_head); | 245 | kfree(sdata->u.ap.beacon_head); |
247 | kfree(sdata->u.ap.beacon_tail); | 246 | kfree(sdata->u.ap.beacon_tail); |
@@ -318,18 +317,16 @@ int ieee80211_if_remove(struct net_device *dev, const char *name, int id) | |||
318 | 317 | ||
319 | ASSERT_RTNL(); | 318 | ASSERT_RTNL(); |
320 | 319 | ||
321 | write_lock_bh(&local->sub_if_lock); | 320 | list_for_each_entry_safe(sdata, n, &local->interfaces, list) { |
322 | list_for_each_entry_safe(sdata, n, &local->sub_if_list, list) { | ||
323 | if ((sdata->type == id || id == -1) && | 321 | if ((sdata->type == id || id == -1) && |
324 | strcmp(name, sdata->dev->name) == 0 && | 322 | strcmp(name, sdata->dev->name) == 0 && |
325 | sdata->dev != local->mdev) { | 323 | sdata->dev != local->mdev) { |
326 | list_del(&sdata->list); | 324 | list_del_rcu(&sdata->list); |
327 | write_unlock_bh(&local->sub_if_lock); | 325 | synchronize_rcu(); |
328 | __ieee80211_if_del(local, sdata); | 326 | __ieee80211_if_del(local, sdata); |
329 | return 0; | 327 | return 0; |
330 | } | 328 | } |
331 | } | 329 | } |
332 | write_unlock_bh(&local->sub_if_lock); | ||
333 | return -ENODEV; | 330 | return -ENODEV; |
334 | } | 331 | } |
335 | 332 | ||