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>
This commit is contained in:
Jakub Kicinski
2021-04-16 12:27:42 -07:00
committed by David S. Miller
parent bfad2b979d
commit a8b06e9d40
6 changed files with 161 additions and 0 deletions

View File

@@ -405,5 +405,6 @@ extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN];
extern const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN];
extern const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN];
#endif /* _NET_ETHTOOL_NETLINK_H */

View File

@@ -17,6 +17,8 @@ struct stats_reply_data {
struct ethtool_eth_phy_stats phy_stats;
struct ethtool_eth_mac_stats mac_stats;
struct ethtool_eth_ctrl_stats ctrl_stats;
struct ethtool_rmon_stats rmon_stats;
const struct ethtool_rmon_hist_range *rmon_ranges;
};
#define STATS_REPDATA(__reply_base) \
@@ -26,6 +28,7 @@ const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = {
[ETHTOOL_STATS_ETH_PHY] = "eth-phy",
[ETHTOOL_STATS_ETH_MAC] = "eth-mac",
[ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl",
[ETHTOOL_STATS_RMON] = "rmon",
};
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]
[ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived",
};
const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = {
[ETHTOOL_A_STATS_RMON_UNDERSIZE] = "etherStatsUndersizePkts",
[ETHTOOL_A_STATS_RMON_OVERSIZE] = "etherStatsOversizePkts",
[ETHTOOL_A_STATS_RMON_FRAG] = "etherStatsFragments",
[ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers",
};
const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
[ETHTOOL_A_STATS_HEADER] =
NLA_POLICY_NESTED(ethnl_header_policy),
@@ -107,6 +117,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
memset(&data->phy_stats, 0xff, sizeof(data->phy_stats));
memset(&data->mac_stats, 0xff, sizeof(data->mac_stats));
memset(&data->ctrl_stats, 0xff, sizeof(data->mac_stats));
memset(&data->rmon_stats, 0xff, sizeof(data->rmon_stats));
if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
dev->ethtool_ops->get_eth_phy_stats)
@@ -117,6 +128,10 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base,
if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) &&
dev->ethtool_ops->get_eth_ctrl_stats)
dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats);
if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) &&
dev->ethtool_ops->get_rmon_stats)
dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats,
&data->rmon_ranges);
ethnl_ops_complete(dev);
return 0;
@@ -141,6 +156,16 @@ static int stats_reply_size(const struct ethnl_req_info *req_base,
n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64);
n_grps++;
}
if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) {
n_stats += sizeof(struct ethtool_rmon_stats) / sizeof(u64);
n_grps++;
/* Above includes the space for _A_STATS_GRP_HIST_VALs */
len += (nla_total_size(0) + /* _A_STATS_GRP_HIST */
nla_total_size(4) + /* _A_STATS_GRP_HIST_BKT_LOW */
nla_total_size(4)) * /* _A_STATS_GRP_HIST_BKT_HI */
ETHTOOL_RMON_HIST_MAX * 2;
}
len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */
nla_total_size(4) + /* _A_STATS_GRP_ID */
@@ -258,6 +283,65 @@ static int stats_put_ctrl_stats(struct sk_buff *skb,
return 0;
}
static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist,
const struct ethtool_rmon_hist_range *ranges)
{
struct nlattr *nest;
int i;
if (!ranges)
return 0;
for (i = 0; i < ETHTOOL_RMON_HIST_MAX; i++) {
if (!ranges[i].low && !ranges[i].high)
break;
if (hist[i] == ETHTOOL_STAT_NOT_SET)
continue;
nest = nla_nest_start(skb, attr);
if (!nest)
return -EMSGSIZE;
if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW,
ranges[i].low) ||
nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_HI,
ranges[i].high) ||
nla_put_u64_64bit(skb, ETHTOOL_A_STATS_GRP_HIST_VAL,
hist[i], ETHTOOL_A_STATS_GRP_PAD))
goto err_cancel_hist;
nla_nest_end(skb, nest);
}
return 0;
err_cancel_hist:
nla_nest_cancel(skb, nest);
return -EMSGSIZE;
}
static int stats_put_rmon_stats(struct sk_buff *skb,
const struct stats_reply_data *data)
{
if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_RX,
data->rmon_stats.hist, data->rmon_ranges) ||
stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_TX,
data->rmon_stats.hist_tx, data->rmon_ranges))
return -EMSGSIZE;
if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE,
data->rmon_stats.undersize_pkts) ||
stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE,
data->rmon_stats.oversize_pkts) ||
stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG,
data->rmon_stats.fragments) ||
stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER,
data->rmon_stats.jabbers))
return -EMSGSIZE;
return 0;
}
static int stats_put_stats(struct sk_buff *skb,
const struct stats_reply_data *data,
u32 id, u32 ss_id,
@@ -305,6 +389,9 @@ static int stats_fill_reply(struct sk_buff *skb,
ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL,
ETH_SS_STATS_ETH_CTRL,
stats_put_ctrl_stats);
if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask))
ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON,
ETH_SS_STATS_RMON, stats_put_rmon_stats);
return ret;
}

View File

@@ -100,6 +100,11 @@ static const struct strset_info info_template[] = {
.count = __ETHTOOL_A_STATS_ETH_CTRL_CNT,
.strings = stats_eth_ctrl_names,
},
[ETH_SS_STATS_RMON] = {
.per_dev = false,
.count = __ETHTOOL_A_STATS_RMON_CNT,
.strings = stats_rmon_names,
},
};
struct strset_req_info {