mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net-next v5 0/3] net: phy: report ACx00 receive statistics
@ 2026-11-15  7:45 James Hilliard
  2026-11-15  7:45 ` [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name James Hilliard
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: James Hilliard @ 2026-11-15  7:45 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: Oleksij Rempel, netdev, linux-kernel, James Hilliard, linux-doc

AC200 and AC300 count bytes in received frames including those with CRC
errors. Expose that hardware total through a new optional PHY
rx_bytes_total statistic. The existing successful-byte counter cannot be
derived because the hardware does not separately count damaged-frame
lengths.

Report successful packets and CRC errors through the existing rx_packets
and rx_errors fields. Bracket the raw packet count with matching CRC
counts before subtracting; bytes and CRC errors can still advance when
sustained errors prevent a coherent packet sample.

Assisted-by: Codex:gpt-6-astra
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>

---
Changes in v5:
- Split the existing PHY statistics group-name documentation correction
  into a separate patch, following Andrew's review.
- Narrow the generic statistics addition to the inclusive receive-byte
  counter.
- Derive successful packets from a coherent packet/CRC pair and report
  them through the existing rx_packets field.
- Keep collecting CRC errors and inclusive bytes when sustained errors
  prevent a coherent pair, with separate baselines and sampling times.
- Skip invalid or unstable byte samples without discarding usable frame
  samples, retaining the measured byte modulus and sampling-gap limit.
- Rebase onto current net-next.
- Link to v4: https://patch.msgid.link/20261113-submit-acx00-phy-stats-v1-v4-0-986b8a4a1871@gmail.com

Changes in v4:
- Invalidate the ACx00 baseline before accessing counters in soft_reset,
  since phylib may already have reset the hardware before the callback.
- Start a new collection interval on resume and after gaps of at least
  320 seconds, including system sleep, preserving collected totals without
  interpreting unobserved traffic modulo the hardware counter width.
- Clarify that the byte modulus and frame-byte boundaries were measured
  on both AC200 and AC300.
- Specify matching frame-byte boundaries for both PHY byte counters and
  explain that phydev corrects the documented name of the existing group.
- Mark the DP83TG720 conversion as unsuitable for automatic stable
  selection without the new receive-total API.
- Rebase onto current net-next.
- Link to v3: https://patch.msgid.link/20261109-submit-acx00-phy-stats-v1-v3-0-fbc6327c5be4@gmail.com

Changes in v3:
- Add generic PHY receive-total packet and byte counters in a preparatory
  patch, retaining the existing successful-traffic counters and their IDs.
- Report ACx00 packet and byte totals through the new fields, leaving
  successful-only counters unset.
- Convert DP83TG720's inclusive RX packet count to the new total field.
- Bound coherent-counter sampling to three attempts and skip unstable
  snapshots without making phylib take the link down.
- Reject out-of-range byte samples before updating the hardware baseline
  or accumulated totals, retaining MDIO and page-restoration errors.
- Rebase onto current net-next.
- Link to v2: https://patch.msgid.link/20261102-submit-acx00-phy-stats-v1-v2-1-3c8a664407c1@gmail.com

Changes in v2:
- Rebase onto current net-next and repost after the merge window.
- No functional changes.
- Link to v1: https://patch.msgid.link/20261025-submit-acx00-phy-stats-v1-v1-1-d3d461e46db0@gmail.com

To: Andrew Lunn <andrew@lunn.ch>
To: Heiner Kallweit <hkallweit1@gmail.com>
To: Russell King <linux@armlinux.org.uk>
To: "David S. Miller" <davem@davemloft.net>
To: Eric Dumazet <edumazet@kernel.org>
To: Jakub Kicinski <kuba@kernel.org>
To: Paolo Abeni <pabeni@redhat.com>
To: Simon Horman <horms@kernel.org>
To: Jonathan Corbet <corbet@lwn.net>
To: Shuah Khan <skhan@linuxfoundation.org>
To: Randy Dunlap <rdunlap@infradead.org>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-doc@vger.kernel.org

---
James Hilliard (3):
      docs: networking: correct the PHY statistics group name
      ethtool: add a PHY receive byte total
      net: phy: acx00: report receive statistics

 Documentation/networking/ethtool-netlink.rst |  15 +-
 drivers/net/phy/xpowers/acx00.c              | 223 ++++++++++++++++++++++++++-
 include/linux/ethtool.h                      |   8 +-
 include/uapi/linux/ethtool_netlink.h         |   1 +
 net/ethtool/stats.c                          |   5 +-
 5 files changed, 246 insertions(+), 6 deletions(-)
---
base-commit: d1f4b7c6b997c29de67ad7178ababac2328cb1b4
change-id: 20261024-submit-acx00-phy-stats-v1-d838f77444e8

Best regards,
--  
James Hilliard <james.hilliard1@gmail.com>


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name
  2026-11-15  7:45 [PATCH net-next v5 0/3] net: phy: report ACx00 receive statistics James Hilliard
@ 2026-11-15  7:45 ` James Hilliard
  2026-11-15 15:01   ` Andrew Lunn
  2026-11-15  7:45 ` [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total James Hilliard
  2026-11-15  7:45 ` [PATCH net-next v5 3/3] net: phy: acx00: report receive statistics James Hilliard
  2 siblings, 1 reply; 6+ messages in thread
From: James Hilliard @ 2026-11-15  7:45 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: Oleksij Rempel, netdev, linux-kernel, James Hilliard, linux-doc

The ETHTOOL_STATS_PHY group is exported as phydev by stats_std_names[],
but the ethtool netlink documentation lists it as phy. Use the exported
name so the documented group matches the statistics string set returned
to userspace.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 Documentation/networking/ethtool-netlink.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index eba226c79ec3..8659761ebc69 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1665,7 +1665,7 @@ the ``ETHTOOL_A_STATS_GROUPS`` bitset. Currently defined values are:
  ETHTOOL_STATS_ETH_PHY  eth-phy  Basic IEEE 802.3 PHY statistics (30.3.2.1.*)
  ETHTOOL_STATS_ETH_CTRL eth-ctrl Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*)
  ETHTOOL_STATS_RMON     rmon     RMON (RFC 2819) statistics
- ETHTOOL_STATS_PHY      phy      Additional PHY statistics, not defined by IEEE
+ ETHTOOL_STATS_PHY      phydev   Additional PHY statistics, not defined by IEEE
  ====================== ======== ===============================================
 
 Each group should have a corresponding ``ETHTOOL_A_STATS_GRP`` in the reply.

-- 
2.53.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total
  2026-11-15  7:45 [PATCH net-next v5 0/3] net: phy: report ACx00 receive statistics James Hilliard
  2026-11-15  7:45 ` [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name James Hilliard
@ 2026-11-15  7:45 ` James Hilliard
  2026-11-15 15:25   ` Andrew Lunn
  2026-11-15  7:45 ` [PATCH net-next v5 3/3] net: phy: acx00: report receive statistics James Hilliard
  2 siblings, 1 reply; 6+ messages in thread
From: James Hilliard @ 2026-11-15  7:45 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: Oleksij Rempel, netdev, linux-kernel, James Hilliard, linux-doc

The generic PHY receive-byte counter describes successfully received
traffic. Some PHYs instead count bytes in all received frames, including
frames with errors. Unlike frame counts, the successful-byte count cannot
be derived by subtracting a frame error count because damaged frames have
different lengths.

Add an optional rx_bytes_total counter to ethtool_phy_stats and the phydev
statistics group. Count frame bytes including the Ethernet header, padding
and FCS, and excluding the preamble and start frame delimiter. The counter
describes reception at the PHY, before MAC or host filtering. Clarify the
same byte boundaries for the successful-only receive counter.

Append the new attribute so existing statistic IDs and their meanings
remain unchanged. Publish its name through the existing statistics string
set and retain the unset-counter convention.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 Documentation/networking/ethtool-netlink.rst | 13 +++++++++++++
 include/linux/ethtool.h                      |  8 +++++++-
 include/uapi/linux/ethtool_netlink.h         |  1 +
 net/ethtool/stats.c                          |  5 ++++-
 4 files changed, 25 insertions(+), 2 deletions(-)

diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 8659761ebc69..1dad86561823 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -1683,6 +1683,19 @@ by ``ETHTOOL_A_STATS_GRP_SS_ID``. Complex statistics (such as RMON histogram
 entries) are also listed inside ``ETHTOOL_A_STATS_GRP`` and do not have
 a string defined in the string set.
 
+The ``phydev`` group provides separate counters for successfully received
+bytes and total received bytes. ``ETHTOOL_A_STATS_PHY_RX_BYTES`` (``RxOctets``)
+counts bytes in successfully received frames, while
+``ETHTOOL_A_STATS_PHY_RX_BYTES_TOTAL`` (``RxOctetsTotal``) also includes bytes
+in frames with errors. Both include the Ethernet header, padding and FCS,
+but exclude the preamble and start frame delimiter. These counters describe
+reception at the PHY, independently of subsequent filtering or drops at
+the MAC or host.
+
+Each counter is optional. Drivers leave unsupported counters at
+``ETHTOOL_STAT_NOT_SET``, and the kernel omits them from the reply. A PHY can
+report total received bytes even when it cannot separate successful bytes.
+
 RMON "histogram" counters count number of packets within given size range.
 Because RFC does not specify the ranges beyond the standard 1518 MTU devices
 differ in definition of buckets. For this reason the definition of packet ranges
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index c3a41fbd5426..fedc127d9a8a 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -481,11 +481,16 @@ struct ethtool_eth_phy_stats {
 /**
  * struct ethtool_phy_stats - PHY-level statistics counters
  * @rx_packets: Total successfully received frames
- * @rx_bytes: Total successfully received bytes
+ * @rx_bytes: Total successfully received frame bytes. Includes the Ethernet
+ *	header, padding and FCS, but excludes the preamble and start frame
+ *	delimiter.
  * @rx_errors: Total received frames with errors (e.g., CRC errors)
  * @tx_packets: Total successfully transmitted frames
  * @tx_bytes: Total successfully transmitted bytes
  * @tx_errors: Total transmitted frames with errors
+ * @rx_bytes_total: Total received frame bytes, including frames with errors.
+ *	Includes the Ethernet header, padding and FCS, but excludes the preamble
+ *	and start frame delimiter.
  *
  * This structure provides a standardized interface for reporting
  * PHY-level statistics counters. It is designed to expose statistics
@@ -499,6 +504,7 @@ struct ethtool_phy_stats {
 	u64 tx_packets;
 	u64 tx_bytes;
 	u64 tx_errors;
+	u64 rx_bytes_total;
 };
 
 /* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h
index fa5d645140a4..2e81f7345f72 100644
--- a/include/uapi/linux/ethtool_netlink.h
+++ b/include/uapi/linux/ethtool_netlink.h
@@ -202,6 +202,7 @@ enum {
 	ETHTOOL_A_STATS_PHY_TX_PKTS,
 	ETHTOOL_A_STATS_PHY_TX_BYTES,
 	ETHTOOL_A_STATS_PHY_TX_ERRORS,
+	ETHTOOL_A_STATS_PHY_RX_BYTES_TOTAL,
 
 	/* add new constants above here */
 	__ETHTOOL_A_STATS_PHY_CNT,
diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c
index 9b0d8cb07675..9b9ada615ac2 100644
--- a/net/ethtool/stats.c
+++ b/net/ethtool/stats.c
@@ -88,6 +88,7 @@ const char stats_phy_names[__ETHTOOL_A_STATS_PHY_CNT][ETH_GSTRING_LEN] = {
 	[ETHTOOL_A_STATS_PHY_TX_PKTS]		= "TxFrames",
 	[ETHTOOL_A_STATS_PHY_TX_BYTES]		= "TxOctets",
 	[ETHTOOL_A_STATS_PHY_TX_ERRORS]		= "TxErrors",
+	[ETHTOOL_A_STATS_PHY_RX_BYTES_TOTAL]	= "RxOctetsTotal",
 };
 
 const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = {
@@ -295,7 +296,9 @@ static int stats_put_phydev_stats(struct sk_buff *skb,
 	    stat_put(skb, ETHTOOL_A_STATS_PHY_TX_BYTES,
 		     data->phydev_stats.tx_bytes) ||
 	    stat_put(skb, ETHTOOL_A_STATS_PHY_TX_ERRORS,
-		     data->phydev_stats.tx_errors))
+		     data->phydev_stats.tx_errors) ||
+	    stat_put(skb, ETHTOOL_A_STATS_PHY_RX_BYTES_TOTAL,
+		     data->phydev_stats.rx_bytes_total))
 		return -EMSGSIZE;
 	return 0;
 }

-- 
2.53.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH net-next v5 3/3] net: phy: acx00: report receive statistics
  2026-11-15  7:45 [PATCH net-next v5 0/3] net: phy: report ACx00 receive statistics James Hilliard
  2026-11-15  7:45 ` [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name James Hilliard
  2026-11-15  7:45 ` [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total James Hilliard
@ 2026-11-15  7:45 ` James Hilliard
  2 siblings, 0 replies; 6+ messages in thread
From: James Hilliard @ 2026-11-15  7:45 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit, Russell King, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
	Jonathan Corbet, Shuah Khan, Randy Dunlap
  Cc: Oleksij Rempel, netdev, linux-kernel, James Hilliard, linux-doc

AC200 and AC300 count received frames including those with CRC errors.
Subtract the CRC-error count to report successful frames through the
existing PHY rx_packets statistic, and report CRC errors through
rx_errors. Report the hardware byte total through rx_bytes_total, leaving
the successful-only rx_bytes unset because damaged-frame lengths are not
available separately.

The free-running packet and CRC-error counters both wrap at 2^32.
Read each counter high/low/high to detect a carry, and bracket the packet
read with an unchanged CRC count before subtracting. Their difference
also wraps at 2^32. Accumulate its wrap-adjusted deltas in a 64-bit total.

Hardware measurements on AC200 and AC300 established a receive-byte
modulus of 4,000,000,000. The byte counts include Ethernet headers, padding
and FCS, but exclude preamble and SFD. Apply that modulus to byte deltas
and skip byte samples at or above it without changing their baseline.

Bound sampling retries to avoid retaining the PHY and MDIO locks
indefinitely. When CRC errors keep arriving, update rx_errors from the
latest stable error read without advancing the successful-frame baseline.
A later coherent pair accounts for the intervening successful frames.
Collect bytes independently of that pair. An unstable sample is not an
MDIO error: returning a synthetic error from update_stats would make
phylib take the link down. Real MDIO errors, including page-restoration
failures, still propagate.

Serialize sampling, reporting and power transitions with the package
mutex. Commit samples only after page restoration succeeds, and return
cached totals from get_phy_stats without further MDIO access.

Invalidate baselines before soft_reset or power-off, since phylib may
already have reset the counters. Take a best-effort snapshot before
suspend and start new intervals on resume, including when WoL retained
power while polling stopped. Keep a separate sampling time for each
counter and discard deltas after gaps of at least 320 seconds, including
system sleep and register-read time. At 100 Mb/s, that bounds the byte
counter and conservatively bounds the frame counters even at one frame
per received byte. Retain accumulated totals across these gaps and power
transitions, without inventing an unobservable number of counter cycles.

Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
 drivers/net/phy/xpowers/acx00.c | 223 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 220 insertions(+), 3 deletions(-)

diff --git a/drivers/net/phy/xpowers/acx00.c b/drivers/net/phy/xpowers/acx00.c
index cee6c7ff0278..691f4931af46 100644
--- a/drivers/net/phy/xpowers/acx00.c
+++ b/drivers/net/phy/xpowers/acx00.c
@@ -8,12 +8,14 @@
 
 #include <linux/bitfield.h>
 #include <linux/device.h>
+#include <linux/ethtool.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/nvmem-consumer.h>
 #include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/slab.h>
+#include <linux/timekeeping.h>
 
 #include "../phylib.h"
 #include "acx00.h"
@@ -31,6 +33,7 @@
 #define ACX00_PAGE_2				2
 #define ACX00_PAGE_6				6
 #define ACX00_PAGE_8				8
+#define ACX00_PAGE_9				9
 
 #define ACX00_PAGE0_GLOBAL_CONFIG_REG		0x13
 #define ACX00_PAGE0_XMII_RX_CLOCK_INVERT	BIT(12)
@@ -76,6 +79,26 @@
 	(ACX00_PAGE8_AUTO_CAL_TX_LEVEL_ADJUST_BYPASS | \
 	 ACX00_PAGE8_AUTO_CAL_LOW_CAL_OPAQUE_BITS)
 
+#define ACX00_PAGE9_RX_BYTES_HIGH_REG		0x19
+#define ACX00_PAGE9_RX_PACKETS_HIGH_REG		0x1b
+#define ACX00_PAGE9_RX_CRC_ERRORS_HIGH_REG	0x1d
+
+/*
+ * AC200 and AC300 hardware testing confirmed that the receive byte counter
+ * wraps at 4,000,000,000 rather than 2^32.
+ */
+#define ACX00_RX_BYTES_WRAP			4000000000ULL
+/* At 100 Mb/s, a full byte-counter cycle takes at least 320 seconds. */
+#define ACX00_STATS_MAX_INTERVAL_MS		(ACX00_RX_BYTES_WRAP * 8 / (SPEED_100 * 1000))
+#define ACX00_COUNTER_READ_ATTEMPTS		3
+
+enum acx00_ephy_stat {
+	ACX00_STAT_RX_PACKETS,
+	ACX00_STAT_RX_CRC_ERRORS,
+	ACX00_STAT_RX_BYTES,
+	ACX00_NUM_STATS,
+};
+
 /*
  * Another integration of this exact-ID PHY documents its digital vendor
  * register map, which also matches the observed ACx00 reset values. The ACx00
@@ -86,11 +109,16 @@
 struct acx00_ephy_priv {
 	struct phy_device *phydev;
 	struct acx00_ephy_control *control;
-	/* Serializes package state and multi-register power sequences. */
+	/* Serializes package state, power sequences and statistics. */
 	struct mutex state_lock;
 	bool is_ac300;
 	bool use_low_calibration_tuning;
 	bool xmii_rx_clock_inverted;
+	bool stats_active;
+	u8 stats_valid;
+	ktime_t stats_start[ACX00_NUM_STATS];
+	u32 stats_last[ACX00_NUM_STATS];
+	u64 stats[ACX00_NUM_STATS];
 };
 
 static int acx00_ephy_read_page(struct phy_device *phydev)
@@ -110,6 +138,169 @@ static int acx00_ephy_write_page(struct phy_device *phydev, int page)
 			   FIELD_PREP(ACX00_PAGE_SELECT_MASK, page));
 }
 
