[rt2x00-users] [PATCH RFC] rt2800usb: TX_STA_FIFO read timer

Johannes Stezenbach js at sig21.net
Tue Jan 11 02:40:57 EST 2011


Hi,

as discussed some time ago my previous patch
"rt2800usb: read TX_STA_FIFO asynchronously"
(in rt2x00/experimental) makes it more likely
for TX_STA_FIFO entries to be stuck in the FIFO until
the watchdog picks them up, because the TX_STA_FIFO read
is now happening quicker after TX DMA is done.

To fix that, a timer is added which reads FIFO entries
after a few msecs.  Below is a preliminary patch for discussion
(not for applying) which outlines the idea, and contains
some debug printk to verify it works.  Typical output is:


[16840.291307] rt2800usb_tx_dma_done
[16840.291630] rt2800usb_tx_sta_fifo_read_completed: pending
[16840.297192] rt2800usb_tx_sta_fifo_timeout
[16840.297587] rt2800usb_tx_sta_fifo_read_completed: got 0000ffb1
[16846.688729] rt2800usb_tx_dma_done
[16846.689057] rt2800usb_tx_sta_fifo_read_completed: pending
[16846.697989] rt2800usb_tx_sta_fifo_timeout
[16846.698385] rt2800usb_tx_sta_fifo_read_completed: got 0000ff99
[16848.206794] rt2800usb_tx_dma_done
[16848.207120] rt2800usb_tx_sta_fifo_read_completed: pending
[16848.208189] rt2800usb_tx_sta_fifo_timeout
[16848.208621] rt2800usb_tx_sta_fifo_read_completed: pending
[16848.218167] rt2800usb_tx_sta_fifo_timeout
[16848.218583] rt2800usb_tx_sta_fifo_read_completed: pending
[16848.228162] rt2800usb_tx_sta_fifo_timeout
[16848.228578] rt2800usb_tx_sta_fifo_read_completed: got 0000ff89
[16852.106278] rt2800usb_tx_dma_done
[16852.106572] rt2800usb_tx_sta_fifo_read_completed: pending
[16852.108674] rt2800usb_tx_sta_fifo_timeout
[16852.109075] rt2800usb_tx_sta_fifo_read_completed: got 0000ffb1

Note that some of the packets have TX_STA_FIFO_TX_SUCCESS not set.

During iperf run it looks like:

[18207.145619] rt2800usb_tx_dma_done
[18207.145908] rt2800usb_tx_sta_fifo_read_completed: pending
[18207.148024] rt2800usb_tx_sta_fifo_timeout
[18207.148426] rt2800usb_tx_sta_fifo_read_completed: pending
[18207.158121] rt2800usb_tx_sta_fifo_timeout
[18207.158488] rt2800usb_tx_sta_fifo_read_completed: got 0000ff91
[18207.158794] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.159039] rt2800usb_tx_sta_fifo_read_completed: got 40070cb5
[18207.159378] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.159632] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.159874] rt2800usb_tx_sta_fifo_read_completed: got 40060cb5
[18207.160121] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.161262] rt2800usb_tx_dma_done
[18207.161552] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.161798] rt2800usb_tx_sta_fifo_read_completed: got 40070cb5
[18207.162046] rt2800usb_tx_sta_fifo_read_completed: got 40060cbd
[18207.162288] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.162518] rt2800usb_tx_sta_fifo_read_completed: got 40070cb5
[18207.162785] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.163015] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.164391] rt2800usb_tx_dma_done
[18207.164681] rt2800usb_tx_sta_fifo_read_completed: got 40060cb5
[18207.164929] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.165175] rt2800usb_tx_sta_fifo_read_completed: got 40070cad
[18207.165423] rt2800usb_tx_sta_fifo_read_completed: got 40070cb5
[18207.165670] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.166247] rt2800usb_tx_dma_done
[18207.167273] rt2800usb_tx_dma_done
[18207.169268] rt2800usb_tx_dma_done
[18207.169567] rt2800usb_tx_sta_fifo_read_completed: got 40050cad
[18207.169833] rt2800usb_tx_sta_fifo_read_completed: got 40070cb5
[18207.170086] rt2800usb_tx_sta_fifo_read_completed: got 40070cbd
[18207.170725] rt2800usb_tx_dma_done
[18207.172743] rt2800usb_tx_dma_done


(The platform has HZ=100, NOHZ and hrtimers.  Debug output taken with
console log level = 0 and then run dmesg, to get useful timestamps
on serial console.)

The timer is armed after "pending" with a timeout of 10ms
and triggers a new async register read from TX_STA_FIFO.  

In theory it shouldn't take that long to send a packet but the
debug output suggests otherwise so I picked 10ms for the timeout.
I guess there is a lot of traffic from other APs in the neighbourhood
on the same channel.

One could cancel the timer in rt2800usb_tx_dma_done() when the
next packet arrives but it seems not worth it.

I'll do a bit more testing with debug removed tomorrow and send
a clean patch if you agree on the approach.


Thanks,
Johannes


diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c
index 2d91381..ee4e92c 100644
--- a/drivers/net/wireless/rt2x00/rt2800usb.c
+++ b/drivers/net/wireless/rt2x00/rt2800usb.c
@@ -98,6 +98,22 @@ static void rt2800usb_stop_queue(struct data_queue *queue)
 	}
 }
 
+/*
+ * test if there is an entry in any TX queue for which DMA is done
+ * but the TX status has not been returned yet
+ */
+static bool rt2800usb_txstatus_pending(struct rt2x00_dev *rt2x00dev)
+{
+	struct data_queue *queue;
+
+	tx_queue_for_each(rt2x00dev, queue) {
+		if (rt2x00queue_get_entry(queue, Q_INDEX_DMA_DONE) !=
+		    rt2x00queue_get_entry(queue, Q_INDEX_DONE))
+			return true;
+	}
+	return false;
+}
+
 static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
 						 int urb_status, u32 tx_status)
 {
@@ -108,6 +124,7 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
 
 	/* try to read all TX_STA_FIFO entries before scheduling txdone_work */
 	if (rt2x00_get_field32(tx_status, TX_STA_FIFO_VALID)) {
+printk("%s: got %08x\n", __func__, tx_status);
 		if (!kfifo_put(&rt2x00dev->txstatus_fifo, &tx_status)) {
 			WARNING(rt2x00dev, "TX status FIFO overrun, "
 				"drop tx status report.\n");
@@ -117,12 +134,32 @@ static void rt2800usb_tx_sta_fifo_read_completed(struct rt2x00_dev *rt2x00dev,
 						      rt2800usb_tx_sta_fifo_read_completed);
 	} else if (!kfifo_is_empty(&rt2x00dev->txstatus_fifo))
 		ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->txdone_work);
+	else if (rt2800usb_txstatus_pending(rt2x00dev)) {
+printk("%s: pending\n", __func__);
+		/* the hw may delay sending the packet after DMA complete
+		 * if the medium is busy, thus the TX_STA_FIFO entry is
+		 * also delayed; use a timer to pick it up
+		 */
+		mod_timer(&rt2x00dev->txstatus_timer, jiffies + msecs_to_jiffies(10));
+	}
+else
+printk("%s: ---\n", __func__);
 }
 
 static void rt2800usb_tx_dma_done(struct queue_entry *entry)
 {
 	struct rt2x00_dev *rt2x00dev = entry->queue->rt2x00dev;
 
+printk("%s\n", __func__);
+	rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
+				      rt2800usb_tx_sta_fifo_read_completed);
+}
+
+static void rt2800usb_tx_sta_fifo_timeout(unsigned long data)
+{
+	struct rt2x00_dev *rt2x00dev = (struct rt2x00_dev *)data;
+
+printk("%s\n", __func__);
 	rt2x00usb_register_read_async(rt2x00dev, TX_STA_FIFO,
 				      rt2800usb_tx_sta_fifo_read_completed);
 }
@@ -592,6 +629,10 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev)
 	__set_bit(DRIVER_SUPPORT_WATCHDOG, &rt2x00dev->flags);
 	__set_bit(DRIVER_REQUIRE_TXSTATUS_FIFO, &rt2x00dev->flags);
 
+	setup_timer(&rt2x00dev->txstatus_timer,
+		    rt2800usb_tx_sta_fifo_timeout,
+		    (unsigned long) rt2x00dev);
+
 	/*
 	 * Set the rssi offset.
 	 */
diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h
index 5592fca..e861c28 100644
--- a/drivers/net/wireless/rt2x00/rt2x00.h
+++ b/drivers/net/wireless/rt2x00/rt2x00.h
@@ -37,6 +37,7 @@
 #include <linux/etherdevice.h>
 #include <linux/input-polldev.h>
 #include <linux/kfifo.h>
+#include <linux/timer.h>
 
 #include <net/mac80211.h>
 
@@ -899,6 +900,11 @@ struct rt2x00_dev {
 	DECLARE_KFIFO_PTR(txstatus_fifo, u32);
 
 	/*
+	 * Timer to ensure tx status reports are read (rt2800usb).
+	 */
+	struct timer_list txstatus_timer;
+
+	/*
 	 * Tasklet for processing tx status reports (rt2800pci).
 	 */
 	struct tasklet_struct txstatus_tasklet;



More information about the users mailing list