mac80211: track receiver's aggregation reorder buffer size
authorJohannes Berg <johannes.berg@intel.com>
Tue, 18 Jan 2011 12:51:05 +0000 (13:51 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 19 Jan 2011 16:36:11 +0000 (11:36 -0500)
The aggregation code currently doesn't implement the
buffer size negotiation. It will always request a max
buffer size (which is fine, if a little pointless, as
the mac80211 code doesn't know and might just use 0
instead), but if the peer requests a smaller size it
isn't possible to honour this request.

In order to fix this, look at the buffer size in the
addBA response frame, keep track of it and pass it to
the driver in the ampdu_action callback when called
with the IEEE80211_AMPDU_TX_OPERATIONAL action. That
way the driver can limit the number of subframes in
aggregates appropriately.

Note that this doesn't fix any drivers apart from the
addition of the new argument -- they all need to be
updated separately to use this variable!

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
17 files changed:
drivers/net/wireless/ath/ar9170/main.c
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/iwlwifi/iwl-agn.c
drivers/net/wireless/iwlwifi/iwl-agn.h
drivers/net/wireless/mac80211_hwsim.c
drivers/net/wireless/mwl8k.c
drivers/net/wireless/rt2x00/rt2800lib.c
drivers/net/wireless/rt2x00/rt2800lib.h
drivers/net/wireless/rtlwifi/core.c
include/net/mac80211.h
net/mac80211/agg-rx.c
net/mac80211/agg-tx.c
net/mac80211/driver-ops.h
net/mac80211/driver-trace.h
net/mac80211/sta_info.h

index 32bf79e6a320ff2b04722b8a17d9cd7b38088303..a9111e1161fd22d37be4a8b8de1e84d985484c64 100644 (file)
@@ -1945,7 +1945,8 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue,
 static int ar9170_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                              u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
index 187af5b4440d2eaef14d010186e572c23d6e2b28..f14f37d29f45fd06ed1490a9a14f8f314b2fe09b 100644 (file)
@@ -1549,7 +1549,7 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif,
                                  enum ieee80211_ampdu_mlme_action action,
                                  struct ieee80211_sta *sta,
-                                 u16 tid, u16 *ssn)
+                                 u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ath9k_htc_priv *priv = hw->priv;
        struct ath9k_htc_sta *ista;
index 174c016ef89dcb10b6a04016bf2e09c67b164c45..c03184e7bffeb9bc57fe426b998d3793f31304d7 100644 (file)
@@ -2165,7 +2165,7 @@ static int ath9k_ampdu_action(struct ieee80211_hw *hw,
                              struct ieee80211_vif *vif,
                              enum ieee80211_ampdu_mlme_action action,
                              struct ieee80211_sta *sta,
-                             u16 tid, u16 *ssn)
+                             u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ath_wiphy *aphy = hw->priv;
        struct ath_softc *sc = aphy->sc;
index 870df8c42622bab94db2aab3da880ad61381bb16..ecfb80b059d1e80393da2cd9d13aa72e8ea36fe6 100644 (file)
@@ -1279,7 +1279,7 @@ static int carl9170_op_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    enum ieee80211_ampdu_mlme_action action,
                                    struct ieee80211_sta *sta,
