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