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