Commit 18b559d5 authored by Johannes Berg's avatar Johannes Berg

mac80211: split TX aggregation stop action

When TX aggregation is stopped, there are a few
different cases:
 - connection with the peer was dropped
 - session stop was requested locally
 - session stop was requested by the peer
 - connection was dropped while a session is stopping

The behaviour in these cases should be different, if
the connection is dropped then the driver should drop
all frames, otherwise the frames may continue to be
transmitted, aggregated in the case of a locally
requested session stop or unaggregated in the case of
the peer requesting session stop.

Split these different cases so that the driver can
act accordingly; however, treat local and remote stop
the same way and ask the driver to not send frames as
aggregated packets any more.

In the case of connection drop, the stop callback the
driver is otherwise supposed to call is no longer
required.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 30bf5f1f
...@@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, ...@@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
if (!ret) if (!ret)
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid); ath9k_htc_tx_aggr_oper(priv, vif, sta, action, tid);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
......
...@@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw, ...@@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
ath9k_ps_restore(sc); ath9k_ps_restore(sc);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ath9k_ps_wakeup(sc); ath9k_ps_wakeup(sc);
ath_tx_aggr_stop(sc, sta, tid); ath_tx_aggr_stop(sc, sta, tid);
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
......
...@@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw, ...@@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
rcu_read_lock(); rcu_read_lock();
tid_info = rcu_dereference(sta_info->agg[tid]); tid_info = rcu_dereference(sta_info->agg[tid]);
if (tid_info) { if (tid_info) {
......
...@@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, ...@@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
spin_lock_bh(&wl->lock); spin_lock_bh(&wl->lock);
brcms_c_ampdu_flush(wl->wlc, sta, tid); brcms_c_ampdu_flush(wl->wlc, sta, tid);
spin_unlock_bh(&wl->lock); spin_unlock_bh(&wl->lock);
......
...@@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
D_HT("start Tx\n"); D_HT("start Tx\n");
ret = il4965_tx_agg_start(il, vif, sta, tid, ssn); ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
D_HT("stop Tx\n"); D_HT("stop Tx\n");
ret = il4965_tx_agg_stop(il, vif, sta, tid); ret = il4965_tx_agg_stop(il, vif, sta, tid);
if (test_bit(S_EXIT_PENDING, &il->status)) if (test_bit(S_EXIT_PENDING, &il->status))
......
...@@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw, ...@@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
IWL_DEBUG_HT(priv, "start Tx\n"); IWL_DEBUG_HT(priv, "start Tx\n");
ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn); ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
IWL_DEBUG_HT(priv, "stop Tx\n"); IWL_DEBUG_HT(priv, "stop Tx\n");
ret = iwlagn_tx_agg_stop(priv, vif, sta, tid); ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
if ((ret == 0) && (priv->agg_tids_count > 0)) { if ((ret == 0) && (priv->agg_tids_count > 0)) {
......
...@@ -1312,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw, ...@@ -1312,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_OPERATIONAL: case IEEE80211_AMPDU_TX_OPERATIONAL:
......
...@@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
} }
ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
if (stream) { if (stream) {
if (stream->state == AMPDU_STREAM_ACTIVE) { if (stream->state == AMPDU_STREAM_ACTIVE) {
spin_unlock(&priv->stream_lock); spin_unlock(&priv->stream_lock);
......
...@@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, ...@@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
break; break;
case IEEE80211_AMPDU_TX_OPERATIONAL: case IEEE80211_AMPDU_TX_OPERATIONAL:
......
...@@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw, ...@@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
"IEEE80211_AMPDU_TX_START: TID:%d\n", tid); "IEEE80211_AMPDU_TX_START: TID:%d\n", tid);
return rtl_tx_agg_start(hw, sta, tid, ssn); return rtl_tx_agg_start(hw, sta, tid, ssn);
break; break;
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE,
"IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid); "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid);
return rtl_tx_agg_stop(hw, sta, tid); return rtl_tx_agg_stop(hw, sta, tid);
......
...@@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw, ...@@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
* Falling break here on purpose for all TX APDU commands. * Falling break here on purpose for all TX APDU commands.
*/ */
case IEEE80211_AMPDU_TX_START: case IEEE80211_AMPDU_TX_START:
case IEEE80211_AMPDU_TX_STOP: case IEEE80211_AMPDU_TX_STOP_CONT:
case IEEE80211_AMPDU_TX_STOP_FLUSH:
case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT:
case IEEE80211_AMPDU_TX_OPERATIONAL: case IEEE80211_AMPDU_TX_OPERATIONAL:
ret = -EINVAL; ret = -EINVAL;
break; break;
......
...@@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags { ...@@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags {
* calling ieee80211_start_tx_ba_cb_irqsafe, because the peer * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer
* might receive the addBA frame and send a delBA right away! * might receive the addBA frame and send a delBA right away!
* *
* @IEEE80211_AMPDU_RX_START: start Rx aggregation * @IEEE80211_AMPDU_RX_START: start RX aggregation
* @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
* @IEEE80211_AMPDU_TX_START: start Tx aggregation * @IEEE80211_AMPDU_TX_START: start TX aggregation
* @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
* @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational * @IEEE80211_AMPDU_TX_OPERATIONAL: TX aggregation has become operational
* @IEEE80211_AMPDU_TX_STOP_CONT: stop TX aggregation but continue transmitting
* queued packets, now unaggregated. After all packets are transmitted the
* driver has to call ieee80211_stop_tx_ba_cb_irqsafe().
* @IEEE80211_AMPDU_TX_STOP_FLUSH: stop TX aggregation and flush all packets,
* called when the station is removed. There's no need or reason to call
* ieee80211_stop_tx_ba_cb_irqsafe() in this case as mac80211 assumes the
* session is gone and removes the station.
* @IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: called when TX aggregation is stopped
* but the driver hasn't called ieee80211_stop_tx_ba_cb_irqsafe() yet and
* now the connection is dropped and the station will be removed. Drivers
* should clean up and drop remaining packets when this is called.
*/ */
enum ieee80211_ampdu_mlme_action { enum ieee80211_ampdu_mlme_action {
IEEE80211_AMPDU_RX_START, IEEE80211_AMPDU_RX_START,
IEEE80211_AMPDU_RX_STOP, IEEE80211_AMPDU_RX_STOP,
IEEE80211_AMPDU_TX_START, IEEE80211_AMPDU_TX_START,
IEEE80211_AMPDU_TX_STOP, IEEE80211_AMPDU_TX_STOP_CONT,
IEEE80211_AMPDU_TX_STOP_FLUSH,
IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
IEEE80211_AMPDU_TX_OPERATIONAL, IEEE80211_AMPDU_TX_OPERATIONAL,
}; };
......
...@@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, ...@@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
{ {
struct ieee80211_local *local = sta->local; struct ieee80211_local *local = sta->local;
struct tid_ampdu_tx *tid_tx; struct tid_ampdu_tx *tid_tx;
enum ieee80211_ampdu_mlme_action action;
int ret; int ret;
lockdep_assert_held(&sta->ampdu_mlme.mtx); lockdep_assert_held(&sta->ampdu_mlme.mtx);
switch (reason) {
case AGG_STOP_DECLINED:
case AGG_STOP_LOCAL_REQUEST:
case AGG_STOP_PEER_REQUEST:
action = IEEE80211_AMPDU_TX_STOP_CONT;
break;
case AGG_STOP_DESTROY_STA:
action = IEEE80211_AMPDU_TX_STOP_FLUSH;
break;
default:
WARN_ON_ONCE(1);
return -EINVAL;
}
spin_lock_bh(&sta->lock); spin_lock_bh(&sta->lock);
tid_tx = rcu_dereference_protected_tid_tx(sta, tid); tid_tx = rcu_dereference_protected_tid_tx(sta, tid);
...@@ -269,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, ...@@ -269,10 +284,19 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
return -ENOENT; return -ENOENT;
} }
/* if we're already stopping ignore any new requests to stop */ /*
* if we're already stopping ignore any new requests to stop
* unless we're destroying it in which case notify the driver
*/
if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) { if (test_bit(HT_AGG_STATE_STOPPING, &tid_tx->state)) {
spin_unlock_bh(&sta->lock); spin_unlock_bh(&sta->lock);
if (reason != AGG_STOP_DESTROY_STA)
return -EALREADY; return -EALREADY;
ret = drv_ampdu_action(local, sta->sdata,
IEEE80211_AMPDU_TX_STOP_FLUSH_CONT,
&sta->sta, tid, NULL, 0);
WARN_ON_ONCE(ret);
goto remove_tid_tx;
} }
if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) { if (test_bit(HT_AGG_STATE_WANT_START, &tid_tx->state)) {
...@@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, ...@@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
WLAN_BACK_INITIATOR; WLAN_BACK_INITIATOR;
tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST; tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
ret = drv_ampdu_action(local, sta->sdata, ret = drv_ampdu_action(local, sta->sdata, action,
IEEE80211_AMPDU_TX_STOP,
&sta->sta, tid, NULL, 0); &sta->sta, tid, NULL, 0);
/* HW shall not deny going back to legacy */ /* HW shall not deny going back to legacy */
...@@ -331,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid, ...@@ -331,7 +354,14 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
*/ */
} }
return ret; if (reason == AGG_STOP_DESTROY_STA) {
remove_tid_tx:
spin_lock_bh(&sta->lock);
ieee80211_remove_tid_tx(sta, tid);
spin_unlock_bh(&sta->lock);
}
return 0;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment