summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-05-27 10:32:27 -0400
committerJohannes Berg <johannes.berg@intel.com>2014-06-23 05:05:25 -0400
commit5ac2e35030113ed881ce9ad413d80f13ffe5b5a0 (patch)
tree81533719d9cf8b661c8eaf34ba69cce511cbc239 /net
parent20edb50e593dca7522b4f3a95b801dbf99f24c52 (diff)
mac80211: fix station/driver powersave race
It is currently possible to have a race due to the station PS unblock work like this: * station goes to sleep with frames buffered in the driver * driver blocks wakeup * station wakes up again * driver flushes/returns frames, and unblocks, which schedules the unblock work * unblock work starts to run, and checks that the station is awake (i.e. that the WLAN_STA_PS_STA flag isn't set) * we process a received frame with PM=1, setting the flag again * ieee80211_sta_ps_deliver_wakeup() runs, delivering all frames to the driver, and then clearing the WLAN_STA_PS_DRIVER and WLAN_STA_PS_STA flags In this scenario, mac80211 will think that the station is awake, while it really is asleep, and any TX'ed frames should be filtered by the device (it will know that the station is sleeping) but then passed to mac80211 again, which will not buffer it either as it thinks the station is awake, and eventually the packets will be dropped. Fix this by moving the clearing of the flags to exactly where we learn about the situation. This creates a problem of reordering, so introduce another flag indicating that delivery is being done, this new flag also queues frames and is cleared only while the spinlock is held (which the queuing code also holds) so that any concurrent delivery/TX is handled correctly. Reported-by: Andrei Otcheretianski <andrei.otcheretianski@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/rx.c2
-rw-r--r--net/mac80211/sta_info.c65
-rw-r--r--net/mac80211/sta_info.h7
-rw-r--r--net/mac80211/tx.c6
4 files changed, 51 insertions, 29 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 394e201cde6d..5f572bed1761 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1107,6 +1107,8 @@ static void sta_ps_end(struct sta_info *sta)
1107 return; 1107 return;
1108 } 1108 }
1109 1109
1110 set_sta_flag(sta, WLAN_STA_PS_DELIVER);
1111 clear_sta_flag(sta, WLAN_STA_PS_STA);
1110 ieee80211_sta_ps_deliver_wakeup(sta); 1112 ieee80211_sta_ps_deliver_wakeup(sta);
1111} 1113}
1112 1114
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a9b46d8ea22f..ae7c16ad5f22 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -100,7 +100,8 @@ static void __cleanup_single_sta(struct sta_info *sta)
100 struct ps_data *ps; 100 struct ps_data *ps;
101 101
102 if (test_sta_flag(sta, WLAN_STA_PS_STA) || 102 if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
103 test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { 103 test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
104 test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
104 if (sta->sdata->vif.type == NL80211_IFTYPE_AP || 105 if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
105 sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) 106 sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
106 ps = &sdata->bss->ps; 107 ps = &sdata->bss->ps;
@@ -111,6 +112,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
111 112
112 clear_sta_flag(sta, WLAN_STA_PS_STA); 113 clear_sta_flag(sta, WLAN_STA_PS_STA);
113 clear_sta_flag(sta, WLAN_STA_PS_DRIVER); 114 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
115 clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
114 116
115 atomic_dec(&ps->num_sta_ps); 117 atomic_dec(&ps->num_sta_ps);
116 sta_info_recalc_tim(sta); 118 sta_info_recalc_tim(sta);
@@ -125,7 +127,7 @@ static void __cleanup_single_sta(struct sta_info *sta)
125 if (ieee80211_vif_is_mesh(&sdata->vif)) 127 if (ieee80211_vif_is_mesh(&sdata->vif))
126 mesh_sta_cleanup(sta); 128 mesh_sta_cleanup(sta);
127 129
128 cancel_work_sync(&sta->drv_unblock_wk); 130 cancel_work_sync(&sta->drv_deliver_wk);
129 131
130 /* 132 /*
131 * Destroy aggregation state here. It would be nice to wait for the 133 * Destroy aggregation state here. It would be nice to wait for the
@@ -253,33 +255,23 @@ static void sta_info_hash_add(struct ieee80211_local *local,
253 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta); 255 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
254} 256}
255 257
256static void sta_unblock(struct work_struct *wk) 258static void sta_deliver_ps_frames(struct work_struct *wk)
257{ 259{
258 struct sta_info *sta; 260 struct sta_info *sta;
259 261
260 sta = container_of(wk, struct sta_info, drv_unblock_wk); 262 sta = container_of(wk, struct sta_info, drv_deliver_wk);
261 263
262 if (sta->dead) 264 if (sta->dead)
263 return; 265 return;
264 266
265 if (!test_sta_flag(sta, WLAN_STA_PS_STA)) { 267 local_bh_disable();
266 local_bh_disable(); 268 if (!test_sta_flag(sta, WLAN_STA_PS_STA))
267 ieee80211_sta_ps_deliver_wakeup(sta); 269 ieee80211_sta_ps_deliver_wakeup(sta);
268 local_bh_enable(); 270 else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL))
269 } else if (test_and_clear_sta_flag(sta, WLAN_STA_PSPOLL)) {
270 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
271
272 local_bh_disable();
273 ieee80211_sta_ps_deliver_poll_response(sta); 271 ieee80211_sta_ps_deliver_poll_response(sta);
274 local_bh_enable(); 272 else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD))
275 } else if (test_and_clear_sta_flag(sta, WLAN_STA_UAPSD)) {
276 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
277
278 local_bh_disable();
279 ieee80211_sta_ps_deliver_uapsd(sta); 273 ieee80211_sta_ps_deliver_uapsd(sta);
280 local_bh_enable(); 274 local_bh_enable();
281 } else
282 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
283} 275}
284 276
285static int sta_prepare_rate_control(struct ieee80211_local *local, 277static int sta_prepare_rate_control(struct ieee80211_local *local,
@@ -341,7 +333,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
341 333
342 spin_lock_init(&sta->lock); 334 spin_lock_init(&sta->lock);
343 spin_lock_init(&sta->ps_lock); 335 spin_lock_init(&sta->ps_lock);
344 INIT_WORK(&sta->drv_unblock_wk, sta_unblock); 336 INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
345 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); 337 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
346 mutex_init(&sta->ampdu_mlme.mtx); 338 mutex_init(&sta->ampdu_mlme.mtx);
347#ifdef CONFIG_MAC80211_MESH 339#ifdef CONFIG_MAC80211_MESH
@@ -1141,8 +1133,15 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
1141 } 1133 }
1142 1134
1143 ieee80211_add_pending_skbs(local, &pending); 1135 ieee80211_add_pending_skbs(local, &pending);
1144 clear_sta_flag(sta, WLAN_STA_PS_DRIVER); 1136
1145 clear_sta_flag(sta, WLAN_STA_PS_STA); 1137 /* now we're no longer in the deliver code */
1138 clear_sta_flag(sta, WLAN_STA_PS_DELIVER);
1139
1140 /* The station might have polled and then woken up before we responded,
1141 * so clear these flags now to avoid them sticking around.
1142 */
1143 clear_sta_flag(sta, WLAN_STA_PSPOLL);
1144 clear_sta_flag(sta, WLAN_STA_UAPSD);
1146 spin_unlock(&sta->ps_lock); 1145 spin_unlock(&sta->ps_lock);
1147 1146
1148 atomic_dec(&ps->num_sta_ps); 1147 atomic_dec(&ps->num_sta_ps);
@@ -1543,10 +1542,26 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
1543 1542
1544 trace_api_sta_block_awake(sta->local, pubsta, block); 1543 trace_api_sta_block_awake(sta->local, pubsta, block);
1545 1544
1546 if (block) 1545 if (block) {
1547 set_sta_flag(sta, WLAN_STA_PS_DRIVER); 1546 set_sta_flag(sta, WLAN_STA_PS_DRIVER);
1548 else if (test_sta_flag(sta, WLAN_STA_PS_DRIVER)) 1547 return;
1549 ieee80211_queue_work(hw, &sta->drv_unblock_wk); 1548 }
1549
1550 if (!test_sta_flag(sta, WLAN_STA_PS_DRIVER))
1551 return;
1552
1553 if (!test_sta_flag(sta, WLAN_STA_PS_STA)) {
1554 set_sta_flag(sta, WLAN_STA_PS_DELIVER);
1555 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
1556 ieee80211_queue_work(hw, &sta->drv_deliver_wk);
1557 } else if (test_sta_flag(sta, WLAN_STA_PSPOLL) ||
1558 test_sta_flag(sta, WLAN_STA_UAPSD)) {
1559 /* must be asleep in this case */
1560 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
1561 ieee80211_queue_work(hw, &sta->drv_deliver_wk);
1562 } else {
1563 clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
1564 }
1550} 1565}
1551EXPORT_SYMBOL(ieee80211_sta_block_awake); 1566EXPORT_SYMBOL(ieee80211_sta_block_awake);
1552 1567
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 4acc5fc402fa..dee0b645b34c 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -58,6 +58,8 @@
58 * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid. 58 * @WLAN_STA_TOFFSET_KNOWN: toffset calculated for this station is valid.
59 * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period. 59 * @WLAN_STA_MPSP_OWNER: local STA is owner of a mesh Peer Service Period.
60 * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP. 60 * @WLAN_STA_MPSP_RECIPIENT: local STA is recipient of a MPSP.
61 * @WLAN_STA_PS_DELIVER: station woke up, but we're still blocking TX
62 * until pending frames are delivered
61 */ 63 */
62enum ieee80211_sta_info_flags { 64enum ieee80211_sta_info_flags {
63 WLAN_STA_AUTH, 65 WLAN_STA_AUTH,
@@ -82,6 +84,7 @@ enum ieee80211_sta_info_flags {
82 WLAN_STA_TOFFSET_KNOWN, 84 WLAN_STA_TOFFSET_KNOWN,
83 WLAN_STA_MPSP_OWNER, 85 WLAN_STA_MPSP_OWNER,
84 WLAN_STA_MPSP_RECIPIENT, 86 WLAN_STA_MPSP_RECIPIENT,
87 WLAN_STA_PS_DELIVER,
85}; 88};
86 89
87#define ADDBA_RESP_INTERVAL HZ 90#define ADDBA_RESP_INTERVAL HZ
@@ -265,7 +268,7 @@ struct ieee80211_tx_latency_stat {
265 * @last_rx_rate_vht_nss: rx status nss of last data packet 268 * @last_rx_rate_vht_nss: rx status nss of last data packet
266 * @lock: used for locking all fields that require locking, see comments 269 * @lock: used for locking all fields that require locking, see comments
267 * in the header file. 270 * in the header file.
268 * @drv_unblock_wk: used for driver PS unblocking 271 * @drv_deliver_wk: used for delivering frames after driver PS unblocking
269 * @listen_interval: listen interval of this station, when we're acting as AP 272 * @listen_interval: listen interval of this station, when we're acting as AP
270 * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly 273 * @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
271 * @ps_lock: used for powersave (when mac80211 is the AP) related locking 274 * @ps_lock: used for powersave (when mac80211 is the AP) related locking
@@ -345,7 +348,7 @@ struct sta_info {
345 void *rate_ctrl_priv; 348 void *rate_ctrl_priv;
346 spinlock_t lock; 349 spinlock_t lock;
347 350
348 struct work_struct drv_unblock_wk; 351 struct work_struct drv_deliver_wk;
349 352
350 u16 listen_interval; 353 u16 listen_interval;
351 354
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 5214686d9fd1..8170d9945d6d 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -469,7 +469,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
469 return TX_CONTINUE; 469 return TX_CONTINUE;
470 470
471 if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) || 471 if (unlikely((test_sta_flag(sta, WLAN_STA_PS_STA) ||
472 test_sta_flag(sta, WLAN_STA_PS_DRIVER)) && 472 test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
473 test_sta_flag(sta, WLAN_STA_PS_DELIVER)) &&
473 !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) { 474 !(info->flags & IEEE80211_TX_CTL_NO_PS_BUFFER))) {
474 int ac = skb_get_queue_mapping(tx->skb); 475 int ac = skb_get_queue_mapping(tx->skb);
475 476
@@ -486,7 +487,8 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
486 * ahead and Tx the packet. 487 * ahead and Tx the packet.
487 */ 488 */
488 if (!test_sta_flag(sta, WLAN_STA_PS_STA) && 489 if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
489 !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) { 490 !test_sta_flag(sta, WLAN_STA_PS_DRIVER) &&
491 !test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
490 spin_unlock(&sta->ps_lock); 492 spin_unlock(&sta->ps_lock);
491 return TX_CONTINUE; 493 return TX_CONTINUE;
492 } 494 }