From 7116ffb48ad0af7a6d066c3e80329270920858fe Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 25 May 2026 18:05:41 +0800 Subject: [PATCH 1/3] net: wangxun: introduce WX_STATE_DOWN to serialize device shutdown state Replace various netif_running() checks with an explicit WX_STATE_DOWN state bit to track whether the device datapath and interrupt handling are operational. The previous logic relied on netif_running() to gate interrupt reenablement, queue wakeups, statistics updates, and service task execution. However, netif_running() only reflects the administrative state of the netdevice and does not fully serialize against teardown and reset paths. During device shutdown and reset flows, asynchronous contexts such as interrupt handlers, NAPI poll, and service work could still observe netif_running() as true while device resources were already being disabled or freed. Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260525100543.27140-2-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/libwx/wx_hw.c | 3 ++- drivers/net/ethernet/wangxun/libwx/wx_lib.c | 9 ++++++--- drivers/net/ethernet/wangxun/libwx/wx_sriov.c | 2 +- drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 + drivers/net/ethernet/wangxun/libwx/wx_vf_common.c | 8 ++++++-- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 12 ++++++++---- drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c | 2 +- drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 4 ++++ 8 files changed, 29 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c index 2451f6b20b11..260e14d5d541 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c @@ -2520,6 +2520,7 @@ int wx_sw_init(struct wx *wx) mutex_init(&wx->reset_lock); bitmap_zero(wx->state, WX_STATE_NBITS); bitmap_zero(wx->flags, WX_PF_FLAGS_NBITS); + set_bit(WX_STATE_DOWN, wx->state); wx->misc_irq_domain = false; return 0; @@ -2875,7 +2876,7 @@ void wx_update_stats(struct wx *wx) u64 restart_queue = 0, tx_busy = 0; u32 i; - if (!netif_running(wx->netdev) || + if (test_bit(WX_STATE_DOWN, wx->state) || test_bit(WX_STATE_RESETTING, wx->state)) return; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c index 746623fa59b4..d042567b8128 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c @@ -876,7 +876,7 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector, if (__netif_subqueue_stopped(tx_ring->netdev, tx_ring->queue_index) && - netif_running(tx_ring->netdev)) { + !test_bit(WX_STATE_DOWN, wx->state)) { netif_wake_subqueue(tx_ring->netdev, tx_ring->queue_index); ++tx_ring->tx_stats.restart_queue; @@ -964,7 +964,7 @@ static int wx_poll(struct napi_struct *napi, int budget) if (likely(napi_complete_done(napi, work_done))) { if (wx->adaptive_itr) wx_update_dim_sample(q_vector); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wx_intr_enable(wx, WX_INTR_Q(q_vector->v_idx)); } @@ -2341,6 +2341,8 @@ int wx_init_interrupt_scheme(struct wx *wx) wx_cache_ring_rss(wx); + set_bit(WX_STATE_DOWN, wx->state); + return 0; } EXPORT_SYMBOL(wx_init_interrupt_scheme); @@ -3314,7 +3316,8 @@ EXPORT_SYMBOL(wx_set_ring); void wx_service_event_schedule(struct wx *wx) { - if (!test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state)) + if (!test_bit(WX_STATE_DOWN, wx->state) && + !test_and_set_bit(WX_STATE_SERVICE_SCHED, wx->state)) queue_work(system_power_efficient_wq, &wx->service_task); } EXPORT_SYMBOL(wx_service_event_schedule); diff --git a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c index a360b06a086a..0152004a2dd3 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_sriov.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_sriov.c @@ -898,7 +898,7 @@ static void wx_set_vf_link_state(struct wx *wx, int vf, int state) wx->vfinfo[vf].link_state = state; switch (state) { case IFLA_VF_LINK_STATE_AUTO: - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wx->vfinfo[vf].link_enable = true; else wx->vfinfo[vf].link_enable = false; diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h index 0da5565ee4ff..c7befe4cdfe9 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_type.h +++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h @@ -1202,6 +1202,7 @@ struct wx_last_stats { }; enum wx_state { + WX_STATE_DOWN, WX_STATE_RESETTING, WX_STATE_SWFW_BUSY, WX_STATE_PTP_RUNNING, diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c index 94ff8f5f0b4c..0d2db8d38cd5 100644 --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c @@ -68,7 +68,7 @@ static irqreturn_t wx_msix_misc_vf(int __always_unused irq, void *data) set_bit(WX_FLAG_NEED_UPDATE_LINK, wx->flags); /* Clear the interrupt */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) wr32(wx, WX_VXIMC, wx->eims_other); return IRQ_HANDLED; @@ -278,6 +278,7 @@ static void wxvf_up_complete(struct wx *wx) wx_configure_msix_vf(wx); smp_mb__before_atomic(); + clear_bit(WX_STATE_DOWN, wx->state); wx_napi_enable_all(wx); /* clear any pending interrupts, may auto mask */ @@ -327,6 +328,9 @@ static void wxvf_down(struct wx *wx) { struct net_device *netdev = wx->netdev; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + timer_delete_sync(&wx->service_timer); netif_tx_stop_all_queues(netdev); netif_tx_disable(netdev); @@ -360,7 +364,7 @@ static void wxvf_reset_subtask(struct wx *wx) rtnl_lock(); if (test_bit(WX_STATE_RESETTING, wx->state) || - !(netif_running(wx->netdev))) { + test_bit(WX_STATE_DOWN, wx->state)) { rtnl_unlock(); return; } diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index d51d8db95a76..f4a2dd6fa493 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -204,7 +204,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data) /* shared interrupt alert! * the interrupt that we masked before the EICR read. */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, true); return IRQ_NONE; /* Not our interrupt */ } @@ -220,7 +220,7 @@ static irqreturn_t ngbe_intr(int __always_unused irq, void *data) /* would disable interrupts here but it is auto disabled */ napi_schedule_irqoff(&q_vector->napi); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, false); return IRQ_HANDLED; @@ -235,7 +235,7 @@ static irqreturn_t __ngbe_msix_misc(struct wx *wx, u32 eicr) wx_ptp_check_pps_event(wx); /* re-enable the original interrupt state, no lsc, no queues */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, false); return IRQ_HANDLED; @@ -262,7 +262,7 @@ static irqreturn_t ngbe_misc_and_queue(int __always_unused irq, void *data) /* queue */ q_vector = wx->q_vector[0]; napi_schedule_irqoff(&q_vector->napi); - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) ngbe_irq_enable(wx, true); return IRQ_HANDLED; } @@ -363,6 +363,9 @@ static void ngbe_disable_device(struct wx *wx) struct net_device *netdev = wx->netdev; u32 i; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + if (wx->num_vfs) { /* Clear EITR Select mapping */ wr32(wx, WX_PX_ITRSEL, 0); @@ -428,6 +431,7 @@ void ngbe_up(struct wx *wx) /* make sure to complete pre-operations */ smp_mb__before_atomic(); + clear_bit(WX_STATE_DOWN, wx->state); wx_napi_enable_all(wx); /* enable transmits */ netif_tx_start_all_queues(wx->netdev); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c index aa14958d439a..8746318ad3bc 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_irq.c @@ -141,7 +141,7 @@ static irqreturn_t txgbe_misc_irq_handle(int irq, void *data) /* shared interrupt alert! * the interrupt that we masked before the ICR read. */ - if (netif_running(wx->netdev)) + if (!test_bit(WX_STATE_DOWN, wx->state)) txgbe_irq_enable(wx, true); return IRQ_NONE; /* Not our interrupt */ } diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 4c549c2644ab..f9cd1caaf0a4 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -151,6 +151,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); wx_napi_enable_all(wx); switch (wx->mac.type) { @@ -213,6 +214,9 @@ static void txgbe_disable_device(struct wx *wx) struct net_device *netdev = wx->netdev; u32 i; + if (test_and_set_bit(WX_STATE_DOWN, wx->state)) + return; + wx_disable_pcie_master(wx); /* disable receives */ wx_disable_rx(wx); From 58ac2f8eb050be93484f154d40f17511cd87164c Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 25 May 2026 18:05:42 +0800 Subject: [PATCH 2/3] net: wangxun: avoid statistics updates during device teardown After introducing WX_STATE_DOWN, wx_update_stats() now explicitly skips statistics collection while the device is in teardown or reset state. Calling wx_update_stats() from the device disable path therefore becomes redundant. Remove wx_update_stats() calls from ngbe_disable_device() and txgbe_disable_device(). Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260525100543.27140-3-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 2 -- drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 2 -- 2 files changed, 4 deletions(-) diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c index f4a2dd6fa493..8678c49b892a 100644 --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c @@ -404,8 +404,6 @@ static void ngbe_disable_device(struct wx *wx) wr32(wx, WX_PX_TR_CFG(reg_idx), WX_PX_TR_CFG_SWFLSH); } - - wx_update_stats(wx); } static void ngbe_reset(struct wx *wx) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index f9cd1caaf0a4..6755464b4637 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -265,8 +265,6 @@ static void txgbe_disable_device(struct wx *wx) /* Disable the Tx DMA engine */ wr32m(wx, WX_TDM_CTL, WX_TDM_CTL_TE, 0); - - wx_update_stats(wx); } void txgbe_down(struct wx *wx) From f67aead16e85f7bae5b4c2546f8972e867cd0873 Mon Sep 17 00:00:00 2001 From: Jiawen Wu Date: Mon, 25 May 2026 18:05:43 +0800 Subject: [PATCH 3/3] net: txgbe: rework service event handling Convert to use test_and_clear_bit() for link event subtasks. Only re-arm the WX_FLAG_NEED_MODULE_RESET flag when module is absent. Unsupported or invalid modules no longer cause the service task to continuously retry module identification. Additionally, explicitly cancel service_task during device teardown to ensure no pending asynchronous service work survives after the device has entered the DOWN state. Signed-off-by: Jiawen Wu Link: https://patch.msgid.link/20260525100543.27140-4-jiawenwu@trustnetic.com Signed-off-by: Paolo Abeni --- .../net/ethernet/wangxun/txgbe/txgbe_aml.c | 10 +++------- .../net/ethernet/wangxun/txgbe/txgbe_aml.h | 2 +- .../net/ethernet/wangxun/txgbe/txgbe_main.c | 19 ++++++------------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c index f0514251d4f3..da1d3976ed33 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.c @@ -186,19 +186,15 @@ static void txgbe_get_mac_link(struct wx *wx, int *speed) *speed = SPEED_UNKNOWN; } -int txgbe_set_phy_link(struct wx *wx) +void txgbe_set_phy_link(struct wx *wx) { int speed, autoneg, duplex, err; txgbe_get_link_capabilities(wx, &speed, &autoneg, &duplex); err = txgbe_set_phy_link_hostif(wx, speed, autoneg, duplex); - if (err) { + if (err) wx_err(wx, "Failed to setup link\n"); - return err; - } - - return 0; } static int txgbe_sfp_to_linkmodes(struct wx *wx, struct txgbe_sff_id *id) @@ -362,7 +358,7 @@ int txgbe_identify_module(struct wx *wx) id->identifier != TXGBE_SFF_IDENTIFIER_QSFP_PLUS && id->identifier != TXGBE_SFF_IDENTIFIER_QSFP28) { wx_err(wx, "Invalid module\n"); - return -ENODEV; + return -EINVAL; } if (id->transceiver_type == 0xFF) diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h index 4f6df0ee860b..379c74ad19c6 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_aml.h @@ -10,7 +10,7 @@ int txgbe_test_hostif(struct wx *wx); int txgbe_read_eeprom_hostif(struct wx *wx, struct txgbe_hic_i2c_read *buffer, u32 length, u8 *data); -int txgbe_set_phy_link(struct wx *wx); +void txgbe_set_phy_link(struct wx *wx); int txgbe_identify_module(struct wx *wx); void txgbe_setup_link(struct wx *wx); int txgbe_phylink_init_aml(struct txgbe *txgbe); diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c index 6755464b4637..ce82e13aa8ae 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c @@ -93,31 +93,23 @@ static void txgbe_module_detection_subtask(struct wx *wx) { int err; - if (!test_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags)) + if (!test_and_clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags)) return; /* wait for SFF module ready */ msleep(200); err = txgbe_identify_module(wx); - if (err) - return; - - clear_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags); + if (err == -ENODEV) + set_bit(WX_FLAG_NEED_MODULE_RESET, wx->flags); } static void txgbe_link_config_subtask(struct wx *wx) { - int err; - - if (!test_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags)) + if (!test_and_clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags)) return; - err = txgbe_set_phy_link(wx); - if (err) - return; - - clear_bit(WX_FLAG_NEED_LINK_CONFIG, wx->flags); + txgbe_set_phy_link(wx); } /** @@ -233,6 +225,7 @@ static void txgbe_disable_device(struct wx *wx) wx_napi_disable_all(wx); timer_delete_sync(&wx->service_timer); + cancel_work_sync(&wx->service_task); if (wx->bus.func < 2) wr32m(wx, TXGBE_MIS_PRB_CTL, TXGBE_MIS_PRB_CTL_LAN_UP(wx->bus.func), 0);