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 }; 84 85 struct strset_req_info { 86 struct ethnl_req_info base; 87 u32 req_ids; 88 bool counts_only; 89 }; 90 91 #define STRSET_REQINFO(__req_base) \ 92 container_of(__req_base, struct strset_req_info, base) 93 94 struct strset_reply_data { 95 struct ethnl_reply_data base; 96 struct strset_info sets[ETH_SS_COUNT]; 97 }; 98 99 #define STRSET_REPDATA(__reply_base) \ 100 container_of(__reply_base, struct strset_reply_data, base) 101 102 const struct nla_policy ethnl_strset_get_policy[] = { 103 [ETHTOOL_A_STRSET_HEADER] = 104 NLA_POLICY_NESTED(ethnl_header_policy), 105 [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED }, 106 [ETHTOOL_A_STRSET_COUNTS_ONLY] = { .type = NLA_FLAG }, 107 }; 108 109 static const struct nla_policy get_stringset_policy[] = { 110 [ETHTOOL_A_STRINGSET_ID] = { .type = NLA_U32 }, 111 }; 112 113 /** 114 * strset_include() - test if a string set should be included in reply 115 * @info: parsed client request 116 * @data: pointer to request data structure 117 * @id: id of string set to check (ETH_SS_* constants) 118 */ 119 static bool strset_include(const struct strset_req_info *info, 120 const struct strset_reply_data *data, u32 id) 121 { 122 bool per_dev; 123 124 BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids)); 125 126 if (info->req_ids) 127 return info->req_ids & (1U << id); 128 per_dev = data->sets[id].per_dev; 129 if (!per_dev && !data->sets[id].strings) 130 return false; 131 132 return data->base.dev ? per_dev : !per_dev; 133 } 134 135 static int strset_get_id(const struct nlattr *nest, u32 *val, 136 struct netlink_ext_ack *extack) 137 { 138 struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)]; 139 int ret; 140 141 ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest, 142 get_stringset_policy, extack); 143 if (ret < 0) 144 return ret; 145 if (!tb[ETHTOOL_A_STRINGSET_ID]) 146 return -EINVAL; 147 148 *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]); 149 return 0; 150 } 151 152 static const struct nla_policy strset_stringsets_policy[] = { 153 [ETHTOOL_A_STRINGSETS_STRINGSET] = { .type = NLA_NESTED }, 154 }; 155 156 static int strset_parse_request(struct ethnl_req_info *req_base, 157 struct nlattr **tb, 158 struct netlink_ext_ack *extack) 159 { 160 struct strset_req_info *req_info = STRSET_REQINFO(req_base); 161 struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS]; 162 struct nlattr *attr; 163 int rem, ret; 164 165 if (!nest) 166 return 0; 167 ret = nla_validate_nested(nest, 168 ARRAY_SIZE(strset_stringsets_policy) - 1, 169 strset_stringsets_policy, extack); 170 if (ret < 0) 171 return ret; 172 173 req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY]; 174 nla_for_each_nested(attr, nest, rem) { 175 u32 id; 176 177 if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET, 178 "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n", 179 nla_type(attr))) 180 return -EINVAL; 181 182 ret = strset_get_id(attr, &id, extack); 183 if (ret < 0) 184 return ret; 185 if (id >= ETH_SS_COUNT) { 186 NL_SET_ERR_MSG_ATTR(extack, attr, 187 "unknown string set id"); 188 return -EOPNOTSUPP; 189 } 190 191 req_info->req_ids |= (1U << id); 192 } 193 194 return 0; 195 } 196 197 static void strset_cleanup_data(struct ethnl_reply_data *reply_base) 198 { 199 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 200 unsigned int i; 201 202 for (i = 0; i < ETH_SS_COUNT; i++) 203 if (data->sets[i].free_strings) { 204 kfree(data->sets[i].strings); 205 data->sets[i].strings = NULL; 206 data->sets[i].free_strings = false; 207 } 208 } 209 210 static int strset_prepare_set(struct strset_info *info, struct net_device *dev, 211 unsigned int id, bool counts_only) 212 { 213 const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops; 214 const struct ethtool_ops *ops = dev->ethtool_ops; 215 void *strings; 216 int count, ret; 217 218 if (id == ETH_SS_PHY_STATS && dev->phydev && 219 !ops->get_ethtool_phy_stats && phy_ops && 220 phy_ops->get_sset_count) 221 ret = phy_ops->get_sset_count(dev->phydev); 222 else if (ops->get_sset_count && ops->get_strings) 223 ret = ops->get_sset_count(dev, id); 224 else 225 ret = -EOPNOTSUPP; 226 if (ret <= 0) { 227 info->count = 0; 228 return 0; 229 } 230 231 count = ret; 232 if (!counts_only) { 233 strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL); 234 if (!strings) 235 return -ENOMEM; 236 if (id == ETH_SS_PHY_STATS && dev->phydev && 237 !ops->get_ethtool_phy_stats && phy_ops && 238 phy_ops->get_strings) 239 phy_ops->get_strings(dev->phydev, strings); 240 else 241 ops->get_strings(dev, id, strings); 242 info->strings = strings; 243 info->free_strings = true; 244 } 245 info->count = count; 246 247 return 0; 248 } 249 250 static int strset_prepare_data(const struct ethnl_req_info *req_base, 251 struct ethnl_reply_data *reply_base, 252 struct genl_info *info) 253 { 254 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 255 struct strset_reply_data *data = STRSET_REPDATA(reply_base); 256 struct net_device *dev = reply_base->dev; 257 unsigned int i; 258 int ret; 259 260 BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT); 261 memcpy(&data->sets, &info_template, sizeof(data->sets)); 262 263 if (!dev) { 264 for (i = 0; i < ETH_SS_COUNT; i++) { 265 if ((req_info->req_ids & (1U << i)) && 266 data->sets[i].per_dev) { 267 if (info) 268 GENL_SET_ERR_MSG(info, "requested per device strings without dev"); 269 return -EINVAL; 270 } 271 } 272 return 0; 273 } 274 275 ret = ethnl_ops_begin(dev); 276 if (ret < 0) 277 goto err_strset; 278 for (i = 0; i < ETH_SS_COUNT; i++) { 279 if (!strset_include(req_info, data, i) || 280 !data->sets[i].per_dev) 281 continue; 282 283 ret = strset_prepare_set(&data->sets[i], dev, i, 284 req_info->counts_only); 285 if (ret < 0) 286 goto err_ops; 287 } 288 ethnl_ops_complete(dev); 289 290 return 0; 291 err_ops: 292 ethnl_ops_complete(dev); 293 err_strset: 294 strset_cleanup_data(reply_base); 295 return ret; 296 } 297 298 /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */ 299 static int strset_set_size(const struct strset_info *info, bool counts_only) 300 { 301 unsigned int len = 0; 302 unsigned int i; 303 304 if (info->count == 0) 305 return 0; 306 if (counts_only) 307 return nla_total_size(2 * nla_total_size(sizeof(u32))); 308 309 for (i = 0; i < info->count; i++) { 310 const char *str = info->strings[i]; 311 312 /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */ 313 len += nla_total_size(nla_total_size(sizeof(u32)) + 314 ethnl_strz_size(str)); 315 } 316 /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */ 317 len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len); 318 319 return nla_total_size(len); 320 } 321 322 static int strset_reply_size(const struct ethnl_req_info *req_base, 323 const struct ethnl_reply_data *reply_base) 324 { 325 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 326 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 327 unsigned int i; 328 int len = 0; 329 int ret; 330 331 for (i = 0; i < ETH_SS_COUNT; i++) { 332 const struct strset_info *set_info = &data->sets[i]; 333 334 if (!strset_include(req_info, data, i)) 335 continue; 336 337 ret = strset_set_size(set_info, req_info->counts_only); 338 if (ret < 0) 339 return ret; 340 len += ret; 341 } 342 343 return len; 344 } 345 346 /* fill one string into reply */ 347 static int strset_fill_string(struct sk_buff *skb, 348 const struct strset_info *set_info, u32 idx) 349 { 350 struct nlattr *string_attr; 351 const char *value; 352 353 value = set_info->strings[idx]; 354 355 string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING); 356 if (!string_attr) 357 return -EMSGSIZE; 358 if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) || 359 ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value)) 360 goto nla_put_failure; 361 nla_nest_end(skb, string_attr); 362 363 return 0; 364 nla_put_failure: 365 nla_nest_cancel(skb, string_attr); 366 return -EMSGSIZE; 367 } 368 369 /* fill one string set into reply */ 370 static int strset_fill_set(struct sk_buff *skb, 371 const struct strset_info *set_info, u32 id, 372 bool counts_only) 373 { 374 struct nlattr *stringset_attr; 375 struct nlattr *strings_attr; 376 unsigned int i; 377 378 if (!set_info->per_dev && !set_info->strings) 379 return -EOPNOTSUPP; 380 if (set_info->count == 0) 381 return 0; 382 stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET); 383 if (!stringset_attr) 384 return -EMSGSIZE; 385 386 if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) || 387 nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count)) 388 goto nla_put_failure; 389 390 if (!counts_only) { 391 strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS); 392 if (!strings_attr) 393 goto nla_put_failure; 394 for (i = 0; i < set_info->count; i++) { 395 if (strset_fill_string(skb, set_info, i) < 0) 396 goto nla_put_failure; 397 } 398 nla_nest_end(skb, strings_attr); 399 } 400 401 nla_nest_end(skb, stringset_attr); 402 return 0; 403 404 nla_put_failure: 405 nla_nest_cancel(skb, stringset_attr); 406 return -EMSGSIZE; 407 } 408 409 static int strset_fill_reply(struct sk_buff *skb, 410 const struct ethnl_req_info *req_base, 411 const struct ethnl_reply_data *reply_base) 412 { 413 const struct strset_req_info *req_info = STRSET_REQINFO(req_base); 414 const struct strset_reply_data *data = STRSET_REPDATA(reply_base); 415 struct nlattr *nest; 416 unsigned int i; 417 int ret; 418 419 nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS); 420 if (!nest) 421 return -EMSGSIZE; 422 423 for (i = 0; i < ETH_SS_COUNT; i++) { 424 if (strset_include(req_info, data, i)) { 425 ret = strset_fill_set(skb, &data->sets[i], i, 426 req_info->counts_only); 427 if (ret < 0) 428 goto nla_put_failure; 429 } 430 } 431 432 nla_nest_end(skb, nest); 433 return 0; 434 435 nla_put_failure: 436 nla_nest_cancel(skb, nest); 437 return ret; 438 } 439 440 const struct ethnl_request_ops ethnl_strset_request_ops = { 441 .request_cmd = ETHTOOL_MSG_STRSET_GET, 442 .reply_cmd = ETHTOOL_MSG_STRSET_GET_REPLY, 443 .hdr_attr = ETHTOOL_A_STRSET_HEADER, 444 .req_info_size = sizeof(struct strset_req_info), 445 .reply_data_size = sizeof(struct strset_reply_data), 446 .allow_nodev_do = true, 447 448 .parse_request = strset_parse_request, 449 .prepare_data = strset_prepare_data, 450 .reply_size = strset_reply_size, 451 .fill_reply = strset_fill_reply, 452 .cleanup_data = strset_cleanup_data, 453 }; 454