aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/ethernet/intel/i40e/i40e_main.c
diff options
context:
space:
mode:
authorAlan Brady <alan.brady@intel.com>2017-04-05 07:50:56 -0400
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>2017-04-08 05:53:49 -0400
commit17daabb5e8db2b7de742f59dd73aa12550143e0d (patch)
tree447f515dcb47619956b8658d0c1a9e4422bc8344 /drivers/net/ethernet/intel/i40e/i40e_main.c
parent373149fc99a077700339e18839484a852e7b0971 (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.c59
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 **/
4516static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi) 4511static 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/**