Skip to contentSkip to conversation

net: mana: Refactor GF stats handling and add rx_missed_errors counter

PATCH Superseded Erni Sri Satya Vennela 2 participants 7 messages Oct 29, 2025 – Nov 11, 2025

Subsystems (5)

v2 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

v2 cover net: mana: Refactor GF stats handling and add rx_missed_errors counter

Restructure mana_query_gf_stats() to operate on the per-VF mana_context, instead of per-port statistics. Introduce mana_ethtool_hc_stats to isolate hardware counter statistics and update the
"ethtool -S <interface>" output to expose all relevant counters while preserving backward compatibility.

Add support for the standard rx_missed_errors counter by mapping it to the hardware's hc_rx_discards_no_wqe metric. Introduce a dedicated workqueue that refreshes statistics every 2 seconds, ensuring timely and consistent updates of hardware counters.

---
Changes in v2:
* Update commit message.
* Stop rescheduling workqueue only when HWC timeout is observed.
* Introduce new variable in mana_context for detecting HWC timeout.
* Warn once in mana_get_stat64 when HWC timeout is observed.
---
Erni Sri Satya Vennela (2):
net: mana: Refactor GF stats to use global mana_context
net: mana: Add standard counter rx_missed_errors

 drivers/net/ethernet/microsoft/mana/mana_en.c | 111 ++++++++++++------
 .../ethernet/microsoft/mana/mana_ethtool.c    |  85 ++++++++------
 include/net/mana/gdma.h                       |   6 +-
 include/net/mana/mana.h                       |  18 ++-
 4 files changed, 140 insertions(+), 80 deletions(-)
Signature · 2 lines
2.34.1
--
#1 lore raw

PATCH v2 1/2 net: mana: Refactor GF stats to use global mana_context

Refactor mana_query_gf_stats() to use mana_context instead of per-port, enabling single query for all VFs. Isolate hardware counter stats by introducing mana_ethtool_hc_stats in mana_context and update the code to ensure all stats are properly reported via ethtool -S <interface>, maintaining consistency with previous behavior.

---
Changes in v2:
* No change.

---
 drivers/net/ethernet/microsoft/mana/mana_en.c | 67 ++++++++-------
 .../ethernet/microsoft/mana/mana_ethtool.c    | 85 ++++++++++---------
 include/net/mana/mana.h                       | 14 +--
 3 files changed, 90 insertions(+), 76 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +34 -33
index 0142fd98392c..009e869ef296 100644
@@ -2769,11 +2769,12 @@ int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx,
 	return 0;
 }
 
