net: wangxun: add pcie error handler

Support AER driver to handle the PCIe errors. Sometimes netdev watchdog
Tx timeout happens before the AER error report when a PCIe error occurs,
CPU blocking would be caused by MMIO during the reset process. To
prevent it, check PCIe error status in .ndo_tx_timeout. The current
function of ngbe is not yet fully developed, it will be completed in the
future.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Link: https://patch.msgid.link/20260803064334.21876-6-jiawenwu@trustnetic.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jiawen Wu
2026-08-03 14:43:34 +08:00
committed by Jakub Kicinski
parent c023e9769d
commit e73e4d187a
5 changed files with 223 additions and 5 deletions

View File

@@ -4,11 +4,136 @@
#include <linux/netdevice.h>
#include <linux/pci.h>
#include <linux/aer.h>
#include "wx_type.h"
#include "wx_lib.h"
#include "wx_err.h"
/**
* wx_io_error_detected - called when PCI error is detected
* @pdev: Pointer to PCI device
* @state: The current pci connection state
*
* Return: pci_ers_result_t.
*
* This function is called after a PCI bus error affecting
* this device has been detected.
*/
static pci_ers_result_t wx_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
if (!wx)
return PCI_ERS_RESULT_DISCONNECT;
netdev = wx->netdev;
if (!netif_device_present(netdev))
return PCI_ERS_RESULT_DISCONNECT;
rtnl_lock();
netif_device_detach(netdev);
set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
wx_soft_quiesce(wx);
if (state == pci_channel_io_perm_failure) {
rtnl_unlock();
return PCI_ERS_RESULT_DISCONNECT;
}
if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
pci_disable_device(pdev);
rtnl_unlock();
/* Request a slot reset. */
return PCI_ERS_RESULT_NEED_RESET;
}
/**
* wx_io_slot_reset - called after the pci bus has been reset.
* @pdev: Pointer to PCI device
*
* Return: pci_ers_result_t.
*
* Restart the card from scratch, as if from a cold-boot.
*/
static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
if (pci_enable_device_mem(pdev)) {
wx_err(wx, "Cannot re-enable PCI device after reset.\n");
return PCI_ERS_RESULT_DISCONNECT;
}
/* make all memory operations done before clearing the flag */
smp_mb__before_atomic();
clear_bit(WX_STATE_DISABLED, wx->state);
clear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
pci_set_master(pdev);
pci_restore_state(pdev);
pci_wake_from_d3(pdev, false);
rtnl_lock();
if (netif_running(wx->netdev) && wx->down_suspend)
wx->down_suspend(wx);
if (wx->do_reset)
wx->do_reset(wx->netdev, false);
rtnl_unlock();
return PCI_ERS_RESULT_RECOVERED;
}
/**
* wx_io_resume - called when traffic can start flowing again.
* @pdev: Pointer to PCI device
*
* This callback is called when the error recovery driver tells us that
* its OK to resume normal operation.
*/
static void wx_io_resume(struct pci_dev *pdev)
{
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
int err;
netdev = wx->netdev;
rtnl_lock();
if (netif_running(netdev)) {
err = netdev->netdev_ops->ndo_open(netdev);
if (err) {
wx_err(wx, "Failed to open netdev after reset\n");
goto out;
}
}
netif_device_attach(netdev);
out:
rtnl_unlock();
}
const struct pci_error_handlers wx_err_handler = {
.error_detected = wx_io_error_detected,
.slot_reset = wx_io_slot_reset,
.resume = wx_io_resume,
};
EXPORT_SYMBOL(wx_err_handler);
static bool wx_check_pcie_error(struct wx *wx)
{
u16 vid, pci_cmd;
pci_read_config_word(wx->pdev, PCI_VENDOR_ID, &vid);
pci_read_config_word(wx->pdev, PCI_COMMAND, &pci_cmd);
/* PCIe link loss or memory space can't access */
if (vid == U16_MAX || !(pci_cmd & PCI_COMMAND_MEMORY))
return true;
return false;
}
static void wx_pf_reset_subtask(struct wx *wx)
{
if (!test_and_clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags))
@@ -25,6 +150,22 @@ static void wx_reset_task(struct work_struct *work)
rtnl_lock();
/* If the device has been detached (e.g., due to AER error handling),
* abort the reset task to prevent operating on a dead or unmanaged
* hardware.
*/
if (!netif_device_present(wx->netdev))
goto out;
if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags)) {
/* Double check: Verify if the PCIe error is still present. */
if (wx_check_pcie_error(wx))
wx_soft_quiesce(wx);
else
clear_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
goto out;
}
if (test_bit(WX_STATE_DOWN, wx->state) ||
test_bit(WX_STATE_RESETTING, wx->state))
goto out;
@@ -139,6 +280,19 @@ void wx_check_hang_subtask(struct wx *wx)
}
EXPORT_SYMBOL(wx_check_hang_subtask);
static void wx_tx_timeout_recovery(struct wx *wx)
{
/*
* When a PCIe hardware error occurs, the driver should initiate a PCIe
* recovery mechanism. However, this recovery flow relies on the AER
* driver for current kernel policy. Therefore, a self-contained
* recovery mechanism is not implemented yet.
*/
set_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags);
wx_err(wx, "PCIe error detected during tx timeout\n");
queue_work(wx->reset_wq, &wx->reset_task);
}
static void wx_tx_timeout_reset(struct wx *wx)
{
if (test_bit(WX_STATE_DOWN, wx->state))
@@ -153,7 +307,10 @@ void wx_tx_timeout(struct net_device *netdev, unsigned int __always_unused txque
{
struct wx *wx = netdev_priv(netdev);
wx_tx_timeout_reset(wx);
if (wx_check_pcie_error(wx))
wx_tx_timeout_recovery(wx);
else
wx_tx_timeout_reset(wx);
}
EXPORT_SYMBOL(wx_tx_timeout);

View File

@@ -7,6 +7,8 @@
#ifndef _WX_ERR_H_
#define _WX_ERR_H_
extern const struct pci_error_handlers wx_err_handler;
void wx_check_err_subtask(struct wx *wx);
int wx_init_err_task(struct wx *wx);
void wx_check_hang_subtask(struct wx *wx);

View File

@@ -1222,6 +1222,8 @@ enum wx_state {
WX_STATE_PTP_RUNNING,
WX_STATE_PTP_TX_IN_PROGRESS,
WX_STATE_SERVICE_SCHED,
WX_STATE_DISABLED,
WX_STATE_RES_FREED,
WX_STATE_NBITS /* must be last */
};
@@ -1288,6 +1290,7 @@ enum wx_pf_flags {
WX_FLAG_NEED_DO_RESET,
WX_FLAG_RX_MERGE_ENABLED,
WX_FLAG_TXHEAD_WB_ENABLED,
WX_FLAG_NEED_PCIE_RECOVERY,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1409,6 +1412,7 @@ struct wx {
void (*configure_fdir)(struct wx *wx);
int (*setup_tc)(struct net_device *netdev, u8 tc);
void (*do_reset)(struct net_device *netdev, bool reinit);
void (*down_suspend)(struct wx *wx);
int (*ptp_setup_sdp)(struct wx *wx);
void (*set_num_queues)(struct wx *wx);

View File

@@ -47,6 +47,20 @@ static const struct pci_device_id ngbe_pci_tbl[] = {
{ }
};
static void ngbe_down_suspend(struct wx *wx)
{
if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
return;
phylink_stop(wx->phylink);
phylink_disconnect_phy(wx->phylink);
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
wx_free_irq(wx);
wx_free_isb_resources(wx);
wx_free_resources(wx);
}
/**
* ngbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
@@ -135,6 +149,7 @@ static int ngbe_sw_init(struct wx *wx)
wx->mbx.size = WX_VXMAILBOX_SIZE;
wx->setup_tc = ngbe_setup_tc;
wx->do_reset = ngbe_do_reset;
wx->down_suspend = ngbe_down_suspend;
set_bit(0, &wx->fwd_bitmask);
return 0;
@@ -413,6 +428,9 @@ static void ngbe_disable_device(struct wx *wx)
static void ngbe_reset(struct wx *wx)
{
if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
return;
wx_flush_sw_mac_table(wx);
wx_mac_set_default_filter(wx, wx->mac.addr);
if (test_bit(WX_STATE_PTP_RUNNING, wx->state))
@@ -435,6 +453,7 @@ static void ngbe_up_complete(struct wx *wx)
/* make sure to complete pre-operations */
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
clear_bit(WX_STATE_RES_FREED, wx->state);
wx_napi_enable_all(wx);
/* enable transmits */
netif_tx_start_all_queues(wx->netdev);
@@ -529,12 +548,16 @@ static int ngbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
if (test_bit(WX_STATE_RES_FREED, wx->state))
goto out;
wx_ptp_stop(wx);
ngbe_down(wx);
wx_free_irq(wx);
wx_free_isb_resources(wx);
wx_free_resources(wx);
phylink_disconnect_phy(wx->phylink);
out:
wx_control_hw(wx, false);
return 0;
@@ -566,7 +589,8 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
*enable_wake = !!wufc;
wx_control_hw(wx, false);
pci_disable_device(pdev);
if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
pci_disable_device(pdev);
}
static void ngbe_shutdown(struct pci_dev *pdev)
@@ -854,6 +878,7 @@ static int ngbe_probe(struct pci_dev *pdev,
goto err_register;
pci_set_drvdata(pdev, wx);
pci_save_state(pdev);
return 0;
@@ -909,7 +934,8 @@ static void ngbe_remove(struct pci_dev *pdev)
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
pci_disable_device(pdev);
}
static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -936,6 +962,7 @@ static int ngbe_resume(struct pci_dev *pdev)
wx_err(wx, "Cannot enable PCI device from suspend\n");
return err;
}
clear_bit(WX_STATE_DISABLED, wx->state);
pci_set_master(pdev);
device_wakeup_disable(&pdev->dev);
@@ -960,6 +987,7 @@ static struct pci_driver ngbe_driver = {
.resume = ngbe_resume,
.shutdown = ngbe_shutdown,
.sriov_configure = wx_pci_sriov_configure,
.err_handler = &wx_err_handler,
};
module_pci_driver(ngbe_driver);

View File

@@ -163,6 +163,7 @@ static void txgbe_up_complete(struct wx *wx)
/* make sure to complete pre-operations */
smp_mb__before_atomic();
clear_bit(WX_STATE_DOWN, wx->state);
clear_bit(WX_STATE_RES_FREED, wx->state);
wx_napi_enable_all(wx);
switch (wx->mac.type) {
@@ -206,6 +207,9 @@ static void txgbe_reset(struct wx *wx)
u8 old_addr[ETH_ALEN];
int err;
if (test_bit(WX_FLAG_NEED_PCIE_RECOVERY, wx->flags))
return;
err = txgbe_reset_hw(wx);
if (err != 0)
wx_err(wx, "Hardware Error: %d\n", err);
@@ -312,6 +316,20 @@ void txgbe_up(struct wx *wx)
txgbe_up_complete(wx);
}
static void txgbe_down_suspend(struct wx *wx)
{
if (test_and_set_bit(WX_STATE_RES_FREED, wx->state))
return;
phylink_stop(wx->phylink);
wx_clean_all_tx_rings(wx);
wx_clean_all_rx_rings(wx);
wx_free_irq(wx);
txgbe_free_misc_irq(wx->priv);
wx_free_resources(wx);
txgbe_fdir_filter_exit(wx);
}
/**
* txgbe_init_type_code - Initialize the shared code
* @wx: pointer to hardware structure
@@ -428,6 +446,7 @@ static int txgbe_sw_init(struct wx *wx)
wx->setup_tc = txgbe_setup_tc;
wx->do_reset = txgbe_do_reset;
wx->down_suspend = txgbe_down_suspend;
set_bit(0, &wx->fwd_bitmask);
switch (wx->mac.type) {
@@ -538,12 +557,16 @@ static int txgbe_close(struct net_device *netdev)
{
struct wx *wx = netdev_priv(netdev);
if (test_bit(WX_STATE_RES_FREED, wx->state))
goto out;
wx_ptp_stop(wx);
txgbe_down(wx);
wx_free_irq(wx);
txgbe_free_misc_irq(wx->priv);
wx_free_resources(wx);
txgbe_fdir_filter_exit(wx);
out:
wx_control_hw(wx, false);
return 0;
@@ -564,7 +587,8 @@ static void txgbe_dev_shutdown(struct pci_dev *pdev)
wx_control_hw(wx, false);
pci_disable_device(pdev);
if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
pci_disable_device(pdev);
}
static void txgbe_shutdown(struct pci_dev *pdev)
@@ -914,6 +938,7 @@ static int txgbe_probe(struct pci_dev *pdev,
goto err_remove_phy;
pci_set_drvdata(pdev, wx);
pci_save_state(pdev);
netif_tx_stop_all_queues(netdev);
@@ -989,7 +1014,8 @@ static void txgbe_remove(struct pci_dev *pdev)
kfree(wx->mac_table);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
if (!test_and_set_bit(WX_STATE_DISABLED, wx->state))
pci_disable_device(pdev);
}
static struct pci_driver txgbe_driver = {
@@ -999,6 +1025,7 @@ static struct pci_driver txgbe_driver = {
.remove = txgbe_remove,
.shutdown = txgbe_shutdown,
.sriov_configure = wx_pci_sriov_configure,
.err_handler = &wx_err_handler,
};
module_pci_driver(txgbe_driver);