[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20250813081453.3567604-4-o.rempel@pengutronix.de>
Date: Wed, 13 Aug 2025 10:14:51 +0200
From: Oleksij Rempel <o.rempel@...gutronix.de>
To: Andrew Lunn <andrew@...n.ch>,
Jakub Kicinski <kuba@...nel.org>,
"David S. Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Paolo Abeni <pabeni@...hat.com>,
Simon Horman <horms@...nel.org>,
Donald Hunter <donald.hunter@...il.com>,
Jonathan Corbet <corbet@....net>,
Heiner Kallweit <hkallweit1@...il.com>,
Russell King <linux@...linux.org.uk>,
Kory Maincent <kory.maincent@...tlin.com>,
Maxime Chevallier <maxime.chevallier@...tlin.com>,
Nishanth Menon <nm@...com>
Cc: Oleksij Rempel <o.rempel@...gutronix.de>,
kernel@...gutronix.de,
linux-kernel@...r.kernel.org,
netdev@...r.kernel.org,
UNGLinuxDriver@...rochip.com,
linux-doc@...r.kernel.org,
Michal Kubecek <mkubecek@...e.cz>,
Roan van Dijk <roan@...tonic.nl>
Subject: [PATCH net-next v1 3/5] ethtool: netlink: add lightweight MSE reporting to LINKSTATE_GET
Extend ETHTOOL_MSG_LINKSTATE_GET to optionally return a simplified
Mean Square Error (MSE) reading alongside existing link status fields.
The new attributes are:
- ETHTOOL_A_LINKSTATE_MSE_VALUE: current average MSE value
- ETHTOOL_A_LINKSTATE_MSE_MAX: scale limit for the reported value
- ETHTOOL_A_LINKSTATE_MSE_CHANNEL: source channel selector
This path reuses the PHY MSE core API, but only retrieves a single
value intended for quick link-health checks:
* If the PHY supports a WORST channel selector, report its current
average MSE.
* Otherwise, if LINK-wide measurements are supported, report those.
* If neither is available, omit the attributes.
Unlike the full MSE_GET interface, LINKSTATE_GET does not expose
per-channel or peak/worst-peak values and incurs minimal overhead.
Drivers that implement get_mse_config() / get_mse_snapshot() will
automatically populate this data.
The intent is to provide tooling with a “fast path” health indicator
without issuing a separate MSE_GET request, though the long-term
overlap with the full interface may need reevaluation.
Signed-off-by: Oleksij Rempel <o.rempel@...gutronix.de>
---
Documentation/networking/ethtool-netlink.rst | 9 ++
.../uapi/linux/ethtool_netlink_generated.h | 3 +
net/ethtool/linkstate.c | 84 +++++++++++++++++++
3 files changed, 96 insertions(+)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index 4f89c0b4e44e..696a6a6bbeb2 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -530,6 +530,9 @@ Kernel response contents:
``ETHTOOL_A_LINKSTATE_EXT_STATE`` u8 link extended state
``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE`` u8 link extended substate
``ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT`` u32 count of link down events
+ ``ETHTOOL_A_LINKSTATE_MSE_VALUE`` u32 Current average MSE value
+ ``ETHTOOL_A_LINKSTATE_MSE_MAX`` u32 Max scale for average MSE
+ ``ETHTOOL_A_LINKSTATE_MSE_CHANNEL`` u32 Source of MSE value
==================================== ====== ============================
For most NIC drivers, the value of ``ETHTOOL_A_LINKSTATE_LINK`` returns
@@ -541,6 +544,12 @@ optional values. ethtool core can provide either both
``ETHTOOL_A_LINKSTATE_EXT_STATE`` and ``ETHTOOL_A_LINKSTATE_EXT_SUBSTATE``,
or only ``ETHTOOL_A_LINKSTATE_EXT_STATE``, or none of them.
+``ETHTOOL_A_LINKSTATE_MSE_VALUE`` and ``ETHTOOL_A_LINKSTATE_MSE_MAX`` are
+optional values. The MSE value provided by this interface is a lightweight,
+less detailed version for quick health checks. If only one channel is used, it
+returns the current average MSE value. If multiple channels are supported, it
+returns the current average MSE of the channel with the worst MSE.
+
``LINKSTATE_GET`` allows dump requests (kernel returns reply messages for all
devices supporting the request).
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 9c37a96a320b..6ef03a7de4ab 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -322,6 +322,9 @@ enum {
ETHTOOL_A_LINKSTATE_EXT_STATE,
ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,
ETHTOOL_A_LINKSTATE_EXT_DOWN_CNT,
+ ETHTOOL_A_LINKSTATE_MSE_VALUE,
+ ETHTOOL_A_LINKSTATE_MSE_MAX,
+ ETHTOOL_A_LINKSTATE_MSE_CHANNEL,
__ETHTOOL_A_LINKSTATE_CNT,
ETHTOOL_A_LINKSTATE_MAX = (__ETHTOOL_A_LINKSTATE_CNT - 1)
diff --git a/net/ethtool/linkstate.c b/net/ethtool/linkstate.c
index 05a5f72c99fa..b27fb0ffc526 100644
--- a/net/ethtool/linkstate.c
+++ b/net/ethtool/linkstate.c
@@ -14,6 +14,9 @@ struct linkstate_reply_data {
int link;
int sqi;
int sqi_max;
+ u32 mse_value;
+ u32 mse_max;
+ u32 mse_channel;
struct ethtool_link_ext_stats link_stats;
bool link_ext_state_provided;
struct ethtool_link_ext_state_info ethtool_link_ext_state_info;
@@ -76,6 +79,65 @@ static bool linkstate_sqi_valid(struct linkstate_reply_data *data)
data->sqi <= data->sqi_max;
}
+static int linkstate_get_mse(struct phy_device *phydev,
+ struct linkstate_reply_data *data)
+{
+ struct phy_mse_snapshot snapshot = {};
+ struct phy_mse_config config = {};
+ int channel, ret;
+
+ if (!phydev)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&phydev->lock);
+
+ if (!phydev->drv || !phydev->drv->get_mse_config ||
+ !phydev->drv->get_mse_snapshot) {
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ if (!phydev->link) {
+ ret = -ENETDOWN;
+ goto unlock;
+ }
+
+ ret = phydev->drv->get_mse_config(phydev, &config);
+ if (ret)
+ goto unlock;
+
+ if (config.supported_caps & PHY_MSE_CAP_WORST_CHANNEL) {
+ channel = PHY_MSE_CHANNEL_WORST;
+ } else if (config.supported_caps & PHY_MSE_CAP_LINK) {
+ channel = PHY_MSE_CHANNEL_LINK;
+ } else {
+ ret = -EOPNOTSUPP;
+ goto unlock;
+ }
+
+ ret = phydev->drv->get_mse_snapshot(phydev, channel, &snapshot);
+ if (ret)
+ goto unlock;
+
+ data->mse_value = snapshot.average_mse;
+ data->mse_max = config.max_average_mse;
+ data->mse_channel = channel;
+
+unlock:
+ mutex_unlock(&phydev->lock);
+ return ret;
+}
+
+static bool linkstate_mse_critical_error(int err)
+{
+ return err < 0 && err != -EOPNOTSUPP && err != -ENETDOWN;
+}
+
+static bool linkstate_mse_valid(struct linkstate_reply_data *data)
+{
+ return data->mse_max > 0 && data->mse_value <= data->mse_max;
+}
+
static int linkstate_get_link_ext_state(struct net_device *dev,
struct linkstate_reply_data *data)
{
@@ -125,6 +187,10 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
goto out;
data->sqi_max = ret;
+ ret = linkstate_get_mse(phydev, data);
+ if (linkstate_mse_critical_error(ret))
+ goto out;
+
if (dev->flags & IFF_UP) {
ret = linkstate_get_link_ext_state(dev, data);
if (ret < 0 && ret != -EOPNOTSUPP && ret != -ENODATA)
@@ -164,6 +230,12 @@ static int linkstate_reply_size(const struct ethnl_req_info *req_base,
len += nla_total_size(sizeof(u32)); /* LINKSTATE_SQI_MAX */
}
+ if (linkstate_mse_valid(data)) {
+ len += nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_VALUE */
+ len += nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_MAX */
+ len += nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_CHANNEL */
+ }
+
if (data->link_ext_state_provided)
len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_STATE */
@@ -195,6 +267,18 @@ static int linkstate_fill_reply(struct sk_buff *skb,
return -EMSGSIZE;
}
+ if (linkstate_mse_valid(data)) {
+ if (nla_put_u32(skb, ETHTOOL_A_LINKSTATE_MSE_VALUE,
+ data->mse_value))
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHTOOL_A_LINKSTATE_MSE_MAX,
+ data->mse_max))
+ return -EMSGSIZE;
+ if (nla_put_u32(skb, ETHTOOL_A_LINKSTATE_MSE_CHANNEL,
+ data->mse_channel))
+ return -EMSGSIZE;
+ }
+
if (data->link_ext_state_provided) {
if (nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_STATE,
data->ethtool_link_ext_state_info.link_ext_state))
--
2.39.5
Powered by blists - more mailing lists