mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Merge branch 'ethtool-introduce-phy-mse-diagnostics-uapi-and-drivers'
Oleksij Rempel says:
====================
ethtool: introduce PHY MSE diagnostics UAPI and drivers
This series introduces a generic kernel-userspace API for retrieving PHY
Mean Square Error (MSE) diagnostics, together with netlink integration,
a fast-path reporting hook in LINKSTATE_GET, and initial driver
implementations for the KSZ9477 and DP83TD510E PHYs.
MSE is defined by the OPEN Alliance "Advanced diagnostic features for
100BASE-T1 automotive Ethernet PHYs" specification [1] as a measure of
slicer error rate, typically used internally to derive the Signal
Quality Indicator (SQI). While SQI is useful as a normalized quality
index, it hides raw measurement data, varies in scaling and thresholds
between vendors, and may not indicate certain failure modes - for
example, cases where autonegotiation would fail even though SQI reports
a good link. In practice, such scenarios can only be investigated in
fixed-link mode; here, MSE can provide an empirically estimated value
indicating conditions under which autonegotiation would not succeed.
Example output with current implementation:
root@DistroKit:~ ethtool lan1
Settings for lan1:
...
Speed: 1000Mb/s
Duplex: Full
...
Link detected: yes
SQI: 5/7
MSE: 3/127 (channel: worst)
root@DistroKit:~ ethtool --show-mse lan1
MSE diagnostics for lan1:
MSE Configuration:
Max Average MSE: 127
Refresh Rate: 2000000 ps
Symbols per Sample: 250
Supported capabilities: average channel-a channel-b channel-c
channel-d worst
MSE Snapshot (Channel: a):
Average MSE: 4
MSE Snapshot (Channel: b):
Average MSE: 3
MSE Snapshot (Channel: c):
Average MSE: 2
MSE Snapshot (Channel: d):
Average MSE: 3
[1] https://opensig.org/wp-content/uploads/2024/01/Advanced_PHY_features_for_automotive_Ethernet_V1.0.pdf
====================
Link: https://patch.msgid.link/20251027122801.982364-1-o.rempel@pengutronix.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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
|
||||
===================
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
329
net/ethtool/mse.c
Normal file
329
net/ethtool/mse.c
Normal file
@@ -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,
|
||||
};
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user