There were cases where we'd run out of msdu_ids.
This can be fixed by telling mac80211 to stop
sending us more frames if we run out of resources.

It was possible to trigger with massive TCP RX
~300mbps (e.g. using iperf).

Signed-off-by: Michal Kazior <michal.kaz...@tieto.com>
---
 drivers/net/wireless/ath/ath10k/htt.h    |    2 ++
 drivers/net/wireless/ath/ath10k/htt_tx.c |   46 ++++++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath10k/txrx.c   |    1 +
 3 files changed, 49 insertions(+)

diff --git a/drivers/net/wireless/ath/ath10k/htt.h 
b/drivers/net/wireless/ath/ath10k/htt.h
index 85de0b3..8ae1f03 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -1262,6 +1262,7 @@ struct ath10k_htt {
 
        /* Protects access to %pending_tx, %used_msdu_ids */
        spinlock_t tx_lock;
+       int num_pending_tx;
        struct sk_buff *pending_tx[HTT_MAX_NUM_PENDING_TX];
        DECLARE_BITMAP(used_msdu_ids, HTT_MAX_NUM_PENDING_TX);
        wait_queue_head_t empty_tx_wq;
@@ -1330,6 +1331,7 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct 
sk_buff *skb);
 int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt);
 int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt);
 
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt);
 int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt);
 void ath10k_htt_tx_free_msdu_id(struct ath10k_htt *htt, u16 msdu_id);
 int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c 
b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 21add5d..7dd366f 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -18,9 +18,44 @@
 #include <linux/etherdevice.h>
 #include "htt.h"
 #include "mac.h"
+#include "hif.h"
 #include "txrx.h"
 #include "debug.h"
 
+void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       htt->num_pending_tx--;
+       if (htt->num_pending_tx == HTT_MAX_NUM_PENDING_TX - 1)
+               ieee80211_wake_queues(htt->ar->hw);
+}
+
+static void ath10k_htt_tx_dec_pending(struct ath10k_htt *htt)
+{
+       spin_lock_bh(&htt->tx_lock);
+       __ath10k_htt_tx_dec_pending(htt);
+       spin_unlock_bh(&htt->tx_lock);
+}
+
+static int ath10k_htt_tx_inc_pending(struct ath10k_htt *htt)
+{
+       int ret = 0;
+
+       spin_lock_bh(&htt->tx_lock);
+
+       if (htt->num_pending_tx >= HTT_MAX_NUM_PENDING_TX) {
+               ret = -EBUSY;
+               goto exit;
+       }
+
+       htt->num_pending_tx++;
+       if (htt->num_pending_tx == HTT_MAX_NUM_PENDING_TX)
+               ieee80211_stop_queues(htt->ar->hw);
+
+exit:
+       spin_unlock_bh(&htt->tx_lock);
+       return ret;
+}
+
 int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt)
 {
        int msdu_id;
@@ -238,6 +273,11 @@ int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct 
sk_buff *msdu)
        int msdu_id = -1;
        int res;
 
+
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
        len += sizeof(cmd->hdr);
        len += sizeof(cmd->mgmt_tx);
 
@@ -295,6 +335,7 @@ err:
                ath10k_htt_tx_free_msdu_id(htt, msdu_id);
                spin_unlock_bh(&htt->tx_lock);
        }
+       ath10k_htt_tx_dec_pending(htt);
        return res;
 }
 
@@ -316,6 +357,10 @@ int ath10k_htt_tx(struct ath10k_htt *htt, struct sk_buff 
*msdu)
        u8 flags0;
        u16 flags1;
 
+       res = ath10k_htt_tx_inc_pending(htt);
+       if (res)
+               return res;
+
        prefetch_len = min(htt->prefetch_len, msdu->len);
        prefetch_len = roundup(prefetch_len, 4);
 
@@ -431,6 +476,7 @@ err:
                ath10k_htt_tx_free_msdu_id(htt, msdu_id);
                spin_unlock_bh(&htt->tx_lock);
        }
+       ath10k_htt_tx_dec_pending(htt);
        ath10k_skb_unmap(dev, msdu);
        return res;
 }
diff --git a/drivers/net/wireless/ath/ath10k/txrx.c 
b/drivers/net/wireless/ath/ath10k/txrx.c
index a32448c..ce44ab1 100644
--- a/drivers/net/wireless/ath/ath10k/txrx.c
+++ b/drivers/net/wireless/ath/ath10k/txrx.c
@@ -95,6 +95,7 @@ exit:
        spin_lock_bh(&htt->tx_lock);
        htt->pending_tx[ATH10K_SKB_CB(txdesc)->htt.msdu_id] = NULL;
        ath10k_htt_tx_free_msdu_id(htt, ATH10K_SKB_CB(txdesc)->htt.msdu_id);
+       __ath10k_htt_tx_dec_pending(htt);
        if (bitmap_empty(htt->used_msdu_ids, HTT_MAX_NUM_PENDING_TX))
                wake_up(&htt->empty_tx_wq);
        spin_unlock_bh(&htt->tx_lock);
-- 
1.7.9.5

_______________________________________________
ath9k-devel mailing list
ath9k-devel@lists.ath9k.org
https://lists.ath9k.org/mailman/listinfo/ath9k-devel

Reply via email to