diff options
author | Daniel Pieczko <dpieczko@solarflare.com> | 2012-10-02 08:36:18 -0400 |
---|---|---|
committer | Ben Hutchings <bhutchings@solarflare.com> | 2012-11-30 21:37:27 -0500 |
commit | 525d9e824018cd7cc8d8d44832ddcd363abfe6e1 (patch) | |
tree | cedb42187455981ebd1cb05a9bba0a836b5a94eb /drivers/net/ethernet/sfc | |
parent | 876be083b669c43203c0ee8709d749896e1d8d60 (diff) |
sfc: Work-around flush timeout when flushes have completed
We sometimes hit a "failed to flush" timeout on some TX queues, but the
flushes have completed and the flush completion events seem to go missing.
In this case, we can check the TX_DESC_PTR_TBL register and drain the
queues if the flushes had finished.
[bwh: Minor fixes to coding style]
Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
Diffstat (limited to 'drivers/net/ethernet/sfc')
-rw-r--r-- | drivers/net/ethernet/sfc/net_driver.h | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/sfc/nic.c | 56 |
2 files changed, 53 insertions, 4 deletions
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 1014556df0e7..2d756c1d7142 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h | |||
@@ -200,6 +200,7 @@ struct efx_tx_queue { | |||
200 | /* Members shared between paths and sometimes updated */ | 200 | /* Members shared between paths and sometimes updated */ |
201 | unsigned int empty_read_count ____cacheline_aligned_in_smp; | 201 | unsigned int empty_read_count ____cacheline_aligned_in_smp; |
202 | #define EFX_EMPTY_COUNT_VALID 0x80000000 | 202 | #define EFX_EMPTY_COUNT_VALID 0x80000000 |
203 | atomic_t flush_outstanding; | ||
203 | }; | 204 | }; |
204 | 205 | ||
205 | /** | 206 | /** |
diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index 1327f29d086f..0ad790cc473c 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c | |||
@@ -73,6 +73,8 @@ | |||
73 | _EFX_CHANNEL_MAGIC(_EFX_CHANNEL_MAGIC_TX_DRAIN, \ | 73 | _EFX_CHANNEL_MAGIC(_EFX_CHANNEL_MAGIC_TX_DRAIN, \ |
74 | (_tx_queue)->queue) | 74 | (_tx_queue)->queue) |
75 | 75 | ||
76 | static void efx_magic_event(struct efx_channel *channel, u32 magic); | ||
77 | |||
76 | /************************************************************************** | 78 | /************************************************************************** |
77 | * | 79 | * |
78 | * Solarstorm hardware access | 80 | * Solarstorm hardware access |
@@ -491,6 +493,9 @@ static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue) | |||
491 | struct efx_nic *efx = tx_queue->efx; | 493 | struct efx_nic *efx = tx_queue->efx; |
492 | efx_oword_t tx_flush_descq; | 494 | efx_oword_t tx_flush_descq; |
493 | 495 | ||
496 | WARN_ON(atomic_read(&tx_queue->flush_outstanding)); | ||
497 | atomic_set(&tx_queue->flush_outstanding, 1); | ||
498 | |||
494 | EFX_POPULATE_OWORD_2(tx_flush_descq, | 499 | EFX_POPULATE_OWORD_2(tx_flush_descq, |
495 | FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, | 500 | FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, |
496 | FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); | 501 | FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); |
@@ -666,6 +671,47 @@ static bool efx_flush_wake(struct efx_nic *efx) | |||
666 | && atomic_read(&efx->rxq_flush_pending) > 0)); | 671 | && atomic_read(&efx->rxq_flush_pending) > 0)); |
667 | } | 672 | } |
668 | 673 | ||
674 | static bool efx_check_tx_flush_complete(struct efx_nic *efx) | ||
675 | { | ||
676 | bool i = true; | ||
677 | efx_oword_t txd_ptr_tbl; | ||
678 | struct efx_channel *channel; | ||
679 | struct efx_tx_queue *tx_queue; | ||
680 | |||
681 | efx_for_each_channel(channel, efx) { | ||
682 | efx_for_each_channel_tx_queue(tx_queue, channel) { | ||
683 | efx_reado_table(efx, &txd_ptr_tbl, | ||
684 | FR_BZ_TX_DESC_PTR_TBL, tx_queue->queue); | ||
685 | if (EFX_OWORD_FIELD(txd_ptr_tbl, | ||
686 | FRF_AZ_TX_DESCQ_FLUSH) || | ||
687 | EFX_OWORD_FIELD(txd_ptr_tbl, | ||
688 | FRF_AZ_TX_DESCQ_EN)) { | ||
689 | netif_dbg(efx, hw, efx->net_dev, | ||
690 | "flush did not complete on TXQ %d\n", | ||
691 | tx_queue->queue); | ||
692 | i = false; | ||
693 | } else if (atomic_cmpxchg(&tx_queue->flush_outstanding, | ||
694 | 1, 0)) { | ||
695 | /* The flush is complete, but we didn't | ||
696 | * receive a flush completion event | ||
697 | */ | ||
698 | netif_dbg(efx, hw, efx->net_dev, | ||
699 | "flush complete on TXQ %d, so drain " | ||
700 | "the queue\n", tx_queue->queue); | ||
701 | /* Don't need to increment drain_pending as it | ||
702 | * has already been incremented for the queues | ||
703 | * which did not drain | ||
704 | */ | ||
705 | efx_magic_event(channel, | ||
706 | EFX_CHANNEL_MAGIC_TX_DRAIN( | ||
707 | tx_queue)); | ||
708 | } | ||
709 | } | ||
710 | } | ||
711 | |||
712 | return i; | ||
713 | } | ||
714 | |||
669 | /* Flush all the transmit queues, and continue flushing receive queues until | 715 | /* Flush all the transmit queues, and continue flushing receive queues until |
670 | * they're all flushed. Wait for the DRAIN events to be recieved so that there | 716 | * they're all flushed. Wait for the DRAIN events to be recieved so that there |
671 | * are no more RX and TX events left on any channel. */ | 717 | * are no more RX and TX events left on any channel. */ |
@@ -726,7 +772,8 @@ int efx_nic_flush_queues(struct efx_nic *efx) | |||
726 | timeout); | 772 | timeout); |
727 | } | 773 | } |
728 | 774 | ||
729 | if (atomic_read(&efx->drain_pending)) { | 775 | if (atomic_read(&efx->drain_pending) && |
776 | !efx_check_tx_flush_complete(efx)) { | ||
730 | netif_err(efx, hw, efx->net_dev, "failed to flush %d queues " | 777 | netif_err(efx, hw, efx->net_dev, "failed to flush %d queues " |
731 | "(rx %d+%d)\n", atomic_read(&efx->drain_pending), | 778 | "(rx %d+%d)\n", atomic_read(&efx->drain_pending), |
732 | atomic_read(&efx->rxq_flush_outstanding), | 779 | atomic_read(&efx->rxq_flush_outstanding), |
@@ -1018,9 +1065,10 @@ efx_handle_tx_flush_done(struct efx_nic *efx, efx_qword_t *event) | |||
1018 | if (qid < EFX_TXQ_TYPES * efx->n_tx_channels) { | 1065 | if (qid < EFX_TXQ_TYPES * efx->n_tx_channels) { |
1019 | tx_queue = efx_get_tx_queue(efx, qid / EFX_TXQ_TYPES, | 1066 | tx_queue = efx_get_tx_queue(efx, qid / EFX_TXQ_TYPES, |
1020 | qid % EFX_TXQ_TYPES); | 1067 | qid % EFX_TXQ_TYPES); |
1021 | 1068 | if (atomic_cmpxchg(&tx_queue->flush_outstanding, 1, 0)) { | |
1022 | efx_magic_event(tx_queue->channel, | 1069 | efx_magic_event(tx_queue->channel, |
1023 | EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); | 1070 | EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); |
1071 | } | ||
1024 | } | 1072 | } |
1025 | } | 1073 | } |
1026 | 1074 | ||