Search Linux Wireless

[RFC 33/34] ath9k_htc: Add a timer to cleanup WMI events

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



From: Sujith Manoharan <Sujith.Manoharan@xxxxxxxxxxx>

Occasionally, a WMI event would arrive ahead of the TX
URB completion handler. Discarding these events would exhaust
the available TX slots, so handle them by running a timer
cleaning up such events.

Signed-off-by: Sujith Manoharan <Sujith.Manoharan@xxxxxxxxxxx>
---
 drivers/net/wireless/ath/ath9k/htc.h          |    3 +
 drivers/net/wireless/ath/ath9k/htc_drv_init.c |    3 +-
 drivers/net/wireless/ath/ath9k/htc_drv_main.c |   12 +++++
 drivers/net/wireless/ath/ath9k/htc_drv_txrx.c |   59 ++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath9k/wmi.c          |    3 +
 drivers/net/wireless/ath/ath9k/wmi.h          |    8 +++
 6 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index c625c10..f3c8537 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -259,6 +259,7 @@ struct ath9k_htc_rx {
 	spinlock_t rxbuflock;
 };
 
+#define ATH9K_HTC_TX_CLEANUP_INTERVAL 100 /* ms */
 #define ATH9K_HTC_TX_RESERVE   10
 #define ATH9K_HTC_TX_THRESHOLD (MAX_TX_BUF_NUM - ATH9K_HTC_TX_RESERVE)
 
@@ -276,6 +277,7 @@ struct ath9k_htc_tx {
 	struct sk_buff_head data_vo_queue;
 	struct sk_buff_head tx_failed;
 	DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM);
+	struct timer_list cleanup_timer;
 	spinlock_t tx_lock;
 };
 
@@ -551,6 +553,7 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv);
 void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event);
 void ath9k_htc_tx_failed(struct ath9k_htc_priv *priv);
 void ath9k_tx_failed_tasklet(unsigned long data);
+void ath9k_htc_tx_cleanup_timer(unsigned long data);
 
 int ath9k_rx_init(struct ath9k_htc_priv *priv);
 void ath9k_rx_cleanup(struct ath9k_htc_priv *priv);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
index 0f1a8da..7714022 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -668,7 +668,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
 	common->priv = priv;
 	common->debug_mask = ath9k_debug;
 
-	spin_lock_init(&priv->wmi->wmi_lock);
 	spin_lock_init(&priv->beacon_lock);
 	spin_lock_init(&priv->tx.tx_lock);
 	mutex_init(&priv->mutex);
@@ -680,6 +679,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
 	INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work);
 	INIT_WORK(&priv->ps_work, ath9k_ps_work);
 	INIT_WORK(&priv->fatal_work, ath9k_fatal_work);
+	setup_timer(&priv->tx.cleanup_timer, ath9k_htc_tx_cleanup_timer,
+		    (unsigned long)priv);
 
 	/*
 	 * Cache line size is used to size and align various
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index ae85cc4..4de3864 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -194,6 +194,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
 	ath9k_htc_stop_ani(priv);
 	ieee80211_stop_queues(priv->hw);
 
+	del_timer_sync(&priv->tx.cleanup_timer);
 	ath9k_htc_tx_drain(priv);
 
 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -225,6 +226,9 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
 	ath9k_htc_vif_reconfig(priv);
 	ieee80211_wake_queues(priv->hw);
 
+	mod_timer(&priv->tx.cleanup_timer,
+		  jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+
 	ath9k_htc_ps_restore(priv);
 	mutex_unlock(&priv->mutex);
 }
@@ -251,6 +255,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
 
 	ath9k_htc_ps_wakeup(priv);
 
+	del_timer_sync(&priv->tx.cleanup_timer);
 	ath9k_htc_tx_drain(priv);
 
 	WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -301,6 +306,9 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
 	    !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
 		ath9k_htc_vif_reconfig(priv);
 
+	mod_timer(&priv->tx.cleanup_timer,
+		  jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+
 err:
 	ath9k_htc_ps_restore(priv);
 	return ret;
@@ -937,6 +945,9 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
 
 	ieee80211_wake_queues(hw);
 
+	mod_timer(&priv->tx.cleanup_timer,
+		  jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+
 	if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
 		ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
 					   AR_STOMP_LOW_WLAN_WGHT);
@@ -972,6 +983,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
 
 	tasklet_kill(&priv->rx_tasklet);
 
+	del_timer_sync(&priv->tx.cleanup_timer);
 	ath9k_htc_tx_drain(priv);
 	ath9k_wmi_event_drain(priv);
 
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index a9b6bb1..7632338 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -495,6 +495,8 @@ static inline void ath9k_htc_tx_drainq(struct ath9k_htc_priv *priv,
 
 void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
 {
+	struct ath9k_htc_tx_event *event, *tmp;
+
 	spin_lock_bh(&priv->tx.tx_lock);
 	priv->tx.flags |= ATH9K_HTC_OP_TX_DRAIN;
 	spin_unlock_bh(&priv->tx.tx_lock);
@@ -515,6 +517,16 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
 	ath9k_htc_tx_drainq(priv, &priv->tx.data_vo_queue);
 	ath9k_htc_tx_drainq(priv, &priv->tx.tx_failed);
 
+	/*
+	 * The TX cleanup timer has already been killed.
+	 */
+	spin_lock_bh(&priv->wmi->event_lock);
+	list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) {
+		list_del(&event->list);
+		kfree(event);
+	}
+	spin_unlock_bh(&priv->wmi->event_lock);
+
 	spin_lock_bh(&priv->tx.tx_lock);
 	priv->tx.flags &= ~ATH9K_HTC_OP_TX_DRAIN;
 	spin_unlock_bh(&priv->tx.tx_lock);
