xref: /linux/net/ethtool/rss.c (revision c3e91403103974e8f40dc170f384c0b707fe93e4)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <net/netdev_lock.h>
4 
5 #include "netlink.h"
6 #include "common.h"
7 
8 struct rss_req_info {
9 	struct ethnl_req_info		base;
10 	u32				rss_context;
11 };
12 
13 struct rss_reply_data {
14 	struct ethnl_reply_data		base;
15 	bool				has_flow_hash;
16 	bool				no_key_fields;
17 	u32				indir_size;
18 	u32				hkey_size;
19 	u32				hfunc;
20 	u32				input_xfrm;
21 	u32				*indir_table;
22 	u8				*hkey;
23 	int				flow_hash[__ETHTOOL_A_FLOW_CNT];
24 };
25 
26 static const u8 ethtool_rxfh_ft_nl2ioctl[] = {
27 	[ETHTOOL_A_FLOW_ETHER]		= ETHER_FLOW,
28 	[ETHTOOL_A_FLOW_IP4]		= IPV4_FLOW,
29 	[ETHTOOL_A_FLOW_IP6]		= IPV6_FLOW,
30 	[ETHTOOL_A_FLOW_TCP4]		= TCP_V4_FLOW,
31 	[ETHTOOL_A_FLOW_UDP4]		= UDP_V4_FLOW,
32 	[ETHTOOL_A_FLOW_SCTP4]		= SCTP_V4_FLOW,
33 	[ETHTOOL_A_FLOW_AH_ESP4]	= AH_ESP_V4_FLOW,
34 	[ETHTOOL_A_FLOW_TCP6]		= TCP_V6_FLOW,
35 	[ETHTOOL_A_FLOW_UDP6]		= UDP_V6_FLOW,
36 	[ETHTOOL_A_FLOW_SCTP6]		= SCTP_V6_FLOW,
37 	[ETHTOOL_A_FLOW_AH_ESP6]	= AH_ESP_V6_FLOW,
38 	[ETHTOOL_A_FLOW_AH4]		= AH_V4_FLOW,
39 	[ETHTOOL_A_FLOW_ESP4]		= ESP_V4_FLOW,
40 	[ETHTOOL_A_FLOW_AH6]		= AH_V6_FLOW,
41 	[ETHTOOL_A_FLOW_ESP6]		= ESP_V6_FLOW,
42 	[ETHTOOL_A_FLOW_GTPU4]		= GTPU_V4_FLOW,
43 	[ETHTOOL_A_FLOW_GTPU6]		= GTPU_V6_FLOW,
44 	[ETHTOOL_A_FLOW_GTPC4]		= GTPC_V4_FLOW,
45 	[ETHTOOL_A_FLOW_GTPC6]		= GTPC_V6_FLOW,
46 	[ETHTOOL_A_FLOW_GTPC_TEID4]	= GTPC_TEID_V4_FLOW,
47 	[ETHTOOL_A_FLOW_GTPC_TEID6]	= GTPC_TEID_V6_FLOW,
48 	[ETHTOOL_A_FLOW_GTPU_EH4]	= GTPU_EH_V4_FLOW,
49 	[ETHTOOL_A_FLOW_GTPU_EH6]	= GTPU_EH_V6_FLOW,
50 	[ETHTOOL_A_FLOW_GTPU_UL4]	= GTPU_UL_V4_FLOW,
51 	[ETHTOOL_A_FLOW_GTPU_UL6]	= GTPU_UL_V6_FLOW,
52 	[ETHTOOL_A_FLOW_GTPU_DL4]	= GTPU_DL_V4_FLOW,
53 	[ETHTOOL_A_FLOW_GTPU_DL6]	= GTPU_DL_V6_FLOW,
54 };
55 
56 #define RSS_REQINFO(__req_base) \
57 	container_of(__req_base, struct rss_req_info, base)
58 
59 #define RSS_REPDATA(__reply_base) \
60 	container_of(__reply_base, struct rss_reply_data, base)
61 
62 const struct nla_policy ethnl_rss_get_policy[] = {
63 	[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
64 	[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32 },
65 	[ETHTOOL_A_RSS_START_CONTEXT] = { .type = NLA_U32 },
66 };
67 
68 static int
69 rss_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
70 		  struct netlink_ext_ack *extack)
71 {
72 	struct rss_req_info *request = RSS_REQINFO(req_info);
73 
74 	if (tb[ETHTOOL_A_RSS_CONTEXT])
75 		request->rss_context = nla_get_u32(tb[ETHTOOL_A_RSS_CONTEXT]);
76 	if (tb[ETHTOOL_A_RSS_START_CONTEXT]) {
77 		NL_SET_BAD_ATTR(extack, tb[ETHTOOL_A_RSS_START_CONTEXT]);
78 		return -EINVAL;
79 	}
80 
81 	return 0;
82 }
83 
84 static void
85 rss_prepare_flow_hash(const struct rss_req_info *req, struct net_device *dev,
86 		      struct rss_reply_data *data, const struct genl_info *info)
87 {
88 	int i;
89 
90 	data->has_flow_hash = false;
91 
92 	if (!dev->ethtool_ops->get_rxfh_fields)
93 		return;
94 	if (req->rss_context && !dev->ethtool_ops->rxfh_per_ctx_fields)
95 		return;
96 
97 	mutex_lock(&dev->ethtool->rss_lock);
98 	for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
99 		struct ethtool_rxfh_fields fields = {
100 			.flow_type	= ethtool_rxfh_ft_nl2ioctl[i],
101 			.rss_context	= req->rss_context,
102 		};
103 
104 		if (dev->ethtool_ops->get_rxfh_fields(dev, &fields)) {
105 			data->flow_hash[i] = -1; /* Unsupported */
106 			continue;
107 		}
108 
109 		data->flow_hash[i] = fields.data;
110 		data->has_flow_hash = true;
111 	}
112 	mutex_unlock(&dev->ethtool->rss_lock);
113 }
114 
115 static int
116 rss_prepare_get(const struct rss_req_info *request, struct net_device *dev,
117 		struct rss_reply_data *data, const struct genl_info *info)
118 {
119 	struct ethtool_rxfh_param rxfh = {};
120 	const struct ethtool_ops *ops;
121 	u32 total_size, indir_bytes;
122 	u8 *rss_config;
123 	int ret;
124 
125 	ops = dev->ethtool_ops;
126 
127 	ret = ethnl_ops_begin(dev);
128 	if (ret < 0)
129 		return ret;
130 	mutex_lock(&dev->ethtool->rss_lock);
131 
132 	data->indir_size = 0;
133 	data->hkey_size = 0;
134 	if (ops->get_rxfh_indir_size)
135 		data->indir_size = ops->get_rxfh_indir_size(dev);
136 	if (ops->get_rxfh_key_size)
137 		data->hkey_size = ops->get_rxfh_key_size(dev);
138 
139 	indir_bytes = data->indir_size * sizeof(u32);
140 	total_size = indir_bytes + data->hkey_size;
141 	rss_config = kzalloc(total_size, GFP_KERNEL);
142 	if (!rss_config) {
143 		ret = -ENOMEM;
144 		goto out_unlock;
145 	}
146 
147 	if (data->indir_size)
148 		data->indir_table = (u32 *)rss_config;
149 	if (data->hkey_size)
150 		data->hkey = rss_config + indir_bytes;
151 
152 	rxfh.indir_size = data->indir_size;
153 	rxfh.indir = data->indir_table;
154 	rxfh.key_size = data->hkey_size;
155 	rxfh.key = data->hkey;
156 
157 	ret = ops->get_rxfh(dev, &rxfh);
158 	if (ret)
159 		goto out_unlock;
160 
161 	data->hfunc = rxfh.hfunc;
162 	data->input_xfrm = rxfh.input_xfrm;
163 out_unlock:
164 	mutex_unlock(&dev->ethtool->rss_lock);
165 	ethnl_ops_complete(dev);
166 	return ret;
167 }
168 
169 static int
170 rss_prepare_ctx(const struct rss_req_info *request, struct net_device *dev,
171 		struct rss_reply_data *data, const struct genl_info *info)
172 {
173 	struct ethtool_rxfh_context *ctx;
174 	u32 total_size, indir_bytes;
175 	u8 *rss_config;
176 	int ret;
177 
178 	data->no_key_fields = !dev->ethtool_ops->rxfh_per_ctx_key;
179 
180 	mutex_lock(&dev->ethtool->rss_lock);
181 	ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
182 	if (!ctx) {
183 		ret = -ENOENT;
184 		goto out_unlock;
185 	}
186 
187 	data->indir_size = ctx->indir_size;
188 	data->hkey_size = ctx->key_size;
189 	data->hfunc = ctx->hfunc;
190 	data->input_xfrm = ctx->input_xfrm;
191 
192 	indir_bytes = data->indir_size * sizeof(u32);
193 	total_size = indir_bytes + data->hkey_size;
194 	rss_config = kzalloc(total_size, GFP_KERNEL);
195 	if (!rss_config) {
196 		ret = -ENOMEM;
197 		goto out_unlock;
198 	}
199 
200 	data->indir_table = (u32 *)rss_config;
201 	memcpy(data->indir_table, ethtool_rxfh_context_indir(ctx), indir_bytes);
202 
203 	if (data->hkey_size) {
204 		data->hkey = rss_config + indir_bytes;
205 		memcpy(data->hkey, ethtool_rxfh_context_key(ctx),
206 		       data->hkey_size);
207 	}
208 
209 	ret = 0;
210 out_unlock:
211 	mutex_unlock(&dev->ethtool->rss_lock);
212 	return ret;
213 }
214 
215 static int
216 rss_prepare(const struct rss_req_info *request, struct net_device *dev,
217 	    struct rss_reply_data *data, const struct genl_info *info)
218 {
219 	rss_prepare_flow_hash(request, dev, data, info);
220 
221 	/* Coming from RSS_SET, driver may only have flow_hash_fields ops */
222 	if (!dev->ethtool_ops->get_rxfh)
223 		return 0;
224 
225 	if (request->rss_context)
226 		return rss_prepare_ctx(request, dev, data, info);
227 	return rss_prepare_get(request, dev, data, info);
228 }
229 
230 static int
231 rss_prepare_data(const struct ethnl_req_info *req_base,
232 		 struct ethnl_reply_data *reply_base,
233 		 const struct genl_info *info)
234 {
235 	struct rss_reply_data *data = RSS_REPDATA(reply_base);
236 	struct rss_req_info *request = RSS_REQINFO(req_base);
237 	struct net_device *dev = reply_base->dev;
238 	const struct ethtool_ops *ops;
239 
240 	ops = dev->ethtool_ops;
241 	if (!ops->get_rxfh)
242 		return -EOPNOTSUPP;
243 
244 	/* Some drivers don't handle rss_context */
245 	if (request->rss_context && !ops->create_rxfh_context)
246 		return -EOPNOTSUPP;
247 
248 	return rss_prepare(request, dev, data, info);
249 }
250 
251 static int
252 rss_reply_size(const struct ethnl_req_info *req_base,
253 	       const struct ethnl_reply_data *reply_base)
254 {
255 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
256 	int len;
257 
258 	len = nla_total_size(sizeof(u32)) +	/* _RSS_CONTEXT */
259 	      nla_total_size(sizeof(u32)) +	/* _RSS_HFUNC */
260 	      nla_total_size(sizeof(u32)) +	/* _RSS_INPUT_XFRM */
261 	      nla_total_size(sizeof(u32) * data->indir_size) + /* _RSS_INDIR */
262 	      nla_total_size(data->hkey_size) + /* _RSS_HKEY */
263 	      nla_total_size(0) +		/* _RSS_FLOW_HASH */
264 		nla_total_size(sizeof(u32)) * ETHTOOL_A_FLOW_MAX +
265 	      0;
266 
267 	return len;
268 }
269 
270 static int
271 rss_fill_reply(struct sk_buff *skb, const struct ethnl_req_info *req_base,
272 	       const struct ethnl_reply_data *reply_base)
273 {
274 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
275 	struct rss_req_info *request = RSS_REQINFO(req_base);
276 
277 	if (request->rss_context &&
278 	    nla_put_u32(skb, ETHTOOL_A_RSS_CONTEXT, request->rss_context))
279 		return -EMSGSIZE;
280 
281 	if ((data->indir_size &&
282 	     nla_put(skb, ETHTOOL_A_RSS_INDIR,
283 		     sizeof(u32) * data->indir_size, data->indir_table)))
284 		return -EMSGSIZE;
285 
286 	if (!data->no_key_fields &&
287 	    ((data->hfunc &&
288 	      nla_put_u32(skb, ETHTOOL_A_RSS_HFUNC, data->hfunc)) ||
289 	     (data->input_xfrm &&
290 	      nla_put_u32(skb, ETHTOOL_A_RSS_INPUT_XFRM, data->input_xfrm)) ||
291 	     (data->hkey_size &&
292 	      nla_put(skb, ETHTOOL_A_RSS_HKEY, data->hkey_size, data->hkey))))
293 		return -EMSGSIZE;
294 
295 	if (data->has_flow_hash) {
296 		struct nlattr *nest;
297 		int i;
298 
299 		nest = nla_nest_start(skb, ETHTOOL_A_RSS_FLOW_HASH);
300 		if (!nest)
301 			return -EMSGSIZE;
302 
303 		for (i = 1; i < __ETHTOOL_A_FLOW_CNT; i++) {
304 			if (data->flow_hash[i] >= 0 &&
305 			    nla_put_uint(skb, i, data->flow_hash[i])) {
306 				nla_nest_cancel(skb, nest);
307 				return -EMSGSIZE;
308 			}
309 		}
310 
311 		nla_nest_end(skb, nest);
312 	}
313 
314 	return 0;
315 }
316 
317 static void rss_cleanup_data(struct ethnl_reply_data *reply_base)
318 {
319 	const struct rss_reply_data *data = RSS_REPDATA(reply_base);
320 
321 	kfree(data->indir_table);
322 }
323 
324 struct rss_nl_dump_ctx {
325 	unsigned long		ifindex;
326 	unsigned long		ctx_idx;
327 
328 	/* User wants to only dump contexts from given ifindex */
329 	unsigned int		match_ifindex;
330 	unsigned int		start_ctx;
331 };
332 
333 static struct rss_nl_dump_ctx *rss_dump_ctx(struct netlink_callback *cb)
334 {
335 	NL_ASSERT_CTX_FITS(struct rss_nl_dump_ctx);
336 
337 	return (struct rss_nl_dump_ctx *)cb->ctx;
338 }
339 
340 int ethnl_rss_dump_start(struct netlink_callback *cb)
341 {
342 	const struct genl_info *info = genl_info_dump(cb);
343 	struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
344 	struct ethnl_req_info req_info = {};
345 	struct nlattr **tb = info->attrs;
346 	int ret;
347 
348 	/* Filtering by context not supported */
349 	if (tb[ETHTOOL_A_RSS_CONTEXT]) {
350 		NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_CONTEXT]);
351 		return -EINVAL;
352 	}
353 	if (tb[ETHTOOL_A_RSS_START_CONTEXT]) {
354 		ctx->start_ctx = nla_get_u32(tb[ETHTOOL_A_RSS_START_CONTEXT]);
355 		ctx->ctx_idx = ctx->start_ctx;
356 	}
357 
358 	ret = ethnl_parse_header_dev_get(&req_info,
359 					 tb[ETHTOOL_A_RSS_HEADER],
360 					 sock_net(cb->skb->sk), cb->extack,
361 					 false);
362 	if (req_info.dev) {
363 		ctx->match_ifindex = req_info.dev->ifindex;
364 		ctx->ifindex = ctx->match_ifindex;
365 		ethnl_parse_header_dev_put(&req_info);
366 		req_info.dev = NULL;
367 	}
368 
369 	return ret;
370 }
371 
372 static int
373 rss_dump_one_ctx(struct sk_buff *skb, struct netlink_callback *cb,
374 		 struct net_device *dev, u32 rss_context)
375 {
376 	const struct genl_info *info = genl_info_dump(cb);
377 	struct rss_reply_data data = {};
378 	struct rss_req_info req = {};
379 	void *ehdr;
380 	int ret;
381 
382 	req.rss_context = rss_context;
383 
384 	ehdr = ethnl_dump_put(skb, cb, ETHTOOL_MSG_RSS_GET_REPLY);
385 	if (!ehdr)
386 		return -EMSGSIZE;
387 
388 	ret = ethnl_fill_reply_header(skb, dev, ETHTOOL_A_RSS_HEADER);
389 	if (ret < 0)
390 		goto err_cancel;
391 
392 	ret = rss_prepare(&req, dev, &data, info);
393 	if (ret)
394 		goto err_cancel;
395 
396 	ret = rss_fill_reply(skb, &req.base, &data.base);
397 	if (ret)
398 		goto err_cleanup;
399 	genlmsg_end(skb, ehdr);
400 
401 	rss_cleanup_data(&data.base);
402 	return 0;
403 
404 err_cleanup:
405 	rss_cleanup_data(&data.base);
406 err_cancel:
407 	genlmsg_cancel(skb, ehdr);
408 	return ret;
409 }
410 
411 static int
412 rss_dump_one_dev(struct sk_buff *skb, struct netlink_callback *cb,
413 		 struct net_device *dev)
414 {
415 	struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
416 	int ret;
417 
418 	if (!dev->ethtool_ops->get_rxfh)
419 		return 0;
420 
421 	if (!ctx->ctx_idx) {
422 		ret = rss_dump_one_ctx(skb, cb, dev, 0);
423 		if (ret)
424 			return ret;
425 		ctx->ctx_idx++;
426 	}
427 
428 	for (; xa_find(&dev->ethtool->rss_ctx, &ctx->ctx_idx,
429 		       ULONG_MAX, XA_PRESENT); ctx->ctx_idx++) {
430 		ret = rss_dump_one_ctx(skb, cb, dev, ctx->ctx_idx);
431 		if (ret)
432 			return ret;
433 	}
434 	ctx->ctx_idx = ctx->start_ctx;
435 
436 	return 0;
437 }
438 
439 int ethnl_rss_dumpit(struct sk_buff *skb, struct netlink_callback *cb)
440 {
441 	struct rss_nl_dump_ctx *ctx = rss_dump_ctx(cb);
442 	struct net *net = sock_net(skb->sk);
443 	struct net_device *dev;
444 	int ret = 0;
445 
446 	rtnl_lock();
447 	for_each_netdev_dump(net, dev, ctx->ifindex) {
448 		if (ctx->match_ifindex && ctx->match_ifindex != ctx->ifindex)
449 			break;
450 
451 		netdev_lock_ops(dev);
452 		ret = rss_dump_one_dev(skb, cb, dev);
453 		netdev_unlock_ops(dev);
454 		if (ret)
455 			break;
456 	}
457 	rtnl_unlock();
458 
459 	return ret;
460 }
461 
462 /* RSS_NTF */
463 
464 void ethtool_rss_notify(struct net_device *dev, u32 rss_context)
465 {
466 	struct rss_req_info req_info = {
467 		.rss_context = rss_context,
468 	};
469 
470 	ethnl_notify(dev, ETHTOOL_MSG_RSS_NTF, &req_info.base);
471 }
472 
473 /* RSS_SET */
474 
475 const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_START_CONTEXT + 1] = {
476 	[ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
477 	[ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
478 	[ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
479 };
480 
481 static int
482 ethnl_rss_set_validate(struct ethnl_req_info *req_info, struct genl_info *info)
483 {
484 	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
485 	struct rss_req_info *request = RSS_REQINFO(req_info);
486 	struct nlattr **tb = info->attrs;
487 	struct nlattr *bad_attr = NULL;
488 
489 	if (request->rss_context && !ops->create_rxfh_context)
490 		bad_attr = bad_attr ?: tb[ETHTOOL_A_RSS_CONTEXT];
491 
492 	if (bad_attr) {
493 		NL_SET_BAD_ATTR(info->extack, bad_attr);
494 		return -EOPNOTSUPP;
495 	}
496 
497 	return 1;
498 }
499 
500 static int
501 rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
502 		   struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh,
503 		   bool *reset, bool *mod)
504 {
505 	const struct ethtool_ops *ops = dev->ethtool_ops;
506 	struct netlink_ext_ack *extack = info->extack;
507 	struct nlattr **tb = info->attrs;
508 	struct ethtool_rxnfc rx_rings;
509 	size_t alloc_size;
510 	u32 user_size;
511 	int i, err;
512 
513 	if (!tb[ETHTOOL_A_RSS_INDIR])
514 		return 0;
515 	if (!data->indir_size || !ops->get_rxnfc)
516 		return -EOPNOTSUPP;
517 
518 	rx_rings.cmd = ETHTOOL_GRXRINGS;
519 	err = ops->get_rxnfc(dev, &rx_rings, NULL);
520 	if (err)
521 		return err;
522 
523 	if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) {
524 		NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]);
525 		return -EINVAL;
526 	}
527 	user_size = nla_len(tb[ETHTOOL_A_RSS_INDIR]) / 4;
528 	if (!user_size) {
529 		if (rxfh->rss_context) {
530 			NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_RSS_INDIR],
531 					    "can't reset table for a context");
532 			return -EINVAL;
533 		}
534 		*reset = true;
535 	} else if (data->indir_size % user_size) {
536 		NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
537 					"size (%d) mismatch with device indir table (%d)",
538 					user_size, data->indir_size);
539 		return -EINVAL;
540 	}
541 
542 	rxfh->indir_size = data->indir_size;
543 	alloc_size = array_size(data->indir_size, sizeof(rxfh->indir[0]));
544 	rxfh->indir = kzalloc(alloc_size, GFP_KERNEL);
545 	if (!rxfh->indir)
546 		return -ENOMEM;
547 
548 	nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size);
549 	for (i = 0; i < user_size; i++) {
550 		if (rxfh->indir[i] < rx_rings.data)
551 			continue;
552 
553 		NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR],
554 					"entry %d: queue out of range (%d)",
555 					i, rxfh->indir[i]);
556 		err = -EINVAL;
557 		goto err_free;
558 	}
559 
560 	if (user_size) {
561 		/* Replicate the user-provided table to fill the device table */
562 		for (i = user_size; i < data->indir_size; i++)
563 			rxfh->indir[i] = rxfh->indir[i % user_size];
564 	} else {
565 		for (i = 0; i < data->indir_size; i++)
566 			rxfh->indir[i] =
567 				ethtool_rxfh_indir_default(i, rx_rings.data);
568 	}
569 
570 	*mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
571 
572 	return 0;
573 
574 err_free:
575 	kfree(rxfh->indir);
576 	rxfh->indir = NULL;
577 	return err;
578 }
579 
580 static void
581 rss_set_ctx_update(struct ethtool_rxfh_context *ctx, struct nlattr **tb,
582 		   struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh)
583 {
584 	int i;
585 
586 	if (rxfh->indir) {
587 		for (i = 0; i < data->indir_size; i++)
588 			ethtool_rxfh_context_indir(ctx)[i] = rxfh->indir[i];
589 		ctx->indir_configured = !!nla_len(tb[ETHTOOL_A_RSS_INDIR]);
590 	}
591 }
592 
593 static int
594 ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
595 {
596 	struct rss_req_info *request = RSS_REQINFO(req_info);
597 	struct ethtool_rxfh_context *ctx = NULL;
598 	struct net_device *dev = req_info->dev;
599 	struct ethtool_rxfh_param rxfh = {};
600 	bool indir_reset = false, indir_mod;
601 	struct nlattr **tb = info->attrs;
602 	struct rss_reply_data data = {};
603 	const struct ethtool_ops *ops;
604 	bool mod = false;
605 	int ret;
606 
607 	ops = dev->ethtool_ops;
608 	data.base.dev = dev;
609 
610 	ret = rss_prepare(request, dev, &data, info);
611 	if (ret)
612 		return ret;
613 
614 	rxfh.rss_context = request->rss_context;
615 
616 	ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod);
617 	if (ret)
618 		goto exit_clean_data;
619 	indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
620 
621 	rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
622 	rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
623 
624 	mutex_lock(&dev->ethtool->rss_lock);
625 	if (request->rss_context) {
626 		ctx = xa_load(&dev->ethtool->rss_ctx, request->rss_context);
627 		if (!ctx) {
628 			ret = -ENOENT;
629 			goto exit_unlock;
630 		}
631 	}
632 
633 	if (!mod)
634 		ret = 0; /* nothing to tell the driver */
635 	else if (!ops->set_rxfh)
636 		ret = -EOPNOTSUPP;
637 	else if (!rxfh.rss_context)
638 		ret = ops->set_rxfh(dev, &rxfh, info->extack);
639 	else
640 		ret = ops->modify_rxfh_context(dev, ctx, &rxfh, info->extack);
641 	if (ret)
642 		goto exit_unlock;
643 
644 	if (ctx)
645 		rss_set_ctx_update(ctx, tb, &data, &rxfh);
646 	else if (indir_reset)
647 		dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
648 	else if (indir_mod)
649 		dev->priv_flags |= IFF_RXFH_CONFIGURED;
650 
651 exit_unlock:
652 	mutex_unlock(&dev->ethtool->rss_lock);
653 	kfree(rxfh.indir);
654 exit_clean_data:
655 	rss_cleanup_data(&data.base);
656 
657 	return ret ?: mod;
658 }
659 
660 const struct ethnl_request_ops ethnl_rss_request_ops = {
661 	.request_cmd		= ETHTOOL_MSG_RSS_GET,
662 	.reply_cmd		= ETHTOOL_MSG_RSS_GET_REPLY,
663 	.hdr_attr		= ETHTOOL_A_RSS_HEADER,
664 	.req_info_size		= sizeof(struct rss_req_info),
665 	.reply_data_size	= sizeof(struct rss_reply_data),
666 
667 	.parse_request		= rss_parse_request,
668 	.prepare_data		= rss_prepare_data,
669 	.reply_size		= rss_reply_size,
670 	.fill_reply		= rss_fill_reply,
671 	.cleanup_data		= rss_cleanup_data,
672 
673 	.set_validate		= ethnl_rss_set_validate,
674 	.set			= ethnl_rss_set,
675 	.set_ntf_cmd		= ETHTOOL_MSG_RSS_NTF,
676 };
677