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; 19*bfad2b97SJakub Kicinski struct ethtool_eth_ctrl_stats ctrl_stats; 20f09ea6fbSJakub Kicinski }; 21f09ea6fbSJakub Kicinski 22f09ea6fbSJakub Kicinski #define STATS_REPDATA(__reply_base) \ 23f09ea6fbSJakub Kicinski container_of(__reply_base, struct stats_reply_data, base) 24f09ea6fbSJakub Kicinski 25f09ea6fbSJakub Kicinski const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN] = { 26f09ea6fbSJakub Kicinski [ETHTOOL_STATS_ETH_PHY] = "eth-phy", 27ca224454SJakub Kicinski [ETHTOOL_STATS_ETH_MAC] = "eth-mac", 28*bfad2b97SJakub Kicinski [ETHTOOL_STATS_ETH_CTRL] = "eth-ctrl", 29f09ea6fbSJakub Kicinski }; 30f09ea6fbSJakub Kicinski 31f09ea6fbSJakub Kicinski const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN] = { 32f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR] = "SymbolErrorDuringCarrier", 33f09ea6fbSJakub Kicinski }; 34f09ea6fbSJakub Kicinski 35ca224454SJakub Kicinski const char stats_eth_mac_names[__ETHTOOL_A_STATS_ETH_MAC_CNT][ETH_GSTRING_LEN] = { 36ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT] = "FramesTransmittedOK", 37ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL] = "SingleCollisionFrames", 38ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL] = "MultipleCollisionFrames", 39ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT] = "FramesReceivedOK", 40ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR] = "FrameCheckSequenceErrors", 41ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR] = "AlignmentErrors", 42ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES] = "OctetsTransmittedOK", 43ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER] = "FramesWithDeferredXmissions", 44ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL] = "LateCollisions", 45ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_11_XS_COL] = "FramesAbortedDueToXSColls", 46ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR] = "FramesLostDueToIntMACXmitError", 47ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR] = "CarrierSenseErrors", 48ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES] = "OctetsReceivedOK", 49ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR] = "FramesLostDueToIntMACRcvError", 50ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST] = "MulticastFramesXmittedOK", 51ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST] = "BroadcastFramesXmittedOK", 52ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER] = "FramesWithExcessiveDeferral", 53ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST] = "MulticastFramesReceivedOK", 54ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST] = "BroadcastFramesReceivedOK", 55ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR] = "InRangeLengthErrors", 56ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN] = "OutOfRangeLengthField", 57ca224454SJakub Kicinski [ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR] = "FrameTooLongErrors", 58ca224454SJakub Kicinski }; 59ca224454SJakub Kicinski 60*bfad2b97SJakub Kicinski const char stats_eth_ctrl_names[__ETHTOOL_A_STATS_ETH_CTRL_CNT][ETH_GSTRING_LEN] = { 61*bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_3_TX] = "MACControlFramesTransmitted", 62*bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_4_RX] = "MACControlFramesReceived", 63*bfad2b97SJakub Kicinski [ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP] = "UnsupportedOpcodesReceived", 64*bfad2b97SJakub Kicinski }; 65*bfad2b97SJakub Kicinski 66f09ea6fbSJakub Kicinski const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1] = { 67f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_HEADER] = 68f09ea6fbSJakub Kicinski NLA_POLICY_NESTED(ethnl_header_policy), 69f09ea6fbSJakub Kicinski [ETHTOOL_A_STATS_GROUPS] = { .type = NLA_NESTED }, 70f09ea6fbSJakub Kicinski }; 71f09ea6fbSJakub Kicinski 72f09ea6fbSJakub Kicinski static int stats_parse_request(struct ethnl_req_info *req_base, 73f09ea6fbSJakub Kicinski struct nlattr **tb, 74f09ea6fbSJakub Kicinski struct netlink_ext_ack *extack) 75f09ea6fbSJakub Kicinski { 76f09ea6fbSJakub Kicinski struct stats_req_info *req_info = STATS_REQINFO(req_base); 77f09ea6fbSJakub Kicinski bool mod = false; 78f09ea6fbSJakub Kicinski int err; 79f09ea6fbSJakub Kicinski 80f09ea6fbSJakub Kicinski err = ethnl_update_bitset(req_info->stat_mask, __ETHTOOL_STATS_CNT, 81f09ea6fbSJakub Kicinski tb[ETHTOOL_A_STATS_GROUPS], stats_std_names, 82f09ea6fbSJakub Kicinski extack, &mod); 83f09ea6fbSJakub Kicinski if (err) 84f09ea6fbSJakub Kicinski return err; 85f09ea6fbSJakub Kicinski 86f09ea6fbSJakub Kicinski if (!mod) { 87f09ea6fbSJakub Kicinski NL_SET_ERR_MSG(extack, "no stats requested"); 88f09ea6fbSJakub Kicinski return -EINVAL; 89f09ea6fbSJakub Kicinski } 90f09ea6fbSJakub Kicinski 91f09ea6fbSJakub Kicinski return 0; 92f09ea6fbSJakub Kicinski } 93f09ea6fbSJakub Kicinski 94f09ea6fbSJakub Kicinski static int stats_prepare_data(const struct ethnl_req_info *req_base, 95f09ea6fbSJakub Kicinski struct ethnl_reply_data *reply_base, 96f09ea6fbSJakub Kicinski struct genl_info *info) 97f09ea6fbSJakub Kicinski { 98f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 99f09ea6fbSJakub Kicinski struct stats_reply_data *data = STATS_REPDATA(reply_base); 100f09ea6fbSJakub Kicinski struct net_device *dev = reply_base->dev; 101f09ea6fbSJakub Kicinski int ret; 102f09ea6fbSJakub Kicinski 103f09ea6fbSJakub Kicinski ret = ethnl_ops_begin(dev); 104f09ea6fbSJakub Kicinski if (ret < 0) 105f09ea6fbSJakub Kicinski return ret; 106f09ea6fbSJakub Kicinski 107f09ea6fbSJakub Kicinski memset(&data->phy_stats, 0xff, sizeof(data->phy_stats)); 108ca224454SJakub Kicinski memset(&data->mac_stats, 0xff, sizeof(data->mac_stats)); 109*bfad2b97SJakub Kicinski memset(&data->ctrl_stats, 0xff, sizeof(data->mac_stats)); 110f09ea6fbSJakub Kicinski 111f09ea6fbSJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask) && 112f09ea6fbSJakub Kicinski dev->ethtool_ops->get_eth_phy_stats) 113f09ea6fbSJakub Kicinski dev->ethtool_ops->get_eth_phy_stats(dev, &data->phy_stats); 114ca224454SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask) && 115ca224454SJakub Kicinski dev->ethtool_ops->get_eth_mac_stats) 116ca224454SJakub Kicinski dev->ethtool_ops->get_eth_mac_stats(dev, &data->mac_stats); 117*bfad2b97SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask) && 118*bfad2b97SJakub Kicinski dev->ethtool_ops->get_eth_ctrl_stats) 119*bfad2b97SJakub Kicinski dev->ethtool_ops->get_eth_ctrl_stats(dev, &data->ctrl_stats); 120f09ea6fbSJakub Kicinski 121f09ea6fbSJakub Kicinski ethnl_ops_complete(dev); 122f09ea6fbSJakub Kicinski return 0; 123f09ea6fbSJakub Kicinski } 124f09ea6fbSJakub Kicinski 125f09ea6fbSJakub Kicinski static int stats_reply_size(const struct ethnl_req_info *req_base, 126f09ea6fbSJakub Kicinski const struct ethnl_reply_data *reply_base) 127f09ea6fbSJakub Kicinski { 128f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 129f09ea6fbSJakub Kicinski unsigned int n_grps = 0, n_stats = 0; 130f09ea6fbSJakub Kicinski int len = 0; 131f09ea6fbSJakub Kicinski 132f09ea6fbSJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) { 133f09ea6fbSJakub Kicinski n_stats += sizeof(struct ethtool_eth_phy_stats) / sizeof(u64); 134f09ea6fbSJakub Kicinski n_grps++; 135f09ea6fbSJakub Kicinski } 136ca224454SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) { 137ca224454SJakub Kicinski n_stats += sizeof(struct ethtool_eth_mac_stats) / sizeof(u64); 138ca224454SJakub Kicinski n_grps++; 139ca224454SJakub Kicinski } 140*bfad2b97SJakub Kicinski if (test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) { 141*bfad2b97SJakub Kicinski n_stats += sizeof(struct ethtool_eth_ctrl_stats) / sizeof(u64); 142*bfad2b97SJakub Kicinski n_grps++; 143*bfad2b97SJakub Kicinski } 144f09ea6fbSJakub Kicinski 145f09ea6fbSJakub Kicinski len += n_grps * (nla_total_size(0) + /* _A_STATS_GRP */ 146f09ea6fbSJakub Kicinski nla_total_size(4) + /* _A_STATS_GRP_ID */ 147f09ea6fbSJakub Kicinski nla_total_size(4)); /* _A_STATS_GRP_SS_ID */ 148f09ea6fbSJakub Kicinski len += n_stats * (nla_total_size(0) + /* _A_STATS_GRP_STAT */ 149f09ea6fbSJakub Kicinski nla_total_size_64bit(sizeof(u64))); 150f09ea6fbSJakub Kicinski 151f09ea6fbSJakub Kicinski return len; 152f09ea6fbSJakub Kicinski } 153f09ea6fbSJakub Kicinski 154f09ea6fbSJakub Kicinski static int stat_put(struct sk_buff *skb, u16 attrtype, u64 val) 155f09ea6fbSJakub Kicinski { 156f09ea6fbSJakub Kicinski struct nlattr *nest; 157f09ea6fbSJakub Kicinski int ret; 158f09ea6fbSJakub Kicinski 159f09ea6fbSJakub Kicinski if (val == ETHTOOL_STAT_NOT_SET) 160f09ea6fbSJakub Kicinski return 0; 161f09ea6fbSJakub Kicinski 162f09ea6fbSJakub Kicinski /* We want to start stats attr types from 0, so we don't have a type 163f09ea6fbSJakub Kicinski * for pad inside ETHTOOL_A_STATS_GRP_STAT. Pad things on the outside 164f09ea6fbSJakub Kicinski * of ETHTOOL_A_STATS_GRP_STAT. Since we're one nest away from the 165f09ea6fbSJakub Kicinski * actual attr we're 4B off - nla_need_padding_for_64bit() & co. 166f09ea6fbSJakub Kicinski * can't be used. 167f09ea6fbSJakub Kicinski */ 168f09ea6fbSJakub Kicinski #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 169f09ea6fbSJakub Kicinski if (!IS_ALIGNED((unsigned long)skb_tail_pointer(skb), 8)) 170f09ea6fbSJakub Kicinski if (!nla_reserve(skb, ETHTOOL_A_STATS_GRP_PAD, 0)) 171f09ea6fbSJakub Kicinski return -EMSGSIZE; 172f09ea6fbSJakub Kicinski #endif 173f09ea6fbSJakub Kicinski 174f09ea6fbSJakub Kicinski nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP_STAT); 175f09ea6fbSJakub Kicinski if (!nest) 176f09ea6fbSJakub Kicinski return -EMSGSIZE; 177f09ea6fbSJakub Kicinski 178f09ea6fbSJakub Kicinski ret = nla_put_u64_64bit(skb, attrtype, val, -1 /* not used */); 179f09ea6fbSJakub Kicinski if (ret) { 180f09ea6fbSJakub Kicinski nla_nest_cancel(skb, nest); 181f09ea6fbSJakub Kicinski return ret; 182f09ea6fbSJakub Kicinski } 183f09ea6fbSJakub Kicinski 184f09ea6fbSJakub Kicinski nla_nest_end(skb, nest); 185f09ea6fbSJakub Kicinski return 0; 186f09ea6fbSJakub Kicinski } 187f09ea6fbSJakub Kicinski 188f09ea6fbSJakub Kicinski static int stats_put_phy_stats(struct sk_buff *skb, 189f09ea6fbSJakub Kicinski const struct stats_reply_data *data) 190f09ea6fbSJakub Kicinski { 191f09ea6fbSJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_PHY_5_SYM_ERR, 192f09ea6fbSJakub Kicinski data->phy_stats.SymbolErrorDuringCarrier)) 193f09ea6fbSJakub Kicinski return -EMSGSIZE; 194f09ea6fbSJakub Kicinski return 0; 195f09ea6fbSJakub Kicinski } 196f09ea6fbSJakub Kicinski 197ca224454SJakub Kicinski static int stats_put_mac_stats(struct sk_buff *skb, 198ca224454SJakub Kicinski const struct stats_reply_data *data) 199ca224454SJakub Kicinski { 200ca224454SJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_2_TX_PKT, 201ca224454SJakub Kicinski data->mac_stats.FramesTransmittedOK) || 202ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_3_SINGLE_COL, 203ca224454SJakub Kicinski data->mac_stats.SingleCollisionFrames) || 204ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_4_MULTI_COL, 205ca224454SJakub Kicinski data->mac_stats.MultipleCollisionFrames) || 206ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_5_RX_PKT, 207ca224454SJakub Kicinski data->mac_stats.FramesReceivedOK) || 208ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_6_FCS_ERR, 209ca224454SJakub Kicinski data->mac_stats.FrameCheckSequenceErrors) || 210ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_7_ALIGN_ERR, 211ca224454SJakub Kicinski data->mac_stats.AlignmentErrors) || 212ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_8_TX_BYTES, 213ca224454SJakub Kicinski data->mac_stats.OctetsTransmittedOK) || 214ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_9_TX_DEFER, 215ca224454SJakub Kicinski data->mac_stats.FramesWithDeferredXmissions) || 216ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_10_LATE_COL, 217ca224454SJakub Kicinski data->mac_stats.LateCollisions) || 218ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_11_XS_COL, 219ca224454SJakub Kicinski data->mac_stats.FramesAbortedDueToXSColls) || 220ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_12_TX_INT_ERR, 221ca224454SJakub Kicinski data->mac_stats.FramesLostDueToIntMACXmitError) || 222ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_13_CS_ERR, 223ca224454SJakub Kicinski data->mac_stats.CarrierSenseErrors) || 224ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_14_RX_BYTES, 225ca224454SJakub Kicinski data->mac_stats.OctetsReceivedOK) || 226ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_15_RX_INT_ERR, 227ca224454SJakub Kicinski data->mac_stats.FramesLostDueToIntMACRcvError) || 228ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_18_TX_MCAST, 229ca224454SJakub Kicinski data->mac_stats.MulticastFramesXmittedOK) || 230ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_19_TX_BCAST, 231ca224454SJakub Kicinski data->mac_stats.BroadcastFramesXmittedOK) || 232ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_20_XS_DEFER, 233ca224454SJakub Kicinski data->mac_stats.FramesWithExcessiveDeferral) || 234ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_21_RX_MCAST, 235ca224454SJakub Kicinski data->mac_stats.MulticastFramesReceivedOK) || 236ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_22_RX_BCAST, 237ca224454SJakub Kicinski data->mac_stats.BroadcastFramesReceivedOK) || 238ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_23_IR_LEN_ERR, 239ca224454SJakub Kicinski data->mac_stats.InRangeLengthErrors) || 240ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_24_OOR_LEN, 241ca224454SJakub Kicinski data->mac_stats.OutOfRangeLengthField) || 242ca224454SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_MAC_25_TOO_LONG_ERR, 243ca224454SJakub Kicinski data->mac_stats.FrameTooLongErrors)) 244ca224454SJakub Kicinski return -EMSGSIZE; 245ca224454SJakub Kicinski return 0; 246ca224454SJakub Kicinski } 247ca224454SJakub Kicinski 248*bfad2b97SJakub Kicinski static int stats_put_ctrl_stats(struct sk_buff *skb, 249*bfad2b97SJakub Kicinski const struct stats_reply_data *data) 250*bfad2b97SJakub Kicinski { 251*bfad2b97SJakub Kicinski if (stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_3_TX, 252*bfad2b97SJakub Kicinski data->ctrl_stats.MACControlFramesTransmitted) || 253*bfad2b97SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_4_RX, 254*bfad2b97SJakub Kicinski data->ctrl_stats.MACControlFramesReceived) || 255*bfad2b97SJakub Kicinski stat_put(skb, ETHTOOL_A_STATS_ETH_CTRL_5_RX_UNSUP, 256*bfad2b97SJakub Kicinski data->ctrl_stats.UnsupportedOpcodesReceived)) 257*bfad2b97SJakub Kicinski return -EMSGSIZE; 258*bfad2b97SJakub Kicinski return 0; 259*bfad2b97SJakub Kicinski } 260*bfad2b97SJakub Kicinski 261f09ea6fbSJakub Kicinski static int stats_put_stats(struct sk_buff *skb, 262f09ea6fbSJakub Kicinski const struct stats_reply_data *data, 263f09ea6fbSJakub Kicinski u32 id, u32 ss_id, 264f09ea6fbSJakub Kicinski int (*cb)(struct sk_buff *skb, 265f09ea6fbSJakub Kicinski const struct stats_reply_data *data)) 266f09ea6fbSJakub Kicinski { 267f09ea6fbSJakub Kicinski struct nlattr *nest; 268f09ea6fbSJakub Kicinski 269f09ea6fbSJakub Kicinski nest = nla_nest_start(skb, ETHTOOL_A_STATS_GRP); 270f09ea6fbSJakub Kicinski if (!nest) 271f09ea6fbSJakub Kicinski return -EMSGSIZE; 272f09ea6fbSJakub Kicinski 273f09ea6fbSJakub Kicinski if (nla_put_u32(skb, ETHTOOL_A_STATS_GRP_ID, id) || 274f09ea6fbSJakub Kicinski nla_put_u32(skb, ETHTOOL_A_STATS_GRP_SS_ID, ss_id)) 275f09ea6fbSJakub Kicinski goto err_cancel; 276f09ea6fbSJakub Kicinski 277f09ea6fbSJakub Kicinski if (cb(skb, data)) 278f09ea6fbSJakub Kicinski goto err_cancel; 279f09ea6fbSJakub Kicinski 280f09ea6fbSJakub Kicinski nla_nest_end(skb, nest); 281f09ea6fbSJakub Kicinski return 0; 282f09ea6fbSJakub Kicinski 283f09ea6fbSJakub Kicinski err_cancel: 284f09ea6fbSJakub Kicinski nla_nest_cancel(skb, nest); 285f09ea6fbSJakub Kicinski return -EMSGSIZE; 286f09ea6fbSJakub Kicinski } 287f09ea6fbSJakub Kicinski 288f09ea6fbSJakub Kicinski static int stats_fill_reply(struct sk_buff *skb, 289f09ea6fbSJakub Kicinski const struct ethnl_req_info *req_base, 290f09ea6fbSJakub Kicinski const struct ethnl_reply_data *reply_base) 291f09ea6fbSJakub Kicinski { 292f09ea6fbSJakub Kicinski const struct stats_req_info *req_info = STATS_REQINFO(req_base); 293f09ea6fbSJakub Kicinski const struct stats_reply_data *data = STATS_REPDATA(reply_base); 294f09ea6fbSJakub Kicinski int ret = 0; 295f09ea6fbSJakub Kicinski 296f09ea6fbSJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_PHY, req_info->stat_mask)) 297f09ea6fbSJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_PHY, 298f09ea6fbSJakub Kicinski ETH_SS_STATS_ETH_PHY, 299f09ea6fbSJakub Kicinski stats_put_phy_stats); 300ca224454SJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_MAC, req_info->stat_mask)) 301ca224454SJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_MAC, 302ca224454SJakub Kicinski ETH_SS_STATS_ETH_MAC, 303ca224454SJakub Kicinski stats_put_mac_stats); 304*bfad2b97SJakub Kicinski if (!ret && test_bit(ETHTOOL_STATS_ETH_CTRL, req_info->stat_mask)) 305*bfad2b97SJakub Kicinski ret = stats_put_stats(skb, data, ETHTOOL_STATS_ETH_CTRL, 306*bfad2b97SJakub Kicinski ETH_SS_STATS_ETH_CTRL, 307*bfad2b97SJakub Kicinski stats_put_ctrl_stats); 308f09ea6fbSJakub Kicinski 309f09ea6fbSJakub Kicinski return ret; 310f09ea6fbSJakub Kicinski } 311f09ea6fbSJakub Kicinski 312f09ea6fbSJakub Kicinski const struct ethnl_request_ops ethnl_stats_request_ops = { 313f09ea6fbSJakub Kicinski .request_cmd = ETHTOOL_MSG_STATS_GET, 314f09ea6fbSJakub Kicinski .reply_cmd = ETHTOOL_MSG_STATS_GET_REPLY, 315f09ea6fbSJakub Kicinski .hdr_attr = ETHTOOL_A_STATS_HEADER, 316f09ea6fbSJakub Kicinski .req_info_size = sizeof(struct stats_req_info), 317f09ea6fbSJakub Kicinski .reply_data_size = sizeof(struct stats_reply_data), 318f09ea6fbSJakub Kicinski 319f09ea6fbSJakub Kicinski .parse_request = stats_parse_request, 320f09ea6fbSJakub Kicinski .prepare_data = stats_prepare_data, 321f09ea6fbSJakub Kicinski .reply_size = stats_reply_size, 322f09ea6fbSJakub Kicinski .fill_reply = stats_fill_reply, 323f09ea6fbSJakub Kicinski }; 324