Skip to content

Commit a8b06e9

Browse files
kuba-moodavem330
authored andcommitted
ethtool: add interface to read RMON stats
Most devices maintain RMON (RFC 2819) stats - particularly the "histogram" of packets received by size. Unlike other RFCs which duplicate IEEE stats, the short/oversized frame counters in RMON don't seem to match IEEE stats 1-to-1 either, so expose those, too. Do not expose basic packet, CRC errors etc - those are already otherwise covered. Because standard defines packet ranges only up to 1518, and everything above that should theoretically be "oversized" - devices often create their own ranges. Going beyond what the RFC defines - expose the "histogram" in the Tx direction (assume for now that the ranges will be the same). Signed-off-by: Jakub Kicinski <kuba@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent bfad2b9 commit a8b06e9

6 files changed

Lines changed: 161 additions & 0 deletions

File tree

include/linux/ethtool.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,44 @@ struct ethtool_fec_stats {
346346
} corrected_blocks, uncorrectable_blocks, corrected_bits;
347347
};
348348

349+
/**
350+
* struct ethtool_rmon_hist_range - byte range for histogram statistics
351+
* @low: low bound of the bucket (inclusive)
352+
* @high: high bound of the bucket (inclusive)
353+
*/
354+
struct ethtool_rmon_hist_range {
355+
u16 low;
356+
u16 high;
357+
};
358+
359+
#define ETHTOOL_RMON_HIST_MAX 10
360+
361+
/**
362+
* struct ethtool_rmon_stats - selected RMON (RFC 2819) statistics
363+
* @undersize_pkts: Equivalent to `etherStatsUndersizePkts` from the RFC.
364+
* @oversize_pkts: Equivalent to `etherStatsOversizePkts` from the RFC.
365+
* @fragments: Equivalent to `etherStatsFragments` from the RFC.
366+
* @jabbers: Equivalent to `etherStatsJabbers` from the RFC.
367+
* @hist: Packet counter for packet length buckets (e.g.
368+
* `etherStatsPkts128to255Octets` from the RFC).
369+
* @hist_tx: Tx counters in similar form to @hist, not defined in the RFC.
370+
*
371+
* Selection of RMON (RFC 2819) statistics which are not exposed via different
372+
* APIs, primarily the packet-length-based counters.
373+
* Unfortunately different designs choose different buckets beyond
374+
* the 1024B mark (jumbo frame teritory), so the definition of the bucket
375+
* ranges is left to the driver.
376+
*/
377+
struct ethtool_rmon_stats {
378+
u64 undersize_pkts;
379+
u64 oversize_pkts;
380+
u64 fragments;
381+
u64 jabbers;
382+
383+
u64 hist[ETHTOOL_RMON_HIST_MAX];
384+
u64 hist_tx[ETHTOOL_RMON_HIST_MAX];
385+
};
386+
349387
#define ETH_MODULE_EEPROM_PAGE_LEN 128
350388
#define ETH_MODULE_MAX_I2C_ADDRESS 0x7f
351389

@@ -534,6 +572,8 @@ struct ethtool_module_eeprom {
534572
* @get_eth_phy_stats: Query some of the IEEE 802.3 PHY statistics.
535573
* @get_eth_mac_stats: Query some of the IEEE 802.3 MAC statistics.
536574
* @get_eth_ctrl_stats: Query some of the IEEE 802.3 MAC Ctrl statistics.
575+
* @get_rmon_stats: Query some of the RMON (RFC 2819) statistics.
576+
* Set %ranges to a pointer to zero-terminated array of byte ranges.
537577
*
538578
* All operations are optional (i.e. the function pointer may be set
539579
* to %NULL) and callers must take this into account. Callers must
@@ -650,6 +690,9 @@ struct ethtool_ops {
650690
struct ethtool_eth_mac_stats *mac_stats);
651691
void (*get_eth_ctrl_stats)(struct net_device *dev,
652692
struct ethtool_eth_ctrl_stats *ctrl_stats);
693+
void (*get_rmon_stats)(struct net_device *dev,
694+
struct ethtool_rmon_stats *rmon_stats,
695+
const struct ethtool_rmon_hist_range **ranges);
653696
};
654697

655698
int ethtool_check_ops(const struct ethtool_ops *ops);

