Skip to content

Commit 9c6c125

Browse files
kapatilJeff Kirsher
authored andcommitted
i40e: Detection and recovery of TX queue hung logic moved to service_task from tx_timeout
This patch contains following changes: - detection and recovery logic (issue SW interrupt) has been moved to service_task from timeout function. - added some more debug info from tx_timeout. Logic to detect and recover TX queue hung is now two step process: - service_task detects TX queue hung and sets a bit(hung_detected) if it was not set. - if bit was set (means this is back-back hung condition detected), issue SW interrupt and clear the bit. - napi_poll clears the bit unconditionally since it cleans TX/RX queues. Change-ID: Ieed03a48927c845a988b3ff375090bf37caeb903 Signed-off-by: Kiran Patil <kiran.patil@intel.com> Tested-by: Andrew Bowers <andrewx.bowers@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
1 parent 05281eb commit 9c6c125

File tree

5 files changed

+64
-13
lines changed

5 files changed

+64
-13
lines changed

drivers/net/ethernet/intel/i40e/i40e.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,9 @@ struct i40e_q_vector {
579579

580580
u8 num_ringpairs; /* total number of ring pairs in vector */
581581

582+
#define I40E_Q_VECTOR_HUNG_DETECT 0 /* Bit Index for hung detection logic */
583+
unsigned long hung_detected; /* Set/Reset for hung_detection logic */
584+
582585
cpumask_t affinity_mask;
583586
struct rcu_head rcu; /* to avoid race with update stats on free */
584587
char name[I40E_INT_NAME_STR_LEN];

drivers/net/ethernet/intel/i40e/i40e_main.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4368,17 +4368,41 @@ static void i40e_detect_recover_hung_queue(int q_idx, struct i40e_vsi *vsi)
43684368
else
43694369
val = rd32(&pf->hw, I40E_PFINT_DYN_CTL0);
43704370

4371+
/* Bail out if interrupts are disabled because napi_poll
4372+
* execution in-progress or will get scheduled soon.
4373+
* napi_poll cleans TX and RX queues and updates 'next_to_clean'.
4374+
*/
4375+
if (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK))
4376+
return;
4377+
43714378
head = i40e_get_head(tx_ring);
43724379

43734380
tx_pending = i40e_get_tx_pending(tx_ring);
43744381

4375-
/* Interrupts are disabled and TX pending is non-zero,
4376-
* trigger the SW interrupt (don't wait). Worst case
4377-
* there will be one extra interrupt which may result
4378-
* into not cleaning any queues because queues are cleaned.
4382+
/* HW is done executing descriptors, updated HEAD write back,
4383+
* but SW hasn't processed those descriptors. If interrupt is
4384+
* not generated from this point ON, it could result into
4385+
* dev_watchdog detecting timeout on those netdev_queue,
4386+
* hence proactively trigger SW interrupt.
43794387
*/
4380-
if (tx_pending && (!(val & I40E_PFINT_DYN_CTLN_INTENA_MASK)))
4381-
i40e_force_wb(vsi, tx_ring->q_vector);
4388+
if (tx_pending) {
4389+
/* NAPI Poll didn't run and clear since it was set */
4390+
if (test_and_clear_bit(I40E_Q_VECTOR_HUNG_DETECT,
4391+
&tx_ring->q_vector->hung_detected)) {
4392+
netdev_info(vsi->netdev, "VSI_seid %d, Hung TX queue %d, tx_pending: %d, NTC:0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x\n",
4393+
vsi->seid, q_idx, tx_pending,
4394+
tx_ring->next_to_clean, head,
4395+
tx_ring->next_to_use,
4396+
readl(tx_ring->tail));
4397+
netdev_info(vsi->netdev, "VSI_seid %d, Issuing force_wb for TX queue %d, Interrupt Reg: 0x%x\n",
4398+
vsi->seid, q_idx, val);
4399+
i40e_force_wb(vsi, tx_ring->q_vector);
4400+
} else {
4401+
/* First Chance - detected possible hung */
4402+
set_bit(I40E_Q_VECTOR_HUNG_DETECT,
4403+
&tx_ring->q_vector->hung_detected);
4404+
}
4405+
}
43824406
}
43834407

43844408
/**

drivers/net/ethernet/intel/i40e/i40e_txrx.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1891,6 +1891,8 @@ int i40e_napi_poll(struct napi_struct *napi, int budget)
18911891
return 0;
18921892
}
18931893

1894+
/* Clear hung_detected bit */
1895+
clear_bit(I40E_Q_VECTOR_HUNG_DETECT, &q_vector->hung_detected);
18941896
/* Since the actual Tx work is minimal, we can give the Tx a larger
18951897
* budget and be more aggressive about cleaning up the Tx descriptors.
18961898
*/

drivers/net/ethernet/intel/i40evf/i40e_txrx.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,24 @@ void i40evf_free_tx_resources(struct i40e_ring *tx_ring)
127127
}
128128

129129
/**
130-
* i40e_get_head - Retrieve head from head writeback
131-
* @tx_ring: tx ring to fetch head of
130+
* i40evf_get_tx_pending - how many Tx descriptors not processed
131+
* @tx_ring: the ring of descriptors
132132
*
133-
* Returns value of Tx ring head based on value stored
134-
* in head write-back location
133+
* Since there is no access to the ring head register
134+
* in XL710, we need to use our local copies
135135
**/
136-
static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
136+
u32 i40evf_get_tx_pending(struct i40e_ring *ring)
137137
{
138-
void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
138+
u32 head, tail;
139139

140-
return le32_to_cpu(*(volatile __le32 *)head);
140+
head = i40e_get_head(ring);
141+
tail = readl(ring->tail);
142+
143+
if (head != tail)
144+
return (head < tail) ?
145+
tail - head : (tail + ring->count - head);
146+
147+
return 0;
141148
}
142149

143150
#define WB_STRIDE 0x3

drivers/net/ethernet/intel/i40evf/i40e_txrx.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,4 +324,19 @@ int i40evf_setup_rx_descriptors(struct i40e_ring *rx_ring);
324324
void i40evf_free_tx_resources(struct i40e_ring *tx_ring);
325325
void i40evf_free_rx_resources(struct i40e_ring *rx_ring);
326326
int i40evf_napi_poll(struct napi_struct *napi, int budget);
327+
u32 i40evf_get_tx_pending(struct i40e_ring *ring);
328+
329+
/**
330+
* i40e_get_head - Retrieve head from head writeback
331+
* @tx_ring: Tx ring to fetch head of
332+
*
333+
* Returns value of Tx ring head based on value stored
334+
* in head write-back location
335+
**/
336+
static inline u32 i40e_get_head(struct i40e_ring *tx_ring)
337+
{
338+
void *head = (struct i40e_tx_desc *)tx_ring->desc + tx_ring->count;
339+
340+
return le32_to_cpu(*(volatile __le32 *)head);
341+
}
327342
#endif /* _I40E_TXRX_H_ */

0 commit comments

Comments
 (0)