aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/ieee80211_iface.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2007-09-18 17:29:21 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-10-10 19:53:00 -0400
commit79010420cc3f78eab911598bfdd29c4b06a83e1f (patch)
treea9031164d7944f8aa90a455d297780b241f3d865 /net/mac80211/ieee80211_iface.c
parentea49c359f36d5b40bf033c45a08332cb73777aa2 (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.c31
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