Commit b3969fd7 authored by Sudheer Mogilappagari's avatar Sudheer Mogilappagari Committed by Jeff Kirsher

ice: Add support for Tx hang, Tx timeout and malicious driver detection

When a malicious operation is detected, the firmware triggers an
interrupt, which is then picked up by the service task (specifically by
ice_handle_mdd_event). A reset is scheduled if required.

Tx hang detection works in a similar way, except the logic here monitors
the VSI's Tx queues and tries to revive them if stalled. If the hang is
not resolved, the kernel eventually calls ndo_tx_timeout, which is
handled by ice_tx_timeout.
Signed-off-by: default avatarSudheer Mogilappagari <sudheer.mogilappagari@intel.com>
Signed-off-by: default avatarAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: default avatarTony Brelinski <tonyx.brelinski@intel.com>
Signed-off-by: default avatarJeff Kirsher <jeffrey.t.kirsher@intel.com>
parent f80eaa42
...@@ -134,6 +134,7 @@ enum ice_state { ...@@ -134,6 +134,7 @@ enum ice_state {
__ICE_SUSPENDED, /* set on module remove path */ __ICE_SUSPENDED, /* set on module remove path */
__ICE_RESET_FAILED, /* set by reset/rebuild */ __ICE_RESET_FAILED, /* set by reset/rebuild */
__ICE_ADMINQ_EVENT_PENDING, __ICE_ADMINQ_EVENT_PENDING,
__ICE_MDD_EVENT_PENDING,
__ICE_FLTR_OVERFLOW_PROMISC, __ICE_FLTR_OVERFLOW_PROMISC,
__ICE_CFG_BUSY, __ICE_CFG_BUSY,
__ICE_SERVICE_SCHED, __ICE_SERVICE_SCHED,
...@@ -272,6 +273,9 @@ struct ice_pf { ...@@ -272,6 +273,9 @@ struct ice_pf {
struct ice_hw_port_stats stats_prev; struct ice_hw_port_stats stats_prev;
struct ice_hw hw; struct ice_hw hw;
u8 stat_prev_loaded; /* has previous stats been loaded */ u8 stat_prev_loaded; /* has previous stats been loaded */
u32 tx_timeout_count;
unsigned long tx_timeout_last_recovery;
u32 tx_timeout_recovery_level;
char int_name[ICE_INT_NAME_STR_LEN]; char int_name[ICE_INT_NAME_STR_LEN];
}; };
......
...@@ -123,6 +123,45 @@ ...@@ -123,6 +123,45 @@
#define QRX_CTRL_QENA_STAT_M BIT(2) #define QRX_CTRL_QENA_STAT_M BIT(2)
#define QRX_ITR(_QRX) (0x00292000 + ((_QRX) * 4)) #define QRX_ITR(_QRX) (0x00292000 + ((_QRX) * 4))
#define QRX_TAIL(_QRX) (0x00290000 + ((_QRX) * 4)) #define QRX_TAIL(_QRX) (0x00290000 + ((_QRX) * 4))
#define QRX_TAIL_MAX_INDEX 2047
#define QRX_TAIL_TAIL_S 0
#define QRX_TAIL_TAIL_M ICE_M(0x1FFF, 0)
#define GL_MDET_RX 0x00294C00
#define GL_MDET_RX_QNUM_S 0
#define GL_MDET_RX_QNUM_M ICE_M(0x7FFF, 0)
#define GL_MDET_RX_VF_NUM_S 15
#define GL_MDET_RX_VF_NUM_M ICE_M(0xFF, 15)
#define GL_MDET_RX_PF_NUM_S 23
#define GL_MDET_RX_PF_NUM_M ICE_M(0x7, 23)
#define GL_MDET_RX_MAL_TYPE_S 26
#define GL_MDET_RX_MAL_TYPE_M ICE_M(0x1F, 26)
#define GL_MDET_RX_VALID_M BIT(31)
#define GL_MDET_TX_PQM 0x002D2E00
#define GL_MDET_TX_PQM_PF_NUM_S 0
#define GL_MDET_TX_PQM_PF_NUM_M ICE_M(0x7, 0)
#define GL_MDET_TX_PQM_VF_NUM_S 4
#define GL_MDET_TX_PQM_VF_NUM_M ICE_M(0xFF, 4)
#define GL_MDET_TX_PQM_QNUM_S 12
#define GL_MDET_TX_PQM_QNUM_M ICE_M(0x3FFF, 12)
#define GL_MDET_TX_PQM_MAL_TYPE_S 26
#define GL_MDET_TX_PQM_MAL_TYPE_M ICE_M(0x1F, 26)
#define GL_MDET_TX_PQM_VALID_M BIT(31)
#define GL_MDET_TX_TCLAN 0x000FC068
#define GL_MDET_TX_TCLAN_QNUM_S 0
#define GL_MDET_TX_TCLAN_QNUM_M ICE_M(0x7FFF, 0)
#define GL_MDET_TX_TCLAN_VF_NUM_S 15
#define GL_MDET_TX_TCLAN_VF_NUM_M ICE_M(0xFF, 15)
#define GL_MDET_TX_TCLAN_PF_NUM_S 23
#define GL_MDET_TX_TCLAN_PF_NUM_M ICE_M(0x7, 23)
#define GL_MDET_TX_TCLAN_MAL_TYPE_S 26
#define GL_MDET_TX_TCLAN_MAL_TYPE_M ICE_M(0x1F, 26)
#define GL_MDET_TX_TCLAN_VALID_M BIT(31)
#define PF_MDET_RX 0x00294280
#define PF_MDET_RX_VALID_M BIT(0)
#define PF_MDET_TX_PQM 0x002D2C80
#define PF_MDET_TX_PQM_VALID_M BIT(0)
#define PF_MDET_TX_TCLAN 0x000FC000
#define PF_MDET_TX_TCLAN_VALID_M BIT(0)
#define GLNVM_FLA 0x000B6108 #define GLNVM_FLA 0x000B6108
#define GLNVM_FLA_LOCKED_M BIT(6) #define GLNVM_FLA_LOCKED_M BIT(6)
#define GLNVM_GENS 0x000B6100 #define GLNVM_GENS 0x000B6100
......
...@@ -36,6 +36,81 @@ static void ice_vsi_release_all(struct ice_pf *pf); ...@@ -36,6 +36,81 @@ static void ice_vsi_release_all(struct ice_pf *pf);
static void ice_update_vsi_stats(struct ice_vsi *vsi); static void ice_update_vsi_stats(struct ice_vsi *vsi);
static void ice_update_pf_stats(struct ice_pf *pf); static void ice_update_pf_stats(struct ice_pf *pf);
/**
* ice_get_tx_pending - returns number of Tx descriptors not processed
* @ring: the ring of descriptors
*/
static u32 ice_get_tx_pending(struct ice_ring *ring)
{
u32 head, tail;
head = ring->next_to_clean;
tail = readl(ring->tail);
if (head != tail)
return (head < tail) ?
tail - head : (tail + ring->count - head);
return 0;
}
/**
* ice_check_for_hang_subtask - check for and recover hung queues
* @pf: pointer to PF struct
*/
static void ice_check_for_hang_subtask(struct ice_pf *pf)
{
struct ice_vsi *vsi = NULL;
unsigned int i;
u32 v, v_idx;
int packets;
ice_for_each_vsi(pf, v)
if (pf->vsi[v] && pf->vsi[v]->type == ICE_VSI_PF) {
vsi = pf->vsi[v];
break;
}
if (!vsi || test_bit(__ICE_DOWN, vsi->state))
return;
if (!(vsi->netdev && netif_carrier_ok(vsi->netdev)))
return;
for (i = 0; i < vsi->num_txq; i++) {
struct ice_ring *tx_ring = vsi->tx_rings[i];
if (tx_ring && tx_ring->desc) {
int itr = ICE_ITR_NONE;
/* If packet counter has not changed the queue is
* likely stalled, so force an interrupt for this
* queue.
*
* prev_pkt would be negative if there was no
* pending work.
*/
packets = tx_ring->stats.pkts & INT_MAX;
if (tx_ring->tx_stats.prev_pkt == packets) {
/* Trigger sw interrupt to revive the queue */
v_idx = tx_ring->q_vector->v_idx;
wr32(&vsi->back->hw,
GLINT_DYN_CTL(vsi->base_vector + v_idx),
(itr << GLINT_DYN_CTL_ITR_INDX_S) |
GLINT_DYN_CTL_SWINT_TRIG_M |
GLINT_DYN_CTL_INTENA_MSK_M);
continue;
}
/* Memory barrier between read of packet count and call
* to ice_get_tx_pending()
*/
smp_rmb();
tx_ring->tx_stats.prev_pkt =
ice_get_tx_pending(tx_ring) ? packets : -1;
}
}
}
/** /**
* ice_get_free_slot - get the next non-NULL location index in array * ice_get_free_slot - get the next non-NULL location index in array
* @array: array to search * @array: array to search
...@@ -1003,6 +1078,114 @@ static void ice_service_timer(struct timer_list *t) ...@@ -1003,6 +1078,114 @@ static void ice_service_timer(struct timer_list *t)
ice_service_task_schedule(pf); ice_service_task_schedule(pf);
} }
/**
* ice_handle_mdd_event - handle malicious driver detect event
* @pf: pointer to the PF structure
*
* Called from service task. OICR interrupt handler indicates MDD event
*/
static void ice_handle_mdd_event(struct ice_pf *pf)
{
struct ice_hw *hw = &pf->hw;
bool mdd_detected = false;
u32 reg;
if (!test_bit(__ICE_MDD_EVENT_PENDING, pf->state))
return;
/* find what triggered the MDD event */
reg = rd32(hw, GL_MDET_TX_PQM);
if (reg & GL_MDET_TX_PQM_VALID_M) {
u8 pf_num = (reg & GL_MDET_TX_PQM_PF_NUM_M) >>
GL_MDET_TX_PQM_PF_NUM_S;
u16 vf_num = (reg & GL_MDET_TX_PQM_VF_NUM_M) >>
GL_MDET_TX_PQM_VF_NUM_S;
u8 event = (reg & GL_MDET_TX_PQM_MAL_TYPE_M) >>
GL_MDET_TX_PQM_MAL_TYPE_S;
u16 queue = ((reg & GL_MDET_TX_PQM_QNUM_M) >>
GL_MDET_TX_PQM_QNUM_S);
if (netif_msg_tx_err(pf))
dev_info(&pf->pdev->dev, "Malicious Driver Detection event %d on TX queue %d PF# %d VF# %d\n",
event, queue, pf_num, vf_num);
wr32(hw, GL_MDET_TX_PQM, 0xffffffff);
mdd_detected = true;
}
reg = rd32(hw, GL_MDET_TX_TCLAN);
if (reg & GL_MDET_TX_TCLAN_VALID_M) {
u8 pf_num = (reg & GL_MDET_TX_TCLAN_PF_NUM_M) >>
GL_MDET_TX_TCLAN_PF_NUM_S;
u16 vf_num = (reg & GL_MDET_TX_TCLAN_VF_NUM_M) >>
GL_MDET_TX_TCLAN_VF_NUM_S;
u8 event = (reg & GL_MDET_TX_TCLAN_MAL_TYPE_M) >>
GL_MDET_TX_TCLAN_MAL_TYPE_S;
u16 queue = ((reg & GL_MDET_TX_TCLAN_QNUM_M) >>
GL_MDET_TX_TCLAN_QNUM_S);
if (netif_msg_rx_err(pf))
dev_info(&pf->pdev->dev, "Malicious Driver Detection event %d on TX queue %d PF# %d VF# %d\n",
event, queue, pf_num, vf_num);
wr32(hw, GL_MDET_TX_TCLAN, 0xffffffff);
mdd_detected = true;
}
reg = rd32(hw, GL_MDET_RX);
if (reg & GL_MDET_RX_VALID_M) {
u8 pf_num = (reg & GL_MDET_RX_PF_NUM_M) >>
GL_MDET_RX_PF_NUM_S;
u16 vf_num = (reg & GL_MDET_RX_VF_NUM_M) >>
GL_MDET_RX_VF_NUM_S;
u8 event = (reg & GL_MDET_RX_MAL_TYPE_M) >>
GL_MDET_RX_MAL_TYPE_S;
u16 queue = ((reg & GL_MDET_RX_QNUM_M) >>
GL_MDET_RX_QNUM_S);
if (netif_msg_rx_err(pf))
dev_info(&pf->pdev->dev, "Malicious Driver Detection event %d on RX queue %d PF# %d VF# %d\n",
event, queue, pf_num, vf_num);
wr32(hw, GL_MDET_RX, 0xffffffff);
mdd_detected = true;
}
if (mdd_detected) {
bool pf_mdd_detected = false;
reg = rd32(hw, PF_MDET_TX_PQM);
if (reg & PF_MDET_TX_PQM_VALID_M) {
wr32(hw, PF_MDET_TX_PQM, 0xFFFF);
dev_info(&pf->pdev->dev, "TX driver issue detected, PF reset issued\n");
pf_mdd_detected = true;
}
reg = rd32(hw, PF_MDET_TX_TCLAN);
if (reg & PF_MDET_TX_TCLAN_VALID_M) {
wr32(hw, PF_MDET_TX_TCLAN, 0xFFFF);
dev_info(&pf->pdev->dev, "TX driver issue detected, PF reset issued\n");
pf_mdd_detected = true;
}
reg = rd32(hw, PF_MDET_RX);
if (reg & PF_MDET_RX_VALID_M) {
wr32(hw, PF_MDET_RX, 0xFFFF);
dev_info(&pf->pdev->dev, "RX driver issue detected, PF reset issued\n");
pf_mdd_detected = true;
}
/* Queue belongs to the PF initiate a reset */
if (pf_mdd_detected) {
set_bit(__ICE_NEEDS_RESTART, pf->state);
ice_service_task_schedule(pf);
}
}
/* re-enable MDD interrupt cause */
clear_bit(__ICE_MDD_EVENT_PENDING, pf->state);
reg = rd32(hw, PFINT_OICR_ENA);
reg |= PFINT_OICR_MAL_DETECT_M;
wr32(hw, PFINT_OICR_ENA, reg);
ice_flush(hw);
}
/** /**
* ice_service_task - manage and run subtasks * ice_service_task - manage and run subtasks
* @work: pointer to work_struct contained by the PF struct * @work: pointer to work_struct contained by the PF struct
...@@ -1025,7 +1208,9 @@ static void ice_service_task(struct work_struct *work) ...@@ -1025,7 +1208,9 @@ static void ice_service_task(struct work_struct *work)
return; return;
} }
ice_check_for_hang_subtask(pf);
ice_sync_fltr_subtask(pf); ice_sync_fltr_subtask(pf);
ice_handle_mdd_event(pf);
ice_watchdog_subtask(pf); ice_watchdog_subtask(pf);
ice_clean_adminq_subtask(pf); ice_clean_adminq_subtask(pf);
...@@ -1037,6 +1222,7 @@ static void ice_service_task(struct work_struct *work) ...@@ -1037,6 +1222,7 @@ static void ice_service_task(struct work_struct *work)
* schedule the service task now. * schedule the service task now.
*/ */
if (time_after(jiffies, (start_time + pf->serv_tmr_period)) || if (time_after(jiffies, (start_time + pf->serv_tmr_period)) ||
test_bit(__ICE_MDD_EVENT_PENDING, pf->state) ||
test_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state)) test_bit(__ICE_ADMINQ_EVENT_PENDING, pf->state))
mod_timer(&pf->serv_tmr, jiffies); mod_timer(&pf->serv_tmr, jiffies);
} }
...@@ -1747,8 +1933,14 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data) ...@@ -1747,8 +1933,14 @@ static irqreturn_t ice_misc_intr(int __always_unused irq, void *data)
oicr = rd32(hw, PFINT_OICR); oicr = rd32(hw, PFINT_OICR);
ena_mask = rd32(hw, PFINT_OICR_ENA); ena_mask = rd32(hw, PFINT_OICR_ENA);
if (oicr & PFINT_OICR_MAL_DETECT_M) {
ena_mask &= ~PFINT_OICR_MAL_DETECT_M;
set_bit(__ICE_MDD_EVENT_PENDING, pf->state);
}
if (oicr & PFINT_OICR_GRST_M) { if (oicr & PFINT_OICR_GRST_M) {
u32 reset; u32 reset;
/* we have a reset warning */ /* we have a reset warning */
ena_mask &= ~PFINT_OICR_GRST_M; ena_mask &= ~PFINT_OICR_GRST_M;
reset = (rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_RESET_TYPE_M) >> reset = (rd32(hw, GLGEN_RSTAT) & GLGEN_RSTAT_RESET_TYPE_M) >>
...@@ -5503,6 +5695,99 @@ int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size) ...@@ -5503,6 +5695,99 @@ int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut, u16 lut_size)
return 0; return 0;
} }
/**
* ice_tx_timeout - Respond to a Tx Hang
* @netdev: network interface device structure
*/
static void ice_tx_timeout(struct net_device *netdev)
{
struct ice_netdev_priv *np = netdev_priv(netdev);
struct ice_ring *tx_ring = NULL;
struct ice_vsi *vsi = np->vsi;
struct ice_pf *pf = vsi->back;
u32 head, val = 0, i;
int hung_queue = -1;
pf->tx_timeout_count++;
/* find the stopped queue the same way the stack does */
for (i = 0; i < netdev->num_tx_queues; i++) {
struct netdev_queue *q;
unsigned long trans_start;
q = netdev_get_tx_queue(netdev, i);
trans_start = q->trans_start;
if (netif_xmit_stopped(q) &&
time_after(jiffies,
(trans_start + netdev->watchdog_timeo))) {
hung_queue = i;
break;
}
}
if (i == netdev->num_tx_queues) {
netdev_info(netdev, "tx_timeout: no netdev hung queue found\n");
} else {
/* now that we have an index, find the tx_ring struct */
for (i = 0; i < vsi->num_txq; i++) {
if (vsi->tx_rings[i] && vsi->tx_rings[i]->desc) {
if (hung_queue ==
vsi->tx_rings[i]->q_index) {
tx_ring = vsi->tx_rings[i];
break;
}
}
}
}
/* Reset recovery level if enough time has elapsed after last timeout.
* Also ensure no new reset action happens before next timeout period.
*/
if (time_after(jiffies, (pf->tx_timeout_last_recovery + HZ * 20)))
pf->tx_timeout_recovery_level = 1;
else if (time_before(jiffies, (pf->tx_timeout_last_recovery +
netdev->watchdog_timeo)))
return;
if (tx_ring) {
head = tx_ring->next_to_clean;
/* Read interrupt register */
if (test_bit(ICE_FLAG_MSIX_ENA, pf->flags))
val = rd32(&pf->hw,
GLINT_DYN_CTL(tx_ring->q_vector->v_idx +
tx_ring->vsi->base_vector - 1));
netdev_info(netdev, "tx_timeout: VSI_num: %d, Q %d, NTC: 0x%x, HWB: 0x%x, NTU: 0x%x, TAIL: 0x%x, INT: 0x%x\n",
vsi->vsi_num, hung_queue, tx_ring->next_to_clean,
head, tx_ring->next_to_use,
readl(tx_ring->tail), val);
}
pf->tx_timeout_last_recovery = jiffies;
netdev_info(netdev, "tx_timeout recovery level %d, hung_queue %d\n",
pf->tx_timeout_recovery_level, hung_queue);
switch (pf->tx_timeout_recovery_level) {
case 1:
set_bit(__ICE_PFR_REQ, pf->state);
break;
case 2:
set_bit(__ICE_CORER_REQ, pf->state);
break;
case 3:
set_bit(__ICE_GLOBR_REQ, pf->state);
break;
default:
netdev_err(netdev, "tx_timeout recovery unsuccessful, device is in unrecoverable state.\n");
set_bit(__ICE_DOWN, pf->state);
set_bit(__ICE_NEEDS_RESTART, vsi->state);
break;
}
ice_service_task_schedule(pf);
pf->tx_timeout_recovery_level++;
}
/** /**
* ice_open - Called when a network interface becomes active * ice_open - Called when a network interface becomes active
* @netdev: network interface device structure * @netdev: network interface device structure
...@@ -5624,4 +5909,5 @@ static const struct net_device_ops ice_netdev_ops = { ...@@ -5624,4 +5909,5 @@ static const struct net_device_ops ice_netdev_ops = {
.ndo_set_features = ice_set_features, .ndo_set_features = ice_set_features,
.ndo_fdb_add = ice_fdb_add, .ndo_fdb_add = ice_fdb_add,
.ndo_fdb_del = ice_fdb_del, .ndo_fdb_del = ice_fdb_del,
.ndo_tx_timeout = ice_tx_timeout,
}; };
...@@ -251,6 +251,7 @@ int ice_setup_tx_ring(struct ice_ring *tx_ring) ...@@ -251,6 +251,7 @@ int ice_setup_tx_ring(struct ice_ring *tx_ring)
tx_ring->next_to_use = 0; tx_ring->next_to_use = 0;
tx_ring->next_to_clean = 0; tx_ring->next_to_clean = 0;
tx_ring->tx_stats.prev_pkt = -1;
return 0; return 0;
err: err:
......
...@@ -71,6 +71,7 @@ struct ice_txq_stats { ...@@ -71,6 +71,7 @@ struct ice_txq_stats {
u64 restart_q; u64 restart_q;
u64 tx_busy; u64 tx_busy;
u64 tx_linearize; u64 tx_linearize;
int prev_pkt; /* negative if no pending Tx descriptors */
}; };
struct ice_rxq_stats { struct ice_rxq_stats {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment