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