aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211
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
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')
-rw-r--r--net/mac80211/ieee80211.c56
-rw-r--r--net/mac80211/ieee80211_i.h5
-rw-r--r--net/mac80211/ieee80211_iface.c31
-rw-r--r--net/mac80211/ieee80211_sta.c12
-rw-r--r--net/mac80211/rx.c9
-rw-r--r--net/mac80211/tx.c10
6 files changed, 58 insertions, 65 deletions
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index 4e345f82f044..ccf84634f156 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -93,14 +93,13 @@ static int ieee80211_master_open(struct net_device *dev)
93 struct ieee80211_sub_if_data *sdata; 93 struct ieee80211_sub_if_data *sdata;
94 int res = -EOPNOTSUPP; 94 int res = -EOPNOTSUPP;
95 95
96 read_lock(&local->sub_if_lock); 96 /* we hold the RTNL here so can safely walk the list */
97 list_for_each_entry(sdata, &local->sub_if_list, list) { 97 list_for_each_entry(sdata, &local->interfaces, list) {
98 if (sdata->dev != dev && netif_running(sdata->dev)) { 98 if (sdata->dev != dev && netif_running(sdata->dev)) {
99 res = 0; 99 res = 0;
100 break; 100 break;
101 } 101 }
102 } 102 }
103 read_unlock(&local->sub_if_lock);
104 return res; 103 return res;
105} 104}
106 105
@@ -109,11 +108,10 @@ static int ieee80211_master_stop(struct net_device *dev)
109 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); 108 struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
110 struct ieee80211_sub_if_data *sdata; 109 struct ieee80211_sub_if_data *sdata;
111 110
112 read_lock(&local->sub_if_lock); 111 /* we hold the RTNL here so can safely walk the list */
113 list_for_each_entry(sdata, &local->sub_if_list, list) 112 list_for_each_entry(sdata, &local->interfaces, list)
114 if (sdata->dev != dev && netif_running(sdata->dev)) 113 if (sdata->dev != dev && netif_running(sdata->dev))
115 dev_close(sdata->dev); 114 dev_close(sdata->dev);
116 read_unlock(&local->sub_if_lock);
117 115
118 return 0; 116 return 0;
119} 117}
@@ -315,8 +313,8 @@ static int ieee80211_open(struct net_device *dev)
315 313
316 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 314 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
317 315
318 read_lock(&local->sub_if_lock); 316 /* we hold the RTNL here so can safely walk the list */
319 list_for_each_entry(nsdata, &local->sub_if_list, list) { 317 list_for_each_entry(nsdata, &local->interfaces, list) {
320 struct net_device *ndev = nsdata->dev; 318 struct net_device *ndev = nsdata->dev;
321 319
322 if (ndev != dev && ndev != local->mdev && netif_running(ndev) && 320 if (ndev != dev && ndev != local->mdev && netif_running(ndev) &&
@@ -325,10 +323,8 @@ static int ieee80211_open(struct net_device *dev)
325 * check whether it may have the same address 323 * check whether it may have the same address
326 */ 324 */
327 if (!identical_mac_addr_allowed(sdata->type, 325 if (!identical_mac_addr_allowed(sdata->type,
328 nsdata->type)) { 326 nsdata->type))
329 read_unlock(&local->sub_if_lock);
330 return -ENOTUNIQ; 327 return -ENOTUNIQ;
331 }
332 328
333 /* 329 /*
334 * can only add VLANs to enabled APs 330 * can only add VLANs to enabled APs
@@ -339,7 +335,6 @@ static int ieee80211_open(struct net_device *dev)
339 sdata->u.vlan.ap = nsdata; 335 sdata->u.vlan.ap = nsdata;
340 } 336 }
341 } 337 }
342 read_unlock(&local->sub_if_lock);
343 338
344 switch (sdata->type) { 339 switch (sdata->type) {
345 case IEEE80211_IF_TYPE_WDS: 340 case IEEE80211_IF_TYPE_WDS:
@@ -466,14 +461,13 @@ static int ieee80211_stop(struct net_device *dev)
466 sdata->u.sta.state = IEEE80211_DISABLED; 461 sdata->u.sta.state = IEEE80211_DISABLED;
467 del_timer_sync(&sdata->u.sta.timer); 462 del_timer_sync(&sdata->u.sta.timer);
468 /* 463 /*
469 * Holding the sub_if_lock for writing here blocks 464 * When we get here, the interface is marked down.
470 * out the receive path and makes sure it's not 465 * Call synchronize_rcu() to wait for the RX path
471 * currently processing a packet that may get 466 * should it be using the interface and enqueuing
472 * added to the queue. 467 * frames at this very time on another CPU.
473 */ 468 */
474 write_lock_bh(&local->sub_if_lock); 469 synchronize_rcu();
475 skb_queue_purge(&sdata->u.sta.skb_queue); 470 skb_queue_purge(&sdata->u.sta.skb_queue);
476 write_unlock_bh(&local->sub_if_lock);
477 471
478 if (!local->ops->hw_scan && 472 if (!local->ops->hw_scan &&
479 local->scan_dev == sdata->dev) { 473 local->scan_dev == sdata->dev) {
@@ -1033,9 +1027,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
1033 1027
1034 rthdr->data_retries = status->retry_count; 1028 rthdr->data_retries = status->retry_count;
1035 1029
1036 read_lock(&local->sub_if_lock); 1030 rcu_read_lock();
1037 monitors = local->monitors; 1031 monitors = local->monitors;
1038 list_for_each_entry(sdata, &local->sub_if_list, list) { 1032 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
1039 /* 1033 /*
1040 * Using the monitors counter is possibly racy, but 1034 * Using the monitors counter is possibly racy, but
1041 * if the value is wrong we simply either clone the skb 1035 * if the value is wrong we simply either clone the skb
@@ -1051,7 +1045,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
1051 continue; 1045 continue;
1052 monitors--; 1046 monitors--;
1053 if (monitors) 1047 if (monitors)
1054 skb2 = skb_clone(skb, GFP_KERNEL); 1048 skb2 = skb_clone(skb, GFP_ATOMIC);
1055 else 1049 else
1056 skb2 = NULL; 1050 skb2 = NULL;
1057 skb->dev = sdata->dev; 1051 skb->dev = sdata->dev;
@@ -1066,7 +1060,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb,
1066 } 1060 }
1067 } 1061 }
1068 out: 1062 out:
1069 read_unlock(&local->sub_if_lock); 1063 rcu_read_unlock();
1070 if (skb) 1064 if (skb)
1071 dev_kfree_skb(skb); 1065 dev_kfree_skb(skb);
1072} 1066}
@@ -1154,8 +1148,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
1154 1148
1155 INIT_LIST_HEAD(&local->modes_list); 1149 INIT_LIST_HEAD(&local->modes_list);
1156 1150
1157 rwlock_init(&local->sub_if_lock); 1151 INIT_LIST_HEAD(&local->interfaces);
1158 INIT_LIST_HEAD(&local->sub_if_list);
1159 1152
1160 INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work); 1153 INIT_DELAYED_WORK(&local->scan_work, ieee80211_sta_scan_work);
1161 ieee80211_rx_bss_list_init(mdev); 1154 ieee80211_rx_bss_list_init(mdev);
@@ -1175,7 +1168,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
1175 sdata->u.ap.force_unicast_rateidx = -1; 1168 sdata->u.ap.force_unicast_rateidx = -1;
1176 sdata->u.ap.max_ratectrl_rateidx = -1; 1169 sdata->u.ap.max_ratectrl_rateidx = -1;
1177 ieee80211_if_sdata_init(sdata); 1170 ieee80211_if_sdata_init(sdata);
1178 list_add_tail(&sdata->list, &local->sub_if_list); 1171 /* no RCU needed since we're still during init phase */
1172 list_add_tail(&sdata->list, &local->interfaces);
1179 1173
1180 tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending, 1174 tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
1181 (unsigned long)local); 1175 (unsigned long)local);
@@ -1334,7 +1328,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
1334{ 1328{
1335 struct ieee80211_local *local = hw_to_local(hw); 1329 struct ieee80211_local *local = hw_to_local(hw);
1336 struct ieee80211_sub_if_data *sdata, *tmp; 1330 struct ieee80211_sub_if_data *sdata, *tmp;
1337 struct list_head tmp_list;
1338 int i; 1331 int i;
1339 1332
1340 tasklet_kill(&local->tx_pending_tasklet); 1333 tasklet_kill(&local->tx_pending_tasklet);
@@ -1348,11 +1341,12 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
1348 if (local->apdev) 1341 if (local->apdev)
1349 ieee80211_if_del_mgmt(local); 1342 ieee80211_if_del_mgmt(local);
1350 1343
1351 write_lock_bh(&local->sub_if_lock); 1344 /*
1352 list_replace_init(&local->sub_if_list, &tmp_list); 1345 * At this point, interface list manipulations are fine
1353 write_unlock_bh(&local->sub_if_lock); 1346 * because the driver cannot be handing us frames any
1354 1347 * more and the tasklet is killed.
1355 list_for_each_entry_safe(sdata, tmp, &tmp_list, list) 1348 */
1349 list_for_each_entry_safe(sdata, tmp, &local->interfaces, list)
1356 __ieee80211_if_del(local, sdata); 1350 __ieee80211_if_del(local, sdata);
1357 1351
1358 rtnl_unlock(); 1352 rtnl_unlock();
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 1a43f3e9b6bd..a5961f16f206 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -475,9 +475,8 @@ struct ieee80211_local {
475 ieee80211_rx_handler *rx_handlers; 475 ieee80211_rx_handler *rx_handlers;
476 ieee80211_tx_handler *tx_handlers; 476 ieee80211_tx_handler *tx_handlers;
477 477
478 rwlock_t sub_if_lock; /* Protects sub_if_list. Cannot be taken under 478 struct list_head interfaces;
479 * sta_bss_lock or sta_lock. */ 479
480 struct list_head sub_if_list;
481 int sta_scanning; 480 int sta_scanning;
482 int scan_channel_idx; 481 int scan_channel_idx;
483 enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state; 482 enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
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
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 17455c6a5229..651aaba1add4 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -2676,8 +2676,8 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
2676 memset(&wrqu, 0, sizeof(wrqu)); 2676 memset(&wrqu, 0, sizeof(wrqu));
2677 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL); 2677 wireless_send_event(dev, SIOCGIWSCAN, &wrqu, NULL);
2678 2678
2679 read_lock(&local->sub_if_lock); 2679 rcu_read_lock();
2680 list_for_each_entry(sdata, &local->sub_if_list, list) { 2680 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2681 2681
2682 /* No need to wake the master device. */ 2682 /* No need to wake the master device. */
2683 if (sdata->dev == local->mdev) 2683 if (sdata->dev == local->mdev)
@@ -2691,7 +2691,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
2691 2691
2692 netif_wake_queue(sdata->dev); 2692 netif_wake_queue(sdata->dev);
2693 } 2693 }
2694 read_unlock(&local->sub_if_lock); 2694 rcu_read_unlock();
2695 2695
2696 sdata = IEEE80211_DEV_TO_SUB_IF(dev); 2696 sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2697 if (sdata->type == IEEE80211_IF_TYPE_IBSS) { 2697 if (sdata->type == IEEE80211_IF_TYPE_IBSS) {
@@ -2828,8 +2828,8 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
2828 2828
2829 local->sta_scanning = 1; 2829 local->sta_scanning = 1;
2830 2830
2831 read_lock(&local->sub_if_lock); 2831 rcu_read_lock();
2832 list_for_each_entry(sdata, &local->sub_if_list, list) { 2832 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
2833 2833
2834 /* Don't stop the master interface, otherwise we can't transmit 2834 /* Don't stop the master interface, otherwise we can't transmit
2835 * probes! */ 2835 * probes! */
@@ -2841,7 +2841,7 @@ static int ieee80211_sta_start_scan(struct net_device *dev,
2841 (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED)) 2841 (sdata->u.sta.flags & IEEE80211_STA_ASSOCIATED))
2842 ieee80211_send_nullfunc(local, sdata, 1); 2842 ieee80211_send_nullfunc(local, sdata, 1);
2843 } 2843 }
2844 read_unlock(&local->sub_if_lock); 2844 rcu_read_unlock();
2845 2845
2846 if (ssid) { 2846 if (ssid) {
2847 local->scan_ssid_len = ssid_len; 2847 local->scan_ssid_len = ssid_len;
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 2535d8d4ce90..cb44a9db0e19 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1383,8 +1383,9 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
1383 } 1383 }
1384 1384
1385 /* 1385 /*
1386 * key references are protected using RCU and this requires that 1386 * key references and virtual interfaces are protected using RCU
1387 * we are in a read-site RCU section during receive processing 1387 * and this requires that we are in a read-side RCU section during
1388 * receive processing
1388 */ 1389 */
1389 rcu_read_lock(); 1390 rcu_read_lock();
1390 1391
@@ -1439,8 +1440,7 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
1439 1440
1440 bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len); 1441 bssid = ieee80211_get_bssid(hdr, skb->len - radiotap_len);
1441 1442
1442 read_lock(&local->sub_if_lock); 1443 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
1443 list_for_each_entry(sdata, &local->sub_if_list, list) {
1444 rx.flags |= IEEE80211_TXRXD_RXRA_MATCH; 1444 rx.flags |= IEEE80211_TXRXD_RXRA_MATCH;
1445 1445
1446 if (!netif_running(sdata->dev)) 1446 if (!netif_running(sdata->dev))
@@ -1493,7 +1493,6 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
1493 &rx, sta); 1493 &rx, sta);
1494 } else 1494 } else
1495 dev_kfree_skb(skb); 1495 dev_kfree_skb(skb);
1496 read_unlock(&local->sub_if_lock);
1497 1496
1498 end: 1497 end:
1499 rcu_read_unlock(); 1498 rcu_read_unlock();
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 38394c40f6ad..244c80d0c8fb 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -295,8 +295,12 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
295 struct ieee80211_sub_if_data *sdata; 295 struct ieee80211_sub_if_data *sdata;
296 struct sta_info *sta; 296 struct sta_info *sta;
297 297
298 read_lock(&local->sub_if_lock); 298 /*
299 list_for_each_entry(sdata, &local->sub_if_list, list) { 299 * virtual interfaces are protected by RCU
300 */
301 rcu_read_lock();
302
303 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
300 struct ieee80211_if_ap *ap; 304 struct ieee80211_if_ap *ap;
301 if (sdata->dev == local->mdev || 305 if (sdata->dev == local->mdev ||
302 sdata->type != IEEE80211_IF_TYPE_AP) 306 sdata->type != IEEE80211_IF_TYPE_AP)
@@ -309,7 +313,7 @@ static void purge_old_ps_buffers(struct ieee80211_local *local)
309 } 313 }
310 total += skb_queue_len(&ap->ps_bc_buf); 314 total += skb_queue_len(&ap->ps_bc_buf);
311 } 315 }
312 read_unlock(&local->sub_if_lock); 316 rcu_read_unlock();
313 317
314 read_lock_bh(&local->sta_lock); 318 read_lock_bh(&local->sta_lock);
315 list_for_each_entry(sta, &local->sta_list, list) { 319 list_for_each_entry(sta, &local->sta_list, list) {