mac80211: split TX aggregation stop action
authorJohannes Berg <johannes.berg@intel.com>
Wed, 18 Jul 2012 11:51:25 +0000 (13:51 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 3 Jan 2013 12:01:42 +0000 (13:01 +0100)
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: Johannes Berg <johannes.berg@intel.com>
13 files changed:
drivers/net/wireless/ath/ath9k/htc_drv_main.c
drivers/net/wireless/ath/ath9k/main.c
drivers/net/wireless/ath/carl9170/main.c
drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
drivers/net/wireless/iwlegacy/4965-mac.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rtlwifi/core.c
drivers/net/wireless/ti/wlcore/main.c
include/net/mac80211.h
net/mac80211/agg-tx.c

index 9c07a8fa5134bb4611b93b9645c20fd0e0c414e3..a8016d70088aa3d492d15ff76f6f16f8f3c2df2f 100644 (file)
@@ -1628,7 +1628,9 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                if (!ret)
                        ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                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);
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                break;
index be30a9af152884d8e073b7a3be5b79e6b3d5a8ee..e1fa70596e613520c5d83edb72eda81b331c36c0 100644 (file)
@@ -1610,7 +1610,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                        ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                ath9k_ps_restore(sc);
                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);
                ath_tx_aggr_stop(sc, sta, tid);
                ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
index 25a1e2f4f73862fb9bc26f6310d87bb3d51b6cf5..9d2051aeb782f44a8b54c75c2f1fd3018f96756f 100644 (file)
@@ -1394,7 +1394,9 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                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();
                tid_info = rcu_dereference(sta_info->agg[tid]);
                if (tid_info) {
index 1fbd8ecbe2ea293a28d6a65648fe011a2fff3fa6..f0fc8cd4d5df9a821f07f94cd8ad930fa8bfe012 100644 (file)
@@ -668,7 +668,9 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw,
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                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);
                brcms_c_ampdu_flush(wl->wlc, sta, tid);
                spin_unlock_bh(&wl->lock);
index c3fbf6717564a3295ea2a87ecdc669e80d2e9d89..6a86ed45835d034412e73b6a38ca182fc4d2f70f 100644 (file)
@@ -5968,7 +5968,9 @@ il4965_mac_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                D_HT("start Tx\n");
                ret = il4965_tx_agg_start(il, vif, sta, tid, ssn);
                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");
                ret = il4965_tx_agg_stop(il, vif, sta, tid);
                if (test_bit(S_EXIT_PENDING, &il->status))
index 3163e0f38c25109a3c5f5a2e7c71f39f37b40eda..02fdcea76b212dca9633facf76f10fabbb53c621 100644 (file)
@@ -679,7 +679,9 @@ static int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                IWL_DEBUG_HT(priv, "start Tx\n");
                ret = iwlagn_tx_agg_start(priv, vif, sta, tid, ssn);
                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");
                ret = iwlagn_tx_agg_stop(priv, vif, sta, tid);
                if ((ret == 0) && (priv->agg_tids_count > 0)) {
index 145498f8411f658be538985943c96c3b6995f718..d248a4cc665645c1aa8c59e0296d5499f596450a 100644 (file)
@@ -1312,7 +1312,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
        case IEEE80211_AMPDU_TX_START:
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                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);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
index f221b95b90b3c2935c67e9c272d5f1c2a89000d4..19b46fdf9f0faa560f890b41b2bb1b2041ef7cf7 100644 (file)
@@ -5170,7 +5170,9 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                }
                ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid);
                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->state == AMPDU_STREAM_ACTIVE) {
                                spin_unlock(&priv->stream_lock);
index 197b4466a5d2a44378f998fdefa2c221ae6124c6..12f93e42e1606143b9e1f7d0cf0d064eacff4ebe 100644 (file)
@@ -5484,7 +5484,9 @@ int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
        case IEEE80211_AMPDU_TX_START:
                ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
                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);
                break;
        case IEEE80211_AMPDU_TX_OPERATIONAL:
index be33aa14c8afaa6672497d156abebb4b3cd146f9..d3ce9fbef00ee0b8d3e88eefcfd7111d9d0080ae 100644 (file)
@@ -879,7 +879,9 @@ static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                         "IEEE80211_AMPDU_TX_START: TID:%d\n", tid);
                return rtl_tx_agg_start(hw, sta, tid, ssn);
                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,
                         "IEEE80211_AMPDU_TX_STOP: TID:%d\n", tid);
                return rtl_tx_agg_stop(hw, sta, tid);
index ea9d8e011bc9d45f5414b43cb6d1e3af492a89c3..d7de06359ae1f71478bdbf47ba5b9140c9083eb3 100644 (file)
@@ -4575,7 +4575,9 @@ static int wl1271_op_ampdu_action(struct ieee80211_hw *hw,
         * Falling break here on purpose for all TX APDU commands.
         */
        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:
                ret = -EINVAL;
                break;
index 0978b0faa88061d62839d8f778f737f387509746..a464f4fb36a004e7a77838de8d1e253ca6bb5f11 100644 (file)
@@ -2033,17 +2033,29 @@ enum ieee80211_filter_flags {
  * calling ieee80211_start_tx_ba_cb_irqsafe, because the peer
  * might receive the addBA frame and send a delBA right away!
  *
- * @IEEE80211_AMPDU_RX_START: start Rx aggregation
- * @IEEE80211_AMPDU_RX_STOP: stop Rx aggregation
- * @IEEE80211_AMPDU_TX_START: start Tx aggregation
- * @IEEE80211_AMPDU_TX_STOP: stop Tx aggregation
+ * @IEEE80211_AMPDU_RX_START: start RX aggregation
+ * @IEEE80211_AMPDU_RX_STOP: stop RX aggregation
+ * @IEEE80211_AMPDU_TX_START: start TX aggregation
  * @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 {
        IEEE80211_AMPDU_RX_START,
        IEEE80211_AMPDU_RX_STOP,
        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,
 };
 
index dda8d7df4b54134c1c2c44064ea87405ecaf50d9..2f0ccbc5f13e82ee104ccd40b48cf9391162fbb3 100644 (file)
@@ -257,10 +257,25 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 {
        struct ieee80211_local *local = sta->local;
        struct tid_ampdu_tx *tid_tx;
+       enum ieee80211_ampdu_mlme_action action;
        int ret;
 
        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);
 
        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,
                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)) {
                spin_unlock_bh(&sta->lock);
-               return -EALREADY;
+               if (reason != AGG_STOP_DESTROY_STA)
+                       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)) {
@@ -319,8 +343,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
                                        WLAN_BACK_INITIATOR;
        tid_tx->tx_stop = reason == AGG_STOP_LOCAL_REQUEST;
 
-       ret = drv_ampdu_action(local, sta->sdata,
-                              IEEE80211_AMPDU_TX_STOP,
+       ret = drv_ampdu_action(local, sta->sdata, action,
                               &sta->sta, tid, NULL, 0);
 
        /* HW shall not deny going back to legacy */
@@ -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;
 }
 
 /*