aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorSujith Manoharan <Sujith.Manoharan@atheros.com>2011-04-13 01:55:59 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-13 15:24:08 -0400
commitb587fc81a80b9656f64e89fe0a106ffa4b35abca (patch)
tree1945232f9c8adad3810229249f5e6aa5ca0f9057 /drivers/net
parentf2820f4583b233827f10d91adea70225e196d852 (diff)
ath9k_htc: Drain pending TX frames properly
When doing a channel set or a reset operation the pending frames queued up for transmission have to be flushed and sent to mac80211. Fixing this has to be done in two separate steps: * Flush queued frames and kill the URB TX completion handler. * Complete all the frames that in the TX pending queue. This patch adds proper support for draining and all the callsites namely, channel change/reset/idle/stop are fixed. A separate queue is used for handling failed frames. 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/hif_usb.c55
-rw-r--r--drivers/net/wireless/ath/ath9k/htc.h6
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_debug.c8
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_gpio.c3
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_main.c15
-rw-r--r--drivers/net/wireless/ath/ath9k/htc_drv_txrx.c31
6 files changed, 69 insertions, 49 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c
index 0b63a48462c..db07e7b9320 100644
--- a/drivers/net/wireless/ath/ath9k/hif_usb.c
+++ b/drivers/net/wireless/ath/ath9k/hif_usb.c
@@ -131,7 +131,19 @@ static inline void ath9k_skb_queue_purge(struct hif_device_usb *hif_dev,
131 131
132 while ((skb = __skb_dequeue(list)) != NULL) { 132 while ((skb = __skb_dequeue(list)) != NULL) {
133 dev_kfree_skb_any(skb); 133 dev_kfree_skb_any(skb);
134 TX_STAT_INC(skb_dropped); 134 }
135}
136
137static inline void ath9k_skb_queue_complete(struct hif_device_usb *hif_dev,
138 struct sk_buff_head *queue,
139 bool txok)
140{
141 struct sk_buff *skb;
142
143 while ((skb = __skb_dequeue(queue)) != NULL) {
144 ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
145 skb, txok);
146 (txok) ? TX_STAT_INC(skb_success) : TX_STAT_INC(skb_failed);
135 } 147 }
136} 148}
137 149
@@ -139,7 +151,7 @@ static void hif_usb_tx_cb(struct urb *urb)
139{ 151{
140 struct tx_buf *tx_buf = (struct tx_buf *) urb->context; 152 struct tx_buf *tx_buf = (struct tx_buf *) urb->context;
141 struct hif_device_usb *hif_dev; 153 struct hif_device_usb *hif_dev;
142 struct sk_buff *skb; 154 bool txok = true;
143 155
144 if (!tx_buf || !tx_buf->hif_dev) 156 if (!tx_buf || !tx_buf->hif_dev)
145 return; 157 return;
@@ -153,10 +165,7 @@ static void hif_usb_tx_cb(struct urb *urb)
153 case -ECONNRESET: 165 case -ECONNRESET:
154 case -ENODEV: 166 case -ENODEV:
155 case -ESHUTDOWN: 167 case -ESHUTDOWN:
156 /* 168 txok = false;
157 * The URB has been killed, free the SKBs.
158 */
159 ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
160 169
161 /* 170 /*
162 * If the URBs are being flushed, no need to add this 171 * If the URBs are being flushed, no need to add this
@@ -165,41 +174,19 @@ static void hif_usb_tx_cb(struct urb *urb)
165 spin_lock(&hif_dev->tx.tx_lock); 174 spin_lock(&hif_dev->tx.tx_lock);
166 if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) { 175 if (hif_dev->tx.flags & HIF_USB_TX_FLUSH) {
167 spin_unlock(&hif_dev->tx.tx_lock); 176 spin_unlock(&hif_dev->tx.tx_lock);
177 ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
168 return; 178 return;
169 } 179 }
170 spin_unlock(&hif_dev->tx.tx_lock); 180 spin_unlock(&hif_dev->tx.tx_lock);
171 181
172 /* 182 break;
173 * In the stop() case, this URB has to be added to
174 * the free list.
175 */
176 goto add_free;
177 default: 183 default:
184 txok = false;
178 break; 185 break;
179 } 186 }
180 187
181 /* 188 ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, txok);
182 * Check if TX has been stopped, this is needed because
183 * this CB could have been invoked just after the TX lock
184 * was released in hif_stop() and kill_urb() hasn't been
185 * called yet.
186 */
187 spin_lock(&hif_dev->tx.tx_lock);
188 if (hif_dev->tx.flags & HIF_USB_TX_STOP) {
189 spin_unlock(&hif_dev->tx.tx_lock);
190 ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue);
191 goto add_free;
192 }
193 spin_unlock(&hif_dev->tx.tx_lock);
194
195 /* Complete the queued SKBs. */
196 while ((skb = __skb_dequeue(&tx_buf->skb_queue)) != NULL) {
197 ath9k_htc_txcompletion_cb(hif_dev->htc_handle,
198 skb, 1);
199 TX_STAT_INC(skb_completed);
200 }
201 189
202add_free:
203 /* Re-initialize the SKB queue */ 190 /* Re-initialize the SKB queue */
204 tx_buf->len = tx_buf->offset = 0; 191 tx_buf->len = tx_buf->offset = 0;
205 __skb_queue_head_init(&tx_buf->skb_queue); 192 __skb_queue_head_init(&tx_buf->skb_queue);
@@ -272,7 +259,7 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev)
272 ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC); 259 ret = usb_submit_urb(tx_buf->urb, GFP_ATOMIC);
273 if (ret) { 260 if (ret) {
274 tx_buf->len = tx_buf->offset = 0; 261 tx_buf->len = tx_buf->offset = 0;
275 ath9k_skb_queue_purge(hif_dev, &tx_buf->skb_queue); 262 ath9k_skb_queue_complete(hif_dev, &tx_buf->skb_queue, false);
276 __skb_queue_head_init(&tx_buf->skb_queue); 263 __skb_queue_head_init(&tx_buf->skb_queue);
277 list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf); 264 list_move_tail(&tx_buf->list, &hif_dev->tx.tx_buf);
278 hif_dev->tx.tx_buf_cnt++; 265 hif_dev->tx.tx_buf_cnt++;
@@ -342,7 +329,7 @@ static void hif_usb_stop(void *hif_handle, u8 pipe_id)
342 unsigned long flags; 329 unsigned long flags;
343 330
344 spin_lock_irqsave(&hif_dev->tx.tx_lock, flags); 331 spin_lock_irqsave(&hif_dev->tx.tx_lock, flags);
345 ath9k_skb_queue_purge(hif_dev, &hif_dev->tx.tx_skb_queue); 332 ath9k_skb_queue_complete(hif_dev, &hif_dev->tx.tx_skb_queue, false);
346 hif_dev->tx.tx_skb_cnt = 0; 333 hif_dev->tx.tx_skb_cnt = 0;
347 hif_dev->tx.flags |= HIF_USB_TX_STOP; 334 hif_dev->tx.flags |= HIF_USB_TX_STOP;
348 spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags); 335 spin_unlock_irqrestore(&hif_dev->tx.tx_lock, flags);
diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h
index 45cf7557943..0d2e2b10358 100644
--- a/drivers/net/wireless/ath/ath9k/htc.h
+++ b/drivers/net/wireless/ath/ath9k/htc.h
@@ -271,6 +271,7 @@ struct ath9k_htc_tx {
271 u8 flags; 271 u8 flags;
272 int queued_cnt; 272 int queued_cnt;
273 struct sk_buff_head tx_queue; 273 struct sk_buff_head tx_queue;
274 struct sk_buff_head tx_failed;
274 DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM); 275 DECLARE_BITMAP(tx_slot, MAX_TX_BUF_NUM);
275 spinlock_t tx_lock; 276 spinlock_t tx_lock;
276}; 277};
@@ -305,8 +306,8 @@ struct ath_tx_stats {
305 u32 buf_queued; 306 u32 buf_queued;
306 u32 buf_completed; 307 u32 buf_completed;
307 u32 skb_queued; 308 u32 skb_queued;
308 u32 skb_completed; 309 u32 skb_success;
309 u32 skb_dropped; 310 u32 skb_failed;
310 u32 cab_queued; 311 u32 cab_queued;
311 u32 queue_stats[WME_NUM_AC]; 312 u32 queue_stats[WME_NUM_AC];
312}; 313};
@@ -544,6 +545,7 @@ void ath9k_htc_check_stop_queues(struct ath9k_htc_priv *priv);
544void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv); 545void ath9k_htc_check_wake_queues(struct ath9k_htc_priv *priv);
545int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv); 546int ath9k_htc_tx_get_slot(struct ath9k_htc_priv *priv);
546void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot); 547void ath9k_htc_tx_clear_slot(struct ath9k_htc_priv *priv, int slot);
548void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv);
547 549
548int ath9k_rx_init(struct ath9k_htc_priv *priv); 550int ath9k_rx_init(struct ath9k_htc_priv *priv);
549void ath9k_rx_cleanup(struct ath9k_htc_priv *priv); 551void ath9k_rx_cleanup(struct ath9k_htc_priv *priv);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
index 6fc6cb74936..91a486cca32 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c
@@ -88,11 +88,11 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf,
88 "%20s : %10u\n", "SKBs queued", 88 "%20s : %10u\n", "SKBs queued",
89 priv->debug.tx_stats.skb_queued); 89 priv->debug.tx_stats.skb_queued);
90 len += snprintf(buf + len, sizeof(buf) - len, 90 len += snprintf(buf + len, sizeof(buf) - len,
91 "%20s : %10u\n", "SKBs completed", 91 "%20s : %10u\n", "SKBs success",
92 priv->debug.tx_stats.skb_completed); 92 priv->debug.tx_stats.skb_success);
93 len += snprintf(buf + len, sizeof(buf) - len, 93 len += snprintf(buf + len, sizeof(buf) - len,
94 "%20s : %10u\n", "SKBs dropped", 94 "%20s : %10u\n", "SKBs failed",
95 priv->debug.tx_stats.skb_dropped); 95 priv->debug.tx_stats.skb_failed);
96 len += snprintf(buf + len, sizeof(buf) - len, 96 len += snprintf(buf + len, sizeof(buf) - len,
97 "%20s : %10u\n", "CAB queued", 97 "%20s : %10u\n", "CAB queued",
98 priv->debug.tx_stats.cab_queued); 98 priv->debug.tx_stats.cab_queued);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
index 92e4b312a98..dc0b33d0121 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c
@@ -429,9 +429,8 @@ void ath9k_htc_radio_disable(struct ieee80211_hw *hw)
429 429
430 /* Stop TX */ 430 /* Stop TX */
431 ieee80211_stop_queues(hw); 431 ieee80211_stop_queues(hw);
432 htc_stop(priv->htc); 432 ath9k_htc_tx_drain(priv);
433 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 433 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
434 skb_queue_purge(&priv->tx.tx_queue);
435 434
436 /* Stop RX */ 435 /* Stop RX */
437 WMI_CMD(WMI_STOP_RECV_CMDID); 436 WMI_CMD(WMI_STOP_RECV_CMDID);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
index c7e056b40e1..fb9ff1188a0 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
@@ -193,7 +193,9 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
193 193
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 htc_stop(priv->htc); 196
197 ath9k_htc_tx_drain(priv);
198
197 WMI_CMD(WMI_DISABLE_INTR_CMDID); 199 WMI_CMD(WMI_DISABLE_INTR_CMDID);
198 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 200 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
199 WMI_CMD(WMI_STOP_RECV_CMDID); 201 WMI_CMD(WMI_STOP_RECV_CMDID);
@@ -248,7 +250,9 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
248 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL); 250 fastcc = !!(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL);
249 251
250 ath9k_htc_ps_wakeup(priv); 252 ath9k_htc_ps_wakeup(priv);
251 htc_stop(priv->htc); 253
254 ath9k_htc_tx_drain(priv);
255
252 WMI_CMD(WMI_DISABLE_INTR_CMDID); 256 WMI_CMD(WMI_DISABLE_INTR_CMDID);
253 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 257 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
254 WMI_CMD(WMI_STOP_RECV_CMDID); 258 WMI_CMD(WMI_STOP_RECV_CMDID);
@@ -263,6 +267,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
263 267
264 if (!fastcc) 268 if (!fastcc)
265 caldata = &priv->caldata; 269 caldata = &priv->caldata;
270
266 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc); 271 ret = ath9k_hw_reset(ah, hchan, caldata, fastcc);
267 if (ret) { 272 if (ret) {
268 ath_err(common, 273 ath_err(common,
@@ -960,16 +965,14 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw)
960 } 965 }
961 966
962 ath9k_htc_ps_wakeup(priv); 967 ath9k_htc_ps_wakeup(priv);
963 htc_stop(priv->htc); 968
964 WMI_CMD(WMI_DISABLE_INTR_CMDID); 969 WMI_CMD(WMI_DISABLE_INTR_CMDID);
965 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID); 970 WMI_CMD(WMI_DRAIN_TXQ_ALL_CMDID);
966 WMI_CMD(WMI_STOP_RECV_CMDID); 971 WMI_CMD(WMI_STOP_RECV_CMDID);
967 972
968 tasklet_kill(&priv->rx_tasklet); 973 tasklet_kill(&priv->rx_tasklet);
969 tasklet_kill(&priv->tx_tasklet);
970
971 skb_queue_purge(&priv->tx.tx_queue);
972 974
975 ath9k_htc_tx_drain(priv);
973 ath9k_wmi_event_drain(priv); 976 ath9k_wmi_event_drain(priv);
974 977
975 mutex_unlock(&priv->mutex); 978 mutex_unlock(&priv->mutex);
diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
index 944440c84c4..9e0c34b0a79 100644
--- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
+++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
@@ -423,6 +423,26 @@ send_mac80211:
423 ieee80211_tx_status(priv->hw, skb); 423 ieee80211_tx_status(priv->hw, skb);
424} 424}
425 425
426void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
427{
428 struct sk_buff *skb = NULL;
429
430 /*
431 * Ensure that all pending TX frames are flushed,
432 * and that the TX completion tasklet is killed.
433 */
434 htc_stop(priv->htc);
435 tasklet_kill(&priv->tx_tasklet);
436
437 while ((skb = skb_dequeue(&priv->tx.tx_queue)) != NULL) {
438 ath9k_htc_tx_process(priv, skb);
439 }
440
441 while ((skb = skb_dequeue(&priv->tx.tx_failed)) != NULL) {
442 ath9k_htc_tx_process(priv, skb);
443 }
444}
445
426void ath9k_tx_tasklet(unsigned long data) 446void ath9k_tx_tasklet(unsigned long data)
427{ 447{
428 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; 448 struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
@@ -432,6 +452,10 @@ void ath9k_tx_tasklet(unsigned long data)
432 ath9k_htc_tx_process(priv, skb); 452 ath9k_htc_tx_process(priv, skb);
433 } 453 }
434 454
455 while ((skb = skb_dequeue(&priv->tx.tx_failed)) != NULL) {
456 ath9k_htc_tx_process(priv, skb);
457 }
458
435 /* Wake TX queues if needed */ 459 /* Wake TX queues if needed */
436 ath9k_htc_check_wake_queues(priv); 460 ath9k_htc_check_wake_queues(priv);
437} 461}
@@ -445,13 +469,18 @@ void ath9k_htc_txep(void *drv_priv, struct sk_buff *skb,
445 tx_ctl = HTC_SKB_CB(skb); 469 tx_ctl = HTC_SKB_CB(skb);
446 tx_ctl->txok = txok; 470 tx_ctl->txok = txok;
447 471
448 skb_queue_tail(&priv->tx.tx_queue, skb); 472 if (txok)
473 skb_queue_tail(&priv->tx.tx_queue, skb);
474 else
475 skb_queue_tail(&priv->tx.tx_failed, skb);
476
449 tasklet_schedule(&priv->tx_tasklet); 477 tasklet_schedule(&priv->tx_tasklet);
450} 478}
451 479
452int ath9k_tx_init(struct ath9k_htc_priv *priv) 480int ath9k_tx_init(struct ath9k_htc_priv *priv)
453{ 481{
454 skb_queue_head_init(&priv->tx.tx_queue); 482 skb_queue_head_init(&priv->tx.tx_queue);
483 skb_queue_head_init(&priv->tx.tx_failed);
455 return 0; 484 return 0;
456} 485}
457 486