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 | |
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')
-rw-r--r-- | net/mac80211/ieee80211.c | 56 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 5 | ||||
-rw-r--r-- | net/mac80211/ieee80211_iface.c | 31 | ||||
-rw-r--r-- | net/mac80211/ieee80211_sta.c | 12 | ||||
-rw-r--r-- | net/mac80211/rx.c | 9 | ||||
-rw-r--r-- | net/mac80211/tx.c | 10 |
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) { |