+/* Return 0 on success, 1 for an unstable sample, or a negative MDIO error. */
+static int acx00_ephy_read_counter(struct phy_device *phydev, u8 reg, u32 *count)
+{
+	int high, low, check;
+	int i;
+
+	/* The free-running counter can carry while its two halves are read. */
+	for (i = 0; i < ACX00_COUNTER_READ_ATTEMPTS; i++) {
+		high = __phy_read(phydev, reg);
+		if (high < 0)
+			return high;
+		low = __phy_read(phydev, reg + 1);
+		if (low < 0)
+			return low;
+		check = __phy_read(phydev, reg);
+		if (check < 0)
+			return check;
+		if (high != check)
+			continue;
+
+		*count = (u32)high << 16 | low;
+		return 0;
+	}
+
+	return 1;
+}
+
+/* Return a mask of usable counters, or a negative MDIO error. */
+static int acx00_ephy_read_counts(struct phy_device *phydev, u32 *counts)
+{
+	u32 packets, errors;
+	int ret;
+	int i;
+
+	ret = acx00_ephy_read_counter(phydev, ACX00_PAGE9_RX_CRC_ERRORS_HIGH_REG,
+				      &counts[ACX00_STAT_RX_CRC_ERRORS]);
+	if (ret)
+		return ret < 0 ? ret : 0;
+
+	/* Bracket the packet counter with an unchanged CRC count. */
+	for (i = 0; i < ACX00_COUNTER_READ_ATTEMPTS; i++) {
+		ret = acx00_ephy_read_counter(phydev, ACX00_PAGE9_RX_PACKETS_HIGH_REG,
+					      &packets);
+		if (ret < 0)
+			return ret;
+		if (ret)
+			break;
+		ret = acx00_ephy_read_counter(phydev, ACX00_PAGE9_RX_CRC_ERRORS_HIGH_REG,
+					      &errors);
+		if (ret < 0)
+			return ret;
+		if (ret)
+			break;
+		if (errors == counts[ACX00_STAT_RX_CRC_ERRORS]) {
+			/* Both hardware counters and their difference wrap at 2^32. */
+			counts[ACX00_STAT_RX_PACKETS] = packets - errors;
+			return BIT(ACX00_STAT_RX_PACKETS) | BIT(ACX00_STAT_RX_CRC_ERRORS);
+		}
+		counts[ACX00_STAT_RX_CRC_ERRORS] = errors;
+	}
+
+	/* Keep reporting errors even when no coherent packet sample is possible. */
+	return BIT(ACX00_STAT_RX_CRC_ERRORS);
+}
+
+static int __acx00_ephy_update_stats(struct phy_device *phydev)
+{
+	struct acx00_ephy_priv *priv = phydev->priv;
+	u32 counts[ACX00_NUM_STATS];
+	ktime_t start;
+	int oldpage;
+	int valid;
+	int ret;
+	int i;
+
+	lockdep_assert_held(&priv->state_lock);
+
+	if (!priv->stats_active)
+		return 0;
+
+	start = ktime_get_boottime();
+	oldpage = phy_select_page(phydev, ACX00_PAGE_9);
+	if (oldpage < 0)
+		return phy_restore_page(phydev, oldpage, oldpage);
+
+	valid = acx00_ephy_read_counts(phydev, counts);
+	if (valid < 0) {
+		ret = valid;
+		goto out_restore_page;
+	}
+
+	ret = acx00_ephy_read_counter(phydev, ACX00_PAGE9_RX_BYTES_HIGH_REG,
+				      &counts[ACX00_STAT_RX_BYTES]);
+	if (ret < 0)
+		goto out_restore_page;
+	if (!ret && counts[ACX00_STAT_RX_BYTES] < ACX00_RX_BYTES_WRAP)
+		valid |= BIT(ACX00_STAT_RX_BYTES);
+	ret = valid;
+
+out_restore_page:
+	ret = phy_restore_page(phydev, oldpage, ret);
+	if (ret < 0)
+		return ret;
+
+	for (i = 0; i < ACX00_NUM_STATS; i++) {
+		u64 count;
+
+		if (!(ret & BIT(i)))
+			continue;
+		count = counts[i];
+
+		/* Include sleep and read time; unseen cycles cannot be recovered. */
+		if ((priv->stats_valid & BIT(i)) &&
+		    ktime_ms_delta(ktime_get_boottime(), priv->stats_start[i]) <
+		    (s64)ACX00_STATS_MAX_INTERVAL_MS) {
+			if (count < priv->stats_last[i])
+				count += i == ACX00_STAT_RX_BYTES ?
+					ACX00_RX_BYTES_WRAP : BIT_ULL(32);
+			priv->stats[i] += count - priv->stats_last[i];
+		}
+		priv->stats_last[i] = counts[i];
+		priv->stats_start[i] = start;
+		priv->stats_valid |= BIT(i);
+	}
+
+	return 0;
+}
+
+static int acx00_ephy_update_stats(struct phy_device *phydev)
+{
+	struct acx00_ephy_priv *priv = phydev->priv;
+	int ret;
+
+	mutex_lock(&priv->state_lock);
+	ret = __acx00_ephy_update_stats(phydev);
+	mutex_unlock(&priv->state_lock);
+
+	return ret;
+}
+
+static void acx00_ephy_get_phy_stats(struct phy_device *phydev,
+				     struct ethtool_eth_phy_stats *eth_stats,
+				     struct ethtool_phy_stats *stats)
+{
+	struct acx00_ephy_priv *priv = phydev->priv;
+
+	mutex_lock(&priv->state_lock);
+	stats->rx_packets = priv->stats[ACX00_STAT_RX_PACKETS];
+	stats->rx_errors = priv->stats[ACX00_STAT_RX_CRC_ERRORS];
+	stats->rx_bytes_total = priv->stats[ACX00_STAT_RX_BYTES];
+	mutex_unlock(&priv->state_lock);
+}
+
+static void acx00_ephy_snapshot_stats(struct phy_device *phydev)
+{
+	int ret;
+
+	/* Statistics collection must not prevent reset or power management. */
+	ret = __acx00_ephy_update_stats(phydev);
+	if (ret)
+		phydev_dbg(phydev, "failed to snapshot statistics: %pe\n", ERR_PTR(ret));
+}
+
 static int acx00_ephy_control_power_on(struct acx00_ephy_priv *priv)
 {
 	lockdep_assert_held(&priv->state_lock);
@@ -122,6 +313,10 @@ static int acx00_ephy_control_power_off(struct acx00_ephy_priv *priv)
 {
 	lockdep_assert_held(&priv->state_lock);
 
+	/* Even a failed power-off can have reset the hardware counter. */
+	priv->stats_active = false;
+	priv->stats_valid = 0;
+
 	return priv->control->power_off(priv->control);
 }
 
@@ -350,6 +545,10 @@ static int acx00_ephy_soft_reset(struct phy_device *phydev)
 
 	mutex_lock(&priv->state_lock);
 
+	/* phylib may already have reset the counters before calling us. */
+	priv->stats_active = false;
+	priv->stats_valid = 0;
+
 	ret = acx00_ephy_set_interface(phydev);
 	if (ret)
 		goto out_power_off;
@@ -475,10 +674,21 @@ static int __acx00_ephy_config_init(struct phy_device *phydev)
 
 	/* Restore the standard EEE policy retained by phylib across resets. */
 	ret = genphy_c45_an_config_eee_aneg(phydev);
-	if (ret <= 0)
+	if (ret < 0)
 		return ret;
+	if (ret > 0) {
+		ret = genphy_restart_aneg(phydev);
+		if (ret)
+			return ret;
+	}
+
+	if (!priv->stats_active) {
+		/* Establish a new baseline after reset, retaining the software total. */
+		priv->stats_active = true;
+		acx00_ephy_snapshot_stats(phydev);
+	}
 
-	return genphy_restart_aneg(phydev);
+	return 0;
 }
 
 static int acx00_ephy_config_init(struct phy_device *phydev)
