cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

strset.c (12113B)


      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
      8struct strset_info {
      9	bool per_dev;
     10	bool free_strings;
     11	unsigned int count;
     12	const char (*strings)[ETH_GSTRING_LEN];
     13};
     14
     15static 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
    110struct 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
    119struct 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
    127const 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
    134static 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 */
    144static 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
    160static 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 (!tb[ETHTOOL_A_STRINGSET_ID])
    171		return -EINVAL;
    172
    173	*val = nla_get_u32(tb[ETHTOOL_A_STRINGSET_ID]);
    174	return 0;
    175}
    176
    177static const struct nla_policy strset_stringsets_policy[] = {
    178	[ETHTOOL_A_STRINGSETS_STRINGSET]	= { .type = NLA_NESTED },
    179};
    180
    181static 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
    222static 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
    235static 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
    275static int strset_prepare_data(const struct ethnl_req_info *req_base,
    276			       struct ethnl_reply_data *reply_base,
    277			       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;
    316err_ops:
    317	ethnl_ops_complete(dev);
    318err_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 */
    324static 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
    347static 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 */
    374static 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;
    391nla_put_failure:
    392	nla_nest_cancel(skb, string_attr);
    393	return -EMSGSIZE;
    394}
    395
    396/* fill one string set into reply */
    397static 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
    431nla_put_failure:
    432	nla_nest_cancel(skb, stringset_attr);
    433	return -EMSGSIZE;
    434}
    435
    436static 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
    462nla_put_failure:
    463	nla_nest_cancel(skb, nest);
    464	return ret;
    465}
    466
    467const 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};