xref: /kernel/linux/linux-6.6/net/ethtool/strset.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ethtool.h>
462306a36Sopenharmony_ci#include <linux/phy.h>
562306a36Sopenharmony_ci#include "netlink.h"
662306a36Sopenharmony_ci#include "common.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_cistruct strset_info {
962306a36Sopenharmony_ci	bool per_dev;
1062306a36Sopenharmony_ci	bool free_strings;
1162306a36Sopenharmony_ci	unsigned int count;
1262306a36Sopenharmony_ci	const char (*strings)[ETH_GSTRING_LEN];
1362306a36Sopenharmony_ci};
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic const struct strset_info info_template[] = {
1662306a36Sopenharmony_ci	[ETH_SS_TEST] = {
1762306a36Sopenharmony_ci		.per_dev	= true,
1862306a36Sopenharmony_ci	},
1962306a36Sopenharmony_ci	[ETH_SS_STATS] = {
2062306a36Sopenharmony_ci		.per_dev	= true,
2162306a36Sopenharmony_ci	},
2262306a36Sopenharmony_ci	[ETH_SS_PRIV_FLAGS] = {
2362306a36Sopenharmony_ci		.per_dev	= true,
2462306a36Sopenharmony_ci	},
2562306a36Sopenharmony_ci	[ETH_SS_FEATURES] = {
2662306a36Sopenharmony_ci		.per_dev	= false,
2762306a36Sopenharmony_ci		.count		= ARRAY_SIZE(netdev_features_strings),
2862306a36Sopenharmony_ci		.strings	= netdev_features_strings,
2962306a36Sopenharmony_ci	},
3062306a36Sopenharmony_ci	[ETH_SS_RSS_HASH_FUNCS] = {
3162306a36Sopenharmony_ci		.per_dev	= false,
3262306a36Sopenharmony_ci		.count		= ARRAY_SIZE(rss_hash_func_strings),
3362306a36Sopenharmony_ci		.strings	= rss_hash_func_strings,
3462306a36Sopenharmony_ci	},
3562306a36Sopenharmony_ci	[ETH_SS_TUNABLES] = {
3662306a36Sopenharmony_ci		.per_dev	= false,
3762306a36Sopenharmony_ci		.count		= ARRAY_SIZE(tunable_strings),
3862306a36Sopenharmony_ci		.strings	= tunable_strings,
3962306a36Sopenharmony_ci	},
4062306a36Sopenharmony_ci	[ETH_SS_PHY_STATS] = {
4162306a36Sopenharmony_ci		.per_dev	= true,
4262306a36Sopenharmony_ci	},
4362306a36Sopenharmony_ci	[ETH_SS_PHY_TUNABLES] = {
4462306a36Sopenharmony_ci		.per_dev	= false,
4562306a36Sopenharmony_ci		.count		= ARRAY_SIZE(phy_tunable_strings),
4662306a36Sopenharmony_ci		.strings	= phy_tunable_strings,
4762306a36Sopenharmony_ci	},
4862306a36Sopenharmony_ci	[ETH_SS_LINK_MODES] = {
4962306a36Sopenharmony_ci		.per_dev	= false,
5062306a36Sopenharmony_ci		.count		= __ETHTOOL_LINK_MODE_MASK_NBITS,
5162306a36Sopenharmony_ci		.strings	= link_mode_names,
5262306a36Sopenharmony_ci	},
5362306a36Sopenharmony_ci	[ETH_SS_MSG_CLASSES] = {
5462306a36Sopenharmony_ci		.per_dev	= false,
5562306a36Sopenharmony_ci		.count		= NETIF_MSG_CLASS_COUNT,
5662306a36Sopenharmony_ci		.strings	= netif_msg_class_names,
5762306a36Sopenharmony_ci	},
5862306a36Sopenharmony_ci	[ETH_SS_WOL_MODES] = {
5962306a36Sopenharmony_ci		.per_dev	= false,
6062306a36Sopenharmony_ci		.count		= WOL_MODE_COUNT,
6162306a36Sopenharmony_ci		.strings	= wol_mode_names,
6262306a36Sopenharmony_ci	},
6362306a36Sopenharmony_ci	[ETH_SS_SOF_TIMESTAMPING] = {
6462306a36Sopenharmony_ci		.per_dev	= false,
6562306a36Sopenharmony_ci		.count		= __SOF_TIMESTAMPING_CNT,
6662306a36Sopenharmony_ci		.strings	= sof_timestamping_names,
6762306a36Sopenharmony_ci	},
6862306a36Sopenharmony_ci	[ETH_SS_TS_TX_TYPES] = {
6962306a36Sopenharmony_ci		.per_dev	= false,
7062306a36Sopenharmony_ci		.count		= __HWTSTAMP_TX_CNT,
7162306a36Sopenharmony_ci		.strings	= ts_tx_type_names,
7262306a36Sopenharmony_ci	},
7362306a36Sopenharmony_ci	[ETH_SS_TS_RX_FILTERS] = {
7462306a36Sopenharmony_ci		.per_dev	= false,
7562306a36Sopenharmony_ci		.count		= __HWTSTAMP_FILTER_CNT,
7662306a36Sopenharmony_ci		.strings	= ts_rx_filter_names,
7762306a36Sopenharmony_ci	},
7862306a36Sopenharmony_ci	[ETH_SS_UDP_TUNNEL_TYPES] = {
7962306a36Sopenharmony_ci		.per_dev	= false,
8062306a36Sopenharmony_ci		.count		= __ETHTOOL_UDP_TUNNEL_TYPE_CNT,
8162306a36Sopenharmony_ci		.strings	= udp_tunnel_type_names,
8262306a36Sopenharmony_ci	},
8362306a36Sopenharmony_ci	[ETH_SS_STATS_STD] = {
8462306a36Sopenharmony_ci		.per_dev	= false,
8562306a36Sopenharmony_ci		.count		= __ETHTOOL_STATS_CNT,
8662306a36Sopenharmony_ci		.strings	= stats_std_names,
8762306a36Sopenharmony_ci	},
8862306a36Sopenharmony_ci	[ETH_SS_STATS_ETH_PHY] = {
8962306a36Sopenharmony_ci		.per_dev	= false,
9062306a36Sopenharmony_ci		.count		= __ETHTOOL_A_STATS_ETH_PHY_CNT,
9162306a36Sopenharmony_ci		.strings	= stats_eth_phy_names,
9262306a36Sopenharmony_ci	},
9362306a36Sopenharmony_ci	[ETH_SS_STATS_ETH_MAC] = {
9462306a36Sopenharmony_ci		.per_dev	= false,
9562306a36Sopenharmony_ci		.count		= __ETHTOOL_A_STATS_ETH_MAC_CNT,
9662306a36Sopenharmony_ci		.strings	= stats_eth_mac_names,
9762306a36Sopenharmony_ci	},
9862306a36Sopenharmony_ci	[ETH_SS_STATS_ETH_CTRL] = {
9962306a36Sopenharmony_ci		.per_dev	= false,
10062306a36Sopenharmony_ci		.count		= __ETHTOOL_A_STATS_ETH_CTRL_CNT,
10162306a36Sopenharmony_ci		.strings	= stats_eth_ctrl_names,
10262306a36Sopenharmony_ci	},
10362306a36Sopenharmony_ci	[ETH_SS_STATS_RMON] = {
10462306a36Sopenharmony_ci		.per_dev	= false,
10562306a36Sopenharmony_ci		.count		= __ETHTOOL_A_STATS_RMON_CNT,
10662306a36Sopenharmony_ci		.strings	= stats_rmon_names,
10762306a36Sopenharmony_ci	},
10862306a36Sopenharmony_ci};
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct strset_req_info {
11162306a36Sopenharmony_ci	struct ethnl_req_info		base;
11262306a36Sopenharmony_ci	u32				req_ids;
11362306a36Sopenharmony_ci	bool				counts_only;
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define STRSET_REQINFO(__req_base) \
11762306a36Sopenharmony_ci	container_of(__req_base, struct strset_req_info, base)
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistruct strset_reply_data {
12062306a36Sopenharmony_ci	struct ethnl_reply_data		base;
12162306a36Sopenharmony_ci	struct strset_info		sets[ETH_SS_COUNT];
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define STRSET_REPDATA(__reply_base) \
12562306a36Sopenharmony_ci	container_of(__reply_base, struct strset_reply_data, base)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciconst struct nla_policy ethnl_strset_get_policy[] = {
12862306a36Sopenharmony_ci	[ETHTOOL_A_STRSET_HEADER]	=
12962306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
13062306a36Sopenharmony_ci	[ETHTOOL_A_STRSET_STRINGSETS]	= { .type = NLA_NESTED },
13162306a36Sopenharmony_ci	[ETHTOOL_A_STRSET_COUNTS_ONLY]	= { .type = NLA_FLAG },
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic const struct nla_policy get_stringset_policy[] = {
13562306a36Sopenharmony_ci	[ETHTOOL_A_STRINGSET_ID]	= { .type = NLA_U32 },
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci/**
13962306a36Sopenharmony_ci * strset_include() - test if a string set should be included in reply
14062306a36Sopenharmony_ci * @info: parsed client request
14162306a36Sopenharmony_ci * @data: pointer to request data structure
14262306a36Sopenharmony_ci * @id:   id of string set to check (ETH_SS_* constants)
14362306a36Sopenharmony_ci */
14462306a36Sopenharmony_cistatic bool strset_include(const struct strset_req_info *info,
14562306a36Sopenharmony_ci			   const struct strset_reply_data *data, u32 id)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	bool per_dev;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	BUILD_BUG_ON(ETH_SS_COUNT >= BITS_PER_BYTE * sizeof(info->req_ids));
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (info->req_ids)
15262306a36Sopenharmony_ci		return info->req_ids & (1U << id);
15362306a36Sopenharmony_ci	per_dev = data->sets[id].per_dev;
15462306a36Sopenharmony_ci	if (!per_dev && !data->sets[id].strings)
15562306a36Sopenharmony_ci		return false;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return data->base.dev ? per_dev : !per_dev;
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int strset_get_id(const struct nlattr *nest, u32 *val,
16162306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(get_stringset_policy)];
16462306a36Sopenharmony_ci	int ret;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(get_stringset_policy) - 1, nest,
16762306a36Sopenharmony_ci			       get_stringset_policy, extack);
16862306a36Sopenharmony_ci	if (ret < 0)
16962306a36Sopenharmony_ci		return ret;
17062306a36Sopenharmony_ci	if (NL_REQ_ATTR_CHECK(extack, nest, tb, ETHTOOL_A_STRINGSET_ID))
17162306a36Sopenharmony_ci		return -EINVAL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	*val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]);
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic const struct nla_policy strset_stringsets_policy[] = {
17862306a36Sopenharmony_ci	[ETHTOOL_A_STRINGSETS_STRINGSET]	= { .type = NLA_NESTED },
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int strset_parse_request(struct ethnl_req_info *req_base,
18262306a36Sopenharmony_ci				struct nlattr **tb,
18362306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct strset_req_info *req_info = STRSET_REQINFO(req_base);
18662306a36Sopenharmony_ci	struct nlattr *nest = tb[ETHTOOL_A_STRSET_STRINGSETS];
18762306a36Sopenharmony_ci	struct nlattr *attr;
18862306a36Sopenharmony_ci	int rem, ret;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!nest)
19162306a36Sopenharmony_ci		return 0;
19262306a36Sopenharmony_ci	ret = nla_validate_nested(nest,
19362306a36Sopenharmony_ci				  ARRAY_SIZE(strset_stringsets_policy) - 1,
19462306a36Sopenharmony_ci				  strset_stringsets_policy, extack);
19562306a36Sopenharmony_ci	if (ret < 0)
19662306a36Sopenharmony_ci		return ret;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	req_info->counts_only = tb[ETHTOOL_A_STRSET_COUNTS_ONLY];
19962306a36Sopenharmony_ci	nla_for_each_nested(attr, nest, rem) {
20062306a36Sopenharmony_ci		u32 id;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		if (WARN_ONCE(nla_type(attr) != ETHTOOL_A_STRINGSETS_STRINGSET,
20362306a36Sopenharmony_ci			      "unexpected attrtype %u in ETHTOOL_A_STRSET_STRINGSETS\n",
20462306a36Sopenharmony_ci			      nla_type(attr)))
20562306a36Sopenharmony_ci			return -EINVAL;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci		ret = strset_get_id(attr, &id, extack);
20862306a36Sopenharmony_ci		if (ret < 0)
20962306a36Sopenharmony_ci			return ret;
21062306a36Sopenharmony_ci		if (id >= ETH_SS_COUNT) {
21162306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, attr,
21262306a36Sopenharmony_ci					    "unknown string set id");
21362306a36Sopenharmony_ci			return -EOPNOTSUPP;
21462306a36Sopenharmony_ci		}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci		req_info->req_ids |= (1U << id);
21762306a36Sopenharmony_ci	}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void strset_cleanup_data(struct ethnl_reply_data *reply_base)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct strset_reply_data *data = STRSET_REPDATA(reply_base);
22562306a36Sopenharmony_ci	unsigned int i;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	for (i = 0; i < ETH_SS_COUNT; i++)
22862306a36Sopenharmony_ci		if (data->sets[i].free_strings) {
22962306a36Sopenharmony_ci			kfree(data->sets[i].strings);
23062306a36Sopenharmony_ci			data->sets[i].strings = NULL;
23162306a36Sopenharmony_ci			data->sets[i].free_strings = false;
23262306a36Sopenharmony_ci		}
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic int strset_prepare_set(struct strset_info *info, struct net_device *dev,
23662306a36Sopenharmony_ci			      unsigned int id, bool counts_only)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	const struct ethtool_phy_ops *phy_ops = ethtool_phy_ops;
23962306a36Sopenharmony_ci	const struct ethtool_ops *ops = dev->ethtool_ops;
24062306a36Sopenharmony_ci	void *strings;
24162306a36Sopenharmony_ci	int count, ret;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (id == ETH_SS_PHY_STATS && dev->phydev &&
24462306a36Sopenharmony_ci	    !ops->get_ethtool_phy_stats && phy_ops &&
24562306a36Sopenharmony_ci	    phy_ops->get_sset_count)
24662306a36Sopenharmony_ci		ret = phy_ops->get_sset_count(dev->phydev);
24762306a36Sopenharmony_ci	else if (ops->get_sset_count && ops->get_strings)
24862306a36Sopenharmony_ci		ret = ops->get_sset_count(dev, id);
24962306a36Sopenharmony_ci	else
25062306a36Sopenharmony_ci		ret = -EOPNOTSUPP;
25162306a36Sopenharmony_ci	if (ret <= 0) {
25262306a36Sopenharmony_ci		info->count = 0;
25362306a36Sopenharmony_ci		return 0;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	count = ret;
25762306a36Sopenharmony_ci	if (!counts_only) {
25862306a36Sopenharmony_ci		strings = kcalloc(count, ETH_GSTRING_LEN, GFP_KERNEL);
25962306a36Sopenharmony_ci		if (!strings)
26062306a36Sopenharmony_ci			return -ENOMEM;
26162306a36Sopenharmony_ci		if (id == ETH_SS_PHY_STATS && dev->phydev &&
26262306a36Sopenharmony_ci		    !ops->get_ethtool_phy_stats && phy_ops &&
26362306a36Sopenharmony_ci		    phy_ops->get_strings)
26462306a36Sopenharmony_ci			phy_ops->get_strings(dev->phydev, strings);
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			ops->get_strings(dev, id, strings);
26762306a36Sopenharmony_ci		info->strings = strings;
26862306a36Sopenharmony_ci		info->free_strings = true;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci	info->count = count;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	return 0;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_cistatic int strset_prepare_data(const struct ethnl_req_info *req_base,
27662306a36Sopenharmony_ci			       struct ethnl_reply_data *reply_base,
27762306a36Sopenharmony_ci			       const struct genl_info *info)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
28062306a36Sopenharmony_ci	struct strset_reply_data *data = STRSET_REPDATA(reply_base);
28162306a36Sopenharmony_ci	struct net_device *dev = reply_base->dev;
28262306a36Sopenharmony_ci	unsigned int i;
28362306a36Sopenharmony_ci	int ret;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	BUILD_BUG_ON(ARRAY_SIZE(info_template) != ETH_SS_COUNT);
28662306a36Sopenharmony_ci	memcpy(&data->sets, &info_template, sizeof(data->sets));
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (!dev) {
28962306a36Sopenharmony_ci		for (i = 0; i < ETH_SS_COUNT; i++) {
29062306a36Sopenharmony_ci			if ((req_info->req_ids & (1U << i)) &&
29162306a36Sopenharmony_ci			    data->sets[i].per_dev) {
29262306a36Sopenharmony_ci				if (info)
29362306a36Sopenharmony_ci					GENL_SET_ERR_MSG(info, "requested per device strings without dev");
29462306a36Sopenharmony_ci				return -EINVAL;
29562306a36Sopenharmony_ci			}
29662306a36Sopenharmony_ci		}
29762306a36Sopenharmony_ci		return 0;
29862306a36Sopenharmony_ci	}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	ret = ethnl_ops_begin(dev);
30162306a36Sopenharmony_ci	if (ret < 0)
30262306a36Sopenharmony_ci		goto err_strset;
30362306a36Sopenharmony_ci	for (i = 0; i < ETH_SS_COUNT; i++) {
30462306a36Sopenharmony_ci		if (!strset_include(req_info, data, i) ||
30562306a36Sopenharmony_ci		    !data->sets[i].per_dev)
30662306a36Sopenharmony_ci			continue;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci		ret = strset_prepare_set(&data->sets[i], dev, i,
30962306a36Sopenharmony_ci					 req_info->counts_only);
31062306a36Sopenharmony_ci		if (ret < 0)
31162306a36Sopenharmony_ci			goto err_ops;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	ethnl_ops_complete(dev);
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	return 0;
31662306a36Sopenharmony_cierr_ops:
31762306a36Sopenharmony_ci	ethnl_ops_complete(dev);
31862306a36Sopenharmony_cierr_strset:
31962306a36Sopenharmony_ci	strset_cleanup_data(reply_base);
32062306a36Sopenharmony_ci	return ret;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci/* calculate size of ETHTOOL_A_STRSET_STRINGSET nest for one string set */
32462306a36Sopenharmony_cistatic int strset_set_size(const struct strset_info *info, bool counts_only)
32562306a36Sopenharmony_ci{
32662306a36Sopenharmony_ci	unsigned int len = 0;
32762306a36Sopenharmony_ci	unsigned int i;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	if (info->count == 0)
33062306a36Sopenharmony_ci		return 0;
33162306a36Sopenharmony_ci	if (counts_only)
33262306a36Sopenharmony_ci		return nla_total_size(2 * nla_total_size(sizeof(u32)));
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	for (i = 0; i < info->count; i++) {
33562306a36Sopenharmony_ci		const char *str = info->strings[i];
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		/* ETHTOOL_A_STRING_INDEX, ETHTOOL_A_STRING_VALUE, nest */
33862306a36Sopenharmony_ci		len += nla_total_size(nla_total_size(sizeof(u32)) +
33962306a36Sopenharmony_ci				      ethnl_strz_size(str));
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci	/* ETHTOOL_A_STRINGSET_ID, ETHTOOL_A_STRINGSET_COUNT */
34262306a36Sopenharmony_ci	len = 2 * nla_total_size(sizeof(u32)) + nla_total_size(len);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	return nla_total_size(len);
34562306a36Sopenharmony_ci}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_cistatic int strset_reply_size(const struct ethnl_req_info *req_base,
34862306a36Sopenharmony_ci			     const struct ethnl_reply_data *reply_base)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
35162306a36Sopenharmony_ci	const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
35262306a36Sopenharmony_ci	unsigned int i;
35362306a36Sopenharmony_ci	int len = 0;
35462306a36Sopenharmony_ci	int ret;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	len += nla_total_size(0); /* ETHTOOL_A_STRSET_STRINGSETS */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	for (i = 0; i < ETH_SS_COUNT; i++) {
35962306a36Sopenharmony_ci		const struct strset_info *set_info = &data->sets[i];
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		if (!strset_include(req_info, data, i))
36262306a36Sopenharmony_ci			continue;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci		ret = strset_set_size(set_info, req_info->counts_only);
36562306a36Sopenharmony_ci		if (ret < 0)
36662306a36Sopenharmony_ci			return ret;
36762306a36Sopenharmony_ci		len += ret;
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	return len;
37162306a36Sopenharmony_ci}
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci/* fill one string into reply */
37462306a36Sopenharmony_cistatic int strset_fill_string(struct sk_buff *skb,
37562306a36Sopenharmony_ci			      const struct strset_info *set_info, u32 idx)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	struct nlattr *string_attr;
37862306a36Sopenharmony_ci	const char *value;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	value = set_info->strings[idx];
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	string_attr = nla_nest_start(skb, ETHTOOL_A_STRINGS_STRING);
38362306a36Sopenharmony_ci	if (!string_attr)
38462306a36Sopenharmony_ci		return -EMSGSIZE;
38562306a36Sopenharmony_ci	if (nla_put_u32(skb, ETHTOOL_A_STRING_INDEX, idx) ||
38662306a36Sopenharmony_ci	    ethnl_put_strz(skb, ETHTOOL_A_STRING_VALUE, value))
38762306a36Sopenharmony_ci		goto nla_put_failure;
38862306a36Sopenharmony_ci	nla_nest_end(skb, string_attr);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	return 0;
39162306a36Sopenharmony_cinla_put_failure:
39262306a36Sopenharmony_ci	nla_nest_cancel(skb, string_attr);
39362306a36Sopenharmony_ci	return -EMSGSIZE;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci/* fill one string set into reply */
39762306a36Sopenharmony_cistatic int strset_fill_set(struct sk_buff *skb,
39862306a36Sopenharmony_ci			   const struct strset_info *set_info, u32 id,
39962306a36Sopenharmony_ci			   bool counts_only)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	struct nlattr *stringset_attr;
40262306a36Sopenharmony_ci	struct nlattr *strings_attr;
40362306a36Sopenharmony_ci	unsigned int i;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	if (!set_info->per_dev && !set_info->strings)
40662306a36Sopenharmony_ci		return -EOPNOTSUPP;
40762306a36Sopenharmony_ci	if (set_info->count == 0)
40862306a36Sopenharmony_ci		return 0;
40962306a36Sopenharmony_ci	stringset_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSETS_STRINGSET);
41062306a36Sopenharmony_ci	if (!stringset_attr)
41162306a36Sopenharmony_ci		return -EMSGSIZE;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if (nla_put_u32(skb, ETHTOOL_A_STRINGSET_ID, id) ||
41462306a36Sopenharmony_ci	    nla_put_u32(skb, ETHTOOL_A_STRINGSET_COUNT, set_info->count))
41562306a36Sopenharmony_ci		goto nla_put_failure;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (!counts_only) {
41862306a36Sopenharmony_ci		strings_attr = nla_nest_start(skb, ETHTOOL_A_STRINGSET_STRINGS);
41962306a36Sopenharmony_ci		if (!strings_attr)
42062306a36Sopenharmony_ci			goto nla_put_failure;
42162306a36Sopenharmony_ci		for (i = 0; i < set_info->count; i++) {
42262306a36Sopenharmony_ci			if (strset_fill_string(skb, set_info, i) < 0)
42362306a36Sopenharmony_ci				goto nla_put_failure;
42462306a36Sopenharmony_ci		}
42562306a36Sopenharmony_ci		nla_nest_end(skb, strings_attr);
42662306a36Sopenharmony_ci	}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	nla_nest_end(skb, stringset_attr);
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cinla_put_failure:
43262306a36Sopenharmony_ci	nla_nest_cancel(skb, stringset_attr);
43362306a36Sopenharmony_ci	return -EMSGSIZE;
43462306a36Sopenharmony_ci}
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic int strset_fill_reply(struct sk_buff *skb,
43762306a36Sopenharmony_ci			     const struct ethnl_req_info *req_base,
43862306a36Sopenharmony_ci			     const struct ethnl_reply_data *reply_base)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	const struct strset_req_info *req_info = STRSET_REQINFO(req_base);
44162306a36Sopenharmony_ci	const struct strset_reply_data *data = STRSET_REPDATA(reply_base);
44262306a36Sopenharmony_ci	struct nlattr *nest;
44362306a36Sopenharmony_ci	unsigned int i;
44462306a36Sopenharmony_ci	int ret;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	nest = nla_nest_start(skb, ETHTOOL_A_STRSET_STRINGSETS);
44762306a36Sopenharmony_ci	if (!nest)
44862306a36Sopenharmony_ci		return -EMSGSIZE;
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	for (i = 0; i < ETH_SS_COUNT; i++) {
45162306a36Sopenharmony_ci		if (strset_include(req_info, data, i)) {
45262306a36Sopenharmony_ci			ret = strset_fill_set(skb, &data->sets[i], i,
45362306a36Sopenharmony_ci					      req_info->counts_only);
45462306a36Sopenharmony_ci			if (ret < 0)
45562306a36Sopenharmony_ci				goto nla_put_failure;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	nla_nest_end(skb, nest);
46062306a36Sopenharmony_ci	return 0;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cinla_put_failure:
46362306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
46462306a36Sopenharmony_ci	return ret;
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_strset_request_ops = {
46862306a36Sopenharmony_ci	.request_cmd		= ETHTOOL_MSG_STRSET_GET,
46962306a36Sopenharmony_ci	.reply_cmd		= ETHTOOL_MSG_STRSET_GET_REPLY,
47062306a36Sopenharmony_ci	.hdr_attr		= ETHTOOL_A_STRSET_HEADER,
47162306a36Sopenharmony_ci	.req_info_size		= sizeof(struct strset_req_info),
47262306a36Sopenharmony_ci	.reply_data_size	= sizeof(struct strset_reply_data),
47362306a36Sopenharmony_ci	.allow_nodev_do		= true,
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	.parse_request		= strset_parse_request,
47662306a36Sopenharmony_ci	.prepare_data		= strset_prepare_data,
47762306a36Sopenharmony_ci	.reply_size		= strset_reply_size,
47862306a36Sopenharmony_ci	.fill_reply		= strset_fill_reply,
47962306a36Sopenharmony_ci	.cleanup_data		= strset_cleanup_data,
48062306a36Sopenharmony_ci};
481