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