* [RFC net-next 0/2] ethtool: add interface caps and PHY tests
@ 2026-09-30 13:21 Shubham Das
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
This series adds two new ethtool netlink commands:
1. intf-caps-get: Discovers the functional block layout of a network
interface. Each block represents a hardware point (MAC/PHY/MODULE) at
a specific 802.3 sublayer (RS/PCS/FEC/PMA/PMD) with its supported
capabilities (loopback, test patterns, BERT, error injection).
2. Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET for per-block,
per-lane PHY testing using test patterns, BERT (Bit Error Ratio Test),
and error injection. The block_id from intf-caps-get identifies which
hardware point to test.
The block model uses:
- component: identifies the hardware entity (MAC, PHY, or module).
- sublayer: identifies the 802.3 sublayer (RS, PCS, FEC, PMA, or PMD).
- instance: identifies multiple instances of the same component, such as
internal and external PHYs.
- depth: identifies the host-side or line-side block within the same
sublayer.
Prior discussion on this proposal:
https://lore.kernel.org/netdev/SN7PR11MB810923BBDEC398E860099150FF1B2
Shubham Das (2):
ethtool: add interface capabilities query (intf-caps-get)
ethtool: add PHY test framework (phy-test-get, phy-test-set)
Documentation/netlink/specs/ethtool.yaml | 211 ++++++++++++++++++
include/linux/ethtool.h | 94 ++++++++
include/linux/phy.h | 16 ++
include/linux/phylink.h | 7 +
.../uapi/linux/ethtool_netlink_generated.h | 106 +++++++++
net/ethtool/Makefile | 2 +-
net/ethtool/intf_caps.c | 130 +++++++++++
net/ethtool/netlink.c | 24 ++
net/ethtool/netlink.h | 10 +
net/ethtool/phytest.c | 183 +++++++++++++++
10 files changed, 782 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/intf_caps.c
create mode 100644 net/ethtool/phytest.c
base-commit: 001b5d347d8ba39b2dccaefcc57967b18caec8fe
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get)
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
@ 2026-09-30 13:21 ` Shubham Das
2026-09-30 13:53 ` Andrew Lunn
2026-09-30 13:21 ` [RFC net-next 2/2] ethtool: add PHY test framework Shubham Das
` (4 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
Modern network interfaces contain multiple functional blocks along
the data path (MAC, PHY, MODULE), each at different 802.3 sublayers
(RS, PCS, FEC, PMA, PMD). Currently there is no standard way for
userspace to discover this topology or query which blocks support
loopback, pattern generation, or BERT.
Add ETHTOOL_MSG_INTF_CAPS_GET so drivers can expose the block layout
and per-block capabilities. This allows userspace to discover the
interface topology and testing capabilities dynamically, rather than
relying on device-specific knowledge.
Each block reports:
- id: unique block identifier for per-block commands
- component: MAC, PHY, or MODULE
- sublayer: RS, PCS, FEC, PMA, PMD, or NONE
- instance: distinguishes multiple instances of the same component
(e.g., internal vs external PHY, host vs port MAC)
- name: driver-chosen label (e.g., host-pma, ext-phy-pma)
- depth: ordering within same (component, sublayer) tuple
- lanes: number of SerDes lanes at this block
- loopback_supported: bitmask of supported loopback directions
- supported_tx_patterns: bitmask of patterns block can generate
- supported_rx_patterns: bitmask of patterns block can check
- error_inject_supported: block supports bit error injection
- bert_supported: block supports BERT (Bit Error Ratio Test)
Signed-off-by: Shubham Das <shubham.das@intel.com>
---
Documentation/netlink/specs/ethtool.yaml | 99 +++++++++++++
include/linux/ethtool.h | 49 +++++++
include/linux/phy.h | 8 ++
include/linux/phylink.h | 3 +
.../uapi/linux/ethtool_netlink_generated.h | 48 +++++++
net/ethtool/Makefile | 2 +-
net/ethtool/intf_caps.c | 130 ++++++++++++++++++
net/ethtool/netlink.c | 10 ++
net/ethtool/netlink.h | 3 +
9 files changed, 351 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/intf_caps.c
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 5dd4d1b5d94b..77d77a896fb0 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -210,6 +210,25 @@ definitions:
-
name: discard
value: 31
+ -
+ name: intf-component
+ type: enum
+ doc: Type of hardware component in the interface.
+ entries:
+ - mac
+ - phy
+ - module
+ -
+ name: intf-sublayer
+ type: enum
+ doc: 802.3 sublayer within a component.
+ entries:
+ - none
+ - rs
+ - pcs
+ - fec
+ - pma
+ - pmd
attribute-sets:
-
@@ -1905,6 +1924,73 @@ attribute-sets:
name: link
type: nest
nested-attributes: mse-snapshot
+ -
+ name: intf-block
+ doc: A single functional block in the interface.
+ attributes:
+ -
+ name: id
+ type: u32
+ -
+ name: component
+ type: u32
+ enum: intf-component
+ -
+ name: sublayer
+ type: u32
+ enum: intf-sublayer
+ -
+ name: instance
+ type: u32
+ doc: |
+ Identifies the hardware entity when multiple instances of
+ the same component exist in the interface. The driver
+ assigns instance numbers sequentially based on position in
+ the physical path. Defaults to 0 when only one instance of
+ a given component exists.
+ For PHY: instance 0 is the internal/host-side PHY,
+ instance 1 is an external PHY further along the path
+ toward the line side.
+ For MAC: instance 0 is the host-facing MAC, instance 1 is
+ a secondary MAC further along the data path (e.g., behind
+ an embedded switch).
+ -
+ name: name
+ type: string
+ -
+ name: depth
+ type: u8
+ -
+ name: lanes
+ type: u32
+ -
+ name: loopback-supported
+ type: u32
+ -
+ name: supported-tx-patterns
+ type: u32
+ -
+ name: supported-rx-patterns
+ type: u32
+ -
+ name: error-inject-supported
+ type: flag
+ -
+ name: bert-supported
+ type: flag
+ -
+ name: intf-caps
+ doc: Interface capabilities message.
+ attributes:
+ -
+ name: header
+ type: nest
+ nested-attributes: header
+ -
+ name: blocks
+ type: nest
+ nested-attributes: intf-block
+ multi-attr: true
operations:
enum-model: directional
@@ -2859,6 +2945,19 @@ operations:
- worst-channel
- link
dump: *mse-get-op
+ -
+ name: intf-caps-get
+ doc: Get interface capabilities (functional block layout).
+ attribute-set: intf-caps
+ do: &intf-caps-get-op
+ request:
+ attributes:
+ - header
+ reply:
+ attributes:
+ - header
+ - blocks
+ dump: *intf-caps-get-op
mcast-groups:
list:
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 12683b5d125e..61730c4500dc 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -1197,6 +1197,53 @@ struct kernel_ethtool_ts_info {
* See &struct net_device and &struct net_device_ops for documentation
* of the generic netdev features interface.
*/
+
+#define INTF_CAPS_MAX_BLOCKS 16
+#define INTF_BLOCK_NAME_LEN 32
+
+#define LOOPBACK_SUPPORT_LOCAL BIT(0)
+#define LOOPBACK_SUPPORT_REMOTE BIT(1)
+
+/**
+ * struct ethtool_intf_block - Single functional block in the interface
+ * @id: Unique block identifier
+ * @component: Hardware component (MAC, PHY, MODULE)
+ * @sublayer: 802.3 sublayer (RS, PCS, FEC, PMA, PMD, or NONE)
+ * @instance: Index when multiple instances of same component exist
+ * @name: Driver-chosen label
+ * @depth: Ordering within same (component, sublayer) tuple
+ * @lanes: Number of lanes
+ * @loopback_supported: Bitmask of supported loopback directions
+ * @supported_tx_patterns: Bitmask of patterns this block can generate
+ * @supported_rx_patterns: Bitmask of patterns this block can check
+ * @error_inject_supported: Block supports bit error injection
+ * @bert_supported: Block supports BERT counters
+ */
+struct ethtool_intf_block {
+ u32 id;
+ enum intf_component component;
+ enum intf_sublayer sublayer;
+ u32 instance;
+ char name[INTF_BLOCK_NAME_LEN];
+ u8 depth;
+ u32 lanes;
+ u32 loopback_supported;
+ u32 supported_tx_patterns;
+ u32 supported_rx_patterns;
+ u8 error_inject_supported;
+ u8 bert_supported;
+};
+
+/**
+ * struct ethtool_intf_caps - Interface capabilities (all blocks)
+ * @num_blocks: Number of valid entries in blocks[]
+ * @blocks: Array of functional blocks
+ */
+struct ethtool_intf_caps {
+ u32 num_blocks;
+ struct ethtool_intf_block blocks[INTF_CAPS_MAX_BLOCKS];
+};
+
struct ethtool_ops {
u32 supported_input_xfrm:8;
u32 cap_link_lanes_supported:1;
@@ -1354,6 +1401,8 @@ struct ethtool_ops {
int (*set_mm)(struct net_device *dev, struct ethtool_mm_cfg *cfg,
struct netlink_ext_ack *extack);
void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);
+ int (*get_intf_caps)(struct net_device *dev,
+ struct ethtool_intf_caps *caps);
};
int ethtool_check_ops(const struct ethtool_ops *ops);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5f8d65868e0f..f3c5bc976b4a 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1391,6 +1391,14 @@ struct phy_driver {
* shall be returned if speed selection is not supported.
*/
int (*set_loopback)(struct phy_device *dev, bool enable, int speed);
+
+ /**
+ * @get_intf_caps: Report functional blocks and capabilities of the PHY.
+ */
+ int (*get_intf_caps)(struct phy_device *dev,
+ struct ethtool_intf_block *blocks,
+ u32 max_blocks);
+
/** @get_sqi: Get the signal quality indication */
int (*get_sqi)(struct phy_device *dev);
/** @get_sqi_max: Get the maximum signal quality indication */
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 2bc0db3d52ac..86a91a4436fe 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -765,6 +765,9 @@ int phylink_ethtool_set_pauseparam(struct phylink *,
int phylink_get_eee_err(struct phylink *);
int phylink_ethtool_get_eee(struct phylink *link, struct ethtool_keee *eee);
int phylink_ethtool_set_eee(struct phylink *link, struct ethtool_keee *eee);
+int phylink_ethtool_get_intf_caps(struct phylink *pl,
+ struct ethtool_intf_block *blocks,
+ u32 max_blocks);
int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
int phylink_speed_down(struct phylink *pl, bool sync);
int phylink_speed_up(struct phylink *pl);
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index 8134baf7860f..bd4cca1cc4fc 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -893,6 +893,7 @@ enum {
ETHTOOL_MSG_RSS_CREATE_ACT,
ETHTOOL_MSG_RSS_DELETE_ACT,
ETHTOOL_MSG_MSE_GET,
+ ETHTOOL_MSG_INTF_CAPS_GET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -954,6 +955,7 @@ enum {
ETHTOOL_MSG_RSS_CREATE_NTF,
ETHTOOL_MSG_RSS_DELETE_NTF,
ETHTOOL_MSG_MSE_GET_REPLY,
+ ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
__ETHTOOL_MSG_KERNEL_CNT,
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
@@ -961,4 +963,50 @@ enum {
#define ETHTOOL_MCGRP_MONITOR_NAME "monitor"
+/* Interface component types */
+enum intf_component {
+ INTF_COMPONENT_MAC,
+ INTF_COMPONENT_PHY,
+ INTF_COMPONENT_MODULE,
+};
+
+/* Interface sublayer types */
+enum intf_sublayer {
+ INTF_SUBLAYER_NONE,
+ INTF_SUBLAYER_RS,
+ INTF_SUBLAYER_PCS,
+ INTF_SUBLAYER_FEC,
+ INTF_SUBLAYER_PMA,
+ INTF_SUBLAYER_PMD,
+};
+
+/* Attributes of a single interface block */
+enum {
+ ETHTOOL_A_INTF_BLOCK_ID,
+ ETHTOOL_A_INTF_BLOCK_COMPONENT,
+ ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+ ETHTOOL_A_INTF_BLOCK_INSTANCE,
+ ETHTOOL_A_INTF_BLOCK_NAME,
+ ETHTOOL_A_INTF_BLOCK_DEPTH,
+ ETHTOOL_A_INTF_BLOCK_LANES,
+ ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+ ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+ ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+ ETHTOOL_A_INTF_BLOCK_ERROR_INJECT,
+ ETHTOOL_A_INTF_BLOCK_BERT,
+
+ __ETHTOOL_A_INTF_BLOCK_CNT,
+ ETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)
+};
+
+/* Attributes of intf-caps-get message */
+enum {
+ ETHTOOL_A_INTF_CAPS_UNSPEC,
+ ETHTOOL_A_INTF_CAPS_HEADER,
+ ETHTOOL_A_INTF_CAPS_BLOCKS,
+
+ __ETHTOOL_A_INTF_CAPS_CNT,
+ ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
+};
+
#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 629c10916670..773b21c7ab7c 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 mse.o
+ phy.o tsconfig.o mse.o intf_caps.o
diff --git a/net/ethtool/intf_caps.c b/net/ethtool/intf_caps.c
new file mode 100644
index 000000000000..4f4594e8cfdc
--- /dev/null
+++ b/net/ethtool/intf_caps.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct intf_caps_req_info {
+ struct ethnl_req_info base;
+};
+
+struct intf_caps_reply_data {
+ struct ethnl_reply_data base;
+ struct ethtool_intf_caps caps;
+};
+
+#define INTF_CAPS_REPDATA(__reply_base) \
+ container_of(__reply_base, struct intf_caps_reply_data, base)
+
+const struct nla_policy
+ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1] = {
+ [ETHTOOL_A_INTF_CAPS_HEADER] =
+ NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+static int intf_caps_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+ int len = 0;
+ u32 i;
+
+ for (i = 0; i < data->caps.num_blocks; i++) {
+ len += nla_total_size(0); /* nested block */
+ len += nla_total_size(sizeof(u32)); /* id */
+ len += nla_total_size(sizeof(u32)); /* component */
+ len += nla_total_size(sizeof(u32)); /* sublayer */
+ len += nla_total_size(sizeof(u32)); /* instance */
+ len += nla_total_size(INTF_BLOCK_NAME_LEN); /* name */
+ len += nla_total_size(sizeof(u8)); /* depth */
+ len += nla_total_size(sizeof(u32)); /* lanes */
+ len += nla_total_size(sizeof(u32)); /* loopback_supported */
+ len += nla_total_size(sizeof(u32)); /* tx_patterns */
+ len += nla_total_size(sizeof(u32)); /* rx_patterns */
+ len += nla_total_size(0); /* error_inject flag */
+ len += nla_total_size(0); /* bert (flag) */
+ }
+
+ /* outer BLOCKS nest */
+ len += nla_total_size(0);
+
+ return len;
+}
+
+static int intf_caps_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ const struct genl_info *info)
+{
+ struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+ struct net_device *dev = reply_base->dev;
+
+ if (!dev->ethtool_ops->get_intf_caps)
+ return -EOPNOTSUPP;
+
+ return dev->ethtool_ops->get_intf_caps(dev, &data->caps);
+}
+
+static int intf_caps_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct intf_caps_reply_data *data = INTF_CAPS_REPDATA(reply_base);
+ struct nlattr *blocks_attr;
+ u32 i;
+
+ blocks_attr = nla_nest_start(skb, ETHTOOL_A_INTF_CAPS_BLOCKS);
+ if (!blocks_attr)
+ return -EMSGSIZE;
+
+ for (i = 0; i < data->caps.num_blocks; i++) {
+ const struct ethtool_intf_block *b = &data->caps.blocks[i];
+ struct nlattr *block_attr;
+
+ block_attr = nla_nest_start(skb, 0);
+ if (!block_attr)
+ goto nla_put_failure;
+
+ if (nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_ID, b->id) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_COMPONENT,
+ b->component) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+ b->sublayer) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_INSTANCE,
+ b->instance) ||
+ nla_put_string(skb, ETHTOOL_A_INTF_BLOCK_NAME, b->name) ||
+ nla_put_u8(skb, ETHTOOL_A_INTF_BLOCK_DEPTH, b->depth) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LANES, b->lanes) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+ b->loopback_supported) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+ b->supported_tx_patterns) ||
+ nla_put_u32(skb, ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+ b->supported_rx_patterns))
+ goto nla_put_failure;
+ if (b->error_inject_supported &&
+ nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_ERROR_INJECT))
+ goto nla_put_failure;
+ if (b->bert_supported &&
+ nla_put_flag(skb, ETHTOOL_A_INTF_BLOCK_BERT))
+ goto nla_put_failure;
+
+ nla_nest_end(skb, block_attr);
+ }
+
+ nla_nest_end(skb, blocks_attr);
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(skb, blocks_attr);
+ return -EMSGSIZE;
+}
+
+const struct ethnl_request_ops ethnl_intf_caps_request_ops = {
+ .request_cmd = ETHTOOL_MSG_INTF_CAPS_GET,
+ .reply_cmd = ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_INTF_CAPS_HEADER,
+ .req_info_size = sizeof(struct intf_caps_req_info),
+ .reply_data_size = sizeof(struct intf_caps_reply_data),
+ .prepare_data = intf_caps_prepare_data,
+ .reply_size = intf_caps_reply_size,
+ .fill_reply = intf_caps_fill_reply,
+};
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 1af395b54330..bf0040a519fa 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -431,6 +431,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_TSCONFIG_SET] = ðnl_tsconfig_request_ops,
[ETHTOOL_MSG_PHY_GET] = ðnl_phy_request_ops,
[ETHTOOL_MSG_MSE_GET] = ðnl_mse_request_ops,
+ [ETHTOOL_MSG_INTF_CAPS_GET] = ðnl_intf_caps_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1572,6 +1573,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_mse_get_policy,
.maxattr = ARRAY_SIZE(ethnl_mse_get_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_INTF_CAPS_GET,
+ .doit = ethnl_default_doit,
+ .start = ethnl_default_start,
+ .dumpit = ethnl_default_dumpit,
+ .done = ethnl_default_done,
+ .policy = ethnl_intf_caps_get_policy,
+ .maxattr = ETHTOOL_A_INTF_CAPS_HEADER,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 3e969a070f9f..73fd23dbc1e3 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -474,6 +474,7 @@ 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 ethnl_request_ops ethnl_intf_caps_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];
@@ -530,6 +531,8 @@ 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];
+extern const struct nla_policy
+ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_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.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC net-next 2/2] ethtool: add PHY test framework
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
@ 2026-09-30 13:21 ` Shubham Das
2026-09-30 13:21 ` [RFC ethtool 0/2] add interface caps and PHY tests Shubham Das
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET to
configure and monitor PHY test functions on individual interface
blocks and lanes.
The framework uses the block identifier returned by
ETHTOOL_MSG_INTF_CAPS_GET to select the hardware block under test.
This allows userspace to target test operations at specific PHY, MAC,
or module datapath components exposed by the driver.
Supported test patterns:
- PRBS: prbs7, prbs9, prbs11, prbs13, prbs15, prbs23, prbs31
- PRBS quaternary (PAM4): prbs13q, prbs31q
- SSPRQ (Short Stress Pattern Random Quaternary)
- Square wave: square-nrz, square-pam4 (JP03A)
- TX linearity (LIN, IEEE 802.3 94.2.9.4)
- Scrambled idle
- 8b/10b comma: K28.5, K28.7
Supported test actions:
- bert start / stop: bit error ratio measurement
- inject-errors N: inject bit errors into the TX stream
- active-tests: bitmask showing which tests are running
Usage flow:
1. ethtool --get-intf-caps eth1 # discover testable blocks
2. ethtool --phy-test eth1 block 3 lane 0 tx-pattern prbs31
3. ethtool --phy-test eth2 block 3 lane 0 rx-pattern prbs31
4. ethtool --phy-test eth2 block 3 lane 0 bert start
5. ethtool --show-phy-test eth2 block 3 # read BERT counters
6. ethtool --phy-test eth2 block 3 lane 0 inject-errors 5
# verify checker
7. ethtool --show-phy-test eth2 block 3 # confirm error count increased
8. ethtool --phy-test eth2 block 3 lane 0 bert stop
Signed-off-by: Shubham Das <shubham.das@intel.com>
---
Documentation/netlink/specs/ethtool.yaml | 112 +++++++++++
include/linux/ethtool.h | 45 +++++
include/linux/phy.h | 8 +
include/linux/phylink.h | 4 +
.../uapi/linux/ethtool_netlink_generated.h | 58 ++++++
net/ethtool/Makefile | 2 +-
net/ethtool/netlink.c | 14 ++
net/ethtool/netlink.h | 7 +
net/ethtool/phytest.c | 183 ++++++++++++++++++
9 files changed, 432 insertions(+), 1 deletion(-)
create mode 100644 net/ethtool/phytest.c
diff --git a/Documentation/netlink/specs/ethtool.yaml b/Documentation/netlink/specs/ethtool.yaml
index 77d77a896fb0..6b6949ca27b2 100644
--- a/Documentation/netlink/specs/ethtool.yaml
+++ b/Documentation/netlink/specs/ethtool.yaml
@@ -229,6 +229,37 @@ definitions:
- fec
- pma
- pmd
+ -
+ name: phy-test-pattern
+ type: enum
+ doc: PHY test pattern types for PRBS generation/checking.
+ entries:
+ - off
+ - prbs7
+ - prbs9
+ - prbs11
+ - prbs13
+ - prbs15
+ - prbs23
+ - prbs31
+ - ssprq
+ - prbs13q
+ - prbs31q
+ - square-nrz
+ - square-pam4
+ - tx-linearity
+ - scrambled-idle
+ - k28-5
+ - k28-7
+ -
+ name: phy-test-action
+ type: enum
+ doc: BERT control actions.
+ entries:
+ - none
+ - start
+ - stop
+ - clear
attribute-sets:
-
@@ -1991,6 +2022,51 @@ attribute-sets:
type: nest
nested-attributes: intf-block
multi-attr: true
+ -
+ name: phy-test
+ doc: PHY test configuration and status.
+ attributes:
+ -
+ name: header
+ type: nest
+ nested-attributes: header
+ -
+ name: block-id
+ type: u32
+ -
+ name: lane
+ type: u32
+ -
+ name: tx-pattern
+ type: u32
+ enum: phy-test-pattern
+ -
+ name: rx-pattern
+ type: u32
+ enum: phy-test-pattern
+ -
+ name: bert-action
+ type: u32
+ enum: phy-test-action
+ -
+ name: inject-error-count
+ type: u32
+ -
+ name: active-tests
+ type: u32
+ doc: Bitmask of currently running tests (bit 0 = BERT).
+ -
+ name: checker-lock
+ type: u8
+ -
+ name: error-count
+ type: u64
+ -
+ name: total-bits-sent
+ type: u64
+ -
+ name: supported-test-patterns
+ type: u32
operations:
enum-model: directional
@@ -2958,6 +3034,42 @@ operations:
- header
- blocks
dump: *intf-caps-get-op
+ -
+ name: phy-test-get
+ doc: Get PHY test status (patterns, BERT counters).
+ attribute-set: phy-test
+ do:
+ request:
+ attributes:
+ - header
+ - block-id
+ - lane
+ reply:
+ attributes:
+ - header
+ - block-id
+ - lane
+ - tx-pattern
+ - rx-pattern
+ - supported-test-patterns
+ - active-tests
+ - checker-lock
+ - error-count
+ - total-bits-sent
+ -
+ name: phy-test-set
+ doc: Configure PHY test (set patterns, start/stop BERT, inject errors).
+ attribute-set: phy-test
+ do:
+ request:
+ attributes:
+ - header
+ - block-id
+ - lane
+ - tx-pattern
+ - rx-pattern
+ - bert-action
+ - inject-error-count
mcast-groups:
list:
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 61730c4500dc..53eaf5526d1a 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -1244,6 +1244,47 @@ struct ethtool_intf_caps {
struct ethtool_intf_block blocks[INTF_CAPS_MAX_BLOCKS];
};
+/* Bitmask of which ethtool_phy_test fields were explicitly specified */
+#define PHY_TEST_CMD_TX_PATTERN BIT(0)
+#define PHY_TEST_CMD_RX_PATTERN BIT(1)
+#define PHY_TEST_CMD_BERT_ACTION BIT(2)
+#define PHY_TEST_CMD_INJECT_COUNT BIT(3)
+#define PHY_TEST_CMD_LANE BIT(4)
+#define PHY_TEST_CMD_BLOCK_ID BIT(5)
+
+/* Bitmask of currently active tests (read-only) */
+#define PHY_TEST_ACTIVE_BERT BIT(0)
+
+/**
+ * struct ethtool_phy_test - PHY test configuration and status
+ * @cmd: Bitmask of which fields are valid (PHY_TEST_CMD_*)
+ * @block_id: Block to operate on (from intf-caps-get)
+ * @lane: Lane number (0-based)
+ * @tx_pattern: TX pattern generator setting
+ * @rx_pattern: RX pattern checker setting
+ * @bert_action: BERT start/stop control
+ * @inject_error_count: Number of errors to inject
+ * @supported_test_patterns: Bitmask of supported patterns (GET only)
+ * @active_tests: Bitmask of running tests (PHY_TEST_ACTIVE_*)
+ * @checker_lock: RX checker lock status (read-only)
+ * @error_count: BERT error counter (read-only)
+ * @total_bits_sent: BERT total bits counter (read-only)
+ */
+struct ethtool_phy_test {
+ u32 cmd;
+ u32 block_id;
+ u32 lane;
+ enum phy_test_pattern tx_pattern;
+ enum phy_test_pattern rx_pattern;
+ enum phy_test_action bert_action;
+ u32 inject_error_count;
+ u32 supported_test_patterns;
+ u32 active_tests;
+ u8 checker_lock;
+ u64 error_count;
+ u64 total_bits_sent;
+};
+
struct ethtool_ops {
u32 supported_input_xfrm:8;
u32 cap_link_lanes_supported:1;
@@ -1403,6 +1444,10 @@ struct ethtool_ops {
void (*get_mm_stats)(struct net_device *dev, struct ethtool_mm_stats *stats);
int (*get_intf_caps)(struct net_device *dev,
struct ethtool_intf_caps *caps);
+ int (*get_phy_test)(struct net_device *dev,
+ struct ethtool_phy_test *test);
+ int (*set_phy_test)(struct net_device *dev,
+ struct ethtool_phy_test *test);
};
int ethtool_check_ops(const struct ethtool_ops *ops);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index f3c5bc976b4a..032f3a57a90b 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1399,6 +1399,14 @@ struct phy_driver {
struct ethtool_intf_block *blocks,
u32 max_blocks);
+ /** @set_phy_test: Configure PRBS/BERT on this PHY. */
+ int (*set_phy_test)(struct phy_device *dev,
+ struct ethtool_phy_test *test);
+
+ /** @get_phy_test: Get PRBS/BERT status from this PHY. */
+ int (*get_phy_test)(struct phy_device *dev,
+ struct ethtool_phy_test *test);
+
/** @get_sqi: Get the signal quality indication */
int (*get_sqi)(struct phy_device *dev);
/** @get_sqi_max: Get the maximum signal quality indication */
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 86a91a4436fe..0df8cbf09c56 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -768,6 +768,10 @@ int phylink_ethtool_set_eee(struct phylink *link, struct ethtool_keee *eee);
int phylink_ethtool_get_intf_caps(struct phylink *pl,
struct ethtool_intf_block *blocks,
u32 max_blocks);
+int phylink_ethtool_get_phy_test(struct phylink *pl,
+ struct ethtool_phy_test *test);
+int phylink_ethtool_set_phy_test(struct phylink *pl,
+ struct ethtool_phy_test *test);
int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
int phylink_speed_down(struct phylink *pl, bool sync);
int phylink_speed_up(struct phylink *pl);
diff --git a/include/uapi/linux/ethtool_netlink_generated.h b/include/uapi/linux/ethtool_netlink_generated.h
index bd4cca1cc4fc..e9dff073d577 100644
--- a/include/uapi/linux/ethtool_netlink_generated.h
+++ b/include/uapi/linux/ethtool_netlink_generated.h
@@ -894,6 +894,8 @@ enum {
ETHTOOL_MSG_RSS_DELETE_ACT,
ETHTOOL_MSG_MSE_GET,
ETHTOOL_MSG_INTF_CAPS_GET,
+ ETHTOOL_MSG_PHY_TEST_GET,
+ ETHTOOL_MSG_PHY_TEST_SET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -956,6 +958,7 @@ enum {
ETHTOOL_MSG_RSS_DELETE_NTF,
ETHTOOL_MSG_MSE_GET_REPLY,
ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+ ETHTOOL_MSG_PHY_TEST_GET_REPLY,
__ETHTOOL_MSG_KERNEL_CNT,
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
@@ -1009,4 +1012,59 @@ enum {
ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
};
+/* PHY test pattern types */
+enum phy_test_pattern {
+ PHY_TEST_PATTERN_OFF,
+ PHY_TEST_PATTERN_PRBS7,
+ PHY_TEST_PATTERN_PRBS9,
+ PHY_TEST_PATTERN_PRBS11,
+ PHY_TEST_PATTERN_PRBS13,
+ PHY_TEST_PATTERN_PRBS15,
+ PHY_TEST_PATTERN_PRBS23,
+ PHY_TEST_PATTERN_PRBS31,
+ PHY_TEST_PATTERN_SSPRQ,
+ PHY_TEST_PATTERN_PRBS13Q,
+ PHY_TEST_PATTERN_PRBS31Q,
+ PHY_TEST_PATTERN_SQUARE_NRZ,
+ PHY_TEST_PATTERN_SQUARE_PAM4,
+ PHY_TEST_PATTERN_TX_LINEARITY,
+ PHY_TEST_PATTERN_SCRAMBLED_IDLE,
+ PHY_TEST_PATTERN_K28_5,
+ PHY_TEST_PATTERN_K28_7,
+
+ __PHY_TEST_PATTERN_COUNT,
+ PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
+};
+
+/* PHY test BERT actions */
+enum phy_test_action {
+ PHY_TEST_ACTION_NONE,
+ PHY_TEST_ACTION_START,
+ PHY_TEST_ACTION_STOP,
+ PHY_TEST_ACTION_CLEAR,
+
+ __PHY_TEST_ACTION_COUNT,
+ PHY_TEST_ACTION_MAX = (__PHY_TEST_ACTION_COUNT - 1)
+};
+
+/* Attributes of phy-test-get / phy-test-set messages */
+enum {
+ ETHTOOL_A_PHY_TEST_UNSPEC,
+ ETHTOOL_A_PHY_TEST_HEADER,
+ ETHTOOL_A_PHY_TEST_BLOCK_ID,
+ ETHTOOL_A_PHY_TEST_LANE,
+ ETHTOOL_A_PHY_TEST_TX_PATTERN,
+ ETHTOOL_A_PHY_TEST_RX_PATTERN,
+ ETHTOOL_A_PHY_TEST_BERT_ACTION,
+ ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,
+ ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+ ETHTOOL_A_PHY_TEST_CHECKER_LOCK,
+ ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+ ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+ ETHTOOL_A_PHY_TEST_SUPPORTED_TEST_PATTERNS,
+
+ __ETHTOOL_A_PHY_TEST_CNT,
+ ETHTOOL_A_PHY_TEST_MAX = (__ETHTOOL_A_PHY_TEST_CNT - 1)
+};
+
#endif /* _UAPI_LINUX_ETHTOOL_NETLINK_GENERATED_H */
diff --git a/net/ethtool/Makefile b/net/ethtool/Makefile
index 773b21c7ab7c..7dd2c5624ced 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 mse.o intf_caps.o
+ phy.o tsconfig.o mse.o intf_caps.o phytest.o
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index bf0040a519fa..c15c1488e267 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -432,6 +432,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_PHY_GET] = ðnl_phy_request_ops,
[ETHTOOL_MSG_MSE_GET] = ðnl_mse_request_ops,
[ETHTOOL_MSG_INTF_CAPS_GET] = ðnl_intf_caps_request_ops,
+ [ETHTOOL_MSG_PHY_TEST_GET] = ðnl_phy_test_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1582,6 +1583,19 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_intf_caps_get_policy,
.maxattr = ETHTOOL_A_INTF_CAPS_HEADER,
},
+ {
+ .cmd = ETHTOOL_MSG_PHY_TEST_GET,
+ .doit = ethnl_default_doit,
+ .policy = ethnl_phy_test_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_phy_test_get_policy) - 1,
+ },
+ {
+ .cmd = ETHTOOL_MSG_PHY_TEST_SET,
+ .flags = GENL_UNS_ADMIN_PERM,
+ .doit = ethnl_set_phy_test,
+ .policy = ethnl_phy_test_set_policy,
+ .maxattr = ARRAY_SIZE(ethnl_phy_test_set_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h
index 73fd23dbc1e3..99055ee080a1 100644
--- a/net/ethtool/netlink.h
+++ b/net/ethtool/netlink.h
@@ -475,6 +475,7 @@ 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 ethnl_request_ops ethnl_intf_caps_request_ops;
+extern const struct ethnl_request_ops ethnl_phy_test_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];
@@ -533,6 +534,12 @@ extern const struct nla_policy ethnl_tsconfig_set_policy[ETHTOOL_A_TSCONFIG_MAX
extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
extern const struct nla_policy
ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];
+extern const struct nla_policy
+ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1];
+extern const struct nla_policy
+ethnl_phy_test_set_policy[ETHTOOL_A_PHY_TEST_MAX + 1];
+
+int ethnl_set_phy_test(struct sk_buff *skb, struct genl_info *info);
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);
diff --git a/net/ethtool/phytest.c b/net/ethtool/phytest.c
new file mode 100644
index 000000000000..9c9e83d38164
--- /dev/null
+++ b/net/ethtool/phytest.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include "netlink.h"
+#include "common.h"
+
+struct phy_test_req_info {
+ struct ethnl_req_info base;
+};
+
+struct phy_test_reply_data {
+ struct ethnl_reply_data base;
+ struct ethtool_phy_test test;
+};
+
+#define PHY_TEST_REPDATA(__reply_base) \
+ container_of(__reply_base, struct phy_test_reply_data, base)
+
+/* PHY_TEST_GET */
+
+const struct nla_policy
+ethnl_phy_test_get_policy[ETHTOOL_A_PHY_TEST_LANE + 1] = {
+ [ETHTOOL_A_PHY_TEST_HEADER] =
+ NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_PHY_TEST_BLOCK_ID] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_LANE] = { .type = NLA_U32 },
+};
+
+static int phy_test_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ return nla_total_size(sizeof(u32)) + /* block_id */
+ nla_total_size(sizeof(u32)) + /* lane */
+ nla_total_size(sizeof(u32)) + /* tx_pattern */
+ nla_total_size(sizeof(u32)) + /* rx_pattern */
+ nla_total_size(sizeof(u32)) + /* supported_test_patterns */
+ nla_total_size(sizeof(u32)) + /* active_tests */
+ nla_total_size(sizeof(u8)) + /* checker_lock */
+ nla_total_size(sizeof(u64)) + /* error_count */
+ nla_total_size(sizeof(u64)); /* total_bits_sent */
+}
+
+static int phy_test_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ const struct genl_info *info)
+{
+ struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+ struct net_device *dev = reply_base->dev;
+ struct nlattr **tb = info->attrs;
+
+ if (!dev->ethtool_ops->get_phy_test)
+ return -EOPNOTSUPP;
+
+ memset(&data->test, 0, sizeof(data->test));
+
+ if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+ struct nlattr *attr = tb[ETHTOOL_A_PHY_TEST_BLOCK_ID];
+
+ data->test.block_id = nla_get_u32(attr);
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_LANE])
+ data->test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+
+ return dev->ethtool_ops->get_phy_test(dev, &data->test);
+}
+
+static int phy_test_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct phy_test_reply_data *data = PHY_TEST_REPDATA(reply_base);
+ const struct ethtool_phy_test *t = &data->test;
+
+ if (nla_put_u32(skb, ETHTOOL_A_PHY_TEST_BLOCK_ID, t->block_id) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_TEST_LANE, t->lane) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_TEST_TX_PATTERN, t->tx_pattern) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_TEST_RX_PATTERN, t->rx_pattern) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_TEST_SUPPORTED_TEST_PATTERNS,
+ t->supported_test_patterns) ||
+ nla_put_u32(skb, ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+ t->active_tests) ||
+ nla_put_u8(skb, ETHTOOL_A_PHY_TEST_CHECKER_LOCK, t->checker_lock) ||
+ nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+ t->error_count, ETHTOOL_A_PHY_TEST_UNSPEC) ||
+ nla_put_u64_64bit(skb, ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+ t->total_bits_sent, ETHTOOL_A_PHY_TEST_UNSPEC))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+const struct ethnl_request_ops ethnl_phy_test_request_ops = {
+ .request_cmd = ETHTOOL_MSG_PHY_TEST_GET,
+ .reply_cmd = ETHTOOL_MSG_PHY_TEST_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_PHY_TEST_HEADER,
+ .req_info_size = sizeof(struct phy_test_req_info),
+ .reply_data_size = sizeof(struct phy_test_reply_data),
+ .prepare_data = phy_test_prepare_data,
+ .reply_size = phy_test_reply_size,
+ .fill_reply = phy_test_fill_reply,
+};
+
+/* PHY_TEST_SET */
+
+const struct nla_policy
+ethnl_phy_test_set_policy[ETHTOOL_A_PHY_TEST_MAX + 1] = {
+ [ETHTOOL_A_PHY_TEST_HEADER] =
+ NLA_POLICY_NESTED(ethnl_header_policy),
+ [ETHTOOL_A_PHY_TEST_BLOCK_ID] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_LANE] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_TX_PATTERN] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_RX_PATTERN] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_BERT_ACTION] = { .type = NLA_U32 },
+ [ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT] = { .type = NLA_U32 },
+};
+
+int ethnl_set_phy_test(struct sk_buff *skb, struct genl_info *info)
+{
+ struct ethnl_req_info req_info = {};
+ struct nlattr **tb = info->attrs;
+ struct ethtool_phy_test test = {};
+ struct net_device *dev;
+ int ret;
+
+ ret = ethnl_parse_header_dev_get(&req_info,
+ tb[ETHTOOL_A_PHY_TEST_HEADER],
+ genl_info_net(info), info->extack,
+ true);
+ if (ret < 0)
+ return ret;
+
+ dev = req_info.dev;
+
+ if (!dev->ethtool_ops->set_phy_test) {
+ ret = -EOPNOTSUPP;
+ goto out_dev;
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+ test.block_id = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]);
+ test.cmd |= PHY_TEST_CMD_BLOCK_ID;
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+ test.lane = nla_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]);
+ test.cmd |= PHY_TEST_CMD_LANE;
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {
+ struct nlattr *attr = tb[ETHTOOL_A_PHY_TEST_TX_PATTERN];
+
+ test.tx_pattern = nla_get_u32(attr);
+ test.cmd |= PHY_TEST_CMD_TX_PATTERN;
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {
+ struct nlattr *attr = tb[ETHTOOL_A_PHY_TEST_RX_PATTERN];
+
+ test.rx_pattern = nla_get_u32(attr);
+ test.cmd |= PHY_TEST_CMD_RX_PATTERN;
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_BERT_ACTION]) {
+ struct nlattr *attr = tb[ETHTOOL_A_PHY_TEST_BERT_ACTION];
+
+ test.bert_action = nla_get_u32(attr);
+ test.cmd |= PHY_TEST_CMD_BERT_ACTION;
+ }
+ if (tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]) {
+ test.inject_error_count =
+ nla_get_u32(tb[ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT]);
+ test.cmd |= PHY_TEST_CMD_INJECT_COUNT;
+ }
+
+ rtnl_lock();
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ goto out_rtnl;
+
+ ret = dev->ethtool_ops->set_phy_test(dev, &test);
+ ethnl_ops_complete(dev);
+
+out_rtnl:
+ rtnl_unlock();
+out_dev:
+ ethnl_parse_header_dev_put(&req_info);
+ return ret;
+}
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC ethtool 0/2] add interface caps and PHY tests
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
2026-09-30 13:21 ` [RFC net-next 2/2] ethtool: add PHY test framework Shubham Das
@ 2026-09-30 13:21 ` Shubham Das
2026-09-30 13:21 ` [RFC ethtool 1/2] ethtool: Add interface capability get support Shubham Das
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
This series adds two new ethtool netlink commands:
1. intf-caps-get: Discovers the functional block layout of a network
interface. Each block represents a hardware point (MAC/PHY/MODULE) at
a specific 802.3 sublayer (RS/PCS/FEC/PMA/PMD) with its supported
capabilities (loopback, test patterns, BERT, error injection).
2. Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET for per-block,
per-lane PHY testing using test patterns, BERT (Bit Error Ratio Test),
and error injection. The block_id from intf-caps-get identifies which
hardware point to test.
The block model uses:
- component: identifies the hardware entity (MAC, PHY, or module).
- sublayer: identifies the 802.3 sublayer (RS, PCS, FEC, PMA, or PMD).
- instance: identifies multiple instances of the same component, such as
internal and external PHYs.
- depth: identifies the host-side or line-side block within the same
sublayer.
Prior discussion on this proposal:
https://lore.kernel.org/netdev/SN7PR11MB810923BBDEC398E860099150FF1B2
Shubham Das (2):
ethtool: Add interface capability get support
ethtool: Add PHY test and BERT support
Makefile.am | 2 +
ethtool.c | 21 ++
netlink/desc-ethtool.c | 18 ++
netlink/extapi.h | 6 +
netlink/intf_caps.c | 230 ++++++++++++++++
netlink/phytest.c | 352 +++++++++++++++++++++++++
uapi/linux/ethtool_netlink_generated.h | 104 ++++++++
7 files changed, 733 insertions(+)
create mode 100644 netlink/intf_caps.c
create mode 100644 netlink/phytest.c
base-commit: ec1ef702cb5e8bda6bd5fe3b2213419c8427bb7c
--
2.25.1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [RFC ethtool 1/2] ethtool: Add interface capability get support
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
` (2 preceding siblings ...)
2026-09-30 13:21 ` [RFC ethtool 0/2] add interface caps and PHY tests Shubham Das
@ 2026-09-30 13:21 ` Shubham Das
2026-09-30 13:21 ` [RFC ethtool 2/2] ethtool: Add PHY test and BERT support Shubham Das
2026-09-30 13:58 ` [RFC net-next 0/2] ethtool: add interface caps and PHY tests Andrew Lunn
5 siblings, 0 replies; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
Add the --interface-capabilities command to display the interface
block topology and per-block support for loopback, PRBS, BERT, and
error injection.
Each block reports:
- id: unique block identifier for per-block commands
- component: MAC, PHY, or MODULE
- sublayer: RS, PCS, FEC, PMA, PMD, or NONE
- instance: distinguishes multiple instances of the same component
(for example, internal vs external PHY, host vs port MAC)
- name: driver-chosen label (for example, host-pma, ext-phy-pma)
- depth: ordering within the same (component, sublayer) tuple
- lanes: number of SerDes lanes at this block
- loopback_supported: supported loopback direction bitmask
- supported_tx_patterns: patterns the block can generate
- supported_rx_patterns: patterns the block can check
- error_inject_supported: block supports bit error injection
- bert_supported: block supports BERT (Bit Error Ratio Test)
Example:
ethtool --interface-capabilities eth1
Signed-off-by: Shubham Das <shubham.das@intel.com>
---
Makefile.am | 1 +
ethtool.c | 5 +
netlink/extapi.h | 2 +
netlink/intf_caps.c | 230 +++++++++++++++++++++++++
uapi/linux/ethtool_netlink_generated.h | 73 ++++++++
5 files changed, 311 insertions(+)
create mode 100644 netlink/intf_caps.c
diff --git a/Makefile.am b/Makefile.am
index db22be4..67f1be4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ ethtool_SOURCES += \
netlink/pse-pd.c \
netlink/phy.c \
netlink/tsconfig.c \
+ netlink/intf_caps.c \
netlink/mse.c \
uapi/linux/const.h uapi/linux/ethtool.h \
uapi/linux/ethtool_netlink.h \
diff --git a/ethtool.c b/ethtool.c
index 30a1d97..bb36754 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6514,6 +6514,11 @@ static const struct option args[] = {
.nlfunc = nl_gmse,
.help = "Show Mean Square Error (MSE) diagnostics",
},
+ {
+ .opts = "--interface-capabilities",
+ .nlfunc = nl_get_intf_caps,
+ .help = "Show interface block capabilities",
+ },
{
.opts = "-h|--help",
.no_dev = true,
diff --git a/netlink/extapi.h b/netlink/extapi.h
index e10af62..696f829 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -67,6 +67,7 @@ void nl_monitor_usage(void);
int nl_get_eeprom_page(struct cmd_context *ctx,
struct ethtool_module_eeprom *request);
+int nl_get_intf_caps(struct cmd_context *ctx);
#else /* ETHTOOL_ENABLE_NETLINK */
static inline void netlink_run_handler(struct cmd_context *ctx __maybe_unused,
@@ -142,6 +143,7 @@ nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
#define nl_flash_module_fw NULL
#define nl_get_phy NULL
#define nl_gmse NULL
+#define nl_get_intf_caps NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
diff --git a/netlink/intf_caps.c b/netlink/intf_caps.c
new file mode 100644
index 0000000..a78d605
--- /dev/null
+++ b/netlink/intf_caps.c
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * intf_caps.c - netlink implementation of interface capabilities
+ *
+ * Implementation of "ethtool --interface-capabilities <dev>"
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+
+static const char *component_to_str(uint32_t component)
+{
+ switch (component) {
+ case INTF_COMPONENT_MAC: return "MAC";
+ case INTF_COMPONENT_PHY: return "PHY";
+ case INTF_COMPONENT_MODULE: return "MODULE";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char *sublayer_to_str(uint32_t sublayer)
+{
+ switch (sublayer) {
+ case INTF_SUBLAYER_NONE: return "(none)";
+ case INTF_SUBLAYER_RS: return "RS";
+ case INTF_SUBLAYER_PCS: return "PCS";
+ case INTF_SUBLAYER_FEC: return "FEC";
+ case INTF_SUBLAYER_PMA: return "PMA";
+ case INTF_SUBLAYER_PMD: return "PMD";
+ default: return "UNKNOWN";
+ }
+}
+
+static const char * const pattern_names[] = {
+ [PHY_TEST_PATTERN_OFF] = "off",
+ [PHY_TEST_PATTERN_PRBS7] = "prbs7",
+ [PHY_TEST_PATTERN_PRBS9] = "prbs9",
+ [PHY_TEST_PATTERN_PRBS11] = "prbs11",
+ [PHY_TEST_PATTERN_PRBS13] = "prbs13",
+ [PHY_TEST_PATTERN_PRBS15] = "prbs15",
+ [PHY_TEST_PATTERN_PRBS23] = "prbs23",
+ [PHY_TEST_PATTERN_PRBS31] = "prbs31",
+ [PHY_TEST_PATTERN_SSPRQ] = "ssprq",
+ [PHY_TEST_PATTERN_PRBS13Q] = "prbs13q",
+ [PHY_TEST_PATTERN_PRBS31Q] = "prbs31q",
+ [PHY_TEST_PATTERN_SQUARE_NRZ] = "square-nrz",
+ [PHY_TEST_PATTERN_SQUARE_PAM4] = "square-pam4",
+ [PHY_TEST_PATTERN_TX_LINEARITY] = "tx-linearity",
+ [PHY_TEST_PATTERN_SCRAMBLED_IDLE] = "scrambled-idle",
+ [PHY_TEST_PATTERN_K28_5] = "k28.5",
+ [PHY_TEST_PATTERN_K28_7] = "k28.7",
+};
+
+static void print_pattern_bitmask(const char *label, u32 bitmask)
+{
+ bool first = true;
+ unsigned int i;
+
+ printf(" %s:", label);
+ if (!bitmask) {
+ printf(" (none)\n");
+ return;
+ }
+
+ for (i = 1; i < ARRAY_SIZE(pattern_names); i++) {
+ if (bitmask & (1U << i)) {
+ const char *name = pattern_names[i];
+
+ printf("%s %s", first ? "" : ",",
+ name ? name : "???");
+ first = false;
+ }
+ }
+ printf("\n");
+}
+
+static void print_loopback_supported(u32 loopback)
+{
+ printf(" Supported loopback:");
+ if (!loopback) {
+ printf(" (none)\n");
+ return;
+ }
+ if (loopback & LOOPBACK_SUPPORT_LOCAL)
+ printf(" local");
+ if (loopback & LOOPBACK_SUPPORT_REMOTE)
+ printf(" remote");
+ printf("\n");
+}
+
+static int parse_intf_block(const struct nlattr *block_attr)
+{
+ const struct nlattr *tb[ETHTOOL_A_INTF_BLOCK_MAX + 1] = {};
+ struct nlattr *attr;
+
+ mnl_attr_for_each_nested(attr, block_attr) {
+ u16 type = mnl_attr_get_type(attr);
+
+ if (type <= ETHTOOL_A_INTF_BLOCK_MAX)
+ tb[type] = attr;
+ }
+
+ /* Block ID */
+ u32 id = tb[ETHTOOL_A_INTF_BLOCK_ID]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_ID]) : 0;
+ u32 component = tb[ETHTOOL_A_INTF_BLOCK_COMPONENT]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_COMPONENT]) : 0;
+ u32 sublayer = tb[ETHTOOL_A_INTF_BLOCK_SUBLAYER]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_SUBLAYER]) : 0;
+ u32 instance = tb[ETHTOOL_A_INTF_BLOCK_INSTANCE]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_INSTANCE]) : 0;
+ const char *name = tb[ETHTOOL_A_INTF_BLOCK_NAME]
+ ? mnl_attr_get_str(tb[ETHTOOL_A_INTF_BLOCK_NAME]) : "";
+ u8 depth = tb[ETHTOOL_A_INTF_BLOCK_DEPTH]
+ ? mnl_attr_get_u8(tb[ETHTOOL_A_INTF_BLOCK_DEPTH]) : 0;
+ u32 lanes = tb[ETHTOOL_A_INTF_BLOCK_LANES]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_LANES]) : 0;
+ u32 loopback = 0;
+ u32 tx_pat = tb[ETHTOOL_A_INTF_BLOCK_TX_PATTERNS]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_TX_PATTERNS]) : 0;
+ u32 rx_pat = tb[ETHTOOL_A_INTF_BLOCK_RX_PATTERNS]
+ ? mnl_attr_get_u32(tb[ETHTOOL_A_INTF_BLOCK_RX_PATTERNS]) : 0;
+ bool bert = !!tb[ETHTOOL_A_INTF_BLOCK_BERT];
+ bool inject = !!tb[ETHTOOL_A_INTF_BLOCK_ERROR_INJECT];
+
+ if (tb[ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED]) {
+ const struct nlattr *attr =
+ tb[ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED];
+
+ loopback = mnl_attr_get_u32(attr);
+ }
+
+ printf(" Block %u: %s\n", id, name);
+ printf(" component: %s\n", component_to_str(component));
+ printf(" instance: %u\n", instance);
+ printf(" sublayer: %s\n", sublayer_to_str(sublayer));
+ printf(" depth: %u\n", depth);
+ printf(" lanes: %u\n", lanes);
+
+ print_loopback_supported(loopback);
+ print_pattern_bitmask("Supported TX patterns", tx_pat);
+ print_pattern_bitmask("Supported RX patterns", rx_pat);
+
+ printf(" Supported tests:");
+ if (!bert && !inject) {
+ printf(" (none)\n");
+ } else {
+ if (bert)
+ printf(" bert");
+ if (inject)
+ printf(" error-inject");
+ printf("\n");
+ }
+ printf("\n");
+
+ return 0;
+}
+
+static int intf_caps_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_INTF_CAPS_MAX + 1] = {};
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ struct nlattr *attr;
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_INTF_CAPS_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ printf("Interface capabilities for %s:\n\n", nlctx->devname);
+
+ if (tb[ETHTOOL_A_INTF_CAPS_BLOCKS]) {
+ mnl_attr_for_each_nested(attr, tb[ETHTOOL_A_INTF_CAPS_BLOCKS]) {
+ parse_intf_block(attr);
+ }
+ }
+
+ return MNL_CB_OK;
+}
+
+int nl_get_intf_caps(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_INTF_CAPS_GET, true))
+ return -EOPNOTSUPP;
+
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_INTF_CAPS_GET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return ret;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_INTF_CAPS_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+
+ return nlsock_process_reply(nlsk, intf_caps_reply_cb, nlctx);
+}
diff --git a/uapi/linux/ethtool_netlink_generated.h b/uapi/linux/ethtool_netlink_generated.h
index 572c5f9..7b9860a 100644
--- a/uapi/linux/ethtool_netlink_generated.h
+++ b/uapi/linux/ethtool_netlink_generated.h
@@ -840,6 +840,77 @@ enum {
ETHTOOL_A_MSE_MAX = (__ETHTOOL_A_MSE_CNT - 1)
};
+enum intf_component {
+ INTF_COMPONENT_MAC,
+ INTF_COMPONENT_PHY,
+ INTF_COMPONENT_MODULE,
+ __INTF_COMPONENT_COUNT,
+ INTF_COMPONENT_MAX = (__INTF_COMPONENT_COUNT - 1)
+};
+
+enum intf_sublayer {
+ INTF_SUBLAYER_NONE,
+ INTF_SUBLAYER_RS,
+ INTF_SUBLAYER_PCS,
+ INTF_SUBLAYER_FEC,
+ INTF_SUBLAYER_PMA,
+ INTF_SUBLAYER_PMD,
+ __INTF_SUBLAYER_COUNT,
+ INTF_SUBLAYER_MAX = (__INTF_SUBLAYER_COUNT - 1)
+};
+
+enum {
+ ETHTOOL_A_INTF_BLOCK_ID,
+ ETHTOOL_A_INTF_BLOCK_COMPONENT,
+ ETHTOOL_A_INTF_BLOCK_SUBLAYER,
+ ETHTOOL_A_INTF_BLOCK_INSTANCE,
+ ETHTOOL_A_INTF_BLOCK_NAME,
+ ETHTOOL_A_INTF_BLOCK_DEPTH,
+ ETHTOOL_A_INTF_BLOCK_LANES,
+ ETHTOOL_A_INTF_BLOCK_LOOPBACK_SUPPORTED,
+ ETHTOOL_A_INTF_BLOCK_TX_PATTERNS,
+ ETHTOOL_A_INTF_BLOCK_RX_PATTERNS,
+ ETHTOOL_A_INTF_BLOCK_ERROR_INJECT,
+ ETHTOOL_A_INTF_BLOCK_BERT,
+ __ETHTOOL_A_INTF_BLOCK_CNT,
+ ETHTOOL_A_INTF_BLOCK_MAX = (__ETHTOOL_A_INTF_BLOCK_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_INTF_CAPS_UNSPEC,
+ ETHTOOL_A_INTF_CAPS_HEADER,
+ ETHTOOL_A_INTF_CAPS_BLOCKS,
+ __ETHTOOL_A_INTF_CAPS_CNT,
+ ETHTOOL_A_INTF_CAPS_MAX = (__ETHTOOL_A_INTF_CAPS_CNT - 1)
+};
+
+#define LOOPBACK_SUPPORT_LOCAL 1U
+#define LOOPBACK_SUPPORT_REMOTE 2U
+
+enum phy_test_pattern {
+ PHY_TEST_PATTERN_OFF,
+ PHY_TEST_PATTERN_PRBS7,
+ PHY_TEST_PATTERN_PRBS9,
+ PHY_TEST_PATTERN_PRBS11,
+ PHY_TEST_PATTERN_PRBS13,
+ PHY_TEST_PATTERN_PRBS15,
+ PHY_TEST_PATTERN_PRBS23,
+ PHY_TEST_PATTERN_PRBS31,
+ PHY_TEST_PATTERN_SSPRQ,
+ PHY_TEST_PATTERN_PRBS13Q,
+ PHY_TEST_PATTERN_PRBS31Q,
+ PHY_TEST_PATTERN_SQUARE_NRZ,
+ PHY_TEST_PATTERN_SQUARE_PAM4,
+ PHY_TEST_PATTERN_TX_LINEARITY,
+ PHY_TEST_PATTERN_SCRAMBLED_IDLE,
+ PHY_TEST_PATTERN_K28_5,
+ PHY_TEST_PATTERN_K28_7,
+ __PHY_TEST_PATTERN_COUNT,
+ PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
+};
+
+#define LOOPBACK_SUPPORT_LOCAL 1U
+#define LOOPBACK_SUPPORT_REMOTE 2U
enum {
ETHTOOL_MSG_USER_NONE = 0,
ETHTOOL_MSG_STRSET_GET = 1,
@@ -893,6 +964,7 @@ enum {
ETHTOOL_MSG_RSS_CREATE_ACT,
ETHTOOL_MSG_RSS_DELETE_ACT,
ETHTOOL_MSG_MSE_GET,
+ ETHTOOL_MSG_INTF_CAPS_GET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -954,6 +1026,7 @@ enum {
ETHTOOL_MSG_RSS_CREATE_NTF,
ETHTOOL_MSG_RSS_DELETE_NTF,
ETHTOOL_MSG_MSE_GET_REPLY,
+ ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
__ETHTOOL_MSG_KERNEL_CNT,
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [RFC ethtool 2/2] ethtool: Add PHY test and BERT support
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
` (3 preceding siblings ...)
2026-09-30 13:21 ` [RFC ethtool 1/2] ethtool: Add interface capability get support Shubham Das
@ 2026-09-30 13:21 ` Shubham Das
2026-09-30 13:58 ` [RFC net-next 0/2] ethtool: add interface caps and PHY tests Andrew Lunn
5 siblings, 0 replies; 11+ messages in thread
From: Shubham Das @ 2026-09-30 13:21 UTC (permalink / raw)
To: netdev, andrew, maxime.chevallier, alexander.duyck, lee
Cc: siddaraju.dh, balaji.chintalapalle, dasshubhamk,
magnus.k.lindberg, jonas.wirandi, Shubham Das
Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET to
configure and monitor PHY test functions, including TX/RX patterns,
BERT control, error injection, and test status reporting.
Supported test patterns:
- PRBS: prbs7, prbs9, prbs11, prbs13, prbs15, prbs23, prbs31
- PRBS quaternary (PAM4): prbs13q, prbs31q
- SSPRQ (Short Stress Pattern Random Quaternary)
- Square wave: square-nrz, square-pam4 (JP03A)
- TX linearity (LIN, IEEE 802.3 94.2.9.4)
- Scrambled idle
- 8b/10b comma: K28.5, K28.7
Supported test actions:
- bert start / stop: bit error ratio measurement
- inject-errors N: inject bit errors into the TX stream
- active-tests: bitmask showing which tests are running
Usage flow:
1. ethtool --get-intf-caps eth1 # discover testable blocks
2. ethtool --phy-test eth1 block 3 lane 0 tx-pattern prbs31
3. ethtool --phy-test eth2 block 3 lane 0 rx-pattern prbs31
4. ethtool --phy-test eth2 block 3 lane 0 bert start
5. ethtool --show-phy-test eth2 block 3 # read BERT counters
6. ethtool --phy-test eth2 block 3 lane 0 inject-errors 5
# verify checker
7. ethtool --show-phy-test eth2 block 3 # confirm error count increased
8. ethtool --phy-test eth2 block 3 lane 0 bert stop
Signed-off-by: Shubham Das <shubham.das@intel.com>
---
Makefile.am | 1 +
ethtool.c | 16 ++
netlink/desc-ethtool.c | 18 ++
netlink/extapi.h | 4 +
netlink/phytest.c | 354 +++++++++++++++++++++++++
uapi/linux/ethtool_netlink_generated.h | 31 +++
6 files changed, 424 insertions(+)
create mode 100644 netlink/phytest.c
diff --git a/Makefile.am b/Makefile.am
index 67f1be4..b20f561 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -55,6 +55,7 @@ ethtool_SOURCES += \
netlink/pse-pd.c \
netlink/phy.c \
netlink/tsconfig.c \
+ netlink/phytest.c \
netlink/intf_caps.c \
netlink/mse.c \
uapi/linux/const.h uapi/linux/ethtool.h \
diff --git a/ethtool.c b/ethtool.c
index bb36754..ff6194f 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -6514,6 +6514,22 @@ static const struct option args[] = {
.nlfunc = nl_gmse,
.help = "Show Mean Square Error (MSE) diagnostics",
},
+ {
+ .opts = "--show-phy-test",
+ .nlfunc = nl_get_phy_test,
+ .help = "Show PHY test configuration and supported patterns",
+ },
+ {
+ .opts = "--phy-test",
+ .nlfunc = nl_set_phy_test,
+ .help = "Configure PHY test patterns and BERT",
+ .xhelp = " [ block N ]\n"
+ " lane N\n"
+ " [ tx-pattern off|prbs7|prbs9|prbs11|prbs13|prbs15|prbs23|prbs31|ssprq|prbs13q|prbs31q|square ]\n"
+ " [ rx-pattern off|prbs7|prbs9|prbs11|prbs13|prbs15|prbs23|prbs31|ssprq|prbs13q|prbs31q|square ]\n"
+ " [ bert start|stop ]\n"
+ " [ inject-errors N ]\n"
+ },
{
.opts = "--interface-capabilities",
.nlfunc = nl_get_intf_caps,
diff --git a/netlink/desc-ethtool.c b/netlink/desc-ethtool.c
index e8d6c59..f8320c8 100644
--- a/netlink/desc-ethtool.c
+++ b/netlink/desc-ethtool.c
@@ -666,6 +666,21 @@ static const struct pretty_nla_desc __mse_desc[] = {
NLATTR_DESC_NESTED(ETHTOOL_A_MSE_LINK, mse_snapshot),
};
+static const struct pretty_nla_desc __phy_test_desc[] = {
+ NLATTR_DESC_INVALID(ETHTOOL_A_PHY_TEST_UNSPEC),
+ NLATTR_DESC_NESTED(ETHTOOL_A_PHY_TEST_HEADER, header),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_BLOCK_ID),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_LANE),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_TX_PATTERN),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_RX_PATTERN),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_BERT_ACTION),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT),
+ NLATTR_DESC_U32(ETHTOOL_A_PHY_TEST_ACTIVE_TESTS),
+ NLATTR_DESC_U8(ETHTOOL_A_PHY_TEST_CHECKER_LOCK),
+ NLATTR_DESC_U64(ETHTOOL_A_PHY_TEST_ERROR_COUNT),
+ NLATTR_DESC_U64(ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT),
+};
+
const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
NLMSG_DESC_INVALID(ETHTOOL_MSG_USER_NONE),
NLMSG_DESC(ETHTOOL_MSG_STRSET_GET, strset),
@@ -719,6 +734,8 @@ const struct pretty_nlmsg_desc ethnl_umsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_RSS_CREATE_ACT, rss),
NLMSG_DESC(ETHTOOL_MSG_RSS_DELETE_ACT, rss),
NLMSG_DESC(ETHTOOL_MSG_MSE_GET, mse),
+ NLMSG_DESC(ETHTOOL_MSG_PHY_TEST_SET, phy_test),
+ NLMSG_DESC(ETHTOOL_MSG_PHY_TEST_GET, phy_test),
};
const unsigned int ethnl_umsg_n_desc = ARRAY_SIZE(ethnl_umsg_desc);
@@ -779,6 +796,7 @@ const struct pretty_nlmsg_desc ethnl_kmsg_desc[] = {
NLMSG_DESC(ETHTOOL_MSG_RSS_CREATE_NTF, rss),
NLMSG_DESC(ETHTOOL_MSG_RSS_DELETE_NTF, rss),
NLMSG_DESC(ETHTOOL_MSG_MSE_GET_REPLY, mse),
+ NLMSG_DESC(ETHTOOL_MSG_PHY_TEST_GET_REPLY, phy_test),
};
const unsigned int ethnl_kmsg_n_desc = ARRAY_SIZE(ethnl_kmsg_desc);
diff --git a/netlink/extapi.h b/netlink/extapi.h
index 696f829..3700f6f 100644
--- a/netlink/extapi.h
+++ b/netlink/extapi.h
@@ -67,6 +67,8 @@ void nl_monitor_usage(void);
int nl_get_eeprom_page(struct cmd_context *ctx,
struct ethtool_module_eeprom *request);
+int nl_get_phy_test(struct cmd_context *ctx);
+int nl_set_phy_test(struct cmd_context *ctx);
int nl_get_intf_caps(struct cmd_context *ctx);
#else /* ETHTOOL_ENABLE_NETLINK */
@@ -143,6 +145,8 @@ nl_get_eeprom_page(struct cmd_context *ctx __maybe_unused,
#define nl_flash_module_fw NULL
#define nl_get_phy NULL
#define nl_gmse NULL
+#define nl_get_phy_test NULL
+#define nl_set_phy_test NULL
#define nl_get_intf_caps NULL
#endif /* ETHTOOL_ENABLE_NETLINK */
diff --git a/netlink/phytest.c b/netlink/phytest.c
new file mode 100644
index 0000000..3dcd2ad
--- /dev/null
+++ b/netlink/phytest.c
@@ -0,0 +1,354 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * phytest.c - netlink implementation of PHY test
+ *
+ * Implementation of
+ * "ethtool --show-phy-test <dev> [lane N]" and
+ * "ethtool --phy-test <dev> lane N
+ * [tx-pattern <pattern>]
+ * [rx-pattern <pattern>] [bert start|stop|stats]
+ * [inject-errors N]"
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "../internal.h"
+#include "../common.h"
+#include "netlink.h"
+#include "parser.h"
+
+static const char *phy_test_pattern_to_str(uint32_t pattern)
+{
+ switch (pattern) {
+ case PHY_TEST_PATTERN_OFF: return "off";
+ case PHY_TEST_PATTERN_PRBS7: return "prbs7";
+ case PHY_TEST_PATTERN_PRBS9: return "prbs9";
+ case PHY_TEST_PATTERN_PRBS11: return "prbs11";
+ case PHY_TEST_PATTERN_PRBS13: return "prbs13";
+ case PHY_TEST_PATTERN_PRBS15: return "prbs15";
+ case PHY_TEST_PATTERN_PRBS23: return "prbs23";
+ case PHY_TEST_PATTERN_PRBS31: return "prbs31";
+ case PHY_TEST_PATTERN_SSPRQ: return "ssprq";
+ case PHY_TEST_PATTERN_PRBS13Q: return "prbs13q";
+ case PHY_TEST_PATTERN_PRBS31Q: return "prbs31q";
+ case PHY_TEST_PATTERN_SQUARE_NRZ: return "square-nrz";
+ case PHY_TEST_PATTERN_SQUARE_PAM4: return "square-pam4";
+ case PHY_TEST_PATTERN_TX_LINEARITY: return "tx-linearity";
+ case PHY_TEST_PATTERN_SCRAMBLED_IDLE: return "scrambled-idle";
+ case PHY_TEST_PATTERN_K28_5: return "k28.5";
+ case PHY_TEST_PATTERN_K28_7: return "k28.7";
+ default: return "unknown";
+ }
+}
+
+/* PHY_TEST_GET reply callback */
+static int phy_test_get_reply_cb(const struct nlmsghdr *nlhdr, void *data)
+{
+ const struct nlattr *tb[ETHTOOL_A_PHY_TEST_MAX + 1] = {};
+ struct nl_context *nlctx = data;
+ DECLARE_ATTR_TB_INFO(tb);
+ bool silent;
+ int err_ret;
+ int ret;
+
+ silent = nlctx->is_dump;
+ err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
+ ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
+ if (ret < 0)
+ return err_ret;
+
+ nlctx->devname = get_dev_name(tb[ETHTOOL_A_PHY_TEST_HEADER]);
+ if (!dev_ok(nlctx))
+ return err_ret;
+
+ if (silent)
+ print_nl();
+
+ printf("PHY test settings for %s:\n", nlctx->devname);
+
+ if (tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]) {
+ printf(" block: %u\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_PHY_TEST_BLOCK_ID]));
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_LANE]) {
+ printf(" lane: %u\n",
+ mnl_attr_get_u32(tb[ETHTOOL_A_PHY_TEST_LANE]));
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]) {
+ u32 pattern;
+
+ pattern = mnl_attr_get_u32(tb[ETHTOOL_A_PHY_TEST_TX_PATTERN]);
+
+ print_string(PRINT_ANY, "tx-pattern",
+ " tx-pattern: %s\n",
+ phy_test_pattern_to_str(pattern));
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]) {
+ u32 pattern;
+
+ pattern = mnl_attr_get_u32(tb[ETHTOOL_A_PHY_TEST_RX_PATTERN]);
+
+ print_string(PRINT_ANY, "rx-pattern",
+ " rx-pattern: %s\n",
+ phy_test_pattern_to_str(pattern));
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_ACTIVE_TESTS]) {
+ u32 active;
+
+ active = mnl_attr_get_u32(tb[ETHTOOL_A_PHY_TEST_ACTIVE_TESTS]);
+
+ printf(" active-tests:");
+ if (active & 0x1)
+ printf(" bert\n");
+ else
+ printf(" (none)\n");
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_CHECKER_LOCK]) {
+ u8 checker_lock =
+ mnl_attr_get_u8(tb[ETHTOOL_A_PHY_TEST_CHECKER_LOCK]);
+
+ print_string(PRINT_ANY, "checker-lock",
+ " checker-lock: %s\n",
+ checker_lock ?
+ "locked" : "not locked");
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_ERROR_COUNT]) {
+ u64 error_count =
+ mnl_attr_get_u64(tb[ETHTOOL_A_PHY_TEST_ERROR_COUNT]);
+
+ printf(" error-count: %llu\n",
+ (unsigned long long)error_count);
+ }
+
+ if (tb[ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT]) {
+ const struct nlattr *attr;
+ u64 total_bits_sent;
+
+ attr = tb[ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT];
+ total_bits_sent = mnl_attr_get_u64(attr);
+
+ printf(" total-bits-sent: %llu\n",
+ (unsigned long long)total_bits_sent);
+ }
+
+ return MNL_CB_OK;
+}
+
+int nl_get_phy_test(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_socket *nlsk = nlctx->ethnl_socket;
+ struct nl_msg_buff *msgbuff = &nlsk->msgbuff;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PHY_TEST_GET, true))
+ return -EOPNOTSUPP;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PHY_TEST_GET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return ret;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PHY_TEST_HEADER,
+ ctx->devname, 0))
+ return -EMSGSIZE;
+
+ /* Parse "block N" (mandatory) and "lane N" (optional) parameters */
+ if (ctx->argc < 2 || strcmp(ctx->argp[0], "block")) {
+ fprintf(stderr,
+ "ethtool (--show-phy-test): block parameter is required\n"
+ "Usage: ethtool --show-phy-test DEVNAME block N [lane N]\n");
+ return -EINVAL;
+ }
+
+ while (ctx->argc >= 2) {
+ if (!strcmp(ctx->argp[0], "block")) {
+ u32 block_id;
+
+ if (parse_u32(ctx->argp[1], &block_id)) {
+ fprintf(stderr,
+ "ethtool: invalid block value '%s'\n",
+ ctx->argp[1]);
+ return -EINVAL;
+ }
+ if (ethnla_put_u32(msgbuff, ETHTOOL_A_PHY_TEST_BLOCK_ID,
+ block_id))
+ return -EMSGSIZE;
+ ctx->argp += 2;
+ ctx->argc -= 2;
+ } else if (!strcmp(ctx->argp[0], "lane")) {
+ u32 lane;
+
+ if (parse_u32(ctx->argp[1], &lane)) {
+ fprintf(stderr,
+ "ethtool: invalid lane value '%s'\n",
+ ctx->argp[1]);
+ return -EINVAL;
+ }
+ if (ethnla_put_u32(msgbuff, ETHTOOL_A_PHY_TEST_LANE,
+ lane))
+ return -EMSGSIZE;
+ ctx->argp += 2;
+ ctx->argc -= 2;
+ } else {
+ break;
+ }
+ }
+
+ if (ctx->argc > 0) {
+ fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
+ *ctx->argp);
+ return 1;
+ }
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+
+ return nlsock_process_reply(nlsk, phy_test_get_reply_cb, nlctx);
+}
+
+/* PHY_TEST_SET */
+static const struct lookup_entry_u32 pattern_values[] = {
+ { .arg = "off", .val = PHY_TEST_PATTERN_OFF },
+ { .arg = "prbs7", .val = PHY_TEST_PATTERN_PRBS7 },
+ { .arg = "prbs9", .val = PHY_TEST_PATTERN_PRBS9 },
+ { .arg = "prbs11", .val = PHY_TEST_PATTERN_PRBS11 },
+ { .arg = "prbs13", .val = PHY_TEST_PATTERN_PRBS13 },
+ { .arg = "prbs15", .val = PHY_TEST_PATTERN_PRBS15 },
+ { .arg = "prbs23", .val = PHY_TEST_PATTERN_PRBS23 },
+ { .arg = "prbs31", .val = PHY_TEST_PATTERN_PRBS31 },
+ { .arg = "ssprq", .val = PHY_TEST_PATTERN_SSPRQ },
+ { .arg = "prbs13q", .val = PHY_TEST_PATTERN_PRBS13Q },
+ { .arg = "prbs31q", .val = PHY_TEST_PATTERN_PRBS31Q },
+ { .arg = "square-nrz", .val = PHY_TEST_PATTERN_SQUARE_NRZ },
+ { .arg = "square-pam4", .val = PHY_TEST_PATTERN_SQUARE_PAM4 },
+ { .arg = "tx-linearity", .val = PHY_TEST_PATTERN_TX_LINEARITY },
+ { .arg = "scrambled-idle",
+ .val = PHY_TEST_PATTERN_SCRAMBLED_IDLE },
+ { .arg = "k28.5", .val = PHY_TEST_PATTERN_K28_5 },
+ { .arg = "k28.7", .val = PHY_TEST_PATTERN_K28_7 },
+ {}
+};
+
+static const struct lookup_entry_u32 bert_action_values[] = {
+ { .arg = "start", .val = PHY_TEST_ACTION_START },
+ { .arg = "stop", .val = PHY_TEST_ACTION_STOP },
+ { .arg = "clear", .val = PHY_TEST_ACTION_CLEAR },
+ {}
+};
+
+static const struct param_parser phy_test_params[] = {
+ {
+ .arg = "block",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_BLOCK_ID,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "lane",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_LANE,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {
+ .arg = "tx-pattern",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_TX_PATTERN,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = pattern_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "rx-pattern",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_RX_PATTERN,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = pattern_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "bert",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_BERT_ACTION,
+ .handler = nl_parse_lookup_u32,
+ .handler_data = bert_action_values,
+ .min_argc = 1,
+ },
+ {
+ .arg = "inject-errors",
+ .group = ETHTOOL_MSG_PHY_TEST_SET,
+ .type = ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,
+ .handler = nl_parse_direct_u32,
+ .min_argc = 1,
+ },
+ {}
+};
+
+int nl_set_phy_test(struct cmd_context *ctx)
+{
+ struct nl_context *nlctx = ctx->nlctx;
+ struct nl_msg_buff *msgbuff;
+ struct nl_socket *nlsk;
+ int ret;
+
+ if (netlink_cmd_check(ctx, ETHTOOL_MSG_PHY_TEST_SET, false))
+ return -EOPNOTSUPP;
+
+ if (ctx->argc < 2) {
+ fprintf(stderr,
+ "ethtool (--phy-test): parameters missing\n"
+ "Usage: ethtool --phy-test DEVNAME block N lane N [tx-pattern ...] [rx-pattern ...] [bert ...]\n");
+ return -EINVAL;
+ }
+
+ if (strcmp(ctx->argp[0], "block")) {
+ fprintf(stderr,
+ "ethtool (--phy-test): block parameter is required as first argument\n"
+ "Usage: ethtool --phy-test DEVNAME block N lane N [tx-pattern ...] [rx-pattern ...] [bert ...]\n");
+ return -EINVAL;
+ }
+
+ nlctx->cmd = "--phy-test";
+ nlctx->argp = ctx->argp;
+ nlctx->argc = ctx->argc;
+ nlctx->devname = ctx->devname;
+ nlsk = nlctx->ethnl_socket;
+ msgbuff = &nlsk->msgbuff;
+
+ ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PHY_TEST_SET,
+ NLM_F_REQUEST | NLM_F_ACK);
+ if (ret < 0)
+ return ret;
+
+ if (ethnla_fill_header(msgbuff, ETHTOOL_A_PHY_TEST_HEADER,
+ ctx->devname, 0)) {
+ return -EMSGSIZE;
+ }
+
+ ret = nl_parser(nlctx, phy_test_params, NULL, PARSER_GROUP_NONE, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_sendmsg(nlsk, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
+ if (ret)
+ return nlctx->exit_code;
+
+ return 0;
+}
diff --git a/uapi/linux/ethtool_netlink_generated.h b/uapi/linux/ethtool_netlink_generated.h
index 7b9860a..3ec5cb5 100644
--- a/uapi/linux/ethtool_netlink_generated.h
+++ b/uapi/linux/ethtool_netlink_generated.h
@@ -909,8 +909,36 @@ enum phy_test_pattern {
PHY_TEST_PATTERN_MAX = (__PHY_TEST_PATTERN_COUNT - 1)
};
+enum phy_test_action {
+ PHY_TEST_ACTION_NONE,
+ PHY_TEST_ACTION_START,
+ PHY_TEST_ACTION_STOP,
+ PHY_TEST_ACTION_CLEAR,
+ __PHY_TEST_ACTION_COUNT,
+ PHY_TEST_ACTION_MAX = (__PHY_TEST_ACTION_COUNT - 1)
+};
+
+enum {
+ ETHTOOL_A_PHY_TEST_UNSPEC,
+ ETHTOOL_A_PHY_TEST_HEADER,
+ ETHTOOL_A_PHY_TEST_BLOCK_ID,
+ ETHTOOL_A_PHY_TEST_LANE,
+ ETHTOOL_A_PHY_TEST_TX_PATTERN,
+ ETHTOOL_A_PHY_TEST_RX_PATTERN,
+ ETHTOOL_A_PHY_TEST_BERT_ACTION,
+ ETHTOOL_A_PHY_TEST_INJECT_ERROR_COUNT,
+ ETHTOOL_A_PHY_TEST_ACTIVE_TESTS,
+ ETHTOOL_A_PHY_TEST_CHECKER_LOCK,
+ ETHTOOL_A_PHY_TEST_ERROR_COUNT,
+ ETHTOOL_A_PHY_TEST_TOTAL_BITS_SENT,
+ ETHTOOL_A_PHY_TEST_SUPPORTED_TEST_PATTERNS,
+ __ETHTOOL_A_PHY_TEST_CNT,
+ ETHTOOL_A_PHY_TEST_MAX = (__ETHTOOL_A_PHY_TEST_CNT - 1)
+};
+
#define LOOPBACK_SUPPORT_LOCAL 1U
#define LOOPBACK_SUPPORT_REMOTE 2U
+
enum {
ETHTOOL_MSG_USER_NONE = 0,
ETHTOOL_MSG_STRSET_GET = 1,
@@ -965,6 +993,8 @@ enum {
ETHTOOL_MSG_RSS_DELETE_ACT,
ETHTOOL_MSG_MSE_GET,
ETHTOOL_MSG_INTF_CAPS_GET,
+ ETHTOOL_MSG_PHY_TEST_GET,
+ ETHTOOL_MSG_PHY_TEST_SET,
__ETHTOOL_MSG_USER_CNT,
ETHTOOL_MSG_USER_MAX = (__ETHTOOL_MSG_USER_CNT - 1)
@@ -1027,6 +1057,7 @@ enum {
ETHTOOL_MSG_RSS_DELETE_NTF,
ETHTOOL_MSG_MSE_GET_REPLY,
ETHTOOL_MSG_INTF_CAPS_GET_REPLY,
+ ETHTOOL_MSG_PHY_TEST_GET_REPLY,
__ETHTOOL_MSG_KERNEL_CNT,
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
--
2.25.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get)
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
@ 2026-09-30 13:53 ` Andrew Lunn
2026-10-05 4:10 ` Das, Shubham
0 siblings, 1 reply; 11+ messages in thread
From: Andrew Lunn @ 2026-09-30 13:53 UTC (permalink / raw)
To: Shubham Das
Cc: netdev, maxime.chevallier, alexander.duyck, lee, siddaraju.dh,
balaji.chintalapalle, dasshubhamk, magnus.k.lindberg,
jonas.wirandi
> extern const struct nla_policy ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
> +extern const struct nla_policy
> +ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];
That is odd wrapping. As far as i can see, no other declaration is
wrapped like this. Is this because of the 80 character rules? It is
better to keep with the existing coding style.
Andrew
---
pw-bot: cr
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC net-next 0/2] ethtool: add interface caps and PHY tests
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
` (4 preceding siblings ...)
2026-09-30 13:21 ` [RFC ethtool 2/2] ethtool: Add PHY test and BERT support Shubham Das
@ 2026-09-30 13:58 ` Andrew Lunn
2026-10-05 4:13 ` Das, Shubham
5 siblings, 1 reply; 11+ messages in thread
From: Andrew Lunn @ 2026-09-30 13:58 UTC (permalink / raw)
To: Shubham Das
Cc: netdev, maxime.chevallier, alexander.duyck, lee, siddaraju.dh,
balaji.chintalapalle, dasshubhamk, magnus.k.lindberg,
jonas.wirandi
On Wed, Sep 30, 2026 at 08:21:46AM -0500, Shubham Das wrote:
> This series adds two new ethtool netlink commands:
>
> 1. intf-caps-get: Discovers the functional block layout of a network
> interface. Each block represents a hardware point (MAC/PHY/MODULE) at
> a specific 802.3 sublayer (RS/PCS/FEC/PMA/PMD) with its supported
> capabilities (loopback, test patterns, BERT, error injection).
>
> 2. Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET for per-block,
> per-lane PHY testing using test patterns, BERT (Bit Error Ratio Test),
> and error injection. The block_id from intf-caps-get identifies which
> hardware point to test.
>
> The block model uses:
> - component: identifies the hardware entity (MAC, PHY, or module).
> - sublayer: identifies the 802.3 sublayer (RS, PCS, FEC, PMA, or PMD).
> - instance: identifies multiple instances of the same component, such as
> internal and external PHYs.
> - depth: identifies the host-side or line-side block within the same
> sublayer.
>
> Prior discussion on this proposal:
> https://lore.kernel.org/netdev/SN7PR11MB810923BBDEC398E860099150FF1B2
When adding new APIs you also need to include a user, so we can see
the API in action.
Andrew
---
pw-bot: cr
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get)
2026-09-30 13:53 ` Andrew Lunn
@ 2026-10-05 4:10 ` Das, Shubham
0 siblings, 0 replies; 11+ messages in thread
From: Das, Shubham @ 2026-10-05 4:10 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev@vger.kernel.org, maxime.chevallier@bootlin.com,
alexander.duyck@gmail.com, lee@trager.us, D H, Siddaraju,
Chintalapalle, Balaji, dasshubhamk@gmail.com, Lindberg, Magnus,
Wirandi, Jonas
> > extern const struct nla_policy
> > ethnl_mse_get_policy[ETHTOOL_A_MSE_HEADER + 1];
> > +extern const struct nla_policy
> > +ethnl_intf_caps_get_policy[ETHTOOL_A_INTF_CAPS_HEADER + 1];
>
> That is odd wrapping. As far as i can see, no other declaration is wrapped like this.
> Is this because of the 80 character rules? It is better to keep with the existing
> coding style.
Thanks for the review. Agreed, that wrapping is unnecessary and inconsistent
with the existing declarations. I've corrected it and will include the change in v2.
> Andrew
>
> ---
> pw-bot: cr
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [RFC net-next 0/2] ethtool: add interface caps and PHY tests
2026-09-30 13:58 ` [RFC net-next 0/2] ethtool: add interface caps and PHY tests Andrew Lunn
@ 2026-10-05 4:13 ` Das, Shubham
2026-10-05 13:43 ` Andrew Lunn
0 siblings, 1 reply; 11+ messages in thread
From: Das, Shubham @ 2026-10-05 4:13 UTC (permalink / raw)
To: Andrew Lunn
Cc: netdev@vger.kernel.org, maxime.chevallier@bootlin.com,
alexander.duyck@gmail.com, lee@trager.us, D H, Siddaraju,
Chintalapalle, Balaji, dasshubhamk@gmail.com, Lindberg, Magnus,
Wirandi, Jonas
> On Wed, Sep 30, 2026 at 08:21:46AM -0500, Shubham Das wrote:
> > This series adds two new ethtool netlink commands:
> >
> > 1. intf-caps-get: Discovers the functional block layout of a network
> > interface. Each block represents a hardware point (MAC/PHY/MODULE) at
> > a specific 802.3 sublayer (RS/PCS/FEC/PMA/PMD) with its supported
> > capabilities (loopback, test patterns, BERT, error injection).
> >
> > 2. Add ETHTOOL_MSG_PHY_TEST_GET and ETHTOOL_MSG_PHY_TEST_SET for
> per-block,
> > per-lane PHY testing using test patterns, BERT (Bit Error Ratio Test),
> > and error injection. The block_id from intf-caps-get identifies which
> > hardware point to test.
> >
> > The block model uses:
> > - component: identifies the hardware entity (MAC, PHY, or module).
> > - sublayer: identifies the 802.3 sublayer (RS, PCS, FEC, PMA, or PMD).
> > - instance: identifies multiple instances of the same component, such as
> > internal and external PHYs.
> > - depth: identifies the host-side or line-side block within the same
> > sublayer.
> >
> > Prior discussion on this proposal:
> >
> https://lore.kernel.org/netdev/SN7PR11MB810923BBDEC398E860099150FF1B2
>
> When adding new APIs you also need to include a user, so we can see the API in
> action.
Thanks for the feedback.
The intended users of this API are the IXD and IDPF drivers. The initial driver support
has already been merged, and the PHY test/capability functionality in this series
has been implemented and exercised in Intel's development tree.
However, there are still a few prerequisite patches required for this functionality that
are not upstream yet. As a result, I am currently unable to post the corresponding
IXD/IDPF changes that plug into these APIs as part of this series.
I can share ethtool command output and test logs demonstrating the API in use
(PHY capability reporting, PHY test execution, etc.) if that would help validate the
interface until the remaining dependencies and driver changes are posted upstream.
> Andrew
>
> ---
> pw-bot: cr
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [RFC net-next 0/2] ethtool: add interface caps and PHY tests
2026-10-05 4:13 ` Das, Shubham
@ 2026-10-05 13:43 ` Andrew Lunn
0 siblings, 0 replies; 11+ messages in thread
From: Andrew Lunn @ 2026-10-05 13:43 UTC (permalink / raw)
To: Das, Shubham
Cc: netdev@vger.kernel.org, maxime.chevallier@bootlin.com,
alexander.duyck@gmail.com, lee@trager.us, D H, Siddaraju,
Chintalapalle, Balaji, dasshubhamk@gmail.com, Lindberg, Magnus,
Wirandi, Jonas
> I can share ethtool command output and test logs demonstrating the API in use
> (PHY capability reporting, PHY test execution, etc.) if that would help validate the
> interface until the remaining dependencies and driver changes are posted upstream.
The code is not going to be merged until the driver side is ready. So
there is no rush.
Andrew
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-10-05 13:43 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-30 13:21 [RFC net-next 0/2] ethtool: add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` [RFC net-next 1/2] ethtool: add interface capabilities query (intf-caps-get) Shubham Das
2026-09-30 13:53 ` Andrew Lunn
2026-10-05 4:10 ` Das, Shubham
2026-09-30 13:21 ` [RFC net-next 2/2] ethtool: add PHY test framework Shubham Das
2026-09-30 13:21 ` [RFC ethtool 0/2] add interface caps and PHY tests Shubham Das
2026-09-30 13:21 ` [RFC ethtool 1/2] ethtool: Add interface capability get support Shubham Das
2026-09-30 13:21 ` [RFC ethtool 2/2] ethtool: Add PHY test and BERT support Shubham Das
2026-09-30 13:58 ` [RFC net-next 0/2] ethtool: add interface caps and PHY tests Andrew Lunn
2026-10-05 4:13 ` Das, Shubham
2026-10-05 13:43 ` Andrew Lunn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox