[PATCH net-next 2/3] net: enetc: separate 64-bit counters from enetc_port_counters

Wei Fang posted 3 patches 3 months, 2 weeks ago
There is a newer version of this series
[PATCH net-next 2/3] net: enetc: separate 64-bit counters from enetc_port_counters
Posted by Wei Fang 3 months, 2 weeks ago
Some counters in enetc_port_counters are 32-bit registers, and some are
64-bit registers. But in the current driver, they are all read through
enetc_port_rd(), which can only read a 32-bit value. Therefore, separate
64-bit counters (enetc_pm_counters) from enetc_port_counters and use
enetc_port_rd64() to read the 64-bit statistics.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
---
 .../net/ethernet/freescale/enetc/enetc_ethtool.c  | 15 ++++++++++++++-
 drivers/net/ethernet/freescale/enetc/enetc_hw.h   |  1 +
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
index 2e5cef646741..2c9aa94c8e3d 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
+++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
@@ -142,7 +142,7 @@ static const struct {
 static const struct {
 	int reg;
 	char name[ETH_GSTRING_LEN] __nonstring;
-} enetc_port_counters[] = {
+} enetc_pm_counters[] = {
 	{ ENETC_PM_REOCT(0),	"MAC rx ethernet octets" },
 	{ ENETC_PM_RALN(0),	"MAC rx alignment errors" },
 	{ ENETC_PM_RXPF(0),	"MAC rx valid pause frames" },
@@ -194,6 +194,12 @@ static const struct {
 	{ ENETC_PM_TSCOL(0),	"MAC tx single collisions" },
 	{ ENETC_PM_TLCOL(0),	"MAC tx late collisions" },
 	{ ENETC_PM_TECOL(0),	"MAC tx excessive collisions" },
+};
+
+static const struct {
+	int reg;
+	char name[ETH_GSTRING_LEN] __nonstring;
+} enetc_port_counters[] = {
 	{ ENETC_UFDMF,		"SI MAC nomatch u-cast discards" },
 	{ ENETC_MFDMF,		"SI MAC nomatch m-cast discards" },
 	{ ENETC_PBFDSIR,	"SI MAC nomatch b-cast discards" },
@@ -240,6 +246,7 @@ static int enetc_get_sset_count(struct net_device *ndev, int sset)
 		return len;
 
 	len += ARRAY_SIZE(enetc_port_counters);
+	len += ARRAY_SIZE(enetc_pm_counters);
 
 	return len;
 }
@@ -266,6 +273,9 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
 		for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
 			ethtool_cpy(&data, enetc_port_counters[i].name);
 
+		for (i = 0; i < ARRAY_SIZE(enetc_pm_counters); i++)
+			ethtool_cpy(&data, enetc_pm_counters[i].name);
+
 		break;
 	}
 }
@@ -302,6 +312,9 @@ static void enetc_get_ethtool_stats(struct net_device *ndev,
 
 	for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
 		data[o++] = enetc_port_rd(hw, enetc_port_counters[i].reg);
+
+	for (i = 0; i < ARRAY_SIZE(enetc_pm_counters); i++)
+		data[o++] = enetc_port_rd64(hw, enetc_pm_counters[i].reg);
 }
 
 static void enetc_pause_stats(struct enetc_hw *hw, int mac,
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index cb26f185f52f..d4bbb07199c5 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -536,6 +536,7 @@ static inline u64 _enetc_rd_reg64_wa(void __iomem *reg)
 /* port register accessors - PF only */
 #define enetc_port_rd(hw, off)		enetc_rd_reg((hw)->port + (off))
 #define enetc_port_wr(hw, off, val)	enetc_wr_reg((hw)->port + (off), val)
+#define enetc_port_rd64(hw, off)	_enetc_rd_reg64_wa((hw)->port + (off))
 #define enetc_port_rd_mdio(hw, off)	_enetc_rd_mdio_reg_wa((hw)->port + (off))
 #define enetc_port_wr_mdio(hw, off, val)	_enetc_wr_mdio_reg_wa(\
 							(hw)->port + (off), val)
-- 
2.34.1
Re: [PATCH net-next 2/3] net: enetc: separate 64-bit counters from enetc_port_counters
Posted by Simon Horman 3 months, 2 weeks ago
On Fri, Jun 20, 2025 at 06:21:39PM +0800, Wei Fang wrote:
> Some counters in enetc_port_counters are 32-bit registers, and some are
> 64-bit registers. But in the current driver, they are all read through
> enetc_port_rd(), which can only read a 32-bit value. Therefore, separate
> 64-bit counters (enetc_pm_counters) from enetc_port_counters and use
> enetc_port_rd64() to read the 64-bit statistics.
> 
> Signed-off-by: Wei Fang <wei.fang@nxp.com>

This patch looks fine to me, as does the following one.
However, they multiple sparse warnings relating
to endianness handling in the ioread32() version of _enetc_rd_reg64().

I've collected together my thoughts on that in the form of a patch.
And I'd appreciate it if we could resolve this one way or another.

From: Simon Horman <horms@kernel.org>
Subject: [PATCH RFC net] net: enetc: Correct endianness handling in
 _enetc_rd_reg64

enetc_hw.h provides two versions of _enetc_rd_reg64.
One which simply calls ioread64() when available.
And another that composes the 64-bit result from ioread32() calls.

In the second case the code appears to assume that each ioread32()
call returns a little-endian value. The high and the low 32 bit
values are then combined to make a 64-bit value which is then
converted to host byte order.

However, both the bit shift and the logical or used to combine
the two 32-bit values assume that they are operating on host-byte
order entities. This seems broken and I assume that the code
has only been tested on little endian systems.

Correct this by converting the 32-bit little endian values
to host byte order before operating on them.

Also, use little endian types to store these values, to make
the logic clearer and is moreover good practice.

Flagged by Sparse

Fixes: 69c663660b06 ("net: enetc: Correct endianness handling in _enetc_rd_reg64")
Signed-off-by: Simon Horman <horms@kernel.org>
---
I have marked this as RFC as I am unsure that the above is correct.

The version of _enetc_rd_reg64() that is a trivial wrapper around
ioread64() assumes that the call to ioread64() returns a host byte order
value?

If that is the case then is it also the case that the ioread32() calls,
in this version of _enetc_rd_reg64() also return host byte order values.
And if so, it is probably sufficient for this version to keep using u32
as the type for low, high, and tmp.  And simply:

	return high << 32 | low;
---
 drivers/net/ethernet/freescale/enetc/enetc_hw.h | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
index cb26f185f52f..3f40fcdbc4a7 100644
--- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
+++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
@@ -502,15 +502,15 @@ static inline u64 _enetc_rd_reg64(void __iomem *reg)
 /* using this to read out stats on 32b systems */
 static inline u64 _enetc_rd_reg64(void __iomem *reg)
 {
-	u32 low, high, tmp;
+	__le32 low, high, tmp;
 
 	do {
-		high = ioread32(reg + 4);
-		low = ioread32(reg);
-		tmp = ioread32(reg + 4);
+		high = (__force __le32)ioread32(reg + 4);
+		low = (__force __le32)ioread32(reg);
+		tmp = (__force __le32)ioread32(reg + 4);
 	} while (high != tmp);
 
-	return le64_to_cpu((__le64)high << 32 | low);
+	return (u64)le32_to_cpu(high) << 32 | le32_to_cpu(low);
 }
 #endif
 
-- 
2.47.2