Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2
  3#include <linux/dim.h>
  4#include "netlink.h"
  5#include "common.h"
  6
  7struct coalesce_req_info {
  8	struct ethnl_req_info		base;
  9};
 10
 11struct coalesce_reply_data {
 12	struct ethnl_reply_data		base;
 13	struct ethtool_coalesce		coalesce;
 14	struct kernel_ethtool_coalesce	kernel_coalesce;
 15	u32				supported_params;
 16};
 17
 18#define COALESCE_REPDATA(__reply_base) \
 19	container_of(__reply_base, struct coalesce_reply_data, base)
 20
 21#define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
 22static u32 attr_to_mask(unsigned int attr_type)
 23{
 24	return BIT(attr_type - __SUPPORTED_OFFSET);
 25}
 26
 27/* build time check that indices in ethtool_ops::supported_coalesce_params
 28 * match corresponding attribute types with an offset
 29 */
 30#define __CHECK_SUPPORTED_OFFSET(x) \
 31	static_assert((ETHTOOL_ ## x) == \
 32		      BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
 33__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
 34__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
 35__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
 36__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
 37__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
 38__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
 39__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
 40__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
 41__CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
 42__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
 43__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
 44__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
 45__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
 46__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
 47__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
 48__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
 49__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
 50__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
 51__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
 52__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
 53__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
 54__CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
 55
 56const struct nla_policy ethnl_coalesce_get_policy[] = {
 57	[ETHTOOL_A_COALESCE_HEADER]		=
 58		NLA_POLICY_NESTED(ethnl_header_policy),
 59};
 60
 61static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
 62				 struct ethnl_reply_data *reply_base,
 63				 const struct genl_info *info)
 64{
 65	struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
 66	struct net_device *dev = reply_base->dev;
 67	int ret;
 68
 69	if (!dev->ethtool_ops->get_coalesce)
 70		return -EOPNOTSUPP;
 71	data->supported_params = dev->ethtool_ops->supported_coalesce_params;
 72	ret = ethnl_ops_begin(dev);
 73	if (ret < 0)
 74		return ret;
 75	ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce,
 76					     &data->kernel_coalesce,
 77					     info->extack);
 78	ethnl_ops_complete(dev);
 79
 80	return ret;
 81}
 82
 83static int coalesce_reply_size(const struct ethnl_req_info *req_base,
 84			       const struct ethnl_reply_data *reply_base)
 85{
 86	int modersz = nla_total_size(0) + /* _PROFILE_IRQ_MODERATION, nest */
 87		      nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_USEC */
 88		      nla_total_size(sizeof(u32)) + /* _IRQ_MODERATION_PKTS */
 89		      nla_total_size(sizeof(u32));  /* _IRQ_MODERATION_COMPS */
 90
 91	int total_modersz = nla_total_size(0) +  /* _{R,T}X_PROFILE, nest */
 92			modersz * NET_DIM_PARAMS_NUM_PROFILES;
 93
 94	return nla_total_size(sizeof(u32)) +	/* _RX_USECS */
 95	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES */
 96	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_IRQ */
 97	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_IRQ */
 98	       nla_total_size(sizeof(u32)) +	/* _TX_USECS */
 99	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES */
100	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_IRQ */
101	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_IRQ */
102	       nla_total_size(sizeof(u32)) +	/* _STATS_BLOCK_USECS */
103	       nla_total_size(sizeof(u8)) +	/* _USE_ADAPTIVE_RX */
104	       nla_total_size(sizeof(u8)) +	/* _USE_ADAPTIVE_TX */
105	       nla_total_size(sizeof(u32)) +	/* _PKT_RATE_LOW */
106	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_LOW */
107	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_LOW */
108	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_LOW */
109	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_LOW */
110	       nla_total_size(sizeof(u32)) +	/* _PKT_RATE_HIGH */
111	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_HIGH */
112	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_HIGH */
113	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_HIGH */
114	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_HIGH */
115	       nla_total_size(sizeof(u32)) +	/* _RATE_SAMPLE_INTERVAL */
116	       nla_total_size(sizeof(u8)) +	/* _USE_CQE_MODE_TX */
117	       nla_total_size(sizeof(u8)) +	/* _USE_CQE_MODE_RX */
118	       nla_total_size(sizeof(u32)) +	/* _TX_AGGR_MAX_BYTES */
119	       nla_total_size(sizeof(u32)) +	/* _TX_AGGR_MAX_FRAMES */
120	       nla_total_size(sizeof(u32)) +	/* _TX_AGGR_TIME_USECS */
121	       total_modersz * 2;		/* _{R,T}X_PROFILE */
122}
123
124static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
125			     u32 supported_params)
126{
127	if (!val && !(supported_params & attr_to_mask(attr_type)))
128		return false;
129	return nla_put_u32(skb, attr_type, val);
130}
131
132static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
133			      u32 supported_params)
134{
135	if (!val && !(supported_params & attr_to_mask(attr_type)))
136		return false;
137	return nla_put_u8(skb, attr_type, !!val);
138}
139
140/**
141 * coalesce_put_profile - fill reply with a nla nest with four child nla nests.
142 * @skb: socket buffer the message is stored in
143 * @attr_type: nest attr type ETHTOOL_A_COALESCE_*X_PROFILE
144 * @profile: data passed to userspace
145 * @coal_flags: modifiable parameters supported by the driver
146 *
147 * Put a dim profile nest attribute. Refer to ETHTOOL_A_PROFILE_IRQ_MODERATION.
148 *
149 * Return: 0 on success or a negative error code.
150 */
151static int coalesce_put_profile(struct sk_buff *skb, u16 attr_type,
152				const struct dim_cq_moder *profile,
153				u8 coal_flags)
154{
155	struct nlattr *profile_attr, *moder_attr;
156	int i, ret;
157
158	if (!profile || !coal_flags)
159		return 0;
160
161	profile_attr = nla_nest_start(skb, attr_type);
162	if (!profile_attr)
163		return -EMSGSIZE;
164
165	for (i = 0; i < NET_DIM_PARAMS_NUM_PROFILES; i++) {
166		moder_attr = nla_nest_start(skb,
167					    ETHTOOL_A_PROFILE_IRQ_MODERATION);
168		if (!moder_attr) {
169			ret = -EMSGSIZE;
170			goto cancel_profile;
171		}
172
173		if (coal_flags & DIM_COALESCE_USEC) {
174			ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_USEC,
175					  profile[i].usec);
176			if (ret)
177				goto cancel_moder;
178		}
179
180		if (coal_flags & DIM_COALESCE_PKTS) {
181			ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_PKTS,
182					  profile[i].pkts);
183			if (ret)
184				goto cancel_moder;
185		}
186
187		if (coal_flags & DIM_COALESCE_COMPS) {
188			ret = nla_put_u32(skb, ETHTOOL_A_IRQ_MODERATION_COMPS,
189					  profile[i].comps);
190			if (ret)
191				goto cancel_moder;
192		}
193
194		nla_nest_end(skb, moder_attr);
195	}
196
197	nla_nest_end(skb, profile_attr);
198
199	return 0;
200
201cancel_moder:
202	nla_nest_cancel(skb, moder_attr);
203cancel_profile:
204	nla_nest_cancel(skb, profile_attr);
205	return ret;
206}
207
208static int coalesce_fill_reply(struct sk_buff *skb,
209			       const struct ethnl_req_info *req_base,
210			       const struct ethnl_reply_data *reply_base)
211{
212	const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
213	const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
214	const struct ethtool_coalesce *coal = &data->coalesce;
215	u32 supported = data->supported_params;
216	struct dim_irq_moder *moder;
217	int ret = 0;
218
219	if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
220			     coal->rx_coalesce_usecs, supported) ||
221	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
222			     coal->rx_max_coalesced_frames, supported) ||
223	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
224			     coal->rx_coalesce_usecs_irq, supported) ||
225	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
226			     coal->rx_max_coalesced_frames_irq, supported) ||
227	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
228			     coal->tx_coalesce_usecs, supported) ||
229	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
230			     coal->tx_max_coalesced_frames, supported) ||
231	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
232			     coal->tx_coalesce_usecs_irq, supported) ||
233	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
234			     coal->tx_max_coalesced_frames_irq, supported) ||
235	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
236			     coal->stats_block_coalesce_usecs, supported) ||
237	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
238			      coal->use_adaptive_rx_coalesce, supported) ||
239	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
240			      coal->use_adaptive_tx_coalesce, supported) ||
241	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
242			     coal->pkt_rate_low, supported) ||
243	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
244			     coal->rx_coalesce_usecs_low, supported) ||
245	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
246			     coal->rx_max_coalesced_frames_low, supported) ||
247	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
248			     coal->tx_coalesce_usecs_low, supported) ||
249	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
250			     coal->tx_max_coalesced_frames_low, supported) ||
251	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
252			     coal->pkt_rate_high, supported) ||
253	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
254			     coal->rx_coalesce_usecs_high, supported) ||
255	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
256			     coal->rx_max_coalesced_frames_high, supported) ||
257	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
258			     coal->tx_coalesce_usecs_high, supported) ||
259	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
260			     coal->tx_max_coalesced_frames_high, supported) ||
261	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
262			     coal->rate_sample_interval, supported) ||
263	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
264			      kcoal->use_cqe_mode_tx, supported) ||
265	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
266			      kcoal->use_cqe_mode_rx, supported) ||
267	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
268			     kcoal->tx_aggr_max_bytes, supported) ||
269	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
270			     kcoal->tx_aggr_max_frames, supported) ||
271	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
272			     kcoal->tx_aggr_time_usecs, supported))
273		return -EMSGSIZE;
274
275	if (!req_base->dev || !req_base->dev->irq_moder)
276		return 0;
277
278	moder = req_base->dev->irq_moder;
279	rcu_read_lock();
280	if (moder->profile_flags & DIM_PROFILE_RX) {
281		ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_RX_PROFILE,
282					   rcu_dereference(moder->rx_profile),
283					   moder->coal_flags);
284		if (ret)
285			goto out;
286	}
287
288	if (moder->profile_flags & DIM_PROFILE_TX)
289		ret = coalesce_put_profile(skb, ETHTOOL_A_COALESCE_TX_PROFILE,
290					   rcu_dereference(moder->tx_profile),
291					   moder->coal_flags);
292
293out:
294	rcu_read_unlock();
295	return ret;
296}
297
298/* COALESCE_SET */
299
300static const struct nla_policy coalesce_irq_moderation_policy[] = {
301	[ETHTOOL_A_IRQ_MODERATION_USEC]	= { .type = NLA_U32 },
302	[ETHTOOL_A_IRQ_MODERATION_PKTS]	= { .type = NLA_U32 },
303	[ETHTOOL_A_IRQ_MODERATION_COMPS] = { .type = NLA_U32 },
304};
305
306static const struct nla_policy coalesce_profile_policy[] = {
307	[ETHTOOL_A_PROFILE_IRQ_MODERATION] =
308		NLA_POLICY_NESTED(coalesce_irq_moderation_policy),
309};
310
311const struct nla_policy ethnl_coalesce_set_policy[] = {
312	[ETHTOOL_A_COALESCE_HEADER]		=
313		NLA_POLICY_NESTED(ethnl_header_policy),
314	[ETHTOOL_A_COALESCE_RX_USECS]		= { .type = NLA_U32 },
315	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES]	= { .type = NLA_U32 },
316	[ETHTOOL_A_COALESCE_RX_USECS_IRQ]	= { .type = NLA_U32 },
317	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]	= { .type = NLA_U32 },
318	[ETHTOOL_A_COALESCE_TX_USECS]		= { .type = NLA_U32 },
319	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES]	= { .type = NLA_U32 },
320	[ETHTOOL_A_COALESCE_TX_USECS_IRQ]	= { .type = NLA_U32 },
321	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]	= { .type = NLA_U32 },
322	[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]	= { .type = NLA_U32 },
323	[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]	= { .type = NLA_U8 },
324	[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]	= { .type = NLA_U8 },
325	[ETHTOOL_A_COALESCE_PKT_RATE_LOW]	= { .type = NLA_U32 },
326	[ETHTOOL_A_COALESCE_RX_USECS_LOW]	= { .type = NLA_U32 },
327	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]	= { .type = NLA_U32 },
328	[ETHTOOL_A_COALESCE_TX_USECS_LOW]	= { .type = NLA_U32 },
329	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]	= { .type = NLA_U32 },
330	[ETHTOOL_A_COALESCE_PKT_RATE_HIGH]	= { .type = NLA_U32 },
331	[ETHTOOL_A_COALESCE_RX_USECS_HIGH]	= { .type = NLA_U32 },
332	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH]	= { .type = NLA_U32 },
333	[ETHTOOL_A_COALESCE_TX_USECS_HIGH]	= { .type = NLA_U32 },
334	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH]	= { .type = NLA_U32 },
335	[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
336	[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX]	= NLA_POLICY_MAX(NLA_U8, 1),
337	[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX]	= NLA_POLICY_MAX(NLA_U8, 1),
338	[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 },
339	[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 },
340	[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 },
341	[ETHTOOL_A_COALESCE_RX_PROFILE] =
342		NLA_POLICY_NESTED(coalesce_profile_policy),
343	[ETHTOOL_A_COALESCE_TX_PROFILE] =
344		NLA_POLICY_NESTED(coalesce_profile_policy),
345};
346
347static int
348ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
349			    struct genl_info *info)
350{
351	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
352	struct dim_irq_moder *irq_moder = req_info->dev->irq_moder;
353	struct nlattr **tb = info->attrs;
354	u32 supported_params;
355	u16 a;
356
357	if (!ops->get_coalesce || !ops->set_coalesce)
358		return -EOPNOTSUPP;
359
360	/* make sure that only supported parameters are present */
361	supported_params = ops->supported_coalesce_params;
362	if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_RX)
363		supported_params |= ETHTOOL_COALESCE_RX_PROFILE;
364
365	if (irq_moder && irq_moder->profile_flags & DIM_PROFILE_TX)
366		supported_params |= ETHTOOL_COALESCE_TX_PROFILE;
367
368	for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
369		if (tb[a] && !(supported_params & attr_to_mask(a))) {
370			NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
371					    "cannot modify an unsupported parameter");
372			return -EINVAL;
373		}
374
375	return 1;
376}
377
378/**
379 * ethnl_update_irq_moder - update a specific field in the given profile
380 * @irq_moder: place that collects dim related information
381 * @irq_field: field in profile to modify
382 * @attr_type: attr type ETHTOOL_A_IRQ_MODERATION_*
383 * @tb: netlink attribute with new values or null
384 * @coal_bit: DIM_COALESCE_* bit from coal_flags
385 * @mod: pointer to bool for modification tracking
386 * @extack: netlink extended ack
387 *
388 * Return: 0 on success or a negative error code.
389 */
390static int ethnl_update_irq_moder(struct dim_irq_moder *irq_moder,
391				  u16 *irq_field, u16 attr_type,
392				  struct nlattr **tb,
393				  u8 coal_bit, bool *mod,
394				  struct netlink_ext_ack *extack)
395{
396	int ret = 0;
397	u32 val;
398
399	if (!tb[attr_type])
400		return 0;
401
402	if (irq_moder->coal_flags & coal_bit) {
403		val = nla_get_u32(tb[attr_type]);
404		if (*irq_field == val)
405			return 0;
406
407		*irq_field = val;
408		*mod = true;
409	} else {
410		NL_SET_BAD_ATTR(extack, tb[attr_type]);
411		ret = -EOPNOTSUPP;
412	}
413
414	return ret;
415}
416
417/**
418 * ethnl_update_profile - get a profile nest with child nests from userspace.
419 * @dev: netdevice to update the profile
420 * @dst: profile get from the driver and modified by ethnl_update_profile.
421 * @nests: nest attr ETHTOOL_A_COALESCE_*X_PROFILE to set profile.
422 * @mod: pointer to bool for modification tracking
423 * @extack: Netlink extended ack
424 *
425 * Layout of nests:
426 *   Nested ETHTOOL_A_COALESCE_*X_PROFILE attr
427 *     Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
428 *       ETHTOOL_A_IRQ_MODERATION_USEC attr
429 *       ETHTOOL_A_IRQ_MODERATION_PKTS attr
430 *       ETHTOOL_A_IRQ_MODERATION_COMPS attr
431 *     ...
432 *     Nested ETHTOOL_A_PROFILE_IRQ_MODERATION attr
433 *       ETHTOOL_A_IRQ_MODERATION_USEC attr
434 *       ETHTOOL_A_IRQ_MODERATION_PKTS attr
435 *       ETHTOOL_A_IRQ_MODERATION_COMPS attr
436 *
437 * Return: 0 on success or a negative error code.
438 */
439static int ethnl_update_profile(struct net_device *dev,
440				struct dim_cq_moder __rcu **dst,
441				const struct nlattr *nests,
442				bool *mod,
443				struct netlink_ext_ack *extack)
444{
445	int len_irq_moder = ARRAY_SIZE(coalesce_irq_moderation_policy);
446	struct nlattr *tb[ARRAY_SIZE(coalesce_irq_moderation_policy)];
447	struct dim_irq_moder *irq_moder = dev->irq_moder;
448	struct dim_cq_moder *new_profile, *old_profile;
449	int ret, rem, i = 0, len;
450	struct nlattr *nest;
451
452	if (!nests)
453		return 0;
454
455	if (!*dst)
456		return -EOPNOTSUPP;
457
458	old_profile = rtnl_dereference(*dst);
459	len = NET_DIM_PARAMS_NUM_PROFILES * sizeof(*old_profile);
460	new_profile = kmemdup(old_profile, len, GFP_KERNEL);
461	if (!new_profile)
462		return -ENOMEM;
463
464	nla_for_each_nested_type(nest, ETHTOOL_A_PROFILE_IRQ_MODERATION,
465				 nests, rem) {
466		ret = nla_parse_nested(tb, len_irq_moder - 1, nest,
467				       coalesce_irq_moderation_policy,
468				       extack);
469		if (ret)
470			goto err_out;
471
472		ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].usec,
473					     ETHTOOL_A_IRQ_MODERATION_USEC,
474					     tb, DIM_COALESCE_USEC,
475					     mod, extack);
476		if (ret)
477			goto err_out;
478
479		ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].pkts,
480					     ETHTOOL_A_IRQ_MODERATION_PKTS,
481					     tb, DIM_COALESCE_PKTS,
482					     mod, extack);
483		if (ret)
484			goto err_out;
485
486		ret = ethnl_update_irq_moder(irq_moder, &new_profile[i].comps,
487					     ETHTOOL_A_IRQ_MODERATION_COMPS,
488					     tb, DIM_COALESCE_COMPS,
489					     mod, extack);
490		if (ret)
491			goto err_out;
492
493		i++;
494	}
495
496	/* After the profile is modified, dim itself is a dynamic
497	 * mechanism and will quickly fit to the appropriate
498	 * coalescing parameters according to the new profile.
499	 */
500	rcu_assign_pointer(*dst, new_profile);
501	kfree_rcu(old_profile, rcu);
502
503	return 0;
504
505err_out:
506	kfree(new_profile);
507	return ret;
508}
509
510static int
511__ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
512		     bool *dual_change)
513{
514	struct kernel_ethtool_coalesce kernel_coalesce = {};
515	struct net_device *dev = req_info->dev;
516	struct ethtool_coalesce coalesce = {};
517	bool mod_mode = false, mod = false;
518	struct nlattr **tb = info->attrs;
519	int ret;
520
521	ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce,
522					     info->extack);
523	if (ret < 0)
524		return ret;
525
526	/* Update values */
527	ethnl_update_u32(&coalesce.rx_coalesce_usecs,
528			 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
529	ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
530			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
531	ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
532			 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
533	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
534			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
535	ethnl_update_u32(&coalesce.tx_coalesce_usecs,
536			 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
537	ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
538			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
539	ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
540			 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
541	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
542			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
543	ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
544			 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
545	ethnl_update_u32(&coalesce.pkt_rate_low,
546			 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
547	ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
548			 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
549	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
550			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
551	ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
552			 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
553	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
554			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
555	ethnl_update_u32(&coalesce.pkt_rate_high,
556			 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
557	ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
558			 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
559	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
560			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
561	ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
562			 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
563	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
564			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
565	ethnl_update_u32(&coalesce.rate_sample_interval,
566			 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
567	ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes,
568			 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod);
569	ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames,
570			 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod);
571	ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
572			 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
573
574	if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_RX) {
575		ret = ethnl_update_profile(dev, &dev->irq_moder->rx_profile,
576					   tb[ETHTOOL_A_COALESCE_RX_PROFILE],
577					   &mod, info->extack);
578		if (ret < 0)
579			return ret;
580	}
581
582	if (dev->irq_moder && dev->irq_moder->profile_flags & DIM_PROFILE_TX) {
583		ret = ethnl_update_profile(dev, &dev->irq_moder->tx_profile,
584					   tb[ETHTOOL_A_COALESCE_TX_PROFILE],
585					   &mod, info->extack);
586		if (ret < 0)
587			return ret;
588	}
589
590	/* Update operation modes */
591	ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
592			    tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);
593	ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
594			    tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode);
595	ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
596			tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode);
597	ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
598			tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode);
599
600	*dual_change = mod && mod_mode;
601	if (!mod && !mod_mode)
602		return 0;
603
604	ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce,
605					     info->extack);
606	return ret < 0 ? ret : 1;
607}
608
609static int
610ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info)
611{
612	bool dual_change;
613	int err, ret;
614
615	/* SET_COALESCE may change operation mode and parameters in one call.
616	 * Changing operation mode may cause the driver to reset the parameter
617	 * values, and therefore ignore user input (driver does not know which
618	 * parameters come from user and which are echoed back from ->get).
619	 * To not complicate the drivers if user tries to change both the mode
620	 * and parameters at once - call the driver twice.
621	 */
622	err = __ethnl_set_coalesce(req_info, info, &dual_change);
623	if (err < 0)
624		return err;
625	ret = err;
626
627	if (ret && dual_change) {
628		err = __ethnl_set_coalesce(req_info, info, &dual_change);
629		if (err < 0)
630			return err;
631	}
632	return ret;
633}
634
635const struct ethnl_request_ops ethnl_coalesce_request_ops = {
636	.request_cmd		= ETHTOOL_MSG_COALESCE_GET,
637	.reply_cmd		= ETHTOOL_MSG_COALESCE_GET_REPLY,
638	.hdr_attr		= ETHTOOL_A_COALESCE_HEADER,
639	.req_info_size		= sizeof(struct coalesce_req_info),
640	.reply_data_size	= sizeof(struct coalesce_reply_data),
641
642	.prepare_data		= coalesce_prepare_data,
643	.reply_size		= coalesce_reply_size,
644	.fill_reply		= coalesce_fill_reply,
645
646	.set_validate		= ethnl_set_coalesce_validate,
647	.set			= ethnl_set_coalesce,
648	.set_ntf_cmd		= ETHTOOL_MSG_COALESCE_NTF,
649};
v6.8
  1// SPDX-License-Identifier: GPL-2.0-only
  2
 
  3#include "netlink.h"
  4#include "common.h"
  5
  6struct coalesce_req_info {
  7	struct ethnl_req_info		base;
  8};
  9
 10struct coalesce_reply_data {
 11	struct ethnl_reply_data		base;
 12	struct ethtool_coalesce		coalesce;
 13	struct kernel_ethtool_coalesce	kernel_coalesce;
 14	u32				supported_params;
 15};
 16
 17#define COALESCE_REPDATA(__reply_base) \
 18	container_of(__reply_base, struct coalesce_reply_data, base)
 19
 20#define __SUPPORTED_OFFSET ETHTOOL_A_COALESCE_RX_USECS
 21static u32 attr_to_mask(unsigned int attr_type)
 22{
 23	return BIT(attr_type - __SUPPORTED_OFFSET);
 24}
 25
 26/* build time check that indices in ethtool_ops::supported_coalesce_params
 27 * match corresponding attribute types with an offset
 28 */
 29#define __CHECK_SUPPORTED_OFFSET(x) \
 30	static_assert((ETHTOOL_ ## x) == \
 31		      BIT((ETHTOOL_A_ ## x) - __SUPPORTED_OFFSET))
 32__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS);
 33__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES);
 34__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_IRQ);
 35__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_IRQ);
 36__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS);
 37__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES);
 38__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_IRQ);
 39__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_IRQ);
 40__CHECK_SUPPORTED_OFFSET(COALESCE_STATS_BLOCK_USECS);
 41__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_RX);
 42__CHECK_SUPPORTED_OFFSET(COALESCE_USE_ADAPTIVE_TX);
 43__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_LOW);
 44__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_LOW);
 45__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_LOW);
 46__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_LOW);
 47__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_LOW);
 48__CHECK_SUPPORTED_OFFSET(COALESCE_PKT_RATE_HIGH);
 49__CHECK_SUPPORTED_OFFSET(COALESCE_RX_USECS_HIGH);
 50__CHECK_SUPPORTED_OFFSET(COALESCE_RX_MAX_FRAMES_HIGH);
 51__CHECK_SUPPORTED_OFFSET(COALESCE_TX_USECS_HIGH);
 52__CHECK_SUPPORTED_OFFSET(COALESCE_TX_MAX_FRAMES_HIGH);
 53__CHECK_SUPPORTED_OFFSET(COALESCE_RATE_SAMPLE_INTERVAL);
 54
 55const struct nla_policy ethnl_coalesce_get_policy[] = {
 56	[ETHTOOL_A_COALESCE_HEADER]		=
 57		NLA_POLICY_NESTED(ethnl_header_policy),
 58};
 59
 60static int coalesce_prepare_data(const struct ethnl_req_info *req_base,
 61				 struct ethnl_reply_data *reply_base,
 62				 const struct genl_info *info)
 63{
 64	struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
 65	struct net_device *dev = reply_base->dev;
 66	int ret;
 67
 68	if (!dev->ethtool_ops->get_coalesce)
 69		return -EOPNOTSUPP;
 70	data->supported_params = dev->ethtool_ops->supported_coalesce_params;
 71	ret = ethnl_ops_begin(dev);
 72	if (ret < 0)
 73		return ret;
 74	ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce,
 75					     &data->kernel_coalesce,
 76					     info->extack);
 77	ethnl_ops_complete(dev);
 78
 79	return ret;
 80}
 81
 82static int coalesce_reply_size(const struct ethnl_req_info *req_base,
 83			       const struct ethnl_reply_data *reply_base)
 84{
 
 
 
 
 
 
 
 
 85	return nla_total_size(sizeof(u32)) +	/* _RX_USECS */
 86	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES */
 87	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_IRQ */
 88	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_IRQ */
 89	       nla_total_size(sizeof(u32)) +	/* _TX_USECS */
 90	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES */
 91	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_IRQ */
 92	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_IRQ */
 93	       nla_total_size(sizeof(u32)) +	/* _STATS_BLOCK_USECS */
 94	       nla_total_size(sizeof(u8)) +	/* _USE_ADAPTIVE_RX */
 95	       nla_total_size(sizeof(u8)) +	/* _USE_ADAPTIVE_TX */
 96	       nla_total_size(sizeof(u32)) +	/* _PKT_RATE_LOW */
 97	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_LOW */
 98	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_LOW */
 99	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_LOW */
100	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_LOW */
101	       nla_total_size(sizeof(u32)) +	/* _PKT_RATE_HIGH */
102	       nla_total_size(sizeof(u32)) +	/* _RX_USECS_HIGH */
103	       nla_total_size(sizeof(u32)) +	/* _RX_MAX_FRAMES_HIGH */
104	       nla_total_size(sizeof(u32)) +	/* _TX_USECS_HIGH */
105	       nla_total_size(sizeof(u32)) +	/* _TX_MAX_FRAMES_HIGH */
106	       nla_total_size(sizeof(u32)) +	/* _RATE_SAMPLE_INTERVAL */
107	       nla_total_size(sizeof(u8)) +	/* _USE_CQE_MODE_TX */
108	       nla_total_size(sizeof(u8)) +	/* _USE_CQE_MODE_RX */
109	       nla_total_size(sizeof(u32)) +	/* _TX_AGGR_MAX_BYTES */
110	       nla_total_size(sizeof(u32)) +	/* _TX_AGGR_MAX_FRAMES */
111	       nla_total_size(sizeof(u32));	/* _TX_AGGR_TIME_USECS */
 
112}
113
114static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val,
115			     u32 supported_params)
116{
117	if (!val && !(supported_params & attr_to_mask(attr_type)))
118		return false;
119	return nla_put_u32(skb, attr_type, val);
120}
121
122static bool coalesce_put_bool(struct sk_buff *skb, u16 attr_type, u32 val,
123			      u32 supported_params)
124{
125	if (!val && !(supported_params & attr_to_mask(attr_type)))
126		return false;
127	return nla_put_u8(skb, attr_type, !!val);
128}
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130static int coalesce_fill_reply(struct sk_buff *skb,
131			       const struct ethnl_req_info *req_base,
132			       const struct ethnl_reply_data *reply_base)
133{
134	const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base);
135	const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce;
136	const struct ethtool_coalesce *coal = &data->coalesce;
137	u32 supported = data->supported_params;
 
 
138
139	if (coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS,
140			     coal->rx_coalesce_usecs, supported) ||
141	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES,
142			     coal->rx_max_coalesced_frames, supported) ||
143	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_IRQ,
144			     coal->rx_coalesce_usecs_irq, supported) ||
145	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ,
146			     coal->rx_max_coalesced_frames_irq, supported) ||
147	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS,
148			     coal->tx_coalesce_usecs, supported) ||
149	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES,
150			     coal->tx_max_coalesced_frames, supported) ||
151	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_IRQ,
152			     coal->tx_coalesce_usecs_irq, supported) ||
153	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ,
154			     coal->tx_max_coalesced_frames_irq, supported) ||
155	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_STATS_BLOCK_USECS,
156			     coal->stats_block_coalesce_usecs, supported) ||
157	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX,
158			      coal->use_adaptive_rx_coalesce, supported) ||
159	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX,
160			      coal->use_adaptive_tx_coalesce, supported) ||
161	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_LOW,
162			     coal->pkt_rate_low, supported) ||
163	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_LOW,
164			     coal->rx_coalesce_usecs_low, supported) ||
165	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW,
166			     coal->rx_max_coalesced_frames_low, supported) ||
167	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_LOW,
168			     coal->tx_coalesce_usecs_low, supported) ||
169	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW,
170			     coal->tx_max_coalesced_frames_low, supported) ||
171	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_PKT_RATE_HIGH,
172			     coal->pkt_rate_high, supported) ||
173	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_USECS_HIGH,
174			     coal->rx_coalesce_usecs_high, supported) ||
175	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH,
176			     coal->rx_max_coalesced_frames_high, supported) ||
177	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_USECS_HIGH,
178			     coal->tx_coalesce_usecs_high, supported) ||
179	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH,
180			     coal->tx_max_coalesced_frames_high, supported) ||
181	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL,
182			     coal->rate_sample_interval, supported) ||
183	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX,
184			      kcoal->use_cqe_mode_tx, supported) ||
185	    coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX,
186			      kcoal->use_cqe_mode_rx, supported) ||
187	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES,
188			     kcoal->tx_aggr_max_bytes, supported) ||
189	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES,
190			     kcoal->tx_aggr_max_frames, supported) ||
191	    coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS,
192			     kcoal->tx_aggr_time_usecs, supported))
193		return -EMSGSIZE;
194
195	return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196}
197
198/* COALESCE_SET */
199
 
 
 
 
 
 
 
 
 
 
 
