1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include <linux/ethtool.h> 4 #include <linux/phy.h> 5 6 #include "common.h" 7 #include "netlink.h" 8 9 struct strset_info { 10 bool per_dev; 11 bool free_strings; 12 unsigned int count; 13 const char (*strings)[ETH_GSTRING_LEN]; 14 }; 15 16 static const struct strset_info info_template[] = { 17 [ETH_SS_TEST] = { 18 .per_dev = true, 19 }, 20 [ETH_SS_STATS] = { 21 .per_dev = true, 22 }, 23 [ETH_SS_PRIV_FLAGS] = { 24 .per_dev = true, 25 }, 26 [ETH_SS_FEATURES] = { 27 .per_dev = false, 28 .count = ARRAY_SIZE(netdev_features_strings), 29 .strings = netdev_features_strings, 30 }, 31 [ETH_SS_RSS_HASH_FUNCS] = { 32 .per_dev = false, 33 .count = ARRAY_SIZE(rss_hash_func_strings), 34 .strings = rss_hash_func_strings, 35 }, 36 [ETH_SS_TUNABLES] = { 37 .per_dev = false, 38 .count = ARRAY_SIZE(tunable_strings), 39 .strings = tunable_strings, 40 }, 41 [ETH_SS_PHY_STATS] = { 42 .per_dev = true, 43 }, 44 [ETH_SS_PHY_TUNABLES] = { 45 .per_dev = false, 46 .count = ARRAY_SIZE(phy_tunable_strings), 47 .strings = phy_tunable_strings, 48 }, 49 [ETH_SS_LINK_MODES] = { 50 .per_dev = false, 51 .count = __ETHTOOL_LINK_MODE_MASK_NBITS, 52 .strings = link_mode_names, 53 }, 54 [ETH_SS_MSG_CLASSES] = { 55 .per_dev = false, 56 .count = NETIF_MSG_CLASS_COUNT, 57 .strings = netif_msg_class_names, 58 }, 59 [ETH_SS_WOL_MODES] = { 60 .per_dev = false, 61 .count = WOL_MODE_COUNT, 62 .strings = wol_mode_names, 63 }, 64 [ETH_SS_SOF_TIMESTAMPING] = { 65 .per_dev = false, 66 .count = __SOF_TIMESTAMPING_CNT, 67 .strings = sof_timestamping_names, 68 }, 69 [ETH_SS_TS_TX_TYPES] = { 70 .per_dev = false, 71 .count = __HWTSTAMP_TX_CNT, 72 .strings = ts_tx_type_names, 73 }, 74 [ETH_SS_TS_RX_FILTERS] = { 75 .per_dev = false, 76 .count = __HWTSTAMP_FILTER_CNT, 77 .strings = ts_rx_filter_names, 78 }, 79 [ETH_SS_TS_FLAGS] = { 80 .per_dev = false, 81 .count = __HWTSTAMP_FLAG_CNT, 82 .strings = ts_flags_names, 83 }, 84 [ETH_SS_UDP_TUNNEL_TYPES] = { 85 .per_dev = false, 86 .count = __ETHTOOL_UDP_TUNNEL_TYPE_CNT, 87 .strings = udp_tunnel_type_names, 88 }, 89 [ETH_SS_STATS_STD] = { 90 .per_dev = false, 91 .count = __ETHTOOL_STATS_CNT, 92 .strings = stats_std_names, 93 }, 94 [ETH_SS_STATS_ETH_PHY] = { 95 .per_dev = false, 96 .count = __ETHTOOL_A_STATS_ETH_PHY_CNT, 97 .strings = stats_eth_phy_names, 98 }, 99 [ETH_SS_STATS_ETH_MAC] = { 100 .per_dev = false, 101 .count = __ETHTOOL_A_STATS_ETH_MAC_CNT, 102 .strings = stats_eth_mac_names, 103 }, 104 [ETH_SS_STATS_ETH_CTRL] = { 105 .per_dev = false, 106 .count = __ETHTOOL_A_STATS_ETH_CTRL_CNT, 107 .strings = stats_eth_ctrl_names, 108 }, 109 [ETH_SS_STATS_RMON] = { 110 .per_dev = false, 111 .count = __ETHTOOL_A_STATS_RMON_CNT, 112 .strings = stats_rmon_names, 113 }, 114 [ETH_SS_STATS_PHY] = { 115 .per_dev = false, 116 .count = __ETHTOOL_A_STATS_PHY_CNT, 117 .strings = stats_phy_names, 118 }, 119 }; 120 121 struct strset_req_info { 122 struct ethnl_req_info base; 123 u32 req_ids; 124 bool counts_only; 125 }; 126 127 #define STRSET_REQINFO(__req_base) \ 128 container_of(__req_base, struct strset_req_info, base) 129 130 struct strset_reply_data { 131 struct ethnl_reply_data base; 132 struct strset_info sets[ETH_SS_COUNT]; 133 }; 134 135 #define STRSET_REPDATA(__reply_base) \ 136 container_of(__reply_base, struct strset_reply_data, base) 137 138 const struct nla_policy ethnl_strset_get_policy[] = { 139 [ETHTOOL_A_STRSET_HEADER] = 140 NLA_POLICY_NESTED(ethnl_header_policy_phy), 141 [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED }, 142 [ETHTOOL_A_STRSET_COUNTS_ONLY] = { .type = NLA_FLAG }, 143 }; 144 145 static const struct nla_policy get_stringset_policy[] = { 146 [ETHTOOL_A_STRINGSET_ID] = { .type = NLA_U32 }, 147 }; 148 149 /** 150 * strset_include() - test if a string set should be included in reply 151 * @info: parsed client request 152 * @data: pointer to request data structure 153 * @id: id of string set to check (ETH_SS_* constants) 154 */ 155 static bool strset_include(const struct strset_req_info *info, 156 const struct strset_reply_data *data, u32 id) 157 { 158 bool per_dev; 159 160 BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids)); 161 162 if (info->req_ids) 163 return info->req_ids & (1U << id); 164 per_dev = data->sets[id].per_dev; 165 if (!per_dev && !data->sets[id].strings) 166 return false; 167 168 return data->base.dev ? per_dev : !per_dev; 169 } 170 171 static int strset_get_id(const struct nlattr *nest, u32 *val, 172 struct netlink_ext_ack *extack) 173 { 174 struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)]; 175 int ret; 176 177 ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest, 178 get_stringset_policy, extack); 179 if (ret < 0) 180 return ret; 181 if (NL_REQ_ATTR_CHECK(extack, nest, tb, ETHTOOL_A_STRINGSET_ID)) 182 return -EINVAL; 183 184 *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]); 185 return 0; 186 } 187 188 static const struct nla_policy strset_stringsets_policy[] = { 189 [ETHTOOL_A_STRINGSETS_STRINGSET] = { .type = NLA_NESTED }, 190 }; 191 192 static int strset_parse_request(struct ethnl_req_info *req_base, 193 const struct genl_info *info, 194 struct nlattr **tb, 195 struct netlink_ext_ack *extack) 196 { 197 struct strset_req_info *req_info = STRSET_REQINFO(req_base); 198 struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS]; 199 struct nlattr *attr; 200 int rem, ret; 201 202 if (!nest) 203 return 0; 204 ret = nla_validate_nested(nest, 205 ARRAY_SIZE(strset_stringsets_policy) - 1, 206 strset_stringsets_policy, extack); 207 if (ret < 0) 208 return ret; 209 210 req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY]; 211 nla_for_each_nested(attr, nest, rem) { 212 u32 id; 213 214 if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET, 215 "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n", 216 nla_type(attr))) 217 return -EINVAL; 218 219 ret = strset_get_id(attr, &id, extack); 220 if (ret < 0) 221 return ret; 222 if (id >= ETH_SS_COUNT) { 223 NL_SET_ERR_MSG_ATTR(extack, attr, 224 "unknown string set id"); 225 return -EOPNOTSUPP; 226 } 227 228 req_info->req_ids |= (1U << id); 229 } 230 231 return 0; 232 } 233 234 static void strset_cleanup_data(struct ethnl_reply_data *reply_base) 235 { 236 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 237 unsigned int i; 238 239 for (i = 0; i < ETH_SS_COUNT; i++) 240 if (data->sets[i].free_strings) { 241 kfree(data->sets[i].strings); 242 data->sets[i].strings = NULL; 243 data->sets[i].free_strings = false; 244 } 245 } 246 247 static int strset_prepare_set(struct strset_info *info, struct net_device *dev, 248 struct phy_device *phydev, unsigned int id, 249 bool counts_only) 250 { 251 const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops; 252 const struct ethtool_ops *ops = dev->ethtool_ops; 253 void *strings; 254 int count, ret; 255 256 if (id == ETH_SS_PHY_STATS && phydev && 257 !ops->get_ethtool_phy_stats && phy_ops && 258 phy_ops->get_sset_count) 259 ret = phy_ops->get_sset_count(phydev); 260 else if (ops->get_sset_count && ops->get_strings) 261 ret = ops->get_sset_count(dev, id); 262 else 263 ret = -EOPNOTSUPP; 264 if (ret <= 0) { 265 info->count = 0; 266 return 0; 267 } 268 269 count = ret; 270 if (!counts_only) { 271 strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL); 272 if (!strings) 273 return -ENOMEM; 274 if (id == ETH_SS_PHY_STATS && phydev && 275 !ops->get_ethtool_phy_stats && phy_ops && 276 phy_ops->get_strings) 277 phy_ops->get_strings(phydev, strings); 278 else 279 ops->get_strings(dev, id, strings); 280 info->strings = strings; 281 info->free_strings = true; 282 } 283 info->count = count; 284 285 return 0; 286 } 287 288 static int strset_prepare_data(const struct ethnl_req_info *req_base, 289 struct ethnl_reply_data *reply_base, 290 const struct genl_info *info) 291 { 292 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 293 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 294 struct net_device *dev = reply_base->dev; 295 struct nlattr **tb = info->attrs; 296 struct phy_device *phydev; 297 unsigned int i; 298 int ret; 299 300 BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT); 301 memcpy(&data->sets, &info_template, sizeof(data->sets)); 302 303 if (!dev) { 304 for (i = 0; i < ETH_SS_COUNT; i++) { 305 if ((req_info->req_ids & (1U << i)) && 306 data->sets[i].per_dev) { 307 GENL_SET_ERR_MSG(info, "requested per device strings without dev"); 308 return -EINVAL; 309 } 310 } 311 return 0; 312 } 313 314 phydev = ethnl_req_get_phydev(req_base, tb, ETHTOOL_A_HEADER_FLAGS, 315 info->extack); 316 317 /* phydev can be NULL, check for errors only */ 318 if (IS_ERR(phydev)) 319 return PTR_ERR(phydev); 320 321 ret = ethnl_ops_begin(dev); 322 if (ret < 0) 323 goto err_strset; 324 for (i = 0; i < ETH_SS_COUNT; i++) { 325 if (!strset_include(req_info, data, i) || 326 !data->sets[i].per_dev) 327 continue; 328 329 ret = strset_prepare_set(&data->sets[i], dev, phydev, i, 330 req_info->counts_only); 331 if (ret < 0) 332 goto err_ops; 333 } 334 ethnl_ops_complete(dev); 335 336 return 0; 337 err_ops: 338 ethnl_ops_complete(dev); 339 err_strset: 340 strset_cleanup_data(reply_base); 341 return ret; 342 } 343 344 /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */ 345 static int strset_set_size(const struct strset_info *info, bool counts_only) 346 { 347 unsigned int len = 0; 348 unsigned int i; 349 350 if (info->count == 0) 351 return 0; 352 if (counts_only) 353 return nla_total_size(2 * nla_total_size(sizeof(u32))); 354 355 for (i = 0; i < info->count; i++) { 356 const char *str = info->strings[i]; 357 358 /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */ 359 len += nla_total_size(nla_total_size(sizeof(u32)) + 360 ethnl_strz_size(str)); 361 } 362 /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */ 363 len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len); 364 365 return nla_total_size(len); 366 } 367 368 static int strset_reply_size(const struct ethnl_req_info *req_base, 369 const struct ethnl_reply_data *reply_base) 370 { 371 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 372 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 373 unsigned int i; 374 int len = 0; 375 int ret; 376 377 len += nla_total_size(0); /* ETHTOOL_A_STRSET_STRINGSETS */ 378 379 for (i = 0; i < ETH_SS_COUNT; i++) { 380 const struct strset_info *set_info = &data->sets[i]; 381 382 if (!strset_include(req_info, data, i)) 383 continue; 384 385 ret = strset_set_size(set_info, req_info->counts_only); 386 if (ret < 0) 387 return ret; 388 len += ret; 389 } 390 391 return len; 392 } 393 394 /* fill one string into reply */ 395 static int strset_fill_string(struct sk_buff *skb, 396 const struct strset_info *set_info, u32 idx) 397 { 398 struct nlattr *string_attr; 399 const char *value; 400 401 value = set_info->strings[idx]; 402 403 string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING); 404 if (!string_attr) 405 return -EMSGSIZE; 406 if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) || 407 ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value)) 408 goto nla_put_failure; 409 nla_nest_end(skb, string_attr); 410 411 return 0; 412 nla_put_failure: 413 nla_nest_cancel(skb, string_attr); 414 return -EMSGSIZE; 415 } 416 417 /* fill one string set into reply */ 418 static int strset_fill_set(struct sk_buff *skb, 419 const struct strset_info *set_info, u32 id, 420 bool counts_only) 421 { 422 struct nlattr *stringset_attr; 423 struct nlattr *strings_attr; 424 unsigned int i; 425 426 if (!set_info->per_dev && !set_info->strings) 427 return -EOPNOTSUPP; 428 if (set_info->count == 0) 429 return 0; 430 stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET); 431 if (!stringset_attr) 432 return -EMSGSIZE; 433 434 if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) || 435 nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count)) 436 goto nla_put_failure; 437 438 if (!counts_only) { 439 strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS); 440 if (!strings_attr) 441 goto nla_put_failure; 442 for (i = 0; i < set_info->count; i++) { 443 if (strset_fill_string(skb, set_info, i) < 0) 444 goto nla_put_failure; 445 } 446 if (nla_nest_end_safe(skb, strings_attr) < 0) 447 goto nla_put_failure; 448 } 449 450 nla_nest_end(skb, stringset_attr); 451 return 0; 452 453 nla_put_failure: 454 nla_nest_cancel(skb, stringset_attr); 455 return -EMSGSIZE; 456 } 457 458 static int strset_fill_reply(struct sk_buff *skb, 459 const struct ethnl_req_info *req_base, 460 const struct ethnl_reply_data *reply_base) 461 { 462 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 463 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 464 struct nlattr *nest; 465 unsigned int i; 466 int ret; 467 468 nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS); 469 if (!nest) 470 return -EMSGSIZE; 471 472 for (i = 0; i < ETH_SS_COUNT; i++) { 473 if (strset_include(req_info, data, i)) { 474 ret = strset_fill_set(skb, &data->sets[i], i, 475 req_info->counts_only); 476 if (ret < 0) 477 goto nla_put_failure; 478 } 479 } 480 481 nla_nest_end(skb, nest); 482 return 0; 483 484 nla_put_failure: 485 nla_nest_cancel(skb, nest); 486 return ret; 487 } 488 489 const struct ethnl_request_ops ethnl_strset_request_ops = { 490 .request_cmd = ETHTOOL_MSG_STRSET_GET, 491 .reply_cmd = ETHTOOL_MSG_STRSET_GET_REPLY, 492 .hdr_attr = ETHTOOL_A_STRSET_HEADER, 493 .req_info_size = sizeof(struct strset_req_info), 494 .reply_data_size = sizeof(struct strset_reply_data), 495 .allow_nodev_do = true, 496 497 .parse_request = strset_parse_request, 498 .prepare_data = strset_prepare_data, 499 .reply_size = strset_reply_size, 500 .fill_reply = strset_fill_reply, 501 .cleanup_data = strset_cleanup_data, 502 }; 503