mirror of
https://github.com/torvalds/linux.git
synced 2026-05-29 17:43:52 +02:00
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:
parent
833b65a3b5
commit
fd394a334b
|
|
@ -36,6 +36,7 @@ enum hbg_nic_state {
|
||||||
HBG_NIC_STATE_EVENT_HANDLING = 0,
|
HBG_NIC_STATE_EVENT_HANDLING = 0,
|
||||||
HBG_NIC_STATE_RESETTING,
|
HBG_NIC_STATE_RESETTING,
|
||||||
HBG_NIC_STATE_RESET_FAIL,
|
HBG_NIC_STATE_RESET_FAIL,
|
||||||
|
HBG_NIC_STATE_NEED_RESET, /* trigger a reset in scheduled task */
|
||||||
};
|
};
|
||||||
|
|
||||||
enum hbg_reset_type {
|
enum hbg_reset_type {
|
||||||
|
|
@ -104,6 +105,7 @@ struct hbg_irq_info {
|
||||||
u32 mask;
|
u32 mask;
|
||||||
bool re_enable;
|
bool re_enable;
|
||||||
bool need_print;
|
bool need_print;
|
||||||
|
bool need_reset;
|
||||||
u64 count;
|
u64 count;
|
||||||
|
|
||||||
void (*irq_handle)(struct hbg_priv *priv, struct hbg_irq_info *info);
|
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_fail_comma_cnt;
|
||||||
|
|
||||||
u64 rx_dma_err_cnt;
|
u64 rx_dma_err_cnt;
|
||||||
|
u64 rx_fifo_less_empty_thrsld_cnt;
|
||||||
|
|
||||||
u64 tx_octets_total_ok_cnt;
|
u64 tx_octets_total_ok_cnt;
|
||||||
u64 tx_uc_pkt_cnt;
|
u64 tx_uc_pkt_cnt;
|
||||||
|
|
@ -268,4 +271,6 @@ struct hbg_priv {
|
||||||
struct delayed_work service_task;
|
struct delayed_work service_task;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void hbg_err_reset_task_schedule(struct hbg_priv *priv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -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++) {
|
for (i = 0; i < priv->vectors.info_array_len; i++) {
|
||||||
info = &priv->vectors.info_array[i];
|
info = &priv->vectors.info_array[i];
|
||||||
seq_printf(s,
|
seq_printf(s,
|
||||||
"%-20s: enabled: %-5s, logged: %-5s, count: %llu\n",
|
"%-20s: enabled: %-5s, reset: %-5s, logged: %-5s, count: %llu\n",
|
||||||
info->name,
|
info->name,
|
||||||
str_true_false(hbg_hw_irq_is_enabled(priv,
|
str_true_false(hbg_hw_irq_is_enabled(priv,
|
||||||
info->mask)),
|
info->mask)),
|
||||||
|
str_true_false(info->need_reset),
|
||||||
str_true_false(info->need_print),
|
str_true_false(info->need_print),
|
||||||
info->count);
|
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));
|
state_str_true_false(priv, HBG_NIC_STATE_RESET_FAIL));
|
||||||
seq_printf(s, "last reset type: %s\n",
|
seq_printf(s, "last reset type: %s\n",
|
||||||
reset_type_str[priv->reset_type]);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,62 @@ int hbg_reset(struct hbg_priv *priv)
|
||||||
return hbg_reset_done(priv, HBG_RESET_TYPE_FUNCTION);
|
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)
|
static void hbg_pci_err_reset_prepare(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct net_device *netdev = pci_get_drvdata(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 = {
|
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_prepare = hbg_pci_err_reset_prepare,
|
||||||
.reset_done = hbg_pci_err_reset_done,
|
.reset_done = hbg_pci_err_reset_done,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,6 @@
|
||||||
void hbg_set_pci_err_handler(struct pci_driver *pdrv);
|
void hbg_set_pci_err_handler(struct pci_driver *pdrv);
|
||||||
int hbg_reset(struct hbg_priv *priv);
|
int hbg_reset(struct hbg_priv *priv);
|
||||||
int hbg_rebuild(struct hbg_priv *priv);
|
int hbg_rebuild(struct hbg_priv *priv);
|
||||||
|
void hbg_err_reset(struct hbg_priv *priv);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,7 @@ static const struct hbg_ethtool_stats hbg_ethtool_stats_info[] = {
|
||||||
HBG_REG_RX_LENGTHFIELD_ERR_CNT_ADDR),
|
HBG_REG_RX_LENGTHFIELD_ERR_CNT_ADDR),
|
||||||
HBG_STATS_REG_I(rx_fail_comma_cnt, HBG_REG_RX_FAIL_COMMA_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_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_uc_pkt_cnt, HBG_REG_TX_UC_PKTS_ADDR),
|
||||||
HBG_STATS_REG_I(tx_vlan_pkt_cnt, HBG_REG_TX_TAGGED_ADDR),
|
HBG_STATS_REG_I(tx_vlan_pkt_cnt, HBG_REG_TX_TAGGED_ADDR),
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,9 @@ static void hbg_irq_handle_err(struct hbg_priv *priv,
|
||||||
if (irq_info->need_print)
|
if (irq_info->need_print)
|
||||||
dev_err(&priv->pdev->dev,
|
dev_err(&priv->pdev->dev,
|
||||||
"receive error interrupt: %s\n", irq_info->name);
|
"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,
|
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);
|
napi_schedule(&priv->rx_ring.napi);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HBG_TXRX_IRQ_I(name, handle) \
|
static void hbg_irq_handle_rx_buf_val(struct hbg_priv *priv,
|
||||||
{#name, HBG_INT_MSK_##name##_B, false, false, 0, handle}
|
struct hbg_irq_info *irq_info)
|
||||||
#define HBG_ERR_IRQ_I(name, need_print) \
|
{
|
||||||
{#name, HBG_INT_MSK_##name##_B, true, need_print, 0, hbg_irq_handle_err}
|
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[] = {
|
static struct hbg_irq_info hbg_irqs[] = {
|
||||||
HBG_TXRX_IRQ_I(RX, hbg_irq_handle_rx),
|
HBG_IRQ_I(RX, hbg_irq_handle_rx),
|
||||||
HBG_TXRX_IRQ_I(TX, hbg_irq_handle_tx),
|
HBG_IRQ_I(TX, hbg_irq_handle_tx),
|
||||||
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true),
|
HBG_ERR_IRQ_I(TX_PKT_CPL, true, true),
|
||||||
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true),
|
HBG_ERR_IRQ_I(MAC_MII_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true),
|
HBG_ERR_IRQ_I(MAC_PCS_RX_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true),
|
HBG_ERR_IRQ_I(MAC_PCS_TX_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true),
|
HBG_ERR_IRQ_I(MAC_APP_RX_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true),
|
HBG_ERR_IRQ_I(MAC_APP_TX_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(TX_AHB_ERR, true),
|
HBG_ERR_IRQ_I(SRAM_PARITY_ERR, true, false),
|
||||||
HBG_ERR_IRQ_I(RX_BUF_AVL, false),
|
HBG_ERR_IRQ_I(TX_AHB_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(REL_BUF_ERR, true),
|
HBG_IRQ_I(RX_BUF_AVL, hbg_irq_handle_rx_buf_val),
|
||||||
HBG_ERR_IRQ_I(TXCFG_AVL, false),
|
HBG_ERR_IRQ_I(REL_BUF_ERR, true, false),
|
||||||
HBG_ERR_IRQ_I(TX_DROP, false),
|
HBG_ERR_IRQ_I(TXCFG_AVL, false, false),
|
||||||
HBG_ERR_IRQ_I(RX_DROP, false),
|
HBG_ERR_IRQ_I(TX_DROP, false, false),
|
||||||
HBG_ERR_IRQ_I(RX_AHB_ERR, true),
|
HBG_ERR_IRQ_I(RX_DROP, false, false),
|
||||||
HBG_ERR_IRQ_I(MAC_FIFO_ERR, false),
|
HBG_ERR_IRQ_I(RX_AHB_ERR, true, false),
|
||||||
HBG_ERR_IRQ_I(RBREQ_ERR, false),
|
HBG_ERR_IRQ_I(MAC_FIFO_ERR, true, true),
|
||||||
HBG_ERR_IRQ_I(WE_ERR, false),
|
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)
|
static irqreturn_t hbg_irq_handle(int irq_num, void *p)
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,9 @@ static void hbg_service_task(struct work_struct *work)
|
||||||
struct hbg_priv *priv = container_of(work, struct hbg_priv,
|
struct hbg_priv *priv = container_of(work, struct hbg_priv,
|
||||||
service_task.work);
|
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,
|
/* The type of statistics register is u32,
|
||||||
* To prevent the statistics register from overflowing,
|
* To prevent the statistics register from overflowing,
|
||||||
* the driver dumps the statistics every 30 seconds.
|
* 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));
|
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)
|
static void hbg_cancel_delayed_work_sync(void *data)
|
||||||
{
|
{
|
||||||
cancel_delayed_work_sync(data);
|
cancel_delayed_work_sync(data);
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@
|
||||||
#define HBG_INT_MSK_MAC_PCS_TX_FIFO_ERR_B BIT(17)
|
#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_PCS_RX_FIFO_ERR_B BIT(16)
|
||||||
#define HBG_INT_MSK_MAC_MII_FIFO_ERR_B BIT(15)
|
#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_TX_B BIT(1) /* just used in driver */
|
||||||
#define HBG_INT_MSK_RX_B BIT(0) /* 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)
|
#define HBG_REG_CF_INTRPT_STAT_ADDR (HBG_REG_SGMII_BASE + 0x0434)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user