xref: /kernel/linux/linux-6.6/net/ethtool/rings.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "netlink.h"
462306a36Sopenharmony_ci#include "common.h"
562306a36Sopenharmony_ci
662306a36Sopenharmony_cistruct rings_req_info {
762306a36Sopenharmony_ci	struct ethnl_req_info		base;
862306a36Sopenharmony_ci};
962306a36Sopenharmony_ci
1062306a36Sopenharmony_cistruct rings_reply_data {
1162306a36Sopenharmony_ci	struct ethnl_reply_data		base;
1262306a36Sopenharmony_ci	struct ethtool_ringparam	ringparam;
1362306a36Sopenharmony_ci	struct kernel_ethtool_ringparam	kernel_ringparam;
1462306a36Sopenharmony_ci	u32				supported_ring_params;
1562306a36Sopenharmony_ci};
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define RINGS_REPDATA(__reply_base) \
1862306a36Sopenharmony_ci	container_of(__reply_base, struct rings_reply_data, base)
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciconst struct nla_policy ethnl_rings_get_policy[] = {
2162306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_HEADER]		=
2262306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
2362306a36Sopenharmony_ci};
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int rings_prepare_data(const struct ethnl_req_info *req_base,
2662306a36Sopenharmony_ci			      struct ethnl_reply_data *reply_base,
2762306a36Sopenharmony_ci			      const struct genl_info *info)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct rings_reply_data *data = RINGS_REPDATA(reply_base);
3062306a36Sopenharmony_ci	struct net_device *dev = reply_base->dev;
3162306a36Sopenharmony_ci	int ret;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (!dev->ethtool_ops->get_ringparam)
3462306a36Sopenharmony_ci		return -EOPNOTSUPP;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	data->supported_ring_params = dev->ethtool_ops->supported_ring_params;
3762306a36Sopenharmony_ci	ret = ethnl_ops_begin(dev);
3862306a36Sopenharmony_ci	if (ret < 0)
3962306a36Sopenharmony_ci		return ret;
4062306a36Sopenharmony_ci	dev->ethtool_ops->get_ringparam(dev, &data->ringparam,
4162306a36Sopenharmony_ci					&data->kernel_ringparam, info->extack);
4262306a36Sopenharmony_ci	ethnl_ops_complete(dev);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic int rings_reply_size(const struct ethnl_req_info *req_base,
4862306a36Sopenharmony_ci			    const struct ethnl_reply_data *reply_base)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	return nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MAX */
5162306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MINI_MAX */
5262306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_JUMBO_MAX */
5362306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_TX_MAX */
5462306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX */
5562306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MINI */
5662306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_JUMBO */
5762306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_TX */
5862306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_BUF_LEN */
5962306a36Sopenharmony_ci	       nla_total_size(sizeof(u8))  +	/* _RINGS_TCP_DATA_SPLIT */
6062306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)  +	/* _RINGS_CQE_SIZE */
6162306a36Sopenharmony_ci	       nla_total_size(sizeof(u8))  +	/* _RINGS_TX_PUSH */
6262306a36Sopenharmony_ci	       nla_total_size(sizeof(u8))) +	/* _RINGS_RX_PUSH */
6362306a36Sopenharmony_ci	       nla_total_size(sizeof(u32)) +	/* _RINGS_TX_PUSH_BUF_LEN */
6462306a36Sopenharmony_ci	       nla_total_size(sizeof(u32));	/* _RINGS_TX_PUSH_BUF_LEN_MAX */
6562306a36Sopenharmony_ci}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic int rings_fill_reply(struct sk_buff *skb,
6862306a36Sopenharmony_ci			    const struct ethnl_req_info *req_base,
6962306a36Sopenharmony_ci			    const struct ethnl_reply_data *reply_base)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	const struct rings_reply_data *data = RINGS_REPDATA(reply_base);
7262306a36Sopenharmony_ci	const struct kernel_ethtool_ringparam *kr = &data->kernel_ringparam;
7362306a36Sopenharmony_ci	const struct ethtool_ringparam *ringparam = &data->ringparam;
7462306a36Sopenharmony_ci	u32 supported_ring_params = data->supported_ring_params;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	WARN_ON(kr->tcp_data_split > ETHTOOL_TCP_DATA_SPLIT_ENABLED);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if ((ringparam->rx_max_pending &&
7962306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX,
8062306a36Sopenharmony_ci			  ringparam->rx_max_pending) ||
8162306a36Sopenharmony_ci	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX,
8262306a36Sopenharmony_ci			  ringparam->rx_pending))) ||
8362306a36Sopenharmony_ci	    (ringparam->rx_mini_max_pending &&
8462306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX,
8562306a36Sopenharmony_ci			  ringparam->rx_mini_max_pending) ||
8662306a36Sopenharmony_ci	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI,
8762306a36Sopenharmony_ci			  ringparam->rx_mini_pending))) ||
8862306a36Sopenharmony_ci	    (ringparam->rx_jumbo_max_pending &&
8962306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX,
9062306a36Sopenharmony_ci			  ringparam->rx_jumbo_max_pending) ||
9162306a36Sopenharmony_ci	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO,
9262306a36Sopenharmony_ci			  ringparam->rx_jumbo_pending))) ||
9362306a36Sopenharmony_ci	    (ringparam->tx_max_pending &&
9462306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX,
9562306a36Sopenharmony_ci			  ringparam->tx_max_pending) ||
9662306a36Sopenharmony_ci	      nla_put_u32(skb, ETHTOOL_A_RINGS_TX,
9762306a36Sopenharmony_ci			  ringparam->tx_pending)))  ||
9862306a36Sopenharmony_ci	    (kr->rx_buf_len &&
9962306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_BUF_LEN, kr->rx_buf_len))) ||
10062306a36Sopenharmony_ci	    (kr->tcp_data_split &&
10162306a36Sopenharmony_ci	     (nla_put_u8(skb, ETHTOOL_A_RINGS_TCP_DATA_SPLIT,
10262306a36Sopenharmony_ci			 kr->tcp_data_split))) ||
10362306a36Sopenharmony_ci	    (kr->cqe_size &&
10462306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_CQE_SIZE, kr->cqe_size))) ||
10562306a36Sopenharmony_ci	    nla_put_u8(skb, ETHTOOL_A_RINGS_TX_PUSH, !!kr->tx_push) ||
10662306a36Sopenharmony_ci	    nla_put_u8(skb, ETHTOOL_A_RINGS_RX_PUSH, !!kr->rx_push) ||
10762306a36Sopenharmony_ci	    ((supported_ring_params & ETHTOOL_RING_USE_TX_PUSH_BUF_LEN) &&
10862306a36Sopenharmony_ci	     (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN_MAX,
10962306a36Sopenharmony_ci			  kr->tx_push_buf_max_len) ||
11062306a36Sopenharmony_ci	      nla_put_u32(skb, ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN,
11162306a36Sopenharmony_ci			  kr->tx_push_buf_len))))
11262306a36Sopenharmony_ci		return -EMSGSIZE;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* RINGS_SET */
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ciconst struct nla_policy ethnl_rings_set_policy[] = {
12062306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_HEADER]		=
12162306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
12262306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_RX]			= { .type = NLA_U32 },
12362306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_RX_MINI]		= { .type = NLA_U32 },
12462306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_RX_JUMBO]		= { .type = NLA_U32 },
12562306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_TX]			= { .type = NLA_U32 },
12662306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_RX_BUF_LEN]            = NLA_POLICY_MIN(NLA_U32, 1),
12762306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_CQE_SIZE]		= NLA_POLICY_MIN(NLA_U32, 1),
12862306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_TX_PUSH]		= NLA_POLICY_MAX(NLA_U8, 1),
12962306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_RX_PUSH]		= NLA_POLICY_MAX(NLA_U8, 1),
13062306a36Sopenharmony_ci	[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN]	= { .type = NLA_U32 },
13162306a36Sopenharmony_ci};
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int
13462306a36Sopenharmony_ciethnl_set_rings_validate(struct ethnl_req_info *req_info,
13562306a36Sopenharmony_ci			 struct genl_info *info)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
13862306a36Sopenharmony_ci	struct nlattr **tb = info->attrs;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (tb[ETHTOOL_A_RINGS_RX_BUF_LEN] &&
14162306a36Sopenharmony_ci	    !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_BUF_LEN)) {
14262306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack,
14362306a36Sopenharmony_ci				    tb[ETHTOOL_A_RINGS_RX_BUF_LEN],
14462306a36Sopenharmony_ci				    "setting rx buf len not supported");
14562306a36Sopenharmony_ci		return -EOPNOTSUPP;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (tb[ETHTOOL_A_RINGS_CQE_SIZE] &&
14962306a36Sopenharmony_ci	    !(ops->supported_ring_params & ETHTOOL_RING_USE_CQE_SIZE)) {
15062306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack,
15162306a36Sopenharmony_ci				    tb[ETHTOOL_A_RINGS_CQE_SIZE],
15262306a36Sopenharmony_ci				    "setting cqe size not supported");
15362306a36Sopenharmony_ci		return -EOPNOTSUPP;
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (tb[ETHTOOL_A_RINGS_TX_PUSH] &&
15762306a36Sopenharmony_ci	    !(ops->supported_ring_params & ETHTOOL_RING_USE_TX_PUSH)) {
15862306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack,
15962306a36Sopenharmony_ci				    tb[ETHTOOL_A_RINGS_TX_PUSH],
16062306a36Sopenharmony_ci				    "setting tx push not supported");
16162306a36Sopenharmony_ci		return -EOPNOTSUPP;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (tb[ETHTOOL_A_RINGS_RX_PUSH] &&
16562306a36Sopenharmony_ci	    !(ops->supported_ring_params & ETHTOOL_RING_USE_RX_PUSH)) {
16662306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack,
16762306a36Sopenharmony_ci				    tb[ETHTOOL_A_RINGS_RX_PUSH],
16862306a36Sopenharmony_ci				    "setting rx push not supported");
16962306a36Sopenharmony_ci		return -EOPNOTSUPP;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN] &&
17362306a36Sopenharmony_ci	    !(ops->supported_ring_params & ETHTOOL_RING_USE_TX_PUSH_BUF_LEN)) {
17462306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack,
17562306a36Sopenharmony_ci				    tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN],
17662306a36Sopenharmony_ci				    "setting tx push buf len is not supported");
17762306a36Sopenharmony_ci		return -EOPNOTSUPP;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	return ops->get_ringparam && ops->set_ringparam ? 1 : -EOPNOTSUPP;
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int
18462306a36Sopenharmony_ciethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct kernel_ethtool_ringparam kernel_ringparam = {};
18762306a36Sopenharmony_ci	struct ethtool_ringparam ringparam = {};
18862306a36Sopenharmony_ci	struct net_device *dev = req_info->dev;
18962306a36Sopenharmony_ci	struct nlattr **tb = info->attrs;
19062306a36Sopenharmony_ci	const struct nlattr *err_attr;
19162306a36Sopenharmony_ci	bool mod = false;
19262306a36Sopenharmony_ci	int ret;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	dev->ethtool_ops->get_ringparam(dev, &ringparam,
19562306a36Sopenharmony_ci					&kernel_ringparam, info->extack);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod);
19862306a36Sopenharmony_ci	ethnl_update_u32(&ringparam.rx_mini_pending,
19962306a36Sopenharmony_ci			 tb[ETHTOOL_A_RINGS_RX_MINI], &mod);
20062306a36Sopenharmony_ci	ethnl_update_u32(&ringparam.rx_jumbo_pending,
20162306a36Sopenharmony_ci			 tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod);
20262306a36Sopenharmony_ci	ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod);
20362306a36Sopenharmony_ci	ethnl_update_u32(&kernel_ringparam.rx_buf_len,
20462306a36Sopenharmony_ci			 tb[ETHTOOL_A_RINGS_RX_BUF_LEN], &mod);
20562306a36Sopenharmony_ci	ethnl_update_u32(&kernel_ringparam.cqe_size,
20662306a36Sopenharmony_ci			 tb[ETHTOOL_A_RINGS_CQE_SIZE], &mod);
20762306a36Sopenharmony_ci	ethnl_update_u8(&kernel_ringparam.tx_push,
20862306a36Sopenharmony_ci			tb[ETHTOOL_A_RINGS_TX_PUSH], &mod);
20962306a36Sopenharmony_ci	ethnl_update_u8(&kernel_ringparam.rx_push,
21062306a36Sopenharmony_ci			tb[ETHTOOL_A_RINGS_RX_PUSH], &mod);
21162306a36Sopenharmony_ci	ethnl_update_u32(&kernel_ringparam.tx_push_buf_len,
21262306a36Sopenharmony_ci			 tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN], &mod);
21362306a36Sopenharmony_ci	if (!mod)
21462306a36Sopenharmony_ci		return 0;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/* ensure new ring parameters are within limits */
21762306a36Sopenharmony_ci	if (ringparam.rx_pending > ringparam.rx_max_pending)
21862306a36Sopenharmony_ci		err_attr = tb[ETHTOOL_A_RINGS_RX];
21962306a36Sopenharmony_ci	else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending)
22062306a36Sopenharmony_ci		err_attr = tb[ETHTOOL_A_RINGS_RX_MINI];
22162306a36Sopenharmony_ci	else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending)
22262306a36Sopenharmony_ci		err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO];
22362306a36Sopenharmony_ci	else if (ringparam.tx_pending > ringparam.tx_max_pending)
22462306a36Sopenharmony_ci		err_attr = tb[ETHTOOL_A_RINGS_TX];
22562306a36Sopenharmony_ci	else
22662306a36Sopenharmony_ci		err_attr = NULL;
22762306a36Sopenharmony_ci	if (err_attr) {
22862306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(info->extack, err_attr,
22962306a36Sopenharmony_ci				    "requested ring size exceeds maximum");
23062306a36Sopenharmony_ci		return -EINVAL;
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (kernel_ringparam.tx_push_buf_len > kernel_ringparam.tx_push_buf_max_len) {
23462306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR_FMT(info->extack, tb[ETHTOOL_A_RINGS_TX_PUSH_BUF_LEN],
23562306a36Sopenharmony_ci					"Requested TX push buffer exceeds the maximum of %u",
23662306a36Sopenharmony_ci					kernel_ringparam.tx_push_buf_max_len);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		return -EINVAL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	ret = dev->ethtool_ops->set_ringparam(dev, &ringparam,
24262306a36Sopenharmony_ci					      &kernel_ringparam, info->extack);
24362306a36Sopenharmony_ci	return ret < 0 ? ret : 1;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_rings_request_ops = {
24762306a36Sopenharmony_ci	.request_cmd		= ETHTOOL_MSG_RINGS_GET,
24862306a36Sopenharmony_ci	.reply_cmd		= ETHTOOL_MSG_RINGS_GET_REPLY,
24962306a36Sopenharmony_ci	.hdr_attr		= ETHTOOL_A_RINGS_HEADER,
25062306a36Sopenharmony_ci	.req_info_size		= sizeof(struct rings_req_info),
25162306a36Sopenharmony_ci	.reply_data_size	= sizeof(struct rings_reply_data),
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	.prepare_data		= rings_prepare_data,
25462306a36Sopenharmony_ci	.reply_size		= rings_reply_size,
25562306a36Sopenharmony_ci	.fill_reply		= rings_fill_reply,
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	.set_validate		= ethnl_set_rings_validate,
25862306a36Sopenharmony_ci	.set			= ethnl_set_rings,
25962306a36Sopenharmony_ci	.set_ntf_cmd		= ETHTOOL_MSG_RINGS_NTF,
26062306a36Sopenharmony_ci};
261