aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/sfc
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2010-11-15 18:53:11 -0500
committerBen Hutchings <bhutchings@solarflare.com>2010-12-06 18:00:07 -0500
commitcd38557d78554fd4318fe448f728a8d7ff1cbabb (patch)
tree3b2ec090953fa1975a6e224417bf362aaae19cd7 /drivers/net/sfc
parente506147271229d6c53b42c6a9897db67b5cfdb6d (diff)
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')
-rw-r--r--drivers/net/sfc/net_driver.h16
-rw-r--r--drivers/net/sfc/nic.c42
-rw-r--r--drivers/net/sfc/tx.c12
3 files changed, 68 insertions, 2 deletions
diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h
index 60d63711d43f..270e217a53f9 100644
--- a/drivers/net/sfc/net_driver.h
+++ b/drivers/net/sfc/net_driver.h
@@ -142,6 +142,12 @@ struct efx_tx_buffer {
142 * @flushed: Used when handling queue flushing 142 * @flushed: Used when handling queue flushing
143 * @read_count: Current read pointer. 143 * @read_count: Current read pointer.
144 * This is the number of buffers that have been removed from both rings. 144 * This is the number of buffers that have been removed from both rings.
145 * @old_write_count: The value of @write_count when last checked.
146 * This is here for performance reasons. The xmit path will
147 * only get the up-to-date value of @write_count if this
148 * variable indicates that the queue is empty. This is to
149 * avoid cache-line ping-pong between the xmit path and the
150 * completion path.
145 * @stopped: Stopped count. 151 * @stopped: Stopped count.
146 * Set if this TX queue is currently stopping its port. 152 * Set if this TX queue is currently stopping its port.
147 * @insert_count: Current insert pointer 153 * @insert_count: Current insert pointer
@@ -163,6 +169,10 @@ struct efx_tx_buffer {
163 * @tso_long_headers: Number of packets with headers too long for standard 169 * @tso_long_headers: Number of packets with headers too long for standard
164 * blocks 170 * blocks
165 * @tso_packets: Number of packets via the TSO xmit path 171 * @tso_packets: Number of packets via the TSO xmit path
172 * @pushes: Number of times the TX push feature has been used
173 * @empty_read_count: If the completion path has seen the queue as empty
174 * and the transmission path has not yet checked this, the value of
175 * @read_count bitwise-added to %EFX_EMPTY_COUNT_VALID; otherwise 0.
166 */ 176 */
167struct efx_tx_queue { 177struct efx_tx_queue {
168 /* Members which don't change on the fast path */ 178 /* Members which don't change on the fast path */
@@ -177,6 +187,7 @@ struct efx_tx_queue {
177 187
178 /* Members used mainly on the completion path */ 188 /* Members used mainly on the completion path */
179 unsigned int read_count ____cacheline_aligned_in_smp; 189 unsigned int read_count ____cacheline_aligned_in_smp;
190 unsigned int old_write_count;
180 int stopped; 191 int stopped;
181 192
182 /* Members used only on the xmit path */ 193 /* Members used only on the xmit path */
@@ -187,6 +198,11 @@ struct efx_tx_queue {
187 unsigned int tso_bursts; 198 unsigned int tso_bursts;
188 unsigned int tso_long_headers; 199 unsigned int tso_long_headers;
189 unsigned int tso_packets; 200 unsigned int tso_packets;
201 unsigned int pushes;
202
203 /* Members shared between paths and sometimes updated */
204 unsigned int empty_read_count ____cacheline_aligned_in_smp;
205#define EFX_EMPTY_COUNT_VALID 0x80000000
190}; 206};
191 207
192/** 208/**
diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c
index 9743cff15130..bda6b1bd072c 100644
--- a/drivers/net/sfc/nic.c
+++ b/drivers/net/sfc/nic.c
@@ -362,6 +362,35 @@ static inline void efx_notify_tx_desc(struct efx_tx_queue *tx_queue)
362 FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue); 362 FR_AZ_TX_DESC_UPD_DWORD_P0, tx_queue->queue);
363} 363}
364 364
365/* Write pointer and first descriptor for TX descriptor ring */
366static inline void efx_push_tx_desc(struct efx_tx_queue *tx_queue,
367 const efx_qword_t *txd)
368{
369 unsigned write_ptr;
370 efx_oword_t reg;
371
372 BUILD_BUG_ON(FRF_AZ_TX_DESC_LBN != 0);
373 BUILD_BUG_ON(FR_AA_TX_DESC_UPD_KER != FR_BZ_TX_DESC_UPD_P0);
374
375 write_ptr = tx_queue->write_count & tx_queue->ptr_mask;
376 EFX_POPULATE_OWORD_2(reg, FRF_AZ_TX_DESC_PUSH_CMD, true,
377 FRF_AZ_TX_DESC_WPTR, write_ptr);
378 reg.qword[0] = *txd;
379 efx_writeo_page(tx_queue->efx, &reg,
380 FR_BZ_TX_DESC_UPD_P0, tx_queue->queue);
381}
382
383static inline bool
384efx_may_push_tx_desc(struct efx_tx_queue *tx_queue, unsigned int write_count)
385{
386 unsigned empty_read_count = ACCESS_ONCE(tx_queue->empty_read_count);
387
388 if (empty_read_count == 0)
389 return false;
390
391 tx_queue->empty_read_count = 0;
392 return ((empty_read_count ^ write_count) & ~EFX_EMPTY_COUNT_VALID) == 0;
393}
365 394
366/* For each entry inserted into the software descriptor ring, create a 395/* For each entry inserted into the software descriptor ring, create a
367 * descriptor in the hardware TX descriptor ring (in host memory), and 396 * descriptor in the hardware TX descriptor ring (in host memory), and
@@ -373,6 +402,7 @@ void efx_nic_push_buffers(struct efx_tx_queue *tx_queue)
373 struct efx_tx_buffer *buffer; 402 struct efx_tx_buffer *buffer;
374 efx_qword_t *txd; 403 efx_qword_t *txd;
375 unsigned write_ptr; 404 unsigned write_ptr;
405 unsigned old_write_count = tx_queue->write_count;
376 406
377 BUG_ON(tx_queue->write_count == tx_queue->insert_count); 407 BUG_ON(tx_queue->write_count == tx_queue->insert_count);
378 408
@@ -391,7 +421,15 @@ void efx_nic_push_buffers(struct efx_tx_queue *tx_queue)
391 } while (tx_queue->write_count != tx_queue->insert_count); 421 } while (tx_queue->write_count != tx_queue->insert_count);
392 422
393 wmb(); /* Ensure descriptors are written before they are fetched */ 423 wmb(); /* Ensure descriptors are written before they are fetched */
394 efx_notify_tx_desc(tx_queue); 424
425 if (efx_may_push_tx_desc(tx_queue, old_write_count)) {
426 txd = efx_tx_desc(tx_queue,
427 old_write_count & tx_queue->ptr_mask);
428 efx_push_tx_desc(tx_queue, txd);
429 ++tx_queue->pushes;
430 } else {
431 efx_notify_tx_desc(tx_queue);
432 }
395} 433}
396 434
397/* Allocate hardware resources for a TX queue */ 435/* Allocate hardware resources for a TX queue */
@@ -1626,7 +1664,7 @@ void efx_nic_init_common(struct efx_nic *efx)
1626 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe); 1664 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER, 0xfe);
1627 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1); 1665 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_RX_SPACER_EN, 1);
1628 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1); 1666 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_ONE_PKT_PER_Q, 1);
1629 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 0); 1667 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_PUSH_EN, 1);
1630 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1); 1668 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_DIS_NON_IP_EV, 1);
1631 /* Enable SW_EV to inherit in char driver - assume harmless here */ 1669 /* Enable SW_EV to inherit in char driver - assume harmless here */
1632 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1); 1670 EFX_SET_OWORD_FIELD(temp, FRF_AZ_TX_SOFT_EVT_EN, 1);
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)
428 __netif_tx_unlock(queue); 428 __netif_tx_unlock(queue);
429 } 429 }
430 } 430 }
431
432 /* Check whether the hardware queue is now empty */
433 if ((int)(tx_queue->read_count - tx_queue->old_write_count) >= 0) {
434 tx_queue->old_write_count = ACCESS_ONCE(tx_queue->write_count);
435 if (tx_queue->read_count == tx_queue->old_write_count) {
436 smp_mb();
437 tx_queue->empty_read_count =
438 tx_queue->read_count | EFX_EMPTY_COUNT_VALID;
439 }
440 }
431} 441}
432 442
433int efx_probe_tx_queue(struct efx_tx_queue *tx_queue) 443int 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)
473 483
474 tx_queue->insert_count = 0; 484 tx_queue->insert_count = 0;
475 tx_queue->write_count = 0; 485 tx_queue->write_count = 0;
486 tx_queue->old_write_count = 0;
476 tx_queue->read_count = 0; 487 tx_queue->read_count = 0;
477 tx_queue->old_read_count = 0; 488 tx_queue->old_read_count = 0;
489 tx_queue->empty_read_count = 0 | EFX_EMPTY_COUNT_VALID;
478 BUG_ON(tx_queue->stopped); 490 BUG_ON(tx_queue->stopped);
479 491
480 /* Set up TX descriptor ring */ 492 /* Set up TX descriptor ring */