Commit 7757dfed authored by Sujith's avatar Sujith Committed by John W. Linville

ath9k_htc: Handle TX queue overflow

Stop/restart TX queues when the internal SKB
queue is full. This helps handle TX better
under heavy load.
Signed-off-by: default avatarSujith <Sujith.Manoharan@atheros.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 6335ed0f
...@@ -349,6 +349,9 @@ struct ath9k_htc_priv { ...@@ -349,6 +349,9 @@ struct ath9k_htc_priv {
struct sk_buff *beacon; struct sk_buff *beacon;
spinlock_t beacon_lock; spinlock_t beacon_lock;
bool tx_queues_stop;
spinlock_t tx_lock;
struct ieee80211_vif *vif; struct ieee80211_vif *vif;
unsigned int rxfilter; unsigned int rxfilter;
struct tasklet_struct wmi_tasklet; struct tasklet_struct wmi_tasklet;
......
...@@ -449,6 +449,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid) ...@@ -449,6 +449,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, u16 devid)
spin_lock_init(&priv->wmi->wmi_lock); spin_lock_init(&priv->wmi->wmi_lock);
spin_lock_init(&priv->beacon_lock); spin_lock_init(&priv->beacon_lock);
spin_lock_init(&priv->tx_lock);
mutex_init(&priv->mutex); mutex_init(&priv->mutex);
mutex_init(&priv->aggr_work.mutex); mutex_init(&priv->aggr_work.mutex);
tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet, tasklet_init(&priv->wmi_tasklet, ath9k_wmi_tasklet,
......
...@@ -994,7 +994,7 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -994,7 +994,7 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{ {
struct ieee80211_hdr *hdr; struct ieee80211_hdr *hdr;
struct ath9k_htc_priv *priv = hw->priv; struct ath9k_htc_priv *priv = hw->priv;
int padpos, padsize; int padpos, padsize, ret;
hdr = (struct ieee80211_hdr *) skb->data; hdr = (struct ieee80211_hdr *) skb->data;
...@@ -1008,8 +1008,19 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -1008,8 +1008,19 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
memmove(skb->data, skb->data + padsize, padpos); memmove(skb->data, skb->data + padsize, padpos);
} }
if (ath9k_htc_tx_start(priv, skb) != 0) { ret = ath9k_htc_tx_start(priv, skb);
ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, "Tx failed"); if (ret != 0) {
if (ret == -ENOMEM) {
ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
"Stopping TX queues\n");
ieee80211_stop_queues(hw);
spin_lock_bh(&priv->tx_lock);
priv->tx_queues_stop = true;
spin_unlock_bh(&priv->tx_lock);
} else {
ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
"Tx failed");
}
goto fail_tx; goto fail_tx;
} }
...@@ -1074,6 +1085,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ...@@ -1074,6 +1085,12 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
priv->op_flags &= ~OP_INVALID; priv->op_flags &= ~OP_INVALID;
htc_start(priv->htc); htc_start(priv->htc);
spin_lock_bh(&priv->tx_lock);
priv->tx_queues_stop = false;
spin_unlock_bh(&priv->tx_lock);
ieee80211_wake_queues(hw);
mutex_unlock: mutex_unlock:
mutex_unlock(&priv->mutex); mutex_unlock(&priv->mutex);
return ret; return ret;
......
...@@ -226,6 +226,18 @@ void ath9k_tx_tasklet(unsigned long data) ...@@ -226,6 +226,18 @@ void ath9k_tx_tasklet(unsigned long data)
/* Send status to mac80211 */ /* Send status to mac80211 */
ieee80211_tx_status(priv->hw, skb); ieee80211_tx_status(priv->hw, skb);
} }
/* Wake TX queues if needed */
spin_lock_bh(&priv->tx_lock);
if (priv->tx_queues_stop) {
priv->tx_queues_stop = false;
spin_unlock_bh(&priv->tx_lock);
ath_print(ath9k_hw_common(priv->ah), ATH_DBG_XMIT,
"Waking up TX queues\n");
ieee80211_wake_queues(priv->hw);
return;
}
spin_unlock_bh(&priv->tx_lock);
} }
void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb, void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment