* [PATCH net-next v6 00/11] Wangxun improvement and new support
@ 2026-03-26 2:13 Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 01/11] net: ngbe: remove netdev->ethtool->wol_enabled setting Jiawen Wu
` (11 more replies)
0 siblings, 12 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:13 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Implement power management function for txgbe. Clean up the same code in
the two drivers, to make more use of lib functions.
Add Tx timeout process and pci_error_handlers ops, to recover the devices.
v6:
- Remove changes to the reset process in patch 04/11.
- Remove the inappropriate comments in patch 09/11, and detail the
commit message.
v5: https://lore.kernel.org/all/20260317073827.4300-1-jiawenwu@trustnetic.com
- Add patch 10/11 to handle Tx hang check with pause frame.
- Clear WX_STATE_DISABLED bit in wx_resume().
v4: https://lore.kernel.org/all/20260306063211.20536-1-jiawenwu@trustnetic.com
- Add an initial patch to remove netdev->ethtool->wol_enabled setting.
v3: https://lore.kernel.org/all/20260303030331.24076-1-jiawenwu@trustnetic.com
- Detail the commit log for improving ngbe reset flow.
- Correct the process of stopping the work queue.
v2: https://lore.kernel.org/all/20260227073450.23680-1-jiawenwu@trustnetic.com
- Split NCSI changes from the WOL patch.
- Properly return and handle error codes.
- Fix the typos.
- Remove the check for PCIe errors and subsequent error handling, because
aer_recover_queue() does not want to be called by the ethernet driver.
The discussion link:
https://lore.kernel.org/linux-acpi/001c01dc9740$c7722540$56566fc0$@trustnetic.com/T/
v1: https://lore.kernel.org/all/20260203075759.5852-1-jiawenwu@trustnetic.com
Jiawen Wu (11):
net: ngbe: remove netdev->ethtool->wol_enabled setting
net: ngbe: move the WOL functions to libwx
net: ngbe: remove redundant macros
net: ngbe: implement libwx reset ops
net: wangxun: move reusable PCI driver ops functions into libwx
net: txgbe: add power management support
net: wangxun: move ethtool_ops.set_channels into libwx
net: wangxun: delete service_timer before cancel service_work
net: wangxun: add Tx timeout process
net: wangxun: improve flow control setting
net: wangxun: implement pci_error_handlers ops
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_err.c | 228 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_err.h | 16 ++
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 39 ++-
.../net/ethernet/wangxun/libwx/wx_ethtool.h | 4 +
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 56 ++++-
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 129 +++++++++-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 4 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 32 ++-
.../net/ethernet/wangxun/libwx/wx_vf_common.c | 1 +
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 52 +---
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 192 +++++++--------
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 19 +-
.../ethernet/wangxun/txgbe/txgbe_ethtool.c | 17 +-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 78 +++---
.../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +-
16 files changed, 635 insertions(+), 237 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.h
--
2.48.1
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH net-next v6 01/11] net: ngbe: remove netdev->ethtool->wol_enabled setting
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
@ 2026-03-26 2:13 ` Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 02/11] net: ngbe: move the WOL functions to libwx Jiawen Wu
` (10 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:13 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
netdev->ethtool->wol_enabled is set in ethtool core code, so remove the
redundant setting in ngbe_set_wol().
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Breno Leitao <leitao@debian.org>
Reviewed-by: Joe Damato <joe@dama.to>
---
drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index fc04040957bf..40779fee0fdf 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -37,9 +37,8 @@ static int ngbe_set_wol(struct net_device *netdev,
wx->wol = 0;
if (wol->wolopts & WAKE_MAGIC)
wx->wol = WX_PSR_WKUP_CTL_MAG;
- netdev->ethtool->wol_enabled = !!(wx->wol);
wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
- device_set_wakeup_enable(&pdev->dev, netdev->ethtool->wol_enabled);
+ device_set_wakeup_enable(&pdev->dev, !!(wx->wol));
return 0;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 02/11] net: ngbe: move the WOL functions to libwx
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 01/11] net: ngbe: remove netdev->ethtool->wol_enabled setting Jiawen Wu
@ 2026-03-26 2:13 ` Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 03/11] net: ngbe: remove redundant macros Jiawen Wu
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:13 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Remove duplicate-defined register macros, move the WOL implementation to
the library module. So that the WOL functions can be reused in txgbe
later.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 33 +++++++++++++++++
.../net/ethernet/wangxun/libwx/wx_ethtool.h | 4 +++
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 35 ++-----------------
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 12 +++----
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 14 --------
5 files changed, 45 insertions(+), 53 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index f362e51c73ee..2de1170db8c7 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -262,6 +262,39 @@ int wx_set_link_ksettings(struct net_device *netdev,
}
EXPORT_SYMBOL(wx_set_link_ksettings);
+void wx_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct wx *wx = netdev_priv(netdev);
+
+ if (!wx->wol_hw_supported)
+ return;
+ wol->supported = WAKE_MAGIC;
+ wol->wolopts = 0;
+ if (wx->wol & WX_PSR_WKUP_CTL_MAG)
+ wol->wolopts |= WAKE_MAGIC;
+}
+EXPORT_SYMBOL(wx_get_wol);
+
+int wx_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol)
+{
+ struct wx *wx = netdev_priv(netdev);
+ struct pci_dev *pdev = wx->pdev;
+
+ if (!wx->wol_hw_supported)
+ return -EOPNOTSUPP;
+
+ wx->wol = 0;
+ if (wol->wolopts & WAKE_MAGIC)
+ wx->wol = WX_PSR_WKUP_CTL_MAG;
+ wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
+ device_set_wakeup_enable(&pdev->dev, !!(wx->wol));
+
+ return 0;
+}
+EXPORT_SYMBOL(wx_set_wol);
+
void wx_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause)
{
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
index 727093970462..5b187d1587b1 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.h
@@ -18,6 +18,10 @@ int wx_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd);
int wx_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd);
+void wx_get_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol);
+int wx_set_wol(struct net_device *netdev,
+ struct ethtool_wolinfo *wol);
void wx_get_pauseparam(struct net_device *netdev,
struct ethtool_pauseparam *pause);
int wx_set_pauseparam(struct net_device *netdev,
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index 40779fee0fdf..2b6356622a13 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -12,37 +12,6 @@
#include "ngbe_ethtool.h"
#include "ngbe_type.h"
-static void ngbe_get_wol(struct net_device *netdev,
- struct ethtool_wolinfo *wol)
-{
- struct wx *wx = netdev_priv(netdev);
-
- if (!wx->wol_hw_supported)
- return;
- wol->supported = WAKE_MAGIC;
- wol->wolopts = 0;
- if (wx->wol & WX_PSR_WKUP_CTL_MAG)
- wol->wolopts |= WAKE_MAGIC;
-}
-
-static int ngbe_set_wol(struct net_device *netdev,
- struct ethtool_wolinfo *wol)
-{
- struct wx *wx = netdev_priv(netdev);
- struct pci_dev *pdev = wx->pdev;
-
- if (!wx->wol_hw_supported)
- return -EOPNOTSUPP;
-
- wx->wol = 0;
- if (wol->wolopts & WAKE_MAGIC)
- wx->wol = WX_PSR_WKUP_CTL_MAG;
- wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
- device_set_wakeup_enable(&pdev->dev, !!(wx->wol));
-
- return 0;
-}
-
static int ngbe_set_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring,
struct kernel_ethtool_ringparam *kernel_ring,
@@ -121,8 +90,8 @@ static const struct ethtool_ops ngbe_ethtool_ops = {
.get_link_ksettings = wx_get_link_ksettings,
.set_link_ksettings = wx_set_link_ksettings,
.nway_reset = wx_nway_reset,
- .get_wol = ngbe_get_wol,
- .set_wol = ngbe_set_wol,
+ .get_wol = wx_get_wol,
+ .set_wol = wx_set_wol,
.get_sset_count = wx_get_sset_count,
.get_strings = wx_get_strings,
.get_ethtool_stats = wx_get_ethtool_stats,
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 58488e138beb..e28ddf684a06 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -58,14 +58,14 @@ static void ngbe_init_type_code(struct wx *wx)
wx->mac.type = wx_mac_em;
type_mask = (u16)(wx->subsystem_device_id & NGBE_OEM_MASK);
ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
- wol_mask = wx->subsystem_device_id & NGBE_WOL_MASK;
+ wol_mask = wx->subsystem_device_id & WX_WOL_MASK;
val = rd32(wx, WX_CFG_PORT_ST);
wx->mac_type = (val & BIT(7)) >> 7 ?
em_mac_type_rgmii :
em_mac_type_mdi;
- wx->wol_hw_supported = (wol_mask == NGBE_WOL_SUP) ? 1 : 0;
+ wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0;
wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK ||
type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0;
@@ -520,9 +520,9 @@ static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
if (wufc) {
wx_set_rx_mode(netdev);
wx_configure_rx(wx);
- wr32(wx, NGBE_PSR_WKUP_CTL, wufc);
+ wr32(wx, WX_PSR_WKUP_CTL, wufc);
} else {
- wr32(wx, NGBE_PSR_WKUP_CTL, 0);
+ wr32(wx, WX_PSR_WKUP_CTL, 0);
}
pci_wake_from_d3(pdev, !!wufc);
*enable_wake = !!wufc;
@@ -742,10 +742,10 @@ static int ngbe_probe(struct pci_dev *pdev,
wx->wol = 0;
if (wx->wol_hw_supported)
- wx->wol = NGBE_PSR_WKUP_CTL_MAG;
+ wx->wol = WX_PSR_WKUP_CTL_MAG;
netdev->ethtool->wol_enabled = !!(wx->wol);
- wr32(wx, NGBE_PSR_WKUP_CTL, wx->wol);
+ wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
device_set_wakeup_enable(&pdev->dev, wx->wol);
/* Save off EEPROM version number and Option Rom version which
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 3b2ca7f47e33..f1957fa0add4 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -39,8 +39,6 @@
#define NGBE_NCSI_SUP 0x8000
#define NGBE_NCSI_MASK 0x8000
-#define NGBE_WOL_SUP 0x4000
-#define NGBE_WOL_MASK 0x4000
/**************** EM Registers ****************************/
/* chip control Registers */
@@ -93,18 +91,6 @@
#define NGBE_CFG_LAN_SPEED 0x14440
#define NGBE_CFG_PORT_ST 0x14404
-/* Wake up registers */
-#define NGBE_PSR_WKUP_CTL 0x15B80
-/* Wake Up Filter Control Bit */
-#define NGBE_PSR_WKUP_CTL_LNKC BIT(0) /* Link Status Change Wakeup Enable*/
-#define NGBE_PSR_WKUP_CTL_MAG BIT(1) /* Magic Packet Wakeup Enable */
-#define NGBE_PSR_WKUP_CTL_EX BIT(2) /* Directed Exact Wakeup Enable */
-#define NGBE_PSR_WKUP_CTL_MC BIT(3) /* Directed Multicast Wakeup Enable*/
-#define NGBE_PSR_WKUP_CTL_BC BIT(4) /* Broadcast Wakeup Enable */
-#define NGBE_PSR_WKUP_CTL_ARP BIT(5) /* ARP Request Packet Wakeup Enable*/
-#define NGBE_PSR_WKUP_CTL_IPV4 BIT(6) /* Directed IPv4 Pkt Wakeup Enable */
-#define NGBE_PSR_WKUP_CTL_IPV6 BIT(7) /* Directed IPv6 Pkt Wakeup Enable */
-
#define NGBE_FW_EEPROM_CHECKSUM_CMD 0xE9
#define NGBE_FW_NVM_DATA_OFFSET 3
#define NGBE_FW_CMD_DEFAULT_CHECKSUM 0xFF /* checksum always 0xFF */
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 03/11] net: ngbe: remove redundant macros
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 01/11] net: ngbe: remove netdev->ethtool->wol_enabled setting Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 02/11] net: ngbe: move the WOL functions to libwx Jiawen Wu
@ 2026-03-26 2:13 ` Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops Jiawen Wu
` (8 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:13 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
NGBE_NCSI_SUP and NGBE_NCSI_MASK are duplicate-defined, they can be
replaced by the macros defined in libwx. Just remove them.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Joe Damato <joe@dama.to>
---
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 4 ++--
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 3 ---
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index e28ddf684a06..8c9d505721b1 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -57,7 +57,7 @@ static void ngbe_init_type_code(struct wx *wx)
wx->mac.type = wx_mac_em;
type_mask = (u16)(wx->subsystem_device_id & NGBE_OEM_MASK);
- ncsi_mask = wx->subsystem_device_id & NGBE_NCSI_MASK;
+ ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK;
wol_mask = wx->subsystem_device_id & WX_WOL_MASK;
val = rd32(wx, WX_CFG_PORT_ST);
@@ -66,7 +66,7 @@ static void ngbe_init_type_code(struct wx *wx)
em_mac_type_mdi;
wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0;
- wx->ncsi_enabled = (ncsi_mask == NGBE_NCSI_MASK ||
+ wx->ncsi_enabled = (ncsi_mask == WX_NCSI_SUP ||
type_mask == NGBE_SUBID_OCP_CARD) ? 1 : 0;
switch (type_mask) {
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index f1957fa0add4..7077a0da4c98 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -37,9 +37,6 @@
#define NGBE_OEM_MASK 0x00FF
-#define NGBE_NCSI_SUP 0x8000
-#define NGBE_NCSI_MASK 0x8000
-
/**************** EM Registers ****************************/
/* chip control Registers */
#define NGBE_MIS_PRB_CTL 0x10010
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (2 preceding siblings ...)
2026-03-26 2:13 ` [PATCH net-next v6 03/11] net: ngbe: remove redundant macros Jiawen Wu
@ 2026-03-26 2:13 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
` (7 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:13 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Implement wx->do_reset() for library module calling. Since LAN reset is
not necessary for NGBE devices, only ngbe_down() and ngbe_up() calls are
considered while the device is running.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 1 -
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 37 ++++++++++++++++++-
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 1 +
3 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index 2b6356622a13..b86980c2418f 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -60,7 +60,6 @@ static int ngbe_set_ringparam(struct net_device *netdev,
wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);
kvfree(temp_ring);
- wx_configure(wx);
ngbe_up(wx);
clear_reset:
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 8c9d505721b1..50a13dcf4b3c 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -133,6 +133,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;
set_bit(0, &wx->fwd_bitmask);
return 0;
@@ -398,7 +399,7 @@ void ngbe_down(struct wx *wx)
wx_clean_all_rx_rings(wx);
}
-void ngbe_up(struct wx *wx)
+static void ngbe_up_complete(struct wx *wx)
{
wx_configure_vectors(wx);
@@ -463,7 +464,7 @@ static int ngbe_open(struct net_device *netdev)
wx_ptp_init(wx);
- ngbe_up(wx);
+ ngbe_up_complete(wx);
return 0;
err_dis_phy:
@@ -502,6 +503,12 @@ static int ngbe_close(struct net_device *netdev)
return 0;
}
+void ngbe_up(struct wx *wx)
+{
+ wx_configure(wx);
+ ngbe_up_complete(wx);
+}
+
static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
{
struct wx *wx = pci_get_drvdata(pdev);
@@ -579,6 +586,32 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
return 0;
}
+static void ngbe_reinit_locked(struct wx *wx)
+{
+ int err = 0;
+
+ netif_trans_update(wx->netdev);
+
+ err = wx_set_state_reset(wx);
+ if (err) {
+ wx_err(wx, "wait device reset timeout\n");
+ return;
+ }
+
+ ngbe_down(wx);
+ ngbe_up(wx);
+
+ clear_bit(WX_STATE_RESETTING, wx->state);
+}
+
+void ngbe_do_reset(struct net_device *netdev)
+{
+ struct wx *wx = netdev_priv(netdev);
+
+ if (netif_running(netdev))
+ ngbe_reinit_locked(wx);
+}
+
static const struct net_device_ops ngbe_netdev_ops = {
.ndo_open = ngbe_open,
.ndo_stop = ngbe_close,
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 7077a0da4c98..4f648f272c08 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -125,5 +125,6 @@ extern char ngbe_driver_name[];
void ngbe_down(struct wx *wx);
void ngbe_up(struct wx *wx);
int ngbe_setup_tc(struct net_device *dev, u8 tc);
+void ngbe_do_reset(struct net_device *netdev);
#endif /* _NGBE_TYPE_H_ */
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (3 preceding siblings ...)
2026-03-26 2:13 ` [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 06/11] net: txgbe: add power management support Jiawen Wu
` (6 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Add function pointer wx->close_suspend() and adjust wx->do_reset(), so
that PCI driver ops such as .shutdown can be called in libwx.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 89 +++++++++++++-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 3 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 3 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 113 ++++--------------
drivers/net/ethernet/wangxun/ngbe/ngbe_type.h | 3 +-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 17 +--
.../net/ethernet/wangxun/txgbe/txgbe_type.h | 3 +-
8 files changed, 126 insertions(+), 107 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 2de1170db8c7..2fda1f6130c0 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -395,7 +395,7 @@ static void wx_update_rsc(struct wx *wx)
/* reset the device to apply the new RSC setting */
if (need_reset && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
}
int wx_set_coalesce(struct net_device *netdev,
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index 746623fa59b4..c65240ca3c28 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3109,7 +3109,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
netdev->features = features;
if (changed & NETIF_F_HW_VLAN_CTAG_RX && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
else if (changed & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER))
wx_set_rx_mode(netdev);
@@ -3159,7 +3159,7 @@ int wx_set_features(struct net_device *netdev, netdev_features_t features)
out:
if (need_reset && wx->do_reset)
- wx->do_reset(netdev);
+ wx->do_reset(netdev, true);
return 0;
}
@@ -3342,5 +3342,90 @@ void wx_service_timer(struct timer_list *t)
}
EXPORT_SYMBOL(wx_service_timer);
+static void wx_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
+{
+ struct wx *wx = pci_get_drvdata(pdev);
+ struct net_device *netdev;
+ u32 wufc = wx->wol;
+
+ netdev = wx->netdev;
+ rtnl_lock();
+ netif_device_detach(netdev);
+
+ if (netif_running(netdev))
+ wx->close_suspend(wx);
+
+ wx_clear_interrupt_scheme(wx);
+ rtnl_unlock();
+
+ if (wufc) {
+ wx_set_rx_mode(netdev);
+ wx_configure_rx(wx);
+ wr32(wx, WX_PSR_WKUP_CTL, wufc);
+ } else {
+ wr32(wx, WX_PSR_WKUP_CTL, 0);
+ }
+ pci_wake_from_d3(pdev, !!wufc);
+ *enable_wake = !!wufc;
+ wx_control_hw(wx, false);
+
+ pci_disable_device(pdev);
+}
+
+int wx_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ bool wake;
+
+ wx_dev_shutdown(pdev, &wake);
+ device_set_wakeup_enable(&pdev->dev, wake);
+
+ return 0;
+}
+EXPORT_SYMBOL(wx_suspend);
+
+int wx_resume(struct pci_dev *pdev)
+{
+ struct net_device *netdev;
+ struct wx *wx;
+ u32 err;
+
+ wx = pci_get_drvdata(pdev);
+ netdev = wx->netdev;
+
+ err = pci_enable_device_mem(pdev);
+ if (err) {
+ wx_err(wx, "Cannot enable PCI device from suspend\n");
+ return err;
+ }
+ pci_set_master(pdev);
+ device_wakeup_disable(&pdev->dev);
+
+ wx->do_reset(netdev, false);
+ rtnl_lock();
+
+ err = wx_init_interrupt_scheme(wx);
+ if (!err && netif_running(netdev))
+ err = netdev->netdev_ops->ndo_open(netdev);
+ if (!err)
+ netif_device_attach(netdev);
+ rtnl_unlock();
+
+ return err;
+}
+EXPORT_SYMBOL(wx_resume);
+
+void wx_shutdown(struct pci_dev *pdev)
+{
+ bool wake;
+
+ wx_dev_shutdown(pdev, &wake);
+
+ if (system_state == SYSTEM_POWER_OFF) {
+ pci_wake_from_d3(pdev, wake);
+ pci_set_power_state(pdev, PCI_D3hot);
+ }
+}
+EXPORT_SYMBOL(wx_shutdown);
+
MODULE_DESCRIPTION("Common library for Wangxun(R) Ethernet drivers.");
MODULE_LICENSE("GPL");
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index aed6ea8cf0d6..42461723a400 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -41,5 +41,8 @@ void 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);
+int wx_suspend(struct pci_dev *pdev, pm_message_t state);
+int wx_resume(struct pci_dev *pdev);
+void wx_shutdown(struct pci_dev *pdev);
#endif /* _WX_LIB_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 29e5c5470c94..e013f05d2cfe 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1372,7 +1372,8 @@ struct wx {
void (*atr)(struct wx_ring *ring, struct wx_tx_buffer *first, u8 ptype);
void (*configure_fdir)(struct wx *wx);
int (*setup_tc)(struct net_device *netdev, u8 tc);
- void (*do_reset)(struct net_device *netdev);
+ void (*do_reset)(struct net_device *netdev, bool reinit);
+ void (*close_suspend)(struct wx *wx);
int (*ptp_setup_sdp)(struct wx *wx);
void (*set_num_queues)(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 50a13dcf4b3c..baffcadb2269 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -134,6 +134,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->close_suspend = ngbe_close_suspend;
set_bit(0, &wx->fwd_bitmask);
return 0;
@@ -477,6 +478,16 @@ static int ngbe_open(struct net_device *netdev)
return err;
}
+void ngbe_close_suspend(struct wx *wx)
+{
+ wx_ptp_suspend(wx);
+ ngbe_down(wx);
+ wx_free_irq(wx);
+ wx_free_isb_resources(wx);
+ wx_free_resources(wx);
+ phylink_disconnect_phy(wx->phylink);
+}
+
/**
* ngbe_close - Disables a network interface
* @netdev: network interface device structure
@@ -493,11 +504,10 @@ static int ngbe_close(struct net_device *netdev)
struct wx *wx = netdev_priv(netdev);
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);
+
+ if (netif_device_present(netdev))
+ ngbe_close_suspend(wx);
+
wx_control_hw(wx, false);
return 0;
@@ -509,50 +519,6 @@ void ngbe_up(struct wx *wx)
ngbe_up_complete(wx);
}
-static void ngbe_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
-{
- struct wx *wx = pci_get_drvdata(pdev);
- struct net_device *netdev;
- u32 wufc = wx->wol;
-
- netdev = wx->netdev;
- rtnl_lock();
- netif_device_detach(netdev);
-
- if (netif_running(netdev))
- ngbe_close(netdev);
- wx_clear_interrupt_scheme(wx);
- rtnl_unlock();
-
- if (wufc) {
- wx_set_rx_mode(netdev);
- wx_configure_rx(wx);
- wr32(wx, WX_PSR_WKUP_CTL, wufc);
- } else {
- wr32(wx, WX_PSR_WKUP_CTL, 0);
- }
- pci_wake_from_d3(pdev, !!wufc);
- *enable_wake = !!wufc;
- wx_control_hw(wx, false);
-
- pci_disable_device(pdev);
-}
-
-static void ngbe_shutdown(struct pci_dev *pdev)
-{
- struct wx *wx = pci_get_drvdata(pdev);
- bool wake;
-
- wake = !!wx->wol;
-
- ngbe_dev_shutdown(pdev, &wake);
-
- if (system_state == SYSTEM_POWER_OFF) {
- pci_wake_from_d3(pdev, wake);
- pci_set_power_state(pdev, PCI_D3hot);
- }
-}
-
/**
* ngbe_setup_tc - routine to configure net_device for multiple traffic
* classes.
@@ -604,11 +570,11 @@ static void ngbe_reinit_locked(struct wx *wx)
clear_bit(WX_STATE_RESETTING, wx->state);
}
-void ngbe_do_reset(struct net_device *netdev)
+void ngbe_do_reset(struct net_device *netdev, bool reinit)
{
struct wx *wx = netdev_priv(netdev);
- if (netif_running(netdev))
+ if (netif_running(netdev) && reinit)
ngbe_reinit_locked(wx);
}
@@ -864,53 +830,14 @@ static void ngbe_remove(struct pci_dev *pdev)
pci_disable_device(pdev);
}
-static int ngbe_suspend(struct pci_dev *pdev, pm_message_t state)
-{
- bool wake;
-
- ngbe_dev_shutdown(pdev, &wake);
- device_set_wakeup_enable(&pdev->dev, wake);
-
- return 0;
-}
-
-static int ngbe_resume(struct pci_dev *pdev)
-{
- struct net_device *netdev;
- struct wx *wx;
- u32 err;
-
- wx = pci_get_drvdata(pdev);
- netdev = wx->netdev;
-
- err = pci_enable_device_mem(pdev);
- if (err) {
- wx_err(wx, "Cannot enable PCI device from suspend\n");
- return err;
- }
- pci_set_master(pdev);
- device_wakeup_disable(&pdev->dev);
-
- ngbe_reset_hw(wx);
- rtnl_lock();
- err = wx_init_interrupt_scheme(wx);
- if (!err && netif_running(netdev))
- err = ngbe_open(netdev);
- if (!err)
- netif_device_attach(netdev);
- rtnl_unlock();
-
- return 0;
-}
-
static struct pci_driver ngbe_driver = {
.name = ngbe_driver_name,
.id_table = ngbe_pci_tbl,
.probe = ngbe_probe,
.remove = ngbe_remove,
- .suspend = ngbe_suspend,
- .resume = ngbe_resume,
- .shutdown = ngbe_shutdown,
+ .suspend = wx_suspend,
+ .resume = wx_resume,
+ .shutdown = wx_shutdown,
.sriov_configure = wx_pci_sriov_configure,
};
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
index 4f648f272c08..eb5c92edae06 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_type.h
@@ -125,6 +125,7 @@ extern char ngbe_driver_name[];
void ngbe_down(struct wx *wx);
void ngbe_up(struct wx *wx);
int ngbe_setup_tc(struct net_device *dev, u8 tc);
-void ngbe_do_reset(struct net_device *netdev);
+void ngbe_do_reset(struct net_device *netdev, bool reinit);
+void ngbe_close_suspend(struct wx *wx);
#endif /* _NGBE_TYPE_H_ */
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 0de051450a82..30f66507809b 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -412,6 +412,7 @@ static int txgbe_sw_init(struct wx *wx)
wx->setup_tc = txgbe_setup_tc;
wx->do_reset = txgbe_do_reset;
+ wx->close_suspend = txgbe_close_suspend;
set_bit(0, &wx->fwd_bitmask);
switch (wx->mac.type) {
@@ -500,10 +501,12 @@ static int txgbe_open(struct net_device *netdev)
* This function should contain the necessary work common to both suspending
* and closing of the device.
*/
-static void txgbe_close_suspend(struct wx *wx)
+void txgbe_close_suspend(struct wx *wx)
{
wx_ptp_suspend(wx);
- txgbe_disable_device(wx);
+ txgbe_down(wx);
+ wx_free_irq(wx);
+ txgbe_free_misc_irq(wx->priv);
wx_free_resources(wx);
}
@@ -523,10 +526,8 @@ static int txgbe_close(struct net_device *netdev)
struct wx *wx = netdev_priv(netdev);
wx_ptp_stop(wx);
- txgbe_down(wx);
- wx_free_irq(wx);
- txgbe_free_misc_irq(wx->priv);
- wx_free_resources(wx);
+ if (netif_device_present(netdev))
+ txgbe_close_suspend(wx);
txgbe_fdir_filter_exit(wx);
wx_control_hw(wx, false);
@@ -614,11 +615,11 @@ static void txgbe_reinit_locked(struct wx *wx)
clear_bit(WX_STATE_RESETTING, wx->state);
}
-void txgbe_do_reset(struct net_device *netdev)
+void txgbe_do_reset(struct net_device *netdev, bool reinit)
{
struct wx *wx = netdev_priv(netdev);
- if (netif_running(netdev))
+ if (netif_running(netdev) && reinit)
txgbe_reinit_locked(wx);
else
txgbe_reset(wx);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
index 82433e9cb0e3..e9360e935682 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_type.h
@@ -313,7 +313,8 @@ extern char txgbe_driver_name[];
void txgbe_down(struct wx *wx);
void txgbe_up(struct wx *wx);
int txgbe_setup_tc(struct net_device *dev, u8 tc);
-void txgbe_do_reset(struct net_device *netdev);
+void txgbe_do_reset(struct net_device *netdev, bool reinit);
+void txgbe_close_suspend(struct wx *wx);
#define TXGBE_LINK_SPEED_UNKNOWN 0
#define TXGBE_LINK_SPEED_10GB_FULL 4
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 06/11] net: txgbe: add power management support
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (4 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 07/11] net: wangxun: move ethtool_ops.set_channels into libwx Jiawen Wu
` (5 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Support .suspend and .resume and Wake-on-LAN.
Implement ethtool ops .get_wol and .set_wol to support Wake-on-LAN
function. WOL requires hardware board support which is identified by
subsystem device ID. Magic packets are checked by firmware, and
currently only WAKE_MAGIC is supported.
And WOL related operations are added to txgbe_shutdown(), it matches the
implementation of wx_shutdown(). So change to call wx_shutdown()
directly in txgbe driver.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
Reviewed-by: Joe Damato <joe@dama.to>
---
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 3 +-
.../ethernet/wangxun/txgbe/txgbe_ethtool.c | 2 +
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 49 +++++++------------
3 files changed, 21 insertions(+), 33 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index bee9e245e792..150a063daafd 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -1313,8 +1313,7 @@ void wx_disable_rx(struct wx *wx)
rxctrl &= ~WX_RDB_PB_CTL_RXEN;
wr32(wx, WX_RDB_PB_CTL, rxctrl);
- if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
- ((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) {
+ if (!(wx->ncsi_enabled || wx->wol_hw_supported)) {
/* disable mac receiver */
wr32m(wx, WX_MAC_RX_CFG,
WX_MAC_RX_CFG_RE, 0);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
index 9157b8275be1..c92eb71e0c15 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
@@ -575,6 +575,8 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
.get_link = ethtool_op_get_link,
.get_link_ksettings = txgbe_get_link_ksettings,
.set_link_ksettings = wx_set_link_ksettings,
+ .get_wol = wx_get_wol,
+ .set_wol = wx_set_wol,
.get_sset_count = wx_get_sset_count,
.get_strings = wx_get_strings,
.get_ethtool_stats = wx_get_ethtool_stats,
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 30f66507809b..ddc7cea9d7c2 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -245,8 +245,7 @@ static void txgbe_disable_device(struct wx *wx)
wx_set_all_vfs(wx);
}
- if (!(((wx->subsystem_device_id & WX_NCSI_MASK) == WX_NCSI_SUP) ||
- ((wx->subsystem_device_id & WX_WOL_MASK) == WX_WOL_SUP))) {
+ if (!(wx->ncsi_enabled || wx->wol_hw_supported)) {
/* disable mac transmiter */
wr32m(wx, WX_MAC_TX_CFG, WX_MAC_TX_CFG_TE, 0);
}
@@ -303,6 +302,7 @@ void txgbe_up(struct wx *wx)
static void txgbe_init_type_code(struct wx *wx)
{
u8 device_type = wx->subsystem_device_id & 0xF0;
+ u16 wol_mask, ncsi_mask;
switch (wx->device_id) {
case TXGBE_DEV_ID_SP1000:
@@ -347,6 +347,11 @@ static void txgbe_init_type_code(struct wx *wx)
wx->media_type = wx_media_unknown;
break;
}
+
+ wol_mask = wx->subsystem_device_id & WX_WOL_MASK;
+ ncsi_mask = wx->subsystem_device_id & WX_NCSI_MASK;
+ wx->wol_hw_supported = (wol_mask == WX_WOL_SUP) ? 1 : 0;
+ wx->ncsi_enabled = (ncsi_mask == WX_NCSI_SUP) ? 1 : 0;
}
/**
@@ -534,34 +539,6 @@ static int txgbe_close(struct net_device *netdev)
return 0;
}
-static void txgbe_dev_shutdown(struct pci_dev *pdev)
-{
- struct wx *wx = pci_get_drvdata(pdev);
- struct net_device *netdev;
-
- netdev = wx->netdev;
- netif_device_detach(netdev);
-
- rtnl_lock();
- if (netif_running(netdev))
- txgbe_close_suspend(wx);
- rtnl_unlock();
-
- wx_control_hw(wx, false);
-
- pci_disable_device(pdev);
-}
-
-static void txgbe_shutdown(struct pci_dev *pdev)
-{
- txgbe_dev_shutdown(pdev);
-
- if (system_state == SYSTEM_POWER_OFF) {
- pci_wake_from_d3(pdev, false);
- pci_set_power_state(pdev, PCI_D3hot);
- }
-}
-
/**
* txgbe_setup_tc - routine to configure net_device for multiple traffic
* classes.
@@ -824,6 +801,14 @@ static int txgbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ wx->wol = 0;
+ if (wx->wol_hw_supported)
+ wx->wol = WX_PSR_WKUP_CTL_MAG;
+
+ netdev->ethtool->wol_enabled = !!(wx->wol);
+ wr32(wx, WX_PSR_WKUP_CTL, wx->wol);
+ device_set_wakeup_enable(&pdev->dev, wx->wol);
+
txgbe_init_service(wx);
err = wx_init_interrupt_scheme(wx);
@@ -975,7 +960,9 @@ static struct pci_driver txgbe_driver = {
.id_table = txgbe_pci_tbl,
.probe = txgbe_probe,
.remove = txgbe_remove,
- .shutdown = txgbe_shutdown,
+ .suspend = wx_suspend,
+ .resume = wx_resume,
+ .shutdown = wx_shutdown,
.sriov_configure = wx_pci_sriov_configure,
};
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 07/11] net: wangxun: move ethtool_ops.set_channels into libwx
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (5 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 06/11] net: txgbe: add power management support Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-26 2:14 ` [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work Jiawen Wu
` (4 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Since function ops wx->setup_tc() is set in txgbe and ngbe,
ethtool_ops.set_channels can be implemented in libwx to reduce
duplicated code.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c | 15 +--------------
.../net/ethernet/wangxun/txgbe/txgbe_ethtool.c | 15 +--------------
3 files changed, 3 insertions(+), 29 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 2fda1f6130c0..9ab7dce956e3 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -555,7 +555,7 @@ int wx_set_channels(struct net_device *dev,
wx->ring_feature[RING_F_RSS].limit = count;
- return 0;
+ return wx->setup_tc(dev, netdev_get_num_tc(dev));
}
EXPORT_SYMBOL(wx_set_channels);
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
index b86980c2418f..c37d108a85a1 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_ethtool.c
@@ -67,19 +67,6 @@ static int ngbe_set_ringparam(struct net_device *netdev,
return err;
}
-static int ngbe_set_channels(struct net_device *dev,
- struct ethtool_channels *ch)
-{
- int err;
-
- err = wx_set_channels(dev, ch);
- if (err < 0)
- return err;
-
- /* use setup TC to update any traffic class queue mapping */
- return ngbe_setup_tc(dev, netdev_get_num_tc(dev));
-}
-
static const struct ethtool_ops ngbe_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
@@ -103,7 +90,7 @@ static const struct ethtool_ops ngbe_ethtool_ops = {
.get_coalesce = wx_get_coalesce,
.set_coalesce = wx_set_coalesce,
.get_channels = wx_get_channels,
- .set_channels = ngbe_set_channels,
+ .set_channels = wx_set_channels,
.get_rxfh_fields = wx_get_rxfh_fields,
.set_rxfh_fields = wx_set_rxfh_fields,
.get_rxfh_indir_size = wx_rss_indir_size,
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
index c92eb71e0c15..fc5d98f38f66 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_ethtool.c
@@ -91,19 +91,6 @@ static int txgbe_set_ringparam(struct net_device *netdev,
return err;
}
-static int txgbe_set_channels(struct net_device *dev,
- struct ethtool_channels *ch)
-{
- int err;
-
- err = wx_set_channels(dev, ch);
- if (err < 0)
- return err;
-
- /* use setup TC to update any traffic class queue mapping */
- return txgbe_setup_tc(dev, netdev_get_num_tc(dev));
-}
-
static int txgbe_get_ethtool_fdir_entry(struct txgbe *txgbe,
struct ethtool_rxnfc *cmd)
{
@@ -589,7 +576,7 @@ static const struct ethtool_ops txgbe_ethtool_ops = {
.get_coalesce = wx_get_coalesce,
.set_coalesce = wx_set_coalesce,
.get_channels = wx_get_channels,
- .set_channels = txgbe_set_channels,
+ .set_channels = wx_set_channels,
.get_rxnfc = txgbe_get_rxnfc,
.set_rxnfc = txgbe_set_rxnfc,
.get_rx_ring_count = txgbe_get_rx_ring_count,
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (6 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 07/11] net: wangxun: move ethtool_ops.set_channels into libwx Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process Jiawen Wu
` (3 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
When removing the device, timer_delete_sync(&wx->service_timer) is
called in .ndo_stop() after cancel_work_sync(&wx->service_task). This
may cause new work to be queued after device down. So we should delete
service_timer first.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_vf_common.c | 1 +
drivers/net/ethernet/wangxun/txgbe/txgbe_main.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
index ade2bfe563aa..5565062c7471 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
@@ -48,6 +48,7 @@ void wxvf_remove(struct pci_dev *pdev)
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
+ timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
netdev = wx->netdev;
unregister_netdev(netdev);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index ddc7cea9d7c2..01ceaf270b70 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -936,6 +936,7 @@ static void txgbe_remove(struct pci_dev *pdev)
struct txgbe *txgbe = wx->priv;
struct net_device *netdev;
+ timer_delete_sync(&wx->service_timer);
cancel_work_sync(&wx->service_task);
netdev = wx->netdev;
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (7 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 10/11] net: wangxun: improve flow control setting Jiawen Wu
` (2 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Implement .ndo_tx_timeout to handle Tx side timeout event. When Tx
timeout event occur, it will triger driver into reset process.
The WX_HANG_CHECK_ARMED bit is set to indicate a potential hang. It will
be cleared if a pause frame is received to remove false hang detection
due to 802.3 frames, which is implemented in later commit.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/Makefile | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_err.c | 126 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_err.h | 14 ++
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 1 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 37 +++++
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 1 +
drivers/net/ethernet/wangxun/libwx/wx_type.h | 12 +-
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 34 ++++-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 3 +
9 files changed, 227 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.c
create mode 100644 drivers/net/ethernet/wangxun/libwx/wx_err.h
diff --git a/drivers/net/ethernet/wangxun/libwx/Makefile b/drivers/net/ethernet/wangxun/libwx/Makefile
index a71b0ad77de3..c8724bb129aa 100644
--- a/drivers/net/ethernet/wangxun/libwx/Makefile
+++ b/drivers/net/ethernet/wangxun/libwx/Makefile
@@ -4,5 +4,5 @@
obj-$(CONFIG_LIBWX) += libwx.o
-libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o
+libwx-objs := wx_hw.o wx_lib.o wx_ethtool.o wx_ptp.o wx_mbx.o wx_sriov.o wx_err.o
libwx-objs += wx_vf.o wx_vf_lib.o wx_vf_common.o
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
new file mode 100644
index 000000000000..bb77bdce69d2
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd. */
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+
+#include "wx_type.h"
+#include "wx_lib.h"
+#include "wx_err.h"
+
+static void wx_reset_subtask(struct wx *wx)
+{
+ if (!test_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
+ return;
+
+ if (!netif_running(wx->netdev) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
+ rtnl_lock();
+
+ wx_warn(wx, "Reset adapter.\n");
+
+ if (test_bit(WX_FLAG_NEED_PF_RESET, wx->flags)) {
+ if (wx->do_reset)
+ wx->do_reset(wx->netdev, true);
+ clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
+ }
+
+ rtnl_unlock();
+}
+
+/*
+ * wx_check_tx_hang_subtask - check for hung queues and dropped interrupts
+ * @wx - pointer to the device wx structure
+ *
+ * This function serves two purposes. First it strobes the interrupt lines
+ * in order to make certain interrupts are occurring. Secondly it sets the
+ * bits needed to check for TX hangs. As a result we should immediately
+ * determine if a hang has occurred.
+ */
+static void wx_check_tx_hang_subtask(struct wx *wx)
+{
+ int i;
+
+ /* If we're down or resetting, just bail */
+ if (!netif_running(wx->netdev) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
+ /* Force detection of hung controller */
+ if (netif_carrier_ok(wx->netdev)) {
+ for (i = 0; i < wx->num_tx_queues; i++)
+ set_bit(WX_TX_DETECT_HANG, wx->tx_ring[i]->state);
+ }
+}
+
+void wx_handle_errors_subtask(struct wx *wx)
+{
+ wx_reset_subtask(wx);
+ wx_check_tx_hang_subtask(wx);
+}
+EXPORT_SYMBOL(wx_handle_errors_subtask);
+
+static void wx_tx_timeout_reset(struct wx *wx)
+{
+ if (!netif_running(wx->netdev))
+ return;
+
+ set_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
+ wx_warn(wx, "initiating reset due to tx timeout\n");
+ wx_service_event_schedule(wx);
+}
+
+void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue)
+{
+ struct wx *wx = netdev_priv(netdev);
+ u32 head, tail;
+ int i;
+
+ for (i = 0; i < wx->num_tx_queues; i++) {
+ struct wx_ring *tx_ring = wx->tx_ring[i];
+
+ if (test_bit(WX_TX_DETECT_HANG, tx_ring->state) &&
+ wx_check_tx_hang(tx_ring))
+ wx_warn(wx, "Real tx hang detected on queue %d\n", i);
+
+ head = rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx));
+ tail = rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx));
+ wx_warn(wx,
+ "tx ring %d next_to_use is %d, next_to_clean is %d\n",
+ i, tx_ring->next_to_use,
+ tx_ring->next_to_clean);
+ wx_warn(wx, "tx ring %d hw rp is 0x%x, wp is 0x%x\n",
+ i, head, tail);
+ }
+
+ wx_tx_timeout_reset(wx);
+}
+EXPORT_SYMBOL(wx_tx_timeout);
+
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next)
+{
+ struct wx *wx = netdev_priv(tx_ring->netdev);
+
+ wx_warn(wx, "Detected Tx Unit Hang\n"
+ " Tx Queue <%d>\n"
+ " TDH, TDT <%x>, <%x>\n"
+ " next_to_use <%x>\n"
+ " next_to_clean <%x>\n"
+ "tx_buffer_info[next_to_clean]\n"
+ " time_stamp <%lx>\n"
+ " jiffies <%lx>\n",
+ tx_ring->queue_index,
+ rd32(wx, WX_PX_TR_RP(tx_ring->reg_idx)),
+ rd32(wx, WX_PX_TR_WP(tx_ring->reg_idx)),
+ tx_ring->next_to_use, next,
+ tx_ring->tx_buffer_info[next].time_stamp, jiffies);
+
+ netif_stop_subqueue(tx_ring->netdev, tx_ring->queue_index);
+
+ wx_warn(wx, "tx hang detected on queue %d, resetting adapter\n",
+ tx_ring->queue_index);
+
+ wx_tx_timeout_reset(wx);
+}
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
new file mode 100644
index 000000000000..e317e6c8d928
--- /dev/null
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * WangXun Gigabit PCI Express Linux driver
+ * Copyright (c) 2015 - 2026 Beijing WangXun Technology Co., Ltd.
+ */
+
+#ifndef _WX_ERR_H_
+#define _WX_ERR_H_
+
+void wx_handle_errors_subtask(struct wx *wx);
+void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue);
+void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next);
+
+#endif /* _WX_ERR_H_ */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 150a063daafd..2f71dc24306d 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -1931,6 +1931,7 @@ static void wx_configure_tx_ring(struct wx *wx,
else
ring->atr_sample_rate = 0;
+ bitmap_zero(ring->state, WX_RING_STATE_NBITS);
/* reinitialize tx_buffer_info */
memset(ring->tx_buffer_info, 0,
sizeof(struct wx_tx_buffer) * ring->count);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index c65240ca3c28..be7a170a4029 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -14,6 +14,7 @@
#include "wx_type.h"
#include "wx_lib.h"
+#include "wx_err.h"
#include "wx_ptp.h"
#include "wx_hw.h"
#include "wx_vf_lib.h"
@@ -742,6 +743,36 @@ static struct netdev_queue *wx_txring_txq(const struct wx_ring *ring)
return netdev_get_tx_queue(ring->netdev, ring->queue_index);
}
+static u64 wx_get_tx_pending(struct wx_ring *ring)
+{
+ unsigned int head, tail;
+
+ head = ring->next_to_clean;
+ tail = ring->next_to_use;
+
+ return ((head <= tail) ? tail : tail + ring->count) - head;
+}
+
+bool wx_check_tx_hang(struct wx_ring *ring)
+{
+ u64 tx_done_old = ring->tx_stats.tx_done_old;
+ u64 tx_pending = wx_get_tx_pending(ring);
+ u64 tx_done = ring->stats.packets;
+
+ clear_bit(WX_TX_DETECT_HANG, ring->state);
+
+ if (tx_done_old == tx_done && tx_pending)
+ /* make sure it is true for two checks in a row */
+ return test_and_set_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+ /* update completed stats and continue */
+ ring->tx_stats.tx_done_old = tx_done;
+ /* reset the countdown */
+ clear_bit(WX_HANG_CHECK_ARMED, ring->state);
+
+ return false;
+}
+
/**
* wx_clean_tx_irq - Reclaim resources after transmit completes
* @q_vector: structure containing interrupt and ring information
@@ -866,6 +897,12 @@ static bool wx_clean_tx_irq(struct wx_q_vector *q_vector,
netdev_tx_completed_queue(wx_txring_txq(tx_ring),
total_packets, total_bytes);
+ if (test_bit(WX_TX_DETECT_HANG, tx_ring->state) &&
+ wx_check_tx_hang(tx_ring)) {
+ wx_handle_tx_hang(tx_ring, i);
+ return true;
+ }
+
#define TX_WAKE_THRESHOLD (DESC_NEEDED * 2)
if (unlikely(total_packets && netif_carrier_ok(tx_ring->netdev) &&
(wx_desc_unused(tx_ring) >= TX_WAKE_THRESHOLD))) {
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index 42461723a400..85f70ea5a64f 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -10,6 +10,7 @@
struct wx_dec_ptype wx_decode_ptype(const u8 ptype);
void wx_alloc_rx_buffers(struct wx_ring *rx_ring, u16 cleaned_count);
u16 wx_desc_unused(struct wx_ring *ring);
+bool wx_check_tx_hang(struct wx_ring *ring);
netdev_tx_t wx_xmit_frame(struct sk_buff *skb,
struct net_device *netdev);
void wx_napi_enable_all(struct wx *wx);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index e013f05d2cfe..434a582393d7 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1036,6 +1036,7 @@ struct wx_queue_stats {
struct wx_tx_queue_stats {
u64 restart_queue;
u64 tx_busy;
+ u64 tx_done_old;
};
struct wx_rx_queue_stats {
@@ -1051,6 +1052,12 @@ struct wx_rx_queue_stats {
#define wx_for_each_ring(posm, headm) \
for (posm = (headm).ring; posm; posm = posm->next)
+enum wx_ring_state {
+ WX_TX_DETECT_HANG,
+ WX_HANG_CHECK_ARMED,
+ WX_RING_STATE_NBITS
+};
+
struct wx_ring_container {
struct wx_ring *ring; /* pointer to linked list of rings */
unsigned int total_bytes; /* total bytes processed this int */
@@ -1070,6 +1077,7 @@ struct wx_ring {
struct wx_tx_buffer *tx_buffer_info;
struct wx_rx_buffer *rx_buffer_info;
};
+ DECLARE_BITMAP(state, WX_RING_STATE_NBITS);
u8 __iomem *tail;
dma_addr_t dma; /* phys. address of descriptor ring */
dma_addr_t headwb_dma;
@@ -1254,6 +1262,7 @@ enum wx_pf_flags {
WX_FLAG_NEED_DO_RESET,
WX_FLAG_RX_MERGE_ENABLED,
WX_FLAG_TXHEAD_WB_ENABLED,
+ WX_FLAG_NEED_PF_RESET,
WX_PF_FLAGS_NBITS /* must be last */
};
@@ -1470,7 +1479,8 @@ wr32ptp(struct wx *wx, u32 reg, u32 value)
#define wx_err(wx, fmt, arg...) \
dev_err(&(wx)->pdev->dev, fmt, ##arg)
-
+#define wx_warn(wx, fmt, arg...) \
+ dev_warn(&(wx)->pdev->dev, fmt, ##arg)
#define wx_dbg(wx, fmt, arg...) \
dev_dbg(&(wx)->pdev->dev, fmt, ##arg)
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index baffcadb2269..b8db772c37c5 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -14,6 +14,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_mbx.h"
#include "../libwx/wx_sriov.h"
@@ -140,6 +141,26 @@ static int ngbe_sw_init(struct wx *wx)
return 0;
}
+/**
+ * ngbe_service_task - manages and runs subtasks
+ * @work: pointer to work_struct containing our data
+ **/
+static void ngbe_service_task(struct work_struct *work)
+{
+ struct wx *wx = container_of(work, struct wx, service_task);
+
+ wx_handle_errors_subtask(wx);
+
+ wx_service_event_complete(wx);
+}
+
+static void ngbe_init_service(struct wx *wx)
+{
+ timer_setup(&wx->service_timer, wx_service_timer, 0);
+ INIT_WORK(&wx->service_task, ngbe_service_task);
+ clear_bit(WX_STATE_SERVICE_SCHED, wx->state);
+}
+
/**
* ngbe_irq_enable - Enable default interrupt generation settings
* @wx: board private structure
@@ -370,6 +391,7 @@ static void ngbe_disable_device(struct wx *wx)
wx_napi_disable_all(wx);
netif_tx_stop_all_queues(netdev);
netif_tx_disable(netdev);
+ timer_delete_sync(&wx->service_timer);
if (wx->gpio_ctrl)
ngbe_sfp_modules_txrx_powerctl(wx, false);
wx_irq_disable(wx);
@@ -409,6 +431,7 @@ static void ngbe_up_complete(struct wx *wx)
wx_napi_enable_all(wx);
/* enable transmits */
netif_tx_start_all_queues(wx->netdev);
+ mod_timer(&wx->service_timer, jiffies);
/* clear any pending interrupts, may auto mask */
rd32(wx, WX_PX_IC(0));
@@ -583,6 +606,7 @@ static const struct net_device_ops ngbe_netdev_ops = {
.ndo_stop = ngbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
+ .ndo_tx_timeout = wx_tx_timeout,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
@@ -769,9 +793,11 @@ static int ngbe_probe(struct pci_dev *pdev,
eth_hw_addr_set(netdev, wx->mac.perm_addr);
wx_mac_set_default_filter(wx, wx->mac.perm_addr);
+ ngbe_init_service(wx);
+
err = wx_init_interrupt_scheme(wx);
if (err)
- goto err_free_mac_table;
+ goto err_cancel_service;
/* phy Interface Configuration */
err = ngbe_mdio_init(wx);
@@ -791,6 +817,9 @@ static int ngbe_probe(struct pci_dev *pdev,
wx_control_hw(wx, false);
err_clear_interrupt_scheme:
wx_clear_interrupt_scheme(wx);
+err_cancel_service:
+ timer_delete_sync(&wx->service_timer);
+ cancel_work_sync(&wx->service_task);
err_free_mac_table:
kfree(wx->rss_key);
kfree(wx->mac_table);
@@ -816,6 +845,9 @@ static void ngbe_remove(struct pci_dev *pdev)
struct wx *wx = pci_get_drvdata(pdev);
struct net_device *netdev;
+ timer_delete_sync(&wx->service_timer);
+ cancel_work_sync(&wx->service_task);
+
netdev = wx->netdev;
wx_disable_sriov(wx);
unregister_netdev(netdev);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index 01ceaf270b70..d4f87fbb9565 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -14,6 +14,7 @@
#include "../libwx/wx_type.h"
#include "../libwx/wx_lib.h"
+#include "../libwx/wx_err.h"
#include "../libwx/wx_ptp.h"
#include "../libwx/wx_hw.h"
#include "../libwx/wx_mbx.h"
@@ -128,6 +129,7 @@ static void txgbe_service_task(struct work_struct *work)
{
struct wx *wx = container_of(work, struct wx, service_task);
+ wx_handle_errors_subtask(wx);
txgbe_module_detection_subtask(wx);
txgbe_link_config_subtask(wx);
@@ -640,6 +642,7 @@ static const struct net_device_ops txgbe_netdev_ops = {
.ndo_stop = txgbe_close,
.ndo_change_mtu = wx_change_mtu,
.ndo_start_xmit = wx_xmit_frame,
+ .ndo_tx_timeout = wx_tx_timeout,
.ndo_set_rx_mode = wx_set_rx_mode,
.ndo_set_features = wx_set_features,
.ndo_fix_features = wx_fix_features,
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 10/11] net: wangxun: improve flow control setting
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (8 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops Jiawen Wu
2026-03-31 0:54 ` [PATCH net-next v6 00/11] Wangxun improvement and new support Jakub Kicinski
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Save the current mode of flow control, check the pause frame statistics
in watchdog to clear WX_HANG_CHECK_ARMED.
The received pause frames are divided into XON and XOFF to be counted.
And due to the hardware defect of SP devices, XON packets cannot be
trasmitted correctly, so Tx XON pause is disabled by default for those
devices.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 2 +-
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 52 +++++++++++++++++--
drivers/net/ethernet/wangxun/libwx/wx_type.h | 16 +++++-
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 3 +-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 3 +-
5 files changed, 66 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 9ab7dce956e3..d1356ff5d69b 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -211,7 +211,7 @@ void wx_get_pause_stats(struct net_device *netdev,
hwstats = &wx->stats;
stats->tx_pause_frames = hwstats->lxontxc + hwstats->lxofftxc;
- stats->rx_pause_frames = hwstats->lxonoffrxc;
+ stats->rx_pause_frames = hwstats->lxonrxc + hwstats->lxoffrxc;
}
EXPORT_SYMBOL(wx_get_pause_stats);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 2f71dc24306d..4e0195f7df39 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -2772,6 +2772,15 @@ int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
}
}
+ if (rx_pause && tx_pause)
+ wx->fc.mode = wx_fc_full;
+ else if (rx_pause)
+ wx->fc.mode = wx_fc_rx_pause;
+ else if (tx_pause)
+ wx->fc.mode = wx_fc_tx_pause;
+ else
+ wx->fc.mode = wx_fc_none;
+
/* Disable any previous flow control settings */
mflcn_reg = rd32(wx, WX_MAC_RX_FLOW_CTRL);
mflcn_reg &= ~WX_MAC_RX_FLOW_CTRL_RFE;
@@ -2790,7 +2799,9 @@ int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
/* Set up and enable Rx high/low water mark thresholds, enable XON. */
if (tx_pause && wx->fc.high_water) {
- fcrtl = (wx->fc.low_water << 10) | WX_RDB_RFCL_XONE;
+ fcrtl = (wx->fc.low_water << 10);
+ if (wx->mac.type != wx_mac_sp)
+ fcrtl |= WX_RDB_RFCL_XONE;
wr32(wx, WX_RDB_RFCL, fcrtl);
fcrth = (wx->fc.high_water << 10) | WX_RDB_RFCH_XOFFE;
} else {
@@ -2831,6 +2842,28 @@ int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
}
EXPORT_SYMBOL(wx_fc_enable);
+static void wx_update_xoff_rx_lfc(struct wx *wx)
+{
+ struct wx_hw_stats *hwstats = &wx->stats;
+ u64 data;
+ int i;
+
+ if (wx->fc.mode != wx_fc_full &&
+ wx->fc.mode != wx_fc_rx_pause)
+ return;
+
+ data = rd64(wx, WX_MAC_LXOFFRXC);
+ hwstats->lxoffrxc += data;
+
+ /* refill credits (no tx hang) if we received xoff */
+ if (!data)
+ return;
+
+ for (i = 0; i < wx->num_tx_queues; i++)
+ clear_bit(WX_HANG_CHECK_ARMED,
+ wx->tx_ring[i]->state);
+}
+
/**
* wx_update_stats - Update the board statistics counters.
* @wx: board private structure
@@ -2844,6 +2877,10 @@ void wx_update_stats(struct wx *wx)
u64 restart_queue = 0, tx_busy = 0;
u32 i;
+ if (!netif_running(wx->netdev) ||
+ test_bit(WX_STATE_RESETTING, wx->state))
+ return;
+
/* gather some stats to the wx struct that are per queue */
for (i = 0; i < wx->num_rx_queues; i++) {
struct wx_ring *rx_ring = wx->rx_ring[i];
@@ -2879,6 +2916,8 @@ void wx_update_stats(struct wx *wx)
wx->restart_queue = restart_queue;
wx->tx_busy = tx_busy;
+ wx_update_xoff_rx_lfc(wx);
+
hwstats->gprc += rd32(wx, WX_RDM_PKT_CNT);
hwstats->gptc += rd32(wx, WX_TDM_PKT_CNT);
hwstats->gorc += rd64(wx, WX_RDM_BYTE_CNT_LSB);
@@ -2893,7 +2932,10 @@ void wx_update_stats(struct wx *wx)
hwstats->mptc += rd64(wx, WX_TX_MC_FRAMES_GOOD_L);
hwstats->roc += rd32(wx, WX_RX_OVERSIZE_FRAMES_GOOD);
hwstats->ruc += rd32(wx, WX_RX_UNDERSIZE_FRAMES_GOOD);
- hwstats->lxonoffrxc += rd32(wx, WX_MAC_LXONOFFRXC);
+ if (wx->mac.type >= wx_mac_aml)
+ hwstats->lxonrxc = rd32(wx, WX_MAC_LXONRXC_AML);
+ else
+ hwstats->lxonrxc += rd32(wx, WX_MAC_LXONRXC);
hwstats->lxontxc += rd32(wx, WX_RDB_LXONTXC);
hwstats->lxofftxc += rd32(wx, WX_RDB_LXOFFTXC);
hwstats->o2bgptc += rd32(wx, WX_TDM_OS2BMC_CNT);
@@ -2946,7 +2988,11 @@ void wx_clear_hw_cntrs(struct wx *wx)
rd64(wx, WX_RX_LEN_ERROR_FRAMES_L);
rd32(wx, WX_RDB_LXONTXC);
rd32(wx, WX_RDB_LXOFFTXC);
- rd32(wx, WX_MAC_LXONOFFRXC);
+ if (wx->mac.type >= wx_mac_aml)
+ wr32(wx, WX_MAC_LXONRXC_AML, 0);
+ else
+ rd32(wx, WX_MAC_LXONRXC);
+ rd32(wx, WX_MAC_LXOFFRXC);
}
EXPORT_SYMBOL(wx_clear_hw_cntrs);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 434a582393d7..510fab4b3a0f 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -79,7 +79,9 @@
#define WX_RX_LEN_ERROR_FRAMES_L 0x11978
#define WX_RX_UNDERSIZE_FRAMES_GOOD 0x11938
#define WX_RX_OVERSIZE_FRAMES_GOOD 0x1193C
-#define WX_MAC_LXONOFFRXC 0x11E0C
+#define WX_MAC_LXOFFRXC 0x11988
+#define WX_MAC_LXONRXC 0x11E0C
+#define WX_MAC_LXONRXC_AML 0x11F84
/*********************** Receive DMA registers **************************/
#define WX_RDM_VF_RE(_i) (0x12004 + ((_i) * 4))
@@ -1156,9 +1158,18 @@ enum wx_isb_idx {
WX_ISB_MAX
};
+/* Flow Control Settings */
+enum wx_fc_mode {
+ wx_fc_none = 0,
+ wx_fc_rx_pause,
+ wx_fc_tx_pause,
+ wx_fc_full
+};
+
struct wx_fc_info {
u32 high_water; /* Flow Ctrl High-water */
u32 low_water; /* Flow Ctrl Low-water */
+ enum wx_fc_mode mode; /* Flow Control Mode */
};
/* Statistics counters collected by the MAC */
@@ -1175,7 +1186,8 @@ struct wx_hw_stats {
u64 mptc;
u64 roc;
u64 ruc;
- u64 lxonoffrxc;
+ u64 lxonrxc;
+ u64 lxoffrxc;
u64 lxontxc;
u64 lxofftxc;
u64 o2bgptc;
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index b8db772c37c5..080310a51ef8 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -150,6 +150,7 @@ static void ngbe_service_task(struct work_struct *work)
struct wx *wx = container_of(work, struct wx, service_task);
wx_handle_errors_subtask(wx);
+ wx_update_stats(wx);
wx_service_event_complete(wx);
}
@@ -401,8 +402,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 d4f87fbb9565..e9439e4ebe61 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -132,6 +132,7 @@ static void txgbe_service_task(struct work_struct *work)
wx_handle_errors_subtask(wx);
txgbe_module_detection_subtask(wx);
txgbe_link_config_subtask(wx);
+ wx_update_stats(wx);
wx_service_event_complete(wx);
}
@@ -261,8 +262,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)
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (9 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 10/11] net: wangxun: improve flow control setting Jiawen Wu
@ 2026-03-26 2:14 ` Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-31 0:54 ` [PATCH net-next v6 00/11] Wangxun improvement and new support Jakub Kicinski
11 siblings, 1 reply; 23+ messages in thread
From: Jiawen Wu @ 2026-03-26 2:14 UTC (permalink / raw)
To: netdev
Cc: Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Richard Cochran, Russell King,
Simon Horman, Michal Swiatkowski, Jacob Keller, Kees Cook,
Joe Damato, Larysa Zaremba, Abdun Nihaal, Breno Leitao, Jiawen Wu
Support AER driver to handle the PCIe errors.
Signed-off-by: Jiawen Wu <jiawenwu@trustnetic.com>
---
drivers/net/ethernet/wangxun/libwx/wx_err.c | 102 ++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_err.h | 2 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 5 +-
drivers/net/ethernet/wangxun/libwx/wx_type.h | 1 +
drivers/net/ethernet/wangxun/ngbe/ngbe_main.c | 5 +-
.../net/ethernet/wangxun/txgbe/txgbe_main.c | 5 +-
6 files changed, 117 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
index bb77bdce69d2..86b318647a3c 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
@@ -3,11 +3,113 @@
#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;
+
+ netdev = wx->netdev;
+ if (!netif_device_present(netdev))
+ return PCI_ERS_RESULT_DISCONNECT;
+
+ rtnl_lock();
+ netif_device_detach(netdev);
+
+ if (netif_running(netdev))
+ wx->close_suspend(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);
+ pci_ers_result_t result;
+
+ if (pci_enable_device_mem(pdev)) {
+ wx_err(wx, "Cannot re-enable PCI device after reset.\n");
+ result = PCI_ERS_RESULT_DISCONNECT;
+ } else {
+ /* make all bar access done before reset. */
+ smp_mb__before_atomic();
+ clear_bit(WX_STATE_DISABLED, wx->state);
+ pci_set_master(pdev);
+ pci_restore_state(pdev);
+ pci_save_state(pdev);
+ pci_wake_from_d3(pdev, false);
+
+ wx->do_reset(wx->netdev, false);
+ result = PCI_ERS_RESULT_RECOVERED;
+ }
+
+ pci_aer_clear_nonfatal_status(pdev);
+
+ return result;
+}
+
+/**
+ * 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;
+
+ netdev = wx->netdev;
+ rtnl_lock();
+ if (netif_running(netdev))
+ netdev->netdev_ops->ndo_open(netdev);
+
+ netif_device_attach(netdev);
+ 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 void wx_reset_subtask(struct wx *wx)
{
if (!test_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.h b/drivers/net/ethernet/wangxun/libwx/wx_err.h
index e317e6c8d928..8b1a7863b5b1 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_err.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_err.h
@@ -7,6 +7,8 @@
#ifndef _WX_ERR_H_
#define _WX_ERR_H_
+extern const struct pci_error_handlers wx_err_handler;
+
void wx_handle_errors_subtask(struct wx *wx);
void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue);
void wx_handle_tx_hang(struct wx_ring *tx_ring, unsigned int next);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index be7a170a4029..09be700f69bb 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3406,7 +3406,8 @@ static void wx_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);
}
int wx_suspend(struct pci_dev *pdev, pm_message_t state)
@@ -3434,6 +3435,8 @@ int wx_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);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_type.h b/drivers/net/ethernet/wangxun/libwx/wx_type.h
index 510fab4b3a0f..117cebbb9a67 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_type.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_type.h
@@ -1208,6 +1208,7 @@ enum wx_state {
WX_STATE_PTP_RUNNING,
WX_STATE_PTP_TX_IN_PROGRESS,
WX_STATE_SERVICE_SCHED,
+ WX_STATE_DISABLED,
WX_STATE_NBITS /* must be last */
};
diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
index 080310a51ef8..3715db4ec912 100644
--- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
+++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
@@ -808,6 +808,7 @@ static int ngbe_probe(struct pci_dev *pdev,
goto err_register;
pci_set_drvdata(pdev, wx);
+ pci_save_state(pdev);
return 0;
@@ -858,7 +859,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 struct pci_driver ngbe_driver = {
@@ -870,6 +872,7 @@ static struct pci_driver ngbe_driver = {
.resume = wx_resume,
.shutdown = wx_shutdown,
.sriov_configure = wx_pci_sriov_configure,
+ .err_handler = &wx_err_handler,
};
module_pci_driver(ngbe_driver);
diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
index e9439e4ebe61..4f0af47260a9 100644
--- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
+++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
@@ -885,6 +885,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);
@@ -955,7 +956,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 = {
@@ -967,6 +969,7 @@ static struct pci_driver txgbe_driver = {
.resume = wx_resume,
.shutdown = wx_shutdown,
.sriov_configure = wx_pci_sriov_configure,
+ .err_handler = &wx_err_handler,
};
module_pci_driver(txgbe_driver);
--
2.48.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 00/11] Wangxun improvement and new support
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
` (10 preceding siblings ...)
2026-03-26 2:14 ` [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops Jiawen Wu
@ 2026-03-31 0:54 ` Jakub Kicinski
11 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:54 UTC (permalink / raw)
To: Jiawen Wu
Cc: netdev, Mengyuan Lou, Andrew Lunn, David S. Miller, Eric Dumazet,
Paolo Abeni, Richard Cochran, Russell King, Simon Horman,
Michal Swiatkowski, Jacob Keller, Kees Cook, Joe Damato,
Larysa Zaremba, Abdun Nihaal, Breno Leitao
On Thu, 26 Mar 2026 10:13:55 +0800 Jiawen Wu wrote:
> Implement power management function for txgbe. Clean up the same code in
> the two drivers, to make more use of lib functions.
>
> Add Tx timeout process and pci_error_handlers ops, to recover the devices.
Split this series up, please. Why are you submitting flow control
and error recovery changes as a single series..
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops
2026-03-26 2:13 ` [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
2026-03-31 7:19 ` Jiawen Wu
0 siblings, 1 reply; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: ngbe: implement libwx reset ops
This commit implements the wx->do_reset() callback for the ngbe driver.
It provides a routine to bring the device down and back up when a reset
is requested while the device is running.
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index 8c9d505721b17..50a13dcf4b3c8 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
[ ... ]
> @@ -579,6 +586,32 @@ int ngbe_setup_tc(struct net_device *dev, u8 tc)
> return 0;
> }
>
> +static void ngbe_reinit_locked(struct wx *wx)
> +{
> + int err = 0;
> +
> + netif_trans_update(wx->netdev);
> +
> + err = wx_set_state_reset(wx);
> + if (err) {
> + wx_err(wx, "wait device reset timeout\n");
> + return;
> + }
> +
> + ngbe_down(wx);
> + ngbe_up(wx);
> +
> + clear_bit(WX_STATE_RESETTING, wx->state);
> +}
Is it possible to use a standard kernel mutex here instead of a bit flag for
synchronization?
Looking at the underlying implementation of wx_set_state_reset(), it relies
on an open-coded polling loop:
static inline int wx_set_state_reset(struct wx *wx)
{
u8 timeout = 50;
while (test_and_set_bit(WX_STATE_RESETTING, wx->state)) {
timeout--;
if (!timeout)
return -EBUSY;
usleep_range(1000, 2000);
}
return 0;
}
Using a bit flag and a sleep-polling loop to guard a teardown and bringup
section acts as an ad-hoc lock. This approach bypasses standard kernel
synchronization guarantees and prevents lockdep from analyzing the lock
ordering.
Since this context is allowed to sleep, could this section be protected by
a standard mutex instead?
--
pw-bot: cr
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx
2026-03-26 2:14 ` [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wangxun: move reusable PCI driver ops functions into libwx
This commit adds a function pointer for close_suspend and adjusts do_reset.
These changes allow PCI driver operations such as shutdown to be called
directly from the common libwx library.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> index 746623fa59b40..c65240ca3c283 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
[ ... ]
> @@ -3342,5 +3342,90 @@ void wx_service_timer(struct timer_list *t)
> }
> EXPORT_SYMBOL(wx_service_timer);
>
> +static void wx_dev_shutdown(struct pci_dev *pdev, bool *enable_wake)
> +{
> + struct wx *wx = pci_get_drvdata(pdev);
> + struct net_device *netdev;
> + u32 wufc = wx->wol;
> +
> + netdev = wx->netdev;
> + rtnl_lock();
> + netif_device_detach(netdev);
> +
> + if (netif_running(netdev))
> + wx->close_suspend(wx);
> +
> + wx_clear_interrupt_scheme(wx);
> + rtnl_unlock();
> +
> + if (wufc) {
> + wx_set_rx_mode(netdev);
> + wx_configure_rx(wx);
Does this sequence result in a use-after-free of the DMA rings?
When the interface is running, wx->close_suspend() is called, which frees
all receive and transmit descriptor rings via dma_free_coherent() in
wx_free_resources().
If Wake-on-LAN is enabled (wufc != 0), the code then calls
wx_configure_rx(). This programs the freed DMA physical addresses from
ring->dma (which is not cleared upon free) into the MACs Receive Descriptor
Base Address registers.
If the MAC fetches from these freed pages, could it interpret garbage data as
buffer addresses and DMA incoming network packets into random physical memory?
> + wr32(wx, WX_PSR_WKUP_CTL, wufc);
> + } else {
> + wr32(wx, WX_PSR_WKUP_CTL, 0);
> + }
> + pci_wake_from_d3(pdev, !!wufc);
> + *enable_wake = !!wufc;
> + wx_control_hw(wx, false);
> +
> + pci_disable_device(pdev);
> +}
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index 50a13dcf4b3c8..baffcadb22690 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
[ ... ]
> @@ -604,11 +570,11 @@ static void ngbe_reinit_locked(struct wx *wx)
> clear_bit(WX_STATE_RESETTING, wx->state);
> }
>
> -void ngbe_do_reset(struct net_device *netdev)
> +void ngbe_do_reset(struct net_device *netdev, bool reinit)
> {
> struct wx *wx = netdev_priv(netdev);
>
> - if (netif_running(netdev))
> + if (netif_running(netdev) && reinit)
> ngbe_reinit_locked(wx);
> }
Is it intentional that ngbe_do_reset() does nothing when reinit is false?
Before this patch, ngbe_resume() explicitly called ngbe_reset_hw().
Now that it uses the shared wx_resume(), it calls wx->do_reset(netdev, false).
Since ngbe_do_reset() lacks an else branch for when reinit is false
(unlike txgbe_do_reset()), the hardware reset is skipped entirely during
resume. Will this leave the MAC/PHY uninitialized and break the interface
after a suspend/resume cycle?
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 06/11] net: txgbe: add power management support
2026-03-26 2:14 ` [PATCH net-next v6 06/11] net: txgbe: add power management support Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: txgbe: add power management support
This commit adds power management support to the txgbe driver, including
.suspend, .resume, and Wake-on-LAN functionality. It updates the pci_driver
struct to use libwx suspend, resume, and shutdown callbacks.
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> index 30f66507809bd..ddc7cea9d7c28 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
[ ... ]
> @@ -975,7 +960,9 @@ static struct pci_driver txgbe_driver = {
> .id_table = txgbe_pci_tbl,
> .probe = txgbe_probe,
> .remove = txgbe_remove,
> - .shutdown = txgbe_shutdown,
> + .suspend = wx_suspend,
> + .resume = wx_resume,
> + .shutdown = wx_shutdown,
> .sriov_configure = wx_pci_sriov_configure,
> };
When Wake-on-LAN is enabled, do wx_shutdown() or wx_suspend() cause a
NULL pointer dereference regression?
If the interface is running, wx_dev_shutdown() calls txgbe_close_suspend(),
which frees the RX and TX rings via wx_free_resources().
wx_free_resources() calls kvfree(rx_ring->rx_buffer_info) and sets it to
NULL. It also calls dma_free_coherent() for rx_ring->desc but leaves
rx_ring->dma holding a stale IOVA.
Later in wx_dev_shutdown(), since Wake-on-LAN is enabled, it calls
wx_configure_rx() which loops over the rings and calls
wx_configure_rx_ring().
wx_configure_rx_ring() explicitly programs the hardware with the stale
DMA address ring->dma, which might lead to memory corruption. Furthermore,
it calls memset(ring->rx_buffer_info, 0, ...), which causes an unconditional
NULL pointer dereference.
Is there also a potential data race regression when configuring Wake-on-LAN
during suspend/shutdown?
In wx_dev_shutdown(), rtnl_lock() is explicitly unlocked right before
checking if Wake-on-LAN is enabled. If it is enabled, the driver calls
wx_set_rx_mode(netdev).
Inside wx_set_rx_mode(), the driver accesses the netdev's unicast and
multicast address lists. Since neither rtnl_lock() nor netif_addr_lock()
is held here, could concurrent list modifications cause list corruption?
Could the wx->service_task workqueue remain uncancelled during suspend and
shutdown?
The driver initializes a work item wx->service_task that runs periodically
via wx->service_timer. During suspend or shutdown, txgbe_disable_device()
deletes the timer with timer_delete_sync(), but
cancel_work_sync(&wx->service_task) is never called.
If the timer fired just before being deleted, the work item could execute
txgbe_service_task() in the background while the PCI device is placed into
D3hot or its PCI master bit is disabled, which might trigger PCI target
aborts or lockups.
Lastly, is the device missing a transition to PCI_D3hot during system
suspend?
The patch adds the .suspend = wx_suspend callback using the legacy PCI
power management API. Legacy PCI PM callbacks do not automatically invoke
pci_prepare_to_sleep() or pci_set_power_state() to place the device into
a low-power state.
While the wx_shutdown() path explicitly calls
pci_set_power_state(pdev, PCI_D3hot), wx_suspend() does not. Consequently,
the device appears to be left in PCI_D0 during system suspend.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work
2026-03-26 2:14 ` [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wangxun: delete service_timer before cancel service_work
This commit reorders timer cancellation before work cancellation in the
device removal path to prevent new work from being queued after the
device goes down.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> index ade2bfe563aaa..5565062c74713 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> @@ -48,6 +48,7 @@ void wxvf_remove(struct pci_dev *pdev)
> struct wx *wx = pci_get_drvdata(pdev);
> struct net_device *netdev;
>
> + timer_delete_sync(&wx->service_timer);
Is timer_delete_sync() the right API to use here? Since this is a device
teardown path, using timer_shutdown_sync() would permanently prevent the
timer from being re-armed by concurrent operations.
> cancel_work_sync(&wx->service_task);
> netdev = wx->netdev;
> unregister_netdev(netdev);
Does cancelling the timer and work before unregister_netdev() leave a race
window?
Since the network interface is still registered and hardware interrupts
are not yet disabled, could an interrupt or a concurrent interface up
operation call wx_service_event_schedule() and place wx->service_task
back into the workqueue?
If unregister_netdev() then completes without cancelling this newly
queued work, could the work execute after the wx structure is freed,
resulting in a use-after-free? Moving the sync cancellations after
unregister_netdev() might prevent this.
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> index ddc7cea9d7c28..01ceaf270b706 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> @@ -936,6 +936,7 @@ static void txgbe_remove(struct pci_dev *pdev)
> struct txgbe *txgbe = wx->priv;
> struct net_device *netdev;
>
> + timer_delete_sync(&wx->service_timer);
> cancel_work_sync(&wx->service_task);
>
> netdev = wx->netdev;
The same questions regarding timer_shutdown_sync() and the potential race
window with unregister_netdev() apply to this removal function as well.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process
2026-03-26 2:14 ` [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wangxun: add Tx timeout process
Implement .ndo_tx_timeout to handle Tx side timeout event. When Tx
timeout event occur, it will triger driver into reset process.
The WX_HANG_CHECK_ARMED bit is set to indicate a potential hang. It will
be cleared if a pause frame is received to remove false hang detection
due to 802.3 frames, which is implemented in later commit.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> new file mode 100644
> index 0000000000000..bb77bdce69d22
> --- /dev/null
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> @@ -0,0 +1,126 @@
[ ... ]
> +static void wx_reset_subtask(struct wx *wx)
> +{
> + if (!test_bit(WX_FLAG_NEED_PF_RESET, wx->flags))
> + return;
> +
> + if (!netif_running(wx->netdev) ||
> + test_bit(WX_STATE_RESETTING, wx->state))
> + return;
> +
> + rtnl_lock();
> +
> + wx_warn(wx, "Reset adapter.\n");
> +
> + if (test_bit(WX_FLAG_NEED_PF_RESET, wx->flags)) {
> + if (wx->do_reset)
> + wx->do_reset(wx->netdev, true);
Can this race with dev_close()?
If the interface is brought down concurrently, dev_close() acquires rtnl_lock(),
clears the running state, and frees resources. When wx_reset_subtask() later
acquires the lock, it does not re-check netif_running().
Could this result in calling do_reset() on a closed interface, leading to
hardware state inconsistencies or use-after-free crashes?
> + clear_bit(WX_FLAG_NEED_PF_RESET, wx->flags);
Could this unconditionally clear WX_FLAG_NEED_PF_RESET even if another
context sets it during the do_reset() execution? Should this use
test_and_clear_bit() before the reset operation to avoid losing concurrent
reset requests?
> + }
> +
> + rtnl_unlock();
> +}
[ ... ]
> +void wx_tx_timeout(struct net_device *netdev, unsigned int txqueue)
> +{
> + struct wx *wx = netdev_priv(netdev);
> + u32 head, tail;
> + int i;
> +
> + for (i = 0; i < wx->num_tx_queues; i++) {
> + struct wx_ring *tx_ring = wx->tx_ring[i];
> +
> + if (test_bit(WX_TX_DETECT_HANG, tx_ring->state) &&
> + wx_check_tx_hang(tx_ring))
Does calling wx_check_tx_hang() from the dev_watchdog softirq context cause
data races with the NAPI polling context (wx_clean_tx_irq)?
wx_check_tx_hang() modifies ring state bits (WX_HANG_CHECK_ARMED) and updates
the 64-bit statistic tx_done_old. Can this lead to torn reads or writes on
32-bit platforms and silent corruption of the hang detection state?
> + wx_warn(wx, "Real tx hang detected on queue %d\n", i);
> +
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index baffcadb22690..b8db772c37c5c 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
[ ... ]
> @@ -370,6 +391,7 @@ static void ngbe_disable_device(struct wx *wx)
> wx_napi_disable_all(wx);
> netif_tx_stop_all_queues(netdev);
> netif_tx_disable(netdev);
> + timer_delete_sync(&wx->service_timer);
Does this code leave the service task running?
timer_delete_sync() is called, but cancel_work_sync(&wx->service_task) is
missing. If the work is already queued, could wx_check_tx_hang_subtask() run
after the transmission and reception rings are freed, resulting in a
use-after-free when accessing wx->tx_ring[i]->state?
> if (wx->gpio_ctrl)
> ngbe_sfp_modules_txrx_powerctl(wx, false);
> wx_irq_disable(wx);
[ ... ]
> @@ -816,6 +845,9 @@ static void ngbe_remove(struct pci_dev *pdev)
> struct wx *wx = pci_get_drvdata(pdev);
> struct net_device *netdev;
>
> + timer_delete_sync(&wx->service_timer);
> + cancel_work_sync(&wx->service_task);
> +
> netdev = wx->netdev;
> wx_disable_sriov(wx);
> unregister_netdev(netdev);
Does this teardown order result in a use-after-free?
cancel_work_sync() is called before unregister_netdev(). Since the network
device is still active, could a concurrent timeout or interrupt re-queue the
service task before unregister_netdev() finishes? This might allow the
re-queued work item to execute and access the wx structure after it has been
freed by devres.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 10/11] net: wangxun: improve flow control setting
2026-03-26 2:14 ` [PATCH net-next v6 10/11] net: wangxun: improve flow control setting Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wangxun: improve flow control setting
Save the current mode of flow control, check the pause frame statistics
in watchdog to clear WX_HANG_CHECK_ARMED. The received pause frames are
divided into XON and XOFF to be counted, and Tx XON pause is disabled by
default for SP devices.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
> index 2f71dc24306d4..4e0195f7df39e 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
[ ... ]
> @@ -2831,6 +2842,28 @@ int wx_fc_enable(struct wx *wx, bool tx_pause, bool rx_pause)
> }
> EXPORT_SYMBOL(wx_fc_enable);
>
> +static void wx_update_xoff_rx_lfc(struct wx *wx)
> +{
> + struct wx_hw_stats *hwstats = &wx->stats;
> + u64 data;
> + int i;
> +
> + if (wx->fc.mode != wx_fc_full &&
> + wx->fc.mode != wx_fc_rx_pause)
> + return;
> +
> + data = rd64(wx, WX_MAC_LXOFFRXC);
Is it possible that rd64() is reading a 32-bit register here?
Looking at wx_clear_hw_cntrs(), WX_MAC_LXOFFRXC is cleared using rd32().
If rd64() reads an adjacent unmapped address, could it populate the upper 32
bits of data with garbage?
> + hwstats->lxoffrxc += data;
> +
> + /* refill credits (no tx hang) if we received xoff */
> + if (!data)
> + return;
> +
> + for (i = 0; i < wx->num_tx_queues; i++)
> + clear_bit(WX_HANG_CHECK_ARMED,
> + wx->tx_ring[i]->state);
> +}
If data is populated with garbage from the upper 32 bits, would this bypass
the check for zero and unconditionally clear the WX_HANG_CHECK_ARMED bit on
all Tx queues, preventing the Tx hang recovery watchdog from operating?
> /**
> * wx_update_stats - Update the board statistics counters.
> * @wx: board private structure
> @@ -2844,6 +2877,10 @@ void wx_update_stats(struct wx *wx)
> u64 restart_queue = 0, tx_busy = 0;
> u32 i;
>
> + if (!netif_running(wx->netdev) ||
> + test_bit(WX_STATE_RESETTING, wx->state))
> + return;
> +
Does this early return cause the final hardware statistics to be lost on
interface teardown?
Since the explicit wx_update_stats() calls were removed from
ngbe_disable_device() and txgbe_disable_device(), and the interface is
marked down before hardware disablement completes, it seems any statistics
accumulated since the last periodic service task will be lost when the
registers are cleared on reset.
[ ... ]
> @@ -2893,7 +2932,10 @@ void wx_update_stats(struct wx *wx)
> hwstats->mptc += rd64(wx, WX_TX_MC_FRAMES_GOOD_L);
> hwstats->roc += rd32(wx, WX_RX_OVERSIZE_FRAMES_GOOD);
> hwstats->ruc += rd32(wx, WX_RX_UNDERSIZE_FRAMES_GOOD);
> - hwstats->lxonoffrxc += rd32(wx, WX_MAC_LXONOFFRXC);
> + if (wx->mac.type >= wx_mac_aml)
> + hwstats->lxonrxc = rd32(wx, WX_MAC_LXONRXC_AML);
Will this 64-bit software statistic wrap back to zero when the hardware
counter overflows?
As seen in wx_clear_hw_cntrs(), WX_MAC_LXONRXC_AML is cleared by writing 0:
wr32(wx, WX_MAC_LXONRXC_AML, 0);
This suggests it is a continuous 32-bit hardware counter rather than
clear-on-read. Direct assignment might break the monotonic property expected
by ethtool.
> + else
> + hwstats->lxonrxc += rd32(wx, WX_MAC_LXONRXC);
> hwstats->lxontxc += rd32(wx, WX_RDB_LXONTXC);
> hwstats->lxofftxc += rd32(wx, WX_RDB_LXOFFTXC);
> hwstats->o2bgptc += rd32(wx, WX_TDM_OS2BMC_CNT);
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> index b8db772c37c5c..080310a51ef86 100644
> --- a/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> +++ b/drivers/net/ethernet/wangxun/ngbe/ngbe_main.c
> @@ -150,6 +150,7 @@ static void ngbe_service_task(struct work_struct *work)
> struct wx *wx = container_of(work, struct wx, service_task);
>
> wx_handle_errors_subtask(wx);
> + wx_update_stats(wx);
>
> wx_service_event_complete(wx);
> }
Can this introduce data races with userspace ethtool callbacks?
The service task executes asynchronously without holding rtnl_lock, while
ethtool callbacks like wx_get_pause_stats() hold rtnl_lock() and also call
wx_update_stats().
This might lead to double-reads of clear-on-read hardware registers and
lost software counter increments.
Also, could this cause a concurrency issue during interface teardown?
During reconfiguration, ngbe_close() deletes the service timer but does not
call cancel_work_sync(&wx->service_task).
If the service task runs concurrently with wx_clear_interrupt_scheme(),
it might bypass early return checks and access wx->tx_ring[i] while it
is being freed and set to NULL, potentially causing a use-after-free or
NULL pointer dereference.
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> index d4f87fbb95655..e9439e4ebe61e 100644
> --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_main.c
> @@ -132,6 +132,7 @@ static void txgbe_service_task(struct work_struct *work)
> wx_handle_errors_subtask(wx);
> txgbe_module_detection_subtask(wx);
> txgbe_link_config_subtask(wx);
> + wx_update_stats(wx);
>
> wx_service_event_complete(wx);
> }
(Same questions apply to txgbe_service_task regarding data races and
teardown concurrency.)
[ ... ]
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops
2026-03-26 2:14 ` [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops Jiawen Wu
@ 2026-03-31 0:55 ` Jakub Kicinski
0 siblings, 0 replies; 23+ messages in thread
From: Jakub Kicinski @ 2026-03-31 0:55 UTC (permalink / raw)
To: jiawenwu
Cc: Jakub Kicinski, netdev, mengyuanlou, andrew+netdev, davem,
edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
net: wangxun: implement pci_error_handlers ops
Support AER driver to handle the PCIe errors for Wangxun devices.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_err.c b/drivers/net/ethernet/wangxun/libwx/wx_err.c
> index bb77bdce69d22..86b318647a3ca 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_err.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_err.c
[ ... ]
> @@ -3,11 +3,113 @@
>
> #include <linux/netdevice.h>
> #include <linux/pci.h>
> +#include <linux/aer.h>
>
> #include "wx_type.h"
> #include "wx_lib.h"
> #include "wx_err.h"
[ ... ]
> +static pci_ers_result_t wx_io_slot_reset(struct pci_dev *pdev)
> +{
> + struct wx *wx = pci_get_drvdata(pdev);
> + pci_ers_result_t result;
> +
> + if (pci_enable_device_mem(pdev)) {
> + wx_err(wx, "Cannot re-enable PCI device after reset.\n");
> + result = PCI_ERS_RESULT_DISCONNECT;
> + } else {
> + /* make all bar access done before reset. */
> + smp_mb__before_atomic();
> + clear_bit(WX_STATE_DISABLED, wx->state);
> + pci_set_master(pdev);
> + pci_restore_state(pdev);
> + pci_save_state(pdev);
> + pci_wake_from_d3(pdev, false);
> +
> + wx->do_reset(wx->netdev, false);
Is it possible that the ngbe driver ignores this hardware reset request?
If ngbe_do_reset() only performs an action when reinit is true, it appears it
would be a no-op when called with false from this AER recovery path. Will
this leave the ngbe MAC hardware uninitialized after the PCI slot reset and
break the device when traffic resumes?
> + result = PCI_ERS_RESULT_RECOVERED;
> + }
> +
> + pci_aer_clear_nonfatal_status(pdev);
> +
> + return result;
> +}
> +
> +/**
> + * 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;
> +
> + netdev = wx->netdev;
> + rtnl_lock();
> + if (netif_running(netdev))
> + netdev->netdev_ops->ndo_open(netdev);
Does this code need to check the return value of ndo_open()?
If ndo_open() fails due to memory allocation or IRQ request failures, the
resources will remain unallocated. Because the failure is ignored and
netif_device_attach() is called unconditionally below, will the networking
stack invoke the transmit handler and dereference the unallocated rings?
> +
> + netif_device_attach(netdev);
> + rtnl_unlock();
> +}
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops
2026-03-31 0:55 ` Jakub Kicinski
@ 2026-03-31 7:19 ` Jiawen Wu
2026-03-31 13:03 ` Andrew Lunn
2026-03-31 23:05 ` Jacob Keller
0 siblings, 2 replies; 23+ messages in thread
From: Jiawen Wu @ 2026-03-31 7:19 UTC (permalink / raw)
To: 'Jakub Kicinski'
Cc: netdev, mengyuanlou, andrew+netdev, davem, edumazet, pabeni,
richardcochran, linux, horms, michal.swiatkowski, jacob.e.keller,
kees, joe, larysa.zaremba, abdun.nihaal, leitao, netdev,
mengyuanlou, andrew+netdev, davem, edumazet, pabeni,
richardcochran, linux, horms, michal.swiatkowski, jacob.e.keller,
kees, joe, larysa.zaremba, abdun.nihaal, leitao
> > +static void ngbe_reinit_locked(struct wx *wx)
> > +{
> > + int err = 0;
> > +
> > + netif_trans_update(wx->netdev);
> > +
> > + err = wx_set_state_reset(wx);
> > + if (err) {
> > + wx_err(wx, "wait device reset timeout\n");
> > + return;
> > + }
> > +
> > + ngbe_down(wx);
> > + ngbe_up(wx);
> > +
> > + clear_bit(WX_STATE_RESETTING, wx->state);
> > +}
>
> Is it possible to use a standard kernel mutex here instead of a bit flag for
> synchronization?
>
> Looking at the underlying implementation of wx_set_state_reset(), it relies
> on an open-coded polling loop:
>
> static inline int wx_set_state_reset(struct wx *wx)
> {
> u8 timeout = 50;
>
> while (test_and_set_bit(WX_STATE_RESETTING, wx->state)) {
> timeout--;
> if (!timeout)
> return -EBUSY;
>
> usleep_range(1000, 2000);
> }
>
> return 0;
> }
>
> Using a bit flag and a sleep-polling loop to guard a teardown and bringup
> section acts as an ad-hoc lock. This approach bypasses standard kernel
> synchronization guarantees and prevents lockdep from analyzing the lock
> ordering.
>
> Since this context is allowed to sleep, could this section be protected by
> a standard mutex instead?
I think using a state flag here would be better. Because other code paths
(like watchdog) need to check if a reset is in process without taking a lock.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops
2026-03-31 7:19 ` Jiawen Wu
@ 2026-03-31 13:03 ` Andrew Lunn
2026-03-31 23:05 ` Jacob Keller
1 sibling, 0 replies; 23+ messages in thread
From: Andrew Lunn @ 2026-03-31 13:03 UTC (permalink / raw)
To: Jiawen Wu
Cc: 'Jakub Kicinski', netdev, mengyuanlou, andrew+netdev,
davem, edumazet, pabeni, richardcochran, linux, horms,
michal.swiatkowski, jacob.e.keller, kees, joe, larysa.zaremba,
abdun.nihaal, leitao
> > static inline int wx_set_state_reset(struct wx *wx)
> > {
> > u8 timeout = 50;
> >
> > while (test_and_set_bit(WX_STATE_RESETTING, wx->state)) {
> > timeout--;
> > if (!timeout)
> > return -EBUSY;
> >
> > usleep_range(1000, 2000);
> > }
> >
> > return 0;
> > }
At minimum, this should use one of the helpers from iopoll.h.
Andrew
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops
2026-03-31 7:19 ` Jiawen Wu
2026-03-31 13:03 ` Andrew Lunn
@ 2026-03-31 23:05 ` Jacob Keller
1 sibling, 0 replies; 23+ messages in thread
From: Jacob Keller @ 2026-03-31 23:05 UTC (permalink / raw)
To: Jiawen Wu, 'Jakub Kicinski'
Cc: netdev, mengyuanlou, andrew+netdev, davem, edumazet, pabeni,
richardcochran, linux, horms, michal.swiatkowski, kees, joe,
larysa.zaremba, abdun.nihaal, leitao
On 3/31/2026 12:19 AM, Jiawen Wu wrote:
>>> +static void ngbe_reinit_locked(struct wx *wx)
>>> +{
>>> + int err = 0;
>>> +
>>> + netif_trans_update(wx->netdev);
>>> +
>>> + err = wx_set_state_reset(wx);
>>> + if (err) {
>>> + wx_err(wx, "wait device reset timeout\n");
>>> + return;
>>> + }
>>> +
>>> + ngbe_down(wx);
>>> + ngbe_up(wx);
>>> +
>>> + clear_bit(WX_STATE_RESETTING, wx->state);
>>> +}
>>
>> Is it possible to use a standard kernel mutex here instead of a bit flag for
>> synchronization?
>>
>> Looking at the underlying implementation of wx_set_state_reset(), it relies
>> on an open-coded polling loop:
>>
>> static inline int wx_set_state_reset(struct wx *wx)
>> {
>> u8 timeout = 50;
>>
>> while (test_and_set_bit(WX_STATE_RESETTING, wx->state)) {
>> timeout--;
>> if (!timeout)
>> return -EBUSY;
>>
>> usleep_range(1000, 2000);
>> }
>>
>> return 0;
>> }
>>
>> Using a bit flag and a sleep-polling loop to guard a teardown and bringup
>> section acts as an ad-hoc lock. This approach bypasses standard kernel
>> synchronization guarantees and prevents lockdep from analyzing the lock
>> ordering.
>>
>> Since this context is allowed to sleep, could this section be protected by
>> a standard mutex instead?
> > I think using a state flag here would be better. Because other code
paths
> (like watchdog) need to check if a reset is in process without taking a lock.
>
>
Couldn't you use an atomic and a mutex together?
mutex_lock(wx->reset_lock);
set_bit(WX_STATE_RESETTING, wx->state);
...
Mutex_unlock(wx->reset_lock);
Paths that only need to know a reset has already started can just
test_bit. Paths which need to wait on reset can acquire the lock.
There shouldn't be any issue doing this that doesn't already exist with
the test_and_set loop, because you can already have a path doing "if
(test_bit(resetting)) exit" flow would still potentially begin executing
even if a reset already acquired the lock, it could have ordered the
test path first regardless, so you already aren't really guaranteed that
a reset won't start until your operation is done unless you hold the lock.
Using a mutex also gives you lockdep checking and is generally more sane
to reason about.
Thanks,
Jake
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-03-31 23:05 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-26 2:13 [PATCH net-next v6 00/11] Wangxun improvement and new support Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 01/11] net: ngbe: remove netdev->ethtool->wol_enabled setting Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 02/11] net: ngbe: move the WOL functions to libwx Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 03/11] net: ngbe: remove redundant macros Jiawen Wu
2026-03-26 2:13 ` [PATCH net-next v6 04/11] net: ngbe: implement libwx reset ops Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-31 7:19 ` Jiawen Wu
2026-03-31 13:03 ` Andrew Lunn
2026-03-31 23:05 ` Jacob Keller
2026-03-26 2:14 ` [PATCH net-next v6 05/11] net: wangxun: move reusable PCI driver ops functions into libwx Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 06/11] net: txgbe: add power management support Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 07/11] net: wangxun: move ethtool_ops.set_channels into libwx Jiawen Wu
2026-03-26 2:14 ` [PATCH net-next v6 08/11] net: wangxun: delete service_timer before cancel service_work Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 09/11] net: wangxun: add Tx timeout process Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 10/11] net: wangxun: improve flow control setting Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-26 2:14 ` [PATCH net-next v6 11/11] net: wangxun: implement pci_error_handlers ops Jiawen Wu
2026-03-31 0:55 ` Jakub Kicinski
2026-03-31 0:54 ` [PATCH net-next v6 00/11] Wangxun improvement and new support Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox