Skip to content

Commit

Permalink
net: hibmcge: Add support for abnormal irq handling feature
Browse files Browse the repository at this point in the history
the hardware error was reported by interrupt,
and need be fixed by doing function reset,
but the whole reset flow takes a long time,
should not do it in irq handler,
so do it in scheduled task.

Signed-off-by: Jijie Shao <shaojijie@huawei.com>
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
  • Loading branch information
Jijie Shao authored and Paolo Abeni committed Mar 4, 2025
1 parent 833b65a commit fd394a3
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 23 deletions.
5 changes: 5 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ enum hbg_nic_state {
HBG_NIC_STATE_EVENT_HANDLING = 0,
HBG_NIC_STATE_RESETTING,
HBG_NIC_STATE_RESET_FAIL,
HBG_NIC_STATE_NEED_RESET, /* trigger a reset in scheduled task */
};

enum hbg_reset_type {
Expand Down Expand Up @@ -104,6 +105,7 @@ struct hbg_irq_info {
u32 mask;
bool re_enable;
bool need_print;
bool need_reset;
u64 count;

void (*irq_handle)(struct hbg_priv *priv, struct hbg_irq_info *info);
Expand Down Expand Up @@ -220,6 +222,7 @@ struct hbg_stats {
u64 rx_fail_comma_cnt;

u64 rx_dma_err_cnt;
u64 rx_fifo_less_empty_thrsld_cnt;

u64 tx_octets_total_ok_cnt;
u64 tx_uc_pkt_cnt;
Expand Down Expand Up @@ -268,4 +271,6 @@ struct hbg_priv {
struct delayed_work service_task;
};

void hbg_err_reset_task_schedule(struct hbg_priv *priv);

#endif
5 changes: 4 additions & 1 deletion drivers/net/ethernet/hisilicon/hibmcge/hbg_debugfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ static int hbg_dbg_irq_info(struct seq_file *s, void *unused)
for (i = 0; i < priv->vectors.info_array_len; i++) {
info = &priv->vectors.info_array[i];
seq_printf(s,
"%-20s: enabled: %-5s, logged: %-5s, count: %llu\n",
"%-20s: enabled: %-5s, reset: %-5s, logged: %-5s, count: %llu\n",
info->name,
str_true_false(hbg_hw_irq_is_enabled(priv,
info->mask)),
str_true_false(info->need_reset),
str_true_false(info->need_print),
info->count);
}
Expand Down Expand Up @@ -114,6 +115,8 @@ static int hbg_dbg_nic_state(struct seq_file *s, void *unused)
state_str_true_false(priv, HBG_NIC_STATE_RESET_FAIL));
seq_printf(s, "last reset type: %s\n",
reset_type_str[priv->reset_type]);
seq_printf(s, "need reset state: %s\n",
state_str_true_false(priv, HBG_NIC_STATE_NEED_RESET));

return 0;
}
Expand Down
58 changes: 58 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_err.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,62 @@ int hbg_reset(struct hbg_priv *priv)
return hbg_reset_done(priv, HBG_RESET_TYPE_FUNCTION);
}

void hbg_err_reset(struct hbg_priv *priv)
{
bool running;

rtnl_lock();
running = netif_running(priv->netdev);
if (running)
dev_close(priv->netdev);

hbg_reset(priv);

/* in hbg_pci_err_detected(), we will detach first,
* so we need to attach before open
*/
if (!netif_device_present(priv->netdev))
netif_device_attach(priv->netdev);

if (running)
dev_open(priv->netdev, NULL);
rtnl_unlock();
}

static pci_ers_result_t hbg_pci_err_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);

netif_device_detach(netdev);

if (state == pci_channel_io_perm_failure)
return PCI_ERS_RESULT_DISCONNECT;

pci_disable_device(pdev);
return PCI_ERS_RESULT_NEED_RESET;
}

static pci_ers_result_t hbg_pci_err_slot_reset(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct hbg_priv *priv = netdev_priv(netdev);

if (pci_enable_device(pdev)) {
dev_err(&pdev->dev,
"failed to re-enable PCI device after reset\n");
return PCI_ERS_RESULT_DISCONNECT;
}

pci_set_master(pdev);
pci_restore_state(pdev);
pci_save_state(pdev);

hbg_err_reset(priv);
netif_device_attach(netdev);
return PCI_ERS_RESULT_RECOVERED;
}

static void hbg_pci_err_reset_prepare(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
Expand All @@ -124,6 +180,8 @@ static void hbg_pci_err_reset_done(struct pci_dev *pdev)
}

static const struct pci_error_handlers hbg_pci_err_handler = {
.error_detected = hbg_pci_err_detected,
.slot_reset = hbg_pci_err_slot_reset,
.reset_prepare = hbg_pci_err_reset_prepare,
.reset_done = hbg_pci_err_reset_done,
};
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_err.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@
void hbg_set_pci_err_handler(struct pci_driver *pdrv);
int hbg_reset(struct hbg_priv *priv);
int hbg_rebuild(struct hbg_priv *priv);
void hbg_err_reset(struct hbg_priv *priv);

#endif
1 change: 1 addition & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_ethtool.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ static const struct hbg_ethtool_stats hbg_ethtool_stats_info[] = {
HBG_REG_RX_LENGTHFIELD_ERR_CNT_ADDR),
HBG_STATS_REG_I(rx_fail_comma_cnt, HBG_REG_RX_FAIL_COMMA_CNT_ADDR),
HBG_STATS_I(rx_dma_err_cnt),
HBG_STATS_I(rx_fifo_less_empty_thrsld_cnt),

