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:
committed by
David S. Miller
parent
bfad2b979d
commit
a8b06e9d40
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user