-                                   u16 tid, u16 *ssn)
+                                   u16 tid, u16 *ssn, u8 buf_size)
 {
        struct ar9170 *ar = hw->priv;
        struct carl9170_sta_info *sta_info = (void *) sta->drv_priv;
index 36335b1b54d4fb989ed3157c75caea1fb1444c88..8b045a401d62032de0af8e2c1fa00a4281e746df 100644 (file)
@@ -3393,7 +3393,8 @@ int iwlagn_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
 int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size)
 {
        struct iwl_priv *priv = hw->priv;
        int ret = -EINVAL;
index da303585f801851037bc8397f98c52bd70d36d06..822221a97e801ad04039746136bcda217aca4a7f 100644 (file)
@@ -349,7 +349,8 @@ void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw,
 int iwlagn_mac_ampdu_action(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size);
 int iwlagn_mac_sta_add(struct ieee80211_hw *hw,
                       struct ieee80211_vif *vif,
                       struct ieee80211_sta *sta);
index 454f045ddff33118525f93142e53349622e015c3..5d39b2840584f28963b575cc2b4c850b25263f55 100644 (file)
@@ -943,7 +943,8 @@ static int mac80211_hwsim_testmode_cmd(struct ieee80211_hw *hw,
 static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
                                       struct ieee80211_vif *vif,
                                       enum ieee80211_ampdu_mlme_action action,
-                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                                      struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                                      u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_TX_START:
index 809f2bf27958c43af6f862f677051c5d89d6357a..106b427d0064424b82f7771f035b9e6cc3ead2d6 100644 (file)
@@ -4356,7 +4356,8 @@ static int mwl8k_get_survey(struct ieee80211_hw *hw, int idx,
 static int
 mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                   enum ieee80211_ampdu_mlme_action action,
-                  struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                  struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                  u8 buf_size)
 {
        switch (action) {
        case IEEE80211_AMPDU_RX_START:
index a25be625ee90aa8f1eef3fc23cd8e280b0e740db..f8ba01cbc6ddf4a0e36fa0467c1bec58b1b4833c 100644 (file)
@@ -3533,7 +3533,8 @@ EXPORT_SYMBOL_GPL(rt2800_get_tsf);
 
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn)
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size)
 {
        int ret = 0;
 
index e3c995a9dec4b7c7f1afcfaf011c1e568b811696..3efafb78ff771082a90a212744f7f6b4315eb5b3 100644 (file)
@@ -198,7 +198,8 @@ int rt2800_conf_tx(struct ieee80211_hw *hw, u16 queue_idx,
 u64 rt2800_get_tsf(struct ieee80211_hw *hw);
 int rt2800_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
                        enum ieee80211_ampdu_mlme_action action,
-                       struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                       u8 buf_size);
 int rt2800_get_survey(struct ieee80211_hw *hw, int idx,
                      struct survey_info *survey);
 
index d6a924a056549a43dba381e19537de76c6e1daae..25d2d667ffba60654dab5aeeb3edd1cdc8a4e850 100644 (file)
@@ -748,7 +748,8 @@ static void rtl_op_sta_notify(struct ieee80211_hw *hw,
 static int rtl_op_ampdu_action(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               enum ieee80211_ampdu_mlme_action action,
-                              struct ieee80211_sta *sta, u16 tid, u16 * ssn)
+                              struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                              u8 buf_size)
 {
        struct rtl_priv *rtlpriv = rtl_priv(hw);
 
index d024fc563e7bc16dab2b605d9c297158086917af..5afe341b40102597c07fda72b87b126bc22c4972 100644 (file)
@@ -1731,6 +1731,10 @@ enum ieee80211_ampdu_mlme_action {
  *     ieee80211_ampdu_mlme_action. Starting sequence number (@ssn)
  *     is the first frame we expect to perform the action on. Notice
  *     that TX/RX_STOP can pass NULL for this parameter.
+ *     The @buf_size parameter is only valid when the action is set to
+ *     %IEEE80211_AMPDU_TX_OPERATIONAL and indicates the peer's reorder
+ *     buffer size (number of subframes) for this session -- aggregates
+ *     containing more subframes than this may not be transmitted to the peer.
  *     Returns a negative error code on failure.
  *     The callback can sleep.
  *
@@ -1833,7 +1837,8 @@ struct ieee80211_ops {
        int (*ampdu_action)(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,
                            enum ieee80211_ampdu_mlme_action action,
-                           struct ieee80211_sta *sta, u16 tid, u16 *ssn);
+                           struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+                           u8 buf_size);
        int (*get_survey)(struct ieee80211_hw *hw, int idx,
                struct survey_info *survey);
        void (*rfkill_poll)(struct ieee80211_hw *hw);
index 002db5e86eb6e51a79412ed42893d7b9472aec6b..1f51f41624264387d276ccfe2d3bc1fa4428bf4e 100644 (file)
@@ -76,7 +76,7 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid,
 #endif /* CONFIG_MAC80211_HT_DEBUG */
 
        if (drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_STOP,
-                            &sta->sta, tid, NULL))
+                            &sta->sta, tid, NULL, 0))
                printk(KERN_DEBUG "HW problem - can not stop rx "
                                "aggregation for tid %d\n", tid);
 
@@ -297,7 +297,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local,
        }
 
        ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START,
-                              &sta->sta, tid, &start_seq_num);
+                              &sta->sta, tid, &start_seq_num, 0);
 #ifdef CONFIG_MAC80211_HT_DEBUG
        printk(KERN_DEBUG "Rx A-MPDU request on tid %d result %d\n", tid, ret);
 #endif /* CONFIG_MAC80211_HT_DEBUG */
