xref: /OK3568_Linux_fs/kernel/net/ethtool/strset.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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