net: wangxun: implement soft quiesce for PCIe error recovery

Function wx_soft_quiesce() provide a lightweight shutdown path during
PCIe error recovery. It avoids MMIO-dependent operations in PCIe error
status.

Waiting for the service task to complete may unnecessarily delay PCIe
error recovery, especially if the work item is already blocked by the
hardware failure that triggered AER. So the service task is not
explicitly cancelled in quiesce path. As a measure to block the service
task, the checking of WX_STATE_DOWN and WX_STATE_RESETTING is added at
the entry of relevant work item.

Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Link: https://patch.msgid.link/20260803064334.21876-5-jiawenwu@trustnetic.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jiawen Wu 2026-08-03 14:43:33 +08:00 committed by Jakub Kicinski
parent c656a3b75c
commit c023e9769d
5 changed files with 63 additions and 0 deletions

View File

@ -3410,5 +3410,23 @@ void wx_service_timer(struct timer_list *t)
}
EXPORT_SYMBOL(wx_service_timer);
void wx_soft_quiesce(struct wx *wx)
{
if (!netif_running(wx->netdev) ||
test_and_set_bit(WX_STATE_DOWN, wx->state))
return;
pci_clear_master(wx->pdev);
netif_tx_stop_all_queues(wx->netdev);
netif_carrier_off(wx->netdev);
netif_tx_disable(wx->netdev);
wx_napi_disable_all(wx);
wx_ptp_quiesce(wx);
clear_bit(WX_FLAG_NEED_DO_RESET, wx->flags);
timer_delete_sync(&wx->service_timer);
}
EXPORT_SYMBOL(wx_soft_quiesce);
MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");

View File

@ -41,5 +41,6 @@ int wx_set_ring(struct wx *wx, u32 new_tx_count,
void wx_service_event_schedule(struct wx *wx);
void wx_service_event_complete(struct wx *wx);
void wx_service_timer(struct timer_list *t);
void wx_soft_quiesce(struct wx *wx);
#endif /* _WX_LIB_H_ */

View File

@ -321,6 +321,9 @@ static long wx_ptp_do_aux_work(struct ptp_clock_info *ptp)
struct wx *wx = container_of(ptp, struct wx, ptp_caps);
int ts_done;
if (!test_bit(WX_STATE_PTP_RUNNING, wx->state))
return HZ;
ts_done = wx_ptp_tx_hwtstamp_work(wx);
wx_ptp_overflow_check(wx);
@ -842,6 +845,30 @@ void wx_ptp_stop(struct wx *wx)
}
EXPORT_SYMBOL(wx_ptp_stop);
void wx_ptp_quiesce(struct wx *wx)
{
if (!test_and_clear_bit(WX_STATE_PTP_RUNNING, wx->state))
return;
clear_bit(WX_FLAG_PTP_PPS_ENABLED, wx->flags);
if (wx->ptp_clock)
ptp_cancel_worker_sync(wx->ptp_clock);
if (wx->ptp_tx_skb) {
dev_kfree_skb_any(wx->ptp_tx_skb);
wx->ptp_tx_skb = NULL;
}
clear_bit_unlock(WX_STATE_PTP_TX_IN_PROGRESS, wx->state);
if (wx->ptp_clock) {
ptp_clock_unregister(wx->ptp_clock);
wx->ptp_clock = NULL;
dev_info(&wx->pdev->dev, "removed PHC on %s\n", wx->netdev->name);
}
}
EXPORT_SYMBOL(wx_ptp_quiesce);
/**
* wx_ptp_rx_hwtstamp - utility function which checks for RX time stamp
* @wx: pointer to wx struct

View File

@ -10,6 +10,7 @@ void wx_ptp_reset(struct wx *wx);
void wx_ptp_init(struct wx *wx);
void wx_ptp_suspend(struct wx *wx);
void wx_ptp_stop(struct wx *wx);
void wx_ptp_quiesce(struct wx *wx);
void wx_ptp_rx_hwtstamp(struct wx *wx, struct sk_buff *skb);
int wx_hwtstamp_get(struct net_device *dev,
struct kernel_hwtstamp_config *cfg);

View File

@ -94,12 +94,24 @@ static void txgbe_module_detection_subtask(struct wx *wx)
{
int err;
if (test_bit(WX_STATE_DOWN, wx->state) ||
test_bit(WX_STATE_RESETTING, wx->state))
return;
if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags))
return;
/* wait for SFF module ready */
msleep(200);
/* Re-check state to avoid racing with down/reset paths.
* Module identification is deferred to the next up event,
* so it is safe to bail out here.
*/
if (test_bit(WX_STATE_DOWN, wx->state) ||
test_bit(WX_STATE_RESETTING, wx->state))
return;
err = txgbe_identify_module(wx);
if (err == -ENODEV)
set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags);
@ -107,6 +119,10 @@ static void txgbe_module_detection_subtask(struct wx *wx)
static void txgbe_link_config_subtask(struct wx *wx)
{
if (test_bit(WX_STATE_DOWN, wx->state) ||
test_bit(WX_STATE_RESETTING, wx->state))
return;
if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags))
return;