]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c
neigh: Send a notification when DELAY_PROBE_TIME changes
[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>
6cf3c971
JP
41#include <net/neighbour.h>
42#include <net/arp.h>
464dce18
IS
43
44#include "spectrum.h"
45#include "core.h"
46#include "reg.h"
47
53342023
JP
48#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
49 for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
50
6b75c480
JP
51static bool
52mlxsw_sp_prefix_usage_subset(struct mlxsw_sp_prefix_usage *prefix_usage1,
53 struct mlxsw_sp_prefix_usage *prefix_usage2)
54{
55 unsigned char prefix;
56
57 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage1) {
58 if (!test_bit(prefix, prefix_usage2->b))
59 return false;
60 }
61 return true;
62}
63
53342023
JP
64static bool
65mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
66 struct mlxsw_sp_prefix_usage *prefix_usage2)
67{
68 return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
69}
70
6b75c480
JP
71static bool
72mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage)
73{
74 struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } };
75
76 return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none);
77}
78
79static void
80mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1,
81 struct mlxsw_sp_prefix_usage *prefix_usage2)
82{
83 memcpy(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
84}
85
86static void
87mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
88{
89 memset(prefix_usage, 0, sizeof(*prefix_usage));
90}
91
5e9c16cc
JP
92static void
93mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
94 unsigned char prefix_len)
95{
96 set_bit(prefix_len, prefix_usage->b);
97}
98
99static void
100mlxsw_sp_prefix_usage_clear(struct mlxsw_sp_prefix_usage *prefix_usage,
101 unsigned char prefix_len)
102{
103 clear_bit(prefix_len, prefix_usage->b);
104}
105
106struct mlxsw_sp_fib_key {
107 unsigned char addr[sizeof(struct in6_addr)];
108 unsigned char prefix_len;
109};
110
61c503f9
JP
111enum mlxsw_sp_fib_entry_type {
112 MLXSW_SP_FIB_ENTRY_TYPE_REMOTE,
113 MLXSW_SP_FIB_ENTRY_TYPE_LOCAL,
114 MLXSW_SP_FIB_ENTRY_TYPE_TRAP,
115};
116
5e9c16cc
JP
117struct mlxsw_sp_fib_entry {
118 struct rhash_head ht_node;
119 struct mlxsw_sp_fib_key key;
61c503f9
JP
120 enum mlxsw_sp_fib_entry_type type;
121 u8 added:1;
122 u16 rif; /* used for action local */
123 struct mlxsw_sp_vr *vr;
5e9c16cc
JP
124};
125
126struct mlxsw_sp_fib {
127 struct rhashtable ht;
128 unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT];
129 struct mlxsw_sp_prefix_usage prefix_usage;
130};
131
132static const struct rhashtable_params mlxsw_sp_fib_ht_params = {
133 .key_offset = offsetof(struct mlxsw_sp_fib_entry, key),
134 .head_offset = offsetof(struct mlxsw_sp_fib_entry, ht_node),
135 .key_len = sizeof(struct mlxsw_sp_fib_key),
136 .automatic_shrinking = true,
137};
138
139static int mlxsw_sp_fib_entry_insert(struct mlxsw_sp_fib *fib,
140 struct mlxsw_sp_fib_entry *fib_entry)
141{
142 unsigned char prefix_len = fib_entry->key.prefix_len;
143 int err;
144
145 err = rhashtable_insert_fast(&fib->ht, &fib_entry->ht_node,
146 mlxsw_sp_fib_ht_params);
147 if (err)
148 return err;
149 if (fib->prefix_ref_count[prefix_len]++ == 0)
150 mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len);
151 return 0;
152}
153
154static void mlxsw_sp_fib_entry_remove(struct mlxsw_sp_fib *fib,
155 struct mlxsw_sp_fib_entry *fib_entry)
156{
157 unsigned char prefix_len = fib_entry->key.prefix_len;
158
159 if (--fib->prefix_ref_count[prefix_len] == 0)
160 mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len);
161 rhashtable_remove_fast(&fib->ht, &fib_entry->ht_node,
162 mlxsw_sp_fib_ht_params);
163}
164
165static struct mlxsw_sp_fib_entry *
166mlxsw_sp_fib_entry_create(struct mlxsw_sp_fib *fib, const void *addr,
167 size_t addr_len, unsigned char prefix_len)
168{
169 struct mlxsw_sp_fib_entry *fib_entry;
170
171 fib_entry = kzalloc(sizeof(*fib_entry), GFP_KERNEL);
172 if (!fib_entry)
173 return NULL;
174 memcpy(fib_entry->key.addr, addr, addr_len);
175 fib_entry->key.prefix_len = prefix_len;
176 return fib_entry;
177}
178
179static void mlxsw_sp_fib_entry_destroy(struct mlxsw_sp_fib_entry *fib_entry)
180{
181 kfree(fib_entry);
182}
183
184static struct mlxsw_sp_fib_entry *
185mlxsw_sp_fib_entry_lookup(struct mlxsw_sp_fib *fib, const void *addr,
186 size_t addr_len, unsigned char prefix_len)
187{
188 struct mlxsw_sp_fib_key key = {{ 0 } };
189
190 memcpy(key.addr, addr, addr_len);
191 key.prefix_len = prefix_len;
192 return rhashtable_lookup_fast(&fib->ht, &key, mlxsw_sp_fib_ht_params);
193}
194
195static struct mlxsw_sp_fib *mlxsw_sp_fib_create(void)
196{
197 struct mlxsw_sp_fib *fib;
198 int err;
199
200 fib = kzalloc(sizeof(*fib), GFP_KERNEL);
201 if (!fib)
202 return ERR_PTR(-ENOMEM);
203 err = rhashtable_init(&fib->ht, &mlxsw_sp_fib_ht_params);
204 if (err)
205 goto err_rhashtable_init;
206 return fib;
207
208err_rhashtable_init:
209 kfree(fib);
210 return ERR_PTR(err);
211}
212
213static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib)
214{
215 rhashtable_destroy(&fib->ht);
216 kfree(fib);
217}
218
53342023
JP
219static struct mlxsw_sp_lpm_tree *
220mlxsw_sp_lpm_tree_find_unused(struct mlxsw_sp *mlxsw_sp, bool one_reserved)
221{
222 static struct mlxsw_sp_lpm_tree *lpm_tree;
223 int i;
224
225 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
226 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
227 if (lpm_tree->ref_count == 0) {
228 if (one_reserved)
229 one_reserved = false;
230 else
231 return lpm_tree;
232 }
233 }
234 return NULL;
235}
236
237static int mlxsw_sp_lpm_tree_alloc(struct mlxsw_sp *mlxsw_sp,
238 struct mlxsw_sp_lpm_tree *lpm_tree)
239{
240 char ralta_pl[MLXSW_REG_RALTA_LEN];
241
242 mlxsw_reg_ralta_pack(ralta_pl, true, lpm_tree->proto, lpm_tree->id);
243 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
244}
245
246static int mlxsw_sp_lpm_tree_free(struct mlxsw_sp *mlxsw_sp,
247 struct mlxsw_sp_lpm_tree *lpm_tree)
248{
249 char ralta_pl[MLXSW_REG_RALTA_LEN];
250
251 mlxsw_reg_ralta_pack(ralta_pl, false, lpm_tree->proto, lpm_tree->id);
252 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralta), ralta_pl);
253}
254
255static int
256mlxsw_sp_lpm_tree_left_struct_set(struct mlxsw_sp *mlxsw_sp,
257 struct mlxsw_sp_prefix_usage *prefix_usage,
258 struct mlxsw_sp_lpm_tree *lpm_tree)
259{
260 char ralst_pl[MLXSW_REG_RALST_LEN];
261 u8 root_bin = 0;
262 u8 prefix;
263 u8 last_prefix = MLXSW_REG_RALST_BIN_NO_CHILD;
264
265 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage)
266 root_bin = prefix;
267
268 mlxsw_reg_ralst_pack(ralst_pl, root_bin, lpm_tree->id);
269 mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) {
270 if (prefix == 0)
271 continue;
272 mlxsw_reg_ralst_bin_pack(ralst_pl, prefix, last_prefix,
273 MLXSW_REG_RALST_BIN_NO_CHILD);
274 last_prefix = prefix;
275 }
276 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralst), ralst_pl);
277}
278
279static struct mlxsw_sp_lpm_tree *
280mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp,
281 struct mlxsw_sp_prefix_usage *prefix_usage,
282 enum mlxsw_sp_l3proto proto, bool one_reserved)
283{
284 struct mlxsw_sp_lpm_tree *lpm_tree;
285 int err;
286
287 lpm_tree = mlxsw_sp_lpm_tree_find_unused(mlxsw_sp, one_reserved);
288 if (!lpm_tree)
289 return ERR_PTR(-EBUSY);
290 lpm_tree->proto = proto;
291 err = mlxsw_sp_lpm_tree_alloc(mlxsw_sp, lpm_tree);
292 if (err)
293 return ERR_PTR(err);
294
295 err = mlxsw_sp_lpm_tree_left_struct_set(mlxsw_sp, prefix_usage,
296 lpm_tree);
297 if (err)
298 goto err_left_struct_set;
299 return lpm_tree;
300
301err_left_struct_set:
302 mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
303 return ERR_PTR(err);
304}
305
306static int mlxsw_sp_lpm_tree_destroy(struct mlxsw_sp *mlxsw_sp,
307 struct mlxsw_sp_lpm_tree *lpm_tree)
308{
309 return mlxsw_sp_lpm_tree_free(mlxsw_sp, lpm_tree);
310}
311
312static struct mlxsw_sp_lpm_tree *
313mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp,
314 struct mlxsw_sp_prefix_usage *prefix_usage,
315 enum mlxsw_sp_l3proto proto, bool one_reserved)
316{
317 struct mlxsw_sp_lpm_tree *lpm_tree;
318 int i;
319
320 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
321 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
322 if (lpm_tree->proto == proto &&
323 mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage,
324 prefix_usage))
325 goto inc_ref_count;
326 }
327 lpm_tree = mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage,
328 proto, one_reserved);
329 if (IS_ERR(lpm_tree))
330 return lpm_tree;
331
332inc_ref_count:
333 lpm_tree->ref_count++;
334 return lpm_tree;
335}
336
337static int mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp,
338 struct mlxsw_sp_lpm_tree *lpm_tree)
339{
340 if (--lpm_tree->ref_count == 0)
341 return mlxsw_sp_lpm_tree_destroy(mlxsw_sp, lpm_tree);
342 return 0;
343}
344
345static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
346{
347 struct mlxsw_sp_lpm_tree *lpm_tree;
348 int i;
349
350 for (i = 0; i < MLXSW_SP_LPM_TREE_COUNT; i++) {
351 lpm_tree = &mlxsw_sp->router.lpm_trees[i];
352 lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN;
353 }
354}
355
6b75c480
JP
356static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
357{
358 struct mlxsw_sp_vr *vr;
359 int i;
360
361 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
362 vr = &mlxsw_sp->router.vrs[i];
363 if (!vr->used)
364 return vr;
365 }
366 return NULL;
367}
368
369static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
370 struct mlxsw_sp_vr *vr)
371{
372 char raltb_pl[MLXSW_REG_RALTB_LEN];
373
374 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
375 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
376}
377
378static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
379 struct mlxsw_sp_vr *vr)
380{
381 char raltb_pl[MLXSW_REG_RALTB_LEN];
382
383 /* Bind to tree 0 which is default */
384 mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
385 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
386}
387
388static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
389{
390 /* For our purpose, squash main and local table into one */
391 if (tb_id == RT_TABLE_LOCAL)
392 tb_id = RT_TABLE_MAIN;
393 return tb_id;
394}
395
396static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
397 u32 tb_id,
398 enum mlxsw_sp_l3proto proto)
399{
400 struct mlxsw_sp_vr *vr;
401 int i;
402
403 tb_id = mlxsw_sp_fix_tb_id(tb_id);
404 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
405 vr = &mlxsw_sp->router.vrs[i];
406 if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
407 return vr;
408 }
409 return NULL;
410}
411
412static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
413 unsigned char prefix_len,
414 u32 tb_id,
415 enum mlxsw_sp_l3proto proto)
416{
417 struct mlxsw_sp_prefix_usage req_prefix_usage;
418 struct mlxsw_sp_lpm_tree *lpm_tree;
419 struct mlxsw_sp_vr *vr;
420 int err;
421
422 vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
423 if (!vr)
424 return ERR_PTR(-EBUSY);
425 vr->fib = mlxsw_sp_fib_create();
426 if (IS_ERR(vr->fib))
427 return ERR_CAST(vr->fib);
428
429 vr->proto = proto;
430 vr->tb_id = tb_id;
431 mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
432 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
433 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
434 proto, true);
435 if (IS_ERR(lpm_tree)) {
436 err = PTR_ERR(lpm_tree);
437 goto err_tree_get;
438 }
439 vr->lpm_tree = lpm_tree;
440 err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
441 if (err)
442 goto err_tree_bind;
443
444 vr->used = true;
445 return vr;
446
447err_tree_bind:
448 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
449err_tree_get:
450 mlxsw_sp_fib_destroy(vr->fib);
451
452 return ERR_PTR(err);
453}
454
455static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
456 struct mlxsw_sp_vr *vr)
457{
458 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
459 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
460 mlxsw_sp_fib_destroy(vr->fib);
461 vr->used = false;
462}
463
464static int
465mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
466 struct mlxsw_sp_prefix_usage *req_prefix_usage)
467{
468 struct mlxsw_sp_lpm_tree *lpm_tree;
469
470 if (mlxsw_sp_prefix_usage_eq(req_prefix_usage,
471 &vr->lpm_tree->prefix_usage))
472 return 0;
473
474 lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
475 vr->proto, false);
476 if (IS_ERR(lpm_tree)) {
477 /* We failed to get a tree according to the required
478 * prefix usage. However, the current tree might be still good
479 * for us if our requirement is subset of the prefixes used
480 * in the tree.
481 */
482 if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
483 &vr->lpm_tree->prefix_usage))
484 return 0;
485 return PTR_ERR(lpm_tree);
486 }
487
488 mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
489 mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
490 vr->lpm_tree = lpm_tree;
491 return mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
492}
493
494static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
495 unsigned char prefix_len,
496 u32 tb_id,
497 enum mlxsw_sp_l3proto proto)
498{
499 struct mlxsw_sp_vr *vr;
500 int err;
501
502 tb_id = mlxsw_sp_fix_tb_id(tb_id);
503 vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
504 if (!vr) {
505 vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
506 if (IS_ERR(vr))
507 return vr;
508 } else {
509 struct mlxsw_sp_prefix_usage req_prefix_usage;
510
511 mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
512 &vr->fib->prefix_usage);
513 mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
514 /* Need to replace LPM tree in case new prefix is required. */
515 err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
516 &req_prefix_usage);
517 if (err)
518 return ERR_PTR(err);
519 }
520 return vr;
521}
522
523static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
524{
525 /* Destroy virtual router entity in case the associated FIB is empty
526 * and allow it to be used for other tables in future. Otherwise,
527 * check if some prefix usage did not disappear and change tree if
528 * that is the case. Note that in case new, smaller tree cannot be
529 * allocated, the original one will be kept being used.
530 */
531 if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
532 mlxsw_sp_vr_destroy(mlxsw_sp, vr);
533 else
534 mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
535 &vr->fib->prefix_usage);
536}
537
538static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
539{
540 struct mlxsw_sp_vr *vr;
541 int i;
542
543 for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
544 vr = &mlxsw_sp->router.vrs[i];
545 vr->id = i;
546 }
547}
548
6cf3c971
JP
549struct mlxsw_sp_neigh_key {
550 unsigned char addr[sizeof(struct in6_addr)];
551 struct net_device *dev;
552};
553
554struct mlxsw_sp_neigh_entry {
555 struct rhash_head ht_node;
556 struct mlxsw_sp_neigh_key key;
557 u16 rif;
558 struct neighbour *n;
559};
560
561static const struct rhashtable_params mlxsw_sp_neigh_ht_params = {
562 .key_offset = offsetof(struct mlxsw_sp_neigh_entry, key),
563 .head_offset = offsetof(struct mlxsw_sp_neigh_entry, ht_node),
564 .key_len = sizeof(struct mlxsw_sp_neigh_key),
565};
566
567static int
568mlxsw_sp_neigh_entry_insert(struct mlxsw_sp *mlxsw_sp,
569 struct mlxsw_sp_neigh_entry *neigh_entry)
570{
571 return rhashtable_insert_fast(&mlxsw_sp->router.neigh_ht,
572 &neigh_entry->ht_node,
573 mlxsw_sp_neigh_ht_params);
574}
575
576static void
577mlxsw_sp_neigh_entry_remove(struct mlxsw_sp *mlxsw_sp,
578 struct mlxsw_sp_neigh_entry *neigh_entry)
579{
580 rhashtable_remove_fast(&mlxsw_sp->router.neigh_ht,
581 &neigh_entry->ht_node,
582 mlxsw_sp_neigh_ht_params);
583}
584
585static struct mlxsw_sp_neigh_entry *
586mlxsw_sp_neigh_entry_create(const void *addr, size_t addr_len,
587 struct net_device *dev, u16 rif,
588 struct neighbour *n)
589{
590 struct mlxsw_sp_neigh_entry *neigh_entry;
591
592 neigh_entry = kzalloc(sizeof(*neigh_entry), GFP_ATOMIC);
593 if (!neigh_entry)
594 return NULL;
595 memcpy(neigh_entry->key.addr, addr, addr_len);
596 neigh_entry->key.dev = dev;
597 neigh_entry->rif = rif;
598 neigh_entry->n = n;
599 return neigh_entry;
600}
601
602static void
603mlxsw_sp_neigh_entry_destroy(struct mlxsw_sp_neigh_entry *neigh_entry)
604{
605 kfree(neigh_entry);
606}
607
608static struct mlxsw_sp_neigh_entry *
609mlxsw_sp_neigh_entry_lookup(struct mlxsw_sp *mlxsw_sp, const void *addr,
610 size_t addr_len, struct net_device *dev)
611{
612 struct mlxsw_sp_neigh_key key = {{ 0 } };
613
614 memcpy(key.addr, addr, addr_len);
615 key.dev = dev;
616 return rhashtable_lookup_fast(&mlxsw_sp->router.neigh_ht,
617 &key, mlxsw_sp_neigh_ht_params);
618}
619
620int mlxsw_sp_router_neigh_construct(struct net_device *dev,
621 struct neighbour *n)
622{
623 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
624 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
625 struct mlxsw_sp_neigh_entry *neigh_entry;
626 struct mlxsw_sp_rif *r;
627 u32 dip;
628 int err;
629
630 if (n->tbl != &arp_tbl)
631 return 0;
632
633 dip = ntohl(*((__be32 *) n->primary_key));
634 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &dip, sizeof(dip),
635 n->dev);
636 if (neigh_entry) {
637 WARN_ON(neigh_entry->n != n);
638 return 0;
639 }
640
641 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, dev);
642 if (WARN_ON(!r))
643 return -EINVAL;
644
645 neigh_entry = mlxsw_sp_neigh_entry_create(&dip, sizeof(dip), n->dev,
646 r->rif, n);
647 if (!neigh_entry)
648 return -ENOMEM;
649 err = mlxsw_sp_neigh_entry_insert(mlxsw_sp, neigh_entry);
650 if (err)
651 goto err_neigh_entry_insert;
652 return 0;
653
654err_neigh_entry_insert:
655 mlxsw_sp_neigh_entry_destroy(neigh_entry);
656 return err;
657}
658
659void mlxsw_sp_router_neigh_destroy(struct net_device *dev,
660 struct neighbour *n)
661{
662 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
663 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
664 struct mlxsw_sp_neigh_entry *neigh_entry;
665 u32 dip;
666
667 if (n->tbl != &arp_tbl)
668 return;
669
670 dip = ntohl(*((__be32 *) n->primary_key));
671 neigh_entry = mlxsw_sp_neigh_entry_lookup(mlxsw_sp, &dip, sizeof(dip),
672 n->dev);
673 if (!neigh_entry)
674 return;
675 mlxsw_sp_neigh_entry_remove(mlxsw_sp, neigh_entry);
676 mlxsw_sp_neigh_entry_destroy(neigh_entry);
677}
678
679static int mlxsw_sp_neigh_init(struct mlxsw_sp *mlxsw_sp)
680{
681 return rhashtable_init(&mlxsw_sp->router.neigh_ht,
682 &mlxsw_sp_neigh_ht_params);
683}
684
685static void mlxsw_sp_neigh_fini(struct mlxsw_sp *mlxsw_sp)
686{
687 rhashtable_destroy(&mlxsw_sp->router.neigh_ht);
688}
689
464dce18
IS
690static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
691{
692 char rgcr_pl[MLXSW_REG_RGCR_LEN];
693
694 mlxsw_reg_rgcr_pack(rgcr_pl, true);
695 mlxsw_reg_rgcr_max_router_interfaces_set(rgcr_pl, MLXSW_SP_RIF_MAX);
696 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
697}
698
699static void __mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
700{
701 char rgcr_pl[MLXSW_REG_RGCR_LEN];
702
703 mlxsw_reg_rgcr_pack(rgcr_pl, false);
704 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rgcr), rgcr_pl);
705}
706
707int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
708{
53342023
JP
709 int err;
710
711 err = __mlxsw_sp_router_init(mlxsw_sp);
712 if (err)
713 return err;
714 mlxsw_sp_lpm_init(mlxsw_sp);
6b75c480 715 mlxsw_sp_vrs_init(mlxsw_sp);
6cf3c971 716 return mlxsw_sp_neigh_init(mlxsw_sp);
464dce18
IS
717}
718
719void mlxsw_sp_router_fini(struct mlxsw_sp *mlxsw_sp)
720{
6cf3c971 721 mlxsw_sp_neigh_fini(mlxsw_sp);
464dce18
IS
722 __mlxsw_sp_router_fini(mlxsw_sp);
723}
61c503f9
JP
724
725static int mlxsw_sp_fib_entry_op4_local(struct mlxsw_sp *mlxsw_sp,
726 struct mlxsw_sp_fib_entry *fib_entry,
727 enum mlxsw_reg_ralue_op op)
728{
729 char ralue_pl[MLXSW_REG_RALUE_LEN];
730 u32 *p_dip = (u32 *) fib_entry->key.addr;
731 struct mlxsw_sp_vr *vr = fib_entry->vr;
732
733 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
734 fib_entry->key.prefix_len, *p_dip);
735 mlxsw_reg_ralue_act_local_pack(ralue_pl,
736 MLXSW_REG_RALUE_TRAP_ACTION_NOP, 0,
737 fib_entry->rif);
738 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
739}
740
741static int mlxsw_sp_fib_entry_op4_trap(struct mlxsw_sp *mlxsw_sp,
742 struct mlxsw_sp_fib_entry *fib_entry,
743 enum mlxsw_reg_ralue_op op)
744{
745 char ralue_pl[MLXSW_REG_RALUE_LEN];
746 u32 *p_dip = (u32 *) fib_entry->key.addr;
747 struct mlxsw_sp_vr *vr = fib_entry->vr;
748
749 mlxsw_reg_ralue_pack4(ralue_pl, vr->proto, op, vr->id,
750 fib_entry->key.prefix_len, *p_dip);
751 mlxsw_reg_ralue_act_ip2me_pack(ralue_pl);
752 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ralue), ralue_pl);
753}
754
755static int mlxsw_sp_fib_entry_op4(struct mlxsw_sp *mlxsw_sp,
756 struct mlxsw_sp_fib_entry *fib_entry,
757 enum mlxsw_reg_ralue_op op)
758{
759 switch (fib_entry->type) {
760 case MLXSW_SP_FIB_ENTRY_TYPE_REMOTE:
761 return -EINVAL;
762 case MLXSW_SP_FIB_ENTRY_TYPE_LOCAL:
763 return mlxsw_sp_fib_entry_op4_local(mlxsw_sp, fib_entry, op);
764 case MLXSW_SP_FIB_ENTRY_TYPE_TRAP:
765 return mlxsw_sp_fib_entry_op4_trap(mlxsw_sp, fib_entry, op);
766 }
767 return -EINVAL;
768}
769
770static int mlxsw_sp_fib_entry_op(struct mlxsw_sp *mlxsw_sp,
771 struct mlxsw_sp_fib_entry *fib_entry,
772 enum mlxsw_reg_ralue_op op)
773{
774 switch (fib_entry->vr->proto) {
775 case MLXSW_SP_L3_PROTO_IPV4:
776 return mlxsw_sp_fib_entry_op4(mlxsw_sp, fib_entry, op);
777 case MLXSW_SP_L3_PROTO_IPV6:
778 return -EINVAL;
779 }
780 return -EINVAL;
781}
782
783static int mlxsw_sp_fib_entry_update(struct mlxsw_sp *mlxsw_sp,
784 struct mlxsw_sp_fib_entry *fib_entry)
785{
786 enum mlxsw_reg_ralue_op op;
787
788 op = !fib_entry->added ? MLXSW_REG_RALUE_OP_WRITE_WRITE :
789 MLXSW_REG_RALUE_OP_WRITE_UPDATE;
790 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry, op);
791}
792
793static int mlxsw_sp_fib_entry_del(struct mlxsw_sp *mlxsw_sp,
794 struct mlxsw_sp_fib_entry *fib_entry)
795{
796 return mlxsw_sp_fib_entry_op(mlxsw_sp, fib_entry,
797 MLXSW_REG_RALUE_OP_WRITE_DELETE);
798}
799
800struct mlxsw_sp_router_fib4_add_info {
801 struct switchdev_trans_item tritem;
802 struct mlxsw_sp *mlxsw_sp;
803 struct mlxsw_sp_fib_entry *fib_entry;
804};
805
806static void mlxsw_sp_router_fib4_add_info_destroy(void const *data)
807{
808 const struct mlxsw_sp_router_fib4_add_info *info = data;
809 struct mlxsw_sp_fib_entry *fib_entry = info->fib_entry;
810 struct mlxsw_sp *mlxsw_sp = info->mlxsw_sp;
811
812 mlxsw_sp_fib_entry_destroy(fib_entry);
813 mlxsw_sp_vr_put(mlxsw_sp, fib_entry->vr);
814 kfree(info);
815}
816
817static int
818mlxsw_sp_router_fib4_entry_init(struct mlxsw_sp *mlxsw_sp,
819 const struct switchdev_obj_ipv4_fib *fib4,
820 struct mlxsw_sp_fib_entry *fib_entry)
821{
822 struct fib_info *fi = fib4->fi;
823
824 if (fib4->type == RTN_LOCAL || fib4->type == RTN_BROADCAST) {
825 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_TRAP;
826 return 0;
827 }
828 if (fib4->type != RTN_UNICAST)
829 return -EINVAL;
830
831 if (fi->fib_scope != RT_SCOPE_UNIVERSE) {
832 struct mlxsw_sp_rif *r;
833
834 fib_entry->type = MLXSW_SP_FIB_ENTRY_TYPE_LOCAL;
835 r = mlxsw_sp_rif_find_by_dev(mlxsw_sp, fi->fib_dev);
836 if (!r)
837 return -EINVAL;
838 fib_entry->rif = r->rif;
839 return 0;
840 }
841 return -EINVAL;
842}
843
844static int
845mlxsw_sp_router_fib4_add_prepare(struct mlxsw_sp_port *mlxsw_sp_port,
846 const struct switchdev_obj_ipv4_fib *fib4,
847 struct switchdev_trans *trans)
848{
849 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
850 struct mlxsw_sp_router_fib4_add_info *info;
851 struct mlxsw_sp_fib_entry *fib_entry;
852 struct mlxsw_sp_vr *vr;
853 int err;
854
855 vr = mlxsw_sp_vr_get(mlxsw_sp, fib4->dst_len, fib4->tb_id,
856 MLXSW_SP_L3_PROTO_IPV4);
857 if (IS_ERR(vr))
858 return PTR_ERR(vr);
859
860 fib_entry = mlxsw_sp_fib_entry_create(vr->fib, &fib4->dst,
861 sizeof(fib4->dst), fib4->dst_len);
862 if (!fib_entry) {
863 err = -ENOMEM;
864 goto err_fib_entry_create;
865 }
866 fib_entry->vr = vr;
867
868 err = mlxsw_sp_router_fib4_entry_init(mlxsw_sp, fib4, fib_entry);
869 if (err)
870 goto err_fib4_entry_init;
871
872 info = kmalloc(sizeof(*info), GFP_KERNEL);
873 if (!info) {
874 err = -ENOMEM;
875 goto err_alloc_info;
876 }
877 info->mlxsw_sp = mlxsw_sp;
878 info->fib_entry = fib_entry;
879 switchdev_trans_item_enqueue(trans, info,
880 mlxsw_sp_router_fib4_add_info_destroy,
881 &info->tritem);
882 return 0;
883
884err_alloc_info:
885err_fib4_entry_init:
886 mlxsw_sp_fib_entry_destroy(fib_entry);
887err_fib_entry_create:
888 mlxsw_sp_vr_put(mlxsw_sp, vr);
889 return err;
890}
891
892static int
893mlxsw_sp_router_fib4_add_commit(struct mlxsw_sp_port *mlxsw_sp_port,
894 const struct switchdev_obj_ipv4_fib *fib4,
895 struct switchdev_trans *trans)
896{
897 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
898 struct mlxsw_sp_router_fib4_add_info *info;
899 struct mlxsw_sp_fib_entry *fib_entry;
900 struct mlxsw_sp_vr *vr;
901 int err;
902
903 info = switchdev_trans_item_dequeue(trans);
904 fib_entry = info->fib_entry;
905 kfree(info);
906
907 vr = fib_entry->vr;
908 err = mlxsw_sp_fib_entry_insert(fib_entry->vr->fib, fib_entry);
909 if (err)
910 goto err_fib_entry_insert;
911 err = mlxsw_sp_fib_entry_update(mlxsw_sp, fib_entry);
912 if (err)
913 goto err_fib_entry_add;
914 return 0;
915
916err_fib_entry_add:
917 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
918err_fib_entry_insert:
919 mlxsw_sp_fib_entry_destroy(fib_entry);
920 mlxsw_sp_vr_put(mlxsw_sp, vr);
921 return err;
922}
923
924int mlxsw_sp_router_fib4_add(struct mlxsw_sp_port *mlxsw_sp_port,
925 const struct switchdev_obj_ipv4_fib *fib4,
926 struct switchdev_trans *trans)
927{
928 if (switchdev_trans_ph_prepare(trans))
929 return mlxsw_sp_router_fib4_add_prepare(mlxsw_sp_port,
930 fib4, trans);
931 return mlxsw_sp_router_fib4_add_commit(mlxsw_sp_port,
932 fib4, trans);
933}
934
935int mlxsw_sp_router_fib4_del(struct mlxsw_sp_port *mlxsw_sp_port,
936 const struct switchdev_obj_ipv4_fib *fib4)
937{
938 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
939 struct mlxsw_sp_fib_entry *fib_entry;
940 struct mlxsw_sp_vr *vr;
941
942 vr = mlxsw_sp_vr_find(mlxsw_sp, fib4->tb_id, MLXSW_SP_L3_PROTO_IPV4);
943 if (!vr) {
944 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find virtual router for FIB4 entry being removed.\n");
945 return -ENOENT;
946 }
947 fib_entry = mlxsw_sp_fib_entry_lookup(vr->fib, &fib4->dst,
948 sizeof(fib4->dst), fib4->dst_len);
949 if (!fib_entry) {
950 dev_warn(mlxsw_sp->bus_info->dev, "Failed to find FIB4 entry being removed.\n");
951 return PTR_ERR(vr);
952 }
953 mlxsw_sp_fib_entry_del(mlxsw_sp_port->mlxsw_sp, fib_entry);
954 mlxsw_sp_fib_entry_remove(vr->fib, fib_entry);
955 mlxsw_sp_fib_entry_destroy(fib_entry);
956 mlxsw_sp_vr_put(mlxsw_sp, vr);
957 return 0;
958}