1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3
4#include <linux/err.h>
5#include <linux/gfp.h>
6#include <linux/kernel.h>
7#include <linux/list.h>
8#include <linux/netlink.h>
9#include <linux/rtnetlink.h>
10#include <linux/slab.h>
11#include <net/inet_ecn.h>
12#include <net/ipv6.h>
13
14#include "reg.h"
15#include "spectrum.h"
16#include "spectrum_nve.h"
17
18const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
19	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp1_nve_vxlan_ops,
20};
21
22const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
23	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp2_nve_vxlan_ops,
24};
25
26struct mlxsw_sp_nve_mc_entry;
27struct mlxsw_sp_nve_mc_record;
28struct mlxsw_sp_nve_mc_list;
29
30struct mlxsw_sp_nve_mc_record_ops {
31	enum mlxsw_reg_tnumt_record_type type;
32	int (*entry_add)(struct mlxsw_sp_nve_mc_record *mc_record,
33			 struct mlxsw_sp_nve_mc_entry *mc_entry,
34			 const union mlxsw_sp_l3addr *addr);
35	void (*entry_del)(const struct mlxsw_sp_nve_mc_record *mc_record,
36			  const struct mlxsw_sp_nve_mc_entry *mc_entry);
37	void (*entry_set)(const struct mlxsw_sp_nve_mc_record *mc_record,
38			  const struct mlxsw_sp_nve_mc_entry *mc_entry,
39			  char *tnumt_pl, unsigned int entry_index);
40	bool (*entry_compare)(const struct mlxsw_sp_nve_mc_record *mc_record,
41			      const struct mlxsw_sp_nve_mc_entry *mc_entry,
42			      const union mlxsw_sp_l3addr *addr);
43};
44
45struct mlxsw_sp_nve_mc_list_key {
46	u16 fid_index;
47};
48
49struct mlxsw_sp_nve_mc_ipv6_entry {
50	struct in6_addr addr6;
51	u32 addr6_kvdl_index;
52};
53
54struct mlxsw_sp_nve_mc_entry {
55	union {
56		__be32 addr4;
57		struct mlxsw_sp_nve_mc_ipv6_entry ipv6_entry;
58	};
59	u8 valid:1;
60};
61
62struct mlxsw_sp_nve_mc_record {
63	struct list_head list;
64	enum mlxsw_sp_l3proto proto;
65	unsigned int num_entries;
66	struct mlxsw_sp *mlxsw_sp;
67	struct mlxsw_sp_nve_mc_list *mc_list;
68	const struct mlxsw_sp_nve_mc_record_ops *ops;
69	u32 kvdl_index;
70	struct mlxsw_sp_nve_mc_entry entries[];
71};
72
73struct mlxsw_sp_nve_mc_list {
74	struct list_head records_list;
75	struct rhash_head ht_node;
76	struct mlxsw_sp_nve_mc_list_key key;
77};
78
79static const struct rhashtable_params mlxsw_sp_nve_mc_list_ht_params = {
80	.key_len = sizeof(struct mlxsw_sp_nve_mc_list_key),
81	.key_offset = offsetof(struct mlxsw_sp_nve_mc_list, key),
82	.head_offset = offsetof(struct mlxsw_sp_nve_mc_list, ht_node),
83};
84
85static int
86mlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
87				      struct mlxsw_sp_nve_mc_entry *mc_entry,
88				      const union mlxsw_sp_l3addr *addr)
89{
90	mc_entry->addr4 = addr->addr4;
91
92	return 0;
93}
94
95static void
96mlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
97				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
98{
99}
100
101static void
102mlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
103				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
104				      char *tnumt_pl, unsigned int entry_index)
105{
106	u32 udip = be32_to_cpu(mc_entry->addr4);
107
108	mlxsw_reg_tnumt_udip_set(tnumt_pl, entry_index, udip);
109}
110
111static bool
112mlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
113					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
114					  const union mlxsw_sp_l3addr *addr)
115{
116	return mc_entry->addr4 == addr->addr4;
117}
118
119static const struct mlxsw_sp_nve_mc_record_ops
120mlxsw_sp_nve_mc_record_ipv4_ops = {
121	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV4,
122	.entry_add	= &mlxsw_sp_nve_mc_record_ipv4_entry_add,
123	.entry_del	= &mlxsw_sp_nve_mc_record_ipv4_entry_del,
124	.entry_set	= &mlxsw_sp_nve_mc_record_ipv4_entry_set,
125	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv4_entry_compare,
126};
127
128static int
129mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
130				      struct mlxsw_sp_nve_mc_entry *mc_entry,
131				      const union mlxsw_sp_l3addr *addr)
132{
133	u32 kvdl_index;
134	int err;
135
136	err = mlxsw_sp_ipv6_addr_kvdl_index_get(mc_record->mlxsw_sp,
137						&addr->addr6, &kvdl_index);
138	if (err)
139		return err;
140
141	mc_entry->ipv6_entry.addr6 = addr->addr6;
142	mc_entry->ipv6_entry.addr6_kvdl_index = kvdl_index;
143	return 0;
144}
145
146static void
147mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
148				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
149{
150	mlxsw_sp_ipv6_addr_put(mc_record->mlxsw_sp,
151			       &mc_entry->ipv6_entry.addr6);
152}
153
154static void
155mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
156				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
157				      char *tnumt_pl, unsigned int entry_index)
158{
159	u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index;
160
161	mlxsw_reg_tnumt_udip_ptr_set(tnumt_pl, entry_index, udip_ptr);
162}
163
164static bool
165mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
166					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
167					  const union mlxsw_sp_l3addr *addr)
168{
169	return ipv6_addr_equal(&mc_entry->ipv6_entry.addr6, &addr->addr6);
170}
171
172static const struct mlxsw_sp_nve_mc_record_ops
173mlxsw_sp_nve_mc_record_ipv6_ops = {
174	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV6,
175	.entry_add	= &mlxsw_sp_nve_mc_record_ipv6_entry_add,
176	.entry_del	= &mlxsw_sp_nve_mc_record_ipv6_entry_del,
177	.entry_set	= &mlxsw_sp_nve_mc_record_ipv6_entry_set,
178	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv6_entry_compare,
179};
180
181static const struct mlxsw_sp_nve_mc_record_ops *
182mlxsw_sp_nve_mc_record_ops_arr[] = {
183	[MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops,
184	[MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
185};
186
187int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
188				    enum mlxsw_sp_l3proto proto,
189				    union mlxsw_sp_l3addr *addr)
190{
191	switch (proto) {
192	case MLXSW_SP_L3_PROTO_IPV4:
193		addr->addr4 = cpu_to_be32(uip);
194		return 0;
195	default:
196		WARN_ON(1);
197		return -EINVAL;
198	}
199}
200
201static struct mlxsw_sp_nve_mc_list *
202mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
203			  const struct mlxsw_sp_nve_mc_list_key *key)
204{
205	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
206
207	return rhashtable_lookup_fast(&nve->mc_list_ht, key,
208				      mlxsw_sp_nve_mc_list_ht_params);
209}
210
211static struct mlxsw_sp_nve_mc_list *
212mlxsw_sp_nve_mc_list_create(struct mlxsw_sp *mlxsw_sp,
213			    const struct mlxsw_sp_nve_mc_list_key *key)
214{
215	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
216	struct mlxsw_sp_nve_mc_list *mc_list;
217	int err;
218
219	mc_list = kmalloc(sizeof(*mc_list), GFP_KERNEL);
220	if (!mc_list)
221		return ERR_PTR(-ENOMEM);
222
223	INIT_LIST_HEAD(&mc_list->records_list);
224	mc_list->key = *key;
225
226	err = rhashtable_insert_fast(&nve->mc_list_ht, &mc_list->ht_node,
227				     mlxsw_sp_nve_mc_list_ht_params);
228	if (err)
229		goto err_rhashtable_insert;
230
231	return mc_list;
232
233err_rhashtable_insert:
234	kfree(mc_list);
235	return ERR_PTR(err);
236}
237
238static void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp,
239					 struct mlxsw_sp_nve_mc_list *mc_list)
240{
241	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
242
243	rhashtable_remove_fast(&nve->mc_list_ht, &mc_list->ht_node,
244			       mlxsw_sp_nve_mc_list_ht_params);
245	WARN_ON(!list_empty(&mc_list->records_list));
246	kfree(mc_list);
247}
248
249static struct mlxsw_sp_nve_mc_list *
250mlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp,
251			 const struct mlxsw_sp_nve_mc_list_key *key)
252{
253	struct mlxsw_sp_nve_mc_list *mc_list;
254
255	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key);
256	if (mc_list)
257		return mc_list;
258
259	return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key);
260}
261
262static void
263mlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp,
264			 struct mlxsw_sp_nve_mc_list *mc_list)
265{
266	if (!list_empty(&mc_list->records_list))
267		return;
268	mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list);
269}
270
271static struct mlxsw_sp_nve_mc_record *
272mlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp,
273			      struct mlxsw_sp_nve_mc_list *mc_list,
274			      enum mlxsw_sp_l3proto proto)
275{
276	unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto];
277	struct mlxsw_sp_nve_mc_record *mc_record;
278	int err;
279
280	mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries),
281			    GFP_KERNEL);
282	if (!mc_record)
283		return ERR_PTR(-ENOMEM);
284
285	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
286				  &mc_record->kvdl_index);
287	if (err)
288		goto err_kvdl_alloc;
289
290	mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto];
291	mc_record->mlxsw_sp = mlxsw_sp;
292	mc_record->mc_list = mc_list;
293	mc_record->proto = proto;
294	list_add_tail(&mc_record->list, &mc_list->records_list);
295
296	return mc_record;
297
298err_kvdl_alloc:
299	kfree(mc_record);
300	return ERR_PTR(err);
301}
302
303static void
304mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record)
305{
306	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
307
308	list_del(&mc_record->list);
309	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
310			   mc_record->kvdl_index);
311	WARN_ON(mc_record->num_entries);
312	kfree(mc_record);
313}
314
315static struct mlxsw_sp_nve_mc_record *
316mlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp,
317			   struct mlxsw_sp_nve_mc_list *mc_list,
318			   enum mlxsw_sp_l3proto proto)
319{
320	struct mlxsw_sp_nve_mc_record *mc_record;
321
322	list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) {
323		unsigned int num_entries = mc_record->num_entries;
324		struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
325
326		if (mc_record->proto == proto &&
327		    num_entries < nve->num_max_mc_entries[proto])
328			return mc_record;
329	}
330
331	return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto);
332}
333
334static void
335mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record)
336{
337	if (mc_record->num_entries != 0)
338		return;
339
340	mlxsw_sp_nve_mc_record_destroy(mc_record);
341}
342
343static struct mlxsw_sp_nve_mc_entry *
344mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record)
345{
346	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
347	unsigned int num_max_entries;
348	int i;
349
350	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
351	for (i = 0; i < num_max_entries; i++) {
352		if (mc_record->entries[i].valid)
353			continue;
354		return &mc_record->entries[i];
355	}
356
357	return NULL;
358}
359
360static int
361mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record)
362{
363	enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type;
364	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
365	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
366	char tnumt_pl[MLXSW_REG_TNUMT_LEN];
367	unsigned int num_max_entries;
368	unsigned int num_entries = 0;
369	u32 next_kvdl_index = 0;
370	bool next_valid = false;
371	int i;
372
373	if (!list_is_last(&mc_record->list, &mc_list->records_list)) {
374		struct mlxsw_sp_nve_mc_record *next_record;
375
376		next_record = list_next_entry(mc_record, list);
377		next_kvdl_index = next_record->kvdl_index;
378		next_valid = true;
379	}
380
381	mlxsw_reg_tnumt_pack(tnumt_pl, type, MLXSW_REG_TUNNEL_PORT_NVE,
382			     mc_record->kvdl_index, next_valid,
383			     next_kvdl_index, mc_record->num_entries);
384
385	num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto];
386	for (i = 0; i < num_max_entries; i++) {
387		struct mlxsw_sp_nve_mc_entry *mc_entry;
388
389		mc_entry = &mc_record->entries[i];
390		if (!mc_entry->valid)
391			continue;
392		mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl,
393					  num_entries++);
394	}
395
396	WARN_ON(num_entries != mc_record->num_entries);
397
398	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnumt), tnumt_pl);
399}
400
401static bool
402mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record)
403{
404	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
405	struct mlxsw_sp_nve_mc_record *first_record;
406
407	first_record = list_first_entry(&mc_list->records_list,
408					struct mlxsw_sp_nve_mc_record, list);
409
410	return mc_record == first_record;
411}
412
413static struct mlxsw_sp_nve_mc_entry *
414mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record,
415			   union mlxsw_sp_l3addr *addr)
416{
417	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
418	unsigned int num_max_entries;
419	int i;
420
421	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
422	for (i = 0; i < num_max_entries; i++) {
423		struct mlxsw_sp_nve_mc_entry *mc_entry;
424
425		mc_entry = &mc_record->entries[i];
426		if (!mc_entry->valid)
427			continue;
428		if (mc_record->ops->entry_compare(mc_record, mc_entry, addr))
429			return mc_entry;
430	}
431
432	return NULL;
433}
434
435static int
436mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record,
437			      union mlxsw_sp_l3addr *addr)
438{
439	struct mlxsw_sp_nve_mc_entry *mc_entry = NULL;
440	int err;
441
442	mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record);
443	if (WARN_ON(!mc_entry))
444		return -EINVAL;
445
446	err = mc_record->ops->entry_add(mc_record, mc_entry, addr);
447	if (err)
448		return err;
449	mc_record->num_entries++;
450	mc_entry->valid = true;
451
452	err = mlxsw_sp_nve_mc_record_refresh(mc_record);
453	if (err)
454		goto err_record_refresh;
455
456	/* If this is a new record and not the first one, then we need to
457	 * update the next pointer of the previous entry
458	 */
459	if (mc_record->num_entries != 1 ||
460	    mlxsw_sp_nve_mc_record_is_first(mc_record))
461		return 0;
462
463	err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list));
464	if (err)
465		goto err_prev_record_refresh;
466
467	return 0;
468
469err_prev_record_refresh:
470err_record_refresh:
471	mc_entry->valid = false;
472	mc_record->num_entries--;
473	mc_record->ops->entry_del(mc_record, mc_entry);
474	return err;
475}
476
477static void
478mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record,
479				 struct mlxsw_sp_nve_mc_entry *mc_entry)
480{
481	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
482
483	mc_entry->valid = false;
484	mc_record->num_entries--;
485
486	/* When the record continues to exist we only need to invalidate
487	 * the requested entry
488	 */
489	if (mc_record->num_entries != 0) {
490		mlxsw_sp_nve_mc_record_refresh(mc_record);
491		mc_record->ops->entry_del(mc_record, mc_entry);
492		return;
493	}
494
495	/* If the record needs to be deleted, but it is not the first,
496	 * then we need to make sure that the previous record no longer
497	 * points to it. Remove deleted record from the list to reflect
498	 * that and then re-add it at the end, so that it could be
499	 * properly removed by the record destruction code
500	 */
501	if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) {
502		struct mlxsw_sp_nve_mc_record *prev_record;
503
504		prev_record = list_prev_entry(mc_record, list);
505		list_del(&mc_record->list);
506		mlxsw_sp_nve_mc_record_refresh(prev_record);
507		list_add_tail(&mc_record->list, &mc_list->records_list);
508		mc_record->ops->entry_del(mc_record, mc_entry);
509		return;
510	}
511
512	/* If the first record needs to be deleted, but the list is not
513	 * singular, then the second record needs to be written in the
514	 * first record's address, as this address is stored as a property
515	 * of the FID
516	 */
517	if (mlxsw_sp_nve_mc_record_is_first(mc_record) &&
518	    !list_is_singular(&mc_list->records_list)) {
519		struct mlxsw_sp_nve_mc_record *next_record;
520
521		next_record = list_next_entry(mc_record, list);
522		swap(mc_record->kvdl_index, next_record->kvdl_index);
523		mlxsw_sp_nve_mc_record_refresh(next_record);
524		mc_record->ops->entry_del(mc_record, mc_entry);
525		return;
526	}
527
528	/* This is the last case where the last remaining record needs to
529	 * be deleted. Simply delete the entry
530	 */
531	mc_record->ops->entry_del(mc_record, mc_entry);
532}
533
534static struct mlxsw_sp_nve_mc_record *
535mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list,
536			    enum mlxsw_sp_l3proto proto,
537			    union mlxsw_sp_l3addr *addr,
538			    struct mlxsw_sp_nve_mc_entry **mc_entry)
539{
540	struct mlxsw_sp_nve_mc_record *mc_record;
541
542	list_for_each_entry(mc_record, &mc_list->records_list, list) {
543		if (mc_record->proto != proto)
544			continue;
545
546		*mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr);
547		if (*mc_entry)
548			return mc_record;
549	}
550
551	return NULL;
552}
553
554static int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp,
555				       struct mlxsw_sp_nve_mc_list *mc_list,
556				       enum mlxsw_sp_l3proto proto,
557				       union mlxsw_sp_l3addr *addr)
558{
559	struct mlxsw_sp_nve_mc_record *mc_record;
560	int err;
561
562	mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto);
563	if (IS_ERR(mc_record))
564		return PTR_ERR(mc_record);
565
566	err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr);
567	if (err)
568		goto err_ip_add;
569
570	return 0;
571
572err_ip_add:
573	mlxsw_sp_nve_mc_record_put(mc_record);
574	return err;
575}
576
577static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
578					struct mlxsw_sp_nve_mc_list *mc_list,
579					enum mlxsw_sp_l3proto proto,
580					union mlxsw_sp_l3addr *addr)
581{
582	struct mlxsw_sp_nve_mc_record *mc_record;
583	struct mlxsw_sp_nve_mc_entry *mc_entry;
584
585	mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
586						&mc_entry);
587	if (!mc_record)
588		return;
589
590	mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
591	mlxsw_sp_nve_mc_record_put(mc_record);
592}
593
594static int
595mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid,
596				 struct mlxsw_sp_nve_mc_list *mc_list)
597{
598	struct mlxsw_sp_nve_mc_record *mc_record;
599
600	/* The address of the first record in the list is a property of
601	 * the FID and we never change it. It only needs to be set when
602	 * a new list is created
603	 */
604	if (mlxsw_sp_fid_nve_flood_index_is_set(fid))
605		return 0;
606
607	mc_record = list_first_entry(&mc_list->records_list,
608				     struct mlxsw_sp_nve_mc_record, list);
609
610	return mlxsw_sp_fid_nve_flood_index_set(fid, mc_record->kvdl_index);
611}
612
613static void
614mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid,
615				   struct mlxsw_sp_nve_mc_list *mc_list)
616{
617	struct mlxsw_sp_nve_mc_record *mc_record;
618
619	/* The address of the first record needs to be invalidated only when
620	 * the last record is about to be removed
621	 */
622	if (!list_is_singular(&mc_list->records_list))
623		return;
624
625	mc_record = list_first_entry(&mc_list->records_list,
626				     struct mlxsw_sp_nve_mc_record, list);
627	if (mc_record->num_entries != 1)
628		return;
629
630	return mlxsw_sp_fid_nve_flood_index_clear(fid);
631}
632
633int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
634			      struct mlxsw_sp_fid *fid,
635			      enum mlxsw_sp_l3proto proto,
636			      union mlxsw_sp_l3addr *addr)
637{
638	struct mlxsw_sp_nve_mc_list_key key = { 0 };
639	struct mlxsw_sp_nve_mc_list *mc_list;
640	int err;
641
642	key.fid_index = mlxsw_sp_fid_index(fid);
643	mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, &key);
644	if (IS_ERR(mc_list))
645		return PTR_ERR(mc_list);
646
647	err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr);
648	if (err)
649		goto err_add_ip;
650
651	err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list);
652	if (err)
653		goto err_fid_flood_index_set;
654
655	return 0;
656
657err_fid_flood_index_set:
658	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
659err_add_ip:
660	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
661	return err;
662}
663
664void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
665			       struct mlxsw_sp_fid *fid,
666			       enum mlxsw_sp_l3proto proto,
667			       union mlxsw_sp_l3addr *addr)
668{
669	struct mlxsw_sp_nve_mc_list_key key = { 0 };
670	struct mlxsw_sp_nve_mc_list *mc_list;
671
672	key.fid_index = mlxsw_sp_fid_index(fid);
673	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
674	if (!mc_list)
675		return;
676
677	mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
678	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
679	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
680}
681
682static void
683mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record)
684{
685	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
686	unsigned int num_max_entries;
687	int i;
688
689	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
690	for (i = 0; i < num_max_entries; i++) {
691		struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i];
692
693		if (!mc_entry->valid)
694			continue;
695		mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
696	}
697
698	WARN_ON(mc_record->num_entries);
699	mlxsw_sp_nve_mc_record_put(mc_record);
700}
701
702static void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp,
703					struct mlxsw_sp_fid *fid)
704{
705	struct mlxsw_sp_nve_mc_record *mc_record, *tmp;
706	struct mlxsw_sp_nve_mc_list_key key = { 0 };
707	struct mlxsw_sp_nve_mc_list *mc_list;
708
709	if (!mlxsw_sp_fid_nve_flood_index_is_set(fid))
710		return;
711
712	mlxsw_sp_fid_nve_flood_index_clear(fid);
713
714	key.fid_index = mlxsw_sp_fid_index(fid);
715	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
716	if (WARN_ON(!mc_list))
717		return;
718
719	list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list)
720		mlxsw_sp_nve_mc_record_delete(mc_record);
721
722	WARN_ON(!list_empty(&mc_list->records_list));
723	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
724}
725
726static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
727				    struct mlxsw_sp_nve_config *config)
728{
729	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
730	const struct mlxsw_sp_nve_ops *ops;
731	int err;
732
733	if (nve->num_nve_tunnels++ != 0)
734		return 0;
735
736	nve->config = *config;
737
738	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
739				  &nve->tunnel_index);
740	if (err)
741		goto err_kvdl_alloc;
742
743	ops = nve->nve_ops_arr[config->type];
744	err = ops->init(nve, config);
745	if (err)
746		goto err_ops_init;
747
748	return 0;
749
750err_ops_init:
751	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
752			   nve->tunnel_index);
753err_kvdl_alloc:
754	memset(&nve->config, 0, sizeof(nve->config));
755	nve->num_nve_tunnels--;
756	return err;
757}
758
759static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
760{
761	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
762	const struct mlxsw_sp_nve_ops *ops;
763
764	ops = nve->nve_ops_arr[nve->config.type];
765
766	if (mlxsw_sp->nve->num_nve_tunnels == 1) {
767		ops->fini(nve);
768		mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
769				   nve->tunnel_index);
770		memset(&nve->config, 0, sizeof(nve->config));
771	}
772	nve->num_nve_tunnels--;
773}
774
775static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
776					  u16 fid_index)
777{
778	char sfdf_pl[MLXSW_REG_SFDF_LEN];
779
780	mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
781	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
782	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
783}
784
785static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
786					   const struct mlxsw_sp_fid *fid,
787					   const struct net_device *nve_dev,
788					   __be32 vni)
789{
790	const struct mlxsw_sp_nve_ops *ops;
791	enum mlxsw_sp_nve_type type;
792
793	if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
794		return;
795
796	ops = mlxsw_sp->nve->nve_ops_arr[type];
797	ops->fdb_clear_offload(nve_dev, vni);
798}
799
800struct mlxsw_sp_nve_ipv6_ht_key {
801	u8 mac[ETH_ALEN];
802	u16 fid_index;
803};
804
805struct mlxsw_sp_nve_ipv6_ht_node {
806	struct rhash_head ht_node;
807	struct list_head list;
808	struct mlxsw_sp_nve_ipv6_ht_key key;
809	struct in6_addr addr6;
810};
811
812static const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = {
813	.key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key),
814	.key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key),
815	.head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node),
816};
817
818int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
819				    const struct in6_addr *addr6,
820				    u32 *p_kvdl_index)
821{
822	return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index);
823}
824
825void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
826				       const struct in6_addr *addr6)
827{
828	mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6);
829}
830
831static struct mlxsw_sp_nve_ipv6_ht_node *
832mlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac,
833				 u16 fid_index)
834{
835	struct mlxsw_sp_nve_ipv6_ht_key key = {};
836
837	ether_addr_copy(key.mac, mac);
838	key.fid_index = fid_index;
839	return rhashtable_lookup_fast(&mlxsw_sp->nve->ipv6_ht, &key,
840				      mlxsw_sp_nve_ipv6_ht_params);
841}
842
843static int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp,
844				       const char *mac, u16 fid_index,
845				       const struct in6_addr *addr6)
846{
847	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
848	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
849	int err;
850
851	ipv6_ht_node = kzalloc(sizeof(*ipv6_ht_node), GFP_KERNEL);
852	if (!ipv6_ht_node)
853		return -ENOMEM;
854
855	ether_addr_copy(ipv6_ht_node->key.mac, mac);
856	ipv6_ht_node->key.fid_index = fid_index;
857	ipv6_ht_node->addr6 = *addr6;
858
859	err = rhashtable_insert_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
860				     mlxsw_sp_nve_ipv6_ht_params);
861	if (err)
862		goto err_rhashtable_insert;
863
864	list_add(&ipv6_ht_node->list, &nve->ipv6_addr_list);
865
866	return 0;
867
868err_rhashtable_insert:
869	kfree(ipv6_ht_node);
870	return err;
871}
872
873static void
874mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp,
875			    struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node)
876{
877	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
878
879	list_del(&ipv6_ht_node->list);
880	rhashtable_remove_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
881			       mlxsw_sp_nve_ipv6_ht_params);
882	kfree(ipv6_ht_node);
883}
884
885int
886mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
887				   u16 fid_index,
888				   const struct in6_addr *new_addr6)
889{
890	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
891
892	ASSERT_RTNL();
893
894	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
895							fid_index);
896	if (!ipv6_ht_node)
897		return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index,
898						   new_addr6);
899
900	mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
901	ipv6_ht_node->addr6 = *new_addr6;
902	return 0;
903}
904
905void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
906				    u16 fid_index)
907{
908	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
909
910	ASSERT_RTNL();
911
912	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
913							fid_index);
914	if (WARN_ON(!ipv6_ht_node))
915		return;
916
917	mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
918}
919
920static void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
921						u16 fid_index)
922{
923	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp;
924	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
925
926	list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list,
927				 list) {
928		if (ipv6_ht_node->key.fid_index != fid_index)
929			continue;
930
931		mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
932		mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
933	}
934}
935
936int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
937			    struct mlxsw_sp_nve_params *params,
938			    struct netlink_ext_ack *extack)
939{
940	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
941	const struct mlxsw_sp_nve_ops *ops;
942	struct mlxsw_sp_nve_config config;
943	int err;
944
945	ops = nve->nve_ops_arr[params->type];
946
947	if (!ops->can_offload(nve, params, extack))
948		return -EINVAL;
949
950	memset(&config, 0, sizeof(config));
951	ops->nve_config(nve, params, &config);
952	if (nve->num_nve_tunnels &&
953	    memcmp(&config, &nve->config, sizeof(config))) {
954		NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
955		return -EINVAL;
956	}
957
958	err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
959	if (err) {
960		NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
961		return err;
962	}
963
964	err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
965				   params->dev->ifindex);
966	if (err) {
967		NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
968		goto err_fid_vni_set;
969	}
970
971	err = ops->fdb_replay(params->dev, params->vni, extack);
972	if (err)
973		goto err_fdb_replay;
974
975	return 0;
976
977err_fdb_replay:
978	mlxsw_sp_fid_vni_clear(fid);
979err_fid_vni_set:
980	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
981	return err;
982}
983
984void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
985			      struct mlxsw_sp_fid *fid)
986{
987	u16 fid_index = mlxsw_sp_fid_index(fid);
988	struct net_device *nve_dev;
989	int nve_ifindex;
990	__be32 vni;
991
992	/* Necessary for __dev_get_by_index() below. */
993	ASSERT_RTNL();
994
995	mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
996	mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
997	mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index);
998
999	if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
1000		    mlxsw_sp_fid_vni(fid, &vni)))
1001		goto out;
1002
1003	nve_dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
1004	if (!nve_dev)
1005		goto out;
1006
1007	mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
1008	mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
1009
1010out:
1011	mlxsw_sp_fid_vni_clear(fid);
1012	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
1013}
1014
1015int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
1016{
1017	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1018	char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
1019
1020	mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
1021	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
1022}
1023
1024void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1025{
1026}
1027
1028static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
1029{
1030	char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
1031
1032	mlxsw_reg_tnqcr_pack(tnqcr_pl);
1033	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
1034}
1035
1036static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
1037{
1038	int i;
1039
1040	/* Iterate over inner ECN values */
1041	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
1042		u8 outer_ecn = INET_ECN_encapsulate(0, i);
1043		char tneem_pl[MLXSW_REG_TNEEM_LEN];
1044		int err;
1045
1046		mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
1047		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
1048				      tneem_pl);
1049		if (err)
1050			return err;
1051	}
1052
1053	return 0;
1054}
1055
1056static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
1057					 u8 inner_ecn, u8 outer_ecn)
1058{
1059	char tndem_pl[MLXSW_REG_TNDEM_LEN];
1060	u8 new_inner_ecn;
1061	bool trap_en;
1062
1063	new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
1064						  &trap_en);
1065	mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
1066			     trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
1067	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
1068}
1069
1070static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
1071{
1072	int i;
1073
1074	/* Iterate over inner ECN values */
1075	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
1076		int j;
1077
1078		/* Iterate over outer ECN values */
1079		for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
1080			int err;
1081
1082			err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
1083			if (err)
1084				return err;
1085		}
1086	}
1087
1088	return 0;
1089}
1090
1091static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
1092{
1093	int err;
1094
1095	err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
1096	if (err)
1097		return err;
1098
1099	return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
1100}
1101
1102static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
1103{
1104	unsigned int max;
1105
1106	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
1107	    !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
1108		return -EIO;
1109	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
1110	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
1111	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
1112	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
1113
1114	return 0;
1115}
1116
1117int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
1118{
1119	struct mlxsw_sp_nve *nve;
1120	int err;
1121
1122	nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
1123	if (!nve)
1124		return -ENOMEM;
1125	mlxsw_sp->nve = nve;
1126	nve->mlxsw_sp = mlxsw_sp;
1127	nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
1128
1129	err = rhashtable_init(&nve->mc_list_ht,
1130			      &mlxsw_sp_nve_mc_list_ht_params);
1131	if (err)
1132		goto err_mc_rhashtable_init;
1133
1134	err = rhashtable_init(&nve->ipv6_ht, &mlxsw_sp_nve_ipv6_ht_params);
1135	if (err)
1136		goto err_ipv6_rhashtable_init;
1137
1138	INIT_LIST_HEAD(&nve->ipv6_addr_list);
1139
1140	err = mlxsw_sp_nve_qos_init(mlxsw_sp);
1141	if (err)
1142		goto err_nve_qos_init;
1143
1144	err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
1145	if (err)
1146		goto err_nve_ecn_init;
1147
1148	err = mlxsw_sp_nve_resources_query(mlxsw_sp);
1149	if (err)
1150		goto err_nve_resources_query;
1151
1152	return 0;
1153
1154err_nve_resources_query:
1155err_nve_ecn_init:
1156err_nve_qos_init:
1157	rhashtable_destroy(&nve->ipv6_ht);
1158err_ipv6_rhashtable_init:
1159	rhashtable_destroy(&nve->mc_list_ht);
1160err_mc_rhashtable_init:
1161	mlxsw_sp->nve = NULL;
1162	kfree(nve);
1163	return err;
1164}
1165
1166void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
1167{
1168	WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
1169	WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list));
1170	rhashtable_destroy(&mlxsw_sp->nve->ipv6_ht);
1171	rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
1172	kfree(mlxsw_sp->nve);
1173	mlxsw_sp->nve = NULL;
1174}
1175