Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e86a547 authored by Sujith Manoharan's avatar Sujith Manoharan Committed by John W. Linville
Browse files

ath9k_htc: Fix TX queue management



Handle queue start/stop properly by maintaining
a counter to check if the pending frame count has
exceeded the threshold. Otherwise, packets would be
dropped needlessly. While at it, use a simple flag
to track queue status and use helper functions too.

Signed-off-by: default avatarSujith Manoharan <Sujith.Manoharan@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 3deff760
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -262,11 +262,16 @@ struct ath9k_htc_rx {
	spinlock_t rxbuflock;
};

struct ath9k_htc_tx {
	bool tx_queues_stop;
	spinlock_t tx_lock;
#define ATH9K_HTC_TX_RESERVE   10
#define ATH9K_HTC_TX_THRESHOLD (MAX_TX_BUF_NUM - ATH9K_HTC_TX_RESERVE)

#define ATH9K_HTC_OP_TX_QUEUES_STOP BIT(0)

struct ath9k_htc_tx {
	u8 flags;
	int queued_cnt;
	struct sk_buff_head tx_queue;
	spinlock_t tx_lock;
};

struct ath9k_htc_tx_ctl {
@@ -532,6 +537,8 @@ int ath9k_htc_cabq_setup(struct ath9k_htc_priv *priv);
int get_hw_qnum(u16 queue, int *hwq_map);
int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum,
		       struct ath9k_tx_queue_info *qinfo);
void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv);
void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv);

int ath9k_rx_init(struct ath9k_htc_priv *priv);
void ath9k_rx_cleanup(struct ath9k_htc_priv *priv);
+4 −0
Original line number Diff line number Diff line
@@ -326,6 +326,10 @@ static void ath9k_htc_send_buffered(struct ath9k_htc_priv *priv,
			ath_dbg(common, ATH_DBG_FATAL,
				"Failed to send CAB frame\n");
			dev_kfree_skb_any(skb);
		} else {
			spin_lock_bh(&priv->tx.tx_lock);
			priv->tx.queued_cnt++;
			spin_unlock_bh(&priv->tx.tx_lock);
		}
	next:
		skb = ieee80211_get_buffered_bc(priv->hw, vif);
+1 −1
Original line number Diff line number Diff line
@@ -399,7 +399,7 @@ void ath9k_htc_radio_enable(struct ieee80211_hw *hw)
	/* Start TX */
	htc_start(priv->htc);
	spin_lock_bh(&priv->tx.tx_lock);
	priv->tx.tx_queues_stop = false;
	priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
	spin_unlock_bh(&priv->tx.tx_lock);
	ieee80211_wake_queues(hw);

+8 −13
Original line number Diff line number Diff line
@@ -833,6 +833,7 @@ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
	struct ieee80211_hdr *hdr;
	struct ath9k_htc_priv *priv = hw->priv;
	struct ath_common *common = ath9k_hw_common(priv->ah);
	int padpos, padsize, ret;

	hdr = (struct ieee80211_hdr *) skb->data;
@@ -841,28 +842,22 @@ static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
	padpos = ath9k_cmn_padpos(hdr->frame_control);
	padsize = padpos & 3;
	if (padsize && skb->len > padpos) {
		if (skb_headroom(skb) < padsize)
		if (skb_headroom(skb) < padsize) {
			ath_dbg(common, ATH_DBG_XMIT, "No room for padding\n");
			goto fail_tx;
		}
		skb_push(skb, padsize);
		memmove(skb->data, skb->data + padsize, padpos);
	}

	ret = ath9k_htc_tx_start(priv, skb, false);
	if (ret != 0) {
		if (ret == -ENOMEM) {
			ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
				"Stopping TX queues\n");
			ieee80211_stop_queues(hw);
			spin_lock_bh(&priv->tx.tx_lock);
			priv->tx.tx_queues_stop = true;
			spin_unlock_bh(&priv->tx.tx_lock);
		} else {
			ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
				"Tx failed\n");
		}
		ath_dbg(common, ATH_DBG_XMIT, "Tx failed\n");
		goto fail_tx;
	}

	ath9k_htc_check_stop_queues(priv);

	return;

fail_tx:
@@ -924,7 +919,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
	htc_start(priv->htc);

	spin_lock_bh(&priv->tx.tx_lock);
	priv->tx.tx_queues_stop = false;
	priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
	spin_unlock_bh(&priv->tx.tx_lock);

	ieee80211_wake_queues(hw);
+29 −10
Original line number Diff line number Diff line
@@ -53,6 +53,29 @@ int get_hw_qnum(u16 queue, int *hwq_map)
	}
}

void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv)
{
	spin_lock_bh(&priv->tx.tx_lock);
	priv->tx.queued_cnt++;
	if ((priv->tx.queued_cnt >= ATH9K_HTC_TX_THRESHOLD) &&
	    !(priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) {
		priv->tx.flags |= ATH9K_HTC_OP_TX_QUEUES_STOP;
		ieee80211_stop_queues(priv->hw);
	}
	spin_unlock_bh(&priv->tx.tx_lock);
}

void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv)
{
	spin_lock_bh(&priv->tx.tx_lock);
	if ((priv->tx.queued_cnt < ATH9K_HTC_TX_THRESHOLD) &&
	    (priv->tx.flags & ATH9K_HTC_OP_TX_QUEUES_STOP)) {
		priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
		ieee80211_wake_queues(priv->hw);
	}
	spin_unlock_bh(&priv->tx.tx_lock);
}

int ath_htc_txq_update(struct ath9k_htc_priv *priv, int qnum,
		       struct ath9k_tx_queue_info *qinfo)
{
@@ -302,21 +325,17 @@ void ath9k_tx_tasklet(unsigned long data)
		rcu_read_unlock();

	send_mac80211:
		spin_lock_bh(&priv->tx.tx_lock);
		if (WARN_ON(--priv->tx.queued_cnt < 0))
			priv->tx.queued_cnt = 0;
		spin_unlock_bh(&priv->tx.tx_lock);

		/* Send status to mac80211 */
		ieee80211_tx_status(priv->hw, skb);
	}

	/* Wake TX queues if needed */
	spin_lock_bh(&priv->tx.tx_lock);
	if (priv->tx.tx_queues_stop) {
		priv->tx.tx_queues_stop = false;
		spin_unlock_bh(&priv->tx.tx_lock);
		ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
			"Waking up TX queues\n");
		ieee80211_wake_queues(priv->hw);
		return;
	}
	spin_unlock_bh(&priv->tx.tx_lock);
	ath9k_htc_check_wake_queues(priv);
}

void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,