linux/net/ethtool/stats.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3#include "netlink.h"
   4#include "common.h"
   5#include "bitset.h"
   6
   7struct stats_req_info {
   8        struct ethnl_req_info           base;
   9        DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT);
  10};
  11
  12#define STATS_REQINFO(__req_base) \
  13        container_of(__req_base, struct stats_req_info, base)
  14
  15struct stats_reply_data {
  16        struct ethnl_reply_data         base;
  17        struct ethtool_eth_phy_stats    phy_stats;
  18        struct ethtool_eth_mac_stats    mac_stats;
  19        struct ethtool_eth_ctrl_stats   ctrl_stats;
  20        struct ethtool_rmon_stats       rmon_stats;
  21        const struct ethtool_rmon_hist_range    *rmon_ranges;
  22};
  23
  24#define STATS_REPDATA(__reply_base) \
  25        container_of(__reply_base, struct stats_reply_data, base)
  26
  27const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = {
  28        [ETHTOOL_STATS_ETH_PHY]                 = "eth-phy",
  29        [ETHTOOL_STATS_ETH_MAC]                 = "eth-mac",
  30        [ETHTOOL_STATS_ETH_CTRL]                = "eth-ctrl",
  31        [ETHTOOL_STATS_RMON]                    = "rmon",
  32};
  33
  34const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = {
  35        [ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR]     = "SymbolErrorDuringCarrier",
  36};
  37
  38const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN] = {
  39        [ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT]      = "FramesTransmittedOK",
  40        [ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL]  = "SingleCollisionFrames",
  41        [ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL]   = "MultipleCollisionFrames",
  42        [ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT]      = "FramesReceivedOK",
  43        [ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR]     = "FrameCheckSequenceErrors",
  44        [ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR]   = "AlignmentErrors",
  45        [ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES]    = "OctetsTransmittedOK",
  46        [ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER]    = "FramesWithDeferredXmissions",
  47        [ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL]   = "LateCollisions",
  48        [ETHTOOL_A_STATS_ETH_MAC_11_XS_COL]     = "FramesAbortedDueToXSColls",
  49        [ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR] = "FramesLostDueToIntMACXmitError",
  50        [ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR]     = "CarrierSenseErrors",
  51        [ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES]   = "OctetsReceivedOK",
  52        [ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR] = "FramesLostDueToIntMACRcvError",
  53        [ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST]   = "MulticastFramesXmittedOK",
  54        [ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST]   = "BroadcastFramesXmittedOK",
  55        [ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER]   = "FramesWithExcessiveDeferral",
  56        [ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST]   = "MulticastFramesReceivedOK",
  57        [ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST]   = "BroadcastFramesReceivedOK",
  58        [ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR] = "InRangeLengthErrors",
  59        [ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN]    = "OutOfRangeLengthField",
  60        [ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR]       = "FrameTooLongErrors",
  61};
  62
  63const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN] = {
  64        [ETHTOOL_A_STATS_ETH_CTRL_3_TX]         = "MACControlFramesTransmitted",
  65        [ETHTOOL_A_STATS_ETH_CTRL_4_RX]         = "MACControlFramesReceived",
  66        [ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP]   = "UnsupportedOpcodesReceived",
  67};
  68
  69const 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
  76const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = {
  77        [ETHTOOL_A_STATS_HEADER]        =
  78                NLA_POLICY_NESTED(ethnl_header_policy),
  79        [ETHTOOL_A_STATS_GROUPS]        = { .type = NLA_NESTED },
  80};
  81
  82static int stats_parse_request(struct ethnl_req_info *req_base,
  83                               struct nlattr **tb,
  84                               struct netlink_ext_ack *extack)
  85{
  86        struct stats_req_info *req_info = STATS_REQINFO(req_base);
  87        bool mod = false;
  88        int err;
  89
  90        err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT,
  91                                  tb[ETHTOOL_A_STATS_GROUPS], stats_std_names,
  92                                  extack, &mod);
  93        if (err)
  94                return err;
  95
  96        if (!mod) {
  97                NL_SET_ERR_MSG(extack, "no stats requested");
  98                return -EINVAL;
  99        }
 100
 101        return 0;
 102}
 103
 104static int stats_prepare_data(const struct ethnl_req_info *req_base,
 105                              struct ethnl_reply_data *reply_base,
 106                              struct genl_info *info)
 107{
 108        const struct stats_req_info *req_info = STATS_REQINFO(req_base);
 109        struct stats_reply_data *data = STATS_REPDATA(reply_base);
 110        struct net_device *dev = reply_base->dev;
 111        int ret;
 112
 113        ret = ethnl_ops_begin(dev);
 114        if (ret < 0)
 115                return ret;
 116
 117        /* Mark all stats as unset (see ETHTOOL_STAT_NOT_SET) to prevent them
 118         * from being reported to user space in case driver did not set them.
 119         */
 120        memset(&data->phy_stats, 0xff, sizeof(data->phy_stats));
 121        memset(&data->mac_stats, 0xff, sizeof(data->mac_stats));
 122        memset(&data->ctrl_stats, 0xff, sizeof(data->ctrl_stats));
 123        memset(&data->rmon_stats, 0xff, sizeof(data->rmon_stats));
 124
 125        if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) &&
 126            dev->ethtool_ops->get_eth_phy_stats)
 127                dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats);
 128        if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask) &&
 129            dev->ethtool_ops->get_eth_mac_stats)
 130                dev->ethtool_ops->get_eth_mac_stats(dev, &data->mac_stats);
 131        if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) &&
 132            dev->ethtool_ops->get_eth_ctrl_stats)
 133                dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats);
 134        if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) &&
 135            dev->ethtool_ops->get_rmon_stats)
 136                dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats,
 137                                                 &data->rmon_ranges);
 138
 139        ethnl_ops_complete(dev);
 140        return 0;
 141}
 142
 143static int stats_reply_size(const struct ethnl_req_info *req_base,
 144                            const struct ethnl_reply_data *reply_base)
 145{
 146        const struct stats_req_info *req_info = STATS_REQINFO(req_base);
 147        unsigned int n_grps = 0, n_stats = 0;
 148        int len = 0;
 149
 150        if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) {
 151                n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64);
 152                n_grps++;
 153        }
 154        if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) {
 155                n_stats += sizeof(struct ethtool_eth_mac_stats) / sizeof(u64);
 156                n_grps++;
 157        }
 158        if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) {
 159                n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64);
 160                n_grps++;
 161        }
 162        if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) {
 163                n_stats += sizeof(struct ethtool_rmon_stats) / sizeof(u64);
 164                n_grps++;
 165                /* Above includes the space for _A_STATS_GRP_HIST_VALs */
 166
 167                len += (nla_total_size(0) +     /* _A_STATS_GRP_HIST */
 168                        nla_total_size(4) +     /* _A_STATS_GRP_HIST_BKT_LOW */
 169                        nla_total_size(4)) *    /* _A_STATS_GRP_HIST_BKT_HI */
 170                        ETHTOOL_RMON_HIST_MAX * 2;
 171        }
 172
 173        len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */
 174                         nla_total_size(4) + /* _A_STATS_GRP_ID */
 175                         nla_total_size(4)); /* _A_STATS_GRP_SS_ID */
 176        len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */
 177                          nla_total_size_64bit(sizeof(u64)));
 178
 179        return len;
 180}
 181
 182static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val)
 183{
 184        struct nlattr *nest;
 185        int ret;
 186
 187        if (val == ETHTOOL_STAT_NOT_SET)
 188                return 0;
 189
 190        /* We want to start stats attr types from 0, so we don't have a type
 191         * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside
 192         * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the
 193         * actual attr we're 4B off - nla_need_padding_for_64bit() & co.
 194         * can't be used.
 195         */
 196#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
 197        if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8))
 198                if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0))
 199                        return -EMSGSIZE;
 200#endif
 201
 202        nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT);
 203        if (!nest)
 204                return -EMSGSIZE;
 205
 206        ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */);
 207        if (ret) {
 208                nla_nest_cancel(skb, nest);
 209                return ret;
 210        }
 211
 212        nla_nest_end(skb, nest);
 213        return 0;
 214}
 215
 216static int stats_put_phy_stats(struct sk_buff *skb,
 217                               const struct stats_reply_data *data)
 218{
 219        if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR,
 220                     data->phy_stats.SymbolErrorDuringCarrier))
 221                return -EMSGSIZE;
 222        return 0;
 223}
 224
 225static int stats_put_mac_stats(struct sk_buff *skb,
 226                               const struct stats_reply_data *data)
 227{
 228        if (stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT,
 229                     data->mac_stats.FramesTransmittedOK) ||
 230            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL,
 231                     data->mac_stats.SingleCollisionFrames) ||
 232            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL,
 233                     data->mac_stats.MultipleCollisionFrames) ||
 234            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT,
 235                     data->mac_stats.FramesReceivedOK) ||
 236            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR,
 237                     data->mac_stats.FrameCheckSequenceErrors) ||
 238            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR,
 239                     data->mac_stats.AlignmentErrors) ||
 240            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES,
 241                     data->mac_stats.OctetsTransmittedOK) ||
 242            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER,
 243                     data->mac_stats.FramesWithDeferredXmissions) ||
 244            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL,
 245                     data->mac_stats.LateCollisions) ||
 246            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_11_XS_COL,
 247                     data->mac_stats.FramesAbortedDueToXSColls) ||
 248            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR,
 249                     data->mac_stats.FramesLostDueToIntMACXmitError) ||
 250            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR,
 251                     data->mac_stats.CarrierSenseErrors) ||
 252            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES,
 253                     data->mac_stats.OctetsReceivedOK) ||
 254            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR,
 255                     data->mac_stats.FramesLostDueToIntMACRcvError) ||
 256            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST,
 257                     data->mac_stats.MulticastFramesXmittedOK) ||
 258            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST,
 259                     data->mac_stats.BroadcastFramesXmittedOK) ||
 260            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER,
 261                     data->mac_stats.FramesWithExcessiveDeferral) ||
 262            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST,
 263                     data->mac_stats.MulticastFramesReceivedOK) ||
 264            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST,
 265                     data->mac_stats.BroadcastFramesReceivedOK) ||
 266            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR,
 267                     data->mac_stats.InRangeLengthErrors) ||
 268            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN,
 269                     data->mac_stats.OutOfRangeLengthField) ||
 270            stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR,
 271                     data->mac_stats.FrameTooLongErrors))
 272                return -EMSGSIZE;
 273        return 0;
 274}
 275
 276static int stats_put_ctrl_stats(struct sk_buff *skb,
 277                                const struct stats_reply_data *data)
 278{
 279        if (stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_3_TX,
 280                     data->ctrl_stats.MACControlFramesTransmitted) ||
 281            stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_4_RX,
 282                     data->ctrl_stats.MACControlFramesReceived) ||
 283            stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP,
 284                     data->ctrl_stats.UnsupportedOpcodesReceived))
 285                return -EMSGSIZE;
 286        return 0;
 287}
 288
 289static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist,
 290                               const struct ethtool_rmon_hist_range *ranges)
 291{
 292        struct nlattr *nest;
 293        int i;
 294
 295        if (!ranges)
 296                return 0;
 297
 298        for (i = 0; i < ETHTOOL_RMON_HIST_MAX; i++) {
 299                if (!ranges[i].low && !ranges[i].high)
 300                        break;
 301                if (hist[i] == ETHTOOL_STAT_NOT_SET)
 302                        continue;
 303
 304                nest = nla_nest_start(skb, attr);
 305                if (!nest)
 306                        return -EMSGSIZE;
 307
 308                if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW,
 309                                ranges[i].low) ||
 310                    nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_HI,
 311                                ranges[i].high) ||
 312                    nla_put_u64_64bit(skb, ETHTOOL_A_STATS_GRP_HIST_VAL,
 313                                      hist[i], ETHTOOL_A_STATS_GRP_PAD))
 314                        goto err_cancel_hist;
 315
 316                nla_nest_end(skb, nest);
 317        }
 318
 319        return 0;
 320
 321err_cancel_hist:
 322        nla_nest_cancel(skb, nest);
 323        return -EMSGSIZE;
 324}
 325
 326static int stats_put_rmon_stats(struct sk_buff *skb,
 327                                const struct stats_reply_data *data)
 328{
 329        if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_RX,
 330                                data->rmon_stats.hist, data->rmon_ranges) ||
 331            stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_TX,
 332                                data->rmon_stats.hist_tx, data->rmon_ranges))
 333                return -EMSGSIZE;
 334
 335        if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE,
 336                     data->rmon_stats.undersize_pkts) ||
 337            stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE,
 338                     data->rmon_stats.oversize_pkts) ||
 339            stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG,
 340                     data->rmon_stats.fragments) ||
 341            stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER,
 342                     data->rmon_stats.jabbers))
 343                return -EMSGSIZE;
 344
 345        return 0;
 346}
 347
 348static int stats_put_stats(struct sk_buff *skb,
 349                           const struct stats_reply_data *data,
 350                           u32 id, u32 ss_id,
 351                           int (*cb)(struct sk_buff *skb,
 352                                     const struct stats_reply_data *data))
 353{
 354        struct nlattr *nest;
 355
 356        nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP);
 357        if (!nest)
 358                return -EMSGSIZE;
 359
 360        if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) ||
 361            nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id))
 362                goto err_cancel;
 363
 364        if (cb(skb, data))
 365                goto err_cancel;
 366
 367        nla_nest_end(skb, nest);
 368        return 0;
 369
 370err_cancel:
 371        nla_nest_cancel(skb, nest);
 372        return -EMSGSIZE;
 373}
 374
 375static int stats_fill_reply(struct sk_buff *skb,
 376                            const struct ethnl_req_info *req_base,
 377                            const struct ethnl_reply_data *reply_base)
 378{
 379        const struct stats_req_info *req_info = STATS_REQINFO(req_base);
 380        const struct stats_reply_data *data = STATS_REPDATA(reply_base);
 381        int ret = 0;
 382
 383        if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask))
 384                ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY,
 385                                      ETH_SS_STATS_ETH_PHY,
 386                                      stats_put_phy_stats);
 387        if (!ret && test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask))
 388                ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_MAC,
 389                                      ETH_SS_STATS_ETH_MAC,
 390                                      stats_put_mac_stats);
 391        if (!ret && test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask))
 392                ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL,
 393                                      ETH_SS_STATS_ETH_CTRL,
 394                                      stats_put_ctrl_stats);
 395        if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask))
 396                ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON,
 397                                      ETH_SS_STATS_RMON, stats_put_rmon_stats);
 398
 399        return ret;
 400}
 401
 402const struct ethnl_request_ops ethnl_stats_request_ops = {
 403        .request_cmd            = ETHTOOL_MSG_STATS_GET,
 404        .reply_cmd              = ETHTOOL_MSG_STATS_GET_REPLY,
 405        .hdr_attr               = ETHTOOL_A_STATS_HEADER,
 406        .req_info_size          = sizeof(struct stats_req_info),
 407        .reply_data_size        = sizeof(struct stats_reply_data),
 408
 409        .parse_request          = stats_parse_request,
 410        .prepare_data           = stats_prepare_data,
 411        .reply_size             = stats_reply_size,
 412        .fill_reply             = stats_fill_reply,
 413};
 414