200const struct nla_policy ethnl_coalesce_set_policy[] = {
201	[ETHTOOL_A_COALESCE_HEADER]		=
202		NLA_POLICY_NESTED(ethnl_header_policy),
203	[ETHTOOL_A_COALESCE_RX_USECS]		= { .type = NLA_U32 },
204	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES]	= { .type = NLA_U32 },
205	[ETHTOOL_A_COALESCE_RX_USECS_IRQ]	= { .type = NLA_U32 },
206	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ]	= { .type = NLA_U32 },
207	[ETHTOOL_A_COALESCE_TX_USECS]		= { .type = NLA_U32 },
208	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES]	= { .type = NLA_U32 },
209	[ETHTOOL_A_COALESCE_TX_USECS_IRQ]	= { .type = NLA_U32 },
210	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ]	= { .type = NLA_U32 },
211	[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS]	= { .type = NLA_U32 },
212	[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX]	= { .type = NLA_U8 },
213	[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX]	= { .type = NLA_U8 },
214	[ETHTOOL_A_COALESCE_PKT_RATE_LOW]	= { .type = NLA_U32 },
215	[ETHTOOL_A_COALESCE_RX_USECS_LOW]	= { .type = NLA_U32 },
216	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW]	= { .type = NLA_U32 },
217	[ETHTOOL_A_COALESCE_TX_USECS_LOW]	= { .type = NLA_U32 },
218	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW]	= { .type = NLA_U32 },
219	[ETHTOOL_A_COALESCE_PKT_RATE_HIGH]	= { .type = NLA_U32 },
220	[ETHTOOL_A_COALESCE_RX_USECS_HIGH]	= { .type = NLA_U32 },
221	[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH]	= { .type = NLA_U32 },
222	[ETHTOOL_A_COALESCE_TX_USECS_HIGH]	= { .type = NLA_U32 },
223	[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH]	= { .type = NLA_U32 },
224	[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 },
225	[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX]	= NLA_POLICY_MAX(NLA_U8, 1),
226	[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX]	= NLA_POLICY_MAX(NLA_U8, 1),
227	[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES] = { .type = NLA_U32 },
228	[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES] = { .type = NLA_U32 },
229	[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS] = { .type = NLA_U32 },
 
 
 
 
230};
231
232static int
233ethnl_set_coalesce_validate(struct ethnl_req_info *req_info,
234			    struct genl_info *info)
235{
236	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
 
237	struct nlattr **tb = info->attrs;
238	u32 supported_params;
239	u16 a;
240
241	if (!ops->get_coalesce || !ops->set_coalesce)
242		return -EOPNOTSUPP;
243
244	/* make sure that only supported parameters are present */
245	supported_params = ops->supported_coalesce_params;
 
 
 
 
 
 
246	for (a = ETHTOOL_A_COALESCE_RX_USECS; a < __ETHTOOL_A_COALESCE_CNT; a++)
247		if (tb[a] && !(supported_params & attr_to_mask(a))) {
248			NL_SET_ERR_MSG_ATTR(info->extack, tb[a],
249					    "cannot modify an unsupported parameter");
250			return -EINVAL;
251		}
252
253	return 1;
254}
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256static int
257__ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info,
258		     bool *dual_change)
259{
260	struct kernel_ethtool_coalesce kernel_coalesce = {};
261	struct net_device *dev = req_info->dev;
262	struct ethtool_coalesce coalesce = {};
263	bool mod_mode = false, mod = false;
264	struct nlattr **tb = info->attrs;
265	int ret;
266
267	ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce,
268					     info->extack);
269	if (ret < 0)
270		return ret;
271
272	/* Update values */
273	ethnl_update_u32(&coalesce.rx_coalesce_usecs,
274			 tb[ETHTOOL_A_COALESCE_RX_USECS], &mod);
275	ethnl_update_u32(&coalesce.rx_max_coalesced_frames,
276			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES], &mod);
277	ethnl_update_u32(&coalesce.rx_coalesce_usecs_irq,
278			 tb[ETHTOOL_A_COALESCE_RX_USECS_IRQ], &mod);
279	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_irq,
280			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_IRQ], &mod);
281	ethnl_update_u32(&coalesce.tx_coalesce_usecs,
282			 tb[ETHTOOL_A_COALESCE_TX_USECS], &mod);
283	ethnl_update_u32(&coalesce.tx_max_coalesced_frames,
284			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES], &mod);
285	ethnl_update_u32(&coalesce.tx_coalesce_usecs_irq,
286			 tb[ETHTOOL_A_COALESCE_TX_USECS_IRQ], &mod);
287	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_irq,
288			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_IRQ], &mod);
289	ethnl_update_u32(&coalesce.stats_block_coalesce_usecs,
290			 tb[ETHTOOL_A_COALESCE_STATS_BLOCK_USECS], &mod);
291	ethnl_update_u32(&coalesce.pkt_rate_low,
292			 tb[ETHTOOL_A_COALESCE_PKT_RATE_LOW], &mod);
293	ethnl_update_u32(&coalesce.rx_coalesce_usecs_low,
294			 tb[ETHTOOL_A_COALESCE_RX_USECS_LOW], &mod);
295	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_low,
296			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_LOW], &mod);
297	ethnl_update_u32(&coalesce.tx_coalesce_usecs_low,
298			 tb[ETHTOOL_A_COALESCE_TX_USECS_LOW], &mod);
299	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_low,
300			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_LOW], &mod);
301	ethnl_update_u32(&coalesce.pkt_rate_high,
302			 tb[ETHTOOL_A_COALESCE_PKT_RATE_HIGH], &mod);
303	ethnl_update_u32(&coalesce.rx_coalesce_usecs_high,
304			 tb[ETHTOOL_A_COALESCE_RX_USECS_HIGH], &mod);
305	ethnl_update_u32(&coalesce.rx_max_coalesced_frames_high,
306			 tb[ETHTOOL_A_COALESCE_RX_MAX_FRAMES_HIGH], &mod);
307	ethnl_update_u32(&coalesce.tx_coalesce_usecs_high,
308			 tb[ETHTOOL_A_COALESCE_TX_USECS_HIGH], &mod);
309	ethnl_update_u32(&coalesce.tx_max_coalesced_frames_high,
310			 tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod);
311	ethnl_update_u32(&coalesce.rate_sample_interval,
312			 tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod);
313	ethnl_update_u32(&kernel_coalesce.tx_aggr_max_bytes,
314			 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_BYTES], &mod);
315	ethnl_update_u32(&kernel_coalesce.tx_aggr_max_frames,
316			 tb[ETHTOOL_A_COALESCE_TX_AGGR_MAX_FRAMES], &mod);
317	ethnl_update_u32(&kernel_coalesce.tx_aggr_time_usecs,
318			 tb[ETHTOOL_A_COALESCE_TX_AGGR_TIME_USECS], &mod);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
320	/* Update operation modes */
321	ethnl_update_bool32(&coalesce.use_adaptive_rx_coalesce,
322			    tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_RX], &mod_mode);
323	ethnl_update_bool32(&coalesce.use_adaptive_tx_coalesce,
324			    tb[ETHTOOL_A_COALESCE_USE_ADAPTIVE_TX], &mod_mode);
325	ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx,
326			tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod_mode);
327	ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx,
328			tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod_mode);
329
330	*dual_change = mod && mod_mode;
331	if (!mod && !mod_mode)
332		return 0;
333
334	ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce,
335					     info->extack);
336	return ret < 0 ? ret : 1;
337}
338
339static int
340ethnl_set_coalesce(struct ethnl_req_info *req_info, struct genl_info *info)
341{
342	bool dual_change;
343	int err, ret;
344
345	/* SET_COALESCE may change operation mode and parameters in one call.
346	 * Changing operation mode may cause the driver to reset the parameter
347	 * values, and therefore ignore user input (driver does not know which
348	 * parameters come from user and which are echoed back from ->get).
349	 * To not complicate the drivers if user tries to change both the mode
350	 * and parameters at once - call the driver twice.
351	 */
352	err = __ethnl_set_coalesce(req_info, info, &dual_change);
353	if (err < 0)
354		return err;
355	ret = err;
356
357	if (ret && dual_change) {
358		err = __ethnl_set_coalesce(req_info, info, &dual_change);
359		if (err < 0)
360			return err;
361	}
362	return ret;
363}
364
365const struct ethnl_request_ops ethnl_coalesce_request_ops = {
366	.request_cmd		= ETHTOOL_MSG_COALESCE_GET,
367	.reply_cmd		= ETHTOOL_MSG_COALESCE_GET_REPLY,
368	.hdr_attr		= ETHTOOL_A_COALESCE_HEADER,
369	.req_info_size		= sizeof(struct coalesce_req_info),
370	.reply_data_size	= sizeof(struct coalesce_reply_data),
371
372	.prepare_data		= coalesce_prepare_data,
373	.reply_size		= coalesce_reply_size,
374	.fill_reply		= coalesce_fill_reply,
375
376	.set_validate		= ethnl_set_coalesce_validate,
377	.set			= ethnl_set_coalesce,
378	.set_ntf_cmd		= ETHTOOL_MSG_COALESCE_NTF,
379};