~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/ethtool/strset.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 (NL_REQ_ATTR_CHECK(extack, nest, 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                                const 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 };
481 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php