diff options
author | Christian Lamparter <chunkeey@googlemail.com> | 2010-10-29 23:41:16 +0200 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2010-11-15 13:25:33 -0500 |
commit | 2a6cef513fab525399e484edc9bfb39b6d462f76 (patch) | |
tree | 3397cf07534825de557e53630eafda5ebc2193b8 | |
parent | e4a668c59080f862af3ecc28b359533027cbe434 (diff) |
carl9170: stop stale uplink BA sessions
This patch fixes a possible lengthy stall if the device
is operating as an experimental 11n AP and an STA
[during heavy txrx action] suddenly signalized to go
off-channel (old NetworkManager), or (sleep - which is
unlikely, because then it wouldn't be *active* at all!?).
Because the driver has to manage the BA Window, the
sudden PSM transition can leave active uplink BA
sessions to the STA in a bad state and a proper
cleanup is needed.
Signed-off-by: Christian Lamparter <chunkeey@googlemail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/carl9170/tx.c | 54 |
1 files changed, 54 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index b575c865142..b27969c4181 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -524,6 +524,59 @@ next: } } +static void carl9170_tx_ampdu_timeout(struct ar9170 *ar) +{ + struct carl9170_sta_tid *iter; + struct sk_buff *skb; + struct ieee80211_tx_info *txinfo; + struct carl9170_tx_info *arinfo; + struct _carl9170_tx_superframe *super; + struct ieee80211_sta *sta; + struct ieee80211_vif *vif; + struct ieee80211_hdr *hdr; + unsigned int vif_id; + + rcu_read_lock(); + list_for_each_entry_rcu(iter, &ar->tx_ampdu_list, list) { + if (iter->state < CARL9170_TID_STATE_IDLE) + continue; + + spin_lock_bh(&iter->lock); + skb = skb_peek(&iter->queue); + if (!skb) + goto unlock; + + txinfo = IEEE80211_SKB_CB(skb); + arinfo = (void *)txinfo->rate_driver_data; + if (time_is_after_jiffies(arinfo->timeout + + msecs_to_jiffies(CARL9170_QUEUE_TIMEOUT))) + goto unlock; + + super = (void *) skb->data; + hdr = (void *) super->frame_data; + + vif_id = (super->s.misc & CARL9170_TX_SUPER_MISC_VIF_ID) >> + CARL9170_TX_SUPER_MISC_VIF_ID_S; + + if (WARN_ON(vif_id >= AR9170_MAX_VIRTUAL_MAC)) + goto unlock; + + vif = rcu_dereference(ar->vif_priv[vif_id].vif); + if (WARN_ON(!vif)) + goto unlock; + + sta = ieee80211_find_sta(vif, hdr->addr1); + if (WARN_ON(!sta)) + goto unlock; + + ieee80211_stop_tx_ba_session(sta, iter->tid); +unlock: + spin_unlock_bh(&iter->lock); + + } + rcu_read_unlock(); +} + void carl9170_tx_janitor(struct work_struct *work) { struct ar9170 *ar = container_of(work, struct ar9170, @@ -534,6 +587,7 @@ void carl9170_tx_janitor(struct work_struct *work) ar->tx_janitor_last_run = jiffies; carl9170_check_queue_stop_timeout(ar); + carl9170_tx_ampdu_timeout(ar); if (!atomic_read(&ar->tx_total_queued)) return; |