HBG_STATS_REG_I(tx_uc_pkt_cnt, HBG_REG_TX_UC_PKTS_ADDR),
HBG_STATS_REG_I(tx_vlan_pkt_cnt, HBG_REG_TX_TAGGED_ADDR),
Expand Down
55 changes: 33 additions & 22 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ static void hbg_irq_handle_err(struct hbg_priv *priv,
if (irq_info->need_print)
dev_err(&priv->pdev->dev,
"receive error interrupt: %s\n", irq_info->name);

if (irq_info->need_reset)
hbg_err_reset_task_schedule(priv);
}

static void hbg_irq_handle_tx(struct hbg_priv *priv,
Expand All @@ -25,30 +28,38 @@ static void hbg_irq_handle_rx(struct hbg_priv *priv,
napi_schedule(&priv->rx_ring.napi);
}

#define HBG_TXRX_IRQ_I(name, handle) \
{#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
#define HBG_ERR_IRQ_I(name, need_print) \
{#name, HBG_INT_MSK_##name##_B, true, need_print, 0, hbg_irq_handle_err}
static void hbg_irq_handle_rx_buf_val(struct hbg_priv *priv,
struct hbg_irq_info *irq_info)
{
priv->stats.rx_fifo_less_empty_thrsld_cnt++;
}

#define HBG_IRQ_I(name, handle) \
{#name, HBG_INT_MSK_##name##_B, false, false, false, 0, handle}
#define HBG_ERR_IRQ_I(name, need_print, ndde_reset) \
{#name, HBG_INT_MSK_##name##_B, true, need_print, \
ndde_reset, 0, hbg_irq_handle_err}

static struct hbg_irq_info hbg_irqs[] = {
HBG_TXRX_IRQ_I(RX, hbg_irq_handle_rx),
HBG_TXRX_IRQ_I(TX, hbg_irq_handle_tx),
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true),
HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true),
HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true),
HBG_ERR_IRQ_I(TX_AHB_ERR, true),
HBG_ERR_IRQ_I(RX_BUF_AVL, false),
HBG_ERR_IRQ_I(REL_BUF_ERR, true),
HBG_ERR_IRQ_I(TXCFG_AVL, false),
HBG_ERR_IRQ_I(TX_DROP, false),
HBG_ERR_IRQ_I(RX_DROP, false),
HBG_ERR_IRQ_I(RX_AHB_ERR, true),
HBG_ERR_IRQ_I(MAC_FIFO_ERR, false),
HBG_ERR_IRQ_I(RBREQ_ERR, false),
HBG_ERR_IRQ_I(WE_ERR, false),
HBG_IRQ_I(RX, hbg_irq_handle_rx),
HBG_IRQ_I(TX, hbg_irq_handle_tx),
HBG_ERR_IRQ_I(TX_PKT_CPL, true, true),
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true, false),
HBG_ERR_IRQ_I(TX_AHB_ERR, true, true),
HBG_IRQ_I(RX_BUF_AVL, hbg_irq_handle_rx_buf_val),
HBG_ERR_IRQ_I(REL_BUF_ERR, true, false),
HBG_ERR_IRQ_I(TXCFG_AVL, false, false),
HBG_ERR_IRQ_I(TX_DROP, false, false),
HBG_ERR_IRQ_I(RX_DROP, false, false),
HBG_ERR_IRQ_I(RX_AHB_ERR, true, false),
HBG_ERR_IRQ_I(MAC_FIFO_ERR, true, true),
HBG_ERR_IRQ_I(RBREQ_ERR, true, true),
HBG_ERR_IRQ_I(WE_ERR, true, true),
};

static irqreturn_t hbg_irq_handle(int irq_num, void *p)
Expand Down
9 changes: 9 additions & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ static void hbg_service_task(struct work_struct *work)
struct hbg_priv *priv = container_of(work, struct hbg_priv,
service_task.work);

if (test_and_clear_bit(HBG_NIC_STATE_NEED_RESET, &priv->state))
hbg_err_reset(priv);

/* The type of statistics register is u32,
* To prevent the statistics register from overflowing,
* the driver dumps the statistics every 30 seconds.
Expand All @@ -292,6 +295,12 @@ static void hbg_service_task(struct work_struct *work)
msecs_to_jiffies(30 * MSEC_PER_SEC));
}

void hbg_err_reset_task_schedule(struct hbg_priv *priv)
{
set_bit(HBG_NIC_STATE_NEED_RESET, &priv->state);
schedule_delayed_work(&priv->service_task, 0);
}

static void hbg_cancel_delayed_work_sync(void *data)
{
cancel_delayed_work_sync(data);
Expand Down
1 change: 1 addition & 0 deletions drivers/net/ethernet/hisilicon/hibmcge/hbg_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@
#define HBG_INT_MSK_MAC_PCS_TX_FIFO_ERR_B BIT(17)
#define HBG_INT_MSK_MAC_PCS_RX_FIFO_ERR_B BIT(16)
#define HBG_INT_MSK_MAC_MII_FIFO_ERR_B BIT(15)
#define HBG_INT_MSK_TX_PKT_CPL_B BIT(14)
#define HBG_INT_MSK_TX_B BIT(1) /* just used in driver */
#define HBG_INT_MSK_RX_B BIT(0) /* just used in driver */
#define HBG_REG_CF_INTRPT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0434)
Expand Down

0 comments on commit fd394a3

Please sign in to comment.