1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun #include <linux/ethtool.h>
4*4882a593Smuzhiyun #include <linux/phy.h>
5*4882a593Smuzhiyun #include "netlink.h"
6*4882a593Smuzhiyun #include "common.h"
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun struct strset_info {
9*4882a593Smuzhiyun bool per_dev;
10*4882a593Smuzhiyun bool free_strings;
11*4882a593Smuzhiyun unsigned int count;
12*4882a593Smuzhiyun const char (*strings)[ETH_GSTRING_LEN];
13*4882a593Smuzhiyun };
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun static const struct strset_info info_template[] = {
16*4882a593Smuzhiyun [ETH_SS_TEST] = {
17*4882a593Smuzhiyun .per_dev = true,
18*4882a593Smuzhiyun },
19*4882a593Smuzhiyun [ETH_SS_STATS] = {
20*4882a593Smuzhiyun .per_dev = true,
21*4882a593Smuzhiyun },
22*4882a593Smuzhiyun [ETH_SS_PRIV_FLAGS] = {
23*4882a593Smuzhiyun .per_dev = true,
24*4882a593Smuzhiyun },
25*4882a593Smuzhiyun [ETH_SS_FEATURES] = {
26*4882a593Smuzhiyun .per_dev = false,
27*4882a593Smuzhiyun .count = ARRAY_SIZE(netdev_features_strings),
28*4882a593Smuzhiyun .strings = netdev_features_strings,
29*4882a593Smuzhiyun },
30*4882a593Smuzhiyun [ETH_SS_RSS_HASH_FUNCS] = {
31*4882a593Smuzhiyun .per_dev = false,
32*4882a593Smuzhiyun .count = ARRAY_SIZE(rss_hash_func_strings),
33*4882a593Smuzhiyun .strings = rss_hash_func_strings,
34*4882a593Smuzhiyun },
35*4882a593Smuzhiyun [ETH_SS_TUNABLES] = {
36*4882a593Smuzhiyun .per_dev = false,
37*4882a593Smuzhiyun .count = ARRAY_SIZE(tunable_strings),
38*4882a593Smuzhiyun .strings = tunable_strings,
39*4882a593Smuzhiyun },
40*4882a593Smuzhiyun [ETH_SS_PHY_STATS] = {
41*4882a593Smuzhiyun .per_dev = true,
42*4882a593Smuzhiyun },
43*4882a593Smuzhiyun [ETH_SS_PHY_TUNABLES] = {
44*4882a593Smuzhiyun .per_dev = false,
45*4882a593Smuzhiyun .count = ARRAY_SIZE(phy_tunable_strings),
46*4882a593Smuzhiyun .strings = phy_tunable_strings,
47*4882a593Smuzhiyun },
48*4882a593Smuzhiyun [ETH_SS_LINK_MODES] = {
49*4882a593Smuzhiyun .per_dev = false,
50*4882a593Smuzhiyun .count = __ETHTOOL_LINK_MODE_MASK_NBITS,
51*4882a593Smuzhiyun .strings = link_mode_names,
52*4882a593Smuzhiyun },
53*4882a593Smuzhiyun [ETH_SS_MSG_CLASSES] = {
54*4882a593Smuzhiyun .per_dev = false,
55*4882a593Smuzhiyun .count = NETIF_MSG_CLASS_COUNT,
56*4882a593Smuzhiyun .strings = netif_msg_class_names,
57*4882a593Smuzhiyun },
58*4882a593Smuzhiyun [ETH_SS_WOL_MODES] = {
59*4882a593Smuzhiyun .per_dev = false,
60*4882a593Smuzhiyun .count = WOL_MODE_COUNT,
61*4882a593Smuzhiyun .strings = wol_mode_names,
62*4882a593Smuzhiyun },
63*4882a593Smuzhiyun [ETH_SS_SOF_TIMESTAMPING] = {
64*4882a593Smuzhiyun .per_dev = false,
65*4882a593Smuzhiyun .count = __SOF_TIMESTAMPING_CNT,
66*4882a593Smuzhiyun .strings = sof_timestamping_names,
67*4882a593Smuzhiyun },
68*4882a593Smuzhiyun [ETH_SS_TS_TX_TYPES] = {
69*4882a593Smuzhiyun .per_dev = false,
70*4882a593Smuzhiyun .count = __HWTSTAMP_TX_CNT,
71*4882a593Smuzhiyun .strings = ts_tx_type_names,
72*4882a593Smuzhiyun },
73*4882a593Smuzhiyun [ETH_SS_TS_RX_FILTERS] = {
74*4882a593Smuzhiyun .per_dev = false,
75*4882a593Smuzhiyun .count = __HWTSTAMP_FILTER_CNT,
76*4882a593Smuzhiyun .strings = ts_rx_filter_names,
77*4882a593Smuzhiyun },
78*4882a593Smuzhiyun [ETH_SS_UDP_TUNNEL_TYPES] = {
79*4882a593Smuzhiyun .per_dev = false,
80*4882a593Smuzhiyun .count = __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
81*4882a593Smuzhiyun .strings = udp_tunnel_type_names,
82*4882a593Smuzhiyun },
83*4882a593Smuzhiyun };
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun struct strset_req_info {
86*4882a593Smuzhiyun struct ethnl_req_info base;
87*4882a593Smuzhiyun u32 req_ids;
88*4882a593Smuzhiyun bool counts_only;
89*4882a593Smuzhiyun };
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun #define STRSET_REQINFO(__req_base) \
92*4882a593Smuzhiyun container_of(__req_base, struct strset_req_info, base)
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun struct strset_reply_data {
95*4882a593Smuzhiyun struct ethnl_reply_data base;
96*4882a593Smuzhiyun struct strset_info sets[ETH_SS_COUNT];
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun #define STRSET_REPDATA(__reply_base) \
100*4882a593Smuzhiyun container_of(__reply_base, struct strset_reply_data, base)
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun const struct nla_policy ethnl_strset_get_policy[] = {
103*4882a593Smuzhiyun [ETHTOOL_A_STRSET_HEADER] =
104*4882a593Smuzhiyun NLA_POLICY_NESTED(ethnl_header_policy),
105*4882a593Smuzhiyun [ETHTOOL_A_STRSET_STRINGSETS] = { .type = NLA_NESTED },
106*4882a593Smuzhiyun [ETHTOOL_A_STRSET_COUNTS_ONLY] = { .type = NLA_FLAG },
107*4882a593Smuzhiyun };
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun static const struct nla_policy get_stringset_policy[] = {
110*4882a593Smuzhiyun [ETHTOOL_A_STRINGSET_ID] = { .type = NLA_U32 },
111*4882a593Smuzhiyun };
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun /**
114*4882a593Smuzhiyun * strset_include() - test if a string set should be included in reply
115*4882a593Smuzhiyun * @info: parsed client request
116*4882a593Smuzhiyun * @data: pointer to request data structure
117*4882a593Smuzhiyun * @id: id of string set to check (ETH_SS_* constants)
118*4882a593Smuzhiyun */
strset_include(const struct strset_req_info * info,const struct strset_reply_data * data,u32 id)119*4882a593Smuzhiyun static bool strset_include(const struct strset_req_info *info,
120*4882a593Smuzhiyun const struct strset_reply_data *data, u32 id)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun bool per_dev;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids));
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun if (info->req_ids)
127*4882a593Smuzhiyun return info->req_ids & (1U << id);
128*4882a593Smuzhiyun per_dev = data->sets[id].per_dev;
129*4882a593Smuzhiyun if (!per_dev && !data->sets[id].strings)
130*4882a593Smuzhiyun return false;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun return data->base.dev ? per_dev : !per_dev;
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
strset_get_id(const struct nlattr * nest,u32 * val,struct netlink_ext_ack * extack)135*4882a593Smuzhiyun static int strset_get_id(const struct nlattr *nest, u32 *val,
136*4882a593Smuzhiyun struct netlink_ext_ack *extack)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)];
139*4882a593Smuzhiyun int ret;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest,
142*4882a593Smuzhiyun get_stringset_policy, extack);
143*4882a593Smuzhiyun if (ret < 0)
144*4882a593Smuzhiyun return ret;
145*4882a593Smuzhiyun if (!tb[ETHTOOL_A_STRINGSET_ID])
146*4882a593Smuzhiyun return -EINVAL;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun *val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]);
149*4882a593Smuzhiyun return 0;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun static const struct nla_policy strset_stringsets_policy[] = {
153*4882a593Smuzhiyun [ETHTOOL_A_STRINGSETS_STRINGSET] = { .type = NLA_NESTED },
154*4882a593Smuzhiyun };
155*4882a593Smuzhiyun
strset_parse_request(struct ethnl_req_info * req_base,struct nlattr ** tb,struct netlink_ext_ack * extack)156*4882a593Smuzhiyun static int strset_parse_request(struct ethnl_req_info *req_base,
157*4882a593Smuzhiyun struct nlattr **tb,
158*4882a593Smuzhiyun struct netlink_ext_ack *extack)
159*4882a593Smuzhiyun {
160*4882a593Smuzhiyun struct strset_req_info *req_info = STRSET_REQINFO(req_base);
161*4882a593Smuzhiyun struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS];
162*4882a593Smuzhiyun struct nlattr *attr;
163*4882a593Smuzhiyun int rem, ret;
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun if (!nest)
166*4882a593Smuzhiyun return 0;
167*4882a593Smuzhiyun ret = nla_validate_nested(nest,
168*4882a593Smuzhiyun ARRAY_SIZE(strset_stringsets_policy) - 1,
169*4882a593Smuzhiyun strset_stringsets_policy, extack);
170*4882a593Smuzhiyun if (ret < 0)
171*4882a593Smuzhiyun return ret;
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY];
174*4882a593Smuzhiyun nla_for_each_nested(attr, nest, rem) {
175*4882a593Smuzhiyun u32 id;
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET,
178*4882a593Smuzhiyun "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n",
179*4882a593Smuzhiyun nla_type(attr)))
180*4882a593Smuzhiyun return -EINVAL;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun ret = strset_get_id(attr, &id, extack);
183*4882a593Smuzhiyun if (ret < 0)
184*4882a593Smuzhiyun return ret;
185*4882a593Smuzhiyun if (id >= ETH_SS_COUNT) {
186*4882a593Smuzhiyun NL_SET_ERR_MSG_ATTR(extack, attr,
187*4882a593Smuzhiyun "unknown string set id");
188*4882a593Smuzhiyun return -EOPNOTSUPP;
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun req_info->req_ids |= (1U << id);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun return 0;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
strset_cleanup_data(struct ethnl_reply_data * reply_base)197*4882a593Smuzhiyun static void strset_cleanup_data(struct ethnl_reply_data *reply_base)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun struct strset_reply_data *data = STRSET_REPDATA(reply_base);
200*4882a593Smuzhiyun unsigned int i;
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun for (i = 0; i < ETH_SS_COUNT; i++)
203*4882a593Smuzhiyun if (data->sets[i].free_strings) {
204*4882a593Smuzhiyun kfree(data->sets[i].strings);
205*4882a593Smuzhiyun data->sets[i].strings = NULL;
206*4882a593Smuzhiyun data->sets[i].free_strings = false;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
strset_prepare_set(struct strset_info * info,struct net_device * dev,unsigned int id,bool counts_only)210*4882a593Smuzhiyun static int strset_prepare_set(struct strset_info *info, struct net_device *dev,
211*4882a593Smuzhiyun unsigned int id, bool counts_only)
212*4882a593Smuzhiyun {
213*4882a593Smuzhiyun const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
214*4882a593Smuzhiyun const struct ethtool_ops *ops = dev->ethtool_ops;
215*4882a593Smuzhiyun void *strings;
216*4882a593Smuzhiyun int count, ret;
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun if (id == ETH_SS_PHY_STATS && dev->phydev &&
219*4882a593Smuzhiyun !ops->get_ethtool_phy_stats && phy_ops &&
220*4882a593Smuzhiyun phy_ops->get_sset_count)
221*4882a593Smuzhiyun ret = phy_ops->get_sset_count(dev->phydev);
222*4882a593Smuzhiyun else if (ops->get_sset_count && ops->get_strings)
223*4882a593Smuzhiyun ret = ops->get_sset_count(dev, id);
224*4882a593Smuzhiyun else
225*4882a593Smuzhiyun ret = -EOPNOTSUPP;
226*4882a593Smuzhiyun if (ret <= 0) {
227*4882a593Smuzhiyun info->count = 0;
228*4882a593Smuzhiyun return 0;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun count = ret;
232*4882a593Smuzhiyun if (!counts_only) {
233*4882a593Smuzhiyun strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
234*4882a593Smuzhiyun if (!strings)
235*4882a593Smuzhiyun return -ENOMEM;
236*4882a593Smuzhiyun if (id == ETH_SS_PHY_STATS && dev->phydev &&
237*4882a593Smuzhiyun !ops->get_ethtool_phy_stats && phy_ops &&
238*4882a593Smuzhiyun phy_ops->get_strings)
239*4882a593Smuzhiyun phy_ops->get_strings(dev->phydev, strings);
240*4882a593Smuzhiyun else
241*4882a593Smuzhiyun ops->get_strings(dev, id, strings);
242*4882a593Smuzhiyun info->strings = strings;
243*4882a593Smuzhiyun info->free_strings = true;
244*4882a593Smuzhiyun }
245*4882a593Smuzhiyun info->count = count;
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun return 0;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
strset_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,struct genl_info * info)250*4882a593Smuzhiyun static int strset_prepare_data(const struct ethnl_req_info *req_base,
251*4882a593Smuzhiyun struct ethnl_reply_data *reply_base,
252*4882a593Smuzhiyun struct genl_info *info)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
255*4882a593Smuzhiyun struct strset_reply_data *data = STRSET_REPDATA(reply_base);
256*4882a593Smuzhiyun struct net_device *dev = reply_base->dev;
257*4882a593Smuzhiyun unsigned int i;
258*4882a593Smuzhiyun int ret;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
261*4882a593Smuzhiyun memcpy(&data->sets, &info_template, sizeof(data->sets));
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun if (!dev) {
264*4882a593Smuzhiyun for (i = 0; i < ETH_SS_COUNT; i++) {
265*4882a593Smuzhiyun if ((req_info->req_ids & (1U << i)) &&
266*4882a593Smuzhiyun data->sets[i].per_dev) {
267*4882a593Smuzhiyun if (info)
268*4882a593Smuzhiyun GENL_SET_ERR_MSG(info, "requested per device strings without dev");
269*4882a593Smuzhiyun return -EINVAL;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun return 0;
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun ret = ethnl_ops_begin(dev);
276*4882a593Smuzhiyun if (ret < 0)
277*4882a593Smuzhiyun goto err_strset;
278*4882a593Smuzhiyun for (i = 0; i < ETH_SS_COUNT; i++) {
279*4882a593Smuzhiyun if (!strset_include(req_info, data, i) ||
280*4882a593Smuzhiyun !data->sets[i].per_dev)
281*4882a593Smuzhiyun continue;
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun ret = strset_prepare_set(&data->sets[i], dev, i,
284*4882a593Smuzhiyun req_info->counts_only);
285*4882a593Smuzhiyun if (ret < 0)
286*4882a593Smuzhiyun goto err_ops;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun ethnl_ops_complete(dev);
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun return 0;
291*4882a593Smuzhiyun err_ops:
292*4882a593Smuzhiyun ethnl_ops_complete(dev);
293*4882a593Smuzhiyun err_strset:
294*4882a593Smuzhiyun strset_cleanup_data(reply_base);
295*4882a593Smuzhiyun return ret;
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun /* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */
strset_set_size(const struct strset_info * info,bool counts_only)299*4882a593Smuzhiyun static int strset_set_size(const struct strset_info *info, bool counts_only)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun unsigned int len = 0;
302*4882a593Smuzhiyun unsigned int i;
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun if (info->count == 0)
305*4882a593Smuzhiyun return 0;
306*4882a593Smuzhiyun if (counts_only)
307*4882a593Smuzhiyun return nla_total_size(2 * nla_total_size(sizeof(u32)));
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun for (i = 0; i < info->count; i++) {
310*4882a593Smuzhiyun const char *str = info->strings[i];
311*4882a593Smuzhiyun
312*4882a593Smuzhiyun /* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */
313*4882a593Smuzhiyun len += nla_total_size(nla_total_size(sizeof(u32)) +
314*4882a593Smuzhiyun ethnl_strz_size(str));
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun /* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */
317*4882a593Smuzhiyun len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return nla_total_size(len);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
strset_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)322*4882a593Smuzhiyun static int strset_reply_size(const struct ethnl_req_info *req_base,
323*4882a593Smuzhiyun const struct ethnl_reply_data *reply_base)
324*4882a593Smuzhiyun {
325*4882a593Smuzhiyun const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
326*4882a593Smuzhiyun const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
327*4882a593Smuzhiyun unsigned int i;
328*4882a593Smuzhiyun int len = 0;
329*4882a593Smuzhiyun int ret;
330*4882a593Smuzhiyun
331*4882a593Smuzhiyun len += nla_total_size(0); /* ETHTOOL_A_STRSET_STRINGSETS */
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun for (i = 0; i < ETH_SS_COUNT; i++) {
334*4882a593Smuzhiyun const struct strset_info *set_info = &data->sets[i];
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun if (!strset_include(req_info, data, i))
337*4882a593Smuzhiyun continue;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun ret = strset_set_size(set_info, req_info->counts_only);
340*4882a593Smuzhiyun if (ret < 0)
341*4882a593Smuzhiyun return ret;
342*4882a593Smuzhiyun len += ret;
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun return len;
346*4882a593Smuzhiyun }
347*4882a593Smuzhiyun
348*4882a593Smuzhiyun /* fill one string into reply */
strset_fill_string(struct sk_buff * skb,const struct strset_info * set_info,u32 idx)349*4882a593Smuzhiyun static int strset_fill_string(struct sk_buff *skb,
350*4882a593Smuzhiyun const struct strset_info *set_info, u32 idx)
351*4882a593Smuzhiyun {
352*4882a593Smuzhiyun struct nlattr *string_attr;
353*4882a593Smuzhiyun const char *value;
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun value = set_info->strings[idx];
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING);
358*4882a593Smuzhiyun if (!string_attr)
359*4882a593Smuzhiyun return -EMSGSIZE;
360*4882a593Smuzhiyun if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) ||
361*4882a593Smuzhiyun ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value))
362*4882a593Smuzhiyun goto nla_put_failure;
363*4882a593Smuzhiyun nla_nest_end(skb, string_attr);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun return 0;
366*4882a593Smuzhiyun nla_put_failure:
367*4882a593Smuzhiyun nla_nest_cancel(skb, string_attr);
368*4882a593Smuzhiyun return -EMSGSIZE;
369*4882a593Smuzhiyun }
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun /* fill one string set into reply */
strset_fill_set(struct sk_buff * skb,const struct strset_info * set_info,u32 id,bool counts_only)372*4882a593Smuzhiyun static int strset_fill_set(struct sk_buff *skb,
373*4882a593Smuzhiyun const struct strset_info *set_info, u32 id,
374*4882a593Smuzhiyun bool counts_only)
375*4882a593Smuzhiyun {
376*4882a593Smuzhiyun struct nlattr *stringset_attr;
377*4882a593Smuzhiyun struct nlattr *strings_attr;
378*4882a593Smuzhiyun unsigned int i;
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun if (!set_info->per_dev && !set_info->strings)
381*4882a593Smuzhiyun return -EOPNOTSUPP;
382*4882a593Smuzhiyun if (set_info->count == 0)
383*4882a593Smuzhiyun return 0;
384*4882a593Smuzhiyun stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET);
385*4882a593Smuzhiyun if (!stringset_attr)
386*4882a593Smuzhiyun return -EMSGSIZE;
387*4882a593Smuzhiyun
388*4882a593Smuzhiyun if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) ||
389*4882a593Smuzhiyun nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count))
390*4882a593Smuzhiyun goto nla_put_failure;
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun if (!counts_only) {
393*4882a593Smuzhiyun strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS);
394*4882a593Smuzhiyun if (!strings_attr)
395*4882a593Smuzhiyun goto nla_put_failure;
396*4882a593Smuzhiyun for (i = 0; i < set_info->count; i++) {
397*4882a593Smuzhiyun if (strset_fill_string(skb, set_info, i) < 0)
398*4882a593Smuzhiyun goto nla_put_failure;
399*4882a593Smuzhiyun }
400*4882a593Smuzhiyun nla_nest_end(skb, strings_attr);
401*4882a593Smuzhiyun }
402*4882a593Smuzhiyun
403*4882a593Smuzhiyun nla_nest_end(skb, stringset_attr);
404*4882a593Smuzhiyun return 0;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun nla_put_failure:
407*4882a593Smuzhiyun nla_nest_cancel(skb, stringset_attr);
408*4882a593Smuzhiyun return -EMSGSIZE;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
strset_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)411*4882a593Smuzhiyun static int strset_fill_reply(struct sk_buff *skb,
412*4882a593Smuzhiyun const struct ethnl_req_info *req_base,
413*4882a593Smuzhiyun const struct ethnl_reply_data *reply_base)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
416*4882a593Smuzhiyun const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
417*4882a593Smuzhiyun struct nlattr *nest;
418*4882a593Smuzhiyun unsigned int i;
419*4882a593Smuzhiyun int ret;
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS);
422*4882a593Smuzhiyun if (!nest)
423*4882a593Smuzhiyun return -EMSGSIZE;
424*4882a593Smuzhiyun
425*4882a593Smuzhiyun for (i = 0; i < ETH_SS_COUNT; i++) {
426*4882a593Smuzhiyun if (strset_include(req_info, data, i)) {
427*4882a593Smuzhiyun ret = strset_fill_set(skb, &data->sets[i], i,
428*4882a593Smuzhiyun req_info->counts_only);
429*4882a593Smuzhiyun if (ret < 0)
430*4882a593Smuzhiyun goto nla_put_failure;
431*4882a593Smuzhiyun }
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun nla_nest_end(skb, nest);
435*4882a593Smuzhiyun return 0;
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun nla_put_failure:
438*4882a593Smuzhiyun nla_nest_cancel(skb, nest);
439*4882a593Smuzhiyun return ret;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun
442*4882a593Smuzhiyun const struct ethnl_request_ops ethnl_strset_request_ops = {
443*4882a593Smuzhiyun .request_cmd = ETHTOOL_MSG_STRSET_GET,
444*4882a593Smuzhiyun .reply_cmd = ETHTOOL_MSG_STRSET_GET_REPLY,
445*4882a593Smuzhiyun .hdr_attr = ETHTOOL_A_STRSET_HEADER,
446*4882a593Smuzhiyun .req_info_size = sizeof(struct strset_req_info),
447*4882a593Smuzhiyun .reply_data_size = sizeof(struct strset_reply_data),
448*4882a593Smuzhiyun .allow_nodev_do = true,
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun .parse_request = strset_parse_request,
451*4882a593Smuzhiyun .prepare_data = strset_prepare_data,
452*4882a593Smuzhiyun .reply_size = strset_reply_size,
453*4882a593Smuzhiyun .fill_reply = strset_fill_reply,
454*4882a593Smuzhiyun .cleanup_data = strset_cleanup_data,
455*4882a593Smuzhiyun };
456