-void mana_query_gf_stats(struct mana_port_context *apc)
+void mana_query_gf_stats(struct mana_context *ac)
 {
 	struct mana_query_gf_stat_resp resp = {};
 	struct mana_query_gf_stat_req req = {};
-	struct net_device *ndev = apc->ndev;
+	struct gdma_context *gc = ac->gdma_dev->gdma_context;
+	struct device *dev = gc->dev;
 	int err;
 
 	mana_gd_init_req_hdr(&req.hdr, MANA_QUERY_GF_STAT,
@@ -2807,52 +2808,52 @@ void mana_query_gf_stats(struct mana_port_context *apc)
 			STATISTICS_FLAGS_HC_TX_BCAST_BYTES |
 			STATISTICS_FLAGS_TX_ERRORS_GDMA_ERROR;
 
-	err = mana_send_request(apc->ac, &req, sizeof(req), &resp,
+	err = mana_send_request(ac, &req, sizeof(req), &resp,
 				sizeof(resp));
 	if (err) {
-		netdev_err(ndev, "Failed to query GF stats: %d\n", err);
+		dev_err(dev, "Failed to query GF stats: %d\n", err);
 		return;
 	}
 	err = mana_verify_resp_hdr(&resp.hdr, MANA_QUERY_GF_STAT,
 				   sizeof(resp));
 	if (err || resp.hdr.status) {
-		netdev_err(ndev, "Failed to query GF stats: %d, 0x%x\n", err,
-			   resp.hdr.status);
+		dev_err(dev, "Failed to query GF stats: %d, 0x%x\n", err,
+			resp.hdr.status);
 		return;
 	}
 
-	apc->eth_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
-	apc->eth_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled;
-	apc->eth_stats.hc_rx_bytes = resp.hc_rx_bytes;
-	apc->eth_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts;
-	apc->eth_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes;
-	apc->eth_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts;
-	apc->eth_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes;
-	apc->eth_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts;
-	apc->eth_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes;
-	apc->eth_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled;
-	apc->eth_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled;
-	apc->eth_stats.hc_tx_err_inval_vportoffset_pkt =
+	ac->hc_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
+	ac->hc_stats.hc_rx_err_vport_disabled = resp.rx_err_vport_disabled;
+	ac->hc_stats.hc_rx_bytes = resp.hc_rx_bytes;
+	ac->hc_stats.hc_rx_ucast_pkts = resp.hc_rx_ucast_pkts;
+	ac->hc_stats.hc_rx_ucast_bytes = resp.hc_rx_ucast_bytes;
+	ac->hc_stats.hc_rx_bcast_pkts = resp.hc_rx_bcast_pkts;
+	ac->hc_stats.hc_rx_bcast_bytes = resp.hc_rx_bcast_bytes;
+	ac->hc_stats.hc_rx_mcast_pkts = resp.hc_rx_mcast_pkts;
+	ac->hc_stats.hc_rx_mcast_bytes = resp.hc_rx_mcast_bytes;
+	ac->hc_stats.hc_tx_err_gf_disabled = resp.tx_err_gf_disabled;
+	ac->hc_stats.hc_tx_err_vport_disabled = resp.tx_err_vport_disabled;
+	ac->hc_stats.hc_tx_err_inval_vportoffset_pkt =
 					     resp.tx_err_inval_vport_offset_pkt;
-	apc->eth_stats.hc_tx_err_vlan_enforcement =
+	ac->hc_stats.hc_tx_err_vlan_enforcement =
 					     resp.tx_err_vlan_enforcement;
-	apc->eth_stats.hc_tx_err_eth_type_enforcement =
+	ac->hc_stats.hc_tx_err_eth_type_enforcement =
 					     resp.tx_err_ethtype_enforcement;
-	apc->eth_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement;
-	apc->eth_stats.hc_tx_err_sqpdid_enforcement =
+	ac->hc_stats.hc_tx_err_sa_enforcement = resp.tx_err_SA_enforcement;
+	ac->hc_stats.hc_tx_err_sqpdid_enforcement =
 					     resp.tx_err_SQPDID_enforcement;
-	apc->eth_stats.hc_tx_err_cqpdid_enforcement =
+	ac->hc_stats.hc_tx_err_cqpdid_enforcement =
 					     resp.tx_err_CQPDID_enforcement;
-	apc->eth_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation;
-	apc->eth_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob;
-	apc->eth_stats.hc_tx_bytes = resp.hc_tx_bytes;
-	apc->eth_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts;
-	apc->eth_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes;
-	apc->eth_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts;
-	apc->eth_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes;
-	apc->eth_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts;
-	apc->eth_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes;
-	apc->eth_stats.hc_tx_err_gdma = resp.tx_err_gdma;
+	ac->hc_stats.hc_tx_err_mtu_violation = resp.tx_err_mtu_violation;
+	ac->hc_stats.hc_tx_err_inval_oob = resp.tx_err_inval_oob;
+	ac->hc_stats.hc_tx_bytes = resp.hc_tx_bytes;
+	ac->hc_stats.hc_tx_ucast_pkts = resp.hc_tx_ucast_pkts;
+	ac->hc_stats.hc_tx_ucast_bytes = resp.hc_tx_ucast_bytes;
+	ac->hc_stats.hc_tx_bcast_pkts = resp.hc_tx_bcast_pkts;
+	ac->hc_stats.hc_tx_bcast_bytes = resp.hc_tx_bcast_bytes;
+	ac->hc_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts;
+	ac->hc_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes;
+	ac->hc_stats.hc_tx_err_gdma = resp.tx_err_gdma;
 }
 
 void mana_query_phy_stats(struct mana_port_context *apc)
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +47 -38

149 lines not on this page · raw message

include/net/mana/mana.h +9 -5
index 0921485565c0..519c4384c51f 100644
@@ -375,6 +375,13 @@ struct mana_tx_qp {
 struct mana_ethtool_stats {
 	u64 stop_queue;
 	u64 wake_queue;
+	u64 tx_cqe_err;
+	u64 tx_cqe_unknown_type;
+	u64 rx_coalesced_err;
+	u64 rx_cqe_unknown_type;
+};
+
+struct mana_ethtool_hc_stats {
 	u64 hc_rx_discards_no_wqe;
 	u64 hc_rx_err_vport_disabled;
 	u64 hc_rx_bytes;
@@ -402,10 +409,6 @@ struct mana_ethtool_stats {
 	u64 hc_tx_mcast_pkts;
 	u64 hc_tx_mcast_bytes;
 	u64 hc_tx_err_gdma;
-	u64 tx_cqe_err;
-	u64 tx_cqe_unknown_type;
-	u64 rx_coalesced_err;
-	u64 rx_cqe_unknown_type;
 };
 
 struct mana_ethtool_phy_stats {
@@ -473,6 +476,7 @@ struct mana_context {
 	u16 num_ports;
 	u8 bm_hostmode;
 
+	struct mana_ethtool_hc_stats hc_stats;
 	struct mana_eq *eqs;
 	struct dentry *mana_eqs_debugfs;
 
@@ -573,7 +577,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
 struct bpf_prog *mana_xdp_get(struct mana_port_context *apc);
 void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog);
 int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
-void mana_query_gf_stats(struct mana_port_context *apc);
+void mana_query_gf_stats(struct mana_context *ac);
 int mana_query_link_cfg(struct mana_port_context *apc);
 int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed,
 		      int enable_clamping);
Signature · 1 line
2.34.1
maintainer
#2 lore raw

Re: [PATCH net-next v2 1/2] net: mana: Refactor GF stats to use global mana_context

Quoted 2 lines · On Wed, 29 Oct 2025 03:37:51 -0700 Erni Sri Satya Vennela wrote:

Refactor mana_query_gf_stats() to use mana_context instead of per-port, enabling single query for all VFs.

What does "single query for all VFs" mean?
All types? All within the host?

Coincidentally I don't know what HC and GF stand for. Please explain things in more detail, all atypical acronyms (for *Linux* networking).

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx, · line 2776

Quoted 12 lines

Isolate hardware counter stats by introducing mana_ethtool_hc_stats in mana_context and update the code to ensure all stats are properly reported via ethtool -S <interface>, maintaining consistency with previous behavior.

drivers/net/ethernet/microsoft/mana/mana_en.c +2 -2
-void mana_query_gf_stats(struct mana_port_context *apc)
+void mana_query_gf_stats(struct mana_context *ac)
 {
 	struct mana_query_gf_stat_resp resp = {};
 	struct mana_query_gf_stat_req req = {};
-	struct net_device *ndev = apc->ndev;
+	struct gdma_context *gc = ac->gdma_dev->gdma_context;

reverse xmas tree, please

Quoted 2 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
+	struct device *dev = gc->dev;
 	int err;
#3 lore raw

Re: [PATCH net-next v2 1/2] net: mana: Refactor GF stats to use global mana_context

Quoted 6 lines · Jakub Kicinski
Quoted 2 lines · On Wed, 29 Oct 2025 03:37:51 -0700 Erni Sri Satya Vennela wrote:

Refactor mana_query_gf_stats() to use mana_context instead of per-port, enabling single query for all VFs.

What does "single query for all VFs" mean?
All types? All within the host?

Considering the case where there are multiple ports for a single VF, the details of the Hardware Counters (HC) requested from the hardware is same across all ports. That is why we have moved the those stats from mana_port_context (port) to mana_context (VF). I will reframe the commit message for more readability.

Quoted 5 lines

Coincidentally I don't know what HC and GF stand for. Please explain things in more detail, all atypical acronyms (for *Linux* networking).

HC stands for Hardware Counters and MANA_QUERY_GF_STAT is a hardware command code for GDMA tx LSO packets and bytes count. I will mention it clearly in the next version.

Quoted 5 lines
Quoted 4 lines

Isolate hardware counter stats by introducing mana_ethtool_hc_stats in mana_context and update the code to ensure all stats are properly reported via ethtool -S <interface>, maintaining consistency with previous behavior.

Quoted 10 lines

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx, · line 2776

Quoted 7 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -2
-void mana_query_gf_stats(struct mana_port_context *apc)
+void mana_query_gf_stats(struct mana_context *ac)
 {
 	struct mana_query_gf_stat_resp resp = {};
 	struct mana_query_gf_stat_req req = {};
-	struct net_device *ndev = apc->ndev;
+	struct gdma_context *gc = ac->gdma_dev->gdma_context;

reverse xmas tree, please

Thanks for pointing out. I will make this change in next version.

Quoted 2 lines
Quoted 2 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
+	struct device *dev = gc->dev;
 	int err;
#4 lore raw

PATCH v2 2/2 net: mana: Add standard counter rx_missed_errors

In reply to Erni Sri Satya Vennela

Report standard counter stats->rx_missed_errors
using hc_rx_discards_no_wqe from the hardware.

Add a dedicated workqueue to periodically run mana_query_gf_stats every 2 seconds to get the latest info in eth_stats and define a driver capability flag to notify hardware of the periodic queries.

To avoid repeated failures and log flooding, the workqueue is not rescheduled if mana_query_gf_stats fails on HWC timeout error and the stats are reset to 0. Other errors are transient which will not need a VF reset for recovery.

---
Changes in v2:
* Update commit message.
* Stop rescheduling workqueue only when HWC timeout is observed.
* Introduce new variable in mana_context for detecting HWC timeout.
* Warn once in mana_get_stat64 when HWC timeout is observed.

---
 drivers/net/ethernet/microsoft/mana/mana_en.c | 46 +++++++++++++++++--
 .../ethernet/microsoft/mana/mana_ethtool.c    |  2 -
 include/net/mana/gdma.h                       |  6 ++-
 include/net/mana/mana.h                       |  6 ++-
 4 files changed, 53 insertions(+), 7 deletions(-)
drivers/net/ethernet/microsoft/mana/mana_en.c +43 -3
index 009e869ef296..48df44889f05 100644
@@ -494,6 +494,11 @@ static void mana_get_stats64(struct net_device *ndev,
 
 	netdev_stats_to_stats64(st, &ndev->stats);
 
+	if (apc->ac->hwc_timeout_occurred)
+		netdev_warn_once(ndev, "HWC timeout occurred\n");
+
+	st->rx_missed_errors = apc->ac->hc_stats.hc_rx_discards_no_wqe;
+
 	for (q = 0; q < num_queues; q++) {
 		rx_stats = &apc->rxqs[q]->stats;
 
@@ -2769,7 +2774,7 @@ int mana_config_rss(struct mana_port_context *apc, enum TRI_STATE rx,
 	return 0;
 }
 
-void mana_query_gf_stats(struct mana_context *ac)
+int mana_query_gf_stats(struct mana_context *ac)
 {
 	struct mana_query_gf_stat_resp resp = {};
 	struct mana_query_gf_stat_req req = {};
@@ -2812,14 +2817,14 @@ void mana_query_gf_stats(struct mana_context *ac)
 				sizeof(resp));
 	if (err) {
 		dev_err(dev, "Failed to query GF stats: %d\n", err);
-		return;
+		return err;
 	}
 	err = mana_verify_resp_hdr(&resp.hdr, MANA_QUERY_GF_STAT,
 				   sizeof(resp));
 	if (err || resp.hdr.status) {
 		dev_err(dev, "Failed to query GF stats: %d, 0x%x\n", err,
 			resp.hdr.status);
-		return;
+		return err;
 	}
 
 	ac->hc_stats.hc_rx_discards_no_wqe = resp.rx_discards_nowqe;
@@ -2854,6 +2859,8 @@ void mana_query_gf_stats(struct mana_context *ac)
 	ac->hc_stats.hc_tx_mcast_pkts = resp.hc_tx_mcast_pkts;
 	ac->hc_stats.hc_tx_mcast_bytes = resp.hc_tx_mcast_bytes;
 	ac->hc_stats.hc_tx_err_gdma = resp.tx_err_gdma;
+
+	return 0;
 }
 
 void mana_query_phy_stats(struct mana_port_context *apc)
@@ -3390,6 +3397,24 @@ int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type even
 	return 0;
 }
 
+#define MANA_GF_STATS_PERIOD (2 * HZ)
+
+static void mana_gf_stats_work_handler(struct work_struct *work)
+{
+	struct mana_context *ac =
+		container_of(to_delayed_work(work), struct mana_context, gf_stats_work);
+	int err;
+
+	err = mana_query_gf_stats(ac);
+	if (err == -ETIMEDOUT) {
+		/* HWC timeout detected - reset stats and stop rescheduling */
+		ac->hwc_timeout_occurred = true;
+		memset(&ac->hc_stats, 0, sizeof(ac->hc_stats));
+		return;
+	}
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+}
+
 int mana_probe(struct gdma_dev *gd, bool resuming)
 {
 	struct gdma_context *gc = gd->gdma_context;
@@ -3478,6 +3503,15 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
 	}
 
 	err = add_adev(gd, "eth");
+	ac->gf_stats_wq = create_singlethread_workqueue("mana_gf_stats");
+	if (!ac->gf_stats_wq) {
+		err = -ENOMEM;
+		goto out;
+	}
+
+	INIT_DELAYED_WORK(&ac->gf_stats_work, mana_gf_stats_work_handler);
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+
 out:
 	if (err) {
 		mana_remove(gd, false);
@@ -3501,6 +3535,12 @@ void mana_remove(struct gdma_dev *gd, bool suspending)
 	int err;
 	int i;
 
+	if (ac->gf_stats_wq) {
+		cancel_delayed_work_sync(&ac->gf_stats_work);
+		destroy_workqueue(ac->gf_stats_wq);
+		ac->gf_stats_wq = NULL;
+	}
+
 	/* adev currently doesn't support suspending, always remove it */
 	if (gd->adev)
 		remove_adev(gd);
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +0 -2
index 3dfd96146424..99e811208683 100644
@@ -213,8 +213,6 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
 
 	if (!apc->port_is_up)
 		return;
-	/* we call mana function to update stats from GDMA */
-	mana_query_gf_stats(apc->ac);
 
 	/* We call this mana function to get the phy stats from GDMA and includes
 	 * aggregate tx/rx drop counters, Per-TC(Traffic Channel) tx/rx and pause
include/net/mana/gdma.h +5 -1
index 57df78cfbf82..88a81fb164a0 100644
@@ -591,6 +591,9 @@ enum {
 /* Driver can self reset on FPGA Reconfig EQE notification */
 #define GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE BIT(17)
 
+/* Driver can send HWC periodically to query stats */
+#define GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY BIT(21)
+
 #define GDMA_DRV_CAP_FLAGS1 \
 	(GDMA_DRV_CAP_FLAG_1_EQ_SHARING_MULTI_VPORT | \
 	 GDMA_DRV_CAP_FLAG_1_NAPI_WKDONE_FIX | \
@@ -599,7 +602,8 @@ enum {
 	 GDMA_DRV_CAP_FLAG_1_DEV_LIST_HOLES_SUP | \
 	 GDMA_DRV_CAP_FLAG_1_DYNAMIC_IRQ_ALLOC_SUPPORT | \
 	 GDMA_DRV_CAP_FLAG_1_SELF_RESET_ON_EQE | \
-	 GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE)
+	 GDMA_DRV_CAP_FLAG_1_HANDLE_RECONFIG_EQE | \
+	 GDMA_DRV_CAP_FLAG_1_PERIODIC_STATS_QUERY)
 
 #define GDMA_DRV_CAP_FLAGS2 0
 
include/net/mana/mana.h +5 -1
index 519c4384c51f..79532490cee6 100644
@@ -480,6 +480,10 @@ struct mana_context {
 	struct mana_eq *eqs;
 	struct dentry *mana_eqs_debugfs;
 
+	struct workqueue_struct *gf_stats_wq;
+	struct delayed_work gf_stats_work;
+	bool hwc_timeout_occurred;
+
 	struct net_device *ports[MAX_PORTS_IN_MANA_DEV];
 };
 
@@ -577,7 +581,7 @@ u32 mana_run_xdp(struct net_device *ndev, struct mana_rxq *rxq,
 struct bpf_prog *mana_xdp_get(struct mana_port_context *apc);
 void mana_chn_setxdp(struct mana_port_context *apc, struct bpf_prog *prog);
 int mana_bpf(struct net_device *ndev, struct netdev_bpf *bpf);
-void mana_query_gf_stats(struct mana_context *ac);
+int mana_query_gf_stats(struct mana_context *ac);
 int mana_query_link_cfg(struct mana_port_context *apc);
 int mana_set_bw_clamp(struct mana_port_context *apc, u32 speed,
 		      int enable_clamping);
Signature · 1 line
2.34.1
maintainer
#5 lore raw

Re: [PATCH net-next v2 2/2] net: mana: Add standard counter rx_missed_errors

drivers/net/ethernet/microsoft/mana/mana_en.c static void mana_get_stats64(struct net_device *ndev, · line 498

Quoted 10 lines · On Wed, 29 Oct 2025 03:37:52 -0700 Erni Sri Satya Vennela wrote:
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -0
index 009e869ef296..48df44889f05 100644
@@ -494,6 +494,11 @@ static void mana_get_stats64(struct net_device *ndev,
 
 	netdev_stats_to_stats64(st, &ndev->stats);
 
+	if (apc->ac->hwc_timeout_occurred)
+		netdev_warn_once(ndev, "HWC timeout occurred\n");

I don't think there's much value in this print.

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type even · line 3412

Quoted 13 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +13 -0
+#define MANA_GF_STATS_PERIOD (2 * HZ)
+
+static void mana_gf_stats_work_handler(struct work_struct *work)
+{
+	struct mana_context *ac =
+		container_of(to_delayed_work(work), struct mana_context, gf_stats_work);
+	int err;
+
+	err = mana_query_gf_stats(ac);
+	if (err == -ETIMEDOUT) {
+		/* HWC timeout detected - reset stats and stop rescheduling */
+		ac->hwc_timeout_occurred = true;
+		memset(&ac->hc_stats, 0, sizeof(ac->hc_stats));

Not sure I've seen another device using this approach but I can't really tell what's the best strategy. The device is unusable if it can't provide stats..

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_probe(struct gdma_dev *gd, bool resuming) · line 3506

Quoted 13 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +6 -0
+		return;
+	}
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+}
+
 int mana_probe(struct gdma_dev *gd, bool resuming)
 {
 	struct gdma_context *gc = gd->gdma_context;
@@ -3478,6 +3503,15 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
 	}
 
 	err = add_adev(gd, "eth");
+	ac->gf_stats_wq = create_singlethread_workqueue("mana_gf_stats");

Why are you creating a workqueue? You can use system queues.

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_probe(struct gdma_dev *gd, bool resuming) · line 3507

Quoted 1 line
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);

ls wrap long lines at 80 chars.

drivers/net/ethernet/microsoft/mana/mana_ethtool.c static void mana_get_ethtool_stats(struct net_device *ndev, · line 217

Quoted 10 lines
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +0 -2
index 3dfd96146424..99e811208683 100644
@@ -213,8 +213,6 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
 
 	if (!apc->port_is_up)
 		return;
-	/* we call mana function to update stats from GDMA */
-	mana_query_gf_stats(apc->ac);

Why delete this? We can get fresh stats for the user in this context.

#6 lore raw

Re: [PATCH net-next v2 2/2] net: mana: Add standard counter rx_missed_errors

Quoted 7 lines · Jakub Kicinski
Quoted 6 lines · On Wed, 29 Oct 2025 03:37:52 -0700 Erni Sri Satya Vennela wrote:
drivers/net/ethernet/microsoft/mana/mana_en.c +0 -0
index 009e869ef296..48df44889f05 100644
@@ -494,6 +494,11 @@ static void mana_get_stats64(struct net_device *ndev,
 
Quoted 6 lines

drivers/net/ethernet/microsoft/mana/mana_en.c static void mana_get_stats64(struct net_device *ndev, · line 498

Quoted 4 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +2 -0
 	netdev_stats_to_stats64(st, &ndev->stats);
 
+	if (apc->ac->hwc_timeout_occurred)
+		netdev_warn_once(ndev, "HWC timeout occurred\n");

I don't think there's much value in this print.

This print is added because, if the user tries to run
"ip -s link interface" for multiple times, he should be warned about the time out. I will add more info into the print to let the user know that it is from this action.

Quoted 10 lines
Quoted 10 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +9 -0
+#define MANA_GF_STATS_PERIOD (2 * HZ)
+
+static void mana_gf_stats_work_handler(struct work_struct *work)
+{
+	struct mana_context *ac =
+		container_of(to_delayed_work(work), struct mana_context, gf_stats_work);
+	int err;
+
+	err = mana_query_gf_stats(ac);
Quoted 8 lines

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_rdma_service_event(struct gdma_context *gc, enum gdma_service_type even · line 3412

Quoted 4 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +4 -0
+	if (err == -ETIMEDOUT) {
+		/* HWC timeout detected - reset stats and stop rescheduling */
+		ac->hwc_timeout_occurred = true;
+		memset(&ac->hc_stats, 0, sizeof(ac->hc_stats));

Not sure I've seen another device using this approach but I can't really tell what's the best strategy. The device is unusable if it can't provide stats..

In the case where the HWC becomes unresponsive,
there will be a brief interval needed for the driver to recover. During this period, if users request ethtool stats, they would receive outdated information. To address this, we proactively reset the stats to zero, ensuring users do not see stale data.

Quoted 12 lines
Quoted 12 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +5 -0
+		return;
+	}
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);
+}
+
 int mana_probe(struct gdma_dev *gd, bool resuming)
 {
 	struct gdma_context *gc = gd->gdma_context;
@@ -3478,6 +3503,15 @@ int mana_probe(struct gdma_dev *gd, bool resuming)
 	}
 
Quoted 4 lines

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_probe(struct gdma_dev *gd, bool resuming) · line 3506

Quoted 2 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
 	err = add_adev(gd, "eth");
+	ac->gf_stats_wq = create_singlethread_workqueue("mana_gf_stats");

Why are you creating a workqueue? You can use system queues.

Thankyou for the suggestion. I will integrate it in the next version.

Quoted 4 lines

drivers/net/ethernet/microsoft/mana/mana_en.c int mana_probe(struct gdma_dev *gd, bool resuming) · line 3507

Quoted 2 lines
drivers/net/ethernet/microsoft/mana/mana_en.c +1 -0
+	queue_delayed_work(ac->gf_stats_wq, &ac->gf_stats_work, MANA_GF_STATS_PERIOD);

ls wrap long lines at 80 chars.

I will make this change for the next verison.

Quoted 7 lines
Quoted 7 lines
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +0 -0
index 3dfd96146424..99e811208683 100644
@@ -213,8 +213,6 @@ static void mana_get_ethtool_stats(struct net_device *ndev,
 
Quoted 6 lines

drivers/net/ethernet/microsoft/mana/mana_ethtool.c static void mana_get_ethtool_stats(struct net_device *ndev, · line 217

Quoted 4 lines
drivers/net/ethernet/microsoft/mana/mana_ethtool.c +0 -2
 	if (!apc->port_is_up)
 		return;
-	/* we call mana function to update stats from GDMA */
-	mana_query_gf_stats(apc->ac);

Why delete this? We can get fresh stats for the user in this context.

We want to prevent some user that run ethtool too frequently, like thousands of times / sec, to overwhelm the HW channel.