xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/en/rss.c (revision 4f59c22f26f7723ab2ca6d787d7997b1314ff4f2)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES.
3 
4 #include <linux/ethtool.h>
5 #include "rss.h"
6 
7 #define mlx5e_rss_warn(__dev, format, ...)			\
8 	dev_warn((__dev)->device, "%s:%d:(pid %d): " format,	\
9 		 __func__, __LINE__, current->pid,		\
10 		 ##__VA_ARGS__)
11 
12 static const struct mlx5e_rss_params_traffic_type rss_default_config[MLX5E_NUM_INDIR_TIRS] = {
13 	[MLX5_TT_IPV4_TCP] = {
14 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
15 		.l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
16 		.rx_hash_fields = MLX5_HASH_IP_L4PORTS,
17 	},
18 	[MLX5_TT_IPV6_TCP] = {
19 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
20 		.l4_prot_type = MLX5_L4_PROT_TYPE_TCP,
21 		.rx_hash_fields = MLX5_HASH_IP_L4PORTS,
22 	},
23 	[MLX5_TT_IPV4_UDP] = {
24 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
25 		.l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
26 		.rx_hash_fields = MLX5_HASH_IP_L4PORTS,
27 	},
28 	[MLX5_TT_IPV6_UDP] = {
29 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
30 		.l4_prot_type = MLX5_L4_PROT_TYPE_UDP,
31 		.rx_hash_fields = MLX5_HASH_IP_L4PORTS,
32 	},
33 	[MLX5_TT_IPV4_IPSEC_AH] = {
34 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
35 		.l4_prot_type = 0,
36 		.rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
37 	},
38 	[MLX5_TT_IPV6_IPSEC_AH] = {
39 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
40 		.l4_prot_type = 0,
41 		.rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
42 	},
43 	[MLX5_TT_IPV4_IPSEC_ESP] = {
44 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
45 		.l4_prot_type = 0,
46 		.rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
47 	},
48 	[MLX5_TT_IPV6_IPSEC_ESP] = {
49 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
50 		.l4_prot_type = 0,
51 		.rx_hash_fields = MLX5_HASH_IP_IPSEC_SPI,
52 	},
53 	[MLX5_TT_IPV4] = {
54 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV4,
55 		.l4_prot_type = 0,
56 		.rx_hash_fields = MLX5_HASH_IP,
57 	},
58 	[MLX5_TT_IPV6] = {
59 		.l3_prot_type = MLX5_L3_PROT_TYPE_IPV6,
60 		.l4_prot_type = 0,
61 		.rx_hash_fields = MLX5_HASH_IP,
62 	},
63 };
64 
65 struct mlx5e_rss_params_traffic_type
66 mlx5e_rss_get_default_tt_config(enum mlx5_traffic_types tt)
67 {
68 	return rss_default_config[tt];
69 }
70 
71 struct mlx5e_rss {
72 	struct mlx5e_rss_params_hash hash;
73 	struct mlx5e_rss_params_indir indir;
74 	u32 rx_hash_fields[MLX5E_NUM_INDIR_TIRS];
75 	struct mlx5e_tir *tir[MLX5E_NUM_INDIR_TIRS];
76 	struct mlx5e_tir *inner_tir[MLX5E_NUM_INDIR_TIRS];
77 	struct mlx5e_rqt rqt;
78 	struct mlx5_core_dev *mdev; /* primary */
79 	struct mlx5e_rss_params params;
80 	bool enabled;
81 	refcount_t refcnt;
82 };
83 
84 bool mlx5e_rss_get_inner_ft_support(struct mlx5e_rss *rss)
85 {
86 	return rss->params.inner_ft_support;
87 }
88 
89 u32 *mlx5e_rss_get_indir_table(struct mlx5e_rss *rss)
90 {
91 	return rss->indir.table;
92 }
93 
94 void mlx5e_rss_set_indir_actual_size(struct mlx5e_rss *rss, u32 size)
95 {
96 	rss->indir.actual_table_size = size;
97 }
98 
99 /* Handles non-default contexts, replicate existing pattern into new entries,
100  * matching what ethtool_rxfh_ctxs_resize() does.
101  */
102 void mlx5e_rss_ctx_resize(struct mlx5e_rss *rss, u32 new_size)
103 {
104 	u32 old_size = rss->indir.actual_table_size;
105 	u32 i;
106 
107 	for (i = old_size; i < new_size; i++)
108 		rss->indir.table[i] = rss->indir.table[i % old_size];
109 }
110 
111 void mlx5e_rss_indir_resize(struct mlx5e_rss *rss, struct net_device *netdev,
112 			    u32 new_size)
113 {
114 	ethtool_rxfh_indir_resize(netdev, rss->indir.table,
115 				  rss->indir.actual_table_size, new_size);
116 }
117 
118 int mlx5e_rss_params_indir_init(struct mlx5e_rss_params_indir *indir,
119 				u32 actual_table_size, u32 max_table_size)
120 {
121 	indir->table = kvmalloc_objs(*indir->table, max_table_size);
122 	if (!indir->table)
123 		return -ENOMEM;
124 
125 	indir->max_table_size = max_table_size;
126 	indir->actual_table_size = actual_table_size;
127 
128 	return 0;
129 }
130 
131 void mlx5e_rss_params_indir_cleanup(struct mlx5e_rss_params_indir *indir)
132 {
133 	kvfree(indir->table);
134 }
135 
136 static int mlx5e_rss_copy(struct mlx5e_rss *to, const struct mlx5e_rss *from)
137 {
138 	u32 *dst_indir_table;
139 
140 	if (to->indir.actual_table_size != from->indir.actual_table_size ||
141 	    to->indir.max_table_size != from->indir.max_table_size) {
142 		mlx5e_rss_warn(to->mdev,
143 			       "Failed to copy RSS due to size mismatch, src (actual %u, max %u) != dst (actual %u, max %u)\n",
144 			       from->indir.actual_table_size, from->indir.max_table_size,
145 			       to->indir.actual_table_size, to->indir.max_table_size);
146 		return -EINVAL;
147 	}
148 
149 	dst_indir_table = to->indir.table;
150 	*to = *from;
151 	to->indir.table = dst_indir_table;
152 	memcpy(to->indir.table, from->indir.table,
153 	       from->indir.actual_table_size * sizeof(*from->indir.table));
154 	return 0;
155 }
156 
157 static struct mlx5e_rss *mlx5e_rss_init_copy(const struct mlx5e_rss *from)
158 {
159 	struct mlx5e_rss *rss;
160 	int err;
161 
162 	rss = kvzalloc_obj(*rss);
163 	if (!rss)
164 		return ERR_PTR(-ENOMEM);
165 
166 	err = mlx5e_rss_params_indir_init(&rss->indir,
167 					  from->indir.actual_table_size,
168 					  from->indir.max_table_size);
169 	if (err)
170 		goto err_free_rss;
171 
172 	err = mlx5e_rss_copy(rss, from);
173 	if (err)
174 		goto err_free_indir;
175 
176 	return rss;
177 
178 err_free_indir:
179 	mlx5e_rss_params_indir_cleanup(&rss->indir);
180 err_free_rss:
181 	kvfree(rss);
182 	return ERR_PTR(err);
183 }
184 
185 static void mlx5e_rss_params_init(struct mlx5e_rss *rss)
186 {
187 	enum mlx5_traffic_types tt;
188 
189 	rss->hash.symmetric = true;
190 	rss->hash.hfunc = ETH_RSS_HASH_TOP;
191 	netdev_rss_key_fill(rss->hash.toeplitz_hash_key,
192 			    sizeof(rss->hash.toeplitz_hash_key));
193 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
194 		rss->rx_hash_fields[tt] =
195 			mlx5e_rss_get_default_tt_config(tt).rx_hash_fields;
196 }
197 
198 static struct mlx5e_tir **rss_get_tirp(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
199 				       bool inner)
200 {
201 	return inner ? &rss->inner_tir[tt] : &rss->tir[tt];
202 }
203 
204 static struct mlx5e_tir *rss_get_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
205 				     bool inner)
206 {
207 	return *rss_get_tirp(rss, tt, inner);
208 }
209 
210 static struct mlx5e_rss_params_traffic_type
211 mlx5e_rss_get_tt_config(struct mlx5e_rss *rss, enum mlx5_traffic_types tt)
212 {
213 	struct mlx5e_rss_params_traffic_type rss_tt;
214 
215 	rss_tt = mlx5e_rss_get_default_tt_config(tt);
216 	rss_tt.rx_hash_fields = rss->rx_hash_fields[tt];
217 	return rss_tt;
218 }
219 
220 static int
221 mlx5e_rss_create_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
222 		     const struct mlx5e_packet_merge_param *pkt_merge_param,
223 		     bool inner)
224 {
225 	bool rss_inner = rss->params.inner_ft_support;
226 	struct mlx5e_rss_params_traffic_type rss_tt;
227 	struct mlx5e_tir_builder *builder;
228 	struct mlx5e_tir **tir_p;
229 	struct mlx5e_tir *tir;
230 	u32 rqtn;
231 	int err;
232 
233 	if (inner && !rss_inner) {
234 		mlx5e_rss_warn(rss->mdev,
235 			       "Cannot create inner indirect TIR[%d], RSS inner FT is not supported.\n",
236 			       tt);
237 		return -EINVAL;
238 	}
239 
240 	tir_p = rss_get_tirp(rss, tt, inner);
241 	if (*tir_p)
242 		return -EINVAL;
243 
244 	tir = kvzalloc_obj(*tir);
245 	if (!tir)
246 		return -ENOMEM;
247 
248 	builder = mlx5e_tir_builder_alloc(false);
249 	if (!builder) {
250 		err = -ENOMEM;
251 		goto free_tir;
252 	}
253 
254 	rqtn = mlx5e_rqt_get_rqtn(&rss->rqt);
255 	mlx5e_tir_builder_build_rqt(builder, rss->mdev->mlx5e_res.hw_objs.td.tdn,
256 				    rqtn, rss_inner);
257 	mlx5e_tir_builder_build_packet_merge(builder, pkt_merge_param);
258 	rss_tt = mlx5e_rss_get_tt_config(rss, tt);
259 	mlx5e_tir_builder_build_self_lb_block(builder, rss->params.self_lb_blk,
260 					      rss->params.self_lb_blk);
261 	mlx5e_tir_builder_build_rss(builder, &rss->hash, &rss_tt, inner);
262 
263 	err = mlx5e_tir_init(tir, builder, rss->mdev, true);
264 	mlx5e_tir_builder_free(builder);
265 	if (err) {
266 		mlx5e_rss_warn(rss->mdev, "Failed to create %sindirect TIR: err = %d, tt = %d\n",
267 			       inner ? "inner " : "", err, tt);
268 		goto free_tir;
269 	}
270 
271 	*tir_p = tir;
272 	return 0;
273 
274 free_tir:
275 	kvfree(tir);
276 	return err;
277 }
278 
279 static void mlx5e_rss_destroy_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
280 				  bool inner)
281 {
282 	struct mlx5e_tir **tir_p;
283 	struct mlx5e_tir *tir;
284 
285 	tir_p = rss_get_tirp(rss, tt, inner);
286 	if (!*tir_p)
287 		return;
288 
289 	tir = *tir_p;
290 	mlx5e_tir_destroy(tir);
291 	kvfree(tir);
292 	*tir_p = NULL;
293 }
294 
295 static int
296 mlx5e_rss_create_tirs(struct mlx5e_rss *rss,
297 		      const struct mlx5e_packet_merge_param *pkt_merge_param,
298 		      bool inner)
299 {
300 	enum mlx5_traffic_types tt, max_tt;
301 	int err;
302 
303 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
304 		err = mlx5e_rss_create_tir(rss, tt, pkt_merge_param, inner);
305 		if (err)
306 			goto err_destroy_tirs;
307 	}
308 
309 	return 0;
310 
311 err_destroy_tirs:
312 	max_tt = tt;
313 	for (tt = 0; tt < max_tt; tt++)
314 		mlx5e_rss_destroy_tir(rss, tt, inner);
315 	return err;
316 }
317 
318 static void mlx5e_rss_destroy_tirs(struct mlx5e_rss *rss, bool inner)
319 {
320 	enum mlx5_traffic_types tt;
321 
322 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++)
323 		mlx5e_rss_destroy_tir(rss, tt, inner);
324 }
325 
326 static int mlx5e_rss_update_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
327 				bool inner)
328 {
329 	struct mlx5e_rss_params_traffic_type rss_tt;
330 	struct mlx5e_tir_builder *builder;
331 	struct mlx5e_tir *tir;
332 	int err;
333 
334 	tir = rss_get_tir(rss, tt, inner);
335 	if (!tir)
336 		return 0;
337 
338 	builder = mlx5e_tir_builder_alloc(true);
339 	if (!builder)
340 		return -ENOMEM;
341 
342 	rss_tt = mlx5e_rss_get_tt_config(rss, tt);
343 
344 	mlx5e_tir_builder_build_rss(builder, &rss->hash, &rss_tt, inner);
345 	err = mlx5e_tir_modify(tir, builder);
346 
347 	mlx5e_tir_builder_free(builder);
348 	return err;
349 }
350 
351 static int mlx5e_rss_update_tirs(struct mlx5e_rss *rss)
352 {
353 	enum mlx5_traffic_types tt;
354 	int err, retval;
355 
356 	retval = 0;
357 
358 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
359 		err = mlx5e_rss_update_tir(rss, tt, false);
360 		if (err) {
361 			retval = retval ? : err;
362 			mlx5e_rss_warn(rss->mdev,
363 				       "Failed to update RSS hash of indirect TIR for traffic type %d: err = %d\n",
364 				       tt, err);
365 		}
366 
367 		if (!rss->params.inner_ft_support)
368 			continue;
369 
370 		err = mlx5e_rss_update_tir(rss, tt, true);
371 		if (err) {
372 			retval = retval ? : err;
373 			mlx5e_rss_warn(rss->mdev,
374 				       "Failed to update RSS hash of inner indirect TIR for traffic type %d: err = %d\n",
375 				       tt, err);
376 		}
377 	}
378 	return retval;
379 }
380 
381 static int mlx5e_rss_init_no_tirs(struct mlx5e_rss *rss)
382 {
383 	mlx5e_rss_params_init(rss);
384 	refcount_set(&rss->refcnt, 1);
385 
386 	return mlx5e_rqt_init_direct(&rss->rqt, rss->mdev, true,
387 				     rss->params.drop_rqn,
388 				     rss->indir.max_table_size);
389 }
390 
391 struct mlx5e_rss *
392 mlx5e_rss_init(struct mlx5_core_dev *mdev,
393 	       const struct mlx5e_rss_params *params,
394 	       const struct mlx5e_rss_init_params *init_params)
395 {
396 	u32 rqt_max_size, rqt_size;
397 	struct mlx5e_rss *rss;
398 	int err;
399 
400 	rss = kvzalloc_obj(*rss);
401 	if (!rss)
402 		return ERR_PTR(-ENOMEM);
403 
404 	rqt_size = mlx5e_rqt_size(mdev, init_params->nch);
405 	rqt_max_size = mlx5e_rqt_size(mdev, init_params->max_nch);
406 	err = mlx5e_rss_params_indir_init(&rss->indir, rqt_size, rqt_max_size);
407 	if (err)
408 		goto err_free_rss;
409 
410 	rss->mdev = mdev;
411 	rss->params = *params;
412 
413 	err = mlx5e_rss_init_no_tirs(rss);
414 	if (err)
415 		goto err_free_indir;
416 
417 	if (init_params->type == MLX5E_RSS_INIT_NO_TIRS)
418 		goto out;
419 
420 	err = mlx5e_rss_create_tirs(rss, init_params->pkt_merge_param,
421 				    false);
422 	if (err)
423 		goto err_destroy_rqt;
424 
425 	if (params->inner_ft_support) {
426 		err = mlx5e_rss_create_tirs(rss,
427 					    init_params->pkt_merge_param,
428 					    true);
429 		if (err)
430 			goto err_destroy_tirs;
431 	}
432 
433 out:
434 	return rss;
435 
436 err_destroy_tirs:
437 	mlx5e_rss_destroy_tirs(rss, false);
438 err_destroy_rqt:
439 	mlx5e_rqt_destroy(&rss->rqt);
440 err_free_indir:
441 	mlx5e_rss_params_indir_cleanup(&rss->indir);
442 err_free_rss:
443 	kvfree(rss);
444 	return ERR_PTR(err);
445 }
446 
447 int mlx5e_rss_cleanup(struct mlx5e_rss *rss)
448 {
449 	if (!refcount_dec_if_one(&rss->refcnt))
450 		return -EBUSY;
451 
452 	mlx5e_rss_destroy_tirs(rss, false);
453 
454 	if (rss->params.inner_ft_support)
455 		mlx5e_rss_destroy_tirs(rss, true);
456 
457 	mlx5e_rqt_destroy(&rss->rqt);
458 	mlx5e_rss_params_indir_cleanup(&rss->indir);
459 	kvfree(rss);
460 
461 	return 0;
462 }
463 
464 void mlx5e_rss_refcnt_inc(struct mlx5e_rss *rss)
465 {
466 	refcount_inc(&rss->refcnt);
467 }
468 
469 void mlx5e_rss_refcnt_dec(struct mlx5e_rss *rss)
470 {
471 	refcount_dec(&rss->refcnt);
472 }
473 
474 unsigned int mlx5e_rss_refcnt_read(struct mlx5e_rss *rss)
475 {
476 	return refcount_read(&rss->refcnt);
477 }
478 
479 u32 mlx5e_rss_get_tirn(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
480 		       bool inner)
481 {
482 	struct mlx5e_tir *tir;
483 
484 	WARN_ON(inner && !rss->params.inner_ft_support);
485 	tir = rss_get_tir(rss, tt, inner);
486 	WARN_ON(!tir);
487 
488 	return mlx5e_tir_get_tirn(tir);
489 }
490 
491 u32 mlx5e_rss_get_rqtn(struct mlx5e_rss *rss)
492 {
493 	return mlx5e_rqt_get_rqtn(&rss->rqt);
494 }
495 
496 bool mlx5e_rss_valid_tir(struct mlx5e_rss *rss, enum mlx5_traffic_types tt, bool inner)
497 {
498 	return !!rss_get_tir(rss, tt, inner);
499 }
500 
501 /* Fill the "tirn" output parameter.
502  * Create the requested TIR if it's its first usage.
503  */
504 int
505 mlx5e_rss_obtain_tirn(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
506 		      const struct mlx5e_packet_merge_param *pkt_merge_param,
507 		      bool inner, u32 *tirn)
508 {
509 	struct mlx5e_tir *tir;
510 
511 	tir = rss_get_tir(rss, tt, inner);
512 	if (!tir) { /* TIR doesn't exist, create one */
513 		int err;
514 
515 		err = mlx5e_rss_create_tir(rss, tt, pkt_merge_param, inner);
516 		if (err)
517 			return err;
518 		tir = rss_get_tir(rss, tt, inner);
519 	}
520 
521 	*tirn = mlx5e_tir_get_tirn(tir);
522 	return 0;
523 }
524 
525 static int mlx5e_rss_apply(struct mlx5e_rss *rss, u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
526 {
527 	int err;
528 
529 	err = mlx5e_rqt_redirect_indir(&rss->rqt, rqns, vhca_ids, num_rqns, rss->hash.hfunc,
530 				       &rss->indir);
531 	if (err)
532 		mlx5e_rss_warn(rss->mdev, "Failed to redirect RQT %#x to channels: err = %d\n",
533 			       mlx5e_rqt_get_rqtn(&rss->rqt), err);
534 	return err;
535 }
536 
537 void mlx5e_rss_enable(struct mlx5e_rss *rss, u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
538 {
539 	rss->enabled = true;
540 	mlx5e_rss_apply(rss, rqns, vhca_ids, num_rqns);
541 }
542 
543 void mlx5e_rss_disable(struct mlx5e_rss *rss)
544 {
545 	int err;
546 
547 	rss->enabled = false;
548 	err = mlx5e_rqt_redirect_direct(&rss->rqt, rss->params.drop_rqn, NULL);
549 	if (err)
550 		mlx5e_rss_warn(rss->mdev, "Failed to redirect RQT %#x to drop RQ %#x: err = %d\n",
551 			       mlx5e_rqt_get_rqtn(&rss->rqt),
552 			       rss->params.drop_rqn, err);
553 }
554 
555 int mlx5e_rss_packet_merge_set_param(struct mlx5e_rss *rss,
556 				     struct mlx5e_packet_merge_param *pkt_merge_param)
557 {
558 	struct mlx5e_tir_builder *builder;
559 	enum mlx5_traffic_types tt;
560 	int err, final_err;
561 
562 	builder = mlx5e_tir_builder_alloc(true);
563 	if (!builder)
564 		return -ENOMEM;
565 
566 	mlx5e_tir_builder_build_packet_merge(builder, pkt_merge_param);
567 
568 	final_err = 0;
569 
570 	for (tt = 0; tt < MLX5E_NUM_INDIR_TIRS; tt++) {
571 		struct mlx5e_tir *tir;
572 
573 		tir = rss_get_tir(rss, tt, false);
574 		if (!tir)
575 			goto inner_tir;
576 		err = mlx5e_tir_modify(tir, builder);
577 		if (err) {
578 			mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of indirect TIR %#x for traffic type %d: err = %d\n",
579 				       mlx5e_tir_get_tirn(tir), tt, err);
580 			if (!final_err)
581 				final_err = err;
582 		}
583 
584 inner_tir:
585 		if (!rss->params.inner_ft_support)
586 			continue;
587 
588 		tir = rss_get_tir(rss, tt, true);
589 		if (!tir)
590 			continue;
591 		err = mlx5e_tir_modify(tir, builder);
592 		if (err) {
593 			mlx5e_rss_warn(rss->mdev, "Failed to update packet merge state of inner indirect TIR %#x for traffic type %d: err = %d\n",
594 				       mlx5e_tir_get_tirn(tir), tt, err);
595 			if (!final_err)
596 				final_err = err;
597 		}
598 	}
599 
600 	mlx5e_tir_builder_free(builder);
601 	return final_err;
602 }
603 
604 void mlx5e_rss_get_rxfh(struct mlx5e_rss *rss, u32 *indir, u8 *key, u8 *hfunc,
605 			bool *symmetric)
606 {
607 	if (indir)
608 		memcpy(indir, rss->indir.table,
609 		       rss->indir.actual_table_size * sizeof(*rss->indir.table));
610 
611 	if (key)
612 		memcpy(key, rss->hash.toeplitz_hash_key,
613 		       sizeof(rss->hash.toeplitz_hash_key));
614 
615 	if (hfunc)
616 		*hfunc = rss->hash.hfunc;
617 
618 	if (symmetric)
619 		*symmetric = rss->hash.symmetric;
620 }
621 
622 int mlx5e_rss_set_rxfh(struct mlx5e_rss *rss, const u32 *indir,
623 		       const u8 *key, const u8 *hfunc, const bool *symmetric,
624 		       u32 *rqns, u32 *vhca_ids, unsigned int num_rqns)
625 {
626 	bool changed_indir = false;
627 	bool changed_hash = false;
628 	struct mlx5e_rss *old_rss;
629 	int err = 0;
630 
631 	old_rss = mlx5e_rss_init_copy(rss);
632 	if (IS_ERR(old_rss))
633 		return PTR_ERR(old_rss);
634 
635 	if (hfunc && *hfunc != rss->hash.hfunc) {
636 		switch (*hfunc) {
637 		case ETH_RSS_HASH_XOR:
638 		case ETH_RSS_HASH_TOP:
639 			break;
640 		default:
641 			err = -EINVAL;
642 			goto out;
643 		}
644 		changed_hash = true;
645 		changed_indir = true;
646 		rss->hash.hfunc = *hfunc;
647 	}
648 
649 	if (key) {
650 		if (rss->hash.hfunc == ETH_RSS_HASH_TOP)
651 			changed_hash = true;
652 		memcpy(rss->hash.toeplitz_hash_key, key,
653 		       sizeof(rss->hash.toeplitz_hash_key));
654 	}
655 
656 	if (indir) {
657 		changed_indir = true;
658 
659 		memcpy(rss->indir.table, indir,
660 		       rss->indir.actual_table_size * sizeof(*rss->indir.table));
661 	}
662 
663 	if (symmetric) {
664 		rss->hash.symmetric = *symmetric;
665 		changed_hash = true;
666 	}
667 
668 	if (changed_indir && rss->enabled) {
669 		err = mlx5e_rss_apply(rss, rqns, vhca_ids, num_rqns);
670 		if (err) {
671 			mlx5e_rss_copy(rss, old_rss);
672 			goto out;
673 		}
674 	}
675 
676 	if (changed_hash)
677 		mlx5e_rss_update_tirs(rss);
678 
679 out:
680 	mlx5e_rss_params_indir_cleanup(&old_rss->indir);
681 	kvfree(old_rss);
682 
683 	return err;
684 }
685 
686 struct mlx5e_rss_params_hash mlx5e_rss_get_hash(struct mlx5e_rss *rss)
687 {
688 	return rss->hash;
689 }
690 
691 u8 mlx5e_rss_get_hash_fields(struct mlx5e_rss *rss, enum mlx5_traffic_types tt)
692 {
693 	return rss->rx_hash_fields[tt];
694 }
695 
696 int mlx5e_rss_set_hash_fields(struct mlx5e_rss *rss, enum mlx5_traffic_types tt,
697 			      u8 rx_hash_fields)
698 {
699 	u8 old_rx_hash_fields;
700 	int err;
701 
702 	old_rx_hash_fields = rss->rx_hash_fields[tt];
703 
704 	if (old_rx_hash_fields == rx_hash_fields)
705 		return 0;
706 
707 	rss->rx_hash_fields[tt] = rx_hash_fields;
708 
709 	err = mlx5e_rss_update_tir(rss, tt, false);
710 	if (err) {
711 		rss->rx_hash_fields[tt] = old_rx_hash_fields;
712 		mlx5e_rss_warn(rss->mdev,
713 			       "Failed to update RSS hash fields of indirect TIR for traffic type %d: err = %d\n",
714 			       tt, err);
715 		return err;
716 	}
717 
718 	if (!(rss->params.inner_ft_support))
719 		return 0;
720 
721 	err = mlx5e_rss_update_tir(rss, tt, true);
722 	if (err) {
723 		/* Partial update happened. Try to revert - it may fail too, but
724 		 * there is nothing more we can do.
725 		 */
726 		rss->rx_hash_fields[tt] = old_rx_hash_fields;
727 		mlx5e_rss_warn(rss->mdev,
728 			       "Failed to update RSS hash fields of inner indirect TIR for traffic type %d: err = %d\n",
729 			       tt, err);
730 		if (mlx5e_rss_update_tir(rss, tt, false))
731 			mlx5e_rss_warn(rss->mdev,
732 				       "Partial update of RSS hash fields happened: failed to revert indirect TIR for traffic type %d to the old values\n",
733 				       tt);
734 	}
735 
736 	return err;
737 }
738 
739 void mlx5e_rss_set_indir_uniform(struct mlx5e_rss *rss, unsigned int nch)
740 {
741 	mlx5e_rss_params_indir_init_uniform(&rss->indir, nch);
742 }
743