Skip to content

Commit 6b75c48

Browse files
jpirkodavem330
authored andcommitted
mlxsw: spectrum_router: Add virtual router management
Virtual router is a construct used inside HW. In this implementation we map kernel tables to virtual routers one to one. Introduce management logic to create virtual routers when needed and destroy in case they are no longer in use. According to that, call into LPM tree management. Each virtual router is always bound to one LPM tree. Signed-off-by: Jiri Pirko <[email protected]> Reviewed-by: Ido Schimmel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 5334202 commit 6b75c48

File tree

2 files changed

+242
-0
lines changed

2 files changed

+242
-0
lines changed

drivers/net/ethernet/mellanox/mlxsw/spectrum.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@
6565
#define MLXSW_SP_LPM_TREE_MAX 22
6666
#define MLXSW_SP_LPM_TREE_COUNT (MLXSW_SP_LPM_TREE_MAX - MLXSW_SP_LPM_TREE_MIN)
6767

68+
#define MLXSW_SP_VIRTUAL_ROUTER_MAX 256
69+
6870
#define MLXSW_SP_PORT_BASE_SPEED 25000 /* Mb/s */
6971

7072
#define MLXSW_SP_BYTES_PER_CELL 96
@@ -183,8 +185,20 @@ struct mlxsw_sp_lpm_tree {
183185
struct mlxsw_sp_prefix_usage prefix_usage;
184186
};
185187

188+
struct mlxsw_sp_fib;
189+
190+
struct mlxsw_sp_vr {
191+
u16 id; /* virtual router ID */
192+
bool used;
193+
enum mlxsw_sp_l3proto proto;
194+
u32 tb_id; /* kernel fib table id */
195+
struct mlxsw_sp_lpm_tree *lpm_tree;
196+
struct mlxsw_sp_fib *fib;
197+
};
198+
186199
struct mlxsw_sp_router {
187200
struct mlxsw_sp_lpm_tree lpm_trees[MLXSW_SP_LPM_TREE_COUNT];
201+
struct mlxsw_sp_vr vrs[MLXSW_SP_VIRTUAL_ROUTER_MAX];
188202
};
189203

190204
struct mlxsw_sp {

drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,47 @@
4646
#define mlxsw_sp_prefix_usage_for_each(prefix, prefix_usage) \
4747
for_each_set_bit(prefix, (prefix_usage)->b, MLXSW_SP_PREFIX_COUNT)
4848

49+
static bool
50+
mlxsw_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+
4962
static bool
5063
mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1,
5164
struct mlxsw_sp_prefix_usage *prefix_usage2)
5265
{
5366
return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1));
5467
}
5568

69+
static bool
70+
mlxsw_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+
77+
static void
78+
mlxsw_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+
84+
static void
85+
mlxsw_sp_prefix_usage_zero(struct mlxsw_sp_prefix_usage *prefix_usage)
86+
{
87+
memset(prefix_usage, 0, sizeof(*prefix_usage));
88+
}
89+
5690
static void
5791
mlxsw_sp_prefix_usage_set(struct mlxsw_sp_prefix_usage *prefix_usage,
5892
unsigned char prefix_len)
@@ -307,6 +341,199 @@ static void mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp)
307341
}
308342
}
309343

