]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
net: introduce default neigh_construct/destroy ndo calls for L2 upper devices
[mirror_ubuntu-artful-kernel.git] / drivers / net / ethernet / mellanox / mlxsw / spectrum_router.c
CommitLineData
464dce18
IS
1/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
3 * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2016 Ido Schimmel <idosch@mellanox.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the names of the copyright holders nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * Alternatively, this software may be distributed under the terms of the
20 * GNU General Public License ("GPL") version 2 as published by the Free
21 * Software Foundation.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36#include <linux/kernel.h>
37#include <linux/types.h>
5e9c16cc
JP
38#include <linux/rhashtable.h>
39#include <linux/bitops.h>
40#include <linux/in6.h>
464dce18
IS
41
42#include "spectrum.h"
43#include "core.h"
44#include "reg.h"
45
53342023
JP
46#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
47 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
48
6b75c480
JP
49static bool
50mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
51 struct mlxsw_sp_prefix_usage *prefix_usage2)
52{
53 unsigned char prefix;
54
55 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
56 if (!test_bit(prefix, prefix_usage2->b))
57 return false;
58 }
59 return true;
60}
61
53342023
JP
62static bool
63mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
64 struct mlxsw_sp_prefix_usage *prefix_usage2)
65{
66 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
67}
68
6b75c480
JP
69static bool
70mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
71{
72 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
73
74 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
75}
76
77static void
78mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
79 struct mlxsw_sp_prefix_usage *prefix_usage2)
80{
81 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
82}
83
84static void
85mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
86{
87 memset(prefix_usage, 0, sizeof(*prefix_usage));
88}
89
5e9c16cc
JP
90static void
91mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
92 unsigned char prefix_len)
93{
94 set_bit(prefix_len, prefix_usage->b);
95}
96
97static void
98mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
99 unsigned char prefix_len)
100{
101 clear_bit(prefix_len, prefix_usage->b);
102}
103
104struct mlxsw_sp_fib_key {
105 unsigned char addr[sizeof(struct in6_addr)];
106 unsigned char prefix_len;
107};
108
61c503f9
JP
109enum mlxsw_sp_fib_entry_type {
110 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
111 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
112 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
113};
114
5e9c16cc
JP
115struct mlxsw_sp_fib_entry {
116 struct rhash_head ht_node;
117 struct mlxsw_sp_fib_key key;
61c503f9
JP
118 enum mlxsw_sp_fib_entry_type type;
119 u8 added:1;
120 u16 rif; /* used for action local */
121 struct mlxsw_sp_vr *vr;
5e9c16cc
JP
122};
123
124struct mlxsw_sp_fib {
125 struct rhashtable ht;
126 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
127 struct mlxsw_sp_prefix_usage prefix_usage;
128};
129
130static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
131 .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
132 .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
133 .key_len = sizeof(struct mlxsw_sp_fib_key),
134 .automatic_shrinking = true,
135};
136
137static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
138 struct mlxsw_sp_fib_entry *fib_entry)
139{
140 unsigned char prefix_len = fib_entry->key.prefix_len;
141 int err;
142
143 err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
144 mlxsw_sp_fib_ht_params);
145 if (err)
146 return err;
147 if (fib->prefix_ref_count[prefix_len]++ == 0)
148 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
149 return 0;
150}
151
152static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
153 struct mlxsw_sp_fib_entry *fib_entry)
154{
155 unsigned char prefix_len = fib_entry->key.prefix_len;
156
157 if (--fib->prefix_ref_count[prefix_len] == 0)
158 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
159 rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
160 mlxsw_sp_fib_ht_params);
161}
162
163static struct mlxsw_sp_fib_entry *
164mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
165 size_t addr_len, unsigned char prefix_len)
166{
167 struct mlxsw_sp_fib_entry *fib_entry;
168
169 fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
170 if (!fib_entry)
171 return NULL;
172 memcpy(fib_entry->key.addr, addr, addr_len);
173 fib_entry->key.prefix_len = prefix_len;
174 return fib_entry;
175}
176
177static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
178{
179 kfree(fib_entry);
180}
181
182static struct mlxsw_sp_fib_entry *
183mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
184 size_t addr_len, unsigned char prefix_len)
185{
186 struct mlxsw_sp_fib_key key = {{ 0 } };
187
188 memcpy(key.addr, addr, addr_len);
189 key.prefix_len = prefix_len;
190 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
191}
192
193static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
194{
195 struct mlxsw_sp_fib *fib;
196 int err;
197
198 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
199 if (!fib)
200 return ERR_PTR(-ENOMEM);
201 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
202 if (err)
203 goto err_rhashtable_init;
204 return fib;
205
206err_rhashtable_init:
207 kfree(fib);
208 return ERR_PTR(err);
209}
210
211static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
212{
213 rhashtable_destroy(&fib->ht);
214 kfree(fib);
215}
216
53342023
JP
217static struct mlxsw_sp_lpm_tree *
218mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
219{
220 static struct mlxsw_sp_lpm_tree *lpm_tree;
221 int i;
222
223 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
224 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
225 if (lpm_tree->ref_count == 0) {
226 if (one_reserved)
227 one_reserved = false;
228 else
229 return lpm_tree;
230 }
231 }
232 return NULL;
233}
234
235static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
236 struct mlxsw_sp_lpm_tree *lpm_tree)
237{
238 char ralta_pl[MLXSW_REG_RALTA_LEN];
239
240 mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
241 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
242}
243
244static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
245 struct mlxsw_sp_lpm_tree *lpm_tree)
246{
247 char ralta_pl[MLXSW_REG_RALTA_LEN];
248
249 mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
250 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
251}
252
253static int
254mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
255 struct mlxsw_sp_prefix_usage *prefix_usage,
256 struct mlxsw_sp_lpm_tree *lpm_tree)
257{
258 char ralst_pl[MLXSW_REG_RALST_LEN];
259 u8 root_bin = 0;
260 u8 prefix;
261 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
262
263 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
264 root_bin = prefix;
265
266 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
267 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
268 if (prefix == 0)
269 continue;
270 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
271 MLXSW_REG_RALST_BIN_NO_CHILD);
272 last_prefix = prefix;
273 }
274 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
275}
276
277static struct mlxsw_sp_lpm_tree *
278mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
279 struct mlxsw_sp_prefix_usage *prefix_usage,
280 enum mlxsw_sp_l3proto proto, bool one_reserved)
281{
282 struct mlxsw_sp_lpm_tree *lpm_tree;
283 int err;
284
285 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
286 if (!lpm_tree)
287 return ERR_PTR(-EBUSY);
288 lpm_tree->proto = proto;
289 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
290 if (err)
291 return ERR_PTR(err);
292
293 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
294 lpm_tree);
295 if (err)
296 goto err_left_struct_set;
297 return lpm_tree;
298
299err_left_struct_set:
300 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
301 return ERR_PTR(err);
302}
303
304static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
305 struct mlxsw_sp_lpm_tree *lpm_tree)
306{
307 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
308}
309
310static struct mlxsw_sp_lpm_tree *
311mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
312 struct mlxsw_sp_prefix_usage *prefix_usage,
313 enum mlxsw_sp_l3proto proto, bool one_reserved)
314{
315 struct mlxsw_sp_lpm_tree *lpm_tree;
316 int i;
317
318 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
319 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
320 if (lpm_tree->proto == proto &&
321 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
322 prefix_usage))
323 goto inc_ref_count;
324 }
325 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
326 proto, one_reserved);
327 if (IS_ERR(lpm_tree))
328 return lpm_tree;
329
330inc_ref_count:
331 lpm_tree->ref_count++;
332 return lpm_tree;
333}
334
335static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
336 struct mlxsw_sp_lpm_tree *lpm_tree)
337{
338 if (--lpm_tree->ref_count == 0)
339 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
340 return 0;
341}
342
343static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
344{
345 struct mlxsw_sp_lpm_tree *lpm_tree;
346 int i;
347
348 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
349 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
350 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
351 }
352}
353
6b75c480
JP
354static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
355{
356 struct mlxsw_sp_vr *vr;
357 int i;
358
359 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
360 vr = &mlxsw_sp->router.vrs[i];
361 if (!vr->used)
362 return vr;
363 }
364 return NULL;
365}
366
367static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
368 struct mlxsw_sp_vr *vr)
369{
370 char raltb_pl[MLXSW_REG_RALTB_LEN];
371
372 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
373 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
374}
375
376static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
377 struct mlxsw_sp_vr *vr)
378{
379 char raltb_pl[MLXSW_REG_RALTB_LEN];
380
381 /* Bind to tree 0 which is default */
382 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
383 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
384}
385
386static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
387{
388 /* For our purpose, squash main and local table into one */
389 if (tb_id == RT_TABLE_LOCAL)
390 tb_id = RT_TABLE_MAIN;
391 return tb_id;
392}
393
394static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
395 u32 tb_id,
396 enum mlxsw_sp_l3proto proto)
397{
398 struct mlxsw_sp_vr *vr;
399 int i;
400
401 tb_id = mlxsw_sp_fix_tb_id(tb_id);
402 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
403 vr = &mlxsw_sp->router.vrs[i];
404 if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
405 return vr;
406 }
407 return NULL;
408}
409
410static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
411 unsigned char prefix_len,
412 u32 tb_id,
413 enum mlxsw_sp_l3proto proto)
414{
415 struct mlxsw_sp_prefix_usage req_prefix_usage;
416 struct mlxsw_sp_lpm_tree *lpm_tree;
417 struct mlxsw_sp_vr *vr;
418 int err;
419
420 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
421 if (!vr)
422 return ERR_PTR(-EBUSY);
423 vr->fib = mlxsw_sp_fib_create();
424 if (IS_ERR(vr->fib))
425 return ERR_CAST(vr->fib);
426
427 vr->proto = proto;
428 vr->tb_id = tb_id;
429 mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
430 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
431 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
432 proto, true);
433 if (IS_ERR(lpm_tree)) {
434 err = PTR_ERR(lpm_tree);
435 goto err_tree_get;
436 }
437 vr->lpm_tree = lpm_tree;
438 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
439 if (err)
440 goto err_tree_bind;
441
442 vr->used = true;
443 return vr;
444
445err_tree_bind:
446 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
447err_tree_get:
448 mlxsw_sp_fib_destroy(vr->fib);
449
450 return ERR_PTR(err);
451}
452
453static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
454 struct mlxsw_sp_vr *vr)
455{
456 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
457 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
458 mlxsw_sp_fib_destroy(vr->fib);
459 vr->used = false;
460}
461
462static int
463mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
464 struct mlxsw_sp_prefix_usage *req_prefix_usage)
465{
466 struct mlxsw_sp_lpm_tree *lpm_tree;
467
468 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage,
469 &vr->lpm_tree->prefix_usage))
470 return 0;
471
472 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
473 vr->proto, false);
474 if (IS_ERR(lpm_tree)) {
475 /* We failed to get a tree according to the required
476 * prefix usage. However, the current tree might be still good
477 * for us if our requirement is subset of the prefixes used
478 * in the tree.
479 */
480 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
481 &vr->lpm_tree->prefix_usage))
482 return 0;
483 return PTR_ERR(lpm_tree);
484 }
485
486 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
487 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
488 vr->lpm_tree = lpm_tree;
489 return mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
490}
491
492static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
493 unsigned char prefix_len,
494 u32 tb_id,
495 enum mlxsw_sp_l3proto proto)
496{
497 struct mlxsw_sp_vr *vr;
498 int err;
499
500 tb_id = mlxsw_sp_fix_tb_id(tb_id);
501 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
502 if (!vr) {
503 vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
504 if (IS_ERR(vr))
505 return vr;
506 } else {
507 struct mlxsw_sp_prefix_usage req_prefix_usage;
508
509 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
510 &vr->fib->prefix_usage);
511 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
512 /* Need to replace LPM tree in case new prefix is required. */
513 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
514 &req_prefix_usage);
515 if (err)
516 return ERR_PTR(err);
517 }
518 return vr;
519}
520
521static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
522{
523 /* Destroy virtual router entity in case the associated FIB is empty
524 * and allow it to be used for other tables in future. Otherwise,
525 * check if some prefix usage did not disappear and change tree if
526 * that is the case. Note that in case new, smaller tree cannot be
527 * allocated, the original one will be kept being used.
528 */
529 if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
530 mlxsw_sp_vr_destroy(mlxsw_sp, vr);
531 else
532 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
533 &vr->fib->prefix_usage);
534}
535
536static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
537{
538 struct mlxsw_sp_vr *vr;
539 int i;
540
541 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
542 vr = &mlxsw_sp->router.vrs[i];
543 vr->id = i;
544 }
545}
546
464dce18
IS
547static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
548{
549 char rgcr_pl[MLXSW_REG_RGCR_LEN];
550
551 mlxsw_reg_rgcr_pack(rgcr_pl, true);
552 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
553 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
554}
555
556static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
557{
558 char rgcr_pl[MLXSW_REG_RGCR_LEN];
559
560 mlxsw_reg_rgcr_pack(rgcr_pl, false);
561 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
562}
563
564int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
565{
53342023
JP
566 int err;
567
568 err = __mlxsw_sp_router_init(mlxsw_sp);
569 if (err)
570 return err;
571 mlxsw_sp_lpm_init(mlxsw_sp);
6b75c480 572 mlxsw_sp_vrs_init(mlxsw_sp);
53342023 573 return 0;
464dce18
IS
574}
575
576void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
577{
578 __mlxsw_sp_router_fini(mlxsw_sp);
579}
61c503f9
JP
580
581static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
582 struct mlxsw_sp_fib_entry *fib_entry,
583 enum mlxsw_reg_ralue_op op)
584{
585 char ralue_pl[MLXSW_REG_RALUE_LEN];
586 u32 *p_dip = (u32 *) fib_entry->key.addr;
587 struct mlxsw_sp_vr *vr = fib_entry->vr;
588
589 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
590 fib_entry->key.prefix_len, *p_dip);
591 mlxsw_reg_ralue_act_local_pack(ralue_pl,
592 MLXSW_REG_RALUE_TRAP_ACTION_NOP, 0,
593 fib_entry->rif);
594 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
595}
596
597static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
598 struct mlxsw_sp_fib_entry *fib_entry,
599 enum mlxsw_reg_ralue_op op)
600{
601 char ralue_pl[MLXSW_REG_RALUE_LEN];
602 u32 *p_dip = (u32 *) fib_entry->key.addr;
603 struct mlxsw_sp_vr *vr = fib_entry->vr;
604
605 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
606 fib_entry->key.prefix_len, *p_dip);
607 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
608 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
609}
610
611static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
612 struct mlxsw_sp_fib_entry *fib_entry,
613 enum mlxsw_reg_ralue_op op)
614{
615 switch (fib_entry->type) {
616 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
617 return -EINVAL;
618 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
619 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
620 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
621 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
622 }
623 return -EINVAL;
624}
625
626static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
627 struct mlxsw_sp_fib_entry *fib_entry,
628 enum mlxsw_reg_ralue_op op)
629{
630 switch (fib_entry->vr->proto) {
631 case MLXSW_SP_L3_PROTO_IPV4:
632 return mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
633 case MLXSW_SP_L3_PROTO_IPV6:
634 return -EINVAL;
635 }
636 return -EINVAL;
637}
638
639static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
640 struct mlxsw_sp_fib_entry *fib_entry)
641{
642 enum mlxsw_reg_ralue_op op;
643
644 op = !fib_entry->added ? MLXSW_REG_RALUE_OP_WRITE_WRITE :
645 MLXSW_REG_RALUE_OP_WRITE_UPDATE;
646 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
647}
648
649static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
650 struct mlxsw_sp_fib_entry *fib_entry)
651{
652 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
653 MLXSW_REG_RALUE_OP_WRITE_DELETE);
654}
655
656struct mlxsw_sp_router_fib4_add_info {
657 struct switchdev_trans_item tritem;
658 struct mlxsw_sp *mlxsw_sp;
659 struct mlxsw_sp_fib_entry *fib_entry;
660};
661
662static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
663{
664 const struct mlxsw_sp_router_fib4_add_info *info = data;
665 struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
666 struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
667
668 mlxsw_sp_fib_entry_destroy(fib_entry);
669 mlxsw_sp_vr_put(mlxsw_sp, fib_entry->vr);
670 kfree(info);
671}
672
673static int
674mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
675 const struct switchdev_obj_ipv4_fib *fib4,
676 struct mlxsw_sp_fib_entry *fib_entry)
677{
678 struct fib_info *fi = fib4->fi;
679
680 if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
681 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
682 return 0;
683 }
684 if (fib4->type != RTN_UNICAST)
685 return -EINVAL;
686
687 if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
688 struct mlxsw_sp_rif *r;
689
690 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
691 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
692 if (!r)
693 return -EINVAL;
694 fib_entry->rif = r->rif;
695 return 0;
696 }
697 return -EINVAL;
698}
699
700static int
701mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
702 const struct switchdev_obj_ipv4_fib *fib4,
703 struct switchdev_trans *trans)
704{
705 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
706 struct mlxsw_sp_router_fib4_add_info *info;
707 struct mlxsw_sp_fib_entry *fib_entry;
708 struct mlxsw_sp_vr *vr;
709 int err;
710
711 vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
712 MLXSW_SP_L3_PROTO_IPV4);
713 if (IS_ERR(vr))
714 return PTR_ERR(vr);
715
716 fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
717 sizeof(fib4->dst), fib4->dst_len);
718 if (!fib_entry) {
719 err = -ENOMEM;
720 goto err_fib_entry_create;
721 }
722 fib_entry->vr = vr;
723
724 err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
725 if (err)
726 goto err_fib4_entry_init;
727
728 info = kmalloc(sizeof(*info), GFP_KERNEL);
729 if (!info) {
730 err = -ENOMEM;
731 goto err_alloc_info;
732 }
733 info->mlxsw_sp = mlxsw_sp;
734 info->fib_entry = fib_entry;
735 switchdev_trans_item_enqueue(trans, info,
736 mlxsw_sp_router_fib4_add_info_destroy,
737 &info->tritem);
738 return 0;
739
740err_alloc_info:
741err_fib4_entry_init:
742 mlxsw_sp_fib_entry_destroy(fib_entry);
743err_fib_entry_create:
744 mlxsw_sp_vr_put(mlxsw_sp, vr);
745 return err;
746}
747
748static int
749mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
750 const struct switchdev_obj_ipv4_fib *fib4,
751 struct switchdev_trans *trans)
752{
753 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
754 struct mlxsw_sp_router_fib4_add_info *info;
755 struct mlxsw_sp_fib_entry *fib_entry;
756 struct mlxsw_sp_vr *vr;
757 int err;
758
759 info = switchdev_trans_item_dequeue(trans);
760 fib_entry = info->fib_entry;
761 kfree(info);
762
763 vr = fib_entry->vr;
764 err = mlxsw_sp_fib_entry_insert(fib_entry->vr->fib, fib_entry);
765 if (err)
766 goto err_fib_entry_insert;
767 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
768 if (err)
769 goto err_fib_entry_add;
770 return 0;
771
772err_fib_entry_add:
773 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
774err_fib_entry_insert:
775 mlxsw_sp_fib_entry_destroy(fib_entry);
776 mlxsw_sp_vr_put(mlxsw_sp, vr);
777 return err;
778}
779
780int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
781 const struct switchdev_obj_ipv4_fib *fib4,
782 struct switchdev_trans *trans)
783{
784 if (switchdev_trans_ph_prepare(trans))
785 return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
786 fib4, trans);
787 return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
788 fib4, trans);
789}
790
791int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
792 const struct switchdev_obj_ipv4_fib *fib4)
793{
794 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
795 struct mlxsw_sp_fib_entry *fib_entry;
796 struct mlxsw_sp_vr *vr;
797
798 vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
799 if (!vr) {
800 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find virtual router for FIB4 entry being removed.\n");
801 return -ENOENT;
802 }
803 fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
804 sizeof(fib4->dst), fib4->dst_len);
805 if (!fib_entry) {
806 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
807 return PTR_ERR(vr);
808 }
809 mlxsw_sp_fib_entry_del(mlxsw_sp_port->mlxsw_sp, fib_entry);
810 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
811 mlxsw_sp_fib_entry_destroy(fib_entry);
812 mlxsw_sp_vr_put(mlxsw_sp, vr);
813 return 0;
814}