@@ -534,6 +744,9 @@ static int acx00_ephy_resume(struct phy_device *phydev)
 	int ret;
 
 	mutex_lock(&priv->state_lock);
+	/* WoL can keep the PHY counting while phylib polling is stopped. */
+	priv->stats_active = false;
+	priv->stats_valid = 0;
 	ret = __acx00_ephy_power_on_and_resume(phydev);
 	mutex_unlock(&priv->state_lock);
 
@@ -550,6 +763,8 @@ static int acx00_ephy_suspend(struct phy_device *phydev)
 	mutex_lock(&phydev->lock);
 	mutex_lock(&priv->state_lock);
 
+	acx00_ephy_snapshot_stats(phydev);
+
 	ret = genphy_suspend(phydev);
 	if (ret)
 		goto out_unlock;
@@ -616,6 +831,8 @@ static struct phy_driver acx00_ephy_driver[] = {
 		.soft_reset = acx00_ephy_soft_reset,
 		.config_init = acx00_ephy_config_init,
 		.disable_autonomous_eee = acx00_ephy_disable_autonomous_eee,
+		.get_phy_stats = acx00_ephy_get_phy_stats,
+		.update_stats = acx00_ephy_update_stats,
 		.suspend = acx00_ephy_suspend,
 		.resume = acx00_ephy_resume,
 	},