include/uapi/linux/ethtool.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ enum ethtool_link_ext_substate_cable_issue {
673673
* @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics
674674
* @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics
675675
* @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics
676+
* @ETH_SS_STATS_RMON: names of RMON statistics
676677
*
677678
* @ETH_SS_COUNT: number of defined string sets
678679
*/
@@ -697,6 +698,7 @@ enum ethtool_stringset {
697698
ETH_SS_STATS_ETH_PHY,
698699
ETH_SS_STATS_ETH_MAC,
699700
ETH_SS_STATS_ETH_CTRL,
701+
ETH_SS_STATS_RMON,
700702

701703
/* add new constants above here */
702704
ETH_SS_COUNT

include/uapi/linux/ethtool_netlink.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,7 @@ enum {
700700
ETHTOOL_STATS_ETH_PHY,
701701
ETHTOOL_STATS_ETH_MAC,
702702
ETHTOOL_STATS_ETH_CTRL,
703+
ETHTOOL_STATS_RMON,
703704

704705
/* add new constants above here */
705706
__ETHTOOL_STATS_CNT
@@ -714,6 +715,13 @@ enum {
714715

715716
ETHTOOL_A_STATS_GRP_STAT, /* nest */
716717

718+
ETHTOOL_A_STATS_GRP_HIST_RX, /* nest */
719+
ETHTOOL_A_STATS_GRP_HIST_TX, /* nest */
720+
721+
ETHTOOL_A_STATS_GRP_HIST_BKT_LOW, /* u32 */
722+
ETHTOOL_A_STATS_GRP_HIST_BKT_HI, /* u32 */
723+
ETHTOOL_A_STATS_GRP_HIST_VAL, /* u64 */
724+
717725
/* add new constants above here */
718726
__ETHTOOL_A_STATS_GRP_CNT,
719727
ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_CNT - 1)
@@ -793,6 +801,21 @@ enum {
793801
ETHTOOL_A_STATS_ETH_CTRL_MAX = (__ETHTOOL_A_STATS_ETH_CTRL_CNT - 1)
794802
};
795803

804+
enum {
805+
/* etherStatsUndersizePkts */
806+
ETHTOOL_A_STATS_RMON_UNDERSIZE,
807+
/* etherStatsOversizePkts */
808+
ETHTOOL_A_STATS_RMON_OVERSIZE,
809+
/* etherStatsFragments */
810+
ETHTOOL_A_STATS_RMON_FRAG,
811+
/* etherStatsJabbers */
812+
ETHTOOL_A_STATS_RMON_JABBER,
813+
814+
/* add new constants above here */
815+
__ETHTOOL_A_STATS_RMON_CNT,
816+
ETHTOOL_A_STATS_RMON_MAX = (__ETHTOOL_A_STATS_RMON_CNT - 1)
817+
};
818+
796819
/* generic netlink info */
797820
#define ETHTOOL_GENL_NAME "ethtool"
798821
#define ETHTOOL_GENL_VERSION 1

net/ethtool/netlink.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,5 +405,6 @@ extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
405405
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
406406
extern const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN];
407407
extern const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN];
408+
extern const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN];
408409

409410
#endif /* _NET_ETHTOOL_NETLINK_H */

net/ethtool/stats.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ struct stats_reply_data {
1717
struct ethtool_eth_phy_stats phy_stats;
1818
struct ethtool_eth_mac_stats mac_stats;
1919
struct ethtool_eth_ctrl_stats ctrl_stats;
20+
struct ethtool_rmon_stats rmon_stats;
21+
const struct ethtool_rmon_hist_range *rmon_ranges;
2022
};
2123

2224
#define STATS_REPDATA(__reply_base) \
@@ -26,6 +28,7 @@ const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = {
2628
[ETHTOOL_STATS_ETH_PHY] = "eth-phy",
2729
[ETHTOOL_STATS_ETH_MAC] = "eth-mac",
2830
[ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl",
31+
[ETHTOOL_STATS_RMON] = "rmon",
2932
};
3033

3134
const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = {
@@ -63,6 +66,13 @@ const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN]
6366
[ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived",
6467
};
6568

69+
const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = {
70+
[ETHTOOL_A_STATS_RMON_UNDERSIZE] = "etherStatsUndersizePkts",
71+
[ETHTOOL_A_STATS_RMON_OVERSIZE] = "etherStatsOversizePkts",
72+
[ETHTOOL_A_STATS_RMON_FRAG] = "etherStatsFragments",
73+
[ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers",
74+
};
75+
6676
const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
6777
[ETHTOOL_A_STATS_HEADER] =
6878
NLA_POLICY_NESTED(ethnl_header_policy),
@@ -107,6 +117,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
107117
memset(&data->phy_stats, 0xff, sizeof(data->phy_stats));
108118
memset(&data->mac_stats, 0xff, sizeof(data->mac_stats));
109119
memset(&data->ctrl_stats, 0xff, sizeof(data->mac_stats));
120+
memset(&data->rmon_stats, 0xff, sizeof(data->rmon_stats));
110121

111122
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
112123
dev->ethtool_ops->get_eth_phy_stats)
@@ -117,6 +128,10 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
117128
if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) &&
118129
dev->ethtool_ops->get_eth_ctrl_stats)
119130
dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats);
131+
if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) &&
132+
dev->ethtool_ops->get_rmon_stats)
133+
dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats,
134+
&data->rmon_ranges);
120135

