diff options
author | Johannes Berg <johannes.berg@intel.com> | 2010-11-16 14:51:38 -0500 |
---|---|---|
committer | Wey-Yi Guy <wey-yi.w.guy@intel.com> | 2010-11-24 19:58:15 -0500 |
commit | 2e34034e8c9755ff144379d410d5227926e91cce (patch) | |
tree | dce8a3273b64bc95627d200364617b2466e4cee2 /drivers | |
parent | 67158b67cea0c92dba1dda74cde926d149cc1a2e (diff) |
iwlagn: fix station powersave accounting for aggregation
Since aggregation queues are station-specific, the
device will not reject packets in them but rather
will stop the appropriate aggregation queues when
a station goes to sleep. I forgot to account for
this in the driver, so if a station went to sleep
that had aggregation enabled, traffic would stop
indefinitely.
Fix this by only accounting frames queued on the
normal AC queues for associated station.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 31 |
1 files changed, 25 insertions, 6 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index e8bd0b311946..c114c3a704f8 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |||
@@ -531,6 +531,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
531 | u8 tid = 0; | 531 | u8 tid = 0; |
532 | u8 *qc = NULL; | 532 | u8 *qc = NULL; |
533 | unsigned long flags; | 533 | unsigned long flags; |
534 | bool is_agg = false; | ||
534 | 535 | ||
535 | if (info->control.vif) | 536 | if (info->control.vif) |
536 | ctx = iwl_rxon_ctx_from_vif(info->control.vif); | 537 | ctx = iwl_rxon_ctx_from_vif(info->control.vif); |
@@ -616,6 +617,7 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
616 | if (info->flags & IEEE80211_TX_CTL_AMPDU && | 617 | if (info->flags & IEEE80211_TX_CTL_AMPDU && |
617 | priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) { | 618 | priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) { |
618 | txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; | 619 | txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; |
620 | is_agg = true; | ||
619 | } | 621 | } |
620 | } | 622 | } |
621 | 623 | ||
@@ -763,8 +765,14 @@ int iwlagn_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) | |||
763 | * whether or not we should update the write pointer. | 765 | * whether or not we should update the write pointer. |
764 | */ | 766 | */ |
765 | 767 | ||
766 | /* avoid atomic ops if it isn't an associated client */ | 768 | /* |
767 | if (sta_priv && sta_priv->client) | 769 | * Avoid atomic ops if it isn't an associated client. |
770 | * Also, if this is a packet for aggregation, don't | ||
771 | * increase the counter because the ucode will stop | ||
772 | * aggregation queues when their respective station | ||
773 | * goes to sleep. | ||
774 | */ | ||
775 | if (sta_priv && sta_priv->client && !is_agg) | ||
768 | atomic_inc(&sta_priv->pending_frames); | 776 | atomic_inc(&sta_priv->pending_frames); |
769 | 777 | ||
770 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { | 778 | if ((iwl_queue_space(q) < q->high_mark) && priv->mac80211_registered) { |
@@ -1143,14 +1151,15 @@ int iwlagn_txq_check_empty(struct iwl_priv *priv, | |||
1143 | return 0; | 1151 | return 0; |
1144 | } | 1152 | } |
1145 | 1153 | ||
1146 | static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info) | 1154 | static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, |
1155 | struct iwl_rxon_context *ctx, | ||
1156 | const u8 *addr1) | ||
1147 | { | 1157 | { |
1148 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data; | ||
1149 | struct ieee80211_sta *sta; | 1158 | struct ieee80211_sta *sta; |
1150 | struct iwl_station_priv *sta_priv; | 1159 | struct iwl_station_priv *sta_priv; |
1151 | 1160 | ||
1152 | rcu_read_lock(); | 1161 | rcu_read_lock(); |
1153 | sta = ieee80211_find_sta(tx_info->ctx->vif, hdr->addr1); | 1162 | sta = ieee80211_find_sta(ctx->vif, addr1); |
1154 | if (sta) { | 1163 | if (sta) { |
1155 | sta_priv = (void *)sta->drv_priv; | 1164 | sta_priv = (void *)sta->drv_priv; |
1156 | /* avoid atomic ops if this isn't a client */ | 1165 | /* avoid atomic ops if this isn't a client */ |
@@ -1159,6 +1168,15 @@ static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info) | |||
1159 | ieee80211_sta_block_awake(priv->hw, sta, false); | 1168 | ieee80211_sta_block_awake(priv->hw, sta, false); |
1160 | } | 1169 | } |
1161 | rcu_read_unlock(); | 1170 | rcu_read_unlock(); |
1171 | } | ||
1172 | |||
1173 | static void iwlagn_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info, | ||
1174 | bool is_agg) | ||
1175 | { | ||
1176 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data; | ||
1177 | |||
1178 | if (!is_agg) | ||
1179 | iwlagn_non_agg_tx_status(priv, tx_info->ctx, hdr->addr1); | ||
1162 | 1180 | ||
1163 | ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb); | 1181 | ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb); |
1164 | } | 1182 | } |
@@ -1183,7 +1201,8 @@ int iwlagn_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) | |||
1183 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { | 1201 | q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { |
1184 | 1202 | ||
1185 | tx_info = &txq->txb[txq->q.read_ptr]; | 1203 | tx_info = &txq->txb[txq->q.read_ptr]; |
1186 | iwlagn_tx_status(priv, tx_info); | 1204 | iwlagn_tx_status(priv, tx_info, |
1205 | txq_id >= IWLAGN_FIRST_AMPDU_QUEUE); | ||
1187 | 1206 | ||
1188 | hdr = (struct ieee80211_hdr *)tx_info->skb->data; | 1207 | hdr = (struct ieee80211_hdr *)tx_info->skb->data; |
1189 | if (hdr && ieee80211_is_data_qos(hdr->frame_control)) | 1208 | if (hdr && ieee80211_is_data_qos(hdr->frame_control)) |