* [PATCH net-next v8 1/4] net: phy: introduce internal API for PHY MSE diagnostics
2025-10-27 12:27 [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers Oleksij Rempel
@ 2025-10-27 12:27 ` Oleksij Rempel
2025-10-27 12:27 ` [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access Oleksij Rempel
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-10-27 12:27 UTC (permalink / raw)
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, linux-kernel, netdev, UNGLinuxDriver,
linux-doc, Michal Kubecek, Roan van Dijk
Add the base infrastructure for Mean Square Error (MSE) diagnostics,
as proposed by the OPEN Alliance "Advanced diagnostic features for
100BASE-T1 automotive Ethernet PHYs" [1] specification.
The OPEN Alliance spec defines only average MSE and average peak MSE
over a fixed number of symbols. However, other PHYs, such as the
KSZ9131, additionally expose a worst-peak MSE value latched since the
last channel capture. This API accounts for such vendor extensions by
adding a distinct capability bit and snapshot field.
Channel-to-pair mapping is normally straightforward, but in some cases
(e.g. 100BASE-TX with MDI-X resolution unknown) the mapping is ambiguous.
If hardware does not expose MDI-X status, the exact pair cannot be
determined. To avoid returning misleading per-channel data in this case,
a LINK selector is defined for aggregate MSE measurements.
All investigated devices differ in MSE capabilities, such
as sample rate, number of analyzed symbols, and scaling factors.
For example, the KSZ9131 uses different scaling for MSE and pMSE.
To make this visible to callers, scale limits and timing information
are returned via get_mse_capability().
Some PHYs sample very few symbols at high frequency (e.g. 2 us update
rate). To cover such cases and allow for future high-speed PHYs with
even shorter intervals, the refresh rate is reported as u64 in
picoseconds.
This patch introduces the internal PHY API for Mean Square Error
diagnostics. It defines new kernel-side data types and driver hooks:
- struct phy_mse_capability: describes supported metrics, scale
limits, update interval, and sampling length.
- struct phy_mse_snapshot: holds one correlated measurement set.
- New phy_driver ops: `get_mse_capability()` and `get_mse_snapshot()`.
These definitions form the core kernel API. No user-visible interfaces
are added in this commit.
Standardization notes:
OPEN Alliance defines presence and interpretation of some metrics but does
not fix numeric scales or sampling internals:
- SQI (3-bit, 0..7) is mandatory; correlation to SNR/BER is informative
(OA 100BASE-T1 TC1 v1.0 6.1.2; OA 1000BASE-T1 TC12 v2.2 6.1.2).
- MSE is optional; OA recommends 2^16 symbols and scaling to 0..511,
with a worst-case latch since last read (OA 100BASE-T1 TC1 v1.0 6.1.1; OA
1000BASE-T1 TC12 v2.2 6.1.1). Refresh is recommended (~0.8-2.0 ms for
100BASE-T1; ~80-200 us for 1000BASE-T1). Exact scaling/time windows
are vendor-specific.
- Peak MSE (pMSE) is defined only for 100BASE-T1 as optional, e.g.
128-symbol sliding window with 8-bit range and worst-case latch (OA
100BASE-T1 TC1 v1.0 6.1.3).
Therefore this API exposes which measures and selectors a PHY supports,
and documents where behavior is standard-referenced vs vendor-specific.
[1] <https://opensig.org/wp-content/uploads/2024/01/
Advanced_PHY_features_for_automotive_Ethernet_V1.0.pdf>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
changes v8:
- use enum phy_mse_channel for the
- Make this patch kernel-internal only: no UAPI in this patch; ethtool
netlink exposure moved to patch 2.
- Drop user-space channel selector and capability flags from this patch;
keep only internal API and docs.
- Update commit message; add OA Technical Committee numbers (TC1 / TC12).
- Change get_mse_snapshot() callback to use enum phy_mse_channel
instead of u32
changes v7:
- add Reviewed-by
- fix "Unexpected indentation" error
- fix "Block quote ends without a blank line; unexpected unindent."
warning.
changes v6:
- YAML: generate mask for phy-mse-cap-
- YAML: Reorder phy-mse-capability flags to list channels first, then measures
- YAML: Drop explicit value: 0 on phy-mse-channel’s first enumerator
- YAML: Expand/clarify docs: add a short Standardization block
- UAPI: regenerate ethtool_netlink_generated.h with the new flag ordering
- phy.h: Remove channel field from struct phy_mse_snapshot
- phy.h: Use u64 for all snapshot/capability scalar fields (average_mse,
peak_mse, worst_peak_mse, max_*, refresh_rate_ps, num_symbols) for
range/consistency with netlink.
- phy.h: Update kerneldoc: note values are raw, device-scaled; point to
phy_mse_capability for interpretation; add brief OA references;
changes v5:
- clarify @channel direction in struct phy_mse_snapshot
- add per-field spec references for snapshot values
- refine YAML docstrings for phy-mse-channel and link selector
- update standardization notes (OA v1.0 and v2.2)
changes v4:
- remove -ENETDOWN as expected error value for get_mse_capability() and
get_mse_snapshot()
- fix htmldocs builds
---
include/linux/phy.h | 206 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 206 insertions(+)
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 17a2cdc9f1a0..0c9a2ef0ec75 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -903,6 +903,165 @@ struct phy_led {
#define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
+/*
+ * PHY_MSE_CAP_* - Bitmask flags for Mean Square Error (MSE) capabilities
+ *
+ * These flags describe which MSE metrics and selectors are implemented
+ * by the PHY for the current link mode. They are used in
+ * struct phy_mse_capability.supported_caps.
+ *
+ * Standardization:
+ * The OPEN Alliance (OA) defines the presence of MSE/SQI/pMSE but not their
+ * numeric scaling, update intervals, or aggregation windows. See:
+ * OA 100BASE-T1 TC1 v1.0, sections 6.1.1-6.1.3
+ * OA 1000BASE-T1 TC12 v2.2, sections 6.1.1-6.1.2
+ *
+ * Description of flags:
+ *
+ * PHY_MSE_CAP_CHANNEL_A
+ * Per-pair diagnostics for Channel A are supported. Mapping to the
+ * physical wire pair may depend on MDI/MDI-X polarity.
+ *
+ * PHY_MSE_CAP_CHANNEL_B, _C, _D
+ * Same as above for channels B-D.
+ *
+ * PHY_MSE_CAP_WORST_CHANNEL
+ * The PHY or driver can identify and report the single worst-performing
+ * channel without querying each one individually.
+ *
+ * PHY_MSE_CAP_LINK
+ * The PHY provides only a link-wide aggregate measurement or cannot map
+ * results to a specific pair (for example 100BASE-TX with unknown
+ * MDI/MDI-X).
+ *
+ * PHY_MSE_CAP_AVG
+ * Average MSE (mean DCQ metric) is supported. For 100/1000BASE-T1 the OA
+ * recommends 2^16 symbols, scaled 0..511, but the exact scaling is
+ * vendor-specific.
+ *
+ * PHY_MSE_CAP_PEAK
+ * Peak MSE (current peak within the measurement window) is supported.
+ * Defined as pMSE for 100BASE-T1; vendor-specific for others.
+ *
+ * PHY_MSE_CAP_WORST_PEAK
+ * Latched worst-case peak MSE since the last read (read-to-clear if
+ * implemented). Optional in OA 100BASE-T1 TC1 6.1.3.
+ */
+#define PHY_MSE_CAP_CHANNEL_A BIT(0)
+#define PHY_MSE_CAP_CHANNEL_B BIT(1)
+#define PHY_MSE_CAP_CHANNEL_C BIT(2)
+#define PHY_MSE_CAP_CHANNEL_D BIT(3)
+#define PHY_MSE_CAP_WORST_CHANNEL BIT(4)
+#define PHY_MSE_CAP_LINK BIT(5)
+#define PHY_MSE_CAP_AVG BIT(6)
+#define PHY_MSE_CAP_PEAK BIT(7)
+#define PHY_MSE_CAP_WORST_PEAK BIT(8)
+
+/*
+ * enum phy_mse_channel - Identifiers for selecting MSE measurement channels
+ *
+ * PHY_MSE_CHANNEL_A - PHY_MSE_CHANNEL_D
+ * Select per-pair measurement for the corresponding channel.
+ *
+ * PHY_MSE_CHANNEL_WORST
+ * Select the single worst-performing channel reported by hardware.
+ *
+ * PHY_MSE_CHANNEL_LINK
+ * Select link-wide aggregate data (used when per-pair results are
+ * unavailable).
+ */
+enum phy_mse_channel {
+ PHY_MSE_CHANNEL_A,
+ PHY_MSE_CHANNEL_B,
+ PHY_MSE_CHANNEL_C,
+ PHY_MSE_CHANNEL_D,
+ PHY_MSE_CHANNEL_WORST,
+ PHY_MSE_CHANNEL_LINK,
+};
+
+/**
+ * struct phy_mse_capability - Capabilities of Mean Square Error (MSE)
+ * measurement interface
+ *
+ * Standardization notes:
+ *
+ * - Presence of MSE/SQI/pMSE is defined by OPEN Alliance specs, but numeric
+ * scaling, refresh/update rate and aggregation windows are not fixed and
+ * are vendor-/product-specific. (OA 100BASE-T1 TC1 v1.0 6.1.*;
+ * OA 1000BASE-T1 TC12 v2.2 6.1.*)
+ *
+ * - Typical recommendations: 2^16 symbols and 0..511 scaling for MSE; pMSE only
+ * defined for 100BASE-T1 (sliding window example), others are vendor
+ * extensions. Drivers must report actual scale/limits here.
+ *
+ * Describes the MSE measurement capabilities for the current link mode. These
+ * properties are dynamic and may change when link settings are modified.
+ * Callers should re-query this capability after any link state change to
+ * ensure they have the most up-to-date information.
+ *
+ * Callers should only request measurements for channels and types that are
+ * indicated as supported by the @supported_caps bitmask. If @supported_caps
+ * is 0, the device provides no MSE diagnostics, and driver operations should
+ * typically return -EOPNOTSUPP.
+ *
+ * Snapshot values for average and peak MSE can be normalized to a 0..1 ratio
+ * by dividing the raw snapshot by the corresponding @max_average_mse or
+ * @max_peak_mse value.
+ *
+ * @max_average_mse: The maximum value for an average MSE snapshot. This
+ * defines the scale for the measurement. If the PHY_MSE_CAP_AVG capability is
+ * supported, this value MUST be greater than 0. (vendor-specific units).
+ * @max_peak_mse: The maximum value for a peak MSE snapshot. If either
+ * PHY_MSE_CAP_PEAK or PHY_MSE_CAP_WORST_PEAK is supported, this value MUST
+ * be greater than 0. (vendor-specific units).
+ * @refresh_rate_ps: The typical interval, in picoseconds, between hardware
+ * updates of the MSE values. This is an estimate, and callers should not
+ * assume synchronous sampling. (vendor-specific units).
+ * @num_symbols: The number of symbols aggregated per hardware sample to
+ * calculate the MSE. (vendor-specific units).
+ * @supported_caps: A bitmask of PHY_MSE_CAP_* values indicating which
+ * measurement types (e.g., average, peak) and channels
+ * (e.g., per-pair or link-wide) are supported.
+ */
+struct phy_mse_capability {
+ u64 max_average_mse;
+ u64 max_peak_mse;
+ u64 refresh_rate_ps;
+ u64 num_symbols;
+ u32 supported_caps;
+};
+
+/**
+ * struct phy_mse_snapshot - A snapshot of Mean Square Error (MSE) diagnostics
+ *
+ * Holds a set of MSE diagnostic values that were all captured from a single
+ * measurement window.
+ *
+ * Values are raw, device-scaled and not normalized. Use struct
+ * phy_mse_capability to interpret the scale and sampling window.
+ *
+ * @average_mse: The average MSE value over the measurement window.
+ * OPEN Alliance references MSE as a DCQ metric; recommends 2^16 symbols and
+ * 0..511 scaling. Exact scale and refresh are vendor-specific.
+ * (100BASE-T1 TC1 v1.0 6.1.1; 1000BASE-T1 TC12 v2.2 6.1.1).
+ *
+ * @peak_mse: The peak MSE value observed within the measurement window.
+ * For 100BASE-T1, "pMSE" is optional and may be implemented via a sliding
+ * 128-symbol window with periodic capture; not standardized for 1000BASE-T1.
+ * (100BASE-T1 TC1 v1.0 6.1.3, Table "DCQ.peakMSE").
+ *
+ * @worst_peak_mse: A latched high-water mark of the peak MSE since last read
+ * (read-to-clear if implemented). OPEN Alliance shows a latched "worst case
+ * peak MSE" for 100BASE-T1 pMSE; availability/semantics outside that are
+ * vendor-specific. (100BASE-T1 TC1 v1.0 6.1.3, DCQ.peakMSE high byte;
+ * 1000BASE-T1 TC12 v2.2 treats DCQ details as vendor-specific.)
+ */
+struct phy_mse_snapshot {
+ u64 average_mse;
+ u64 peak_mse;
+ u64 worst_peak_mse;
+};
+
/**
* struct phy_driver - Driver structure for a particular PHY type
*
@@ -1184,6 +1343,53 @@ struct phy_driver {
/** @get_sqi_max: Get the maximum signal quality indication */
int (*get_sqi_max)(struct phy_device *dev);
+ /**
+ * @get_mse_capability: Get capabilities and scale of MSE measurement
+ * @dev: PHY device
+ * @cap: Output (filled on success)
+ *
+ * Fill @cap with the PHY's MSE capability for the current
+ * link mode: scale limits (max_average_mse, max_peak_mse), update
+ * interval (refresh_rate_ps), sample length (num_symbols) and the
+ * capability bitmask (supported_caps).
+ *
+ * Implementations may defer capability report until hardware has
+ * converged; in that case they should return -EAGAIN and allow the
+ * caller to retry later.
+ *
+ * Return: 0 on success. On failure, returns a negative errno code, such
+ * as -EOPNOTSUPP if MSE measurement is not supported by the PHY or in
+ * the current link mode, or -EAGAIN if the capability information is
+ * not yet available.
+ */
+ int (*get_mse_capability)(struct phy_device *dev,
+ struct phy_mse_capability *cap);
+
+ /**
+ * @get_mse_snapshot: Retrieve a snapshot of MSE diagnostic values
+ * @dev: PHY device
+ * @channel: Channel identifier (PHY_MSE_CHANNEL_*)
+ * @snapshot: Output (filled on success)
+ *
+ * Fill @snapshot with a correlated set of MSE values from the most
+ * recent measurement window.
+ *
+ * Callers must validate @channel against supported_caps returned by
+ * get_mse_capability(). Drivers must not coerce @channel; if the
+ * requested selector is not implemented by the device or current link
+ * mode, the operation must fail.
+ *
+ * worst_peak_mse is latched and must be treated as read-to-clear.
+ *
+ * Return: 0 on success. On failure, returns a negative errno code, such
+ * as -EOPNOTSUPP if MSE measurement is not supported by the PHY or in
+ * the current link mode, or -EAGAIN if measurements are not yet
+ * available.
+ */
+ int (*get_mse_snapshot)(struct phy_device *dev,
+ enum phy_mse_channel channel,
+ struct phy_mse_snapshot *snapshot);
+
/* PLCA RS interface */
/** @get_plca_cfg: Return the current PLCA configuration */
int (*get_plca_cfg)(struct phy_device *dev,
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access
2025-10-27 12:27 [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers Oleksij Rempel
2025-10-27 12:27 ` [PATCH net-next v8 1/4] net: phy: introduce internal API for PHY MSE diagnostics Oleksij Rempel
@ 2025-10-27 12:27 ` Oleksij Rempel
2025-10-30 11:04 ` Paolo Abeni
2025-10-27 12:28 ` [PATCH net-next v8 3/4] net: phy: micrel: add MSE interface support for KSZ9477 family Oleksij Rempel
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Oleksij Rempel @ 2025-10-27 12:27 UTC (permalink / raw)
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, linux-kernel, netdev, UNGLinuxDriver,
linux-doc, Michal Kubecek, Roan van Dijk
Introduce the userspace entry point for PHY MSE diagnostics via
ethtool netlink. This exposes the core API added previously and
returns both capability information and one or more snapshots.
Userspace sends ETHTOOL_MSG_MSE_GET. The reply carries:
- ETHTOOL_A_MSE_CAPABILITIES: scale limits and timing information
- ETHTOOL_A_MSE_CHANNEL_* nests: one or more snapshots (per-channel
if available, otherwise WORST, otherwise LINK)
Link down returns -ENETDOWN.
Changes:
- YAML: add attribute sets (mse, mse-capabilities, mse-snapshot)
and the mse-get operation
- UAPI (generated): add ETHTOOL_A_MSE_* enums and message IDs,
ETHTOOL_MSG_MSE_GET/REPLY
- ethtool core: add net/ethtool/mse.c implementing the request,
register genl op, and hook into ethnl dispatch
- docs: document MSE_GET in ethtool-netlink.rst
The include/uapi/linux/ethtool_netlink_generated.h is generated
from Documentation/netlink/specs/ethtool.yaml.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
---
changes v8:
- drop user-space channel selector; kernel always returns available selectors
- drop supported-caps bitset from UAPI; keep only scale/timing fields
- keep capability flags internal to PHY API
- switch docs accordingly; clarify conditional presence of fields
changes v7:
- fix "Malformed table" error
changes v6:
- YAML: rename mse-config -> mse-capabilities;
rename top-level attr "config" -> "capabilities".
- YAML: drop all explicit UNSPEC entries; start enums at 1 with _CNT/_MAX tails.
- YAML: switch scalar fields to type: uint; remove pad attrs.
- YAML: per-channel reply layout:
replace multi-attr "snapshot" with fixed nests
ETHTOOL_A_MSE_CHANNEL_A/B/C/D/WORST_CHANNEL/LINK;
drop inner "channel" field.
- UAPI: regenerate include/uapi/linux/ethtool_netlink_generated.h
- mse.c: implement capabilities container and per-channel snapshot nests;
use nla_put_uint() for all YAML uint fields; size using sizeof(u64) in
reply_size(); no pad usage.
- mse.c: encode supported-caps with ethnl_bitset32_size()/ethnl_put_bitset32();
- mse.c: return -ENETDOWN on link down (commit message updated accordingly).
- docs: ethtool-netlink.rst updated to new attribute names/layout and
terminology (capabilities, per-channel nests).
changes v5:
- add struct phy_mse_snapshot and phy_mse_config in the documentation
changes v4:
- s/__ethtool-a-mse/--ethtool-a-mse
- remove duplicate kernel-doc line
- fix htmldocs compile warnings
---
Documentation/netlink/specs/ethtool.yaml | 86 +++++
Documentation/networking/ethtool-netlink.rst | 64 ++++
.../uapi/linux/ethtool_netlink_generated.h | 35 ++
net/ethtool/Makefile | 2 +-
net/ethtool/mse.c | 329 ++++++++++++++++++
net/ethtool/netlink.c | 10 +
net/ethtool/netlink.h | 2 +
7 files changed, 527 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/mse.c
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 6a0fb1974513..05d2b6508b59 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -1823,6 +1823,73 @@ attribute-sets:
type: uint
enum: pse-event
doc: List of events reported by the PSE controller
+ -
+ name: mse-capabilities
+ doc: MSE capabilities attribute set
+ attr-cnt-name: --ethtool-a-mse-capabilities-cnt
+ attributes:
+ -
+ name: max-average-mse
+ type: uint
+ -
+ name: max-peak-mse
+ type: uint
+ -
+ name: refresh-rate-ps
+ type: uint
+ -
+ name: num-symbols
+ type: uint
+ -
+ name: mse-snapshot
+ doc: MSE snapshot attribute set
+ attr-cnt-name: --ethtool-a-mse-snapshot-cnt
+ attributes:
+ -
+ name: average-mse
+ type: uint
+ -
+ name: peak-mse
+ type: uint
+ -
+ name: worst-peak-mse
+ type: uint
+ -
+ name: mse
+ attr-cnt-name: --ethtool-a-mse-cnt
+ attributes:
+ -
+ name: header
+ type: nest
+ nested-attributes: header
+ -
+ name: capabilities
+ type: nest
+ nested-attributes: mse-capabilities
+ -
+ name: channel-a
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-b
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-c
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: channel-d
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: worst-channel
+ type: nest
+ nested-attributes: mse-snapshot
+ -
+ name: link
+ type: nest
+ nested-attributes: mse-snapshot
operations:
enum-model: directional
@@ -2756,6 +2823,25 @@ operations:
attributes:
- header
- context
+ -
+ name: mse-get
+ doc: Get PHY MSE measurement data and capabilities.
+ attribute-set: mse
+ do: &mse-get-op
+ request:
+ attributes:
+ - header
+ reply:
+ attributes:
+ - header
+ - capabilities
+ - channel-a
+ - channel-b
+ - channel-c
+ - channel-d
+ - worst-channel
+ - link
+ dump: *mse-get-op
mcast-groups:
list:
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
index b270886c5f5d..af56c304cef4 100644
--- a/Documentation/networking/ethtool-netlink.rst
+++ b/Documentation/networking/ethtool-netlink.rst
@@ -242,6 +242,7 @@ Userspace to kernel:
``ETHTOOL_MSG_RSS_SET`` set RSS settings
``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context
``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context
+ ``ETHTOOL_MSG_MSE_GET`` get MSE diagnostic data
===================================== =================================
Kernel to userspace:
@@ -299,6 +300,7 @@ Kernel to userspace:
``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY`` create an additional RSS context
``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created
``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted
+ ``ETHTOOL_MSG_MSE_GET_REPLY`` MSE diagnostic data
======================================== =================================
``GET`` requests are sent by userspace applications to retrieve device
@@ -2458,6 +2460,68 @@ Kernel response contents:
For a description of each attribute, see ``TSCONFIG_GET``.
+MSE_GET
+=======
+
+Retrieves detailed Mean Square Error (MSE) diagnostic information from the PHY.
+
+Request Contents:
+
+ ==================================== ====== ============================
+ ``ETHTOOL_A_MSE_HEADER`` nested request header
+ ==================================== ====== ============================
+
+Kernel Response Contents:
+
+ ==================================== ====== ================================
+ ``ETHTOOL_A_MSE_HEADER`` nested reply header
+ ``ETHTOOL_A_MSE_CAPABILITIES`` nested capability/scale info for MSE
+ measurements
+ ``ETHTOOL_A_MSE_CHANNEL_A`` nested snapshot for Channel A
+ ``ETHTOOL_A_MSE_CHANNEL_B`` nested snapshot for Channel B
+ ``ETHTOOL_A_MSE_CHANNEL_C`` nested snapshot for Channel C
+ ``ETHTOOL_A_MSE_CHANNEL_D`` nested snapshot for Channel D
+ ``ETHTOOL_A_MSE_WORST_CHANNEL`` nested snapshot for worst channel
+ ``ETHTOOL_A_MSE_LINK`` nested snapshot for link-wide aggregate
+ ==================================== ====== ================================
+
+MSE Capabilities
+----------------
+
+This nested attribute reports the capability / scaling properties used to
+interpret snapshot values.
+
+ ============================================== ====== =========================
+ ``ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE`` uint max avg_mse scale
+ ``ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE`` uint max peak_mse scale
+ ``ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS`` uint sample rate (picoseconds)
+ ``ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS`` uint symbols per HW sample
+ ============================================== ====== =========================
+
+The max-average/peak fields are included only if the corresponding metric
+is supported by the PHY. Their absence indicates that the metric is not
+available.
+
+See ``struct phy_mse_capability`` kernel documentation in
+``include/linux/phy.h``.
+
+MSE Snapshot
+------------
+
+Each per-channel nest contains an atomic snapshot of MSE values for that
+selector (channel A/B/C/D, worst channel, or link).
+
+ ========================================== ====== ===================
+ ``ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE`` uint average MSE value
+ ``ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE`` uint current peak MSE
+ ``ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE`` uint worst-case peak MSE
+ ========================================== ====== ===================
+
+Within each channel nest, only the metrics supported by the PHY will be present.
+
+See ``struct phy_mse_snapshot`` kernel documentation in
+``include/linux/phy.h``.
+
Request translation
===================
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 0e8ac0d974e2..b71b175df46d 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -803,6 +803,39 @@ enum {
ETHTOOL_A_PSE_NTF_MAX = (__ETHTOOL_A_PSE_NTF_CNT - 1)
};
+enum {
+ ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE = 1,
+ ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
+ ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
+ ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
+
+ __ETHTOOL_A_MSE_CAPABILITIES_CNT,
+ ETHTOOL_A_MSE_CAPABILITIES_MAX = (__ETHTOOL_A_MSE_CAPABILITIES_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE = 1,
+ ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
+ ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
+
+ __ETHTOOL_A_MSE_SNAPSHOT_CNT,
+ ETHTOOL_A_MSE_SNAPSHOT_MAX = (__ETHTOOL_A_MSE_SNAPSHOT_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_MSE_HEADER = 1,
+ ETHTOOL_A_MSE_CAPABILITIES,
+ ETHTOOL_A_MSE_CHANNEL_A,
+ ETHTOOL_A_MSE_CHANNEL_B,
+ ETHTOOL_A_MSE_CHANNEL_C,
+ ETHTOOL_A_MSE_CHANNEL_D,
+ ETHTOOL_A_MSE_WORST_CHANNEL,
+ ETHTOOL_A_MSE_LINK,
+
+ __ETHTOOL_A_MSE_CNT,
+ ETHTOOL_A_MSE_MAX = (__ETHTOOL_A_MSE_CNT - 1)
+};
+
enum {
ETHTOOL_MSG_USER_NONE = 0,
ETHTOOL_MSG_STRSET_GET = 1,
@@ -855,6 +888,7 @@ enum {
ETHTOOL_MSG_RSS_SET,
ETHTOOL_MSG_RSS_CREATE_ACT,
ETHTOOL_MSG_RSS_DELETE_ACT,
+ ETHTOOL_MSG_MSE_GET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -915,6 +949,7 @@ enum {
ETHTOOL_MSG_RSS_CREATE_ACT_REPLY,
ETHTOOL_MSG_RSS_CREATE_NTF,
ETHTOOL_MSG_RSS_DELETE_NTF,
+ ETHTOOL_MSG_MSE_GET_REPLY,
__ETHTOOL_MSG_KERNEL_CNT,
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 1e493553b977..629c10916670 100644
--- a/net/ethtool/Makefile
+++ b/net/ethtool/Makefile
@@ -9,4 +9,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \
- phy.o tsconfig.o
+ phy.o tsconfig.o mse.o
diff --git a/net/ethtool/mse.c b/net/ethtool/mse.c
new file mode 100644
index 000000000000..dcc4c93c5d04
--- /dev/null
+++ b/net/ethtool/mse.c
@@ -0,0 +1,329 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/slab.h>
+
+#include "netlink.h"
+#include "common.h"
+
+/* Channels A-D only; WORST and LINK are exclusive alternatives */
+#define PHY_MSE_CHANNEL_COUNT 4
+
+struct mse_req_info {
+ struct ethnl_req_info base;
+};
+
+struct mse_snapshot_entry {
+ struct phy_mse_snapshot snapshot;
+ int channel;
+};
+
+struct mse_reply_data {
+ struct ethnl_reply_data base;
+ struct phy_mse_capability capability;
+ struct mse_snapshot_entry *snapshots;
+ unsigned int num_snapshots;
+};
+
+static struct mse_reply_data *
+mse_repdata(const struct ethnl_reply_data *reply_base)
+{
+ return container_of(reply_base, struct mse_reply_data, base);
+}
+
+const struct nla_policy ethnl_mse_get_policy[] = {
+ [ETHTOOL_A_MSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),
+};
+
+static int get_snapshot_if_supported(struct phy_device *phydev,
+ struct mse_reply_data *data,
+ unsigned int *idx, u32 cap_bit,
+ enum phy_mse_channel channel)
+{
+ int ret;
+
+ if (data->capability.supported_caps & cap_bit) {
+ ret = phydev->drv->get_mse_snapshot(phydev, channel,
+ &data->snapshots[*idx].snapshot);
+ if (ret)
+ return ret;
+ data->snapshots[*idx].channel = channel;
+ (*idx)++;
+ }
+
+ return 0;
+}
+
+static int mse_get_channels(struct phy_device *phydev,
+ struct mse_reply_data *data)
+{
+ unsigned int i = 0;
+ int ret;
+
+ if (!data->capability.supported_caps)
+ return 0;
+
+ data->snapshots = kcalloc(PHY_MSE_CHANNEL_COUNT,
+ sizeof(*data->snapshots), GFP_KERNEL);
+ if (!data->snapshots)
+ return -ENOMEM;
+
+ /* Priority 1: Individual channels */
+ ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_A,
+ PHY_MSE_CHANNEL_A);
+ if (ret)
+ return ret;
+ ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_B,
+ PHY_MSE_CHANNEL_B);
+ if (ret)
+ return ret;
+ ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_C,
+ PHY_MSE_CHANNEL_C);
+ if (ret)
+ return ret;
+ ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_D,
+ PHY_MSE_CHANNEL_D);
+ if (ret)
+ return ret;
+
+ /* If any individual channels were found, we are done. */
+ if (i > 0) {
+ data->num_snapshots = i;
+ return 0;
+ }
+
+ /* Priority 2: Worst channel, if no individual channels supported. */
+ ret = get_snapshot_if_supported(phydev, data, &i,
+ PHY_MSE_CAP_WORST_CHANNEL,
+ PHY_MSE_CHANNEL_WORST);
+ if (ret)
+ return ret;
+
+ /* If worst channel was found, we are done. */
+ if (i > 0) {
+ data->num_snapshots = i;
+ return 0;
+ }
+
+ /* Priority 3: Link-wide, if nothing else is supported. */
+ ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_LINK,
+ PHY_MSE_CHANNEL_LINK);
+ if (ret)
+ return ret;
+
+ data->num_snapshots = i;
+ return 0;
+}
+
+static int mse_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ const struct genl_info *info)
+{
+ struct mse_reply_data *data = mse_repdata(reply_base);
+ struct net_device *dev = reply_base->dev;
+ struct phy_device *phydev;
+ int ret;
+
+ phydev = ethnl_req_get_phydev(req_base, info->attrs,
+ ETHTOOL_A_MSE_HEADER, info->extack);
+ if (IS_ERR(phydev))
+ return PTR_ERR(phydev);
+ if (!phydev)
+ return -EOPNOTSUPP;
+
+ ret = ethnl_ops_begin(dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&phydev->lock);
+
+ if (!phydev->drv || !phydev->drv->get_mse_capability ||
+ !phydev->drv->get_mse_snapshot) {
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+ if (!phydev->link) {
+ ret = -ENETDOWN;
+ goto out_unlock;
+ }
+
+ ret = phydev->drv->get_mse_capability(phydev, &data->capability);
+ if (ret)
+ goto out_unlock;
+
+ ret = mse_get_channels(phydev, data);
+
+out_unlock:
+ mutex_unlock(&phydev->lock);
+ ethnl_ops_complete(dev);
+ if (ret)
+ kfree(data->snapshots);
+ return ret;
+}
+
+static void mse_cleanup_data(struct ethnl_reply_data *reply_base)
+{
+ struct mse_reply_data *data = mse_repdata(reply_base);
+
+ kfree(data->snapshots);
+}
+
+static int mse_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct mse_reply_data *data = mse_repdata(reply_base);
+ size_t len = 0;
+ unsigned int i;
+
+ /* ETHTOOL_A_MSE_CAPABILITIES */
+ len += nla_total_size(0);
+ if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
+ /* ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE */
+ len += nla_total_size(sizeof(u64));
+ if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
+ PHY_MSE_CAP_WORST_PEAK))
+ /* ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE */
+ len += nla_total_size(sizeof(u64));
+ /* ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS */
+ len += nla_total_size(sizeof(u64));
+ /* ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS */
+ len += nla_total_size(sizeof(u64));
+
+ for (i = 0; i < data->num_snapshots; i++) {
+ size_t snapshot_len = 0;
+
+ /* Per-channel nest (e.g., ETHTOOL_A_MSE_CHANNEL_A / _B / _C /
+ * _D / _WORST_CHANNEL / _LINK)
+ */
+ snapshot_len += nla_total_size(0);
+
+ if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
+ snapshot_len += nla_total_size(sizeof(u64));
+ if (data->capability.supported_caps & PHY_MSE_CAP_PEAK)
+ snapshot_len += nla_total_size(sizeof(u64));
+ if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK)
+ snapshot_len += nla_total_size(sizeof(u64));
+
+ len += snapshot_len;
+ }
+
+ return len;
+}
+
+static int mse_channel_to_attr(int ch)
+{
+ switch (ch) {
+ case PHY_MSE_CHANNEL_A:
+ return ETHTOOL_A_MSE_CHANNEL_A;
+ case PHY_MSE_CHANNEL_B:
+ return ETHTOOL_A_MSE_CHANNEL_B;
+ case PHY_MSE_CHANNEL_C:
+ return ETHTOOL_A_MSE_CHANNEL_C;
+ case PHY_MSE_CHANNEL_D:
+ return ETHTOOL_A_MSE_CHANNEL_D;
+ case PHY_MSE_CHANNEL_WORST:
+ return ETHTOOL_A_MSE_WORST_CHANNEL;
+ case PHY_MSE_CHANNEL_LINK:
+ return ETHTOOL_A_MSE_LINK;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mse_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct mse_reply_data *data = mse_repdata(reply_base);
+ struct nlattr *nest;
+ unsigned int i;
+ int ret;
+
+ nest = nla_nest_start(skb, ETHTOOL_A_MSE_CAPABILITIES);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
+ ret = nla_put_uint(skb,
+ ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE,
+ data->capability.max_average_mse);
+ if (ret < 0)
+ goto nla_put_nest_failure;
+ }
+
+ if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
+ PHY_MSE_CAP_WORST_PEAK)) {
+ ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
+ data->capability.max_peak_mse);
+ if (ret < 0)
+ goto nla_put_nest_failure;
+ }
+
+ ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
+ data->capability.refresh_rate_ps);
+ if (ret < 0)
+ goto nla_put_nest_failure;
+
+ ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
+ data->capability.num_symbols);
+ if (ret < 0)
+ goto nla_put_nest_failure;
+
+ nla_nest_end(skb, nest);
+
+ for (i = 0; i < data->num_snapshots; i++) {
+ const struct mse_snapshot_entry *s = &data->snapshots[i];
+ int chan_attr;
+
+ chan_attr = mse_channel_to_attr(s->channel);
+ if (chan_attr < 0)
+ return chan_attr;
+
+ nest = nla_nest_start(skb, chan_attr);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
+ ret = nla_put_uint(skb,
+ ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE,
+ s->snapshot.average_mse);
+ if (ret)
+ goto nla_put_nest_failure;
+ }
+ if (data->capability.supported_caps & PHY_MSE_CAP_PEAK) {
+ ret = nla_put_uint(skb, ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
+ s->snapshot.peak_mse);
+ if (ret)
+ goto nla_put_nest_failure;
+ }
+ if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK) {
+ ret = nla_put_uint(skb,
+ ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
+ s->snapshot.worst_peak_mse);
+ if (ret)
+ goto nla_put_nest_failure;
+ }
+
+ nla_nest_end(skb, nest);
+ }
+
+ return 0;
+
+nla_put_nest_failure:
+ nla_nest_cancel(skb, nest);
+ return ret;
+}
+
+const struct ethnl_request_ops ethnl_mse_request_ops = {
+ .request_cmd = ETHTOOL_MSG_MSE_GET,
+ .reply_cmd = ETHTOOL_MSG_MSE_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_MSE_HEADER,
+ .req_info_size = sizeof(struct mse_req_info),
+ .reply_data_size = sizeof(struct mse_reply_data),
+
+ .prepare_data = mse_prepare_data,
+ .cleanup_data = mse_cleanup_data,
+ .reply_size = mse_reply_size,
+ .fill_reply = mse_fill_reply,
+};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 2f813f25f07e..6e5f0f4f815a 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -420,6 +420,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_TSCONFIG_GET] = ðnl_tsconfig_request_ops,
[ETHTOOL_MSG_TSCONFIG_SET] = ðnl_tsconfig_request_ops,
[ETHTOOL_MSG_PHY_GET] = ðnl_phy_request_ops,
+ [ETHTOOL_MSG_MSE_GET] = ðnl_mse_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1534,6 +1535,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_rss_delete_policy,
.maxattr = ARRAY_SIZE(ethnl_rss_delete_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_MSE_GET,
+ .doit = ethnl_default_doit,
+ .start = ethnl_perphy_start,
+ .dumpit = ethnl_perphy_dumpit,
+ .done = ethnl_perphy_done,
+ .policy = ethnl_mse_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 1d4f9ecb3d26..89010eaa67df 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -442,6 +442,7 @@ extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
extern const struct ethnl_request_ops ethnl_mm_request_ops;
extern const struct ethnl_request_ops ethnl_phy_request_ops;
extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;
+extern const struct ethnl_request_ops ethnl_mse_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -497,6 +498,7 @@ extern const struct nla_policy ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE
extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];
extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];
extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];
+extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access
2025-10-27 12:27 ` [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access Oleksij Rempel
@ 2025-10-30 11:04 ` Paolo Abeni
2025-10-30 11:40 ` Oleksij Rempel
0 siblings, 1 reply; 8+ messages in thread
From: Paolo Abeni @ 2025-10-30 11:04 UTC (permalink / raw)
To: Oleksij Rempel, Andrew Lunn, Jakub Kicinski, David S. Miller,
Eric Dumazet, Simon Horman, Donald Hunter, Jonathan Corbet,
Heiner Kallweit, Russell King, Kory Maincent, Maxime Chevallier,
Nishanth Menon
Cc: kernel, linux-kernel, netdev, UNGLinuxDriver, linux-doc,
Michal Kubecek, Roan van Dijk
On 10/27/25 1:27 PM, Oleksij Rempel wrote:
> Introduce the userspace entry point for PHY MSE diagnostics via
> ethtool netlink. This exposes the core API added previously and
> returns both capability information and one or more snapshots.
>
> Userspace sends ETHTOOL_MSG_MSE_GET. The reply carries:
> - ETHTOOL_A_MSE_CAPABILITIES: scale limits and timing information
> - ETHTOOL_A_MSE_CHANNEL_* nests: one or more snapshots (per-channel
> if available, otherwise WORST, otherwise LINK)
>
> Link down returns -ENETDOWN.
>
> Changes:
> - YAML: add attribute sets (mse, mse-capabilities, mse-snapshot)
> and the mse-get operation
> - UAPI (generated): add ETHTOOL_A_MSE_* enums and message IDs,
> ETHTOOL_MSG_MSE_GET/REPLY
> - ethtool core: add net/ethtool/mse.c implementing the request,
> register genl op, and hook into ethnl dispatch
> - docs: document MSE_GET in ethtool-netlink.rst
>
> The include/uapi/linux/ethtool_netlink_generated.h is generated
> from Documentation/netlink/specs/ethtool.yaml.
>
> Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> ---
> changes v8:
> - drop user-space channel selector; kernel always returns available selectors
Overall LGTM, but it's unclear why you dropped the above. I understand
one of the goal here is to achieve fast data retrival. I _guess_ most of
the overhead is possibly due to phy regs access. Explicitly selecting a
single/limited number of channels could/should reduce the number of
registers access; it looks like a worthy option. What am I missing?
Thanks,
Paolo
> - drop supported-caps bitset from UAPI; keep only scale/timing fields
> - keep capability flags internal to PHY API
> - switch docs accordingly; clarify conditional presence of fields
> changes v7:
> - fix "Malformed table" error
> changes v6:
> - YAML: rename mse-config -> mse-capabilities;
> rename top-level attr "config" -> "capabilities".
> - YAML: drop all explicit UNSPEC entries; start enums at 1 with _CNT/_MAX tails.
> - YAML: switch scalar fields to type: uint; remove pad attrs.
> - YAML: per-channel reply layout:
> replace multi-attr "snapshot" with fixed nests
> ETHTOOL_A_MSE_CHANNEL_A/B/C/D/WORST_CHANNEL/LINK;
> drop inner "channel" field.
> - UAPI: regenerate include/uapi/linux/ethtool_netlink_generated.h
> - mse.c: implement capabilities container and per-channel snapshot nests;
> use nla_put_uint() for all YAML uint fields; size using sizeof(u64) in
> reply_size(); no pad usage.
> - mse.c: encode supported-caps with ethnl_bitset32_size()/ethnl_put_bitset32();
> - mse.c: return -ENETDOWN on link down (commit message updated accordingly).
> - docs: ethtool-netlink.rst updated to new attribute names/layout and
> terminology (capabilities, per-channel nests).
> changes v5:
> - add struct phy_mse_snapshot and phy_mse_config in the documentation
> changes v4:
> - s/__ethtool-a-mse/--ethtool-a-mse
> - remove duplicate kernel-doc line
> - fix htmldocs compile warnings
> ---
> Documentation/netlink/specs/ethtool.yaml | 86 +++++
> Documentation/networking/ethtool-netlink.rst | 64 ++++
> .../uapi/linux/ethtool_netlink_generated.h | 35 ++
> net/ethtool/Makefile | 2 +-
> net/ethtool/mse.c | 329 ++++++++++++++++++
> net/ethtool/netlink.c | 10 +
> net/ethtool/netlink.h | 2 +
> 7 files changed, 527 insertions(+), 1 deletion(-)
> create mode 100644 net/ethtool/mse.c
>
> diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
> index 6a0fb1974513..05d2b6508b59 100644
> --- a/Documentation/netlink/specs/ethtool.yaml
> +++ b/Documentation/netlink/specs/ethtool.yaml
> @@ -1823,6 +1823,73 @@ attribute-sets:
> type: uint
> enum: pse-event
> doc: List of events reported by the PSE controller
> + -
> + name: mse-capabilities
> + doc: MSE capabilities attribute set
> + attr-cnt-name: --ethtool-a-mse-capabilities-cnt
> + attributes:
> + -
> + name: max-average-mse
> + type: uint
> + -
> + name: max-peak-mse
> + type: uint
> + -
> + name: refresh-rate-ps
> + type: uint
> + -
> + name: num-symbols
> + type: uint
> + -
> + name: mse-snapshot
> + doc: MSE snapshot attribute set
> + attr-cnt-name: --ethtool-a-mse-snapshot-cnt
> + attributes:
> + -
> + name: average-mse
> + type: uint
> + -
> + name: peak-mse
> + type: uint
> + -
> + name: worst-peak-mse
> + type: uint
> + -
> + name: mse
> + attr-cnt-name: --ethtool-a-mse-cnt
> + attributes:
> + -
> + name: header
> + type: nest
> + nested-attributes: header
> + -
> + name: capabilities
> + type: nest
> + nested-attributes: mse-capabilities
> + -
> + name: channel-a
> + type: nest
> + nested-attributes: mse-snapshot
> + -
> + name: channel-b
> + type: nest
> + nested-attributes: mse-snapshot
> + -
> + name: channel-c
> + type: nest
> + nested-attributes: mse-snapshot
> + -
> + name: channel-d
> + type: nest
> + nested-attributes: mse-snapshot
> + -
> + name: worst-channel
> + type: nest
> + nested-attributes: mse-snapshot
> + -
> + name: link
> + type: nest
> + nested-attributes: mse-snapshot
>
> operations:
> enum-model: directional
> @@ -2756,6 +2823,25 @@ operations:
> attributes:
> - header
> - context
> + -
> + name: mse-get
> + doc: Get PHY MSE measurement data and capabilities.
> + attribute-set: mse
> + do: &mse-get-op
> + request:
> + attributes:
> + - header
> + reply:
> + attributes:
> + - header
> + - capabilities
> + - channel-a
> + - channel-b
> + - channel-c
> + - channel-d
> + - worst-channel
> + - link
> + dump: *mse-get-op
>
> mcast-groups:
> list:
> diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst
> index b270886c5f5d..af56c304cef4 100644
> --- a/Documentation/networking/ethtool-netlink.rst
> +++ b/Documentation/networking/ethtool-netlink.rst
> @@ -242,6 +242,7 @@ Userspace to kernel:
> ``ETHTOOL_MSG_RSS_SET`` set RSS settings
> ``ETHTOOL_MSG_RSS_CREATE_ACT`` create an additional RSS context
> ``ETHTOOL_MSG_RSS_DELETE_ACT`` delete an additional RSS context
> + ``ETHTOOL_MSG_MSE_GET`` get MSE diagnostic data
> ===================================== =================================
>
> Kernel to userspace:
> @@ -299,6 +300,7 @@ Kernel to userspace:
> ``ETHTOOL_MSG_RSS_CREATE_ACT_REPLY`` create an additional RSS context
> ``ETHTOOL_MSG_RSS_CREATE_NTF`` additional RSS context created
> ``ETHTOOL_MSG_RSS_DELETE_NTF`` additional RSS context deleted
> + ``ETHTOOL_MSG_MSE_GET_REPLY`` MSE diagnostic data
> ======================================== =================================
>
> ``GET`` requests are sent by userspace applications to retrieve device
> @@ -2458,6 +2460,68 @@ Kernel response contents:
>
> For a description of each attribute, see ``TSCONFIG_GET``.
>
> +MSE_GET
> +=======
> +
> +Retrieves detailed Mean Square Error (MSE) diagnostic information from the PHY.
> +
> +Request Contents:
> +
> + ==================================== ====== ============================
> + ``ETHTOOL_A_MSE_HEADER`` nested request header
> + ==================================== ====== ============================
> +
> +Kernel Response Contents:
> +
> + ==================================== ====== ================================
> + ``ETHTOOL_A_MSE_HEADER`` nested reply header
> + ``ETHTOOL_A_MSE_CAPABILITIES`` nested capability/scale info for MSE
> + measurements
> + ``ETHTOOL_A_MSE_CHANNEL_A`` nested snapshot for Channel A
> + ``ETHTOOL_A_MSE_CHANNEL_B`` nested snapshot for Channel B
> + ``ETHTOOL_A_MSE_CHANNEL_C`` nested snapshot for Channel C
> + ``ETHTOOL_A_MSE_CHANNEL_D`` nested snapshot for Channel D
> + ``ETHTOOL_A_MSE_WORST_CHANNEL`` nested snapshot for worst channel
> + ``ETHTOOL_A_MSE_LINK`` nested snapshot for link-wide aggregate
> + ==================================== ====== ================================
> +
> +MSE Capabilities
> +----------------
> +
> +This nested attribute reports the capability / scaling properties used to
> +interpret snapshot values.
> +
> + ============================================== ====== =========================
> + ``ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE`` uint max avg_mse scale
> + ``ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE`` uint max peak_mse scale
> + ``ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS`` uint sample rate (picoseconds)
> + ``ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS`` uint symbols per HW sample
> + ============================================== ====== =========================
> +
> +The max-average/peak fields are included only if the corresponding metric
> +is supported by the PHY. Their absence indicates that the metric is not
> +available.
> +
> +See ``struct phy_mse_capability`` kernel documentation in
> +``include/linux/phy.h``.
> +
> +MSE Snapshot
> +------------
> +
> +Each per-channel nest contains an atomic snapshot of MSE values for that
> +selector (channel A/B/C/D, worst channel, or link).
> +
> + ========================================== ====== ===================
> + ``ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE`` uint average MSE value
> + ``ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE`` uint current peak MSE
> + ``ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE`` uint worst-case peak MSE
> + ========================================== ====== ===================
> +
> +Within each channel nest, only the metrics supported by the PHY will be present.
> +
> +See ``struct phy_mse_snapshot`` kernel documentation in
> +``include/linux/phy.h``.
> +
> Request translation
> ===================
>
> diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
> index 0e8ac0d974e2..b71b175df46d 100644
> --- a/include/uapi/linux/ethtool_netlink_generated.h
> +++ b/include/uapi/linux/ethtool_netlink_generated.h
> @@ -803,6 +803,39 @@ enum {
> ETHTOOL_A_PSE_NTF_MAX = (__ETHTOOL_A_PSE_NTF_CNT - 1)
> };
>
> +enum {
> + ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE = 1,
> + ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
> + ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
> + ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
> +
> + __ETHTOOL_A_MSE_CAPABILITIES_CNT,
> + ETHTOOL_A_MSE_CAPABILITIES_MAX = (__ETHTOOL_A_MSE_CAPABILITIES_CNT - 1)
> +};
> +
> +enum {
> + ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE = 1,
> + ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
> + ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
> +
> + __ETHTOOL_A_MSE_SNAPSHOT_CNT,
> + ETHTOOL_A_MSE_SNAPSHOT_MAX = (__ETHTOOL_A_MSE_SNAPSHOT_CNT - 1)
> +};
> +
> +enum {
> + ETHTOOL_A_MSE_HEADER = 1,
> + ETHTOOL_A_MSE_CAPABILITIES,
> + ETHTOOL_A_MSE_CHANNEL_A,
> + ETHTOOL_A_MSE_CHANNEL_B,
> + ETHTOOL_A_MSE_CHANNEL_C,
> + ETHTOOL_A_MSE_CHANNEL_D,
> + ETHTOOL_A_MSE_WORST_CHANNEL,
> + ETHTOOL_A_MSE_LINK,
> +
> + __ETHTOOL_A_MSE_CNT,
> + ETHTOOL_A_MSE_MAX = (__ETHTOOL_A_MSE_CNT - 1)
> +};
> +
> enum {
> ETHTOOL_MSG_USER_NONE = 0,
> ETHTOOL_MSG_STRSET_GET = 1,
> @@ -855,6 +888,7 @@ enum {
> ETHTOOL_MSG_RSS_SET,
> ETHTOOL_MSG_RSS_CREATE_ACT,
> ETHTOOL_MSG_RSS_DELETE_ACT,
> + ETHTOOL_MSG_MSE_GET,
>
> __ETHTOOL_MSG_USER_CNT,
> ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
> @@ -915,6 +949,7 @@ enum {
> ETHTOOL_MSG_RSS_CREATE_ACT_REPLY,
> ETHTOOL_MSG_RSS_CREATE_NTF,
> ETHTOOL_MSG_RSS_DELETE_NTF,
> + ETHTOOL_MSG_MSE_GET_REPLY,
>
> __ETHTOOL_MSG_KERNEL_CNT,
> ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
> diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
> index 1e493553b977..629c10916670 100644
> --- a/net/ethtool/Makefile
> +++ b/net/ethtool/Makefile
> @@ -9,4 +9,4 @@ ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o rss.o \
> channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \
> tunnels.o fec.o eeprom.o stats.o phc_vclocks.o mm.o \
> module.o cmis_fw_update.o cmis_cdb.o pse-pd.o plca.o \
> - phy.o tsconfig.o
> + phy.o tsconfig.o mse.o
> diff --git a/net/ethtool/mse.c b/net/ethtool/mse.c
> new file mode 100644
> index 000000000000..dcc4c93c5d04
> --- /dev/null
> +++ b/net/ethtool/mse.c
> @@ -0,0 +1,329 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#include <linux/ethtool.h>
> +#include <linux/phy.h>
> +#include <linux/slab.h>
> +
> +#include "netlink.h"
> +#include "common.h"
> +
> +/* Channels A-D only; WORST and LINK are exclusive alternatives */
> +#define PHY_MSE_CHANNEL_COUNT 4
> +
> +struct mse_req_info {
> + struct ethnl_req_info base;
> +};
> +
> +struct mse_snapshot_entry {
> + struct phy_mse_snapshot snapshot;
> + int channel;
> +};
> +
> +struct mse_reply_data {
> + struct ethnl_reply_data base;
> + struct phy_mse_capability capability;
> + struct mse_snapshot_entry *snapshots;
> + unsigned int num_snapshots;
> +};
> +
> +static struct mse_reply_data *
> +mse_repdata(const struct ethnl_reply_data *reply_base)
> +{
> + return container_of(reply_base, struct mse_reply_data, base);
> +}
> +
> +const struct nla_policy ethnl_mse_get_policy[] = {
> + [ETHTOOL_A_MSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),
> +};
> +
> +static int get_snapshot_if_supported(struct phy_device *phydev,
> + struct mse_reply_data *data,
> + unsigned int *idx, u32 cap_bit,
> + enum phy_mse_channel channel)
> +{
> + int ret;
> +
> + if (data->capability.supported_caps & cap_bit) {
> + ret = phydev->drv->get_mse_snapshot(phydev, channel,
> + &data->snapshots[*idx].snapshot);
> + if (ret)
> + return ret;
> + data->snapshots[*idx].channel = channel;
> + (*idx)++;
> + }
> +
> + return 0;
> +}
> +
> +static int mse_get_channels(struct phy_device *phydev,
> + struct mse_reply_data *data)
> +{
> + unsigned int i = 0;
> + int ret;
> +
> + if (!data->capability.supported_caps)
> + return 0;
> +
> + data->snapshots = kcalloc(PHY_MSE_CHANNEL_COUNT,
> + sizeof(*data->snapshots), GFP_KERNEL);
> + if (!data->snapshots)
> + return -ENOMEM;
> +
> + /* Priority 1: Individual channels */
> + ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_A,
> + PHY_MSE_CHANNEL_A);
> + if (ret)
> + return ret;
> + ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_B,
> + PHY_MSE_CHANNEL_B);
> + if (ret)
> + return ret;
> + ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_C,
> + PHY_MSE_CHANNEL_C);
> + if (ret)
> + return ret;
> + ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_CHANNEL_D,
> + PHY_MSE_CHANNEL_D);
> + if (ret)
> + return ret;
> +
> + /* If any individual channels were found, we are done. */
> + if (i > 0) {
> + data->num_snapshots = i;
> + return 0;
> + }
> +
> + /* Priority 2: Worst channel, if no individual channels supported. */
> + ret = get_snapshot_if_supported(phydev, data, &i,
> + PHY_MSE_CAP_WORST_CHANNEL,
> + PHY_MSE_CHANNEL_WORST);
> + if (ret)
> + return ret;
> +
> + /* If worst channel was found, we are done. */
> + if (i > 0) {
> + data->num_snapshots = i;
> + return 0;
> + }
> +
> + /* Priority 3: Link-wide, if nothing else is supported. */
> + ret = get_snapshot_if_supported(phydev, data, &i, PHY_MSE_CAP_LINK,
> + PHY_MSE_CHANNEL_LINK);
> + if (ret)
> + return ret;
> +
> + data->num_snapshots = i;
> + return 0;
> +}
> +
> +static int mse_prepare_data(const struct ethnl_req_info *req_base,
> + struct ethnl_reply_data *reply_base,
> + const struct genl_info *info)
> +{
> + struct mse_reply_data *data = mse_repdata(reply_base);
> + struct net_device *dev = reply_base->dev;
> + struct phy_device *phydev;
> + int ret;
> +
> + phydev = ethnl_req_get_phydev(req_base, info->attrs,
> + ETHTOOL_A_MSE_HEADER, info->extack);
> + if (IS_ERR(phydev))
> + return PTR_ERR(phydev);
> + if (!phydev)
> + return -EOPNOTSUPP;
> +
> + ret = ethnl_ops_begin(dev);
> + if (ret)
> + return ret;
> +
> + mutex_lock(&phydev->lock);
> +
> + if (!phydev->drv || !phydev->drv->get_mse_capability ||
> + !phydev->drv->get_mse_snapshot) {
> + ret = -EOPNOTSUPP;
> + goto out_unlock;
> + }
> + if (!phydev->link) {
> + ret = -ENETDOWN;
> + goto out_unlock;
> + }
> +
> + ret = phydev->drv->get_mse_capability(phydev, &data->capability);
> + if (ret)
> + goto out_unlock;
> +
> + ret = mse_get_channels(phydev, data);
> +
> +out_unlock:
> + mutex_unlock(&phydev->lock);
> + ethnl_ops_complete(dev);
> + if (ret)
> + kfree(data->snapshots);
> + return ret;
> +}
> +
> +static void mse_cleanup_data(struct ethnl_reply_data *reply_base)
> +{
> + struct mse_reply_data *data = mse_repdata(reply_base);
> +
> + kfree(data->snapshots);
> +}
> +
> +static int mse_reply_size(const struct ethnl_req_info *req_base,
> + const struct ethnl_reply_data *reply_base)
> +{
> + const struct mse_reply_data *data = mse_repdata(reply_base);
> + size_t len = 0;
> + unsigned int i;
> +
> + /* ETHTOOL_A_MSE_CAPABILITIES */
> + len += nla_total_size(0);
> + if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
> + /* ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE */
> + len += nla_total_size(sizeof(u64));
> + if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
> + PHY_MSE_CAP_WORST_PEAK))
> + /* ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE */
> + len += nla_total_size(sizeof(u64));
> + /* ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS */
> + len += nla_total_size(sizeof(u64));
> + /* ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS */
> + len += nla_total_size(sizeof(u64));
> +
> + for (i = 0; i < data->num_snapshots; i++) {
> + size_t snapshot_len = 0;
> +
> + /* Per-channel nest (e.g., ETHTOOL_A_MSE_CHANNEL_A / _B / _C /
> + * _D / _WORST_CHANNEL / _LINK)
> + */
> + snapshot_len += nla_total_size(0);
> +
> + if (data->capability.supported_caps & PHY_MSE_CAP_AVG)
> + snapshot_len += nla_total_size(sizeof(u64));
> + if (data->capability.supported_caps & PHY_MSE_CAP_PEAK)
> + snapshot_len += nla_total_size(sizeof(u64));
> + if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK)
> + snapshot_len += nla_total_size(sizeof(u64));
> +
> + len += snapshot_len;
> + }
> +
> + return len;
> +}
> +
> +static int mse_channel_to_attr(int ch)
> +{
> + switch (ch) {
> + case PHY_MSE_CHANNEL_A:
> + return ETHTOOL_A_MSE_CHANNEL_A;
> + case PHY_MSE_CHANNEL_B:
> + return ETHTOOL_A_MSE_CHANNEL_B;
> + case PHY_MSE_CHANNEL_C:
> + return ETHTOOL_A_MSE_CHANNEL_C;
> + case PHY_MSE_CHANNEL_D:
> + return ETHTOOL_A_MSE_CHANNEL_D;
> + case PHY_MSE_CHANNEL_WORST:
> + return ETHTOOL_A_MSE_WORST_CHANNEL;
> + case PHY_MSE_CHANNEL_LINK:
> + return ETHTOOL_A_MSE_LINK;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int mse_fill_reply(struct sk_buff *skb,
> + const struct ethnl_req_info *req_base,
> + const struct ethnl_reply_data *reply_base)
> +{
> + const struct mse_reply_data *data = mse_repdata(reply_base);
> + struct nlattr *nest;
> + unsigned int i;
> + int ret;
> +
> + nest = nla_nest_start(skb, ETHTOOL_A_MSE_CAPABILITIES);
> + if (!nest)
> + return -EMSGSIZE;
> +
> + if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
> + ret = nla_put_uint(skb,
> + ETHTOOL_A_MSE_CAPABILITIES_MAX_AVERAGE_MSE,
> + data->capability.max_average_mse);
> + if (ret < 0)
> + goto nla_put_nest_failure;
> + }
> +
> + if (data->capability.supported_caps & (PHY_MSE_CAP_PEAK |
> + PHY_MSE_CAP_WORST_PEAK)) {
> + ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_MAX_PEAK_MSE,
> + data->capability.max_peak_mse);
> + if (ret < 0)
> + goto nla_put_nest_failure;
> + }
> +
> + ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_REFRESH_RATE_PS,
> + data->capability.refresh_rate_ps);
> + if (ret < 0)
> + goto nla_put_nest_failure;
> +
> + ret = nla_put_uint(skb, ETHTOOL_A_MSE_CAPABILITIES_NUM_SYMBOLS,
> + data->capability.num_symbols);
> + if (ret < 0)
> + goto nla_put_nest_failure;
> +
> + nla_nest_end(skb, nest);
> +
> + for (i = 0; i < data->num_snapshots; i++) {
> + const struct mse_snapshot_entry *s = &data->snapshots[i];
> + int chan_attr;
> +
> + chan_attr = mse_channel_to_attr(s->channel);
> + if (chan_attr < 0)
> + return chan_attr;
> +
> + nest = nla_nest_start(skb, chan_attr);
> + if (!nest)
> + return -EMSGSIZE;
> +
> + if (data->capability.supported_caps & PHY_MSE_CAP_AVG) {
> + ret = nla_put_uint(skb,
> + ETHTOOL_A_MSE_SNAPSHOT_AVERAGE_MSE,
> + s->snapshot.average_mse);
> + if (ret)
> + goto nla_put_nest_failure;
> + }
> + if (data->capability.supported_caps & PHY_MSE_CAP_PEAK) {
> + ret = nla_put_uint(skb, ETHTOOL_A_MSE_SNAPSHOT_PEAK_MSE,
> + s->snapshot.peak_mse);
> + if (ret)
> + goto nla_put_nest_failure;
> + }
> + if (data->capability.supported_caps & PHY_MSE_CAP_WORST_PEAK) {
> + ret = nla_put_uint(skb,
> + ETHTOOL_A_MSE_SNAPSHOT_WORST_PEAK_MSE,
> + s->snapshot.worst_peak_mse);
> + if (ret)
> + goto nla_put_nest_failure;
> + }
> +
> + nla_nest_end(skb, nest);
> + }
> +
> + return 0;
> +
> +nla_put_nest_failure:
> + nla_nest_cancel(skb, nest);
> + return ret;
> +}
> +
> +const struct ethnl_request_ops ethnl_mse_request_ops = {
> + .request_cmd = ETHTOOL_MSG_MSE_GET,
> + .reply_cmd = ETHTOOL_MSG_MSE_GET_REPLY,
> + .hdr_attr = ETHTOOL_A_MSE_HEADER,
> + .req_info_size = sizeof(struct mse_req_info),
> + .reply_data_size = sizeof(struct mse_reply_data),
> +
> + .prepare_data = mse_prepare_data,
> + .cleanup_data = mse_cleanup_data,
> + .reply_size = mse_reply_size,
> + .fill_reply = mse_fill_reply,
> +};
> diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
> index 2f813f25f07e..6e5f0f4f815a 100644
> --- a/net/ethtool/netlink.c
> +++ b/net/ethtool/netlink.c
> @@ -420,6 +420,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
> [ETHTOOL_MSG_TSCONFIG_GET] = ðnl_tsconfig_request_ops,
> [ETHTOOL_MSG_TSCONFIG_SET] = ðnl_tsconfig_request_ops,
> [ETHTOOL_MSG_PHY_GET] = ðnl_phy_request_ops,
> + [ETHTOOL_MSG_MSE_GET] = ðnl_mse_request_ops,
> };
>
> static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
> @@ -1534,6 +1535,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
> .policy = ethnl_rss_delete_policy,
> .maxattr = ARRAY_SIZE(ethnl_rss_delete_policy) - 1,
> },
> + {
> + .cmd = ETHTOOL_MSG_MSE_GET,
> + .doit = ethnl_default_doit,
> + .start = ethnl_perphy_start,
> + .dumpit = ethnl_perphy_dumpit,
> + .done = ethnl_perphy_done,
> + .policy = ethnl_mse_get_policy,
> + .maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
> + },
> };
>
> static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
> diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
> index 1d4f9ecb3d26..89010eaa67df 100644
> --- a/net/ethtool/netlink.h
> +++ b/net/ethtool/netlink.h
> @@ -442,6 +442,7 @@ extern const struct ethnl_request_ops ethnl_plca_status_request_ops;
> extern const struct ethnl_request_ops ethnl_mm_request_ops;
> extern const struct ethnl_request_ops ethnl_phy_request_ops;
> extern const struct ethnl_request_ops ethnl_tsconfig_request_ops;
> +extern const struct ethnl_request_ops ethnl_mse_request_ops;
>
> extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
> extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
> @@ -497,6 +498,7 @@ extern const struct nla_policy ethnl_module_fw_flash_act_policy[ETHTOOL_A_MODULE
> extern const struct nla_policy ethnl_phy_get_policy[ETHTOOL_A_PHY_HEADER + 1];
> extern const struct nla_policy ethnl_tsconfig_get_policy[ETHTOOL_A_TSCONFIG_HEADER + 1];
> extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX + 1];
> +extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
>
> int ethnl_set_features(struct sk_buff *skb, struct genl_info *info);
> int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info);
> --
> 2.47.3
>
^ permalink raw reply [flat|nested] 8+ messages in thread* Re: [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access
2025-10-30 11:04 ` Paolo Abeni
@ 2025-10-30 11:40 ` Oleksij Rempel
0 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-10-30 11:40 UTC (permalink / raw)
To: Paolo Abeni
Cc: Andrew Lunn, Jakub Kicinski, David S. Miller, Eric Dumazet,
Simon Horman, Donald Hunter, Jonathan Corbet, Heiner Kallweit,
Russell King, Kory Maincent, Maxime Chevallier, Nishanth Menon,
kernel, linux-kernel, netdev, UNGLinuxDriver, linux-doc,
Michal Kubecek, Roan van Dijk
On Thu, Oct 30, 2025 at 12:04:11PM +0100, Paolo Abeni wrote:
> On 10/27/25 1:27 PM, Oleksij Rempel wrote:
> > Introduce the userspace entry point for PHY MSE diagnostics via
> > ethtool netlink. This exposes the core API added previously and
> > returns both capability information and one or more snapshots.
> >
> > Userspace sends ETHTOOL_MSG_MSE_GET. The reply carries:
> > - ETHTOOL_A_MSE_CAPABILITIES: scale limits and timing information
> > - ETHTOOL_A_MSE_CHANNEL_* nests: one or more snapshots (per-channel
> > if available, otherwise WORST, otherwise LINK)
> >
> > Link down returns -ENETDOWN.
> >
> > Changes:
> > - YAML: add attribute sets (mse, mse-capabilities, mse-snapshot)
> > and the mse-get operation
> > - UAPI (generated): add ETHTOOL_A_MSE_* enums and message IDs,
> > ETHTOOL_MSG_MSE_GET/REPLY
> > - ethtool core: add net/ethtool/mse.c implementing the request,
> > register genl op, and hook into ethnl dispatch
> > - docs: document MSE_GET in ethtool-netlink.rst
> >
> > The include/uapi/linux/ethtool_netlink_generated.h is generated
> > from Documentation/netlink/specs/ethtool.yaml.
> >
> > Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
> > ---
> > changes v8:
> > - drop user-space channel selector; kernel always returns available selectors
>
> Overall LGTM, but it's unclear why you dropped the above. I understand
> one of the goal here is to achieve fast data retrival. I _guess_ most of
> the overhead is possibly due to phy regs access. Explicitly selecting a
> single/limited number of channels could/should reduce the number of
> registers access; it looks like a worthy option. What am I missing?
Yes the goal was fast data retrieval. However, I realized the initial
channel selector was just a guess at the right optimization.
My concern is that the channel selector do not fully achieve the goal.
While reading metrics from just one channel is an advantage, the
optimization could be much better if we also allowed reading only one
metric from that channel, instead of all of them.
So, rather than push another half-guess, I decided it is safer to
remove this part of the interface entirely for now. Once we have data on
where the actual performance bottlenecks are, we can design and add a
filter that solves the right problem.
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH net-next v8 3/4] net: phy: micrel: add MSE interface support for KSZ9477 family
2025-10-27 12:27 [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers Oleksij Rempel
2025-10-27 12:27 ` [PATCH net-next v8 1/4] net: phy: introduce internal API for PHY MSE diagnostics Oleksij Rempel
2025-10-27 12:27 ` [PATCH net-next v8 2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access Oleksij Rempel
@ 2025-10-27 12:28 ` Oleksij Rempel
2025-10-27 12:28 ` [PATCH net-next v8 4/4] net: phy: dp83td510: add MSE interface support for 10BASE-T1L Oleksij Rempel
2025-11-04 2:50 ` [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers patchwork-bot+netdevbpf
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-10-27 12:28 UTC (permalink / raw)
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, linux-kernel, netdev, UNGLinuxDriver,
linux-doc, Michal Kubecek, Roan van Dijk
Implement the get_mse_capability() and get_mse_snapshot() PHY driver ops
for KSZ9477-series integrated PHYs to demonstrate the new PHY MSE
UAPI.
These PHYs do not expose a documented direct MSE register, but the
Signal Quality Indicator (SQI) registers are derived from the
internal MSE computation. This hook maps SQI readings into the MSE
interface so that tooling can retrieve the raw value together with
metadata for correct interpretation in userspace.
Behaviour:
- For 1000BASE-T, report per-channel (A–D) values and support a
WORST channel selector.
- For 100BASE-TX, only LINK-wide measurements are available.
- Report average MSE only, with a max scale based on
KSZ9477_MMD_SQI_MASK and a fixed refresh rate of 2 µs.
This mapping differs from the OPEN Alliance SQI definition, which
assigns thresholds such as pre-fail indices; the MSE interface
instead provides the raw measurement, leaving interpretation to
userspace.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
changes v8:
- use enum phy_mse_channel channel instead of u32
changes v7:
- add Reviewed-by
changes v6:
- update comments
- s/get_mse_config/get_mse_capability/
---
drivers/net/phy/micrel.c | 102 +++++++++++++++++++++++++++++++++++++++
1 file changed, 102 insertions(+)
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index edca0024b7c7..06080b1c753d 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -2325,6 +2325,106 @@ static int kszphy_get_sqi_max(struct phy_device *phydev)
return KSZ9477_SQI_MAX;
}
+static int kszphy_get_mse_capability(struct phy_device *phydev,
+ struct phy_mse_capability *cap)
+{
+ /* Capabilities depend on link mode:
+ * - 1000BASE-T: per-pair SQI registers exist => expose A..D
+ * and a WORST selector.
+ * - 100BASE-TX: HW provides a single MSE/SQI reading in the "channel A"
+ * register, but with auto MDI-X there is no MDI-X resolution bit,
+ * so we cannot map that register to a specific wire pair reliably.
+ * To avoid misleading per-channel data, advertise only LINK.
+ * Other speeds: no MSE exposure via this driver.
+ *
+ * Note: WORST is *not* a hardware selector on this family.
+ * We expose it because the driver computes it in software
+ * by scanning per-channel readouts (A..D) and picking the
+ * maximum average MSE.
+ */
+ if (phydev->speed == SPEED_1000)
+ cap->supported_caps = PHY_MSE_CAP_CHANNEL_A |
+ PHY_MSE_CAP_CHANNEL_B |
+ PHY_MSE_CAP_CHANNEL_C |
+ PHY_MSE_CAP_CHANNEL_D |
+ PHY_MSE_CAP_WORST_CHANNEL;
+ else if (phydev->speed == SPEED_100)
+ cap->supported_caps = PHY_MSE_CAP_LINK;
+ else
+ return -EOPNOTSUPP;
+
+ cap->max_average_mse = FIELD_MAX(KSZ9477_MMD_SQI_MASK);
+ cap->refresh_rate_ps = 2000000; /* 2 us */
+ /* Estimated from link modulation (125 MBd per channel) and documented
+ * refresh rate of 2 us
+ */
+ cap->num_symbols = 250;
+
+ cap->supported_caps |= PHY_MSE_CAP_AVG;
+
+ return 0;
+}
+
+static int kszphy_get_mse_snapshot(struct phy_device *phydev,
+ enum phy_mse_channel channel,
+ struct phy_mse_snapshot *snapshot)
+{
+ u8 num_channels;
+ int ret;
+
+ if (phydev->speed == SPEED_1000)
+ num_channels = 4;
+ else if (phydev->speed == SPEED_100)
+ num_channels = 1;
+ else
+ return -EOPNOTSUPP;
+
+ if (channel == PHY_MSE_CHANNEL_WORST) {
+ u32 worst_val = 0;
+ int i;
+
+ /* WORST is implemented in software: select the maximum
+ * average MSE across the available per-channel registers.
+ * Only defined when multiple channels exist (1000BASE-T).
+ */
+ if (num_channels < 2)
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < num_channels; i++) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + i);
+ if (ret < 0)
+ return ret;
+
+ ret = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ if (ret > worst_val)
+ worst_val = ret;
+ }
+ snapshot->average_mse = worst_val;
+ } else if (channel == PHY_MSE_CHANNEL_LINK && num_channels == 1) {
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A);
+ if (ret < 0)
+ return ret;
+ snapshot->average_mse = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ } else if (channel >= PHY_MSE_CHANNEL_A &&
+ channel <= PHY_MSE_CHANNEL_D) {
+ /* Per-channel readouts are valid only for 1000BASE-T. */
+ if (phydev->speed != SPEED_1000)
+ return -EOPNOTSUPP;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+ KSZ9477_MMD_SIGNAL_QUALITY_CHAN_A + channel);
+ if (ret < 0)
+ return ret;
+ snapshot->average_mse = FIELD_GET(KSZ9477_MMD_SQI_MASK, ret);
+ } else {
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static void kszphy_enable_clk(struct phy_device *phydev)
{
struct kszphy_priv *priv = phydev->priv;
@@ -6497,6 +6597,8 @@ static struct phy_driver ksphy_driver[] = {
.cable_test_get_status = ksz9x31_cable_test_get_status,
.get_sqi = kszphy_get_sqi,
.get_sqi_max = kszphy_get_sqi_max,
+ .get_mse_capability = kszphy_get_mse_capability,
+ .get_mse_snapshot = kszphy_get_mse_snapshot,
} };
module_phy_driver(ksphy_driver);
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v8 4/4] net: phy: dp83td510: add MSE interface support for 10BASE-T1L
2025-10-27 12:27 [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers Oleksij Rempel
` (2 preceding siblings ...)
2025-10-27 12:28 ` [PATCH net-next v8 3/4] net: phy: micrel: add MSE interface support for KSZ9477 family Oleksij Rempel
@ 2025-10-27 12:28 ` Oleksij Rempel
2025-11-04 2:50 ` [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers patchwork-bot+netdevbpf
4 siblings, 0 replies; 8+ messages in thread
From: Oleksij Rempel @ 2025-10-27 12:28 UTC (permalink / raw)
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, linux-kernel, netdev, UNGLinuxDriver,
linux-doc, Michal Kubecek, Roan van Dijk
Implement get_mse_capability() and get_mse_snapshot() for the DP83TD510E
to expose its Mean Square Error (MSE) register via the new PHY MSE
UAPI.
The DP83TD510E does not document any peak MSE values; it only exposes
a single average MSE register used internally to derive SQI. This
implementation therefore advertises only PHY_MSE_CAP_AVG, along with
LINK and channel-A selectors. Scaling is fixed to 0xFFFF, and the
refresh interval/number of symbols are estimated from 10BASE-T1L
symbol rate (7.5 MBd) and typical diagnostic intervals (~1 ms).
For 10BASE-T1L deployments, SQI is a reliable indicator of link
modulation quality once the link is established, but it does not
indicate whether autonegotiation pulses will be correctly received
in marginal conditions. MSE provides a direct measurement of slicer
error rate that can be used to evaluate if autonegotiation is likely
to succeed under a given cable length and condition. In practice,
testing such scenarios often requires forcing a fixed-link setup to
isolate MSE behaviour from the autonegotiation process.
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
changes v8:
- use enum phy_mse_channel channel instead of u32
changes v7:
- add Reviewed-by
---
drivers/net/phy/dp83td510.c | 62 +++++++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/drivers/net/phy/dp83td510.c b/drivers/net/phy/dp83td510.c
index 23af1ac194fa..d75dae6071ad 100644
--- a/drivers/net/phy/dp83td510.c
+++ b/drivers/net/phy/dp83td510.c
@@ -61,6 +61,7 @@
#define DP83TD510E_MASTER_SLAVE_RESOL_FAIL BIT(15)
#define DP83TD510E_MSE_DETECT 0xa85
+#define DP83TD510E_MSE_MAX U16_MAX
#define DP83TD510_SQI_MAX 7
@@ -249,6 +250,64 @@ struct dp83td510_priv {
#define DP83TD510E_ALCD_COMPLETE BIT(15)
#define DP83TD510E_ALCD_CABLE_LENGTH GENMASK(10, 0)
+static int dp83td510_get_mse_capability(struct phy_device *phydev,
+ struct phy_mse_capability *cap)
+{
+ /* DP83TD510E documents only a single (average) MSE register
+ * (used to derive SQI); no peak or worst-peak counters are
+ * described. Advertise only PHY_MSE_CAP_AVG.
+ */
+ cap->supported_caps = PHY_MSE_CAP_AVG;
+ /* 10BASE-T1L is a single-pair medium, so there are no B/C/D channels.
+ * We still advertise PHY_MSE_CAP_CHANNEL_A to indicate that the PHY
+ * can attribute the measurement to a specific pair (the only one),
+ * rather than exposing it only as a link-aggregate.
+ *
+ * Rationale:
+ * - Keeps the ethtool MSE_GET selection logic consistent: per-channel
+ * (A/B/C/D) is preferred over WORST/LINK, so userspace receives a
+ * CHANNEL_A nest instead of LINK.
+ * - Signals to tools that "per-pair" data is available (even if there's
+ * just one pair), avoiding the impression that only aggregate values
+ * are supported.
+ * - Remains compatible with multi-pair PHYs and uniform UI handling.
+ *
+ * Note: WORST and other channels are not advertised on 10BASE-T1L.
+ */
+ cap->supported_caps |= PHY_MSE_CHANNEL_A | PHY_MSE_CAP_LINK;
+ cap->max_average_mse = DP83TD510E_MSE_MAX;
+
+ /* The datasheet does not specify the refresh rate or symbol count,
+ * but based on similar PHYs and standards, we can assume a common
+ * value. For 10BASE-T1L, the symbol rate is 7.5 MBd. A common
+ * diagnostic interval is around 1ms.
+ * 7.5e6 symbols/sec * 0.001 sec = 7500 symbols.
+ */
+ cap->refresh_rate_ps = 1000000000; /* 1 ms */
+ cap->num_symbols = 7500;
+
+ return 0;
+}
+
+static int dp83td510_get_mse_snapshot(struct phy_device *phydev,
+ enum phy_mse_channel channel,
+ struct phy_mse_snapshot *snapshot)
+{
+ int ret;
+
+ if (channel != PHY_MSE_CHANNEL_LINK &&
+ channel != PHY_MSE_CHANNEL_A)
+ return -EOPNOTSUPP;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
+ if (ret < 0)
+ return ret;
+
+ snapshot->average_mse = ret;
+
+ return 0;
+}
+
static int dp83td510_led_brightness_set(struct phy_device *phydev, u8 index,
enum led_brightness brightness)
{
@@ -893,6 +952,9 @@ static struct phy_driver dp83td510_driver[] = {
.get_phy_stats = dp83td510_get_phy_stats,
.update_stats = dp83td510_update_stats,
+ .get_mse_capability = dp83td510_get_mse_capability,
+ .get_mse_snapshot = dp83td510_get_mse_snapshot,
+
.led_brightness_set = dp83td510_led_brightness_set,
.led_hw_is_supported = dp83td510_led_hw_is_supported,
.led_hw_control_set = dp83td510_led_hw_control_set,
--
2.47.3
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers
2025-10-27 12:27 [PATCH net-next v8 0/4] ethtool: introduce PHY MSE diagnostics UAPI and drivers Oleksij Rempel
` (3 preceding siblings ...)
2025-10-27 12:28 ` [PATCH net-next v8 4/4] net: phy: dp83td510: add MSE interface support for 10BASE-T1L Oleksij Rempel
@ 2025-11-04 2:50 ` patchwork-bot+netdevbpf
4 siblings, 0 replies; 8+ messages in thread
From: patchwork-bot+netdevbpf @ 2025-11-04 2:50 UTC (permalink / raw)
To: Oleksij Rempel
Cc: andrew, kuba, davem, edumazet, pabeni, horms, donald.hunter,
corbet, hkallweit1, linux, kory.maincent, maxime.chevallier, nm,
kernel, linux-kernel, netdev, UNGLinuxDriver, linux-doc, mkubecek,
roan
Hello:
This series was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:
On Mon, 27 Oct 2025 13:27:57 +0100 you wrote:
> changes v8:
> - Drop capability bitfield support from UAPI
> - Drop channel selection support from user space
> - Kernel now returns all available channels automatically
> - Add OA Technical Committee numbers (TC1 / TC12)
> - Minor doc and type cleanups
> changes v7:
> - htmldoc fixes
> changes v6:
> - rework the code to use uint instead of u32/u64
> - use bitset for flags
> - use nest for each separate channel
> changes v5:
> - add struct phy_mse_snapshot and phy_mse_config to the documentation
> changes v4:
> - remove -ENETDOWN as expected error value for get_mse_config() and
> get_mse_snapshot()
> - fix htmldocs builds
> - s/__ethtool-a-mse/--ethtool-a-mse
> changes v3:
> - add missing ETHTOOL_A_LINKSTATE_MSE_* yaml changes
> changes v2:
> - rebase on latest net-next
>
> [...]
Here is the summary with links:
- [net-next,v8,1/4] net: phy: introduce internal API for PHY MSE diagnostics
https://git.kernel.org/netdev/net-next/c/abcf6eef90c6
- [net-next,v8,2/4] ethtool: netlink: add ETHTOOL_MSG_MSE_GET and wire up PHY MSE access
https://git.kernel.org/netdev/net-next/c/e6e93fb01302
- [net-next,v8,3/4] net: phy: micrel: add MSE interface support for KSZ9477 family
https://git.kernel.org/netdev/net-next/c/335a9660e141
- [net-next,v8,4/4] net: phy: dp83td510: add MSE interface support for 10BASE-T1L
https://git.kernel.org/netdev/net-next/c/fd93ed77efe4
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 8+ messages in thread