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).
Unified · Split
| @@ -346,6 +346,44 @@ struct ethtool_fec_stats { · 345 unmodified lines | ||
| 346 | 346 | } corrected_blocks, uncorrectable_blocks, corrected_bits; |
| 347 | 347 | }; |
| 348 | 348 | |
| 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 | + | |
| 349 | 387 | #define ETH_MODULE_EEPROM_PAGE_LEN 128 |
| 350 | 388 | #define ETH_MODULE_MAX_I2C_ADDRESS 0x7f |
| 351 | 389 | |
| @@ -534,6 +572,8 @@ struct ethtool_module_eeprom { · 182 unmodified lines | ||
| 534 | 572 | * @get_eth_phy_stats: Query some of the IEEE 802.3 PHY statistics. |
| 535 | 573 | * @get_eth_mac_stats: Query some of the IEEE 802.3 MAC statistics. |
| 536 | 574 | * @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. | |
| 537 | 577 | * |
| 538 | 578 | * All operations are optional (i.e. the function pointer may be set |
| 539 | 579 | * to %NULL) and callers must take this into account. Callers must |
| @@ -650,6 +690,9 @@ struct ethtool_ops { · 110 unmodified lines | ||
| 650 | 690 | struct ethtool_eth_mac_stats *mac_stats); |
| 651 | 691 | void (*get_eth_ctrl_stats)(struct net_device *dev, |
| 652 | 692 | 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); | |
| 653 | 696 | }; |
| 654 | 697 | |
| 655 | 698 | int ethtool_check_ops(const struct ethtool_ops *ops); |
| @@ -673,6 +673,7 @@ enum ethtool_link_ext_substate_cable_issue { · 672 unmodified lines | ||
| 673 | 673 | * @ETH_SS_STATS_ETH_PHY: names of IEEE 802.3 PHY statistics |
| 674 | 674 | * @ETH_SS_STATS_ETH_MAC: names of IEEE 802.3 MAC statistics |
| 675 | 675 | * @ETH_SS_STATS_ETH_CTRL: names of IEEE 802.3 MAC Control statistics |
| 676 | + * @ETH_SS_STATS_RMON: names of RMON statistics | |
| 676 | 677 | * |
| 677 | 678 | * @ETH_SS_COUNT: number of defined string sets |
| 678 | 679 | */ |
| @@ -697,6 +698,7 @@ enum ethtool_stringset { · 18 unmodified lines | ||
| 697 | 698 | ETH_SS_STATS_ETH_PHY, |
| 698 | 699 | ETH_SS_STATS_ETH_MAC, |
| 699 | 700 | ETH_SS_STATS_ETH_CTRL, |
| 701 | + ETH_SS_STATS_RMON, | |
| 700 | 702 | |
| 701 | 703 | /* add new constants above here */ |
| 702 | 704 | ETH_SS_COUNT |
| @@ -700,6 +700,7 @@ enum { · 699 unmodified lines | ||
| 700 | 700 | ETHTOOL_STATS_ETH_PHY, |
| 701 | 701 | ETHTOOL_STATS_ETH_MAC, |
| 702 | 702 | ETHTOOL_STATS_ETH_CTRL, |
| 703 | + ETHTOOL_STATS_RMON, | |
| 703 | 704 | |
| 704 | 705 | /* add new constants above here */ |
| 705 | 706 | __ETHTOOL_STATS_CNT |
| @@ -714,6 +715,13 @@ enum { · 8 unmodified lines | ||
| 714 | 715 | |
| 715 | 716 | ETHTOOL_A_STATS_GRP_STAT, /* nest */ |
| 716 | 717 | |
| 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 | + | |
| 717 | 725 | /* add new constants above here */ |
| 718 | 726 | __ETHTOOL_A_STATS_GRP_CNT, |
| 719 | 727 | ETHTOOL_A_STATS_GRP_MAX = (__ETHTOOL_A_STATS_CNT - 1) |
| @@ -793,6 +801,21 @@ enum { · 73 unmodified lines | ||
| 793 | 801 | ETHTOOL_A_STATS_ETH_CTRL_MAX = (__ETHTOOL_A_STATS_ETH_CTRL_CNT - 1) |
| 794 | 802 | }; |
| 795 | 803 | |
| 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 | + | |
| 796 | 819 | /* generic netlink info */ |
| 797 | 820 | #define ETHTOOL_GENL_NAME "ethtool" |
| 798 | 821 | #define ETHTOOL_GENL_VERSION 1 |
| @@ -405,5 +405,6 @@ extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; · 404 unmodified lines | ||
| 405 | 405 | extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN]; |
| 406 | 406 | extern const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN]; |
| 407 | 407 | 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]; | |
| 408 | 409 | |
| 409 | 410 | #endif /* _NET_ETHTOOL_NETLINK_H */ |
| @@ -17,6 +17,8 @@ struct stats_reply_data { · 16 unmodified lines | ||
| 17 | 17 | struct ethtool_eth_phy_stats phy_stats; |
| 18 | 18 | struct ethtool_eth_mac_stats mac_stats; |
| 19 | 19 | struct ethtool_eth_ctrl_stats ctrl_stats; |
| 20 | + struct ethtool_rmon_stats rmon_stats; | |
| 21 | + const struct ethtool_rmon_hist_range *rmon_ranges; | |
| 20 | 22 | }; |
| 21 | 23 | |
| 22 | 24 | #define STATS_REPDATA(__reply_base) \ |
| @@ -26,6 +28,7 @@ const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { · 3 unmodified lines | ||
| 26 | 28 | [ETHTOOL_STATS_ETH_PHY] = "eth-phy", |
| 27 | 29 | [ETHTOOL_STATS_ETH_MAC] = "eth-mac", |
| 28 | 30 | [ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl", |
| 31 | + [ETHTOOL_STATS_RMON] = "rmon", | |
| 29 | 32 | }; |
| 30 | 33 | |
| 31 | 34 | 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] · 31 unmodified lines | ||
| 63 | 66 | [ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived", |
| 64 | 67 | }; |
| 65 | 68 | |
| 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 | + | |
| 66 | 76 | const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = { |
| 67 | 77 | [ETHTOOL_A_STATS_HEADER] = |
| 68 | 78 | NLA_POLICY_NESTED(ethnl_header_policy), |
| @@ -107,6 +117,7 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base, · 38 unmodified lines | ||
| 107 | 117 | memset(&data->phy_stats, 0xff, sizeof(data->phy_stats)); |
| 108 | 118 | memset(&data->mac_stats, 0xff, sizeof(data->mac_stats)); |
| 109 | 119 | memset(&data->ctrl_stats, 0xff, sizeof(data->mac_stats)); |
| 120 | + memset(&data->rmon_stats, 0xff, sizeof(data->rmon_stats)); | |
| 110 | 121 | |
| 111 | 122 | if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && |
| 112 | 123 | dev->ethtool_ops->get_eth_phy_stats) |
| @@ -117,6 +128,10 @@ static int stats_prepare_data(const struct ethnl_req_info *req_base, · 4 unmodified lines | ||
| 117 | 128 | if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) && |
| 118 | 129 | dev->ethtool_ops->get_eth_ctrl_stats) |
| 119 | 130 | 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); | |
| 120 | 135 | |
| 121 | 136 | ethnl_ops_complete(dev); |
| 122 | 137 | return 0; |
| @@ -141,6 +156,16 @@ static int stats_reply_size(const struct ethnl_req_info *req_base, · 18 unmodified lines | ||
| 141 | 156 | n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64); |
| 142 | 157 | n_grps++; |
| 143 | 158 | } |
| 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 | + } | |
| 144 | 169 | |
| 145 | 170 | len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */ |
| 146 | 171 | nla_total_size(4) + /* _A_STATS_GRP_ID */ |
| @@ -258,6 +283,65 @@ static int stats_put_ctrl_stats(struct sk_buff *skb, · 111 unmodified lines | ||
| 258 | 283 | return 0; |
| 259 | 284 | } |
| 260 | 285 | |
| 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 | + | |
| 261 | 345 | static int stats_put_stats(struct sk_buff *skb, |
| 262 | 346 | const struct stats_reply_data *data, |
| 263 | 347 | u32 id, u32 ss_id, |
| @@ -305,6 +389,9 @@ static int stats_fill_reply(struct sk_buff *skb, · 41 unmodified lines | ||
| 305 | 389 | ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL, |
| 306 | 390 | ETH_SS_STATS_ETH_CTRL, |
| 307 | 391 | 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); | |
| 308 | 395 | |
| 309 | 396 | return ret; |
| 310 | 397 | } |
| @@ -100,6 +100,11 @@ static const struct strset_info info_template[] = { · 99 unmodified lines | ||
| 100 | 100 | .count = __ETHTOOL_A_STATS_ETH_CTRL_CNT, |
| 101 | 101 | .strings = stats_eth_ctrl_names, |
| 102 | 102 | }, |
| 103 | + [ETH_SS_STATS_RMON] = { | |
| 104 | + .per_dev = false, | |
| 105 | + .count = __ETHTOOL_A_STATS_RMON_CNT, | |
| 106 | + .strings = stats_rmon_names, | |
| 107 | + }, | |
| 103 | 108 | }; |
| 104 | 109 | |
| 105 | 110 | struct strset_req_info { |