diff options
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-agn-tx.c | 28 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-debugfs.c | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-shared.h | 11 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | 17 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | 31 |
5 files changed, 54 insertions, 38 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 1cac701c4182..69d0f9972988 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c | |||
@@ -772,7 +772,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, | |||
772 | struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; | 772 | struct iwlagn_tx_resp *tx_resp = (void *)&pkt->u.raw[0]; |
773 | struct ieee80211_hdr *hdr; | 773 | struct ieee80211_hdr *hdr; |
774 | u32 status = le16_to_cpu(tx_resp->status.status); | 774 | u32 status = le16_to_cpu(tx_resp->status.status); |
775 | u32 ssn = iwlagn_get_scd_ssn(tx_resp); | 775 | u16 ssn = iwlagn_get_scd_ssn(tx_resp); |
776 | int tid; | 776 | int tid; |
777 | int sta_id; | 777 | int sta_id; |
778 | int freed; | 778 | int freed; |
@@ -794,8 +794,31 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb, | |||
794 | iwl_rx_reply_tx_agg(priv, tx_resp); | 794 | iwl_rx_reply_tx_agg(priv, tx_resp); |
795 | 795 | ||
796 | if (tx_resp->frame_count == 1) { | 796 | if (tx_resp->frame_count == 1) { |
797 | IWL_DEBUG_TX_REPLY(priv, "Q %d, ssn %d", txq_id, ssn); | 797 | u16 next_reclaimed = le16_to_cpu(tx_resp->seq_ctl); |
798 | next_reclaimed = SEQ_TO_SN(next_reclaimed + 0x10); | ||
799 | |||
800 | if (is_agg) { | ||
801 | /* If this is an aggregation queue, we can rely on the | ||
802 | * ssn since the wifi sequence number corresponds to | ||
803 | * the index in the TFD ring (%256). | ||
804 | * The seq_ctl is the sequence control of the packet | ||
805 | * to which this Tx response relates. But if there is a | ||
806 | * hole in the bitmap of the BA we received, this Tx | ||
807 | * response may allow to reclaim the hole and all the | ||
808 | * subsequent packets that were already acked. | ||
809 | * In that case, seq_ctl != ssn, and the next packet | ||
810 | * to be reclaimed will be ssn and not seq_ctl. | ||
811 | */ | ||
812 | next_reclaimed = ssn; | ||
813 | } | ||
814 | |||
798 | __skb_queue_head_init(&skbs); | 815 | __skb_queue_head_init(&skbs); |
816 | priv->shrd->tid_data[sta_id][tid].next_reclaimed = | ||
817 | next_reclaimed; | ||
818 | |||
819 | IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d", | ||
820 | next_reclaimed); | ||
821 | |||
799 | /*we can free until ssn % q.n_bd not inclusive */ | 822 | /*we can free until ssn % q.n_bd not inclusive */ |
800 | iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, | 823 | iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, |
801 | ssn, status, &skbs); | 824 | ssn, status, &skbs); |
@@ -951,6 +974,7 @@ int iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, | |||
951 | /* Release all TFDs before the SSN, i.e. all TFDs in front of | 974 | /* Release all TFDs before the SSN, i.e. all TFDs in front of |
952 | * block-ack window (we assume that they've been successfully | 975 | * block-ack window (we assume that they've been successfully |
953 | * transmitted ... if not, it's too late anyway). */ | 976 | * transmitted ... if not, it's too late anyway). */ |
977 | priv->shrd->tid_data[sta_id][tid].next_reclaimed = ba_resp_scd_ssn; | ||
954 | iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn, | 978 | iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn, |
955 | 0, &reclaimed_skbs); | 979 | 0, &reclaimed_skbs); |
956 | freed = 0; | 980 | freed = 0; |
diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index 074068e78320..7c5114da4f6f 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c | |||
@@ -372,15 +372,14 @@ static ssize_t iwl_dbgfs_stations_read(struct file *file, char __user *user_buf, | |||
372 | i, station->sta.sta.addr, | 372 | i, station->sta.sta.addr, |
373 | station->sta.station_flags_msk); | 373 | station->sta.station_flags_msk); |
374 | pos += scnprintf(buf + pos, bufsz - pos, | 374 | pos += scnprintf(buf + pos, bufsz - pos, |
375 | "TID\tseq_num\ttxq_id\ttfds\trate_n_flags\n"); | 375 | "TID\tseq_num\ttxq_id\trate_n_flags\n"); |
376 | 376 | ||
377 | for (j = 0; j < IWL_MAX_TID_COUNT; j++) { | 377 | for (j = 0; j < IWL_MAX_TID_COUNT; j++) { |
378 | tid_data = &priv->shrd->tid_data[i][j]; | 378 | tid_data = &priv->shrd->tid_data[i][j]; |
379 | pos += scnprintf(buf + pos, bufsz - pos, | 379 | pos += scnprintf(buf + pos, bufsz - pos, |
380 | "%d:\t%#x\t%#x\t%u\t%#x", | 380 | "%d:\t%#x\t%#x\t%#x", |
381 | j, tid_data->seq_number, | 381 | j, tid_data->seq_number, |
382 | tid_data->agg.txq_id, | 382 | tid_data->agg.txq_id, |
383 | tid_data->tfds_in_queue, | ||
384 | tid_data->agg.rate_n_flags); | 383 | tid_data->agg.rate_n_flags); |
385 | 384 | ||
386 | if (tid_data->agg.wait_for_ba) | 385 | if (tid_data->agg.wait_for_ba) |
diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index df6d2123fe4f..8a9690714c13 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h | |||
@@ -231,12 +231,17 @@ enum iwl_agg_state { | |||
231 | * @state: state of the BA agreement establishment / tear down. | 231 | * @state: state of the BA agreement establishment / tear down. |
232 | * @txq_id: Tx queue used by the BA session - used by the transport layer. | 232 | * @txq_id: Tx queue used by the BA session - used by the transport layer. |
233 | * Needed by the upper layer for debugfs only. | 233 | * Needed by the upper layer for debugfs only. |
234 | * @ssn: the first packet to be sent in AGG HW queue in Tx AGG start flow, or | ||
235 | * the first packet to be sent in legacy HW queue in Tx AGG stop flow. | ||
236 | * Basically when next_reclaimed reaches ssn, we can tell mac80211 that | ||
237 | * we are ready to finish the Tx AGG stop / start flow. | ||
234 | * @wait_for_ba: Expect block-ack before next Tx reply | 238 | * @wait_for_ba: Expect block-ack before next Tx reply |
235 | */ | 239 | */ |
236 | struct iwl_ht_agg { | 240 | struct iwl_ht_agg { |
237 | u32 rate_n_flags; | 241 | u32 rate_n_flags; |
238 | enum iwl_agg_state state; | 242 | enum iwl_agg_state state; |
239 | u16 txq_id; | 243 | u16 txq_id; |
244 | u16 ssn; | ||
240 | bool wait_for_ba; | 245 | bool wait_for_ba; |
241 | }; | 246 | }; |
242 | 247 | ||
@@ -246,13 +251,13 @@ struct iwl_ht_agg { | |||
246 | * This structs holds the states for each RA / TID. | 251 | * This structs holds the states for each RA / TID. |
247 | 252 | ||
248 | * @seq_number: the next WiFi sequence number to use | 253 | * @seq_number: the next WiFi sequence number to use |
249 | * @tfds_in_queue: number of packets sent to the HW queues. | 254 | * @next_reclaimed: the WiFi sequence number of the next packet to be acked. |
250 | * Exported for debugfs only | 255 | * This is basically (last acked packet++). |
251 | * @agg: aggregation state machine | 256 | * @agg: aggregation state machine |
252 | */ | 257 | */ |
253 | struct iwl_tid_data { | 258 | struct iwl_tid_data { |
254 | u16 seq_number; | 259 | u16 seq_number; |
255 | u16 tfds_in_queue; | 260 | u16 next_reclaimed; |
256 | struct iwl_ht_agg agg; | 261 | struct iwl_ht_agg agg; |
257 | }; | 262 | }; |
258 | 263 | ||
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c index 79331fb10aa5..1b1077cc3534 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie-tx.c | |||
@@ -555,18 +555,22 @@ int iwl_trans_pcie_tx_agg_alloc(struct iwl_trans *trans, | |||
555 | 555 | ||
556 | spin_lock_irqsave(&trans->shrd->sta_lock, flags); | 556 | spin_lock_irqsave(&trans->shrd->sta_lock, flags); |
557 | tid_data = &trans->shrd->tid_data[sta_id][tid]; | 557 | tid_data = &trans->shrd->tid_data[sta_id][tid]; |
558 | *ssn = SEQ_TO_SN(tid_data->seq_number); | ||
559 | tid_data->agg.txq_id = txq_id; | 558 | tid_data->agg.txq_id = txq_id; |
559 | tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); | ||
560 | |||
561 | *ssn = tid_data->agg.ssn; | ||
560 | iwl_set_swq_id(&trans_pcie->txq[txq_id], get_ac_from_tid(tid), txq_id); | 562 | iwl_set_swq_id(&trans_pcie->txq[txq_id], get_ac_from_tid(tid), txq_id); |
561 | 563 | ||
562 | if (tid_data->tfds_in_queue == 0) { | 564 | if (*ssn == tid_data->next_reclaimed) { |
563 | IWL_DEBUG_TX_QUEUES(trans, "HW queue is empty\n"); | 565 | IWL_DEBUG_TX_QUEUES(trans, "Proceed: ssn = next_recl = %d", |
566 | tid_data->agg.ssn); | ||
564 | tid_data->agg.state = IWL_AGG_ON; | 567 | tid_data->agg.state = IWL_AGG_ON; |
565 | iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid); | 568 | iwl_start_tx_ba_trans_ready(priv(trans), ctx, sta_id, tid); |
566 | } else { | 569 | } else { |
567 | IWL_DEBUG_TX_QUEUES(trans, | 570 | IWL_DEBUG_TX_QUEUES(trans, "Can't proceed: ssn %d, " |
568 | "HW queue is NOT empty: %d packets in HW" | 571 | "next_recl = %d", |
569 | " queue\n", tid_data->tfds_in_queue); | 572 | tid_data->agg.ssn, |
573 | tid_data->next_reclaimed); | ||
570 | tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; | 574 | tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; |
571 | } | 575 | } |
572 | spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); | 576 | spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); |
@@ -647,6 +651,7 @@ int iwl_trans_pcie_tx_agg_disable(struct iwl_trans *trans, | |||
647 | "Stopping a non empty AGG HW QUEUE\n"); | 651 | "Stopping a non empty AGG HW QUEUE\n"); |
648 | trans->shrd->tid_data[sta_id][tid].agg.state = | 652 | trans->shrd->tid_data[sta_id][tid].agg.state = |
649 | IWL_EMPTYING_HW_QUEUE_DELBA; | 653 | IWL_EMPTYING_HW_QUEUE_DELBA; |
654 | tid_data->agg.ssn = SEQ_TO_SN(tid_data->seq_number); | ||
650 | spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); | 655 | spin_unlock_irqrestore(&trans->shrd->sta_lock, flags); |
651 | return 0; | 656 | return 0; |
652 | } | 657 | } |
diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c index 66e1b9fa0b8b..4d318431270b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-pcie.c | |||
@@ -1109,7 +1109,7 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1109 | IWL_ERR(trans, "sta_id = %d, tid = %d " | 1109 | IWL_ERR(trans, "sta_id = %d, tid = %d " |
1110 | "txq_id = %d, seq_num = %d", sta_id, | 1110 | "txq_id = %d, seq_num = %d", sta_id, |
1111 | tid, tid_data->agg.txq_id, | 1111 | tid, tid_data->agg.txq_id, |
1112 | seq_number >> 4); | 1112 | SEQ_TO_SN(seq_number)); |
1113 | } | 1113 | } |
1114 | txq_id = tid_data->agg.txq_id; | 1114 | txq_id = tid_data->agg.txq_id; |
1115 | is_agg = true; | 1115 | is_agg = true; |
@@ -1222,12 +1222,10 @@ static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, | |||
1222 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); | 1222 | q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); |
1223 | iwl_txq_update_write_ptr(trans, txq); | 1223 | iwl_txq_update_write_ptr(trans, txq); |
1224 | 1224 | ||
1225 | if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc)) { | 1225 | if (ieee80211_is_data_qos(fc) && !ieee80211_is_qos_nullfunc(fc) && |
1226 | trans->shrd->tid_data[sta_id][tid].tfds_in_queue++; | 1226 | !ieee80211_has_morefrags(fc)) |
1227 | if (!ieee80211_has_morefrags(fc)) | ||
1228 | trans->shrd->tid_data[sta_id][tid].seq_number = | 1227 | trans->shrd->tid_data[sta_id][tid].seq_number = |
1229 | seq_number; | 1228 | seq_number; |
1230 | } | ||
1231 | 1229 | ||
1232 | /* | 1230 | /* |
1233 | * At this point the frame is "transmitted" successfully | 1231 | * At this point the frame is "transmitted" successfully |
@@ -1304,10 +1302,11 @@ static int iwlagn_txq_check_empty(struct iwl_trans *trans, | |||
1304 | } | 1302 | } |
1305 | break; | 1303 | break; |
1306 | case IWL_EMPTYING_HW_QUEUE_ADDBA: | 1304 | case IWL_EMPTYING_HW_QUEUE_ADDBA: |
1307 | /* We are reclaiming the last packet of the queue */ | 1305 | /* There are no packets for this RA / TID in the HW any more */ |
1308 | if (tid_data->tfds_in_queue == 0) { | 1306 | if (tid_data->agg.ssn == tid_data->next_reclaimed) { |
1309 | IWL_DEBUG_TX_QUEUES(trans, | 1307 | IWL_DEBUG_TX_QUEUES(trans, |
1310 | "HW queue empty: continue ADDBA flow\n"); | 1308 | "Can continue ADDBA flow ssn = next_recl =" |
1309 | " %d", tid_data->next_reclaimed); | ||
1311 | tid_data->agg.state = IWL_AGG_ON; | 1310 | tid_data->agg.state = IWL_AGG_ON; |
1312 | iwl_start_tx_ba_trans_ready(priv(trans), | 1311 | iwl_start_tx_ba_trans_ready(priv(trans), |
1313 | NUM_IWL_RXON_CTX, | 1312 | NUM_IWL_RXON_CTX, |
@@ -1321,21 +1320,6 @@ static int iwlagn_txq_check_empty(struct iwl_trans *trans, | |||
1321 | return 0; | 1320 | return 0; |
1322 | } | 1321 | } |
1323 | 1322 | ||
1324 | static void iwl_free_tfds_in_queue(struct iwl_trans *trans, | ||
1325 | int sta_id, int tid, int freed) | ||
1326 | { | ||
1327 | lockdep_assert_held(&trans->shrd->sta_lock); | ||
1328 | |||
1329 | if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) | ||
1330 | trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; | ||
1331 | else { | ||
1332 | IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n", | ||
1333 | trans->shrd->tid_data[sta_id][tid].tfds_in_queue, | ||
1334 | freed); | ||
1335 | trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; | ||
1336 | } | ||
1337 | } | ||
1338 | |||
1339 | static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, | 1323 | static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, |
1340 | int txq_id, int ssn, u32 status, | 1324 | int txq_id, int ssn, u32 status, |
1341 | struct sk_buff_head *skbs) | 1325 | struct sk_buff_head *skbs) |
@@ -1367,7 +1351,6 @@ static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, | |||
1367 | iwl_wake_queue(trans, txq, "Packets reclaimed"); | 1351 | iwl_wake_queue(trans, txq, "Packets reclaimed"); |
1368 | } | 1352 | } |
1369 | 1353 | ||
1370 | iwl_free_tfds_in_queue(trans, sta_id, tid, freed); | ||
1371 | iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); | 1354 | iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); |
1372 | } | 1355 | } |
1373 | 1356 | ||