xref: /kernel/linux/linux-5.10/net/ethtool/rings.c (revision 8c2ecf20)
1// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5
6struct rings_req_info {
7	struct ethnl_req_info		base;
8};
9
10struct rings_reply_data {
11	struct ethnl_reply_data		base;
12	struct ethtool_ringparam	ringparam;
13};
14
15#define RINGS_REPDATA(__reply_base) \
16	container_of(__reply_base, struct rings_reply_data, base)
17
18const struct nla_policy ethnl_rings_get_policy[] = {
19	[ETHTOOL_A_RINGS_HEADER]		=
20		NLA_POLICY_NESTED(ethnl_header_policy),
21};
22
23static int rings_prepare_data(const struct ethnl_req_info *req_base,
24			      struct ethnl_reply_data *reply_base,
25			      struct genl_info *info)
26{
27	struct rings_reply_data *data = RINGS_REPDATA(reply_base);
28	struct net_device *dev = reply_base->dev;
29	int ret;
30
31	if (!dev->ethtool_ops->get_ringparam)
32		return -EOPNOTSUPP;
33	ret = ethnl_ops_begin(dev);
34	if (ret < 0)
35		return ret;
36	dev->ethtool_ops->get_ringparam(dev, &data->ringparam);
37	ethnl_ops_complete(dev);
38
39	return 0;
40}
41
42static int rings_reply_size(const struct ethnl_req_info *req_base,
43			    const struct ethnl_reply_data *reply_base)
44{
45	return nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MAX */
46	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MINI_MAX */
47	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_JUMBO_MAX */
48	       nla_total_size(sizeof(u32)) +	/* _RINGS_TX_MAX */
49	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX */
50	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_MINI */
51	       nla_total_size(sizeof(u32)) +	/* _RINGS_RX_JUMBO */
52	       nla_total_size(sizeof(u32));	/* _RINGS_TX */
53}
54
55static int rings_fill_reply(struct sk_buff *skb,
56			    const struct ethnl_req_info *req_base,
57			    const struct ethnl_reply_data *reply_base)
58{
59	const struct rings_reply_data *data = RINGS_REPDATA(reply_base);
60	const struct ethtool_ringparam *ringparam = &data->ringparam;
61
62	if ((ringparam->rx_max_pending &&
63	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MAX,
64			  ringparam->rx_max_pending) ||
65	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX,
66			  ringparam->rx_pending))) ||
67	    (ringparam->rx_mini_max_pending &&
68	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI_MAX,
69			  ringparam->rx_mini_max_pending) ||
70	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX_MINI,
71			  ringparam->rx_mini_pending))) ||
72	    (ringparam->rx_jumbo_max_pending &&
73	     (nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO_MAX,
74			  ringparam->rx_jumbo_max_pending) ||
75	      nla_put_u32(skb, ETHTOOL_A_RINGS_RX_JUMBO,
76			  ringparam->rx_jumbo_pending))) ||
77	    (ringparam->tx_max_pending &&
78	     (nla_put_u32(skb, ETHTOOL_A_RINGS_TX_MAX,
79			  ringparam->tx_max_pending) ||
80	      nla_put_u32(skb, ETHTOOL_A_RINGS_TX,
81			  ringparam->tx_pending))))
82		return -EMSGSIZE;
83
84	return 0;
85}
86
87const struct ethnl_request_ops ethnl_rings_request_ops = {
88	.request_cmd		= ETHTOOL_MSG_RINGS_GET,
89	.reply_cmd		= ETHTOOL_MSG_RINGS_GET_REPLY,
90	.hdr_attr		= ETHTOOL_A_RINGS_HEADER,
91	.req_info_size		= sizeof(struct rings_req_info),
92	.reply_data_size	= sizeof(struct rings_reply_data),
93
94	.prepare_data		= rings_prepare_data,
95	.reply_size		= rings_reply_size,
96	.fill_reply		= rings_fill_reply,
97};
98
99/* RINGS_SET */
100
101const struct nla_policy ethnl_rings_set_policy[] = {
102	[ETHTOOL_A_RINGS_HEADER]		=
103		NLA_POLICY_NESTED(ethnl_header_policy),
104	[ETHTOOL_A_RINGS_RX]			= { .type = NLA_U32 },
105	[ETHTOOL_A_RINGS_RX_MINI]		= { .type = NLA_U32 },
106	[ETHTOOL_A_RINGS_RX_JUMBO]		= { .type = NLA_U32 },
107	[ETHTOOL_A_RINGS_TX]			= { .type = NLA_U32 },
108};
109
110int ethnl_set_rings(struct sk_buff *skb, struct genl_info *info)
111{
112	struct ethtool_ringparam ringparam = {};
113	struct ethnl_req_info req_info = {};
114	struct nlattr **tb = info->attrs;
115	const struct nlattr *err_attr;
116	const struct ethtool_ops *ops;
117	struct net_device *dev;
118	bool mod = false;
119	int ret;
120
121	ret = ethnl_parse_header_dev_get(&req_info,
122					 tb[ETHTOOL_A_RINGS_HEADER],
123					 genl_info_net(info), info->extack,
124					 true);
125	if (ret < 0)
126		return ret;
127	dev = req_info.dev;
128	ops = dev->ethtool_ops;
129	ret = -EOPNOTSUPP;
130	if (!ops->get_ringparam || !ops->set_ringparam)
131		goto out_dev;
132
133	rtnl_lock();
134	ret = ethnl_ops_begin(dev);
135	if (ret < 0)
136		goto out_rtnl;
137	ops->get_ringparam(dev, &ringparam);
138
139	ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod);
140	ethnl_update_u32(&ringparam.rx_mini_pending,
141			 tb[ETHTOOL_A_RINGS_RX_MINI], &mod);
142	ethnl_update_u32(&ringparam.rx_jumbo_pending,
143			 tb[ETHTOOL_A_RINGS_RX_JUMBO], &mod);
144	ethnl_update_u32(&ringparam.tx_pending, tb[ETHTOOL_A_RINGS_TX], &mod);
145	ret = 0;
146	if (!mod)
147		goto out_ops;
148
149	/* ensure new ring parameters are within limits */
150	if (ringparam.rx_pending > ringparam.rx_max_pending)
151		err_attr = tb[ETHTOOL_A_RINGS_RX];
152	else if (ringparam.rx_mini_pending > ringparam.rx_mini_max_pending)
153		err_attr = tb[ETHTOOL_A_RINGS_RX_MINI];
154	else if (ringparam.rx_jumbo_pending > ringparam.rx_jumbo_max_pending)
155		err_attr = tb[ETHTOOL_A_RINGS_RX_JUMBO];
156	else if (ringparam.tx_pending > ringparam.tx_max_pending)
157		err_attr = tb[ETHTOOL_A_RINGS_TX];
158	else
159		err_attr = NULL;
160	if (err_attr) {
161		ret = -EINVAL;
162		NL_SET_ERR_MSG_ATTR(info->extack, err_attr,
163				    "requested ring size exceeds maximum");
164		goto out_ops;
165	}
166
167	ret = dev->ethtool_ops->set_ringparam(dev, &ringparam);
168	if (ret < 0)
169		goto out_ops;
170	ethtool_notify(dev, ETHTOOL_MSG_RINGS_NTF, NULL);
171
172out_ops:
173	ethnl_ops_complete(dev);
174out_rtnl:
175	rtnl_unlock();
176out_dev:
177	dev_put(dev);
178	return ret;
179}
180