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