344+
static struct mlxsw_sp_vr *mlxsw_sp_vr_find_unused(struct mlxsw_sp *mlxsw_sp)
345+
{
346+
struct mlxsw_sp_vr *vr;
347+
int i;
348+
349+
for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
350+
vr = &mlxsw_sp->router.vrs[i];
351+
if (!vr->used)
352+
return vr;
353+
}
354+
return NULL;
355+
}
356+
357+
static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp,
358+
struct mlxsw_sp_vr *vr)
359+
{
360+
char raltb_pl[MLXSW_REG_RALTB_LEN];
361+
362+
mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, vr->lpm_tree->id);
363+
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
364+
}
365+
366+
static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp,
367+
struct mlxsw_sp_vr *vr)
368+
{
369+
char raltb_pl[MLXSW_REG_RALTB_LEN];
370+
371+
/* Bind to tree 0 which is default */
372+
mlxsw_reg_raltb_pack(raltb_pl, vr->id, vr->proto, 0);
373+
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(raltb), raltb_pl);
374+
}
375+
376+
static u32 mlxsw_sp_fix_tb_id(u32 tb_id)
377+
{
378+
/* For our purpose, squash main and local table into one */
379+
if (tb_id == RT_TABLE_LOCAL)
380+
tb_id = RT_TABLE_MAIN;
381+
return tb_id;
382+
}
383+
384+
static struct mlxsw_sp_vr *mlxsw_sp_vr_find(struct mlxsw_sp *mlxsw_sp,
385+
u32 tb_id,
386+
enum mlxsw_sp_l3proto proto)
387+
{
388+
struct mlxsw_sp_vr *vr;
389+
int i;
390+
391+
tb_id = mlxsw_sp_fix_tb_id(tb_id);
392+
for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
393+
vr = &mlxsw_sp->router.vrs[i];
394+
if (vr->used && vr->proto == proto && vr->tb_id == tb_id)
395+
return vr;
396+
}
397+
return NULL;
398+
}
399+
400+
static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp,
401+
unsigned char prefix_len,
402+
u32 tb_id,
403+
enum mlxsw_sp_l3proto proto)
404+
{
405+
struct mlxsw_sp_prefix_usage req_prefix_usage;
406+
struct mlxsw_sp_lpm_tree *lpm_tree;
407+
struct mlxsw_sp_vr *vr;
408+
int err;
409+
410+
vr = mlxsw_sp_vr_find_unused(mlxsw_sp);
411+
if (!vr)
412+
return ERR_PTR(-EBUSY);
413+
vr->fib = mlxsw_sp_fib_create();
414+
if (IS_ERR(vr->fib))
415+
return ERR_CAST(vr->fib);
416+
417+
vr->proto = proto;
418+
vr->tb_id = tb_id;
419+
mlxsw_sp_prefix_usage_zero(&req_prefix_usage);
420+
mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
421+
lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage,
422+
proto, true);
423+
if (IS_ERR(lpm_tree)) {
424+
err = PTR_ERR(lpm_tree);
425+
goto err_tree_get;
426+
}
427+
vr->lpm_tree = lpm_tree;
428+
err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
429+
if (err)
430+
goto err_tree_bind;
431+
432+
vr->used = true;
433+
return vr;
434+
435+
err_tree_bind:
436+
mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
437+
err_tree_get:
438+
mlxsw_sp_fib_destroy(vr->fib);
439+
440+
return ERR_PTR(err);
441+
}
442+
443+
static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp,
444+
struct mlxsw_sp_vr *vr)
445+
{
446+
mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
447+
mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
448+
mlxsw_sp_fib_destroy(vr->fib);
449+
vr->used = false;
450+
}
451+
452+
static int
453+
mlxsw_sp_vr_lpm_tree_check(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr,
454+
struct mlxsw_sp_prefix_usage *req_prefix_usage)
455+
{
456+
struct mlxsw_sp_lpm_tree *lpm_tree;
457+
458+
if (mlxsw_sp_prefix_usage_eq(req_prefix_usage,
459+
&vr->lpm_tree->prefix_usage))
460+
return 0;
461+
462+
lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, req_prefix_usage,
463+
vr->proto, false);
464+
if (IS_ERR(lpm_tree)) {
465+
/* We failed to get a tree according to the required
466+
* prefix usage. However, the current tree might be still good
467+
* for us if our requirement is subset of the prefixes used
468+
* in the tree.
469+
*/
470+
if (mlxsw_sp_prefix_usage_subset(req_prefix_usage,
471+
&vr->lpm_tree->prefix_usage))
472+
return 0;
473+
return PTR_ERR(lpm_tree);
474+
}
475+
476+
mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, vr);
477+
mlxsw_sp_lpm_tree_put(mlxsw_sp, vr->lpm_tree);
478+
vr->lpm_tree = lpm_tree;
479+
return mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, vr);
480+
}
481+
482+
static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp,
483+
unsigned char prefix_len,
484+
u32 tb_id,
485+
enum mlxsw_sp_l3proto proto)
486+
{
487+
struct mlxsw_sp_vr *vr;
488+
int err;
489+
490+
tb_id = mlxsw_sp_fix_tb_id(tb_id);
491+
vr = mlxsw_sp_vr_find(mlxsw_sp, tb_id, proto);
492+
if (!vr) {
493+
vr = mlxsw_sp_vr_create(mlxsw_sp, prefix_len, tb_id, proto);
494+
if (IS_ERR(vr))
495+
return vr;
496+
} else {
497+
struct mlxsw_sp_prefix_usage req_prefix_usage;
498+
499+
mlxsw_sp_prefix_usage_cpy(&req_prefix_usage,
500+
&vr->fib->prefix_usage);
501+
mlxsw_sp_prefix_usage_set(&req_prefix_usage, prefix_len);
502+
/* Need to replace LPM tree in case new prefix is required. */
503+
err = mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
504+
&req_prefix_usage);
505+
if (err)
506+
return ERR_PTR(err);
507+
}
508+
return vr;
509+
}
510+
511+
static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr)
512+
{
513+
/* Destroy virtual router entity in case the associated FIB is empty
514+
* and allow it to be used for other tables in future. Otherwise,
515+
* check if some prefix usage did not disappear and change tree if
516+
* that is the case. Note that in case new, smaller tree cannot be
517+
* allocated, the original one will be kept being used.
518+
*/
519+
if (mlxsw_sp_prefix_usage_none(&vr->fib->prefix_usage))
520+
mlxsw_sp_vr_destroy(mlxsw_sp, vr);
521+
else
522+
mlxsw_sp_vr_lpm_tree_check(mlxsw_sp, vr,
523+
&vr->fib->prefix_usage);
524+
}
525+
526+
static void mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp)
527+
{
528+
struct mlxsw_sp_vr *vr;
529+
int i;
530+
531+
for (i = 0; i < MLXSW_SP_VIRTUAL_ROUTER_MAX; i++) {
532+
vr = &mlxsw_sp->router.vrs[i];
533+
vr->id = i;
534+
}
535+
}
536+
310537
static int __mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
311538
{
312539
char rgcr_pl[MLXSW_REG_RGCR_LEN];
@@ -332,6 +559,7 @@ int mlxsw_sp_router_init(struct mlxsw_sp *mlxsw_sp)
332559
if (err)
333560
return err;
334561
mlxsw_sp_lpm_init(mlxsw_sp);
562+
mlxsw_sp_vrs_init(mlxsw_sp);
335563
return 0;
336564
}
337565

0 commit comments

Comments
 (0)