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

channels.c (7023B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include <net/xdp_sock_drv.h>
      4
      5#include "netlink.h"
      6#include "common.h"
      7
      8struct channels_req_info {
      9	struct ethnl_req_info		base;
     10};
     11
     12struct channels_reply_data {
     13	struct ethnl_reply_data		base;
     14	struct ethtool_channels		channels;
     15};
     16
     17#define CHANNELS_REPDATA(__reply_base) \
     18	container_of(__reply_base, struct channels_reply_data, base)
     19
     20const struct nla_policy ethnl_channels_get_policy[] = {
     21	[ETHTOOL_A_CHANNELS_HEADER]		=
     22		NLA_POLICY_NESTED(ethnl_header_policy),
     23};
     24
     25static int channels_prepare_data(const struct ethnl_req_info *req_base,
     26				 struct ethnl_reply_data *reply_base,
     27				 struct genl_info *info)
     28{
     29	struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
     30	struct net_device *dev = reply_base->dev;
     31	int ret;
     32
     33	if (!dev->ethtool_ops->get_channels)
     34		return -EOPNOTSUPP;
     35	ret = ethnl_ops_begin(dev);
     36	if (ret < 0)
     37		return ret;
     38	dev->ethtool_ops->get_channels(dev, &data->channels);
     39	ethnl_ops_complete(dev);
     40
     41	return 0;
     42}
     43
     44static int channels_reply_size(const struct ethnl_req_info *req_base,
     45			       const struct ethnl_reply_data *reply_base)
     46{
     47	return nla_total_size(sizeof(u32)) +	/* _CHANNELS_RX_MAX */
     48	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_TX_MAX */
     49	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_OTHER_MAX */
     50	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_COMBINED_MAX */
     51	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_RX_COUNT */
     52	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_TX_COUNT */
     53	       nla_total_size(sizeof(u32)) +	/* _CHANNELS_OTHER_COUNT */
     54	       nla_total_size(sizeof(u32));	/* _CHANNELS_COMBINED_COUNT */
     55}
     56
     57static int channels_fill_reply(struct sk_buff *skb,
     58			       const struct ethnl_req_info *req_base,
     59			       const struct ethnl_reply_data *reply_base)
     60{
     61	const struct channels_reply_data *data = CHANNELS_REPDATA(reply_base);
     62	const struct ethtool_channels *channels = &data->channels;
     63
     64	if ((channels->max_rx &&
     65	     (nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_MAX,
     66			  channels->max_rx) ||
     67	      nla_put_u32(skb, ETHTOOL_A_CHANNELS_RX_COUNT,
     68			  channels->rx_count))) ||
     69	    (channels->max_tx &&
     70	     (nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_MAX,
     71			  channels->max_tx) ||
     72	      nla_put_u32(skb, ETHTOOL_A_CHANNELS_TX_COUNT,
     73			  channels->tx_count))) ||
     74	    (channels->max_other &&
     75	     (nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_MAX,
     76			  channels->max_other) ||
     77	      nla_put_u32(skb, ETHTOOL_A_CHANNELS_OTHER_COUNT,
     78			  channels->other_count))) ||
     79	    (channels->max_combined &&
     80	     (nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_MAX,
     81			  channels->max_combined) ||
     82	      nla_put_u32(skb, ETHTOOL_A_CHANNELS_COMBINED_COUNT,
     83			  channels->combined_count))))
     84		return -EMSGSIZE;
     85
     86	return 0;
     87}
     88
     89const struct ethnl_request_ops ethnl_channels_request_ops = {
     90	.request_cmd		= ETHTOOL_MSG_CHANNELS_GET,
     91	.reply_cmd		= ETHTOOL_MSG_CHANNELS_GET_REPLY,
     92	.hdr_attr		= ETHTOOL_A_CHANNELS_HEADER,
     93	.req_info_size		= sizeof(struct channels_req_info),
     94	.reply_data_size	= sizeof(struct channels_reply_data),
     95
     96	.prepare_data		= channels_prepare_data,
     97	.reply_size		= channels_reply_size,
     98	.fill_reply		= channels_fill_reply,
     99};
    100
    101/* CHANNELS_SET */
    102
    103const struct nla_policy ethnl_channels_set_policy[] = {
    104	[ETHTOOL_A_CHANNELS_HEADER]		=
    105		NLA_POLICY_NESTED(ethnl_header_policy),
    106	[ETHTOOL_A_CHANNELS_RX_COUNT]		= { .type = NLA_U32 },
    107	[ETHTOOL_A_CHANNELS_TX_COUNT]		= { .type = NLA_U32 },
    108	[ETHTOOL_A_CHANNELS_OTHER_COUNT]	= { .type = NLA_U32 },
    109	[ETHTOOL_A_CHANNELS_COMBINED_COUNT]	= { .type = NLA_U32 },
    110};
    111
    112int ethnl_set_channels(struct sk_buff *skb, struct genl_info *info)
    113{
    114	unsigned int from_channel, old_total, i;
    115	bool mod = false, mod_combined = false;
    116	struct ethtool_channels channels = {};
    117	struct ethnl_req_info req_info = {};
    118	struct nlattr **tb = info->attrs;
    119	u32 err_attr, max_rx_in_use = 0;
    120	const struct ethtool_ops *ops;
    121	struct net_device *dev;
    122	int ret;
    123
    124	ret = ethnl_parse_header_dev_get(&req_info,
    125					 tb[ETHTOOL_A_CHANNELS_HEADER],
    126					 genl_info_net(info), info->extack,
    127					 true);
    128	if (ret < 0)
    129		return ret;
    130	dev = req_info.dev;
    131	ops = dev->ethtool_ops;
    132	ret = -EOPNOTSUPP;
    133	if (!ops->get_channels || !ops->set_channels)
    134		goto out_dev;
    135
    136	rtnl_lock();
    137	ret = ethnl_ops_begin(dev);
    138	if (ret < 0)
    139		goto out_rtnl;
    140	ops->get_channels(dev, &channels);
    141	old_total = channels.combined_count +
    142		    max(channels.rx_count, channels.tx_count);
    143
    144	ethnl_update_u32(&channels.rx_count, tb[ETHTOOL_A_CHANNELS_RX_COUNT],
    145			 &mod);
    146	ethnl_update_u32(&channels.tx_count, tb[ETHTOOL_A_CHANNELS_TX_COUNT],
    147			 &mod);
    148	ethnl_update_u32(&channels.other_count,
    149			 tb[ETHTOOL_A_CHANNELS_OTHER_COUNT], &mod);
    150	ethnl_update_u32(&channels.combined_count,
    151			 tb[ETHTOOL_A_CHANNELS_COMBINED_COUNT], &mod_combined);
    152	mod |= mod_combined;
    153	ret = 0;
    154	if (!mod)
    155		goto out_ops;
    156
    157	/* ensure new channel counts are within limits */
    158	if (channels.rx_count > channels.max_rx)
    159		err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
    160	else if (channels.tx_count > channels.max_tx)
    161		err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
    162	else if (channels.other_count > channels.max_other)
    163		err_attr = ETHTOOL_A_CHANNELS_OTHER_COUNT;
    164	else if (channels.combined_count > channels.max_combined)
    165		err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
    166	else
    167		err_attr = 0;
    168	if (err_attr) {
    169		ret = -EINVAL;
    170		NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
    171				    "requested channel count exceeds maximum");
    172		goto out_ops;
    173	}
    174
    175	/* ensure there is at least one RX and one TX channel */
    176	if (!channels.combined_count && !channels.rx_count)
    177		err_attr = ETHTOOL_A_CHANNELS_RX_COUNT;
    178	else if (!channels.combined_count && !channels.tx_count)
    179		err_attr = ETHTOOL_A_CHANNELS_TX_COUNT;
    180	else
    181		err_attr = 0;
    182	if (err_attr) {
    183		if (mod_combined)
    184			err_attr = ETHTOOL_A_CHANNELS_COMBINED_COUNT;
    185		ret = -EINVAL;
    186		NL_SET_ERR_MSG_ATTR(info->extack, tb[err_attr],
    187				    "requested channel counts would result in no RX or TX channel being configured");
    188		goto out_ops;
    189	}
    190
    191	/* ensure the new Rx count fits within the configured Rx flow
    192	 * indirection table settings
    193	 */
    194	if (netif_is_rxfh_configured(dev) &&
    195	    !ethtool_get_max_rxfh_channel(dev, &max_rx_in_use) &&
    196	    (channels.combined_count + channels.rx_count) <= max_rx_in_use) {
    197		ret = -EINVAL;
    198		GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing indirection table settings");
    199		goto out_ops;
    200	}
    201
    202	/* Disabling channels, query zero-copy AF_XDP sockets */
    203	from_channel = channels.combined_count +
    204		       min(channels.rx_count, channels.tx_count);
    205	for (i = from_channel; i < old_total; i++)
    206		if (xsk_get_pool_from_qid(dev, i)) {
    207			ret = -EINVAL;
    208			GENL_SET_ERR_MSG(info, "requested channel counts are too low for existing zerocopy AF_XDP sockets");
    209			goto out_ops;
    210		}
    211
    212	ret = dev->ethtool_ops->set_channels(dev, &channels);
    213	if (ret < 0)
    214		goto out_ops;
    215	ethtool_notify(dev, ETHTOOL_MSG_CHANNELS_NTF, NULL);
    216
    217out_ops:
    218	ethnl_ops_complete(dev);
    219out_rtnl:
    220	rtnl_unlock();
    221out_dev:
    222	ethnl_parse_header_dev_put(&req_info);
    223	return ret;
    224}