|
@@ -737,7 +737,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
|
|
|
struct i40e_eth_stats *oes;
|
|
|
struct i40e_eth_stats *es; /* device's eth stats */
|
|
|
u32 tx_restart, tx_busy;
|
|
|
- u64 tx_lost_interrupt;
|
|
|
struct i40e_ring *p;
|
|
|
u32 rx_page, rx_buf;
|
|
|
u64 bytes, packets;
|
|
@@ -763,7 +762,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
|
|
|
rx_b = rx_p = 0;
|
|
|
tx_b = tx_p = 0;
|
|
|
tx_restart = tx_busy = tx_linearize = tx_force_wb = 0;
|
|
|
- tx_lost_interrupt = 0;
|
|
|
rx_page = 0;
|
|
|
rx_buf = 0;
|
|
|
rcu_read_lock();
|
|
@@ -782,7 +780,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
|
|
|
tx_busy += p->tx_stats.tx_busy;
|
|
|
tx_linearize += p->tx_stats.tx_linearize;
|
|
|
tx_force_wb += p->tx_stats.tx_force_wb;
|
|
|
- tx_lost_interrupt += p->tx_stats.tx_lost_interrupt;
|
|
|
|
|
|
/* Rx queue is part of the same block as Tx queue */
|
|
|
p = &p[1];
|
|
@@ -801,7 +798,6 @@ static void i40e_update_vsi_stats(struct i40e_vsi *vsi)
|
|
|
vsi->tx_busy = tx_busy;
|
|
|
vsi->tx_linearize = tx_linearize;
|
|
|
vsi->tx_force_wb = tx_force_wb;
|
|
|
- vsi->tx_lost_interrupt = tx_lost_interrupt;
|
|
|
vsi->rx_page_failed = rx_page;
|
|
|
vsi->rx_buf_failed = rx_buf;
|
|
|
|
|
@@ -4508,16 +4504,15 @@ static int i40e_pf_wait_queues_disabled(struct i40e_pf *pf)
|
|
|
* @vsi: Pointer to VSI struct
|
|
|
*
|
|
|
* This function checks specified queue for given VSI. Detects hung condition.
|
|
|
- * Sets hung bit since it is two step process. Before next run of service task
|
|
|
- * if napi_poll runs, it reset 'hung' bit for respective q_vector. If not,
|
|
|
- * hung condition remain unchanged and during subsequent run, this function
|
|
|
- * issues SW interrupt to recover from hung condition.
|
|
|
+ * We proactively detect hung TX queues by checking if interrupts are disabled
|
|
|
+ * but there are pending descriptors. If it appears hung, attempt to recover
|
|
|
+ * by triggering a SW interrupt.
|
|
|
**/
|
|
|
static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
|
|
|
{
|
|
|
struct i40e_ring *tx_ring = NULL;
|
|
|
struct i40e_pf *pf;
|
|
|
- u32 head, val, tx_pending_hw;
|
|
|
+ u32 val, tx_pending;
|
|
|
int i;
|
|
|
|
|
|
pf = vsi->back;
|
|
@@ -4543,47 +4538,15 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
|
|
|
else
|
|
|
val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
|
|
|
|
|
|
- head = i40e_get_head(tx_ring);
|
|
|
+ tx_pending = i40e_get_tx_pending(tx_ring);
|
|
|
|
|
|
- tx_pending_hw = i40e_get_tx_pending(tx_ring, false);
|
|
|
-
|
|
|
- /* HW is done executing descriptors, updated HEAD write back,
|
|
|
- * but SW hasn't processed those descriptors. If interrupt is
|
|
|
- * not generated from this point ON, it could result into
|
|
|
- * dev_watchdog detecting timeout on those netdev_queue,
|
|
|
- * hence proactively trigger SW interrupt.
|
|
|
+ /* Interrupts are disabled and TX pending is non-zero,
|
|
|
+ * trigger the SW interrupt (don't wait). Worst case
|
|
|
+ * there will be one extra interrupt which may result
|
|
|
+ * into not cleaning any queues because queues are cleaned.
|
|
|
*/
|
|
|
- if (tx_pending_hw && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) {
|
|
|
- /* NAPI Poll didn't run and clear since it was set */
|
|
|
- if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT,
|
|
|
- &tx_ring->q_vector->hung_detected)) {
|
|
|
- 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",
|
|
|
- vsi->seid, q_idx, tx_pending_hw,
|
|
|
- tx_ring->next_to_clean, head,
|
|
|
- tx_ring->next_to_use,
|
|
|
- readl(tx_ring->tail));
|
|
|
- netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n",
|
|
|
- vsi->seid, q_idx, val);
|
|
|
- i40e_force_wb(vsi, tx_ring->q_vector);
|
|
|
- } else {
|
|
|
- /* First Chance - detected possible hung */
|
|
|
- set_bit(I40E_Q_VECTOR_HUNG_DETECT,
|
|
|
- &tx_ring->q_vector->hung_detected);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /* This is the case where we have interrupts missing,
|
|
|
- * so the tx_pending in HW will most likely be 0, but we
|
|
|
- * will have tx_pending in SW since the WB happened but the
|
|
|
- * interrupt got lost.
|
|
|
- */
|
|
|
- if ((!tx_pending_hw) && i40e_get_tx_pending(tx_ring, true) &&
|
|
|
- (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))) {
|
|
|
- local_bh_disable();
|
|
|
- if (napi_reschedule(&tx_ring->q_vector->napi))
|
|
|
- tx_ring->tx_stats.tx_lost_interrupt++;
|
|
|
- local_bh_enable();
|
|
|
- }
|
|
|
+ if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)))
|
|
|
+ i40e_force_wb(vsi, tx_ring->q_vector);
|
|
|
}
|
|
|
|
|
|
/**
|