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