xref: /linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c (revision 34dc1baba215b826e454b8d19e4f24adbeb7d00d)
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 
18 const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
19 	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp1_nve_vxlan_ops,
20 };
21 
22 const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
23 	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp2_nve_vxlan_ops,
24 };
25 
26 struct mlxsw_sp_nve_mc_entry;
27 struct mlxsw_sp_nve_mc_record;
28 struct mlxsw_sp_nve_mc_list;
29 
30 struct 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 
45 struct mlxsw_sp_nve_mc_list_key {
46 	u16 fid_index;
47 };
48 
49 struct mlxsw_sp_nve_mc_ipv6_entry {
50 	struct in6_addr addr6;
51 	u32 addr6_kvdl_index;
52 };
53 
54 struct 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 
62 struct 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 
73 struct 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 
79 static 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 
85 static int
86 mlxsw_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 
95 static void
96 mlxsw_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 
101 static void
102 mlxsw_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 
111 static bool
112 mlxsw_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 
119 static const struct mlxsw_sp_nve_mc_record_ops
120 mlxsw_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 
128 static int
129 mlxsw_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 
146 static void
147 mlxsw_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 
154 static void
155 mlxsw_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 
164 static bool
165 mlxsw_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 
172 static const struct mlxsw_sp_nve_mc_record_ops
173 mlxsw_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 
181 static const struct mlxsw_sp_nve_mc_record_ops *
182 mlxsw_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 
187 int 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 
201 static struct mlxsw_sp_nve_mc_list *
202 mlxsw_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 
211 static struct mlxsw_sp_nve_mc_list *
212 mlxsw_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 
233 err_rhashtable_insert:
234 	kfree(mc_list);
235 	return ERR_PTR(err);
236 }
237 
238 static 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 
249 static struct mlxsw_sp_nve_mc_list *
250 mlxsw_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 
262 static void
263 mlxsw_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 
271 static struct mlxsw_sp_nve_mc_record *
272 mlxsw_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 
298 err_kvdl_alloc:
299 	kfree(mc_record);
300 	return ERR_PTR(err);
301 }
302 
303 static void
304 mlxsw_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 
315 static struct mlxsw_sp_nve_mc_record *
316 mlxsw_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 
334 static void
335 mlxsw_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 
343 static struct mlxsw_sp_nve_mc_entry *
344 mlxsw_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 
360 static int
361 mlxsw_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 
401 static bool
402 mlxsw_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 
413 static struct mlxsw_sp_nve_mc_entry *
414 mlxsw_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 
435 static int
436 mlxsw_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 
469 err_prev_record_refresh:
470 err_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 
477 static void
478 mlxsw_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 
534 static struct mlxsw_sp_nve_mc_record *
535 mlxsw_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 
554 static 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 
572 err_ip_add:
573 	mlxsw_sp_nve_mc_record_put(mc_record);
574 	return err;
575 }
576 
577 static 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 
594 static int
595 mlxsw_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 
613 static void
614 mlxsw_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 
633 int 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 
657 err_fid_flood_index_set:
658 	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
659 err_add_ip:
660 	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
661 	return err;
662 }
663 
664 void 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 
682 static void
683 mlxsw_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 
702 static 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 
726 static 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 
750 err_ops_init:
751 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
752 			   nve->tunnel_index);
753 err_kvdl_alloc:
754 	memset(&nve->config, 0, sizeof(nve->config));
755 	nve->num_nve_tunnels--;
756 	return err;
757 }
758 
759 static 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 
775 static 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 
785 static 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 
800 struct mlxsw_sp_nve_ipv6_ht_key {
801 	u8 mac[ETH_ALEN];
802 	u16 fid_index;
803 };
804 
805 struct 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 
812 static 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 
818 int 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 
825 void 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 
831 static struct mlxsw_sp_nve_ipv6_ht_node *
832 mlxsw_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 
843 static 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 
868 err_rhashtable_insert:
869 	kfree(ipv6_ht_node);
870 	return err;
871 }
872 
873 static void
874 mlxsw_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 
885 int
886 mlxsw_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 
905 void 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 
920 static 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 
936 int 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 
977 err_fdb_replay:
978 	mlxsw_sp_fid_vni_clear(fid);
979 err_fid_vni_set:
980 	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
981 	return err;
982 }
983 
984 void 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 
1010 out:
1011 	mlxsw_sp_fid_vni_clear(fid);
1012 	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
1013 }
1014 
1015 int 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 
1024 void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1025 {
1026 }
1027 
1028 static 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 
1036 static 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 
1056 static 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 
1070 static 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 
1091 static 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 
1102 static 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 
1117 int 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 
1154 err_nve_resources_query:
1155 err_nve_ecn_init:
1156 err_nve_qos_init:
1157 	rhashtable_destroy(&nve->ipv6_ht);
1158 err_ipv6_rhashtable_init:
1159 	rhashtable_destroy(&nve->mc_list_ht);
1160 err_mc_rhashtable_init:
1161 	mlxsw_sp->nve = NULL;
1162 	kfree(nve);
1163 	return err;
1164 }
1165 
1166 void 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