* [PATCH net-next v6 0/3] net: libwx: improve VF ethtool support
@ 2026-07-01 10:01 Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 1/3] net: libwx: add support for set_ringparam in wx_ethtool_ops_vf Mengyuan Lou
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Mengyuan Lou @ 2026-07-01 10:01 UTC (permalink / raw)
To: netdev; +Cc: jiawenwu, duanqiangwen, horms, kuba, pabeni, Mengyuan Lou
This series improves ethtool support for Wangxun VF drivers
(ngbevf and txgbevf) in libwx.
This series extends VF support by enabling:
ring parameter configuration via ethtool -G
interrupt coalescing configuration via ethtool -C
per-queue statistics reporting via ethtool -S
Patch 1 adds support for set_ringparam in wx_ethtool_ops_vf,
allowing VF users to adjust TX/RX descriptor ring sizes.
Patch 2 enables set_coalesce support for VF devices and updates
EITR programming to use the VF-specific register access helper.
Patch 3 adds VF statistics support so ethtool -S can
report useful runtime counters for VF interfaces.
Changelog:
v6:
- Patch 1:
Clarify comments describing wx_set_ring() failure semantics.
- Patch 2:
Expand commit message to explain VF ITR encoding changes.
- Patch 3:
Protect ring accesses in wx_update_stats().
v5: https://lore.kernel.org/all/20260615104849.60444-1-mengyuanlou@net-swift.com
- Patch 3:
Convert WX_NUM_{RX,TX}_QUEUES macros to take explicit netdev parameter
as suggested by Simon Horman.
v4: https://lore.kernel.org/all/20260608103946.25786-1-mengyuanlou@net-swift.com
sashiko: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260529075147.88398-1-mengyuanlou%40net-swift.com
- Patch 2:
PF and VF share the same ITR value range. Update wx_write_eitr_vf() to apply
identical range clamping as the PF path, ensuring consistent coalesce behavior
across both device types.
- Patch 3:
Remove wx_update_stats in wxvf_down.
Replace both WX_GLOBAL_STATS_LEN and WX_STATS_LEN macros with type-safe
static helpers wx_global_stats_len() and wx_stats_len().
v3: https://lore.kernel.org/all/20260529075147.88398-1-mengyuanlou@net-swift.com
- Patch 3:
Drop netdev_stat_ops support from this patch.
v2: https://lore.kernel.org/all/20260525101115.13151-1-mengyuanlou@net-swift.com
- Patch 3:
Remove some stats which can be replaced by standard stats defined in
include/net/netdev_queues.h.
- Patch 1:
Adding a return value to wx_set_ring to make wx_set_ringparam_vf can
be passed back to userspace.
Remove freeing and requesting of IRQs. Ring resize only updates descriptor
resources and does not change MSI-X vector or interrupt configuration,
so IRQs do not need to be reallocated.
v1: https://lore.kernel.org/all/20260514103405.42175-1-mengyuanlou@net-swift.com
Mengyuan Lou (3):
net: libwx: add support for set_ringparam in wx_ethtool_ops_vf
net: libwx: add support for set_coalesce in wx_ethtool_ops_vf
net: libwx: support vf per-queue statistics via ethtool -S
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 139 +++++++++++++++---
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 8 +
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 9 +-
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 4 +-
drivers/net/ethernet/wangxun/libwx/wx_vf.h | 1 -
.../net/ethernet/wangxun/libwx/wx_vf_common.c | 6 +-
.../net/ethernet/wangxun/libwx/wx_vf_common.h | 2 +
.../net/ethernet/wangxun/libwx/wx_vf_lib.c | 13 +-
8 files changed, 150 insertions(+), 32 deletions(-)
--
2.30.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH net-next v6 1/3] net: libwx: add support for set_ringparam in wx_ethtool_ops_vf
2026-07-01 10:01 [PATCH net-next v6 0/3] net: libwx: improve VF ethtool support Mengyuan Lou
@ 2026-07-01 10:01 ` Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 2/3] net: libwx: add support for set_coalesce " Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S Mengyuan Lou
2 siblings, 0 replies; 6+ messages in thread
From: Mengyuan Lou @ 2026-07-01 10:01 UTC (permalink / raw)
To: netdev; +Cc: jiawenwu, duanqiangwen, horms, kuba, pabeni, Mengyuan Lou
Add support for the set_ringparam in wx_ethtool_ops_vf,
which is used to set ring sizes for ngbevf and txgbevf.
Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 61 +++++++++++++++++++
drivers/net/ethernet/wangxun/libwx/wx_lib.c | 9 +--
drivers/net/ethernet/wangxun/libwx/wx_lib.h | 4 +-
.../net/ethernet/wangxun/libwx/wx_vf_common.c | 4 +-
.../net/ethernet/wangxun/libwx/wx_vf_common.h | 2 +
5 files changed, 72 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 5df971aca9e3..eae038df6875 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -9,6 +9,7 @@
#include "wx_ethtool.h"
#include "wx_hw.h"
#include "wx_lib.h"
+#include "wx_vf_common.h"
struct wx_stats {
char stat_string[ETH_GSTRING_LEN];
@@ -775,6 +776,65 @@ static int wx_get_link_ksettings_vf(struct net_device *netdev,
return 0;
}
+static int wx_set_ringparam_vf(struct net_device *netdev,
+ struct ethtool_ringparam *ring,
+ struct kernel_ethtool_ringparam *kernel_ring,
+ struct netlink_ext_ack *extack)
+{
+ struct wx *wx = netdev_priv(netdev);
+ u32 new_rx_count, new_tx_count;
+ struct wx_ring *temp_ring;
+ int i, err = 0;
+
+ new_tx_count = clamp_t(u32, ring->tx_pending, WX_MIN_TXD, WX_MAX_TXD);
+ new_tx_count = ALIGN(new_tx_count, WX_REQ_TX_DESCRIPTOR_MULTIPLE);
+
+ new_rx_count = clamp_t(u32, ring->rx_pending, WX_MIN_RXD, WX_MAX_RXD);
+ new_rx_count = ALIGN(new_rx_count, WX_REQ_RX_DESCRIPTOR_MULTIPLE);
+
+ if (new_tx_count == wx->tx_ring_count &&
+ new_rx_count == wx->rx_ring_count)
+ return 0;
+
+ mutex_lock(&wx->reset_lock);
+ set_bit(WX_STATE_RESETTING, wx->state);
+
+ if (!netif_running(wx->netdev)) {
+ for (i = 0; i < wx->num_tx_queues; i++)
+ wx->tx_ring[i]->count = new_tx_count;
+ for (i = 0; i < wx->num_rx_queues; i++)
+ wx->rx_ring[i]->count = new_rx_count;
+ wx->tx_ring_count = new_tx_count;
+ wx->rx_ring_count = new_rx_count;
+
+ goto clear_reset;
+ }
+
+ /* allocate temporary buffer to store rings in */
+ i = max_t(int, wx->num_tx_queues, wx->num_rx_queues);
+ temp_ring = kvmalloc_objs(struct wx_ring, i);
+ if (!temp_ring) {
+ err = -ENOMEM;
+ goto clear_reset;
+ }
+
+ wxvf_down(wx);
+ /* wx_set_ring() may partially apply changes before
+ * returning an error. The error indicates that not all
+ * requested ring parameters could be configured.
+ */
+ err = wx_set_ring(wx, new_tx_count, new_rx_count, temp_ring);
+ if (err)
+ wx_err(wx, "failed to set ring parameters: %d", err);
+ wx_configure_vf(wx);
+ wxvf_up_complete(wx);
+ kvfree(temp_ring);
+clear_reset:
+ clear_bit(WX_STATE_RESETTING, wx->state);
+ mutex_unlock(&wx->reset_lock);
+ return err;
+}
+
static const struct ethtool_ops wx_ethtool_ops_vf = {
.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ |
@@ -782,6 +842,7 @@ static const struct ethtool_ops wx_ethtool_ops_vf = {
.get_drvinfo = wx_get_drvinfo,
.get_link = ethtool_op_get_link,
.get_ringparam = wx_get_ringparam,
+ .set_ringparam = wx_set_ringparam_vf,
.get_msglevel = wx_get_msglevel,
.get_coalesce = wx_get_coalesce,
.get_ts_info = ethtool_op_get_ts_info,
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
index d042567b8128..2e5dba88d04a 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.c
@@ -3248,8 +3248,8 @@ netdev_features_t wx_features_check(struct sk_buff *skb,
}
EXPORT_SYMBOL(wx_features_check);
-void wx_set_ring(struct wx *wx, u32 new_tx_count,
- u32 new_rx_count, struct wx_ring *temp_ring)
+int wx_set_ring(struct wx *wx, u32 new_tx_count,
+ u32 new_rx_count, struct wx_ring *temp_ring)
{
int i, err = 0;
@@ -3271,7 +3271,7 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
i--;
wx_free_tx_resources(&temp_ring[i]);
}
- return;
+ return err;
}
}
@@ -3299,7 +3299,7 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
i--;
wx_free_rx_resources(&temp_ring[i]);
}
- return;
+ return err;
}
}
@@ -3311,6 +3311,7 @@ void wx_set_ring(struct wx *wx, u32 new_tx_count,
wx->rx_ring_count = new_rx_count;
}
+ return 0;
}
EXPORT_SYMBOL(wx_set_ring);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_lib.h b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
index aed6ea8cf0d6..bc671786978e 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_lib.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_lib.h
@@ -36,8 +36,8 @@ netdev_features_t wx_fix_features(struct net_device *netdev,
netdev_features_t wx_features_check(struct sk_buff *skb,
struct net_device *netdev,
netdev_features_t features);
-void wx_set_ring(struct wx *wx, u32 new_tx_count,
- u32 new_rx_count, struct wx_ring *temp_ring);
+int wx_set_ring(struct wx *wx, u32 new_tx_count,
+ u32 new_rx_count, struct wx_ring *temp_ring);
void wx_service_event_schedule(struct wx *wx);
void wx_service_event_complete(struct wx *wx);
void wx_service_timer(struct timer_list *t);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
index 0d2db8d38cd5..26de78e9a69e 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
@@ -269,7 +269,7 @@ static void wxvf_irq_enable(struct wx *wx)
wr32(wx, WX_VXIMC, wx->eims_enable_mask);
}
-static void wxvf_up_complete(struct wx *wx)
+void wxvf_up_complete(struct wx *wx)
{
/* Always set the carrier off */
netif_carrier_off(wx->netdev);
@@ -324,7 +324,7 @@ int wxvf_open(struct net_device *netdev)
}
EXPORT_SYMBOL(wxvf_open);
-static void wxvf_down(struct wx *wx)
+void wxvf_down(struct wx *wx)
{
struct net_device *netdev = wx->netdev;
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h
index cbbb1b178cb2..d45d5d8ac3ab 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.h
@@ -15,7 +15,9 @@ void wx_set_rx_mode_vf(struct net_device *netdev);
void wx_configure_vf(struct wx *wx);
int wx_set_mac_vf(struct net_device *netdev, void *p);
void wxvf_watchdog_update_link(struct wx *wx);
+void wxvf_up_complete(struct wx *wx);
int wxvf_open(struct net_device *netdev);
+void wxvf_down(struct wx *wx);
int wxvf_close(struct net_device *netdev);
void wxvf_init_service(struct wx *wx);
--
2.30.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next v6 2/3] net: libwx: add support for set_coalesce in wx_ethtool_ops_vf
2026-07-01 10:01 [PATCH net-next v6 0/3] net: libwx: improve VF ethtool support Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 1/3] net: libwx: add support for set_ringparam in wx_ethtool_ops_vf Mengyuan Lou
@ 2026-07-01 10:01 ` Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S Mengyuan Lou
2 siblings, 0 replies; 6+ messages in thread
From: Mengyuan Lou @ 2026-07-01 10:01 UTC (permalink / raw)
To: netdev; +Cc: jiawenwu, duanqiangwen, horms, kuba, pabeni, Mengyuan Lou
Add support for set_coalesce in wx_ethtool_ops_vf, which
is used to set interrupt coalescing parameters.
Update wx_write_eitr_vf() to use the same interrupt
moderation encoding as PF devices, since PF and VF share
the same register layout. And remove the now-unused
WX_VXITR_MASK definition.
Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
---
drivers/net/ethernet/wangxun/libwx/wx_ethtool.c | 7 ++++++-
drivers/net/ethernet/wangxun/libwx/wx_vf.h | 1 -
drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c | 13 ++++++++++++-
3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index eae038df6875..22037f015ded 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -10,6 +10,7 @@
#include "wx_hw.h"
#include "wx_lib.h"
#include "wx_vf_common.h"
+#include "wx_vf_lib.h"
struct wx_stats {
char stat_string[ETH_GSTRING_LEN];
@@ -488,7 +489,10 @@ int wx_set_coalesce(struct net_device *netdev,
else
/* rx only or mixed */
q_vector->itr = rx_itr_param;
- wx_write_eitr(q_vector);
+ if (wx->pdev->is_virtfn)
+ wx_write_eitr_vf(q_vector);
+ else
+ wx_write_eitr(q_vector);
}
wx_update_rsc(wx);
@@ -845,6 +849,7 @@ static const struct ethtool_ops wx_ethtool_ops_vf = {
.set_ringparam = wx_set_ringparam_vf,
.get_msglevel = wx_get_msglevel,
.get_coalesce = wx_get_coalesce,
+ .set_coalesce = wx_set_coalesce,
.get_ts_info = ethtool_op_get_ts_info,
.get_link_ksettings = wx_get_link_ksettings_vf,
};
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf.h b/drivers/net/ethernet/wangxun/libwx/wx_vf.h
index eb6ca3fe4e97..b64a4de089f2 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf.h
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf.h
@@ -41,7 +41,6 @@
#define WX_VF_MAX_RX_QUEUES 4
#define WX_VXITR(i) (0x200 + (4 * (i))) /* i=[0,1] */
-#define WX_VXITR_MASK GENMASK(8, 0)
#define WX_VXITR_CNT_WDIS BIT(31)
#define WX_VXIVAR_MISC 0x260
#define WX_VXIVAR(i) (0x240 + (4 * (i))) /* i=[0,3] */
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
index aa8be036956c..7325b475ee10 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_lib.c
@@ -16,7 +16,18 @@ void wx_write_eitr_vf(struct wx_q_vector *q_vector)
int v_idx = q_vector->v_idx;
u32 itr_reg;
- itr_reg = q_vector->itr & WX_VXITR_MASK;
+ switch (wx->mac.type) {
+ case wx_mac_sp:
+ itr_reg = q_vector->itr & WX_SP_MAX_EITR;
+ break;
+ case wx_mac_aml:
+ case wx_mac_aml40:
+ itr_reg = (q_vector->itr >> 3) & WX_AML_MAX_EITR;
+ break;
+ default:
+ itr_reg = q_vector->itr & WX_EM_MAX_EITR;
+ break;
+ }
/* set the WDIS bit to not clear the timer bits and cause an
* immediate assertion of the interrupt
--
2.30.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S
2026-07-01 10:01 [PATCH net-next v6 0/3] net: libwx: improve VF ethtool support Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 1/3] net: libwx: add support for set_ringparam in wx_ethtool_ops_vf Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 2/3] net: libwx: add support for set_coalesce " Mengyuan Lou
@ 2026-07-01 10:01 ` Mengyuan Lou
2026-07-07 9:50 ` Paolo Abeni
2 siblings, 1 reply; 6+ messages in thread
From: Mengyuan Lou @ 2026-07-01 10:01 UTC (permalink / raw)
To: netdev; +Cc: jiawenwu, duanqiangwen, horms, kuba, pabeni, Mengyuan Lou
Add per-queue TX/RX packet and byte counters to the VF ethtool stats
table.
Convert WX_NUM_{RX,TX}_QUEUES macros to take explicit netdev parameter.
Then replace both WX_GLOBAL_STATS_LEN and WX_STATS_LEN macros with
type-safe static helpers wx_global_stats_len() and wx_stats_len().
The stats length is now dynamically determined based on whether the
device is a VF or PF via wx_stats_len().
Add ring access protection in wx_update_stats.
Signed-off-by: Mengyuan Lou <mengyuanlou@net-swift.com>
---
.../net/ethernet/wangxun/libwx/wx_ethtool.c | 71 +++++++++++++------
drivers/net/ethernet/wangxun/libwx/wx_hw.c | 8 +++
.../net/ethernet/wangxun/libwx/wx_vf_common.c | 2 +
3 files changed, 60 insertions(+), 21 deletions(-)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
index 22037f015ded..f72908630ea4 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
@@ -48,6 +48,10 @@ static const struct wx_stats wx_gstrings_stats[] = {
WX_STAT("rx_hwtstamp_cleared", rx_hwtstamp_cleared),
};
+static const struct wx_stats wx_gstrings_stats_vf[] = {
+ WX_STAT("non_eop_descs", non_eop_descs),
+};
+
static const struct wx_stats wx_gstrings_fdir_stats[] = {
WX_STAT("fdir_match", stats.fdirmatch),
WX_STAT("fdir_miss", stats.fdirmiss),
@@ -63,21 +67,33 @@ static const struct wx_stats wx_gstrings_rsc_stats[] = {
* used because we do not have a good way to get the max number of
* rx queues with CONFIG_RPS disabled.
*/
-#define WX_NUM_RX_QUEUES netdev->num_tx_queues
-#define WX_NUM_TX_QUEUES netdev->num_tx_queues
-
-#define WX_QUEUE_STATS_LEN ( \
- (WX_NUM_TX_QUEUES + WX_NUM_RX_QUEUES) * \
- (sizeof(struct wx_queue_stats) / sizeof(u64)))
-#define WX_GLOBAL_STATS_LEN ARRAY_SIZE(wx_gstrings_stats)
+#define WX_NUM_RX_QUEUES(netdev) ((netdev)->num_tx_queues)
+#define WX_NUM_TX_QUEUES(netdev) ((netdev)->num_tx_queues)
#define WX_FDIR_STATS_LEN ARRAY_SIZE(wx_gstrings_fdir_stats)
#define WX_RSC_STATS_LEN ARRAY_SIZE(wx_gstrings_rsc_stats)
-#define WX_STATS_LEN (WX_GLOBAL_STATS_LEN + WX_QUEUE_STATS_LEN)
+
+static unsigned int wx_global_stats_len(const struct wx *wx)
+{
+ return wx->pdev->is_virtfn ?
+ ARRAY_SIZE(wx_gstrings_stats_vf) : ARRAY_SIZE(wx_gstrings_stats);
+}
+
+static unsigned int wx_stats_len(const struct wx *wx)
+{
+ struct net_device *netdev = wx->netdev;
+ unsigned int queue_stats_len;
+
+ queue_stats_len = (WX_NUM_TX_QUEUES(netdev) +
+ WX_NUM_RX_QUEUES(netdev)) *
+ (sizeof(struct wx_queue_stats) / sizeof(u64));
+
+ return wx_global_stats_len(wx) + queue_stats_len;
+}
int wx_get_sset_count(struct net_device *netdev, int sset)
{
struct wx *wx = netdev_priv(netdev);
- int len = WX_STATS_LEN;
+ int len = wx_stats_len(wx);
switch (sset) {
case ETH_SS_STATS:
@@ -100,8 +116,11 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
switch (stringset) {
case ETH_SS_STATS:
- for (i = 0; i < WX_GLOBAL_STATS_LEN; i++)
- ethtool_puts(&p, wx_gstrings_stats[i].stat_string);
+ for (i = 0; i < wx_global_stats_len(wx); i++)
+ if (wx->pdev->is_virtfn)
+ ethtool_puts(&p, wx_gstrings_stats_vf[i].stat_string);
+ else
+ ethtool_puts(&p, wx_gstrings_stats[i].stat_string);
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) {
for (i = 0; i < WX_FDIR_STATS_LEN; i++)
ethtool_puts(&p, wx_gstrings_fdir_stats[i].stat_string);
@@ -114,7 +133,7 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
ethtool_sprintf(&p, "tx_queue_%u_packets", i);
ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
}
- for (i = 0; i < WX_NUM_RX_QUEUES; i++) {
+ for (i = 0; i < WX_NUM_RX_QUEUES(netdev); i++) {
ethtool_sprintf(&p, "rx_queue_%u_packets", i);
ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
}
@@ -134,10 +153,16 @@ void wx_get_ethtool_stats(struct net_device *netdev,
wx_update_stats(wx);
- for (i = 0; i < WX_GLOBAL_STATS_LEN; i++) {
- p = (char *)wx + wx_gstrings_stats[i].stat_offset;
- data[i] = (wx_gstrings_stats[i].sizeof_stat ==
- sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ for (i = 0; i < wx_global_stats_len(wx); i++) {
+ if (wx->pdev->is_virtfn) {
+ p = (char *)wx + wx_gstrings_stats_vf[i].stat_offset;
+ data[i] = (wx_gstrings_stats_vf[i].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ } else {
+ p = (char *)wx + wx_gstrings_stats[i].stat_offset;
+ data[i] = (wx_gstrings_stats[i].sizeof_stat ==
+ sizeof(u64)) ? *(u64 *)p : *(u32 *)p;
+ }
}
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags)) {
@@ -169,7 +194,7 @@ void wx_get_ethtool_stats(struct net_device *netdev,
} while (u64_stats_fetch_retry(&ring->syncp, start));
i += 2;
}
- for (j = 0; j < WX_NUM_RX_QUEUES; j++) {
+ for (j = 0; j < WX_NUM_RX_QUEUES(netdev); j++) {
ring = wx->rx_ring[j];
if (!ring) {
data[i++] = 0;
@@ -219,19 +244,20 @@ EXPORT_SYMBOL(wx_get_pause_stats);
void wx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info)
{
- unsigned int stats_len = WX_STATS_LEN;
struct wx *wx = netdev_priv(netdev);
+ unsigned int stats_len;
+ stats_len = wx_stats_len(wx);
if (test_bit(WX_FLAG_FDIR_CAPABLE, wx->flags))
stats_len += WX_FDIR_STATS_LEN;
strscpy(info->driver, wx->driver_name, sizeof(info->driver));
strscpy(info->fw_version, wx->eeprom_id, sizeof(info->fw_version));
strscpy(info->bus_info, pci_name(wx->pdev), sizeof(info->bus_info));
- if (wx->num_tx_queues <= WX_NUM_TX_QUEUES) {
+ if (wx->num_tx_queues <= WX_NUM_TX_QUEUES(netdev)) {
info->n_stats = stats_len -
- (WX_NUM_TX_QUEUES - wx->num_tx_queues) *
- (sizeof(struct wx_queue_stats) / sizeof(u64)) * 2;
+ (WX_NUM_TX_QUEUES(netdev) - wx->num_tx_queues) *
+ (sizeof(struct wx_queue_stats) / sizeof(u64)) * 2;
} else {
info->n_stats = stats_len;
}
@@ -852,6 +878,9 @@ static const struct ethtool_ops wx_ethtool_ops_vf = {
.set_coalesce = wx_set_coalesce,
.get_ts_info = ethtool_op_get_ts_info,
.get_link_ksettings = wx_get_link_ksettings_vf,
+ .get_sset_count = wx_get_sset_count,
+ .get_strings = wx_get_strings,
+ .get_ethtool_stats = wx_get_ethtool_stats,
};
void wx_set_ethtool_ops_vf(struct net_device *netdev)
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
index 260e14d5d541..349afbe60132 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
@@ -2886,6 +2886,8 @@ void wx_update_stats(struct wx *wx)
for (i = 0; i < wx->num_rx_queues; i++) {
struct wx_ring *rx_ring = wx->rx_ring[i];
+ if (!rx_ring)
+ continue;
non_eop_descs += rx_ring->rx_stats.non_eop_descs;
alloc_rx_buff_failed += rx_ring->rx_stats.alloc_rx_buff_failed;
hw_csum_rx_good += rx_ring->rx_stats.csum_good_cnt;
@@ -2911,12 +2913,17 @@ void wx_update_stats(struct wx *wx)
for (i = 0; i < wx->num_tx_queues; i++) {
struct wx_ring *tx_ring = wx->tx_ring[i];
+ if (!tx_ring)
+ continue;
restart_queue += tx_ring->tx_stats.restart_queue;
tx_busy += tx_ring->tx_stats.tx_busy;
}
wx->restart_queue = restart_queue;
wx->tx_busy = tx_busy;
+ if (wx->pdev->is_virtfn)
+ goto skip_hw_stats;
+
wx_update_xoff_rx_lfc(wx);
hwstats->gprc += rd32(wx, WX_RDM_PKT_CNT);
@@ -2956,6 +2963,7 @@ void wx_update_stats(struct wx *wx)
hwstats->qmprc += rd32_wrap(wx, WX_PX_MPRC(i),
&wx->last_stats.qmprc[i]);
+skip_hw_stats:
spin_unlock(&wx->hw_stats_lock);
}
EXPORT_SYMBOL(wx_update_stats);
diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
index 26de78e9a69e..1d84fe35d7d9 100644
--- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
+++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
@@ -5,6 +5,7 @@
#include <linux/pci.h>
#include "wx_type.h"
+#include "wx_hw.h"
#include "wx_mbx.h"
#include "wx_lib.h"
#include "wx_vf.h"
@@ -409,6 +410,7 @@ static void wxvf_service_task(struct work_struct *work)
wxvf_link_config_subtask(wx);
wxvf_reset_subtask(wx);
+ wx_update_stats(wx);
wx_service_event_complete(wx);
}
--
2.30.1
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S
2026-07-01 10:01 ` [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S Mengyuan Lou
@ 2026-07-07 9:50 ` Paolo Abeni
2026-07-08 10:23 ` mengyuanlou
0 siblings, 1 reply; 6+ messages in thread
From: Paolo Abeni @ 2026-07-07 9:50 UTC (permalink / raw)
To: mengyuanlou; +Cc: netdev, jiawenwu, duanqiangwen, horms, kuba, pabeni
From: AI Reviewer <ai@example.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
Now I have enough information to produce the report.
commit e59c58bb080afb34cac34c91b8c320a1f05134e6
Author: Mengyuan Lou <mengyuanlou@net-swift.com>
net: libwx: support vf per-queue statistics via ethtool -S
The patch wires wx_get_sset_count, wx_get_strings, and wx_get_ethtool_stats
into wx_ethtool_ops_vf, adds a small vf-only global stats table, and starts
calling wx_update_stats() from wxvf_service_task. It also adds NULL checks
for wx->rx_ring[i] / wx->tx_ring[i] inside wx_update_stats.
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> index 22037f015ded..f72908630ea4 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
[ ... ]
> @@ -114,7 +133,7 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
> ethtool_sprintf(&p, "tx_queue_%u_packets", i);
> ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
> }
> - for (i = 0; i < WX_NUM_RX_QUEUES; i++) {
> + for (i = 0; i < WX_NUM_RX_QUEUES(netdev); i++) {
> ethtool_sprintf(&p, "rx_queue_%u_packets", i);
> ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
> }
[ ... ]
> @@ -852,6 +878,9 @@ static const struct ethtool_ops wx_ethtool_ops_vf = {
> .set_coalesce = wx_set_coalesce,
> .get_ts_info = ethtool_op_get_ts_info,
> .get_link_ksettings = wx_get_link_ksettings_vf,
> + .get_sset_count = wx_get_sset_count,
> + .get_strings = wx_get_strings,
> + .get_ethtool_stats = wx_get_ethtool_stats,
> };
[Medium]
Before this change wx_ethtool_ops_vf had no get_sset_count / get_strings /
get_ethtool_stats callbacks, so the VF exposed no ethtool -S output at all.
Wiring these in also picks up the shared per-queue string loop that emits
tx_queue_%u_packets / tx_queue_%u_bytes and rx_queue_%u_packets /
rx_queue_%u_bytes.
Per-queue TX/RX packet and byte counters are a category that already has a
standard uAPI (netdev_stat_ops with queue_stats_rx / queue_stats_tx,
surfaced by netlink and ynl). Documentation/networking/statistics.rst and
the ethtool -S guidance state that new counters covering values that have a
standard uAPI should not be added to ethtool -S even when the driver does
not yet implement the standard interface.
The wangxun tree does not implement netdev_stat_ops (no
get_base_stats / get_queue_stats_rx / get_queue_stats_tx callbacks are
present under drivers/net/ethernet/wangxun/).
Would it be preferable to expose the per-queue counters on the VF via
netdev_stat_ops instead of adding them to ethtool -S here?
> void wx_set_ethtool_ops_vf(struct net_device *netdev)
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
> index 260e14d5d541..349afbe60132 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
> @@ -2886,6 +2886,8 @@ void wx_update_stats(struct wx *wx)
> for (i = 0; i < wx->num_rx_queues; i++) {
> struct wx_ring *rx_ring = wx->rx_ring[i];
>
> + if (!rx_ring)
> + continue;
> non_eop_descs += rx_ring->rx_stats.non_eop_descs;
[ ... ]
> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> index 26de78e9a69e..1d84fe35d7d9 100644
> --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
> @@ -409,6 +410,7 @@ static void wxvf_service_task(struct work_struct *work)
>
> wxvf_link_config_subtask(wx);
> wxvf_reset_subtask(wx);
> + wx_update_stats(wx);
> wx_service_event_complete(wx);
> }
[High]
Can this call race with VF PCI suspend and shutdown to produce a
use-after-free on wx->rx_ring[i] / wx->tx_ring[i]?
Both ngbevf and txgbevf use wxvf_suspend as their PM suspend callback and
wxvf_shutdown (which calls wxvf_suspend) as their pci_driver ->shutdown
callback:
static DEFINE_SIMPLE_DEV_PM_OPS(ngbevf_pm_ops, wxvf_suspend, wxvf_resume);
...
.shutdown = wxvf_shutdown,
wxvf_suspend() in wx_vf_common.c does:
int wxvf_suspend(struct device *dev_d)
{
struct pci_dev *pdev = to_pci_dev(dev_d);
struct wx *wx = pci_get_drvdata(pdev);
netif_device_detach(wx->netdev);
wx_clear_interrupt_scheme(wx);
pci_disable_device(pdev);
return 0;
}
wx_clear_interrupt_scheme() ends up freeing the q_vectors (via
kfree_rcu), and the ring allocations are embedded in the q_vector, so
wx->rx_ring[i] / wx->tx_ring[i] end up pointing at memory that will be
freed after an RCU grace period.
wxvf_suspend / wxvf_shutdown do not call timer_shutdown_sync on
wx->service_timer or cancel_work_sync on wx->service_task, and they do
not set WX_STATE_DOWN.
With this hunk in place, the service_task now walks wx->rx_ring[] and
wx->tx_ring[]:
void wx_update_stats(struct wx *wx)
{
...
for (i = 0; i < wx->num_rx_queues; i++) {
struct wx_ring *rx_ring = wx->rx_ring[i];
if (!rx_ring)
continue;
non_eop_descs += rx_ring->rx_stats.non_eop_descs;
...
}
...
for (i = 0; i < wx->num_tx_queues; i++) {
struct wx_ring *tx_ring = wx->tx_ring[i];
if (!tx_ring)
continue;
restart_queue += tx_ring->tx_stats.restart_queue;
...
}
If the service_task latches a non-NULL rx_ring / tx_ring pointer just
before wx_clear_interrupt_scheme runs, the added NULL check does not
help, and the subsequent ->rx_stats / ->tx_stats reads touch memory
that has been kfree_rcu'd. wxvf_remove() covers this by calling
timer_shutdown_sync + cancel_work_sync before wx_clear_interrupt_scheme,
but wxvf_suspend / wxvf_shutdown do not.
Should wxvf_suspend (and hence wxvf_shutdown) quiesce the service_timer
and service_task, or set WX_STATE_DOWN, before wx_clear_interrupt_scheme
now that wx_update_stats is on the service_task's call path?
--
This is an AI-generated review.
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S
2026-07-07 9:50 ` Paolo Abeni
@ 2026-07-08 10:23 ` mengyuanlou
0 siblings, 0 replies; 6+ messages in thread
From: mengyuanlou @ 2026-07-08 10:23 UTC (permalink / raw)
To: Paolo Abeni; +Cc: netdev, jiawenwu, duanqiangwen, horms, kuba
> 2026年7月7日 17:50,Paolo Abeni <pabeni@redhat.com> 写道:
>
> From: AI Reviewer <ai@example.com>
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://netdev-ai.bots.linux.dev/sashiko/
> ---
> Now I have enough information to produce the report.
>
> commit e59c58bb080afb34cac34c91b8c320a1f05134e6
> Author: Mengyuan Lou <mengyuanlou@net-swift.com>
>
> net: libwx: support vf per-queue statistics via ethtool -S
>
> The patch wires wx_get_sset_count, wx_get_strings, and wx_get_ethtool_stats
> into wx_ethtool_ops_vf, adds a small vf-only global stats table, and starts
> calling wx_update_stats() from wxvf_service_task. It also adds NULL checks
> for wx->rx_ring[i] / wx->tx_ring[i] inside wx_update_stats.
>
>> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
>> index 22037f015ded..f72908630ea4 100644
>> --- a/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
>> +++ b/drivers/net/ethernet/wangxun/libwx/wx_ethtool.c
>
> [ ... ]
>
>> @@ -114,7 +133,7 @@ void wx_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
>> ethtool_sprintf(&p, "tx_queue_%u_packets", i);
>> ethtool_sprintf(&p, "tx_queue_%u_bytes", i);
>> }
>> - for (i = 0; i < WX_NUM_RX_QUEUES; i++) {
>> + for (i = 0; i < WX_NUM_RX_QUEUES(netdev); i++) {
>> ethtool_sprintf(&p, "rx_queue_%u_packets", i);
>> ethtool_sprintf(&p, "rx_queue_%u_bytes", i);
>> }
>
> [ ... ]
>
>> @@ -852,6 +878,9 @@ static const struct ethtool_ops wx_ethtool_ops_vf = {
>> .set_coalesce = wx_set_coalesce,
>> .get_ts_info = ethtool_op_get_ts_info,
>> .get_link_ksettings = wx_get_link_ksettings_vf,
>> + .get_sset_count = wx_get_sset_count,
>> + .get_strings = wx_get_strings,
>> + .get_ethtool_stats = wx_get_ethtool_stats,
>> };
>
> [Medium]
> Before this change wx_ethtool_ops_vf had no get_sset_count / get_strings /
> get_ethtool_stats callbacks, so the VF exposed no ethtool -S output at all.
> Wiring these in also picks up the shared per-queue string loop that emits
> tx_queue_%u_packets / tx_queue_%u_bytes and rx_queue_%u_packets /
> rx_queue_%u_bytes.
>
> Per-queue TX/RX packet and byte counters are a category that already has a
> standard uAPI (netdev_stat_ops with queue_stats_rx / queue_stats_tx,
> surfaced by netlink and ynl). Documentation/networking/statistics.rst and
> the ethtool -S guidance state that new counters covering values that have a
> standard uAPI should not be added to ethtool -S even when the driver does
> not yet implement the standard interface.
>
> The wangxun tree does not implement netdev_stat_ops (no
> get_base_stats / get_queue_stats_rx / get_queue_stats_tx callbacks are
> present under drivers/net/ethernet/wangxun/).
Ok,I will remove patch3(support vf per-queue statistics via ethtool -S).
>
> Would it be preferable to expose the per-queue counters on the VF via
> netdev_stat_ops instead of adding them to ethtool -S here?
netdev_stat_ops support is deferred to a follow-up patch; this one only extends ethtool ops.
>
>> void wx_set_ethtool_ops_vf(struct net_device *netdev)
>> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_hw.c b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
>> index 260e14d5d541..349afbe60132 100644
>> --- a/drivers/net/ethernet/wangxun/libwx/wx_hw.c
>> +++ b/drivers/net/ethernet/wangxun/libwx/wx_hw.c
>> @@ -2886,6 +2886,8 @@ void wx_update_stats(struct wx *wx)
>> for (i = 0; i < wx->num_rx_queues; i++) {
>> struct wx_ring *rx_ring = wx->rx_ring[i];
>>
>> + if (!rx_ring)
>> + continue;
>> non_eop_descs += rx_ring->rx_stats.non_eop_descs;
>
> [ ... ]
>
>> diff --git a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
>> index 26de78e9a69e..1d84fe35d7d9 100644
>> --- a/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
>> +++ b/drivers/net/ethernet/wangxun/libwx/wx_vf_common.c
>> @@ -409,6 +410,7 @@ static void wxvf_service_task(struct work_struct *work)
>>
>> wxvf_link_config_subtask(wx);
>> wxvf_reset_subtask(wx);
>> + wx_update_stats(wx);
>> wx_service_event_complete(wx);
>> }
>
> [High]
> Can this call race with VF PCI suspend and shutdown to produce a
> use-after-free on wx->rx_ring[i] / wx->tx_ring[i]?
>
> Both ngbevf and txgbevf use wxvf_suspend as their PM suspend callback and
> wxvf_shutdown (which calls wxvf_suspend) as their pci_driver ->shutdown
> callback:
>
> static DEFINE_SIMPLE_DEV_PM_OPS(ngbevf_pm_ops, wxvf_suspend, wxvf_resume);
> ...
> .shutdown = wxvf_shutdown,
>
> wxvf_suspend() in wx_vf_common.c does:
>
> int wxvf_suspend(struct device *dev_d)
> {
> struct pci_dev *pdev = to_pci_dev(dev_d);
> struct wx *wx = pci_get_drvdata(pdev);
>
> netif_device_detach(wx->netdev);
> wx_clear_interrupt_scheme(wx);
> pci_disable_device(pdev);
>
> return 0;
> }
>
> wx_clear_interrupt_scheme() ends up freeing the q_vectors (via
> kfree_rcu), and the ring allocations are embedded in the q_vector, so
> wx->rx_ring[i] / wx->tx_ring[i] end up pointing at memory that will be
> freed after an RCU grace period.
>
> wxvf_suspend / wxvf_shutdown do not call timer_shutdown_sync on
> wx->service_timer or cancel_work_sync on wx->service_task, and they do
> not set WX_STATE_DOWN.
>
> With this hunk in place, the service_task now walks wx->rx_ring[] and
> wx->tx_ring[]:
>
> void wx_update_stats(struct wx *wx)
> {
> ...
> for (i = 0; i < wx->num_rx_queues; i++) {
> struct wx_ring *rx_ring = wx->rx_ring[i];
>
> if (!rx_ring)
> continue;
> non_eop_descs += rx_ring->rx_stats.non_eop_descs;
> ...
> }
> ...
> for (i = 0; i < wx->num_tx_queues; i++) {
> struct wx_ring *tx_ring = wx->tx_ring[i];
>
> if (!tx_ring)
> continue;
> restart_queue += tx_ring->tx_stats.restart_queue;
> ...
> }
>
> If the service_task latches a non-NULL rx_ring / tx_ring pointer just
> before wx_clear_interrupt_scheme runs, the added NULL check does not
> help, and the subsequent ->rx_stats / ->tx_stats reads touch memory
> that has been kfree_rcu'd. wxvf_remove() covers this by calling
> timer_shutdown_sync + cancel_work_sync before wx_clear_interrupt_scheme,
> but wxvf_suspend / wxvf_shutdown do not.
>
> Should wxvf_suspend (and hence wxvf_shutdown) quiesce the service_timer
> and service_task, or set WX_STATE_DOWN, before wx_clear_interrupt_scheme
> now that wx_update_stats is on the service_task's call path?
> --
> This is an AI-generated review.
>
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-07-08 10:24 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-01 10:01 [PATCH net-next v6 0/3] net: libwx: improve VF ethtool support Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 1/3] net: libwx: add support for set_ringparam in wx_ethtool_ops_vf Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 2/3] net: libwx: add support for set_coalesce " Mengyuan Lou
2026-07-01 10:01 ` [PATCH net-next v6 3/3] net: libwx: support vf per-queue statistics via ethtool -S Mengyuan Lou
2026-07-07 9:50 ` Paolo Abeni
2026-07-08 10:23 ` mengyuanlou
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox