summaryrefslogtreecommitdiff
path: root/drivers/net/sfc/tx.c
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2010-11-15 23:53:11 +0000
committerBen Hutchings <bhutchings@solarflare.com>2010-12-06 23:00:07 +0000
commitcd38557d78554fd4318fe448f728a8d7ff1cbabb (patch)
tree3b2ec090953fa1975a6e224417bf362aaae19cd7 /drivers/net/sfc/tx.c
parente506147271229d6c53b42c6a9897db67b5cfdb6d (diff)
downloadlwn-cd38557d78554fd4318fe448f728a8d7ff1cbabb.tar.gz
lwn-cd38557d78554fd4318fe448f728a8d7ff1cbabb.zip
sfc: Use TX push whenever adding descriptors to an empty queue
Whenever we add DMA descriptors to a TX ring and update the ring pointer, the TX DMA engine must first read the new DMA descriptors and then start reading packet data. However, all released Solarflare 10G controllers have a 'TX push' feature that allows us to reduce latency by writing the first new DMA descriptor along with the pointer update. This is only useful when the queue is empty. The hardware should ignore the pushed descriptor if the queue is not empty, but this check is buggy, so we must do it in software. In order to tell whether a TX queue is empty, we need to compare the previous transmission count (write_count) and completion count (read_count). However, if we do that every time we update the ring pointer then read_count may ping-pong between the caches of two CPUs running the transmission and completion paths for the queue. Therefore, we split the check for an empty queue between the completion path and the transmission path: - Add an empty_read_count field representing a point at which the completion path saw the TX queue as empty. - Add an old_write_count field for use on the completion path. - On the completion path, whenever read_count reaches or passes old_write_count the TX queue may be empty. We then read write_count, set empty_read_count if read_count == write_count, and update old_write_count. - On the transmission path, we read empty_read_count. If it's set, we compare it with the value of write_count before the current set of descriptors was added. If they match, the queue really is empty and we can use TX push. Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/sfc/tx.c')
-rw-r--r--drivers/net/sfc/tx.c12
1 files changed, 12 insertions, 0 deletions
diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c
index fef22351ddbd..bdb92b4af683 100644
--- a/drivers/net/sfc/tx.c
+++ b/drivers/net/sfc/tx.c
@@ -428,6 +428,16 @@ void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index)
__netif_tx_unlock(queue);
}
}
+
+ /* Check whether the hardware queue is now empty */
+ if ((int)(tx_queue->read_count - tx_queue->old_write_count) >= 0) {
+ tx_queue->old_write_count = ACCESS_ONCE(tx_queue->write_count);
+ if (tx_queue->read_count == tx_queue->old_write_count) {
+ smp_mb();
+ tx_queue->empty_read_count =
+ tx_queue->read_count | EFX_EMPTY_COUNT_VALID;
+ }
+ }
}
int efx_probe_tx_queue(struct efx_tx_queue *tx_queue)
@@ -473,8 +483,10 @@ void efx_init_tx_queue(struct efx_tx_queue *tx_queue)
tx_queue->insert_count = 0;
tx_queue->write_count = 0;
+ tx_queue->old_write_count = 0;
tx_queue->read_count = 0;
tx_queue->old_read_count = 0;
+ tx_queue->empty_read_count = 0 | EFX_EMPTY_COUNT_VALID;
BUG_ON(tx_queue->stopped);
/* Set up TX descriptor ring */