summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 }