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

fec.c (8360B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2
      3#include "netlink.h"
      4#include "common.h"
      5#include "bitset.h"
      6
      7struct fec_req_info {
      8	struct ethnl_req_info		base;
      9};
     10
     11struct fec_reply_data {
     12	struct ethnl_reply_data		base;
     13	__ETHTOOL_DECLARE_LINK_MODE_MASK(fec_link_modes);
     14	u32 active_fec;
     15	u8 fec_auto;
     16	struct fec_stat_grp {
     17		u64 stats[1 + ETHTOOL_MAX_LANES];
     18		u8 cnt;
     19	} corr, uncorr, corr_bits;
     20};
     21
     22#define FEC_REPDATA(__reply_base) \
     23	container_of(__reply_base, struct fec_reply_data, base)
     24
     25#define ETHTOOL_FEC_MASK	((ETHTOOL_FEC_LLRS << 1) - 1)
     26
     27const struct nla_policy ethnl_fec_get_policy[ETHTOOL_A_FEC_HEADER + 1] = {
     28	[ETHTOOL_A_FEC_HEADER]	= NLA_POLICY_NESTED(ethnl_header_policy_stats),
     29};
     30
     31static void
     32ethtool_fec_to_link_modes(u32 fec, unsigned long *link_modes, u8 *fec_auto)
     33{
     34	if (fec_auto)
     35		*fec_auto = !!(fec & ETHTOOL_FEC_AUTO);
     36
     37	if (fec & ETHTOOL_FEC_OFF)
     38		__set_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes);
     39	if (fec & ETHTOOL_FEC_RS)
     40		__set_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes);
     41	if (fec & ETHTOOL_FEC_BASER)
     42		__set_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes);
     43	if (fec & ETHTOOL_FEC_LLRS)
     44		__set_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes);
     45}
     46
     47static int
     48ethtool_link_modes_to_fecparam(struct ethtool_fecparam *fec,
     49			       unsigned long *link_modes, u8 fec_auto)
     50{
     51	memset(fec, 0, sizeof(*fec));
     52
     53	if (fec_auto)
     54		fec->fec |= ETHTOOL_FEC_AUTO;
     55
     56	if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_NONE_BIT, link_modes))
     57		fec->fec |= ETHTOOL_FEC_OFF;
     58	if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_RS_BIT, link_modes))
     59		fec->fec |= ETHTOOL_FEC_RS;
     60	if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_BASER_BIT, link_modes))
     61		fec->fec |= ETHTOOL_FEC_BASER;
     62	if (__test_and_clear_bit(ETHTOOL_LINK_MODE_FEC_LLRS_BIT, link_modes))
     63		fec->fec |= ETHTOOL_FEC_LLRS;
     64
     65	if (!bitmap_empty(link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS))
     66		return -EINVAL;
     67
     68	return 0;
     69}
     70
     71static void
     72fec_stats_recalc(struct fec_stat_grp *grp, struct ethtool_fec_stat *stats)
     73{
     74	int i;
     75
     76	if (stats->lanes[0] == ETHTOOL_STAT_NOT_SET) {
     77		grp->stats[0] = stats->total;
     78		grp->cnt = stats->total != ETHTOOL_STAT_NOT_SET;
     79		return;
     80	}
     81
     82	grp->cnt = 1;
     83	grp->stats[0] = 0;
     84	for (i = 0; i < ETHTOOL_MAX_LANES; i++) {
     85		if (stats->lanes[i] == ETHTOOL_STAT_NOT_SET)
     86			break;
     87
     88		grp->stats[0] += stats->lanes[i];
     89		grp->stats[grp->cnt++] = stats->lanes[i];
     90	}
     91}
     92
     93static int fec_prepare_data(const struct ethnl_req_info *req_base,
     94			    struct ethnl_reply_data *reply_base,
     95			    struct genl_info *info)
     96{
     97	__ETHTOOL_DECLARE_LINK_MODE_MASK(active_fec_modes) = {};
     98	struct fec_reply_data *data = FEC_REPDATA(reply_base);
     99	struct net_device *dev = reply_base->dev;
    100	struct ethtool_fecparam fec = {};
    101	int ret;
    102
    103	if (!dev->ethtool_ops->get_fecparam)
    104		return -EOPNOTSUPP;
    105	ret = ethnl_ops_begin(dev);
    106	if (ret < 0)
    107		return ret;
    108	ret = dev->ethtool_ops->get_fecparam(dev, &fec);
    109	if (ret)
    110		goto out_complete;
    111	if (req_base->flags & ETHTOOL_FLAG_STATS &&
    112	    dev->ethtool_ops->get_fec_stats) {
    113		struct ethtool_fec_stats stats;
    114
    115		ethtool_stats_init((u64 *)&stats, sizeof(stats) / 8);
    116		dev->ethtool_ops->get_fec_stats(dev, &stats);
    117
    118		fec_stats_recalc(&data->corr, &stats.corrected_blocks);
    119		fec_stats_recalc(&data->uncorr, &stats.uncorrectable_blocks);
    120		fec_stats_recalc(&data->corr_bits, &stats.corrected_bits);
    121	}
    122
    123	WARN_ON_ONCE(fec.reserved);
    124
    125	ethtool_fec_to_link_modes(fec.fec, data->fec_link_modes,
    126				  &data->fec_auto);
    127
    128	ethtool_fec_to_link_modes(fec.active_fec, active_fec_modes, NULL);
    129	data->active_fec = find_first_bit(active_fec_modes,
    130					  __ETHTOOL_LINK_MODE_MASK_NBITS);
    131	/* Don't report attr if no FEC mode set. Note that
    132	 * ethtool_fecparam_to_link_modes() ignores NONE and AUTO.
    133	 */
    134	if (data->active_fec == __ETHTOOL_LINK_MODE_MASK_NBITS)
    135		data->active_fec = 0;
    136
    137out_complete:
    138	ethnl_ops_complete(dev);
    139	return ret;
    140}
    141
    142static int fec_reply_size(const struct ethnl_req_info *req_base,
    143			  const struct ethnl_reply_data *reply_base)
    144{
    145	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
    146	const struct fec_reply_data *data = FEC_REPDATA(reply_base);
    147	int len = 0;
    148	int ret;
    149
    150	ret = ethnl_bitset_size(data->fec_link_modes, NULL,
    151				__ETHTOOL_LINK_MODE_MASK_NBITS,
    152				link_mode_names, compact);
    153	if (ret < 0)
    154		return ret;
    155	len += ret;
    156
    157	len += nla_total_size(sizeof(u8)) +	/* _FEC_AUTO */
    158	       nla_total_size(sizeof(u32));	/* _FEC_ACTIVE */
    159
    160	if (req_base->flags & ETHTOOL_FLAG_STATS)
    161		len += 3 * nla_total_size_64bit(sizeof(u64) *
    162						(1 + ETHTOOL_MAX_LANES));
    163
    164	return len;
    165}
    166
    167static int fec_put_stats(struct sk_buff *skb, const struct fec_reply_data *data)
    168{
    169	struct nlattr *nest;
    170
    171	nest = nla_nest_start(skb, ETHTOOL_A_FEC_STATS);
    172	if (!nest)
    173		return -EMSGSIZE;
    174
    175	if (nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_CORRECTED,
    176			  sizeof(u64) * data->corr.cnt,
    177			  data->corr.stats, ETHTOOL_A_FEC_STAT_PAD) ||
    178	    nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_UNCORR,
    179			  sizeof(u64) * data->uncorr.cnt,
    180			  data->uncorr.stats, ETHTOOL_A_FEC_STAT_PAD) ||
    181	    nla_put_64bit(skb, ETHTOOL_A_FEC_STAT_CORR_BITS,
    182			  sizeof(u64) * data->corr_bits.cnt,
    183			  data->corr_bits.stats, ETHTOOL_A_FEC_STAT_PAD))
    184		goto err_cancel;
    185
    186	nla_nest_end(skb, nest);
    187	return 0;
    188
    189err_cancel:
    190	nla_nest_cancel(skb, nest);
    191	return -EMSGSIZE;
    192}
    193
    194static int fec_fill_reply(struct sk_buff *skb,
    195			  const struct ethnl_req_info *req_base,
    196			  const struct ethnl_reply_data *reply_base)
    197{
    198	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
    199	const struct fec_reply_data *data = FEC_REPDATA(reply_base);
    200	int ret;
    201
    202	ret = ethnl_put_bitset(skb, ETHTOOL_A_FEC_MODES,
    203			       data->fec_link_modes, NULL,
    204			       __ETHTOOL_LINK_MODE_MASK_NBITS,
    205			       link_mode_names, compact);
    206	if (ret < 0)
    207		return ret;
    208
    209	if (nla_put_u8(skb, ETHTOOL_A_FEC_AUTO, data->fec_auto) ||
    210	    (data->active_fec &&
    211	     nla_put_u32(skb, ETHTOOL_A_FEC_ACTIVE, data->active_fec)))
    212		return -EMSGSIZE;
    213
    214	if (req_base->flags & ETHTOOL_FLAG_STATS && fec_put_stats(skb, data))
    215		return -EMSGSIZE;
    216
    217	return 0;
    218}
    219
    220const struct ethnl_request_ops ethnl_fec_request_ops = {
    221	.request_cmd		= ETHTOOL_MSG_FEC_GET,
    222	.reply_cmd		= ETHTOOL_MSG_FEC_GET_REPLY,
    223	.hdr_attr		= ETHTOOL_A_FEC_HEADER,
    224	.req_info_size		= sizeof(struct fec_req_info),
    225	.reply_data_size	= sizeof(struct fec_reply_data),
    226
    227	.prepare_data		= fec_prepare_data,
    228	.reply_size		= fec_reply_size,
    229	.fill_reply		= fec_fill_reply,
    230};
    231
    232/* FEC_SET */
    233
    234const struct nla_policy ethnl_fec_set_policy[ETHTOOL_A_FEC_AUTO + 1] = {
    235	[ETHTOOL_A_FEC_HEADER]	= NLA_POLICY_NESTED(ethnl_header_policy),
    236	[ETHTOOL_A_FEC_MODES]	= { .type = NLA_NESTED },
    237	[ETHTOOL_A_FEC_AUTO]	= NLA_POLICY_MAX(NLA_U8, 1),
    238};
    239
    240int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info)
    241{
    242	__ETHTOOL_DECLARE_LINK_MODE_MASK(fec_link_modes) = {};
    243	struct ethnl_req_info req_info = {};
    244	struct nlattr **tb = info->attrs;
    245	struct ethtool_fecparam fec = {};
    246	const struct ethtool_ops *ops;
    247	struct net_device *dev;
    248	bool mod = false;
    249	u8 fec_auto;
    250	int ret;
    251
    252	ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_FEC_HEADER],
    253					 genl_info_net(info), info->extack,
    254					 true);
    255	if (ret < 0)
    256		return ret;
    257	dev = req_info.dev;
    258	ops = dev->ethtool_ops;
    259	ret = -EOPNOTSUPP;
    260	if (!ops->get_fecparam || !ops->set_fecparam)
    261		goto out_dev;
    262
    263	rtnl_lock();
    264	ret = ethnl_ops_begin(dev);
    265	if (ret < 0)
    266		goto out_rtnl;
    267	ret = ops->get_fecparam(dev, &fec);
    268	if (ret < 0)
    269		goto out_ops;
    270
    271	ethtool_fec_to_link_modes(fec.fec, fec_link_modes, &fec_auto);
    272
    273	ret = ethnl_update_bitset(fec_link_modes,
    274				  __ETHTOOL_LINK_MODE_MASK_NBITS,
    275				  tb[ETHTOOL_A_FEC_MODES],
    276				  link_mode_names, info->extack, &mod);
    277	if (ret < 0)
    278		goto out_ops;
    279	ethnl_update_u8(&fec_auto, tb[ETHTOOL_A_FEC_AUTO], &mod);
    280
    281	ret = 0;
    282	if (!mod)
    283		goto out_ops;
    284
    285	ret = ethtool_link_modes_to_fecparam(&fec, fec_link_modes, fec_auto);
    286	if (ret) {
    287		NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES],
    288				    "invalid FEC modes requested");
    289		goto out_ops;
    290	}
    291	if (!fec.fec) {
    292		ret = -EINVAL;
    293		NL_SET_ERR_MSG_ATTR(info->extack, tb[ETHTOOL_A_FEC_MODES],
    294				    "no FEC modes set");
    295		goto out_ops;
    296	}
    297
    298	ret = dev->ethtool_ops->set_fecparam(dev, &fec);
    299	if (ret < 0)
    300		goto out_ops;
    301	ethtool_notify(dev, ETHTOOL_MSG_FEC_NTF, NULL);
    302
    303out_ops:
    304	ethnl_ops_complete(dev);
    305out_rtnl:
    306	rtnl_unlock();
    307out_dev:
    308	ethnl_parse_header_dev_put(&req_info);
    309	return ret;
    310}