Commit 525d9e824018cd7cc8d8d44832ddcd363abfe6e1
Committed by
Ben Hutchings
1 parent
876be083b6
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
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>
Showing 2 changed files with 53 additions and 4 deletions Side-by-side Diff
drivers/net/ethernet/sfc/net_driver.h
drivers/net/ethernet/sfc/nic.c
... | ... | @@ -73,6 +73,8 @@ |
73 | 73 | _EFX_CHANNEL_MAGIC(_EFX_CHANNEL_MAGIC_TX_DRAIN, \ |
74 | 74 | (_tx_queue)->queue) |
75 | 75 | |
76 | +static void efx_magic_event(struct efx_channel *channel, u32 magic); | |
77 | + | |
76 | 78 | /************************************************************************** |
77 | 79 | * |
78 | 80 | * Solarstorm hardware access |
... | ... | @@ -491,6 +493,9 @@ |
491 | 493 | struct efx_nic *efx = tx_queue->efx; |
492 | 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 | 499 | EFX_POPULATE_OWORD_2(tx_flush_descq, |
495 | 500 | FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, |
496 | 501 | FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); |
... | ... | @@ -666,6 +671,47 @@ |
666 | 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 | 715 | /* Flush all the transmit queues, and continue flushing receive queues until |
670 | 716 | * they're all flushed. Wait for the DRAIN events to be recieved so that there |
671 | 717 | * are no more RX and TX events left on any channel. */ |
... | ... | @@ -726,7 +772,8 @@ |
726 | 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 | 777 | netif_err(efx, hw, efx->net_dev, "failed to flush %d queues " |
731 | 778 | "(rx %d+%d)\n", atomic_read(&efx->drain_pending), |
732 | 779 | atomic_read(&efx->rxq_flush_outstanding), |
... | ... | @@ -1018,9 +1065,10 @@ |
1018 | 1065 | if (qid < EFX_TXQ_TYPES * efx->n_tx_channels) { |
1019 | 1066 | tx_queue = efx_get_tx_queue(efx, qid / EFX_TXQ_TYPES, |
1020 | 1067 | qid % EFX_TXQ_TYPES); |
1021 | - | |
1022 | - efx_magic_event(tx_queue->channel, | |
1023 | - EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); | |
1068 | + if (atomic_cmpxchg(&tx_queue->flush_outstanding, 1, 0)) { | |
1069 | + efx_magic_event(tx_queue->channel, | |
1070 | + EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); | |
1071 | + } | |
1024 | 1072 | } |
1025 | 1073 | } |
1026 | 1074 |