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