-- 
2.53.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name
  2026-11-15  7:45 ` [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name James Hilliard
@ 2026-11-15 15:01   ` Andrew Lunn
  0 siblings, 0 replies; 6+ messages in thread
From: Andrew Lunn @ 2026-11-15 15:01 UTC (permalink / raw)
  To: James Hilliard
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Oleksij Rempel, netdev, linux-kernel,
	linux-doc

On Sun, Nov 15, 2026 at 12:45:45AM -0700, James Hilliard wrote:
> The ETHTOOL_STATS_PHY group is exported as phydev by stats_std_names[],
> but the ethtool netlink documentation lists it as phy. Use the exported
> name so the documented group matches the statistics string set returned
> to userspace.
> 
> Signed-off-by: James Hilliard <james.hilliard1@gmail.com>

Thanks

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total
  2026-11-15  7:45 ` [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total James Hilliard
@ 2026-11-15 15:25   ` Andrew Lunn
  0 siblings, 0 replies; 6+ messages in thread
From: Andrew Lunn @ 2026-11-15 15:25 UTC (permalink / raw)
  To: James Hilliard
  Cc: Heiner Kallweit, Russell King, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Jonathan Corbet,
	Shuah Khan, Randy Dunlap, Oleksij Rempel, netdev, linux-kernel,
	linux-doc

