Skip to content

Commit b7a2c1f

Browse files
kuba-mooPaolo Abeni
authored andcommitted
net: ethtool: plumb PHY stats to PHY drivers
Introduce support for standardized PHY statistics reporting in ethtool by extending the PHYLIB framework. Add the functions phy_ethtool_get_phy_stats() and phy_ethtool_get_link_ext_stats() to provide a consistent interface for retrieving PHY-level and link-specific statistics. These functions are used within the ethtool implementation to avoid direct access to the phy_device structure outside of the PHYLIB framework. A new structure, ethtool_phy_stats, is introduced to standardize PHY statistics such as packet counts, byte counts, and error counters. Drivers are updated to include callbacks for retrieving PHY and link-specific statistics, ensuring values are explicitly set only for supported fields, initialized with ETHTOOL_STAT_NOT_SET to avoid ambiguity. Signed-off-by: Jakub Kicinski <[email protected]> Signed-off-by: Oleksij Rempel <[email protected]> Signed-off-by: Paolo Abeni <[email protected]>
1 parent fe55b1d commit b7a2c1f

File tree

7 files changed

+167
-2
lines changed

7 files changed

+167
-2
lines changed

drivers/net/phy/phy.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,49 @@ int phy_ethtool_get_stats(struct phy_device *phydev,
615615
}
616616
EXPORT_SYMBOL(phy_ethtool_get_stats);
617617

618+
/**
619+
* __phy_ethtool_get_phy_stats - Retrieve standardized PHY statistics
620+
* @phydev: Pointer to the PHY device
621+
* @phy_stats: Pointer to ethtool_eth_phy_stats structure
622+
* @phydev_stats: Pointer to ethtool_phy_stats structure
623+
*
624+
* Fetches PHY statistics using a kernel-defined interface for consistent
625+
* diagnostics. Unlike phy_ethtool_get_stats(), which allows custom stats,
626+
* this function enforces a standardized format for better interoperability.
627+
*/
628+
void __phy_ethtool_get_phy_stats(struct phy_device *phydev,
629+
struct ethtool_eth_phy_stats *phy_stats,
630+
struct ethtool_phy_stats *phydev_stats)
631+
{
632+
if (!phydev->drv || !phydev->drv->get_phy_stats)
633+
return;
634+
635+
mutex_lock(&phydev->lock);
636+
phydev->drv->get_phy_stats(phydev, phy_stats, phydev_stats);
637+
mutex_unlock(&phydev->lock);
638+
}
639+
640+
/**
641+
* __phy_ethtool_get_link_ext_stats - Retrieve extended link statistics for a PHY
642+
* @phydev: Pointer to the PHY device
643+
* @link_stats: Pointer to the structure to store extended link statistics
644+
*
645+
* Populates the ethtool_link_ext_stats structure with link down event counts
646+
* and additional driver-specific link statistics, if available.
647+
*/
648+
void __phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
649+
struct ethtool_link_ext_stats *link_stats)
650+
{
651+
link_stats->link_down_events = READ_ONCE(phydev->link_down_events);
652+
653+
if (!phydev->drv || !phydev->drv->get_link_stats)
654+
return;
655+
656+
mutex_lock(&phydev->lock);
657+
phydev->drv->get_link_stats(phydev, link_stats);
658+
mutex_unlock(&phydev->lock);
659+
}
660+
618661
/**
619662
* phy_ethtool_get_plca_cfg - Get PLCA RS configuration
620663
* @phydev: the phy_device struct

drivers/net/phy/phy_device.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3800,6 +3800,8 @@ static const struct ethtool_phy_ops phy_ethtool_phy_ops = {
38003800
static const struct phylib_stubs __phylib_stubs = {
38013801
.hwtstamp_get = __phy_hwtstamp_get,
38023802
.hwtstamp_set = __phy_hwtstamp_set,
3803+
.get_phy_stats = __phy_ethtool_get_phy_stats,
3804+
.get_link_ext_stats = __phy_ethtool_get_link_ext_stats,
38033805
};
38043806

38053807
static void phylib_register_stubs(void)

include/linux/ethtool.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,29 @@ struct ethtool_eth_phy_stats {
412412
);
413413
};
414414

415+
/**
416+
* struct ethtool_phy_stats - PHY-level statistics counters
417+
* @rx_packets: Total successfully received frames
418+
* @rx_bytes: Total successfully received bytes
419+
* @rx_errors: Total received frames with errors (e.g., CRC errors)
420+
* @tx_packets: Total successfully transmitted frames
421+
* @tx_bytes: Total successfully transmitted bytes
422+
* @tx_errors: Total transmitted frames with errors
423+
*
424+
* This structure provides a standardized interface for reporting
425+
* PHY-level statistics counters. It is designed to expose statistics
426+
* commonly provided by PHYs but not explicitly defined in the IEEE
427+
* 802.3 standard.
428+
*/
429+
struct ethtool_phy_stats {
430+
u64 rx_packets;
431+
u64 rx_bytes;
432+
u64 rx_errors;
433+
u64 tx_packets;
434+
u64 tx_bytes;
435+
u64 tx_errors;
436+
};
437+
415438
/* Basic IEEE 802.3 MAC Ctrl statistics (30.3.3.*), not otherwise exposed
416439
* via a more targeted API.
417440
*/