index 9cc472c6a6a5497dcadea0af44fe011f14c2f5a7..42f7c900733102f191d86802eaad7a1e77d32c7b 100644 (file)
@@ -190,7 +190,7 @@ int ___ieee80211_stop_tx_ba_session(struct sta_info *sta, u16 tid,
 
        ret = drv_ampdu_action(local, sta->sdata,
                               IEEE80211_AMPDU_TX_STOP,
-                              &sta->sta, tid, NULL);
+                              &sta->sta, tid, NULL, 0);
 
        /* HW shall not deny going back to legacy */
        if (WARN_ON(ret)) {
@@ -311,7 +311,7 @@ void ieee80211_tx_ba_session_handle_start(struct sta_info *sta, int tid)
        start_seq_num = sta->tid_seq[tid] >> 4;
 
        ret = drv_ampdu_action(local, sdata, IEEE80211_AMPDU_TX_START,
-                              &sta->sta, tid, &start_seq_num);
+                              &sta->sta, tid, &start_seq_num, 0);
        if (ret) {
 #ifdef CONFIG_MAC80211_HT_DEBUG
                printk(KERN_DEBUG "BA request denied - HW unavailable for"
@@ -487,7 +487,8 @@ static void ieee80211_agg_tx_operational(struct ieee80211_local *local,
 
        drv_ampdu_action(local, sta->sdata,
                         IEEE80211_AMPDU_TX_OPERATIONAL,
-                        &sta->sta, tid, NULL);
+                        &sta->sta, tid, NULL,
+                        sta->ampdu_mlme.tid_tx[tid]->buf_size);
 
        /*
         * synchronize with TX path, while splicing the TX path
@@ -742,9 +743,11 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
 {
        struct tid_ampdu_tx *tid_tx;
        u16 capab, tid;
+       u8 buf_size;
 
        capab = le16_to_cpu(mgmt->u.action.u.addba_resp.capab);
        tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2;
+       buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6;
 
        mutex_lock(&sta->ampdu_mlme.mtx);
 
@@ -767,12 +770,23 @@ void ieee80211_process_addba_resp(struct ieee80211_local *local,
 
        if (le16_to_cpu(mgmt->u.action.u.addba_resp.status)
                        == WLAN_STATUS_SUCCESS) {
+               /*
+                * IEEE 802.11-2007 7.3.1.14:
+                * In an ADDBA Response frame, when the Status Code field
+                * is set to 0, the Buffer Size subfield is set to a value
+                * of at least 1.
+                */
+               if (!buf_size)
+                       goto out;
+
                if (test_and_set_bit(HT_AGG_STATE_RESPONSE_RECEIVED,
                                     &tid_tx->state)) {
                        /* ignore duplicate response */
                        goto out;
                }
 
+               tid_tx->buf_size = buf_size;
+
                if (test_bit(HT_AGG_STATE_DRV_READY, &tid_tx->state))
                        ieee80211_agg_tx_operational(local, sta, tid);
 
index 98d589960a4913c96f3b717f9b6a997215c9484d..78af32d4bc582bc053d58c14ad14509d9123e9cd 100644 (file)
@@ -382,17 +382,17 @@ static inline int drv_ampdu_action(struct ieee80211_local *local,
                                   struct ieee80211_sub_if_data *sdata,
                                   enum ieee80211_ampdu_mlme_action action,
                                   struct ieee80211_sta *sta, u16 tid,
-                                  u16 *ssn)
+                                  u16 *ssn, u8 buf_size)
 {
        int ret = -EOPNOTSUPP;
 
        might_sleep();
 
-       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn);
+       trace_drv_ampdu_action(local, sdata, action, sta, tid, ssn, buf_size);
 
        if (local->ops->ampdu_action)
                ret = local->ops->ampdu_action(&local->hw, &sdata->vif, action,
-                                              sta, tid, ssn);
+                                              sta, tid, ssn, buf_size);
 
        trace_drv_return_int(local, ret);
 
index 49c84218b2f41036bf34a28c6b2c02f5580803ed..fbabbc2f181a373ff9e7f6c65f4e5c6c7f2e8630 100644 (file)
@@ -784,9 +784,9 @@ TRACE_EVENT(drv_ampdu_action,
                 struct ieee80211_sub_if_data *sdata,
                 enum ieee80211_ampdu_mlme_action action,
                 struct ieee80211_sta *sta, u16 tid,
-                u16 *ssn),
+                u16 *ssn, u8 buf_size),
 
-       TP_ARGS(local, sdata, action, sta, tid, ssn),
+       TP_ARGS(local, sdata, action, sta, tid, ssn, buf_size),
 
        TP_STRUCT__entry(
                LOCAL_ENTRY
@@ -794,6 +794,7 @@ TRACE_EVENT(drv_ampdu_action,
                __field(u32, action)
                __field(u16, tid)
                __field(u16, ssn)
+               __field(u8, buf_size)
                VIF_ENTRY
        ),
 
@@ -804,11 +805,13 @@ TRACE_EVENT(drv_ampdu_action,
                __entry->action = action;
                __entry->tid = tid;
                __entry->ssn = ssn ? *ssn : 0;
+               __entry->buf_size = buf_size;
        ),
 
        TP_printk(
-               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d",
-               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action, __entry->tid
+               LOCAL_PR_FMT VIF_PR_FMT STA_PR_FMT " action:%d tid:%d buf:%d",
+               LOCAL_PR_ARG, VIF_PR_ARG, STA_PR_ARG, __entry->action,
+               __entry->tid, __entry->buf_size
        )
 );
 
index bbdd2a86a94b2ad4eb0863f5a1be8a110c9c456e..ca0b69060ef758c0aea41fde531042e14caf8040 100644 (file)
@@ -82,6 +82,7 @@ enum ieee80211_sta_info_flags {
  * @state: session state (see above)
  * @stop_initiator: initiator of a session stop
  * @tx_stop: TX DelBA frame when stopping
+ * @buf_size: reorder buffer size at receiver
  *
  * This structure's lifetime is managed by RCU, assignments to
  * the array holding it must hold the aggregation mutex.
@@ -101,6 +102,7 @@ struct tid_ampdu_tx {
        u8 dialog_token;
        u8 stop_initiator;
        bool tx_stop;
+       u8 buf_size;
 };
 
 /**