aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvo van Doorn <ivdoorn@gmail.com>2010-08-30 15:15:19 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-08-31 14:22:25 -0400
commit652a9dd2a0c07251e328519cc23f1316ab13ed51 (patch)
tree836afb7f5062716ce7391ad242786c1d2b3d2bd9
parent0e3afe5b20c4ccdeff5178c62b557a917945a828 (diff)
rt2x00: Split watchdog check into a DMA and STATUS timeout
The watchdog for rt2800usb triggers frequently causing all URB's to be canceled often enough to interrupt the normal TX flow. More research indicated that not the URB upload to the USB host were hanging, but instead the TX status reports. To correctly detect what is going on, we introduce Q_INDEX_DMA_DONE which is an index counter between Q_INDEX_DONE and Q_INDEX and indicates if the frame has been transfered to the device. This also requires the rt2x00queue timeout functions to be updated to differentiate between a DMA timeout (time between Q_INDEX and Q_INDEX_DMA_DONE timeout) and a STATUS timeout (time between Q_INDEX_DMA_DONE and Q_INDEX_DONE timeout) All Q_INDEX_DMA_DONE code was taken from the RFC from Helmut Schaa <helmut.schaa@googlemail.com> for the implementation for watchdog for rt2800pci. Signed-off-by: Ivo van Doorn <IvDoorn@gmail.com> Acked-by: Gertjan van Wingerde <gwingerde@gmail.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00.h1
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00debug.c5
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00dev.c6
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.c13
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00queue.h21
-rw-r--r--drivers/net/wireless/rt2x00/rt2x00usb.c27
6 files changed, 59 insertions, 14 deletions
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 762f6b4e7afc..0ae942cb66df 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -1070,6 +1070,7 @@ static inline void rt2x00debug_dump_frame(struct rt2x00_dev *rt2x00dev,
1070 */ 1070 */
1071void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev); 1071void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev);
1072void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev); 1072void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev);
1073void rt2x00lib_dmadone(struct queue_entry *entry);
1073void rt2x00lib_txdone(struct queue_entry *entry, 1074void rt2x00lib_txdone(struct queue_entry *entry,
1074 struct txdone_entry_desc *txdesc); 1075 struct txdone_entry_desc *txdesc);
1075void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status); 1076void rt2x00lib_txdone_noinfo(struct queue_entry *entry, u32 status);
diff --git a/drivers/net/wireless/rt2x00/rt2x00debug.c b/drivers/net/wireless/rt2x00/rt2x00debug.c
index daf7f950a28f..54dc44bb415c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00debug.c
+++ b/drivers/net/wireless/rt2x00/rt2x00debug.c
@@ -338,14 +338,15 @@ static ssize_t rt2x00debug_read_queue_stats(struct file *file,
338 return -ENOMEM; 338 return -ENOMEM;
339 339
340 temp = data + 340 temp = data +
341 sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdone\n"); 341 sprintf(data, "qid\tcount\tlimit\tlength\tindex\tdma done\tdone\n");
342 342
343 queue_for_each(intf->rt2x00dev, queue) { 343 queue_for_each(intf->rt2x00dev, queue) {
344 spin_lock_irqsave(&queue->lock, irqflags); 344 spin_lock_irqsave(&queue->lock, irqflags);
345 345
346 temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid, 346 temp += sprintf(temp, "%d\t%d\t%d\t%d\t%d\t%d\t%d\n", queue->qid,
347 queue->count, queue->limit, queue->length, 347 queue->count, queue->limit, queue->length,
348 queue->index[Q_INDEX], 348 queue->index[Q_INDEX],
349 queue->index[Q_INDEX_DMA_DONE],
349 queue->index[Q_INDEX_DONE]); 350 queue->index[Q_INDEX_DONE]);
350 351
351 spin_unlock_irqrestore(&queue->lock, irqflags); 352 spin_unlock_irqrestore(&queue->lock, irqflags);
diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c
index 580595ba5683..053fdd3bd720 100644
--- a/drivers/net/wireless/rt2x00/rt2x00dev.c
+++ b/drivers/net/wireless/rt2x00/rt2x00dev.c
@@ -251,6 +251,12 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev)
251} 251}
252EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); 252EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt);
253 253
254void rt2x00lib_dmadone(struct queue_entry *entry)
255{
256 rt2x00queue_index_inc(entry->queue, Q_INDEX_DMA_DONE);
257}
258EXPORT_SYMBOL_GPL(rt2x00lib_dmadone);
259
254void rt2x00lib_txdone(struct queue_entry *entry, 260void rt2x00lib_txdone(struct queue_entry *entry,
255 struct txdone_entry_desc *txdesc) 261 struct txdone_entry_desc *txdesc)
256{ 262{
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.c b/drivers/net/wireless/rt2x00/rt2x00queue.c
index ecf57635ae51..6d41599a090c 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.c
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.c
@@ -731,13 +731,13 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
731 if (queue->index[index] >= queue->limit) 731 if (queue->index[index] >= queue->limit)
732 queue->index[index] = 0; 732 queue->index[index] = 0;
733 733
734 queue->last_action[index] = jiffies;
735
734 if (index == Q_INDEX) { 736 if (index == Q_INDEX) {
735 queue->length++; 737 queue->length++;
736 queue->last_index = jiffies;
737 } else if (index == Q_INDEX_DONE) { 738 } else if (index == Q_INDEX_DONE) {
738 queue->length--; 739 queue->length--;
739 queue->count++; 740 queue->count++;
740 queue->last_index_done = jiffies;
741 } 741 }
742 742
743 spin_unlock_irqrestore(&queue->lock, irqflags); 743 spin_unlock_irqrestore(&queue->lock, irqflags);
@@ -746,14 +746,17 @@ void rt2x00queue_index_inc(struct data_queue *queue, enum queue_index index)
746static void rt2x00queue_reset(struct data_queue *queue) 746static void rt2x00queue_reset(struct data_queue *queue)
747{ 747{
748 unsigned long irqflags; 748 unsigned long irqflags;
749 unsigned int i;
749 750
750 spin_lock_irqsave(&queue->lock, irqflags); 751 spin_lock_irqsave(&queue->lock, irqflags);
751 752
752 queue->count = 0; 753 queue->count = 0;
753 queue->length = 0; 754 queue->length = 0;
754 queue->last_index = jiffies; 755
755 queue->last_index_done = jiffies; 756 for (i = 0; i < Q_INDEX_MAX; i++) {
756 memset(queue->index, 0, sizeof(queue->index)); 757 queue->index[i] = 0;
758 queue->last_action[i] = jiffies;
759 }
757 760
758 spin_unlock_irqrestore(&queue->lock, irqflags); 761 spin_unlock_irqrestore(&queue->lock, irqflags);
759} 762}
diff --git a/drivers/net/wireless/rt2x00/rt2x00queue.h b/drivers/net/wireless/rt2x00/rt2x00queue.h
index 0e38a911195d..d81d85f34866 100644
--- a/drivers/net/wireless/rt2x00/rt2x00queue.h
+++ b/drivers/net/wireless/rt2x00/rt2x00queue.h
@@ -401,6 +401,8 @@ struct queue_entry {
401 * 401 *
402 * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is 402 * @Q_INDEX: Index pointer to the current entry in the queue, if this entry is
403 * owned by the hardware then the queue is considered to be full. 403 * owned by the hardware then the queue is considered to be full.
404 * @Q_INDEX_DMA_DONE: Index pointer for the next entry which will have been
405 * transfered to the hardware.
404 * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by 406 * @Q_INDEX_DONE: Index pointer to the next entry which will be completed by
405 * the hardware and for which we need to run the txdone handler. If this 407 * the hardware and for which we need to run the txdone handler. If this
406 * entry is not owned by the hardware the queue is considered to be empty. 408 * entry is not owned by the hardware the queue is considered to be empty.
@@ -409,6 +411,7 @@ struct queue_entry {
409 */ 411 */
410enum queue_index { 412enum queue_index {
411 Q_INDEX, 413 Q_INDEX,
414 Q_INDEX_DMA_DONE,
412 Q_INDEX_DONE, 415 Q_INDEX_DONE,
413 Q_INDEX_MAX, 416 Q_INDEX_MAX,
414}; 417};
@@ -445,13 +448,12 @@ struct data_queue {
445 enum data_queue_qid qid; 448 enum data_queue_qid qid;
446 449
447 spinlock_t lock; 450 spinlock_t lock;
448 unsigned long last_index;
449 unsigned long last_index_done;
450 unsigned int count; 451 unsigned int count;
451 unsigned short limit; 452 unsigned short limit;
452 unsigned short threshold; 453 unsigned short threshold;
453 unsigned short length; 454 unsigned short length;
454 unsigned short index[Q_INDEX_MAX]; 455 unsigned short index[Q_INDEX_MAX];
456 unsigned long last_action[Q_INDEX_MAX];
455 457
456 unsigned short txop; 458 unsigned short txop;
457 unsigned short aifs; 459 unsigned short aifs;
@@ -616,12 +618,23 @@ static inline int rt2x00queue_threshold(struct data_queue *queue)
616} 618}
617 619
618/** 620/**
619 * rt2x00queue_timeout - Check if a timeout occured for this queue 621 * rt2x00queue_timeout - Check if a timeout occured for STATUS reorts
620 * @queue: Queue to check. 622 * @queue: Queue to check.
621 */ 623 */
622static inline int rt2x00queue_timeout(struct data_queue *queue) 624static inline int rt2x00queue_timeout(struct data_queue *queue)
623{ 625{
624 return time_after(queue->last_index, queue->last_index_done + (HZ / 10)); 626 return time_after(queue->last_action[Q_INDEX_DMA_DONE],
627 queue->last_action[Q_INDEX_DONE] + (HZ / 10));
628}
629
630/**
631 * rt2x00queue_timeout - Check if a timeout occured for DMA transfers
632 * @queue: Queue to check.
633 */
634static inline int rt2x00queue_dma_timeout(struct data_queue *queue)
635{
636 return time_after(queue->last_action[Q_INDEX],
637 queue->last_action[Q_INDEX_DMA_DONE] + (HZ / 10));
625} 638}
626 639
627/** 640/**
diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.c b/drivers/net/wireless/rt2x00/rt2x00usb.c
index 6cc7aa418d87..aec6440d364a 100644
--- a/drivers/net/wireless/rt2x00/rt2x00usb.c
+++ b/drivers/net/wireless/rt2x00/rt2x00usb.c
@@ -213,6 +213,11 @@ static void rt2x00usb_interrupt_txdone(struct urb *urb)
213 return; 213 return;
214 214
215 /* 215 /*
216 * Report the frame as DMA done
217 */
218 rt2x00lib_dmadone(entry);
219
220 /*
216 * Check if the frame was correctly uploaded 221 * Check if the frame was correctly uploaded
217 */ 222 */
218 if (urb->status) 223 if (urb->status)
@@ -283,13 +288,14 @@ void rt2x00usb_kill_tx_queue(struct data_queue *queue)
283} 288}
284EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue); 289EXPORT_SYMBOL_GPL(rt2x00usb_kill_tx_queue);
285 290
286static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue) 291static void rt2x00usb_watchdog_tx_dma(struct data_queue *queue)
287{ 292{
288 struct queue_entry *entry; 293 struct queue_entry *entry;
289 struct queue_entry_priv_usb *entry_priv; 294 struct queue_entry_priv_usb *entry_priv;
290 unsigned short threshold = queue->threshold; 295 unsigned short threshold = queue->threshold;
291 296
292 WARNING(queue->rt2x00dev, "TX queue %d timed out, invoke reset", queue->qid); 297 WARNING(queue->rt2x00dev, "TX queue %d DMA timed out,"
298 " invoke forced forced reset", queue->qid);
293 299
294 /* 300 /*
295 * Temporarily disable the TX queue, this will force mac80211 301 * Temporarily disable the TX queue, this will force mac80211
@@ -331,13 +337,23 @@ static void rt2x00usb_watchdog_reset_tx(struct data_queue *queue)
331 ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid); 337 ieee80211_wake_queue(queue->rt2x00dev->hw, queue->qid);
332} 338}
333 339
340static void rt2x00usb_watchdog_tx_status(struct data_queue *queue)
341{
342 WARNING(queue->rt2x00dev, "TX queue %d status timed out,"
343 " invoke forced tx handler", queue->qid);
344
345 ieee80211_queue_work(queue->rt2x00dev->hw, &queue->rt2x00dev->txdone_work);
346}
347
334void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev) 348void rt2x00usb_watchdog(struct rt2x00_dev *rt2x00dev)
335{ 349{
336 struct data_queue *queue; 350 struct data_queue *queue;
337 351
338 tx_queue_for_each(rt2x00dev, queue) { 352 tx_queue_for_each(rt2x00dev, queue) {
353 if (rt2x00queue_dma_timeout(queue))
354 rt2x00usb_watchdog_tx_dma(queue);
339 if (rt2x00queue_timeout(queue)) 355 if (rt2x00queue_timeout(queue))
340 rt2x00usb_watchdog_reset_tx(queue); 356 rt2x00usb_watchdog_tx_status(queue);
341 } 357 }
342} 358}
343EXPORT_SYMBOL_GPL(rt2x00usb_watchdog); 359EXPORT_SYMBOL_GPL(rt2x00usb_watchdog);
@@ -383,6 +399,11 @@ static void rt2x00usb_interrupt_rxdone(struct urb *urb)
383 return; 399 return;
384 400
385 /* 401 /*
402 * Report the frame as DMA done
403 */
404 rt2x00lib_dmadone(entry);
405
406 /*
386 * Check if the received data is simply too small 407 * Check if the received data is simply too small
387 * to be actually valid, or if the urb is signaling 408 * to be actually valid, or if the urb is signaling
388 * a problem. 409 * a problem.