include/linux/phy.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1144,6 +1144,35 @@ struct phy_driver {
11441144
int (*cable_test_get_status)(struct phy_device *dev, bool *finished);
11451145

11461146
/* Get statistics from the PHY using ethtool */
1147+
/**
1148+
* @get_phy_stats: Retrieve PHY statistics.
1149+
* @dev: The PHY device for which the statistics are retrieved.
1150+
* @eth_stats: structure where Ethernet PHY stats will be stored.
1151+
* @stats: structure where additional PHY-specific stats will be stored.
1152+
*
1153+
* Retrieves the supported PHY statistics and populates the provided
1154+
* structures. The input structures are pre-initialized with
1155+
* `ETHTOOL_STAT_NOT_SET`, and the driver must only modify members
1156+
* corresponding to supported statistics. Unmodified members will remain
1157+
* set to `ETHTOOL_STAT_NOT_SET` and will not be returned to userspace.
1158+
*/
1159+
void (*get_phy_stats)(struct phy_device *dev,
1160+
struct ethtool_eth_phy_stats *eth_stats,
1161+
struct ethtool_phy_stats *stats);
1162+
1163+
/**
1164+
* @get_link_stats: Retrieve link statistics.
1165+
* @dev: The PHY device for which the statistics are retrieved.
1166+
* @link_stats: structure where link-specific stats will be stored.
1167+
*
1168+
* Retrieves link-related statistics for the given PHY device. The input
1169+
* structure is pre-initialized with `ETHTOOL_STAT_NOT_SET`, and the
1170+
* driver must only modify members corresponding to supported
1171+
* statistics. Unmodified members will remain set to
1172+
* `ETHTOOL_STAT_NOT_SET` and will not be returned to userspace.
1173+
*/
1174+
void (*get_link_stats)(struct phy_device *dev,
1175+
struct ethtool_link_ext_stats *link_stats);
11471176
/** @get_sset_count: Number of statistic counters */
11481177
int (*get_sset_count)(struct phy_device *dev);
11491178
/** @get_strings: Names of the statistic counters */
@@ -2124,6 +2153,13 @@ int phy_ethtool_get_strings(struct phy_device *phydev, u8 *data);
21242153
int phy_ethtool_get_sset_count(struct phy_device *phydev);
21252154
int phy_ethtool_get_stats(struct phy_device *phydev,
21262155
struct ethtool_stats *stats, u64 *data);
2156+
2157+
void __phy_ethtool_get_phy_stats(struct phy_device *phydev,
2158+
struct ethtool_eth_phy_stats *phy_stats,
2159+
struct ethtool_phy_stats *phydev_stats);
2160+
void __phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
2161+
struct ethtool_link_ext_stats *link_stats);
2162+
21272163
int phy_ethtool_get_plca_cfg(struct phy_device *phydev,
21282164
struct phy_plca_cfg *plca_cfg);
21292165
int phy_ethtool_set_plca_cfg(struct phy_device *phydev,

include/linux/phylib_stubs.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
#include <linux/rtnetlink.h>
77

8+
struct ethtool_eth_phy_stats;
9+
struct ethtool_link_ext_stats;
10+
struct ethtool_phy_stats;
811
struct kernel_hwtstamp_config;
912
struct netlink_ext_ack;
1013
struct phy_device;
@@ -19,6 +22,11 @@ struct phylib_stubs {
1922
int (*hwtstamp_set)(struct phy_device *phydev,
2023
struct kernel_hwtstamp_config *config,
2124
struct netlink_ext_ack *extack);
25+
void (*get_phy_stats)(struct phy_device *phydev,
26+
struct ethtool_eth_phy_stats *phy_stats,
27+
struct ethtool_phy_stats *phydev_stats);
28+
void (*get_link_ext_stats)(struct phy_device *phydev,
29+
struct ethtool_link_ext_stats *link_stats);
2230
};
2331

2432
static inline int phy_hwtstamp_get(struct phy_device *phydev,
@@ -50,6 +58,29 @@ static inline int phy_hwtstamp_set(struct phy_device *phydev,
5058
return phylib_stubs->hwtstamp_set(phydev, config, extack);
5159
}
5260

61+
static inline void phy_ethtool_get_phy_stats(struct phy_device *phydev,
62+
struct ethtool_eth_phy_stats *phy_stats,
63+
struct ethtool_phy_stats *phydev_stats)
64+
{
65+
ASSERT_RTNL();
66+
67+
if (!phylib_stubs)
68+
return;
69+
70+
phylib_stubs->get_phy_stats(phydev, phy_stats, phydev_stats);
71+
}
72+
73+
static inline void phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
74+
struct ethtool_link_ext_stats *link_stats)
75+
{
76+
ASSERT_RTNL();
77+
78+
if (!phylib_stubs)
79+
return;
80+
81+
phylib_stubs->get_link_ext_stats(phydev, link_stats);
82+
}
83+
5384
#else
5485

5586
static inline int phy_hwtstamp_get(struct phy_device *phydev,
@@ -65,4 +96,15 @@ static inline int phy_hwtstamp_set(struct phy_device *phydev,
6596
return -EOPNOTSUPP;
6697
}
6798

99+
static inline void phy_ethtool_get_phy_stats(struct phy_device *phydev,
100+
struct ethtool_eth_phy_stats *phy_stats,
101+
struct ethtool_phy_stats *phydev_stats)
102+
{
103+
}
104+
105+
static inline void phy_ethtool_get_link_ext_stats(struct phy_device *phydev,
106+
struct ethtool_link_ext_stats *link_stats)
107+
{
108+
}
109+
68110
#endif

net/ethtool/linkstate.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "netlink.h"
44
#include "common.h"
55
#include <linux/phy.h>
6+
#include <linux/phylib_stubs.h>
67

78
struct linkstate_req_info {
89
struct ethnl_req_info base;
@@ -135,8 +136,8 @@ static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
135136

136137
if (req_base->flags & ETHTOOL_FLAG_STATS) {
137138
if (phydev)
138-
data->link_stats.link_down_events =
139-
READ_ONCE(phydev->link_down_events);
139+
phy_ethtool_get_link_ext_stats(phydev,
140+
&data->link_stats);
140141

141142
if (dev->ethtool_ops->get_link_ext_stats)
142143
dev->ethtool_ops->get_link_ext_stats(dev,

net/ethtool/stats.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
// SPDX-License-Identifier: GPL-2.0-only
22

3+
#include <linux/phy.h>
4+
#include <linux/phylib_stubs.h>
5+
36
#include "netlink.h"
47
#include "common.h"
58
#include "bitset.h"
@@ -20,6 +23,7 @@ struct stats_reply_data {
2023
struct ethtool_eth_mac_stats mac_stats;
2124
struct ethtool_eth_ctrl_stats ctrl_stats;
2225
struct ethtool_rmon_stats rmon_stats;
26+
struct ethtool_phy_stats phydev_stats;
2327
);
2428
const struct ethtool_rmon_hist_range *rmon_ranges;
2529
};
@@ -120,8 +124,15 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
120124
struct stats_reply_data *data = STATS_REPDATA(reply_base);
121125
enum ethtool_mac_stats_src src = req_info->src;
122126
struct net_device *dev = reply_base->dev;
127+
struct nlattr **tb = info->attrs;
128+
struct phy_device *phydev;
123129
int ret;
124130

131+
phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_STATS_HEADER],
132+
info->extack);
133+
if (IS_ERR(phydev))
134+
return PTR_ERR(phydev);
135+
125136
ret = ethnl_ops_begin(dev);
126137
if (ret < 0)
127138
return ret;
@@ -145,6 +156,13 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
145156
data->ctrl_stats.src = src;
146157
data->rmon_stats.src = src;
147158

159+
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
160+
src == ETHTOOL_MAC_STATS_SRC_AGGREGATE) {
161+
if (phydev)
162+
phy_ethtool_get_phy_stats(phydev, &data->phy_stats,
163+
&data->phydev_stats);
164+
}
165+
148166
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
149167
dev->ethtool_ops->get_eth_phy_stats)
150168
dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);

0 commit comments

Comments
 (0)