@@ -595,6 +607,7 @@ void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event)
 	struct wmi_event_txstatus *txs = (struct wmi_event_txstatus *)wmi_event;
 	struct __wmi_event_txstatus *__txs;
 	struct sk_buff *skb;
+	struct ath9k_htc_tx_event *tx_pend;
 	int i;
 
 	for (i = 0; i < txs->cnt; i++) {
@@ -603,8 +616,23 @@ void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event)
 		__txs = &txs->txstatus[i];
 
 		skb = ath9k_htc_tx_get_packet(priv, __txs);
-		if (!skb)
+		if (!skb) {
+			/*
+			 * Store this event, so that the TX cleanup
+			 * routine can check later for the needed packet.
+			 */
+			tx_pend = kzalloc(sizeof(struct ath9k_htc_tx_event),
+					  GFP_ATOMIC);
+			memcpy(&tx_pend->txs, __txs,
+			       sizeof(struct __wmi_event_txstatus));
+
+			spin_lock(&priv->wmi->event_lock);
+			list_add_tail(&tx_pend->list,
+				      &priv->wmi->pending_tx_events);
+			spin_unlock(&priv->wmi->event_lock);
+
 			continue;
+		}
 
 		ath9k_htc_tx_process(priv, skb, __txs);
 	}
@@ -638,6 +666,35 @@ void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
 	skb_queue_tail(epid_queue, skb);
 }
 
+void ath9k_htc_tx_cleanup_timer(unsigned long data)
+{
+	struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) data;
+	struct ath_common *common = ath9k_hw_common(priv->ah);
+	struct ath9k_htc_tx_event *event, *tmp;
+	struct sk_buff *skb;
+
+	spin_lock(&priv->wmi->event_lock);
+	list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) {
+
+		skb = ath9k_htc_tx_get_packet(priv, &event->txs);
+		if (skb) {
+			ath_dbg(common, ATH_DBG_XMIT,
+				"Found packet for cookie: %d, epid: %d\n",
+				event->txs.cookie,
+				MS(event->txs.ts_rate, ATH9K_HTC_TXSTAT_EPID));
+
+			ath9k_htc_tx_process(priv, skb, &event->txs);
+		}
+
+		list_del(&event->list);
+		kfree(event);
+	}
+	spin_unlock(&priv->wmi->event_lock);
+
+	mod_timer(&priv->tx.cleanup_timer,
+		  jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
+}
+
 int ath9k_tx_init(struct ath9k_htc_priv *priv)
 {
 	skb_queue_head_init(&priv->tx.mgmt_ep_queue);
diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
index 5c15d8e..6348e3c 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -89,9 +89,12 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
 	wmi->drv_priv = priv;
 	wmi->stopped = false;
 	skb_queue_head_init(&wmi->wmi_event_queue);
+	spin_lock_init(&wmi->wmi_lock);
+	spin_lock_init(&wmi->event_lock);
 	mutex_init(&wmi->op_mutex);
 	mutex_init(&wmi->multi_write_mutex);
 	init_completion(&wmi->cmd_wait);
+	INIT_LIST_HEAD(&wmi->pending_tx_events);
 	tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
 		     (unsigned long)wmi);
 
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 3fa8510..95ac74b 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -123,6 +123,11 @@ struct register_write {
 	__be32 val;
 };
 
+struct ath9k_htc_tx_event {
+	struct __wmi_event_txstatus txs;
+	struct list_head list;
+};
+
 struct wmi {
 	struct ath9k_htc_priv *drv_priv;
 	struct htc_target *htc;
@@ -137,6 +142,9 @@ struct wmi {
 	u32 cmd_rsp_len;
 	bool stopped;
 
+	struct list_head pending_tx_events;
+	spinlock_t event_lock;
+
 	spinlock_t wmi_lock;
 
 	atomic_t mwrite_cnt;
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux