From nobody Tue Feb 10 08:01:10 2026 Received: from metis.whiteo.stw.pengutronix.de (metis.whiteo.stw.pengutronix.de [185.203.201.7]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DB13A1D5AD7 for ; Tue, 3 Dec 2024 07:56:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.203.201.7 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733212605; cv=none; b=C68cBZF4OIqIYGcPA/XjnrRqCV554bDyPaMiinT+niSlQwHn+ykdSXFUrPdV/Z2igAldiQ7+TTfKwg4+bf9ty7dzGt0c143YT1UZ+D4/6kSjEDtRIoyMxRfwJoeEiBcaf7j/c8hRE5ahw1C/03xcuPdkF2ehxT6XzTndFhB3x9k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1733212605; c=relaxed/simple; bh=IJz3BTVVUVHjJPI4ChFZs5cSm+d66rR2ttqqpJVqLko=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=npM5HZOSZUG0zz0ZnrIAMSXPfWvznKsyCDqYnrg5j0kyu7PUJYHH+RTKy0U3YgJgKztxV0BesrOkKks4BsAwEw0XMqEtav3vHO1hhiIYlUU5NU1E5mTuQOCMqA2Uh0pcQpg6TrEpimh8a9mY/TDWNRLf3j1rqUHU/vR/JEZEVOk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de; spf=pass smtp.mailfrom=pengutronix.de; arc=none smtp.client-ip=185.203.201.7 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=pengutronix.de Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pengutronix.de Received: from drehscheibe.grey.stw.pengutronix.de ([2a0a:edc0:0:c01:1d::a2]) by metis.whiteo.stw.pengutronix.de with esmtps (TLS1.3:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1tINlj-000394-Ki; Tue, 03 Dec 2024 08:56:27 +0100 Received: from dude04.red.stw.pengutronix.de ([2a0a:edc0:0:1101:1d::ac]) by drehscheibe.grey.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1tINle-001R8w-0a; Tue, 03 Dec 2024 08:56:22 +0100 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.96) (envelope-from ) id 1tINle-00AHvy-2q; Tue, 03 Dec 2024 08:56:22 +0100 From: Oleksij Rempel To: "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Andrew Lunn , Heiner Kallweit , Jonathan Corbet Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, Simon Horman , Russell King , Maxime Chevallier , linux-doc@vger.kernel.org Subject: [PATCH net-next v1 2/7] net: ethtool: add support for structured PHY statistics Date: Tue, 3 Dec 2024 08:56:16 +0100 Message-Id: <20241203075622.2452169-3-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20241203075622.2452169-1-o.rempel@pengutronix.de> References: <20241203075622.2452169-1-o.rempel@pengutronix.de> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-SA-Exim-Connect-IP: 2a0a:edc0:0:c01:1d::a2 X-SA-Exim-Mail-From: ore@pengutronix.de X-SA-Exim-Scanned: No (on metis.whiteo.stw.pengutronix.de); SAEximRunCond expanded to false X-PTX-Original-Recipient: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Jakub Kicinski Introduce a new way to report PHY statistics in a structured and standardized format using the netlink API. This new method does not replace the old driver-specific stats, which can still be accessed with `ethtool -S `. The structured stats are available with `ethtool -S --all-groups`. This new method makes it easier to diagnose problems by organizing stats in a consistent and documented way. Signed-off-by: Jakub Kicinski Signed-off-by: Oleksij Rempel --- Documentation/networking/ethtool-netlink.rst | 1 + include/linux/ethtool.h | 23 +++++++++++ include/linux/phy.h | 3 +- include/uapi/linux/ethtool.h | 2 + include/uapi/linux/ethtool_netlink.h | 15 +++++++ net/ethtool/netlink.h | 1 + net/ethtool/stats.c | 43 +++++++++++++++++++- net/ethtool/strset.c | 5 +++ 8 files changed, 90 insertions(+), 3 deletions(-) diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/n= etworking/ethtool-netlink.rst index b25926071ece..3bc6003185a1 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -1611,6 +1611,7 @@ the ``ETHTOOL_A_STATS_GROUPS`` bitset. Currently defi= ned 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 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D =3D=3D= =3D=3D=3D=3D=3D=3D =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D =20 Each group should have a corresponding ``ETHTOOL_A_STATS_GRP`` in the repl= y. diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index b8b935b52603..b0ed740ca749 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -412,6 +412,29 @@ struct ethtool_eth_phy_stats { ); }; =20 +/** + * struct ethtool_phy_stats - PHY-level statistics counters + * @rx_packets: Total successfully received frames + * @rx_bytes: Total successfully received bytes + * @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 + * + * This structure provides a standardized interface for reporting + * PHY-level statistics counters. It is designed to expose statistics + * commonly provided by PHYs but not explicitly defined in the IEEE + * 802.3 standard. + */ +struct ethtool_phy_stats { + u64 rx_packets; + u64 rx_bytes; + u64 rx_errors; + u64 tx_packets; + u64 tx_bytes; + u64 tx_errors; +}; + /* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed * via a more targeted API. */ diff --git a/include/linux/phy.h b/include/linux/phy.h index 523195c724b5..20a0d43ab5d4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1097,7 +1097,8 @@ struct phy_driver { * must only set statistics which are actually collected by the device. */ void (*get_phy_stats)(struct phy_device *dev, - struct ethtool_eth_phy_stats *eth_stats); + struct ethtool_eth_phy_stats *eth_stats, + struct ethtool_phy_stats *stats); void (*get_link_stats)(struct phy_device *dev, struct ethtool_link_ext_stats *link_stats); /** @get_sset_count: Number of statistic counters */ diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 7e1b3820f91f..d1089b88efc7 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -681,6 +681,7 @@ enum ethtool_link_ext_substate_module { * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics * @ETH_SS_STATS_RMON: names of RMON statistics + * @ETH_SS_STATS_PHY: names of PHY(dev) statistics * * @ETH_SS_COUNT: number of defined string sets */ @@ -706,6 +707,7 @@ enum ethtool_stringset { ETH_SS_STATS_ETH_MAC, ETH_SS_STATS_ETH_CTRL, ETH_SS_STATS_RMON, + ETH_SS_STATS_PHY, =20 /* add new constants above here */ ETH_SS_COUNT diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/etht= ool_netlink.h index 283305f6b063..dc332f8aa3a6 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -820,6 +820,7 @@ enum { ETHTOOL_STATS_ETH_MAC, ETHTOOL_STATS_ETH_CTRL, ETHTOOL_STATS_RMON, + ETHTOOL_STATS_PHY, =20 /* add new constants above here */ __ETHTOOL_STATS_CNT @@ -935,6 +936,20 @@ enum { ETHTOOL_A_STATS_RMON_MAX =3D (__ETHTOOL_A_STATS_RMON_CNT - 1) }; =20 +enum { + /* Basic packet counters if PHY has separate counters from the MAC */ + ETHTOOL_A_STATS_PHY_RX_PKTS, + ETHTOOL_A_STATS_PHY_RX_BYTES, + ETHTOOL_A_STATS_PHY_RX_ERRORS, + ETHTOOL_A_STATS_PHY_TX_PKTS, + ETHTOOL_A_STATS_PHY_TX_BYTES, + ETHTOOL_A_STATS_PHY_TX_ERRORS, + + /* add new constants above here */ + __ETHTOOL_A_STATS_PHY_CNT, + ETHTOOL_A_STATS_PHY_MAX =3D (__ETHTOOL_A_STATS_PHY_CNT - 1) +}; + /* MODULE */ =20 enum { diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index 203b08eb6c6f..3a6ecdcb5b8c 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -505,5 +505,6 @@ extern const char stats_eth_phy_names[__ETHTOOL_A_STATS= _ETH_PHY_CNT][ETH_GSTRING extern const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_G= STRING_LEN]; extern const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH= _GSTRING_LEN]; extern const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING= _LEN]; +extern const char stats_phy_names[__ETHTOOL_A_STATS_PHY_CNT][ETH_GSTRING_L= EN]; =20 #endif /* _NET_ETHTOOL_NETLINK_H */ diff --git a/net/ethtool/stats.c b/net/ethtool/stats.c index cf802b1cda6f..ec3b0a2e01e8 100644 --- a/net/ethtool/stats.c +++ b/net/ethtool/stats.c @@ -22,6 +22,7 @@ struct stats_reply_data { struct ethtool_eth_mac_stats mac_stats; struct ethtool_eth_ctrl_stats ctrl_stats; struct ethtool_rmon_stats rmon_stats; + struct ethtool_phy_stats phydev_stats; ); const struct ethtool_rmon_hist_range *rmon_ranges; }; @@ -34,6 +35,7 @@ const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRI= NG_LEN] =3D { [ETHTOOL_STATS_ETH_MAC] =3D "eth-mac", [ETHTOOL_STATS_ETH_CTRL] =3D "eth-ctrl", [ETHTOOL_STATS_RMON] =3D "rmon", + [ETHTOOL_STATS_PHY] =3D "phydev", }; =20 const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_= LEN] =3D { @@ -78,6 +80,15 @@ const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][= ETH_GSTRING_LEN] =3D { [ETHTOOL_A_STATS_RMON_JABBER] =3D "etherStatsJabbers", }; =20 +const char stats_phy_names[__ETHTOOL_A_STATS_PHY_CNT][ETH_GSTRING_LEN] =3D= { + [ETHTOOL_A_STATS_PHY_RX_PKTS] =3D "RxFrames", + [ETHTOOL_A_STATS_PHY_RX_BYTES] =3D "RxOctets", + [ETHTOOL_A_STATS_PHY_RX_ERRORS] =3D "RxErrors", + [ETHTOOL_A_STATS_PHY_TX_PKTS] =3D "TxFrames", + [ETHTOOL_A_STATS_PHY_TX_BYTES] =3D "TxOctets", + [ETHTOOL_A_STATS_PHY_TX_ERRORS] =3D "TxErrors", +}; + const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = =3D { [ETHTOOL_A_STATS_HEADER] =3D NLA_POLICY_NESTED(ethnl_header_policy), @@ -123,7 +134,8 @@ ethtool_get_phydev_stats(struct net_device *dev, struct= stats_reply_data *data) return; =20 mutex_lock(&phydev->lock); - phydev->drv->get_phy_stats(phydev, &data->phy_stats); + phydev->drv->get_phy_stats(phydev, &data->phy_stats, + &data->phydev_stats); mutex_unlock(&phydev->lock); } =20 @@ -160,7 +172,8 @@ static int stats_prepare_data(const struct ethnl_req_in= fo *req_base, data->ctrl_stats.src =3D src; data->rmon_stats.src =3D src; =20 - if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && + if ((test_bit(ETHTOOL_STATS_PHY, req_info->stat_mask) || + test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) && src =3D=3D ETHTOOL_MAC_STATS_SRC_AGGREGATE) ethtool_get_phydev_stats(dev, data); =20 @@ -213,6 +226,10 @@ static int stats_reply_size(const struct ethnl_req_inf= o *req_base, nla_total_size(4)) * /* _A_STATS_GRP_HIST_BKT_HI */ ETHTOOL_RMON_HIST_MAX * 2; } + if (test_bit(ETHTOOL_STATS_PHY, req_info->stat_mask)) { + n_stats +=3D sizeof(struct ethtool_phy_stats) / sizeof(u64); + n_grps++; + } =20 len +=3D n_grps * (nla_total_size(0) + /* _A_STATS_GRP */ nla_total_size(4) + /* _A_STATS_GRP_ID */ @@ -266,6 +283,25 @@ static int stats_put_phy_stats(struct sk_buff *skb, return 0; } =20 +static int stats_put_phydev_stats(struct sk_buff *skb, + const struct stats_reply_data *data) +{ + if (stat_put(skb, ETHTOOL_A_STATS_PHY_RX_PKTS, + data->phydev_stats.rx_packets) || + stat_put(skb, ETHTOOL_A_STATS_PHY_RX_BYTES, + data->phydev_stats.rx_bytes) || + stat_put(skb, ETHTOOL_A_STATS_PHY_RX_ERRORS, + data->phydev_stats.rx_errors) || + stat_put(skb, ETHTOOL_A_STATS_PHY_TX_PKTS, + data->phydev_stats.tx_packets) || + 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)) + return -EMSGSIZE; + return 0; +} + static int stats_put_mac_stats(struct sk_buff *skb, const struct stats_reply_data *data) { @@ -442,6 +478,9 @@ static int stats_fill_reply(struct sk_buff *skb, if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) ret =3D stats_put_stats(skb, data, ETHTOOL_STATS_RMON, ETH_SS_STATS_RMON, stats_put_rmon_stats); + if (!ret && test_bit(ETHTOOL_STATS_PHY, req_info->stat_mask)) + ret =3D stats_put_stats(skb, data, ETHTOOL_STATS_PHY, + ETH_SS_STATS_PHY, stats_put_phydev_stats); =20 return ret; } diff --git a/net/ethtool/strset.c b/net/ethtool/strset.c index b3382b3cf325..818cf01f0911 100644 --- a/net/ethtool/strset.c +++ b/net/ethtool/strset.c @@ -105,6 +105,11 @@ static const struct strset_info info_template[] =3D { .count =3D __ETHTOOL_A_STATS_RMON_CNT, .strings =3D stats_rmon_names, }, + [ETH_SS_STATS_PHY] =3D { + .per_dev =3D false, + .count =3D __ETHTOOL_A_STATS_PHY_CNT, + .strings =3D stats_phy_names, + }, }; =20 struct strset_req_info { --=20 2.39.5