diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index e91a0ee1189b4940797ebb8e8f8d0bf207c14d6d..45eb45af59538e8019d6905639fd0a8d49a8ac18 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -477,43 +477,6 @@ int iwlagn_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, return 0; } -static int iwlagn_txq_check_empty(struct iwl_priv *priv, - int sta_id, u8 tid, int txq_id) -{ - struct iwl_queue *q = &priv->txq[txq_id].q; - u8 *addr = priv->stations[sta_id].sta.sta.addr; - struct iwl_tid_data *tid_data = &priv->shrd->tid_data[sta_id][tid]; - struct iwl_rxon_context *ctx; - - ctx = &priv->contexts[priv->stations[sta_id].ctxid]; - - lockdep_assert_held(&priv->shrd->sta_lock); - - switch (priv->shrd->tid_data[sta_id][tid].agg.state) { - case IWL_EMPTYING_HW_QUEUE_DELBA: - /* We are reclaiming the last packet of the */ - /* aggregated HW queue */ - if ((txq_id == tid_data->agg.txq_id) && - (q->read_ptr == q->write_ptr)) { - IWL_DEBUG_HT(priv, "HW queue empty: continue DELBA flow\n"); - iwl_trans_txq_agg_disable(trans(priv), txq_id); - tid_data->agg.state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid); - } - break; - case IWL_EMPTYING_HW_QUEUE_ADDBA: - /* We are reclaiming the last packet of the queue */ - if (tid_data->tfds_in_queue == 0) { - IWL_DEBUG_HT(priv, "HW queue empty: continue ADDBA flow\n"); - tid_data->agg.state = IWL_AGG_ON; - ieee80211_start_tx_ba_cb_irqsafe(ctx->vif, addr, tid); - } - break; - } - - return 0; -} - static void iwlagn_non_agg_tx_status(struct iwl_priv *priv, struct iwl_rxon_context *ctx, const u8 *addr1) @@ -724,21 +687,6 @@ static inline u32 iwlagn_get_scd_ssn(struct iwlagn_tx_resp *tx_resp) tx_resp->frame_count) & MAX_SN; } -static void iwl_free_tfds_in_queue(struct iwl_priv *priv, - int sta_id, int tid, int freed) -{ - lockdep_assert_held(&priv->shrd->sta_lock); - - if (priv->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) - priv->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; - else { - IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n", - priv->shrd->tid_data[sta_id][tid].tfds_in_queue, - freed); - priv->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; - } -} - static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) { status &= TX_STATUS_MSK; @@ -889,7 +837,8 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) __skb_queue_head_init(&skbs); /*we can free until ssn % q.n_bd not inclusive */ - iwl_trans_reclaim(trans(priv), txq_id, ssn, status, &skbs); + iwl_trans_reclaim(trans(priv), sta_id, tid, txq_id, + ssn, status, &skbs); freed = 0; while (!skb_queue_empty(&skbs)) { skb = __skb_dequeue(&skbs); @@ -939,9 +888,6 @@ void iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) } WARN_ON(!is_agg && freed != 1); - - iwl_free_tfds_in_queue(priv, sta_id, tid, freed); - iwlagn_txq_check_empty(priv, sta_id, tid, txq_id); } iwl_check_abort_status(priv, tx_resp->frame_count, status); @@ -1050,8 +996,8 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, /* Release all TFDs before the SSN, i.e. all TFDs in front of * block-ack window (we assume that they've been successfully * transmitted ... if not, it's too late anyway). */ - iwl_trans_reclaim(trans(priv), scd_flow, ba_resp_scd_ssn, 0, - &reclaimed_skbs); + iwl_trans_reclaim(trans(priv), sta_id, tid, scd_flow, ba_resp_scd_ssn, + 0, &reclaimed_skbs); freed = 0; while (!skb_queue_empty(&reclaimed_skbs)) { @@ -1082,8 +1028,5 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, ieee80211_tx_status_irqsafe(priv->hw, skb); } - iwl_free_tfds_in_queue(priv, sta_id, tid, freed); - iwlagn_txq_check_empty(priv, sta_id, tid, scd_flow); - spin_unlock_irqrestore(&priv->shrd->sta_lock, flags); } diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 38a3c3187ea34d3f744d58f94fb76a48016a4afe..c6f8e682d03c1c3676bae958f14cc2aa1cd346cc 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -1858,11 +1858,30 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, return cpu_to_le32(res); } -void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, u8 ctx, +void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, + enum iwl_rxon_context_id ctx, u8 sta_id, u8 tid) { struct ieee80211_vif *vif = priv->contexts[ctx].vif; u8 *addr = priv->stations[sta_id].sta.sta.addr; + if (ctx == NUM_IWL_RXON_CTX) + ctx = priv->stations[sta_id].ctxid; + vif = priv->contexts[ctx].vif; + ieee80211_start_tx_ba_cb_irqsafe(vif, addr, tid); } + +void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv, + enum iwl_rxon_context_id ctx, + u8 sta_id, u8 tid) +{ + struct ieee80211_vif *vif; + u8 *addr = priv->stations[sta_id].sta.sta.addr; + + if (ctx == NUM_IWL_RXON_CTX) + ctx = priv->stations[sta_id].ctxid; + vif = priv->contexts[ctx].vif; + + ieee80211_stop_tx_ba_cb_irqsafe(vif, addr, tid); +} diff --git a/drivers/net/wireless/iwlwifi/iwl-shared.h b/drivers/net/wireless/iwlwifi/iwl-shared.h index a2be28a925eee4360a862e35cdcb863fbaada99d..0bd6f7d54433a9ea69196d403a51b5ac2654c32b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-shared.h +++ b/drivers/net/wireless/iwlwifi/iwl-shared.h @@ -346,8 +346,12 @@ int iwl_probe(struct iwl_bus *bus, const struct iwl_trans_ops *trans_ops, struct iwl_cfg *cfg); void __devexit iwl_remove(struct iwl_priv * priv); -void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, u8 ctx, +void iwl_start_tx_ba_trans_ready(struct iwl_priv *priv, + enum iwl_rxon_context_id ctx, u8 sta_id, u8 tid); +void iwl_stop_tx_ba_trans_ready(struct iwl_priv *priv, + enum iwl_rxon_context_id ctx, + u8 sta_id, u8 tid); /***************************************************** * DRIVER STATUS FUNCTIONS diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h index ece9408262b138c0d4b22b9791d7f43e70754a03..ba82c8bca24239a5d6f6e9133ebdf9ec31ba62b9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans-int-pcie.h @@ -202,8 +202,8 @@ void iwl_trans_pcie_txq_agg_setup(struct iwl_priv *priv, int sta_id, int tid, int frame_limit); void iwlagn_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, int index); -void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs); +int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, + struct sk_buff_head *skbs); /***************************************************** * Error handling diff --git a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c index 93922265feb39ddc84408feffc3af10d8c5f66f8..da8d79eb4dc5a2c8cd2c88f7e4e7d3aa26814f77 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans-tx-pcie.c @@ -1118,12 +1118,13 @@ int iwl_trans_pcie_send_cmd_pdu(struct iwl_trans *trans, u8 id, u32 flags, } /* Frees buffers until index _not_ inclusive */ -void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs) +int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, + struct sk_buff_head *skbs) { struct iwl_tx_queue *txq = &priv(trans)->txq[txq_id]; struct iwl_queue *q = &txq->q; int last_to_free; + int freed = 0; /*Since we free until index _not_ inclusive, the one before index is * the last we will free. This one must be used */ @@ -1135,14 +1136,14 @@ void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, "last_to_free %d is out of range [0-%d] %d %d.\n", __func__, txq_id, last_to_free, q->n_bd, q->write_ptr, q->read_ptr); - return; + return 0; } IWL_DEBUG_TX_REPLY(trans, "reclaim: [%d, %d, %d]\n", txq_id, q->read_ptr, index); if (WARN_ON(!skb_queue_empty(skbs))) - return; + return 0; for (; q->read_ptr != index; @@ -1158,5 +1159,7 @@ void iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, iwlagn_txq_inval_byte_cnt_tbl(trans, txq); iwlagn_txq_free_tfd(trans, txq, txq->q.read_ptr); + freed++; } + return freed; } diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.c b/drivers/net/wireless/iwlwifi/iwl-trans.c index 13e8fdc4c01235a2999844a69cf85013fe7d4d15..0256454427fd6d414a844ea48239c9665653cc2e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/iwlwifi/iwl-trans.c @@ -1265,19 +1265,75 @@ static int iwl_trans_pcie_request_irq(struct iwl_trans *trans) return 0; } -static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, - int ssn, u32 status, struct sk_buff_head *skbs) +static int iwlagn_txq_check_empty(struct iwl_trans *trans, + int sta_id, u8 tid, int txq_id) { - struct iwl_priv *priv = priv(trans); - struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct iwl_queue *q = &priv(trans)->txq[txq_id].q; + struct iwl_tid_data *tid_data = &trans->shrd->tid_data[sta_id][tid]; + + lockdep_assert_held(&trans->shrd->sta_lock); + + switch (trans->shrd->tid_data[sta_id][tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* We are reclaiming the last packet of the */ + /* aggregated HW queue */ + if ((txq_id == tid_data->agg.txq_id) && + (q->read_ptr == q->write_ptr)) { + IWL_DEBUG_HT(trans, + "HW queue empty: continue DELBA flow\n"); + iwl_trans_pcie_txq_agg_disable(priv(trans), txq_id); + tid_data->agg.state = IWL_AGG_OFF; + iwl_stop_tx_ba_trans_ready(priv(trans), + NUM_IWL_RXON_CTX, + sta_id, tid); + iwl_wake_queue(priv(trans), &priv(trans)->txq[txq_id]); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* We are reclaiming the last packet of the queue */ + if (tid_data->tfds_in_queue == 0) { + IWL_DEBUG_HT(trans, + "HW queue empty: continue ADDBA flow\n"); + tid_data->agg.state = IWL_AGG_ON; + iwl_start_tx_ba_trans_ready(priv(trans), + NUM_IWL_RXON_CTX, + sta_id, tid); + } + break; + } + + return 0; +} + +static void iwl_free_tfds_in_queue(struct iwl_trans *trans, + int sta_id, int tid, int freed) +{ + lockdep_assert_held(&trans->shrd->sta_lock); + + if (trans->shrd->tid_data[sta_id][tid].tfds_in_queue >= freed) + trans->shrd->tid_data[sta_id][tid].tfds_in_queue -= freed; + else { + IWL_DEBUG_TX(trans, "free more than tfds_in_queue (%u:%d)\n", + trans->shrd->tid_data[sta_id][tid].tfds_in_queue, + freed); + trans->shrd->tid_data[sta_id][tid].tfds_in_queue = 0; + } +} + +static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int sta_id, int tid, + int txq_id, int ssn, u32 status, + struct sk_buff_head *skbs) +{ + struct iwl_tx_queue *txq = &priv(trans)->txq[txq_id]; /* n_bd is usually 256 => n_bd - 1 = 0xff */ int tfd_num = ssn & (txq->q.n_bd - 1); + int freed = 0; u8 agg_state; bool cond; if (txq->sched_retry) { agg_state = - priv->shrd->tid_data[txq->sta_id][txq->tid].agg.state; + trans->shrd->tid_data[txq->sta_id][txq->tid].agg.state; cond = (agg_state != IWL_EMPTYING_HW_QUEUE_DELBA); } else { cond = (status != TX_STATUS_FAIL_PASSIVE_NO_RX); @@ -1287,10 +1343,13 @@ static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, IWL_DEBUG_TX_REPLY(trans, "Retry scheduler reclaim " "scd_ssn=%d idx=%d txq=%d swq=%d\n", ssn , tfd_num, txq_id, txq->swq_id); - iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); + freed = iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); if (iwl_queue_space(&txq->q) > txq->q.low_mark && cond) - iwl_wake_queue(priv, txq); + iwl_wake_queue(priv(trans), txq); } + + iwl_free_tfds_in_queue(trans, sta_id, tid, freed); + iwlagn_txq_check_empty(trans, sta_id, tid, txq_id); } static void iwl_trans_pcie_free(struct iwl_trans *trans) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index 8aaab087ba54cf081c15c1fd355df3e08c50ba85..7586a1512e843d2b48649c8f17c20c5e69cde6bb 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -123,8 +123,9 @@ struct iwl_trans_ops { const void *data); int (*tx)(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_cmd *dev_cmd, u8 ctx, u8 sta_id); - void (*reclaim)(struct iwl_trans *trans, int txq_id, int ssn, - u32 status, struct sk_buff_head *skbs); + void (*reclaim)(struct iwl_trans *trans, int sta_id, int tid, + int txq_id, int ssn, u32 status, + struct sk_buff_head *skbs); int (*txq_agg_disable)(struct iwl_priv *priv, u16 txq_id); int (*tx_agg_alloc)(struct iwl_trans *trans, @@ -208,11 +209,11 @@ static inline int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, return trans->ops->tx(trans, skb, dev_cmd, ctx, sta_id); } -static inline void iwl_trans_reclaim(struct iwl_trans *trans, int txq_id, - int ssn, u32 status, +static inline void iwl_trans_reclaim(struct iwl_trans *trans, int sta_id, + int tid, int txq_id, int ssn, u32 status, struct sk_buff_head *skbs) { - trans->ops->reclaim(trans, txq_id, ssn, status, skbs); + trans->ops->reclaim(trans, sta_id, tid, txq_id, ssn, status, skbs); } static inline int iwl_trans_txq_agg_disable(struct iwl_trans *trans, u16 txq_id)