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