On Sun, Nov 15, 2026 at 12:45:46AM -0700, James Hilliard wrote:

> The generic PHY receive-byte counter describes successfully received
> traffic. Some PHYs instead count bytes in all received frames,
> including frames with errors.

In the general case, that is wrong. A PHY basically does digital to
analogue conversion. It takes a stream of bit from the MAC and
converts them to analogue signals on the media. In the opposite
direction, it recovers the analogue signals, converts it to digital,
and passes it to the MAC.

Thinks about SGMII. When running to 10Mbps, the MAC has to duplicate
the bits 100 times. At 100Mbps, it duplicates the bits 10 times. The
PHY then puts the bits onto the wire, it has no need to understand the
duplication, the boundary between the MAC level bits, it is just a
dumb DAC/ADC.

In order to see frames, not a stream of bits, you need a MAC.

Now, some PHYs do in fact have MACs. Those that implement PTP for
example need to reassemble the bit stream into frames. Anything which
does MACSEC. Or rate adaptation.

But in the general case, its just a stream of bits. And the byte
counters should be for the general case. And for SGMII, the definition
is a bit more fuzzy, does it include the repetition or not?

If the PHY has a MAC, it can additionally fill in the frame counters.

       Andrew

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-15 15:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-15  7:45 [PATCH net-next v5 0/3] net: phy: report ACx00 receive statistics James Hilliard
2026-11-15  7:45 ` [PATCH net-next v5 1/3] docs: networking: correct the PHY statistics group name James Hilliard
2026-11-15 15:01   ` Andrew Lunn
2026-11-15  7:45 ` [PATCH net-next v5 2/3] ethtool: add a PHY receive byte total James Hilliard
2026-11-15 15:25   ` Andrew Lunn
2026-11-15  7:45 ` [PATCH net-next v5 3/3] net: phy: acx00: report receive statistics James Hilliard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®