From nobody Wed Sep 10 05:16:08 2025 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 5A169304994 for ; Mon, 8 Sep 2025 12:46:29 +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=1757335591; cv=none; b=K8z3yDqBdTvUrQCNuLhWIPkSV+CinkFcBNuypAIKSZ1WlSy/475VhGKp4Pof6vJvwc6+dFwg4EQM+u6UOGve4gLCfUy+YKNCik+qWzNGdA+b6WI5EyYnktvQKZcnNUZ1zgRiaF1VwwEXgRtfAD6sCB9MyA1kr5D71bZn7kkV5z8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1757335591; c=relaxed/simple; bh=eIoIGMNmHpncgrZqDWheiR7G/ij0Gu/wz7/Y5EiAJMU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=cUkZImmuag9bzz4HXk/EiVM02OkBOgCSFr/v8r9sw+rF+wZSLi1rlPBHf6NgAd2+Srlgs6YSTHg1bqicrpdSIpTzNua4dSoP9rJufAYx69xVP86R0+9yt9GZYQL2w40oIpg7sYDDLl7+ay7NNnqWsaSYqNHD52+cc7ODcnnRKEg= 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 1uvbG9-0003S7-F8; Mon, 08 Sep 2025 14:46:13 +0200 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 1uvbG7-000Fiq-2a; Mon, 08 Sep 2025 14:46:11 +0200 Received: from ore by dude04.red.stw.pengutronix.de with local (Exim 4.98.2) (envelope-from ) id 1uvbG7-0000000CKJd-2xFm; Mon, 08 Sep 2025 14:46:11 +0200 From: Oleksij Rempel To: Andrew Lunn , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Donald Hunter , Jonathan Corbet , Heiner Kallweit , Russell King , Kory Maincent , Maxime Chevallier , Nishanth Menon Cc: Oleksij Rempel , kernel@pengutronix.de, linux-kernel@vger.kernel.org, netdev@vger.kernel.org, UNGLinuxDriver@microchip.com, linux-doc@vger.kernel.org, Michal Kubecek , Roan van Dijk Subject: [PATCH net-next v5 3/5] ethtool: netlink: add lightweight MSE reporting to LINKSTATE_GET Date: Mon, 8 Sep 2025 14:46:08 +0200 Message-ID: <20250908124610.2937939-4-o.rempel@pengutronix.de> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20250908124610.2937939-1-o.rempel@pengutronix.de> References: <20250908124610.2937939-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-Type: text/plain; charset="utf-8" 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 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 =E2=80=9Cfast path=E2=80=9D 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 Reviewed-by: Kory Maincent --- changes v3: - add missing yaml spec --- Documentation/netlink/specs/ethtool.yaml | 9 ++ Documentation/networking/ethtool-netlink.rst | 9 ++ .../uapi/linux/ethtool_netlink_generated.h | 3 + net/ethtool/linkstate.c | 84 +++++++++++++++++++ 4 files changed, 105 insertions(+) diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netli= nk/specs/ethtool.yaml index d69dd3fb534b..3f1f19c9c18a 100644 --- a/Documentation/netlink/specs/ethtool.yaml +++ b/Documentation/netlink/specs/ethtool.yaml @@ -708,6 +708,15 @@ attribute-sets: - name: ext-down-cnt type: u32 + - + name: mse-value + type: u32 + - + name: mse-max + type: u32 + - + name: mse-channel + type: u32 - name: debug attr-cnt-name: __ethtool-a-debug-cnt diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/n= etworking/ethtool-netlink.rst index aae53f39c2b0..b3ebd4da3562 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 =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 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. =20 +``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). =20 diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/= linux/ethtool_netlink_generated.h index 8317e4b230a5..a9ef0ec11695 100644 --- a/include/uapi/linux/ethtool_netlink_generated.h +++ b/include/uapi/linux/ethtool_netlink_generated.h @@ -331,6 +331,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, =20 __ETHTOOL_A_LINKSTATE_CNT, ETHTOOL_A_LINKSTATE_MAX =3D (__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_d= ata *data) data->sqi <=3D data->sqi_max; } =20 +static int linkstate_get_mse(struct phy_device *phydev, + struct linkstate_reply_data *data) +{ + struct phy_mse_snapshot snapshot =3D {}; + struct phy_mse_config config =3D {}; + 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 =3D -EOPNOTSUPP; + goto unlock; + } + + if (!phydev->link) { + ret =3D -ENETDOWN; + goto unlock; + } + + ret =3D phydev->drv->get_mse_config(phydev, &config); + if (ret) + goto unlock; + + if (config.supported_caps & PHY_MSE_CAP_WORST_CHANNEL) { + channel =3D PHY_MSE_CHANNEL_WORST; + } else if (config.supported_caps & PHY_MSE_CAP_LINK) { + channel =3D PHY_MSE_CHANNEL_LINK; + } else { + ret =3D -EOPNOTSUPP; + goto unlock; + } + + ret =3D phydev->drv->get_mse_snapshot(phydev, channel, &snapshot); + if (ret) + goto unlock; + + data->mse_value =3D snapshot.average_mse; + data->mse_max =3D config.max_average_mse; + data->mse_channel =3D channel; + +unlock: + mutex_unlock(&phydev->lock); + return ret; +} + +static bool linkstate_mse_critical_error(int err) +{ + return err < 0 && err !=3D -EOPNOTSUPP && err !=3D -ENETDOWN; +} + +static bool linkstate_mse_valid(struct linkstate_reply_data *data) +{ + return data->mse_max > 0 && data->mse_value <=3D 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_r= eq_info *req_base, goto out; data->sqi_max =3D ret; =20 + ret =3D linkstate_get_mse(phydev, data); + if (linkstate_mse_critical_error(ret)) + goto out; + if (dev->flags & IFF_UP) { ret =3D linkstate_get_link_ext_state(dev, data); if (ret < 0 && ret !=3D -EOPNOTSUPP && ret !=3D -ENODATA) @@ -164,6 +230,12 @@ static int linkstate_reply_size(const struct ethnl_req= _info *req_base, len +=3D nla_total_size(sizeof(u32)); /* LINKSTATE_SQI_MAX */ } =20 + if (linkstate_mse_valid(data)) { + len +=3D nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_VALUE */ + len +=3D nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_MAX */ + len +=3D nla_total_size(sizeof(u32)); /* LINKSTATE_MSE_CHANNEL */ + } + if (data->link_ext_state_provided) len +=3D nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_STATE */ =20 @@ -195,6 +267,18 @@ static int linkstate_fill_reply(struct sk_buff *skb, return -EMSGSIZE; } =20 + 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)) --=20 2.47.3