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 */
strset_include(const struct strset_req_info * info,const struct strset_reply_data * data,u32 id)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
strset_get_id(const struct nlattr * nest,u32 * val,struct netlink_ext_ack * extack)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
strset_parse_request(struct ethnl_req_info * req_base,const struct genl_info * info,struct nlattr ** tb,struct netlink_ext_ack * extack)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
strset_cleanup_data(struct ethnl_reply_data * reply_base)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
strset_prepare_set(struct strset_info * info,struct net_device * dev,struct phy_device * phydev,unsigned int id,bool counts_only)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
strset_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)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_STRSET_HEADER,
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 */
strset_set_size(const struct strset_info * info,bool counts_only)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
strset_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)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 */
strset_fill_string(struct sk_buff * skb,const struct strset_info * set_info,u32 idx)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 */
strset_fill_set(struct sk_buff * skb,const struct strset_info * set_info,u32 id,bool counts_only)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
strset_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)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