aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorSujith Manoharan <Sujith.Manoharan@atheros.com>2011-04-13 01:56:39 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-13 15:24:30 -0400
commit859c3ca1e4608615788dc6cbc199210fe4b5efa2 (patch)
tree523b8939326f1c8605b037201142f73816cdee37 /drivers/net
parentc4d04186c7023d54445b695da226b3e98e0a55f9 (diff)
ath9k_htc: Add a timer to cleanup WMI events
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. Also, timeout packets for which TX completion events have not arrived. Signed-off-by: Sujith Manoharan <Sujith.Manoharan@atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h8
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_init.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c12
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c127
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/wmi.h9
6 files changed, 159 insertions, 3 deletions
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index b40753ca6706..b413b46119b0 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -262,7 +262,10 @@ struct ath9k_htc_rx {
262 spinlock_t rxbuflock; 262 spinlock_t rxbuflock;
263}; 263};
264 264
265#define ATH9K_HTC_TX_RESERVE 10 265#define ATH9K_HTC_TX_CLEANUP_INTERVAL 50 /* ms */
266#define ATH9K_HTC_TX_TIMEOUT_INTERVAL 2500 /* ms */
267#define ATH9K_HTC_TX_RESERVE 10
268#define ATH9K_HTC_TX_TIMEOUT_COUNT 20
266#define ATH9K_HTC_TX_THRESHOLD (MAX_TX_BUF_NUM - ATH9K_HTC_TX_RESERVE) 269#define ATH9K_HTC_TX_THRESHOLD (MAX_TX_BUF_NUM - ATH9K_HTC_TX_RESERVE)
267 270
268#define ATH9K_HTC_OP_TX_QUEUES_STOP BIT(0) 271#define ATH9K_HTC_OP_TX_QUEUES_STOP BIT(0)
@@ -279,6 +282,7 @@ struct ath9k_htc_tx {
279 struct sk_buff_head data_vo_queue; 282 struct sk_buff_head data_vo_queue;
280 struct sk_buff_head tx_failed; 283 struct sk_buff_head tx_failed;
281 DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM); 284 DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM);
285 struct timer_list cleanup_timer;
282 spinlock_t tx_lock; 286 spinlock_t tx_lock;
283}; 287};
284 288
@@ -287,6 +291,7 @@ struct ath9k_htc_tx_ctl {
287 u8 epid; 291 u8 epid;
288 u8 txok; 292 u8 txok;
289 u8 sta_idx; 293 u8 sta_idx;
294 unsigned long timestamp;
290}; 295};
291 296
292static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb) 297static inline struct ath9k_htc_tx_ctl *HTC_SKB_CB(struct sk_buff *skb)
@@ -557,6 +562,7 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv);
557void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event); 562void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event);
558void ath9k_htc_tx_failed(struct ath9k_htc_priv *priv); 563void ath9k_htc_tx_failed(struct ath9k_htc_priv *priv);
559void ath9k_tx_failed_tasklet(unsigned long data); 564void ath9k_tx_failed_tasklet(unsigned long data);
565void ath9k_htc_tx_cleanup_timer(unsigned long data);
560 566
561int ath9k_rx_init(struct ath9k_htc_priv *priv); 567int ath9k_rx_init(struct ath9k_htc_priv *priv);
562void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); 568void 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 afceeaa6b916..0aec25920c0a 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
@@ -671,7 +671,6 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
671 common->priv = priv; 671 common->priv = priv;
672 common->debug_mask = ath9k_debug; 672 common->debug_mask = ath9k_debug;
673 673
674 spin_lock_init(&priv->wmi->wmi_lock);
675 spin_lock_init(&priv->beacon_lock); 674 spin_lock_init(&priv->beacon_lock);
676 spin_lock_init(&priv->tx.tx_lock); 675 spin_lock_init(&priv->tx.tx_lock);
677 mutex_init(&priv->mutex); 676 mutex_init(&priv->mutex);
@@ -683,6 +682,8 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv,
683 INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work); 682 INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work);
684 INIT_WORK(&priv->ps_work, ath9k_ps_work); 683 INIT_WORK(&priv->ps_work, ath9k_ps_work);
685 INIT_WORK(&priv->fatal_work, ath9k_fatal_work); 684 INIT_WORK(&priv->fatal_work, ath9k_fatal_work);
685 setup_timer(&priv->tx.cleanup_timer, ath9k_htc_tx_cleanup_timer,
686 (unsigned long)priv);
686 687
687 /* 688 /*
688 * Cache line size is used to size and align various 689 * 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 ae85cc4373f0..4de38643cb53 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)
194 ath9k_htc_stop_ani(priv); 194 ath9k_htc_stop_ani(priv);
195 ieee80211_stop_queues(priv->hw); 195 ieee80211_stop_queues(priv->hw);
196 196
197 del_timer_sync(&priv->tx.cleanup_timer);
197 ath9k_htc_tx_drain(priv); 198 ath9k_htc_tx_drain(priv);
198 199
199 WMI_CMD(WMI_DISABLE_INTR_CMDID); 200 WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -225,6 +226,9 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
225 ath9k_htc_vif_reconfig(priv); 226 ath9k_htc_vif_reconfig(priv);
226 ieee80211_wake_queues(priv->hw); 227 ieee80211_wake_queues(priv->hw);
227 228
229 mod_timer(&priv->tx.cleanup_timer,
230 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
231
228 ath9k_htc_ps_restore(priv); 232 ath9k_htc_ps_restore(priv);
229 mutex_unlock(&priv->mutex); 233 mutex_unlock(&priv->mutex);
230} 234}
@@ -251,6 +255,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
251 255
252 ath9k_htc_ps_wakeup(priv); 256 ath9k_htc_ps_wakeup(priv);
253 257
258 del_timer_sync(&priv->tx.cleanup_timer);
254 ath9k_htc_tx_drain(priv); 259 ath9k_htc_tx_drain(priv);
255 260
256 WMI_CMD(WMI_DISABLE_INTR_CMDID); 261 WMI_CMD(WMI_DISABLE_INTR_CMDID);
@@ -301,6 +306,9 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
301 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) 306 !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
302 ath9k_htc_vif_reconfig(priv); 307 ath9k_htc_vif_reconfig(priv);
303 308
309 mod_timer(&priv->tx.cleanup_timer,
310 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
311
304err: 312err:
305 ath9k_htc_ps_restore(priv); 313 ath9k_htc_ps_restore(priv);
306 return ret; 314 return ret;
@@ -937,6 +945,9 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
937 945
938 ieee80211_wake_queues(hw); 946 ieee80211_wake_queues(hw);
939 947
948 mod_timer(&priv->tx.cleanup_timer,
949 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
950
940 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) { 951 if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) {
941 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT, 952 ath9k_hw_btcoex_set_weight(ah, AR_BT_COEX_WGHT,
942 AR_STOMP_LOW_WLAN_WGHT); 953 AR_STOMP_LOW_WLAN_WGHT);
@@ -972,6 +983,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
972 983
973 tasklet_kill(&priv->rx_tasklet); 984 tasklet_kill(&priv->rx_tasklet);
974 985
986 del_timer_sync(&priv->tx.cleanup_timer);
975 ath9k_htc_tx_drain(priv); 987 ath9k_htc_tx_drain(priv);
976 ath9k_wmi_event_drain(priv); 988 ath9k_wmi_event_drain(priv);
977 989
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index a9b6bb1ef287..86f5ce9b6e0e 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,
495 495
496void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv) 496void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
497{ 497{
498 struct ath9k_htc_tx_event *event, *tmp;
499
498 spin_lock_bh(&priv->tx.tx_lock); 500 spin_lock_bh(&priv->tx.tx_lock);
499 priv->tx.flags |= ATH9K_HTC_OP_TX_DRAIN; 501 priv->tx.flags |= ATH9K_HTC_OP_TX_DRAIN;
500 spin_unlock_bh(&priv->tx.tx_lock); 502 spin_unlock_bh(&priv->tx.tx_lock);
@@ -515,6 +517,16 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
515 ath9k_htc_tx_drainq(priv, &priv->tx.data_vo_queue); 517 ath9k_htc_tx_drainq(priv, &priv->tx.data_vo_queue);
516 ath9k_htc_tx_drainq(priv, &priv->tx.tx_failed); 518 ath9k_htc_tx_drainq(priv, &priv->tx.tx_failed);
517 519
520 /*
521 * The TX cleanup timer has already been killed.
522 */
523 spin_lock_bh(&priv->wmi->event_lock);
524 list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) {
525 list_del(&event->list);
526 kfree(event);
527 }
528 spin_unlock_bh(&priv->wmi->event_lock);
529
518 spin_lock_bh(&priv->tx.tx_lock); 530 spin_lock_bh(&priv->tx.tx_lock);
519 priv->tx.flags &= ~ATH9K_HTC_OP_TX_DRAIN; 531 priv->tx.flags &= ~ATH9K_HTC_OP_TX_DRAIN;
520 spin_unlock_bh(&priv->tx.tx_lock); 532 spin_unlock_bh(&priv->tx.tx_lock);
@@ -595,6 +607,7 @@ void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event)
595 struct wmi_event_txstatus *txs = (struct wmi_event_txstatus *)wmi_event; 607 struct wmi_event_txstatus *txs = (struct wmi_event_txstatus *)wmi_event;
596 struct __wmi_event_txstatus *__txs; 608 struct __wmi_event_txstatus *__txs;
597 struct sk_buff *skb; 609 struct sk_buff *skb;
610 struct ath9k_htc_tx_event *tx_pend;
598 int i; 611 int i;
599 612
600 for (i = 0; i < txs->cnt; i++) { 613 for (i = 0; i < txs->cnt; i++) {
@@ -603,8 +616,26 @@ void ath9k_htc_txstatus(struct ath9k_htc_priv *priv, void *wmi_event)
603 __txs = &txs->txstatus[i]; 616 __txs = &txs->txstatus[i];
604 617
605 skb = ath9k_htc_tx_get_packet(priv, __txs); 618 skb = ath9k_htc_tx_get_packet(priv, __txs);
606 if (!skb) 619 if (!skb) {
620 /*
621 * Store this event, so that the TX cleanup
622 * routine can check later for the needed packet.
623 */
624 tx_pend = kzalloc(sizeof(struct ath9k_htc_tx_event),
625 GFP_ATOMIC);
626 if (!tx_pend)
627 continue;
628
629 memcpy(&tx_pend->txs, __txs,
630 sizeof(struct __wmi_event_txstatus));
631
632 spin_lock(&priv->wmi->event_lock);
633 list_add_tail(&tx_pend->list,
634 &priv->wmi->pending_tx_events);
635 spin_unlock(&priv->wmi->event_lock);
636
607 continue; 637 continue;
638 }
608 639
609 ath9k_htc_tx_process(priv, skb, __txs); 640 ath9k_htc_tx_process(priv, skb, __txs);
610 } 641 }
@@ -622,6 +653,7 @@ void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
622 653
623 tx_ctl = HTC_SKB_CB(skb); 654 tx_ctl = HTC_SKB_CB(skb);
624 tx_ctl->txok = txok; 655 tx_ctl->txok = txok;
656 tx_ctl->timestamp = jiffies;
625 657
626 if (!txok) { 658 if (!txok) {
627 skb_queue_tail(&priv->tx.tx_failed, skb); 659 skb_queue_tail(&priv->tx.tx_failed, skb);
@@ -638,6 +670,99 @@ void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
638 skb_queue_tail(epid_queue, skb); 670 skb_queue_tail(epid_queue, skb);
639} 671}
640 672
673static inline bool check_packet(struct ath9k_htc_priv *priv, struct sk_buff *skb)
674{
675 struct ath_common *common = ath9k_hw_common(priv->ah);
676 struct ath9k_htc_tx_ctl *tx_ctl;
677
678 tx_ctl = HTC_SKB_CB(skb);
679
680 if (time_after(jiffies,
681 tx_ctl->timestamp +
682 msecs_to_jiffies(ATH9K_HTC_TX_TIMEOUT_INTERVAL))) {
683 ath_dbg(common, ATH_DBG_XMIT,
684 "Dropping a packet due to TX timeout\n");
685 return true;
686 }
687
688 return false;
689}
690
691static void ath9k_htc_tx_cleanup_queue(struct ath9k_htc_priv *priv,
692 struct sk_buff_head *epid_queue)
693{
694 bool process = false;
695 unsigned long flags;
696 struct sk_buff *skb, *tmp;
697 struct sk_buff_head queue;
698
699 skb_queue_head_init(&queue);
700
701 spin_lock_irqsave(&epid_queue->lock, flags);
702 skb_queue_walk_safe(epid_queue, skb, tmp) {
703 if (check_packet(priv, skb)) {
704 __skb_unlink(skb, epid_queue);
705 __skb_queue_tail(&queue, skb);
706 process = true;
707 }
708 }
709 spin_unlock_irqrestore(&epid_queue->lock, flags);
710
711 if (process) {
712 skb_queue_walk_safe(&queue, skb, tmp) {
713 __skb_unlink(skb, &queue);
714 ath9k_htc_tx_process(priv, skb, NULL);
715 }
716 }
717}
718
719void ath9k_htc_tx_cleanup_timer(unsigned long data)
720{
721 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *) data;
722 struct ath_common *common = ath9k_hw_common(priv->ah);
723 struct ath9k_htc_tx_event *event, *tmp;
724 struct sk_buff *skb;
725
726 spin_lock(&priv->wmi->event_lock);
727 list_for_each_entry_safe(event, tmp, &priv->wmi->pending_tx_events, list) {
728
729 skb = ath9k_htc_tx_get_packet(priv, &event->txs);
730 if (skb) {
731 ath_dbg(common, ATH_DBG_XMIT,
732 "Found packet for cookie: %d, epid: %d\n",
733 event->txs.cookie,
734 MS(event->txs.ts_rate, ATH9K_HTC_TXSTAT_EPID));
735
736 ath9k_htc_tx_process(priv, skb, &event->txs);
737 list_del(&event->list);
738 kfree(event);
739 continue;
740 }
741
742 if (++event->count >= ATH9K_HTC_TX_TIMEOUT_COUNT) {
743 list_del(&event->list);
744 kfree(event);
745 }
746 }
747 spin_unlock(&priv->wmi->event_lock);
748
749 /*
750 * Check if status-pending packets have to be cleaned up.
751 */
752 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.mgmt_ep_queue);
753 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.cab_ep_queue);
754 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_be_queue);
755 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_bk_queue);
756 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_vi_queue);
757 ath9k_htc_tx_cleanup_queue(priv, &priv->tx.data_vo_queue);
758
759 /* Wake TX queues if needed */
760 ath9k_htc_check_wake_queues(priv);
761
762 mod_timer(&priv->tx.cleanup_timer,
763 jiffies + msecs_to_jiffies(ATH9K_HTC_TX_CLEANUP_INTERVAL));
764}
765
641int ath9k_tx_init(struct ath9k_htc_priv *priv) 766int ath9k_tx_init(struct ath9k_htc_priv *priv)
642{ 767{
643 skb_queue_head_init(&priv->tx.mgmt_ep_queue); 768 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 3f5a4d1fe077..697e5af842c1 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.c
+++ b/drivers/net/wireless/ath/ath9k/wmi.c
@@ -91,9 +91,12 @@ struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
91 wmi->drv_priv = priv; 91 wmi->drv_priv = priv;
92 wmi->stopped = false; 92 wmi->stopped = false;
93 skb_queue_head_init(&wmi->wmi_event_queue); 93 skb_queue_head_init(&wmi->wmi_event_queue);
94 spin_lock_init(&wmi->wmi_lock);
95 spin_lock_init(&wmi->event_lock);
94 mutex_init(&wmi->op_mutex); 96 mutex_init(&wmi->op_mutex);
95 mutex_init(&wmi->multi_write_mutex); 97 mutex_init(&wmi->multi_write_mutex);
96 init_completion(&wmi->cmd_wait); 98 init_completion(&wmi->cmd_wait);
99 INIT_LIST_HEAD(&wmi->pending_tx_events);
97 tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet, 100 tasklet_init(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet,
98 (unsigned long)wmi); 101 (unsigned long)wmi);
99 102
diff --git a/drivers/net/wireless/ath/ath9k/wmi.h b/drivers/net/wireless/ath/ath9k/wmi.h
index 44b17385374f..310d94eaed19 100644
--- a/drivers/net/wireless/ath/ath9k/wmi.h
+++ b/drivers/net/wireless/ath/ath9k/wmi.h
@@ -130,6 +130,12 @@ struct register_write {
130 __be32 val; 130 __be32 val;
131}; 131};
132 132
133struct ath9k_htc_tx_event {
134 int count;
135 struct __wmi_event_txstatus txs;
136 struct list_head list;
137};
138
133struct wmi { 139struct wmi {
134 struct ath9k_htc_priv *drv_priv; 140 struct ath9k_htc_priv *drv_priv;
135 struct htc_target *htc; 141 struct htc_target *htc;
@@ -144,6 +150,9 @@ struct wmi {
144 u32 cmd_rsp_len; 150 u32 cmd_rsp_len;
145 bool stopped; 151 bool stopped;
146 152
153 struct list_head pending_tx_events;
154 spinlock_t event_lock;
155
147 spinlock_t wmi_lock; 156 spinlock_t wmi_lock;
148 157
149 atomic_t mwrite_cnt; 158 atomic_t mwrite_cnt;