diff options
author | Alan Brady <alan.brady@intel.com> | 2017-04-05 07:50:56 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2017-04-08 05:53:49 -0400 |
commit | 17daabb5e8db2b7de742f59dd73aa12550143e0d (patch) | |
tree | 447f515dcb47619956b8658d0c1a9e4422bc8344 /drivers/net/ethernet/intel/i40e/i40e_main.c | |
parent | 373149fc99a077700339e18839484a852e7b0971 (diff) |
i40e: Simplify i40e_detect_recover_hung_queue logic
This patch greatly reduces the unneeded complexity in the
i40e_detect_recover_hung_queue code path. The previous implementation
set a 'hung bit' which would then get cleared while polling. If the
detection routine was called a second time with the bit already set, we
would issue a software interrupt. This patch makes it such that if
interrupts are disabled and we have pending TX descriptors, we trigger a
software interrupt since in, the worst case, queues are already clean
and we have an extra interrupt.
Additionally this patch removes the workaround for lost interrupts as
calling napi_reschedule in this context can cause software interrupts to
fire on the wrong CPU.
Change-ID: Iae108582a3ceb6229ed1d22e4ed6e69cf97aad8d
Signed-off-by: Alan Brady <alan.brady@intel.com>
Tested-by: Andrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet/intel/i40e/i40e_main.c')
-rw-r--r-- | drivers/net/ethernet/intel/i40e/i40e_main.c | 59 |
1 files changed, 11 insertions, 48 deletions
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 8181647f512e..22831a4a9099 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c | |||
@@ -737,7 +737,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) | |||
737 | struct i40e_eth_stats *oes; | 737 | struct i40e_eth_stats *oes; |
738 | struct i40e_eth_stats *es; /* device's eth stats */ | 738 | struct i40e_eth_stats *es; /* device's eth stats */ |
739 | u32 tx_restart, tx_busy; | 739 | u32 tx_restart, tx_busy; |
740 | u64 tx_lost_interrupt; | ||
741 | struct i40e_ring *p; | 740 | struct i40e_ring *p; |
742 | u32 rx_page, rx_buf; | 741 | u32 rx_page, rx_buf; |
743 | u64 bytes, packets; | 742 | u64 bytes, packets; |
@@ -763,7 +762,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) | |||
763 | rx_b = rx_p = 0; | 762 | rx_b = rx_p = 0; |
764 | tx_b = tx_p = 0; | 763 | tx_b = tx_p = 0; |
765 | tx_restart = tx_busy = tx_linearize = tx_force_wb = 0; | 764 | tx_restart = tx_busy = tx_linearize = tx_force_wb = 0; |
766 | tx_lost_interrupt = 0; | ||
767 | rx_page = 0; | 765 | rx_page = 0; |
768 | rx_buf = 0; | 766 | rx_buf = 0; |
769 | rcu_read_lock(); | 767 | rcu_read_lock(); |
@@ -782,7 +780,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) | |||
782 | tx_busy += p->tx_stats.tx_busy; | 780 | tx_busy += p->tx_stats.tx_busy; |
783 | tx_linearize += p->tx_stats.tx_linearize; | 781 | tx_linearize += p->tx_stats.tx_linearize; |
784 | tx_force_wb += p->tx_stats.tx_force_wb; | 782 | tx_force_wb += p->tx_stats.tx_force_wb; |
785 | tx_lost_interrupt += p->tx_stats.tx_lost_interrupt; | ||
786 | 783 | ||
787 | /* Rx queue is part of the same block as Tx queue */ | 784 | /* Rx queue is part of the same block as Tx queue */ |
788 | p = &p[1]; | 785 | p = &p[1]; |
@@ -801,7 +798,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi) | |||
801 | vsi->tx_busy = tx_busy; | 798 | vsi->tx_busy = tx_busy; |
802 | vsi->tx_linearize = tx_linearize; | 799 | vsi->tx_linearize = tx_linearize; |
803 | vsi->tx_force_wb = tx_force_wb; | 800 | vsi->tx_force_wb = tx_force_wb; |
804 | vsi->tx_lost_interrupt = tx_lost_interrupt; | ||
805 | vsi->rx_page_failed = rx_page; | 801 | vsi->rx_page_failed = rx_page; |
806 | vsi->rx_buf_failed = rx_buf; | 802 | vsi->rx_buf_failed = rx_buf; |
807 | 803 | ||
@@ -4508,16 +4504,15 @@ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf) | |||
4508 | * @vsi: Pointer to VSI struct | 4504 | * @vsi: Pointer to VSI struct |
4509 | * | 4505 | * |
4510 | * This function checks specified queue for given VSI. Detects hung condition. | 4506 | * This function checks specified queue for given VSI. Detects hung condition. |
4511 | * Sets hung bit since it is two step process. Before next run of service task | 4507 | * We proactively detect hung TX queues by checking if interrupts are disabled |
4512 | * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not, | 4508 | * but there are pending descriptors. If it appears hung, attempt to recover |
4513 | * hung condition remain unchanged and during subsequent run, this function | 4509 | * by triggering a SW interrupt. |
4514 | * issues SW interrupt to recover from hung condition. | ||
4515 | **/ | 4510 | **/ |
4516 | static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) | 4511 | static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) |
4517 | { | 4512 | { |
4518 | struct i40e_ring *tx_ring = NULL; | 4513 | struct i40e_ring *tx_ring = NULL; |
4519 | struct i40e_pf *pf; | 4514 | struct i40e_pf *pf; |
4520 | u32 head, val, tx_pending_hw; | 4515 | u32 val, tx_pending; |
4521 | int i; | 4516 | int i; |
4522 | 4517 | ||
4523 | pf = vsi->back; | 4518 | pf = vsi->back; |
@@ -4543,47 +4538,15 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) | |||
4543 | else | 4538 | else |
4544 | val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); | 4539 | val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0); |
4545 | 4540 | ||
4546 | head = i40e_get_head(tx_ring); | 4541 | tx_pending = i40e_get_tx_pending(tx_ring); |
4547 | 4542 | ||
4548 | tx_pending_hw = i40e_get_tx_pending(tx_ring, false); | 4543 | /* Interrupts are disabled and TX pending is non-zero, |
4549 | 4544 | * trigger the SW interrupt (don't wait). Worst case | |
4550 | /* HW is done executing descriptors, updated HEAD write back, | 4545 | * there will be one extra interrupt which may result |
4551 | * but SW hasn't processed those descriptors. If interrupt is | 4546 | * into not cleaning any queues because queues are cleaned. |
4552 | * not generated from this point ON, it could result into | ||
4553 | * dev_watchdog detecting timeout on those netdev_queue, | ||
4554 | * hence proactively trigger SW interrupt. | ||
4555 | */ | 4547 | */ |
4556 | if (tx_pending_hw && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) { | 4548 | if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) |
4557 | /* NAPI Poll didn't run and clear since it was set */ | 4549 | i40e_force_wb(vsi, tx_ring->q_vector); |
4558 | if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT, | ||
4559 | &tx_ring->q_vector->hung_detected)) { | ||
4560 | netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending_hw: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n", | ||
4561 | vsi->seid, q_idx, tx_pending_hw, | ||
4562 | tx_ring->next_to_clean, head, | ||
4563 | tx_ring->next_to_use, | ||
4564 | readl(tx_ring->tail)); | ||
4565 | netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n", | ||
4566 | vsi->seid, q_idx, val); | ||
4567 | i40e_force_wb(vsi, tx_ring->q_vector); | ||
4568 | } else { | ||
4569 | /* First Chance - detected possible hung */ | ||
4570 | set_bit(I40E_Q_VECTOR_HUNG_DETECT, | ||
4571 | &tx_ring->q_vector->hung_detected); | ||
4572 | } | ||
4573 | } | ||
4574 | |||
4575 | /* This is the case where we have interrupts missing, | ||
4576 | * so the tx_pending in HW will most likely be 0, but we | ||
4577 | * will have tx_pending in SW since the WB happened but the | ||
4578 | * interrupt got lost. | ||
4579 | */ | ||
4580 | if ((!tx_pending_hw) && i40e_get_tx_pending(tx_ring, true) && | ||
4581 | (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) { | ||
4582 | local_bh_disable(); | ||
4583 | if (napi_reschedule(&tx_ring->q_vector->napi)) | ||
4584 | tx_ring->tx_stats.tx_lost_interrupt++; | ||
4585 | local_bh_enable(); | ||
4586 | } | ||
4587 | } | 4550 | } |
4588 | 4551 | ||
4589 | /** | 4552 | /** |