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	WARN_ON(1);
134
135	return -EINVAL;
136}
137
138static void
139mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
140				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
141{
142}
143
144static void
145mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
146				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
147				      char *tnumt_pl, unsigned int entry_index)
148{
149	u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index;
150
151	mlxsw_reg_tnumt_udip_ptr_set(tnumt_pl, entry_index, udip_ptr);
152}
153
154static bool
155mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
156					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
157					  const union mlxsw_sp_l3addr *addr)
158{
159	return ipv6_addr_equal(&mc_entry->ipv6_entry.addr6, &addr->addr6);
160}
161
162static const struct mlxsw_sp_nve_mc_record_ops
163mlxsw_sp_nve_mc_record_ipv6_ops = {
164	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV6,
165	.entry_add	= &mlxsw_sp_nve_mc_record_ipv6_entry_add,
166	.entry_del	= &mlxsw_sp_nve_mc_record_ipv6_entry_del,
167	.entry_set	= &mlxsw_sp_nve_mc_record_ipv6_entry_set,
168	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv6_entry_compare,
169};
170
171static const struct mlxsw_sp_nve_mc_record_ops *
172mlxsw_sp_nve_mc_record_ops_arr[] = {
173	[MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops,
174	[MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
175};
176
177int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
178				    enum mlxsw_sp_l3proto proto,
179				    union mlxsw_sp_l3addr *addr)
180{
181	switch (proto) {
182	case MLXSW_SP_L3_PROTO_IPV4:
183		addr->addr4 = cpu_to_be32(uip);
184		return 0;
185	default:
186		WARN_ON(1);
187		return -EINVAL;
188	}
189}
190
191static struct mlxsw_sp_nve_mc_list *
192mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
193			  const struct mlxsw_sp_nve_mc_list_key *key)
194{
195	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
196
197	return rhashtable_lookup_fast(&nve->mc_list_ht, key,
198				      mlxsw_sp_nve_mc_list_ht_params);
199}
200
201static struct mlxsw_sp_nve_mc_list *
202mlxsw_sp_nve_mc_list_create(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	struct mlxsw_sp_nve_mc_list *mc_list;
207	int err;
208
209	mc_list = kmalloc(sizeof(*mc_list), GFP_KERNEL);
210	if (!mc_list)
211		return ERR_PTR(-ENOMEM);
212
213	INIT_LIST_HEAD(&mc_list->records_list);
214	mc_list->key = *key;
215
216	err = rhashtable_insert_fast(&nve->mc_list_ht, &mc_list->ht_node,
217				     mlxsw_sp_nve_mc_list_ht_params);
218	if (err)
219		goto err_rhashtable_insert;
220
221	return mc_list;
222
223err_rhashtable_insert:
224	kfree(mc_list);
225	return ERR_PTR(err);
226}
227
228static void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp,
229					 struct mlxsw_sp_nve_mc_list *mc_list)
230{
231	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
232
233	rhashtable_remove_fast(&nve->mc_list_ht, &mc_list->ht_node,
234			       mlxsw_sp_nve_mc_list_ht_params);
235	WARN_ON(!list_empty(&mc_list->records_list));
236	kfree(mc_list);
237}
238
239static struct mlxsw_sp_nve_mc_list *
240mlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp,
241			 const struct mlxsw_sp_nve_mc_list_key *key)
242{
243	struct mlxsw_sp_nve_mc_list *mc_list;
244
245	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key);
246	if (mc_list)
247		return mc_list;
248
249	return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key);
250}
251
252static void
253mlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp,
254			 struct mlxsw_sp_nve_mc_list *mc_list)
255{
256	if (!list_empty(&mc_list->records_list))
257		return;
258	mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list);
259}
260
261static struct mlxsw_sp_nve_mc_record *
262mlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp,
263			      struct mlxsw_sp_nve_mc_list *mc_list,
264			      enum mlxsw_sp_l3proto proto)
265{
266	unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto];
267	struct mlxsw_sp_nve_mc_record *mc_record;
268	int err;
269
270	mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries),
271			    GFP_KERNEL);
272	if (!mc_record)
273		return ERR_PTR(-ENOMEM);
274
275	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
276				  &mc_record->kvdl_index);
277	if (err)
278		goto err_kvdl_alloc;
279
280	mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto];
281	mc_record->mlxsw_sp = mlxsw_sp;
282	mc_record->mc_list = mc_list;
283	mc_record->proto = proto;
284	list_add_tail(&mc_record->list, &mc_list->records_list);
285
286	return mc_record;
287
288err_kvdl_alloc:
289	kfree(mc_record);
290	return ERR_PTR(err);
291}
292
293static void
294mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record)
295{
296	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
297
298	list_del(&mc_record->list);
299	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
300			   mc_record->kvdl_index);
301	WARN_ON(mc_record->num_entries);
302	kfree(mc_record);
303}
304
305static struct mlxsw_sp_nve_mc_record *
306mlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp,
307			   struct mlxsw_sp_nve_mc_list *mc_list,
308			   enum mlxsw_sp_l3proto proto)
309{
310	struct mlxsw_sp_nve_mc_record *mc_record;
311
312	list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) {
313		unsigned int num_entries = mc_record->num_entries;
314		struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
315
316		if (mc_record->proto == proto &&
317		    num_entries < nve->num_max_mc_entries[proto])
318			return mc_record;
319	}
320
321	return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto);
322}
323
324static void
325mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record)
326{
327	if (mc_record->num_entries != 0)
328		return;
329
330	mlxsw_sp_nve_mc_record_destroy(mc_record);
331}
332
333static struct mlxsw_sp_nve_mc_entry *
334mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record)
335{
336	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
337	unsigned int num_max_entries;
338	int i;
339
340	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
341	for (i = 0; i < num_max_entries; i++) {
342		if (mc_record->entries[i].valid)
343			continue;
344		return &mc_record->entries[i];
345	}
346
347	return NULL;
348}
349
350static int
351mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record)
352{
353	enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type;
354	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
355	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
356	char tnumt_pl[MLXSW_REG_TNUMT_LEN];
357	unsigned int num_max_entries;
358	unsigned int num_entries = 0;
359	u32 next_kvdl_index = 0;
360	bool next_valid = false;
361	int i;
362
363	if (!list_is_last(&mc_record->list, &mc_list->records_list)) {
364		struct mlxsw_sp_nve_mc_record *next_record;
365
366		next_record = list_next_entry(mc_record, list);
367		next_kvdl_index = next_record->kvdl_index;
368		next_valid = true;
369	}
370
371	mlxsw_reg_tnumt_pack(tnumt_pl, type, MLXSW_REG_TNUMT_TUNNEL_PORT_NVE,
372			     mc_record->kvdl_index, next_valid,
373			     next_kvdl_index, mc_record->num_entries);
374
375	num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto];
376	for (i = 0; i < num_max_entries; i++) {
377		struct mlxsw_sp_nve_mc_entry *mc_entry;
378
379		mc_entry = &mc_record->entries[i];
380		if (!mc_entry->valid)
381			continue;
382		mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl,
383					  num_entries++);
384	}
385
386	WARN_ON(num_entries != mc_record->num_entries);
387
388	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnumt), tnumt_pl);
389}
390
391static bool
392mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record)
393{
394	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
395	struct mlxsw_sp_nve_mc_record *first_record;
396
397	first_record = list_first_entry(&mc_list->records_list,
398					struct mlxsw_sp_nve_mc_record, list);
399
400	return mc_record == first_record;
401}
402
403static struct mlxsw_sp_nve_mc_entry *
404mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record,
405			   union mlxsw_sp_l3addr *addr)
406{
407	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
408	unsigned int num_max_entries;
409	int i;
410
411	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
412	for (i = 0; i < num_max_entries; i++) {
413		struct mlxsw_sp_nve_mc_entry *mc_entry;
414
415		mc_entry = &mc_record->entries[i];
416		if (!mc_entry->valid)
417			continue;
418		if (mc_record->ops->entry_compare(mc_record, mc_entry, addr))
419			return mc_entry;
420	}
421
422	return NULL;
423}
424
425static int
426mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record,
427			      union mlxsw_sp_l3addr *addr)
428{
429	struct mlxsw_sp_nve_mc_entry *mc_entry = NULL;
430	int err;
431
432	mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record);
433	if (WARN_ON(!mc_entry))
434		return -EINVAL;
435
436	err = mc_record->ops->entry_add(mc_record, mc_entry, addr);
437	if (err)
438		return err;
439	mc_record->num_entries++;
440	mc_entry->valid = true;
441
442	err = mlxsw_sp_nve_mc_record_refresh(mc_record);
443	if (err)
444		goto err_record_refresh;
445
446	/* If this is a new record and not the first one, then we need to
447	 * update the next pointer of the previous entry
448	 */
449	if (mc_record->num_entries != 1 ||
450	    mlxsw_sp_nve_mc_record_is_first(mc_record))
451		return 0;
452
453	err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list));
454	if (err)
455		goto err_prev_record_refresh;
456
457	return 0;
458
459err_prev_record_refresh:
460err_record_refresh:
461	mc_entry->valid = false;
462	mc_record->num_entries--;
463	mc_record->ops->entry_del(mc_record, mc_entry);
464	return err;
465}
466
467static void
468mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record,
469				 struct mlxsw_sp_nve_mc_entry *mc_entry)
470{
471	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
472
473	mc_entry->valid = false;
474	mc_record->num_entries--;
475
476	/* When the record continues to exist we only need to invalidate
477	 * the requested entry
478	 */
479	if (mc_record->num_entries != 0) {
480		mlxsw_sp_nve_mc_record_refresh(mc_record);
481		mc_record->ops->entry_del(mc_record, mc_entry);
482		return;
483	}
484
485	/* If the record needs to be deleted, but it is not the first,
486	 * then we need to make sure that the previous record no longer
487	 * points to it. Remove deleted record from the list to reflect
488	 * that and then re-add it at the end, so that it could be
489	 * properly removed by the record destruction code
490	 */
491	if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) {
492		struct mlxsw_sp_nve_mc_record *prev_record;
493
494		prev_record = list_prev_entry(mc_record, list);
495		list_del(&mc_record->list);
496		mlxsw_sp_nve_mc_record_refresh(prev_record);
497		list_add_tail(&mc_record->list, &mc_list->records_list);
498		mc_record->ops->entry_del(mc_record, mc_entry);
499		return;
500	}
501
502	/* If the first record needs to be deleted, but the list is not
503	 * singular, then the second record needs to be written in the
504	 * first record's address, as this address is stored as a property
505	 * of the FID
506	 */
507	if (mlxsw_sp_nve_mc_record_is_first(mc_record) &&
508	    !list_is_singular(&mc_list->records_list)) {
509		struct mlxsw_sp_nve_mc_record *next_record;
510
511		next_record = list_next_entry(mc_record, list);
512		swap(mc_record->kvdl_index, next_record->kvdl_index);
513		mlxsw_sp_nve_mc_record_refresh(next_record);
514		mc_record->ops->entry_del(mc_record, mc_entry);
515		return;
516	}
517
518	/* This is the last case where the last remaining record needs to
519	 * be deleted. Simply delete the entry
520	 */
521	mc_record->ops->entry_del(mc_record, mc_entry);
522}
523
524static struct mlxsw_sp_nve_mc_record *
525mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list,
526			    enum mlxsw_sp_l3proto proto,
527			    union mlxsw_sp_l3addr *addr,
528			    struct mlxsw_sp_nve_mc_entry **mc_entry)
529{
530	struct mlxsw_sp_nve_mc_record *mc_record;
531
532	list_for_each_entry(mc_record, &mc_list->records_list, list) {
533		if (mc_record->proto != proto)
534			continue;
535
536		*mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr);
537		if (*mc_entry)
538			return mc_record;
539	}
540
541	return NULL;
542}
543
544static int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp,
545				       struct mlxsw_sp_nve_mc_list *mc_list,
546				       enum mlxsw_sp_l3proto proto,
547				       union mlxsw_sp_l3addr *addr)
548{
549	struct mlxsw_sp_nve_mc_record *mc_record;
550	int err;
551
552	mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto);
553	if (IS_ERR(mc_record))
554		return PTR_ERR(mc_record);
555
556	err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr);
557	if (err)
558		goto err_ip_add;
559
560	return 0;
561
562err_ip_add:
563	mlxsw_sp_nve_mc_record_put(mc_record);
564	return err;
565}
566
567static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
568					struct mlxsw_sp_nve_mc_list *mc_list,
569					enum mlxsw_sp_l3proto proto,
570					union mlxsw_sp_l3addr *addr)
571{
572	struct mlxsw_sp_nve_mc_record *mc_record;
573	struct mlxsw_sp_nve_mc_entry *mc_entry;
574
575	mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
576						&mc_entry);
577	if (!mc_record)
578		return;
579
580	mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
581	mlxsw_sp_nve_mc_record_put(mc_record);
582}
583
584static int
585mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid,
586				 struct mlxsw_sp_nve_mc_list *mc_list)
587{
588	struct mlxsw_sp_nve_mc_record *mc_record;
589
590	/* The address of the first record in the list is a property of
591	 * the FID and we never change it. It only needs to be set when
592	 * a new list is created
593	 */
594	if (mlxsw_sp_fid_nve_flood_index_is_set(fid))
595		return 0;
596
597	mc_record = list_first_entry(&mc_list->records_list,
598				     struct mlxsw_sp_nve_mc_record, list);
599
600	return mlxsw_sp_fid_nve_flood_index_set(fid, mc_record->kvdl_index);
601}
602
603static void
604mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid,
605				   struct mlxsw_sp_nve_mc_list *mc_list)
606{
607	struct mlxsw_sp_nve_mc_record *mc_record;
608
609	/* The address of the first record needs to be invalidated only when
610	 * the last record is about to be removed
611	 */
612	if (!list_is_singular(&mc_list->records_list))
613		return;
614
615	mc_record = list_first_entry(&mc_list->records_list,
616				     struct mlxsw_sp_nve_mc_record, list);
617	if (mc_record->num_entries != 1)
618		return;
619
620	return mlxsw_sp_fid_nve_flood_index_clear(fid);
621}
622
623int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
624			      struct mlxsw_sp_fid *fid,
625			      enum mlxsw_sp_l3proto proto,
626			      union mlxsw_sp_l3addr *addr)
627{
628	struct mlxsw_sp_nve_mc_list_key key = { 0 };
629	struct mlxsw_sp_nve_mc_list *mc_list;
630	int err;
631
632	key.fid_index = mlxsw_sp_fid_index(fid);
633	mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, &key);
634	if (IS_ERR(mc_list))
635		return PTR_ERR(mc_list);
636
637	err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr);
638	if (err)
639		goto err_add_ip;
640
641	err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list);
642	if (err)
643		goto err_fid_flood_index_set;
644
645	return 0;
646
647err_fid_flood_index_set:
648	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
649err_add_ip:
650	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
651	return err;
652}
653
654void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
655			       struct mlxsw_sp_fid *fid,
656			       enum mlxsw_sp_l3proto proto,
657			       union mlxsw_sp_l3addr *addr)
658{
659	struct mlxsw_sp_nve_mc_list_key key = { 0 };
660	struct mlxsw_sp_nve_mc_list *mc_list;
661
662	key.fid_index = mlxsw_sp_fid_index(fid);
663	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
664	if (!mc_list)
665		return;
666
667	mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
668	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
669	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
670}
671
672static void
673mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record)
674{
675	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
676	unsigned int num_max_entries;
677	int i;
678
679	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
680	for (i = 0; i < num_max_entries; i++) {
681		struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i];
682
683		if (!mc_entry->valid)
684			continue;
685		mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
686	}
687
688	WARN_ON(mc_record->num_entries);
689	mlxsw_sp_nve_mc_record_put(mc_record);
690}
691
692static void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp,
693					struct mlxsw_sp_fid *fid)
694{
695	struct mlxsw_sp_nve_mc_record *mc_record, *tmp;
696	struct mlxsw_sp_nve_mc_list_key key = { 0 };
697	struct mlxsw_sp_nve_mc_list *mc_list;
698
699	if (!mlxsw_sp_fid_nve_flood_index_is_set(fid))
700		return;
701
702	mlxsw_sp_fid_nve_flood_index_clear(fid);
703
704	key.fid_index = mlxsw_sp_fid_index(fid);
705	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
706	if (WARN_ON(!mc_list))
707		return;
708
709	list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list)
710		mlxsw_sp_nve_mc_record_delete(mc_record);
711
712	WARN_ON(!list_empty(&mc_list->records_list));
713	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
714}
715
716static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
717				    struct mlxsw_sp_nve_config *config)
718{
719	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
720	const struct mlxsw_sp_nve_ops *ops;
721	int err;
722
723	if (nve->num_nve_tunnels++ != 0)
724		return 0;
725
726	nve->config = *config;
727
728	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
729				  &nve->tunnel_index);
730	if (err)
731		goto err_kvdl_alloc;
732
733	ops = nve->nve_ops_arr[config->type];
734	err = ops->init(nve, config);
735	if (err)
736		goto err_ops_init;
737
738	return 0;
739
740err_ops_init:
741	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
742			   nve->tunnel_index);
743err_kvdl_alloc:
744	memset(&nve->config, 0, sizeof(nve->config));
745	nve->num_nve_tunnels--;
746	return err;
747}
748
749static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
750{
751	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
752	const struct mlxsw_sp_nve_ops *ops;
753
754	ops = nve->nve_ops_arr[nve->config.type];
755
756	if (mlxsw_sp->nve->num_nve_tunnels == 1) {
757		ops->fini(nve);
758		mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
759				   nve->tunnel_index);
760		memset(&nve->config, 0, sizeof(nve->config));
761	}
762	nve->num_nve_tunnels--;
763}
764
765static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
766					  u16 fid_index)
767{
768	char sfdf_pl[MLXSW_REG_SFDF_LEN];
769
770	mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
771	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
772	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
773}
774
775static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
776					   const struct mlxsw_sp_fid *fid,
777					   const struct net_device *nve_dev,
778					   __be32 vni)
779{
780	const struct mlxsw_sp_nve_ops *ops;
781	enum mlxsw_sp_nve_type type;
782
783	if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
784		return;
785
786	ops = mlxsw_sp->nve->nve_ops_arr[type];
787	ops->fdb_clear_offload(nve_dev, vni);
788}
789
790int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
791			    struct mlxsw_sp_nve_params *params,
792			    struct netlink_ext_ack *extack)
793{
794	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
795	const struct mlxsw_sp_nve_ops *ops;
796	struct mlxsw_sp_nve_config config;
797	int err;
798
799	ops = nve->nve_ops_arr[params->type];
800
801	if (!ops->can_offload(nve, params->dev, extack))
802		return -EINVAL;
803
804	memset(&config, 0, sizeof(config));
805	ops->nve_config(nve, params->dev, &config);
806	if (nve->num_nve_tunnels &&
807	    memcmp(&config, &nve->config, sizeof(config))) {
808		NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
809		return -EINVAL;
810	}
811
812	err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
813	if (err) {
814		NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
815		return err;
816	}
817
818	err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
819				   params->dev->ifindex);
820	if (err) {
821		NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
822		goto err_fid_vni_set;
823	}
824
825	err = ops->fdb_replay(params->dev, params->vni, extack);
826	if (err)
827		goto err_fdb_replay;
828
829	return 0;
830
831err_fdb_replay:
832	mlxsw_sp_fid_vni_clear(fid);
833err_fid_vni_set:
834	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
835	return err;
836}
837
838void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
839			      struct mlxsw_sp_fid *fid)
840{
841	u16 fid_index = mlxsw_sp_fid_index(fid);
842	struct net_device *nve_dev;
843	int nve_ifindex;
844	__be32 vni;
845
846	mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
847	mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
848
849	if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
850		    mlxsw_sp_fid_vni(fid, &vni)))
851		goto out;
852
853	nve_dev = dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
854	if (!nve_dev)
855		goto out;
856
857	mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
858	mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
859
860	dev_put(nve_dev);
861
862out:
863	mlxsw_sp_fid_vni_clear(fid);
864	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
865}
866
867int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
868{
869	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
870	char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
871
872	mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
873	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
874}
875
876void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
877{
878}
879
880static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
881{
882	char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
883
884	mlxsw_reg_tnqcr_pack(tnqcr_pl);
885	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
886}
887
888static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
889{
890	int i;
891
892	/* Iterate over inner ECN values */
893	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
894		u8 outer_ecn = INET_ECN_encapsulate(0, i);
895		char tneem_pl[MLXSW_REG_TNEEM_LEN];
896		int err;
897
898		mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
899		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
900				      tneem_pl);
901		if (err)
902			return err;
903	}
904
905	return 0;
906}
907
908static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
909					 u8 inner_ecn, u8 outer_ecn)
910{
911	char tndem_pl[MLXSW_REG_TNDEM_LEN];
912	u8 new_inner_ecn;
913	bool trap_en;
914
915	new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
916						  &trap_en);
917	mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
918			     trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
919	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
920}
921
922static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
923{
924	int i;
925
926	/* Iterate over inner ECN values */
927	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
928		int j;
929
930		/* Iterate over outer ECN values */
931		for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
932			int err;
933
934			err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
935			if (err)
936				return err;
937		}
938	}
939
940	return 0;
941}
942
943static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
944{
945	int err;
946
947	err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
948	if (err)
949		return err;
950
951	return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
952}
953
954static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
955{
956	unsigned int max;
957
958	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
959	    !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
960		return -EIO;
961	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
962	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
963	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
964	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
965
966	return 0;
967}
968
969int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
970{
971	struct mlxsw_sp_nve *nve;
972	int err;
973
974	nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
975	if (!nve)
976		return -ENOMEM;
977	mlxsw_sp->nve = nve;
978	nve->mlxsw_sp = mlxsw_sp;
979	nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
980
981	err = rhashtable_init(&nve->mc_list_ht,
982			      &mlxsw_sp_nve_mc_list_ht_params);
983	if (err)
984		goto err_rhashtable_init;
985
986	err = mlxsw_sp_nve_qos_init(mlxsw_sp);
987	if (err)
988		goto err_nve_qos_init;
989
990	err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
991	if (err)
992		goto err_nve_ecn_init;
993
994	err = mlxsw_sp_nve_resources_query(mlxsw_sp);
995	if (err)
996		goto err_nve_resources_query;
997
998	return 0;
999
1000err_nve_resources_query:
1001err_nve_ecn_init:
1002err_nve_qos_init:
1003	rhashtable_destroy(&nve->mc_list_ht);
1004err_rhashtable_init:
1005	mlxsw_sp->nve = NULL;
1006	kfree(nve);
1007	return err;
1008}
1009
1010void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
1011{
1012	WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
1013	rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
1014	kfree(mlxsw_sp->nve);
1015	mlxsw_sp->nve = NULL;
1016}
1017