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