net: hibmcge: Add support for abnormal irq handling feature

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>
This commit is contained in:
Jijie Shao
2025-02-28 19:54:08 +08:00
committed by Paolo Abeni
parent 833b65a3b5
commit fd394a334b
8 changed files with 112 additions and 23 deletions

View File

@@ -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 {
@@ -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);
@@ -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;
@@ -268,4 +271,6 @@ struct hbg_priv {
struct delayed_work service_task;
};
void hbg_err_reset_task_schedule(struct hbg_priv *priv);
#endif

View File

@@ -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);
}
@@ -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;
}

View File

@@ -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);
@@ -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,
};

View File

@@ -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

View File

@@ -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),

View File

@@ -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,
@@ -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)

View File

@@ -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.
@@ -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);

View File

@@ -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)