1f09ea6fbSJakub Kicinski // SPDX-License-Identifier: GPL-2.0-only 2f09ea6fbSJakub Kicinski 3f09ea6fbSJakub Kicinski #include "netlink.h" 4f09ea6fbSJakub Kicinski #include "common.h" 5f09ea6fbSJakub Kicinski #include "bitset.h" 6f09ea6fbSJakub Kicinski 7f09ea6fbSJakub Kicinski struct stats_req_info { 8f09ea6fbSJakub Kicinski struct ethnl_req_info base; 9f09ea6fbSJakub Kicinski DECLARE_BITMAP(stat_mask, __ETHTOOL_STATS_CNT); 1004692c90SVladimir Oltean enum ethtool_mac_stats_src src; 11f09ea6fbSJakub Kicinski }; 12f09ea6fbSJakub Kicinski 13f09ea6fbSJakub Kicinski #define STATS_REQINFO(__req_base) \ 14f09ea6fbSJakub Kicinski container_of(__req_base, struct stats_req_info, base) 15f09ea6fbSJakub Kicinski 16f09ea6fbSJakub Kicinski struct stats_reply_data { 17f09ea6fbSJakub Kicinski struct ethnl_reply_data base; 18812ad3d2SKees Cook struct_group(stats, 19f09ea6fbSJakub Kicinski struct ethtool_eth_phy_stats phy_stats; 20ca224454SJakub Kicinski struct ethtool_eth_mac_stats mac_stats; 21bfad2b97SJakub Kicinski struct ethtool_eth_ctrl_stats ctrl_stats; 22a8b06e9dSJakub Kicinski struct ethtool_rmon_stats rmon_stats; 23812ad3d2SKees Cook ); 24a8b06e9dSJakub Kicinski const struct ethtool_rmon_hist_range *rmon_ranges; 25f09ea6fbSJakub Kicinski }; 26f09ea6fbSJakub Kicinski 27f09ea6fbSJakub Kicinski #define STATS_REPDATA(__reply_base) \ 28f09ea6fbSJakub Kicinski container_of(__reply_base, struct stats_reply_data, base) 29f09ea6fbSJakub Kicinski 30f09ea6fbSJakub Kicinski const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { 31f09ea6fbSJakub Kicinski [ETHTOOL_STATS_ETH_PHY] = "eth-phy", 32ca224454SJakub Kicinski [ETHTOOL_STATS_ETH_MAC] = "eth-mac", 33bfad2b97SJakub Kicinski [ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl", 34a8b06e9dSJakub Kicinski [ETHTOOL_STATS_RMON] = "rmon", 35f09ea6fbSJakub Kicinski }; 36f09ea6fbSJakub Kicinski 37f09ea6fbSJakub Kicinski const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = { 38f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR] = "SymbolErrorDuringCarrier", 39f09ea6fbSJakub Kicinski }; 40f09ea6fbSJakub Kicinski 41ca224454SJakub Kicinski const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN] = { 42ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT] = "FramesTransmittedOK", 43ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL] = "SingleCollisionFrames", 44ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL] = "MultipleCollisionFrames", 45ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT] = "FramesReceivedOK", 46ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR] = "FrameCheckSequenceErrors", 47ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR] = "AlignmentErrors", 48ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES] = "OctetsTransmittedOK", 49ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER] = "FramesWithDeferredXmissions", 50ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL] = "LateCollisions", 51ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_11_XS_COL] = "FramesAbortedDueToXSColls", 52ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR] = "FramesLostDueToIntMACXmitError", 53ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR] = "CarrierSenseErrors", 54ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES] = "OctetsReceivedOK", 55ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR] = "FramesLostDueToIntMACRcvError", 56ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST] = "MulticastFramesXmittedOK", 57ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST] = "BroadcastFramesXmittedOK", 58ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER] = "FramesWithExcessiveDeferral", 59ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST] = "MulticastFramesReceivedOK", 60ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST] = "BroadcastFramesReceivedOK", 61ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR] = "InRangeLengthErrors", 62ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN] = "OutOfRangeLengthField", 63ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR] = "FrameTooLongErrors", 64ca224454SJakub Kicinski }; 65ca224454SJakub Kicinski 66bfad2b97SJakub Kicinski const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN] = { 67bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_3_TX] = "MACControlFramesTransmitted", 68bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_4_RX] = "MACControlFramesReceived", 69bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived", 70bfad2b97SJakub Kicinski }; 71bfad2b97SJakub Kicinski 72a8b06e9dSJakub Kicinski const char stats_rmon_names[__ETHTOOL_A_STATS_RMON_CNT][ETH_GSTRING_LEN] = { 73a8b06e9dSJakub Kicinski [ETHTOOL_A_STATS_RMON_UNDERSIZE] = "etherStatsUndersizePkts", 74a8b06e9dSJakub Kicinski [ETHTOOL_A_STATS_RMON_OVERSIZE] = "etherStatsOversizePkts", 75a8b06e9dSJakub Kicinski [ETHTOOL_A_STATS_RMON_FRAG] = "etherStatsFragments", 76a8b06e9dSJakub Kicinski [ETHTOOL_A_STATS_RMON_JABBER] = "etherStatsJabbers", 77a8b06e9dSJakub Kicinski }; 78a8b06e9dSJakub Kicinski 7904692c90SVladimir Oltean const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_SRC + 1] = { 80f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_HEADER] = 81f09ea6fbSJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 82f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED }, 8304692c90SVladimir Oltean [ETHTOOL_A_STATS_SRC] = 8404692c90SVladimir Oltean NLA_POLICY_MAX(NLA_U32, ETHTOOL_MAC_STATS_SRC_PMAC), 85f09ea6fbSJakub Kicinski }; 86f09ea6fbSJakub Kicinski 87f09ea6fbSJakub Kicinski static int stats_parse_request(struct ethnl_req_info *req_base, 88f09ea6fbSJakub Kicinski struct nlattr **tb, 89f09ea6fbSJakub Kicinski struct netlink_ext_ack *extack) 90f09ea6fbSJakub Kicinski { 9104692c90SVladimir Oltean enum ethtool_mac_stats_src src = ETHTOOL_MAC_STATS_SRC_AGGREGATE; 92f09ea6fbSJakub Kicinski struct stats_req_info *req_info = STATS_REQINFO(req_base); 93f09ea6fbSJakub Kicinski bool mod = false; 94f09ea6fbSJakub Kicinski int err; 95f09ea6fbSJakub Kicinski 96f09ea6fbSJakub Kicinski err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT, 97f09ea6fbSJakub Kicinski tb[ETHTOOL_A_STATS_GROUPS], stats_std_names, 98f09ea6fbSJakub Kicinski extack, &mod); 99f09ea6fbSJakub Kicinski if (err) 100f09ea6fbSJakub Kicinski return err; 101f09ea6fbSJakub Kicinski 102f09ea6fbSJakub Kicinski if (!mod) { 103f09ea6fbSJakub Kicinski NL_SET_ERR_MSG(extack, "no stats requested"); 104f09ea6fbSJakub Kicinski return -EINVAL; 105f09ea6fbSJakub Kicinski } 106f09ea6fbSJakub Kicinski 10704692c90SVladimir Oltean if (tb[ETHTOOL_A_STATS_SRC]) 10804692c90SVladimir Oltean src = nla_get_u32(tb[ETHTOOL_A_STATS_SRC]); 10904692c90SVladimir Oltean 11004692c90SVladimir Oltean req_info->src = src; 11104692c90SVladimir Oltean 112f09ea6fbSJakub Kicinski return 0; 113f09ea6fbSJakub Kicinski } 114f09ea6fbSJakub Kicinski 115f09ea6fbSJakub Kicinski static int stats_prepare_data(const struct ethnl_req_info *req_base, 116f09ea6fbSJakub Kicinski struct ethnl_reply_data *reply_base, 117f09ea6fbSJakub Kicinski struct genl_info *info) 118f09ea6fbSJakub Kicinski { 119f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 120f09ea6fbSJakub Kicinski struct stats_reply_data *data = STATS_REPDATA(reply_base); 12104692c90SVladimir Oltean enum ethtool_mac_stats_src src = req_info->src; 12204692c90SVladimir Oltean struct netlink_ext_ack *extack = info->extack; 123f09ea6fbSJakub Kicinski struct net_device *dev = reply_base->dev; 124f09ea6fbSJakub Kicinski int ret; 125f09ea6fbSJakub Kicinski 126f09ea6fbSJakub Kicinski ret = ethnl_ops_begin(dev); 127f09ea6fbSJakub Kicinski if (ret < 0) 128f09ea6fbSJakub Kicinski return ret; 129f09ea6fbSJakub Kicinski 13004692c90SVladimir Oltean if ((src == ETHTOOL_MAC_STATS_SRC_EMAC || 13104692c90SVladimir Oltean src == ETHTOOL_MAC_STATS_SRC_PMAC) && 13204692c90SVladimir Oltean !__ethtool_dev_mm_supported(dev)) { 13304692c90SVladimir Oltean NL_SET_ERR_MSG_MOD(extack, 13404692c90SVladimir Oltean "Device does not support MAC merge layer"); 13504692c90SVladimir Oltean ethnl_ops_complete(dev); 13604692c90SVladimir Oltean return -EOPNOTSUPP; 13704692c90SVladimir Oltean } 13804692c90SVladimir Oltean 139d1f0a5e1SJakub Kicinski /* Mark all stats as unset (see ETHTOOL_STAT_NOT_SET) to prevent them 140d1f0a5e1SJakub Kicinski * from being reported to user space in case driver did not set them. 141d1f0a5e1SJakub Kicinski */ 142812ad3d2SKees Cook memset(&data->stats, 0xff, sizeof(data->stats)); 143f09ea6fbSJakub Kicinski 14404692c90SVladimir Oltean data->phy_stats.src = src; 14504692c90SVladimir Oltean data->mac_stats.src = src; 14604692c90SVladimir Oltean data->ctrl_stats.src = src; 14704692c90SVladimir Oltean data->rmon_stats.src = src; 14804692c90SVladimir Oltean 149f09ea6fbSJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && 150f09ea6fbSJakub Kicinski dev->ethtool_ops->get_eth_phy_stats) 151f09ea6fbSJakub Kicinski dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats); 152ca224454SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask) && 153ca224454SJakub Kicinski dev->ethtool_ops->get_eth_mac_stats) 154ca224454SJakub Kicinski dev->ethtool_ops->get_eth_mac_stats(dev, &data->mac_stats); 155bfad2b97SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) && 156bfad2b97SJakub Kicinski dev->ethtool_ops->get_eth_ctrl_stats) 157bfad2b97SJakub Kicinski dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats); 158a8b06e9dSJakub Kicinski if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask) && 159a8b06e9dSJakub Kicinski dev->ethtool_ops->get_rmon_stats) 160a8b06e9dSJakub Kicinski dev->ethtool_ops->get_rmon_stats(dev, &data->rmon_stats, 161a8b06e9dSJakub Kicinski &data->rmon_ranges); 162f09ea6fbSJakub Kicinski 163f09ea6fbSJakub Kicinski ethnl_ops_complete(dev); 164f09ea6fbSJakub Kicinski return 0; 165f09ea6fbSJakub Kicinski } 166f09ea6fbSJakub Kicinski 167f09ea6fbSJakub Kicinski static int stats_reply_size(const struct ethnl_req_info *req_base, 168f09ea6fbSJakub Kicinski const struct ethnl_reply_data *reply_base) 169f09ea6fbSJakub Kicinski { 170f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 171f09ea6fbSJakub Kicinski unsigned int n_grps = 0, n_stats = 0; 172f09ea6fbSJakub Kicinski int len = 0; 173f09ea6fbSJakub Kicinski 17404692c90SVladimir Oltean len += nla_total_size(sizeof(u32)); /* _STATS_SRC */ 17504692c90SVladimir Oltean 176f09ea6fbSJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) { 177f09ea6fbSJakub Kicinski n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64); 178f09ea6fbSJakub Kicinski n_grps++; 179f09ea6fbSJakub Kicinski } 180ca224454SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) { 181ca224454SJakub Kicinski n_stats += sizeof(struct ethtool_eth_mac_stats) / sizeof(u64); 182ca224454SJakub Kicinski n_grps++; 183ca224454SJakub Kicinski } 184bfad2b97SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) { 185bfad2b97SJakub Kicinski n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64); 186bfad2b97SJakub Kicinski n_grps++; 187bfad2b97SJakub Kicinski } 188a8b06e9dSJakub Kicinski if (test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) { 189a8b06e9dSJakub Kicinski n_stats += sizeof(struct ethtool_rmon_stats) / sizeof(u64); 190a8b06e9dSJakub Kicinski n_grps++; 191a8b06e9dSJakub Kicinski /* Above includes the space for _A_STATS_GRP_HIST_VALs */ 192a8b06e9dSJakub Kicinski 193a8b06e9dSJakub Kicinski len += (nla_total_size(0) + /* _A_STATS_GRP_HIST */ 194a8b06e9dSJakub Kicinski nla_total_size(4) + /* _A_STATS_GRP_HIST_BKT_LOW */ 195a8b06e9dSJakub Kicinski nla_total_size(4)) * /* _A_STATS_GRP_HIST_BKT_HI */ 196a8b06e9dSJakub Kicinski ETHTOOL_RMON_HIST_MAX * 2; 197a8b06e9dSJakub Kicinski } 198f09ea6fbSJakub Kicinski 199f09ea6fbSJakub Kicinski len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */ 200f09ea6fbSJakub Kicinski nla_total_size(4) + /* _A_STATS_GRP_ID */ 201f09ea6fbSJakub Kicinski nla_total_size(4)); /* _A_STATS_GRP_SS_ID */ 202f09ea6fbSJakub Kicinski len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */ 203f09ea6fbSJakub Kicinski nla_total_size_64bit(sizeof(u64))); 204f09ea6fbSJakub Kicinski 205f09ea6fbSJakub Kicinski return len; 206f09ea6fbSJakub Kicinski } 207f09ea6fbSJakub Kicinski 208f09ea6fbSJakub Kicinski static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val) 209f09ea6fbSJakub Kicinski { 210f09ea6fbSJakub Kicinski struct nlattr *nest; 211f09ea6fbSJakub Kicinski int ret; 212f09ea6fbSJakub Kicinski 213f09ea6fbSJakub Kicinski if (val == ETHTOOL_STAT_NOT_SET) 214f09ea6fbSJakub Kicinski return 0; 215f09ea6fbSJakub Kicinski 216f09ea6fbSJakub Kicinski /* We want to start stats attr types from 0, so we don't have a type 217f09ea6fbSJakub Kicinski * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside 218f09ea6fbSJakub Kicinski * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the 219f09ea6fbSJakub Kicinski * actual attr we're 4B off - nla_need_padding_for_64bit() & co. 220f09ea6fbSJakub Kicinski * can't be used. 221f09ea6fbSJakub Kicinski */ 222f09ea6fbSJakub Kicinski #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 223f09ea6fbSJakub Kicinski if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8)) 224f09ea6fbSJakub Kicinski if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0)) 225f09ea6fbSJakub Kicinski return -EMSGSIZE; 226f09ea6fbSJakub Kicinski #endif 227f09ea6fbSJakub Kicinski 228f09ea6fbSJakub Kicinski nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT); 229f09ea6fbSJakub Kicinski if (!nest) 230f09ea6fbSJakub Kicinski return -EMSGSIZE; 231f09ea6fbSJakub Kicinski 232f09ea6fbSJakub Kicinski ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */); 233f09ea6fbSJakub Kicinski if (ret) { 234f09ea6fbSJakub Kicinski nla_nest_cancel(skb, nest); 235f09ea6fbSJakub Kicinski return ret; 236f09ea6fbSJakub Kicinski } 237f09ea6fbSJakub Kicinski 238f09ea6fbSJakub Kicinski nla_nest_end(skb, nest); 239f09ea6fbSJakub Kicinski return 0; 240f09ea6fbSJakub Kicinski } 241f09ea6fbSJakub Kicinski 242f09ea6fbSJakub Kicinski static int stats_put_phy_stats(struct sk_buff *skb, 243f09ea6fbSJakub Kicinski const struct stats_reply_data *data) 244f09ea6fbSJakub Kicinski { 245f09ea6fbSJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, 246f09ea6fbSJakub Kicinski data->phy_stats.SymbolErrorDuringCarrier)) 247f09ea6fbSJakub Kicinski return -EMSGSIZE; 248f09ea6fbSJakub Kicinski return 0; 249f09ea6fbSJakub Kicinski } 250f09ea6fbSJakub Kicinski 251ca224454SJakub Kicinski static int stats_put_mac_stats(struct sk_buff *skb, 252ca224454SJakub Kicinski const struct stats_reply_data *data) 253ca224454SJakub Kicinski { 254ca224454SJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT, 255ca224454SJakub Kicinski data->mac_stats.FramesTransmittedOK) || 256ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL, 257ca224454SJakub Kicinski data->mac_stats.SingleCollisionFrames) || 258ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL, 259ca224454SJakub Kicinski data->mac_stats.MultipleCollisionFrames) || 260ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT, 261ca224454SJakub Kicinski data->mac_stats.FramesReceivedOK) || 262ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR, 263ca224454SJakub Kicinski data->mac_stats.FrameCheckSequenceErrors) || 264ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR, 265ca224454SJakub Kicinski data->mac_stats.AlignmentErrors) || 266ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES, 267ca224454SJakub Kicinski data->mac_stats.OctetsTransmittedOK) || 268ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER, 269ca224454SJakub Kicinski data->mac_stats.FramesWithDeferredXmissions) || 270ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL, 271ca224454SJakub Kicinski data->mac_stats.LateCollisions) || 272ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_11_XS_COL, 273ca224454SJakub Kicinski data->mac_stats.FramesAbortedDueToXSColls) || 274ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR, 275ca224454SJakub Kicinski data->mac_stats.FramesLostDueToIntMACXmitError) || 276ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR, 277ca224454SJakub Kicinski data->mac_stats.CarrierSenseErrors) || 278ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES, 279ca224454SJakub Kicinski data->mac_stats.OctetsReceivedOK) || 280ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR, 281ca224454SJakub Kicinski data->mac_stats.FramesLostDueToIntMACRcvError) || 282ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST, 283ca224454SJakub Kicinski data->mac_stats.MulticastFramesXmittedOK) || 284ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST, 285ca224454SJakub Kicinski data->mac_stats.BroadcastFramesXmittedOK) || 286ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER, 287ca224454SJakub Kicinski data->mac_stats.FramesWithExcessiveDeferral) || 288ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST, 289ca224454SJakub Kicinski data->mac_stats.MulticastFramesReceivedOK) || 290ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST, 291ca224454SJakub Kicinski data->mac_stats.BroadcastFramesReceivedOK) || 292ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR, 293ca224454SJakub Kicinski data->mac_stats.InRangeLengthErrors) || 294ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN, 295ca224454SJakub Kicinski data->mac_stats.OutOfRangeLengthField) || 296ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR, 297ca224454SJakub Kicinski data->mac_stats.FrameTooLongErrors)) 298ca224454SJakub Kicinski return -EMSGSIZE; 299ca224454SJakub Kicinski return 0; 300ca224454SJakub Kicinski } 301ca224454SJakub Kicinski 302bfad2b97SJakub Kicinski static int stats_put_ctrl_stats(struct sk_buff *skb, 303bfad2b97SJakub Kicinski const struct stats_reply_data *data) 304bfad2b97SJakub Kicinski { 305bfad2b97SJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_3_TX, 306bfad2b97SJakub Kicinski data->ctrl_stats.MACControlFramesTransmitted) || 307bfad2b97SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_4_RX, 308bfad2b97SJakub Kicinski data->ctrl_stats.MACControlFramesReceived) || 309bfad2b97SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP, 310bfad2b97SJakub Kicinski data->ctrl_stats.UnsupportedOpcodesReceived)) 311bfad2b97SJakub Kicinski return -EMSGSIZE; 312bfad2b97SJakub Kicinski return 0; 313bfad2b97SJakub Kicinski } 314bfad2b97SJakub Kicinski 315a8b06e9dSJakub Kicinski static int stats_put_rmon_hist(struct sk_buff *skb, u32 attr, const u64 *hist, 316a8b06e9dSJakub Kicinski const struct ethtool_rmon_hist_range *ranges) 317a8b06e9dSJakub Kicinski { 318a8b06e9dSJakub Kicinski struct nlattr *nest; 319a8b06e9dSJakub Kicinski int i; 320a8b06e9dSJakub Kicinski 321a8b06e9dSJakub Kicinski if (!ranges) 322a8b06e9dSJakub Kicinski return 0; 323a8b06e9dSJakub Kicinski 324a8b06e9dSJakub Kicinski for (i = 0; i < ETHTOOL_RMON_HIST_MAX; i++) { 325a8b06e9dSJakub Kicinski if (!ranges[i].low && !ranges[i].high) 326a8b06e9dSJakub Kicinski break; 327a8b06e9dSJakub Kicinski if (hist[i] == ETHTOOL_STAT_NOT_SET) 328a8b06e9dSJakub Kicinski continue; 329a8b06e9dSJakub Kicinski 330a8b06e9dSJakub Kicinski nest = nla_nest_start(skb, attr); 331a8b06e9dSJakub Kicinski if (!nest) 332a8b06e9dSJakub Kicinski return -EMSGSIZE; 333a8b06e9dSJakub Kicinski 334a8b06e9dSJakub Kicinski if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_LOW, 335a8b06e9dSJakub Kicinski ranges[i].low) || 336a8b06e9dSJakub Kicinski nla_put_u32(skb, ETHTOOL_A_STATS_GRP_HIST_BKT_HI, 337a8b06e9dSJakub Kicinski ranges[i].high) || 338a8b06e9dSJakub Kicinski nla_put_u64_64bit(skb, ETHTOOL_A_STATS_GRP_HIST_VAL, 339a8b06e9dSJakub Kicinski hist[i], ETHTOOL_A_STATS_GRP_PAD)) 340a8b06e9dSJakub Kicinski goto err_cancel_hist; 341a8b06e9dSJakub Kicinski 342a8b06e9dSJakub Kicinski nla_nest_end(skb, nest); 343a8b06e9dSJakub Kicinski } 344a8b06e9dSJakub Kicinski 345a8b06e9dSJakub Kicinski return 0; 346a8b06e9dSJakub Kicinski 347a8b06e9dSJakub Kicinski err_cancel_hist: 348a8b06e9dSJakub Kicinski nla_nest_cancel(skb, nest); 349a8b06e9dSJakub Kicinski return -EMSGSIZE; 350a8b06e9dSJakub Kicinski } 351a8b06e9dSJakub Kicinski 352a8b06e9dSJakub Kicinski static int stats_put_rmon_stats(struct sk_buff *skb, 353a8b06e9dSJakub Kicinski const struct stats_reply_data *data) 354a8b06e9dSJakub Kicinski { 355a8b06e9dSJakub Kicinski if (stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_RX, 356a8b06e9dSJakub Kicinski data->rmon_stats.hist, data->rmon_ranges) || 357a8b06e9dSJakub Kicinski stats_put_rmon_hist(skb, ETHTOOL_A_STATS_GRP_HIST_TX, 358a8b06e9dSJakub Kicinski data->rmon_stats.hist_tx, data->rmon_ranges)) 359a8b06e9dSJakub Kicinski return -EMSGSIZE; 360a8b06e9dSJakub Kicinski 361a8b06e9dSJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_RMON_UNDERSIZE, 362a8b06e9dSJakub Kicinski data->rmon_stats.undersize_pkts) || 363a8b06e9dSJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_RMON_OVERSIZE, 364a8b06e9dSJakub Kicinski data->rmon_stats.oversize_pkts) || 365a8b06e9dSJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_RMON_FRAG, 366a8b06e9dSJakub Kicinski data->rmon_stats.fragments) || 367a8b06e9dSJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_RMON_JABBER, 368a8b06e9dSJakub Kicinski data->rmon_stats.jabbers)) 369a8b06e9dSJakub Kicinski return -EMSGSIZE; 370a8b06e9dSJakub Kicinski 371a8b06e9dSJakub Kicinski return 0; 372a8b06e9dSJakub Kicinski } 373a8b06e9dSJakub Kicinski 374f09ea6fbSJakub Kicinski static int stats_put_stats(struct sk_buff *skb, 375f09ea6fbSJakub Kicinski const struct stats_reply_data *data, 376f09ea6fbSJakub Kicinski u32 id, u32 ss_id, 377f09ea6fbSJakub Kicinski int (*cb)(struct sk_buff *skb, 378f09ea6fbSJakub Kicinski const struct stats_reply_data *data)) 379f09ea6fbSJakub Kicinski { 380f09ea6fbSJakub Kicinski struct nlattr *nest; 381f09ea6fbSJakub Kicinski 382f09ea6fbSJakub Kicinski nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP); 383f09ea6fbSJakub Kicinski if (!nest) 384f09ea6fbSJakub Kicinski return -EMSGSIZE; 385f09ea6fbSJakub Kicinski 386f09ea6fbSJakub Kicinski if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) || 387f09ea6fbSJakub Kicinski nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id)) 388f09ea6fbSJakub Kicinski goto err_cancel; 389f09ea6fbSJakub Kicinski 390f09ea6fbSJakub Kicinski if (cb(skb, data)) 391f09ea6fbSJakub Kicinski goto err_cancel; 392f09ea6fbSJakub Kicinski 393f09ea6fbSJakub Kicinski nla_nest_end(skb, nest); 394f09ea6fbSJakub Kicinski return 0; 395f09ea6fbSJakub Kicinski 396f09ea6fbSJakub Kicinski err_cancel: 397f09ea6fbSJakub Kicinski nla_nest_cancel(skb, nest); 398f09ea6fbSJakub Kicinski return -EMSGSIZE; 399f09ea6fbSJakub Kicinski } 400f09ea6fbSJakub Kicinski 401f09ea6fbSJakub Kicinski static int stats_fill_reply(struct sk_buff *skb, 402f09ea6fbSJakub Kicinski const struct ethnl_req_info *req_base, 403f09ea6fbSJakub Kicinski const struct ethnl_reply_data *reply_base) 404f09ea6fbSJakub Kicinski { 405f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 406f09ea6fbSJakub Kicinski const struct stats_reply_data *data = STATS_REPDATA(reply_base); 407f09ea6fbSJakub Kicinski int ret = 0; 408f09ea6fbSJakub Kicinski 40904692c90SVladimir Oltean if (nla_put_u32(skb, ETHTOOL_A_STATS_SRC, req_info->src)) 41004692c90SVladimir Oltean return -EMSGSIZE; 41104692c90SVladimir Oltean 412f09ea6fbSJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) 413f09ea6fbSJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY, 414f09ea6fbSJakub Kicinski ETH_SS_STATS_ETH_PHY, 415f09ea6fbSJakub Kicinski stats_put_phy_stats); 416ca224454SJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) 417ca224454SJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_MAC, 418ca224454SJakub Kicinski ETH_SS_STATS_ETH_MAC, 419ca224454SJakub Kicinski stats_put_mac_stats); 420bfad2b97SJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) 421bfad2b97SJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL, 422bfad2b97SJakub Kicinski ETH_SS_STATS_ETH_CTRL, 423bfad2b97SJakub Kicinski stats_put_ctrl_stats); 424a8b06e9dSJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_RMON, req_info->stat_mask)) 425a8b06e9dSJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_RMON, 426a8b06e9dSJakub Kicinski ETH_SS_STATS_RMON, stats_put_rmon_stats); 427f09ea6fbSJakub Kicinski 428f09ea6fbSJakub Kicinski return ret; 429f09ea6fbSJakub Kicinski } 430f09ea6fbSJakub Kicinski 431f09ea6fbSJakub Kicinski const struct ethnl_request_ops ethnl_stats_request_ops = { 432f09ea6fbSJakub Kicinski .request_cmd = ETHTOOL_MSG_STATS_GET, 433f09ea6fbSJakub Kicinski .reply_cmd = ETHTOOL_MSG_STATS_GET_REPLY, 434f09ea6fbSJakub Kicinski .hdr_attr = ETHTOOL_A_STATS_HEADER, 435f09ea6fbSJakub Kicinski .req_info_size = sizeof(struct stats_req_info), 436f09ea6fbSJakub Kicinski .reply_data_size = sizeof(struct stats_reply_data), 437f09ea6fbSJakub Kicinski 438f09ea6fbSJakub Kicinski .parse_request = stats_parse_request, 439f09ea6fbSJakub Kicinski .prepare_data = stats_prepare_data, 440f09ea6fbSJakub Kicinski .reply_size = stats_reply_size, 441f09ea6fbSJakub Kicinski .fill_reply = stats_fill_reply, 442f09ea6fbSJakub Kicinski }; 443*449c5459SVladimir Oltean 444*449c5459SVladimir Oltean static u64 ethtool_stats_sum(u64 a, u64 b) 445*449c5459SVladimir Oltean { 446*449c5459SVladimir Oltean if (a == ETHTOOL_STAT_NOT_SET) 447*449c5459SVladimir Oltean return b; 448*449c5459SVladimir Oltean if (b == ETHTOOL_STAT_NOT_SET) 449*449c5459SVladimir Oltean return a; 450*449c5459SVladimir Oltean return a + b; 451*449c5459SVladimir Oltean } 452*449c5459SVladimir Oltean 453*449c5459SVladimir Oltean /* Avoid modifying the aggregation procedure every time a new counter is added 454*449c5459SVladimir Oltean * by treating the structures as an array of u64 statistics. 455*449c5459SVladimir Oltean */ 456*449c5459SVladimir Oltean static void ethtool_aggregate_stats(void *aggr_stats, const void *emac_stats, 457*449c5459SVladimir Oltean const void *pmac_stats, size_t stats_size, 458*449c5459SVladimir Oltean size_t stats_offset) 459*449c5459SVladimir Oltean { 460*449c5459SVladimir Oltean size_t num_stats = stats_size / sizeof(u64); 461*449c5459SVladimir Oltean const u64 *s1 = emac_stats + stats_offset; 462*449c5459SVladimir Oltean const u64 *s2 = pmac_stats + stats_offset; 463*449c5459SVladimir Oltean u64 *s = aggr_stats + stats_offset; 464*449c5459SVladimir Oltean int i; 465*449c5459SVladimir Oltean 466*449c5459SVladimir Oltean for (i = 0; i < num_stats; i++) 467*449c5459SVladimir Oltean s[i] = ethtool_stats_sum(s1[i], s2[i]); 468*449c5459SVladimir Oltean } 469*449c5459SVladimir Oltean 470*449c5459SVladimir Oltean void ethtool_aggregate_mac_stats(struct net_device *dev, 471*449c5459SVladimir Oltean struct ethtool_eth_mac_stats *mac_stats) 472*449c5459SVladimir Oltean { 473*449c5459SVladimir Oltean const struct ethtool_ops *ops = dev->ethtool_ops; 474*449c5459SVladimir Oltean struct ethtool_eth_mac_stats pmac, emac; 475*449c5459SVladimir Oltean 476*449c5459SVladimir Oltean memset(&emac, 0xff, sizeof(emac)); 477*449c5459SVladimir Oltean memset(&pmac, 0xff, sizeof(pmac)); 478*449c5459SVladimir Oltean emac.src = ETHTOOL_MAC_STATS_SRC_EMAC; 479*449c5459SVladimir Oltean pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC; 480*449c5459SVladimir Oltean 481*449c5459SVladimir Oltean ops->get_eth_mac_stats(dev, &emac); 482*449c5459SVladimir Oltean ops->get_eth_mac_stats(dev, &pmac); 483*449c5459SVladimir Oltean 484*449c5459SVladimir Oltean ethtool_aggregate_stats(mac_stats, &emac, &pmac, 485*449c5459SVladimir Oltean sizeof(mac_stats->stats), 486*449c5459SVladimir Oltean offsetof(struct ethtool_eth_mac_stats, stats)); 487*449c5459SVladimir Oltean } 488*449c5459SVladimir Oltean EXPORT_SYMBOL(ethtool_aggregate_mac_stats); 489*449c5459SVladimir Oltean 490*449c5459SVladimir Oltean void ethtool_aggregate_phy_stats(struct net_device *dev, 491*449c5459SVladimir Oltean struct ethtool_eth_phy_stats *phy_stats) 492*449c5459SVladimir Oltean { 493*449c5459SVladimir Oltean const struct ethtool_ops *ops = dev->ethtool_ops; 494*449c5459SVladimir Oltean struct ethtool_eth_phy_stats pmac, emac; 495*449c5459SVladimir Oltean 496*449c5459SVladimir Oltean memset(&emac, 0xff, sizeof(emac)); 497*449c5459SVladimir Oltean memset(&pmac, 0xff, sizeof(pmac)); 498*449c5459SVladimir Oltean emac.src = ETHTOOL_MAC_STATS_SRC_EMAC; 499*449c5459SVladimir Oltean pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC; 500*449c5459SVladimir Oltean 501*449c5459SVladimir Oltean ops->get_eth_phy_stats(dev, &emac); 502*449c5459SVladimir Oltean ops->get_eth_phy_stats(dev, &pmac); 503*449c5459SVladimir Oltean 504*449c5459SVladimir Oltean ethtool_aggregate_stats(phy_stats, &emac, &pmac, 505*449c5459SVladimir Oltean sizeof(phy_stats->stats), 506*449c5459SVladimir Oltean offsetof(struct ethtool_eth_phy_stats, stats)); 507*449c5459SVladimir Oltean } 508*449c5459SVladimir Oltean EXPORT_SYMBOL(ethtool_aggregate_phy_stats); 509*449c5459SVladimir Oltean 510*449c5459SVladimir Oltean void ethtool_aggregate_ctrl_stats(struct net_device *dev, 511*449c5459SVladimir Oltean struct ethtool_eth_ctrl_stats *ctrl_stats) 512*449c5459SVladimir Oltean { 513*449c5459SVladimir Oltean const struct ethtool_ops *ops = dev->ethtool_ops; 514*449c5459SVladimir Oltean struct ethtool_eth_ctrl_stats pmac, emac; 515*449c5459SVladimir Oltean 516*449c5459SVladimir Oltean memset(&emac, 0xff, sizeof(emac)); 517*449c5459SVladimir Oltean memset(&pmac, 0xff, sizeof(pmac)); 518*449c5459SVladimir Oltean emac.src = ETHTOOL_MAC_STATS_SRC_EMAC; 519*449c5459SVladimir Oltean pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC; 520*449c5459SVladimir Oltean 521*449c5459SVladimir Oltean ops->get_eth_ctrl_stats(dev, &emac); 522*449c5459SVladimir Oltean ops->get_eth_ctrl_stats(dev, &pmac); 523*449c5459SVladimir Oltean 524*449c5459SVladimir Oltean ethtool_aggregate_stats(ctrl_stats, &emac, &pmac, 525*449c5459SVladimir Oltean sizeof(ctrl_stats->stats), 526*449c5459SVladimir Oltean offsetof(struct ethtool_eth_ctrl_stats, stats)); 527*449c5459SVladimir Oltean } 528*449c5459SVladimir Oltean EXPORT_SYMBOL(ethtool_aggregate_ctrl_stats); 529*449c5459SVladimir Oltean 530*449c5459SVladimir Oltean void ethtool_aggregate_pause_stats(struct net_device *dev, 531*449c5459SVladimir Oltean struct ethtool_pause_stats *pause_stats) 532*449c5459SVladimir Oltean { 533*449c5459SVladimir Oltean const struct ethtool_ops *ops = dev->ethtool_ops; 534*449c5459SVladimir Oltean struct ethtool_pause_stats pmac, emac; 535*449c5459SVladimir Oltean 536*449c5459SVladimir Oltean memset(&emac, 0xff, sizeof(emac)); 537*449c5459SVladimir Oltean memset(&pmac, 0xff, sizeof(pmac)); 538*449c5459SVladimir Oltean emac.src = ETHTOOL_MAC_STATS_SRC_EMAC; 539*449c5459SVladimir Oltean pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC; 540*449c5459SVladimir Oltean 541*449c5459SVladimir Oltean ops->get_pause_stats(dev, &emac); 542*449c5459SVladimir Oltean ops->get_pause_stats(dev, &pmac); 543*449c5459SVladimir Oltean 544*449c5459SVladimir Oltean ethtool_aggregate_stats(pause_stats, &emac, &pmac, 545*449c5459SVladimir Oltean sizeof(pause_stats->stats), 546*449c5459SVladimir Oltean offsetof(struct ethtool_pause_stats, stats)); 547*449c5459SVladimir Oltean } 548*449c5459SVladimir Oltean EXPORT_SYMBOL(ethtool_aggregate_pause_stats); 549*449c5459SVladimir Oltean 550*449c5459SVladimir Oltean void ethtool_aggregate_rmon_stats(struct net_device *dev, 551*449c5459SVladimir Oltean struct ethtool_rmon_stats *rmon_stats) 552*449c5459SVladimir Oltean { 553*449c5459SVladimir Oltean const struct ethtool_ops *ops = dev->ethtool_ops; 554*449c5459SVladimir Oltean const struct ethtool_rmon_hist_range *dummy; 555*449c5459SVladimir Oltean struct ethtool_rmon_stats pmac, emac; 556*449c5459SVladimir Oltean 557*449c5459SVladimir Oltean memset(&emac, 0xff, sizeof(emac)); 558*449c5459SVladimir Oltean memset(&pmac, 0xff, sizeof(pmac)); 559*449c5459SVladimir Oltean emac.src = ETHTOOL_MAC_STATS_SRC_EMAC; 560*449c5459SVladimir Oltean pmac.src = ETHTOOL_MAC_STATS_SRC_PMAC; 561*449c5459SVladimir Oltean 562*449c5459SVladimir Oltean ops->get_rmon_stats(dev, &emac, &dummy); 563*449c5459SVladimir Oltean ops->get_rmon_stats(dev, &pmac, &dummy); 564*449c5459SVladimir Oltean 565*449c5459SVladimir Oltean ethtool_aggregate_stats(rmon_stats, &emac, &pmac, 566*449c5459SVladimir Oltean sizeof(rmon_stats->stats), 567*449c5459SVladimir Oltean offsetof(struct ethtool_rmon_stats, stats)); 568*449c5459SVladimir Oltean } 569*449c5459SVladimir Oltean EXPORT_SYMBOL(ethtool_aggregate_rmon_stats); 570