121136
ethnl_ops_complete(dev);
122137
return 0;
@@ -141,6 +156,16 @@ static int stats_reply_size(const struct ethnl_req_info *req_base,
141156
n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64);
142157
n_grps++;
143158
}
159+
if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) {
160+
n_stats += sizeof(struct ethtool_rmon_stats) / sizeof(u64);
161+
n_grps++;
162+
/* Above includes the space for _A_STATS_GRP_HIST_VALs */
163+
164+
len += (nla_total_size(0) + /* _A_STATS_GRP_HIST */
165+
nla_total_size(4) + /* _A_STATS_GRP_HIST_BKT_LOW */
166+
nla_total_size(4)) * /* _A_STATS_GRP_HIST_BKT_HI */
167+
ETHTOOL_RMON_HIST_MAX * 2;
168+
}
144169

145170
len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */
146171
nla_total_size(4) + /* _A_STATS_GRP_ID */
@@ -258,6 +283,65 @@ static int stats_put_ctrl_stats(struct sk_buff *skb,
258283
return 0;
259284
}
260285

286+
static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist,
287+
const struct ethtool_rmon_hist_range *ranges)
288+
{
289+
struct nlattr *nest;
290+
int i;
291+
292+
if (!ranges)
293+
return 0;
294+
295+
for (i = 0; i < ETHTOOL_RMON_HIST_MAX; i++) {
296+
if (!ranges[i].low && !ranges[i].high)
297+
break;
298+
if (hist[i] == ETHTOOL_STAT_NOT_SET)
299+
continue;
300+
301+
nest = nla_nest_start(skb, attr);
302+
if (!nest)
303+
return -EMSGSIZE;
304+
305+
if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW,
306+
ranges[i].low) ||
307+
nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_HI,
308+
ranges[i].high) ||
309+
nla_put_u64_64bit(skb, ETHTOOL_A_STATS_GRP_HIST_VAL,
310+
hist[i], ETHTOOL_A_STATS_GRP_PAD))
311+
goto err_cancel_hist;
312+
313+
nla_nest_end(skb, nest);
314+
}
315+
316+
return 0;
317+
318+
err_cancel_hist:
319+
nla_nest_cancel(skb, nest);
320+
return -EMSGSIZE;
321+
}
322+
323+
static int stats_put_rmon_stats(struct sk_buff *skb,
324+
const struct stats_reply_data *data)
325+
{
326+
if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_RX,
327+
data->rmon_stats.hist, data->rmon_ranges) ||
328+
stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_TX,
329+
data->rmon_stats.hist_tx, data->rmon_ranges))
330+
return -EMSGSIZE;
331+
332+
if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE,
333+
data->rmon_stats.undersize_pkts) ||
334+
stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE,
335+
data->rmon_stats.oversize_pkts) ||
336+
stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG,
337+
data->rmon_stats.fragments) ||
338+
stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER,
339+
data->rmon_stats.jabbers))
340+
return -EMSGSIZE;
341+
342+
return 0;
343+
}
344+
261345
static int stats_put_stats(struct sk_buff *skb,
262346
const struct stats_reply_data *data,
263347
u32 id, u32 ss_id,
@@ -305,6 +389,9 @@ static int stats_fill_reply(struct sk_buff *skb,
305389
ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL,
306390
ETH_SS_STATS_ETH_CTRL,
307391
stats_put_ctrl_stats);
392+
if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask))
393+
ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON,
394+
ETH_SS_STATS_RMON, stats_put_rmon_stats);
308395

309396
return ret;
310397
}

net/ethtool/strset.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ static const struct strset_info info_template[] = {
100100
.count = __ETHTOOL_A_STATS_ETH_CTRL_CNT,
101101
.strings = stats_eth_ctrl_names,
102102
},
103+
[ETH_SS_STATS_RMON] = {
104+
.per_dev = false,
105+
.count = __ETHTOOL_A_STATS_RMON_CNT,
106+
.strings = stats_rmon_names,
107+
},
103108
};
104109

105110
struct strset_req_info {

0 commit comments

Comments
 (0)