diff options
author | David S. Miller <davem@davemloft.net> | 2018-01-22 09:22:11 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-01-22 09:22:11 -0500 |
commit | 2ae50dea1d5566ea054d96e413a86017066d3528 (patch) | |
tree | 5f8907de0f205a4f96fd8c4dd5772592006816af | |
parent | 9d6474e458b13a94a0d5b141f2b8f38adf1991ae (diff) | |
parent | 2b52ce02e1d17986ad7da8bc6e96c632dfa73219 (diff) |
Merge branch 'mlxsw-spectrum_router-Optimize-LPM-trees'
Jiri Pirko says:
====================
mlxsw: spectrum_router: Optimize LPM trees
Ido says:
This set tries to optimize the structure of the LPM trees used for route
lookup by avoiding lookups that are guaranteed not to return a result.
This is done by making sure only used prefix lengths are present in the
tree.
First two patches are small preparatory steps towards the actual change
in the last patch.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 243 |
1 files changed, 132 insertions, 111 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 31891ae11c9b..91170d940268 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | |||
@@ -71,6 +71,7 @@ | |||
71 | #include "spectrum_mr_tcam.h" | 71 | #include "spectrum_mr_tcam.h" |
72 | #include "spectrum_router.h" | 72 | #include "spectrum_router.h" |
73 | 73 | ||
74 | struct mlxsw_sp_fib; | ||
74 | struct mlxsw_sp_vr; | 75 | struct mlxsw_sp_vr; |
75 | struct mlxsw_sp_lpm_tree; | 76 | struct mlxsw_sp_lpm_tree; |
76 | struct mlxsw_sp_rif_ops; | 77 | struct mlxsw_sp_rif_ops; |
@@ -84,6 +85,8 @@ struct mlxsw_sp_router { | |||
84 | struct rhashtable nexthop_ht; | 85 | struct rhashtable nexthop_ht; |
85 | struct list_head nexthop_list; | 86 | struct list_head nexthop_list; |
86 | struct { | 87 | struct { |
88 | /* One tree for each protocol: IPv4 and IPv6 */ | ||
89 | struct mlxsw_sp_lpm_tree *proto_trees[2]; | ||
87 | struct mlxsw_sp_lpm_tree *trees; | 90 | struct mlxsw_sp_lpm_tree *trees; |
88 | unsigned int tree_count; | 91 | unsigned int tree_count; |
89 | } lpm; | 92 | } lpm; |
@@ -162,6 +165,15 @@ struct mlxsw_sp_rif_ops { | |||
162 | struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif); | 165 | struct mlxsw_sp_fid * (*fid_get)(struct mlxsw_sp_rif *rif); |
163 | }; | 166 | }; |
164 | 167 | ||
168 | static void mlxsw_sp_lpm_tree_hold(struct mlxsw_sp_lpm_tree *lpm_tree); | ||
169 | static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, | ||
170 | struct mlxsw_sp_lpm_tree *lpm_tree); | ||
171 | static int mlxsw_sp_vr_lpm_tree_bind(struct mlxsw_sp *mlxsw_sp, | ||
172 | const struct mlxsw_sp_fib *fib, | ||
173 | u8 tree_id); | ||
174 | static int mlxsw_sp_vr_lpm_tree_unbind(struct mlxsw_sp *mlxsw_sp, | ||
175 | const struct mlxsw_sp_fib *fib); | ||
176 | |||
165 | static unsigned int * | 177 | static unsigned int * |
166 | mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif, | 178 | mlxsw_sp_rif_p_counter_get(struct mlxsw_sp_rif *rif, |
167 | enum mlxsw_sp_rif_counter_dir dir) | 179 | enum mlxsw_sp_rif_counter_dir dir) |
@@ -349,14 +361,6 @@ mlxsw_sp_prefix_usage_eq(struct mlxsw_sp_prefix_usage *prefix_usage1, | |||
349 | return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1)); | 361 | return !memcmp(prefix_usage1, prefix_usage2, sizeof(*prefix_usage1)); |
350 | } | 362 | } |
351 | 363 | ||
352 | static bool | ||
353 | mlxsw_sp_prefix_usage_none(struct mlxsw_sp_prefix_usage *prefix_usage) | ||
354 | { | ||
355 | struct mlxsw_sp_prefix_usage prefix_usage_none = {{ 0 } }; | ||
356 | |||
357 | return mlxsw_sp_prefix_usage_eq(prefix_usage, &prefix_usage_none); | ||
358 | } | ||
359 | |||
360 | static void | 364 | static void |
361 | mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1, | 365 | mlxsw_sp_prefix_usage_cpy(struct mlxsw_sp_prefix_usage *prefix_usage1, |
362 | struct mlxsw_sp_prefix_usage *prefix_usage2) | 366 | struct mlxsw_sp_prefix_usage *prefix_usage2) |
@@ -398,7 +402,6 @@ enum mlxsw_sp_fib_entry_type { | |||
398 | }; | 402 | }; |
399 | 403 | ||
400 | struct mlxsw_sp_nexthop_group; | 404 | struct mlxsw_sp_nexthop_group; |
401 | struct mlxsw_sp_fib; | ||
402 | 405 | ||
403 | struct mlxsw_sp_fib_node { | 406 | struct mlxsw_sp_fib_node { |
404 | struct list_head entry_list; | 407 | struct list_head entry_list; |
@@ -445,6 +448,7 @@ struct mlxsw_sp_lpm_tree { | |||
445 | u8 id; /* tree ID */ | 448 | u8 id; /* tree ID */ |
446 | unsigned int ref_count; | 449 | unsigned int ref_count; |
447 | enum mlxsw_sp_l3proto proto; | 450 | enum mlxsw_sp_l3proto proto; |
451 | unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT]; | ||
448 | struct mlxsw_sp_prefix_usage prefix_usage; | 452 | struct mlxsw_sp_prefix_usage prefix_usage; |
449 | }; | 453 | }; |
450 | 454 | ||
@@ -453,8 +457,6 @@ struct mlxsw_sp_fib { | |||
453 | struct list_head node_list; | 457 | struct list_head node_list; |
454 | struct mlxsw_sp_vr *vr; | 458 | struct mlxsw_sp_vr *vr; |
455 | struct mlxsw_sp_lpm_tree *lpm_tree; | 459 | struct mlxsw_sp_lpm_tree *lpm_tree; |
456 | unsigned long prefix_ref_count[MLXSW_SP_PREFIX_COUNT]; | ||
457 | struct mlxsw_sp_prefix_usage prefix_usage; | ||
458 | enum mlxsw_sp_l3proto proto; | 460 | enum mlxsw_sp_l3proto proto; |
459 | }; | 461 | }; |
460 | 462 | ||
@@ -469,12 +471,15 @@ struct mlxsw_sp_vr { | |||
469 | 471 | ||
470 | static const struct rhashtable_params mlxsw_sp_fib_ht_params; | 472 | static const struct rhashtable_params mlxsw_sp_fib_ht_params; |
471 | 473 | ||
472 | static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr, | 474 | static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp *mlxsw_sp, |
475 | struct mlxsw_sp_vr *vr, | ||
473 | enum mlxsw_sp_l3proto proto) | 476 | enum mlxsw_sp_l3proto proto) |
474 | { | 477 | { |
478 | struct mlxsw_sp_lpm_tree *lpm_tree; | ||
475 | struct mlxsw_sp_fib *fib; | 479 | struct mlxsw_sp_fib *fib; |
476 | int err; | 480 | int err; |
477 | 481 | ||
482 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[proto]; | ||
478 | fib = kzalloc(sizeof(*fib), GFP_KERNEL); | 483 | fib = kzalloc(sizeof(*fib), GFP_KERNEL); |
479 | if (!fib) | 484 | if (!fib) |
480 | return ERR_PTR(-ENOMEM); | 485 | return ERR_PTR(-ENOMEM); |
@@ -484,17 +489,26 @@ static struct mlxsw_sp_fib *mlxsw_sp_fib_create(struct mlxsw_sp_vr *vr, | |||
484 | INIT_LIST_HEAD(&fib->node_list); | 489 | INIT_LIST_HEAD(&fib->node_list); |
485 | fib->proto = proto; | 490 | fib->proto = proto; |
486 | fib->vr = vr; | 491 | fib->vr = vr; |
492 | fib->lpm_tree = lpm_tree; | ||
493 | mlxsw_sp_lpm_tree_hold(lpm_tree); | ||
494 | err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, lpm_tree->id); | ||
495 | if (err) | ||
496 | goto err_lpm_tree_bind; | ||
487 | return fib; | 497 | return fib; |
488 | 498 | ||
499 | err_lpm_tree_bind: | ||
500 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | ||
489 | err_rhashtable_init: | 501 | err_rhashtable_init: |
490 | kfree(fib); | 502 | kfree(fib); |
491 | return ERR_PTR(err); | 503 | return ERR_PTR(err); |
492 | } | 504 | } |
493 | 505 | ||
494 | static void mlxsw_sp_fib_destroy(struct mlxsw_sp_fib *fib) | 506 | static void mlxsw_sp_fib_destroy(struct mlxsw_sp *mlxsw_sp, |
507 | struct mlxsw_sp_fib *fib) | ||
495 | { | 508 | { |
509 | mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); | ||
510 | mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree); | ||
496 | WARN_ON(!list_empty(&fib->node_list)); | 511 | WARN_ON(!list_empty(&fib->node_list)); |
497 | WARN_ON(fib->lpm_tree); | ||
498 | rhashtable_destroy(&fib->ht); | 512 | rhashtable_destroy(&fib->ht); |
499 | kfree(fib); | 513 | kfree(fib); |
500 | } | 514 | } |
@@ -581,6 +595,9 @@ mlxsw_sp_lpm_tree_create(struct mlxsw_sp *mlxsw_sp, | |||
581 | goto err_left_struct_set; | 595 | goto err_left_struct_set; |
582 | memcpy(&lpm_tree->prefix_usage, prefix_usage, | 596 | memcpy(&lpm_tree->prefix_usage, prefix_usage, |
583 | sizeof(lpm_tree->prefix_usage)); | 597 | sizeof(lpm_tree->prefix_usage)); |
598 | memset(&lpm_tree->prefix_ref_count, 0, | ||
599 | sizeof(lpm_tree->prefix_ref_count)); | ||
600 | lpm_tree->ref_count = 1; | ||
584 | return lpm_tree; | 601 | return lpm_tree; |
585 | 602 | ||
586 | err_left_struct_set: | 603 | err_left_struct_set: |
@@ -607,8 +624,10 @@ mlxsw_sp_lpm_tree_get(struct mlxsw_sp *mlxsw_sp, | |||
607 | if (lpm_tree->ref_count != 0 && | 624 | if (lpm_tree->ref_count != 0 && |
608 | lpm_tree->proto == proto && | 625 | lpm_tree->proto == proto && |
609 | mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, | 626 | mlxsw_sp_prefix_usage_eq(&lpm_tree->prefix_usage, |
610 | prefix_usage)) | 627 | prefix_usage)) { |
628 | mlxsw_sp_lpm_tree_hold(lpm_tree); | ||
611 | return lpm_tree; | 629 | return lpm_tree; |
630 | } | ||
612 | } | 631 | } |
613 | return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto); | 632 | return mlxsw_sp_lpm_tree_create(mlxsw_sp, prefix_usage, proto); |
614 | } | 633 | } |
@@ -629,9 +648,10 @@ static void mlxsw_sp_lpm_tree_put(struct mlxsw_sp *mlxsw_sp, | |||
629 | 648 | ||
630 | static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) | 649 | static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) |
631 | { | 650 | { |
651 | struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; | ||
632 | struct mlxsw_sp_lpm_tree *lpm_tree; | 652 | struct mlxsw_sp_lpm_tree *lpm_tree; |
633 | u64 max_trees; | 653 | u64 max_trees; |
634 | int i; | 654 | int err, i; |
635 | 655 | ||
636 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES)) | 656 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_LPM_TREES)) |
637 | return -EIO; | 657 | return -EIO; |
@@ -649,11 +669,42 @@ static int mlxsw_sp_lpm_init(struct mlxsw_sp *mlxsw_sp) | |||
649 | lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN; | 669 | lpm_tree->id = i + MLXSW_SP_LPM_TREE_MIN; |
650 | } | 670 | } |
651 | 671 | ||
672 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | ||
673 | MLXSW_SP_L3_PROTO_IPV4); | ||
674 | if (IS_ERR(lpm_tree)) { | ||
675 | err = PTR_ERR(lpm_tree); | ||
676 | goto err_ipv4_tree_get; | ||
677 | } | ||
678 | mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4] = lpm_tree; | ||
679 | |||
680 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | ||
681 | MLXSW_SP_L3_PROTO_IPV6); | ||
682 | if (IS_ERR(lpm_tree)) { | ||
683 | err = PTR_ERR(lpm_tree); | ||
684 | goto err_ipv6_tree_get; | ||
685 | } | ||
686 | mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6] = lpm_tree; | ||
687 | |||
652 | return 0; | 688 | return 0; |
689 | |||
690 | err_ipv6_tree_get: | ||
691 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4]; | ||
692 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | ||
693 | err_ipv4_tree_get: | ||
694 | kfree(mlxsw_sp->router->lpm.trees); | ||
695 | return err; | ||
653 | } | 696 | } |
654 | 697 | ||
655 | static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) | 698 | static void mlxsw_sp_lpm_fini(struct mlxsw_sp *mlxsw_sp) |
656 | { | 699 | { |
700 | struct mlxsw_sp_lpm_tree *lpm_tree; | ||
701 | |||
702 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV6]; | ||
703 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | ||
704 | |||
705 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[MLXSW_SP_L3_PROTO_IPV4]; | ||
706 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | ||
707 | |||
657 | kfree(mlxsw_sp->router->lpm.trees); | 708 | kfree(mlxsw_sp->router->lpm.trees); |
658 | } | 709 | } |
659 | 710 | ||
@@ -745,10 +796,10 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, | |||
745 | NL_SET_ERR_MSG(extack, "spectrum: Exceeded number of supported virtual routers"); | 796 | NL_SET_ERR_MSG(extack, "spectrum: Exceeded number of supported virtual routers"); |
746 | return ERR_PTR(-EBUSY); | 797 | return ERR_PTR(-EBUSY); |
747 | } | 798 | } |
748 | vr->fib4 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV4); | 799 | vr->fib4 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV4); |
749 | if (IS_ERR(vr->fib4)) | 800 | if (IS_ERR(vr->fib4)) |
750 | return ERR_CAST(vr->fib4); | 801 | return ERR_CAST(vr->fib4); |
751 | vr->fib6 = mlxsw_sp_fib_create(vr, MLXSW_SP_L3_PROTO_IPV6); | 802 | vr->fib6 = mlxsw_sp_fib_create(mlxsw_sp, vr, MLXSW_SP_L3_PROTO_IPV6); |
752 | if (IS_ERR(vr->fib6)) { | 803 | if (IS_ERR(vr->fib6)) { |
753 | err = PTR_ERR(vr->fib6); | 804 | err = PTR_ERR(vr->fib6); |
754 | goto err_fib6_create; | 805 | goto err_fib6_create; |
@@ -763,21 +814,22 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_create(struct mlxsw_sp *mlxsw_sp, | |||
763 | return vr; | 814 | return vr; |
764 | 815 | ||
765 | err_mr_table_create: | 816 | err_mr_table_create: |
766 | mlxsw_sp_fib_destroy(vr->fib6); | 817 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6); |
767 | vr->fib6 = NULL; | 818 | vr->fib6 = NULL; |
768 | err_fib6_create: | 819 | err_fib6_create: |
769 | mlxsw_sp_fib_destroy(vr->fib4); | 820 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4); |
770 | vr->fib4 = NULL; | 821 | vr->fib4 = NULL; |
771 | return ERR_PTR(err); | 822 | return ERR_PTR(err); |
772 | } | 823 | } |
773 | 824 | ||
774 | static void mlxsw_sp_vr_destroy(struct mlxsw_sp_vr *vr) | 825 | static void mlxsw_sp_vr_destroy(struct mlxsw_sp *mlxsw_sp, |
826 | struct mlxsw_sp_vr *vr) | ||
775 | { | 827 | { |
776 | mlxsw_sp_mr_table_destroy(vr->mr4_table); | 828 | mlxsw_sp_mr_table_destroy(vr->mr4_table); |
777 | vr->mr4_table = NULL; | 829 | vr->mr4_table = NULL; |
778 | mlxsw_sp_fib_destroy(vr->fib6); | 830 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib6); |
779 | vr->fib6 = NULL; | 831 | vr->fib6 = NULL; |
780 | mlxsw_sp_fib_destroy(vr->fib4); | 832 | mlxsw_sp_fib_destroy(mlxsw_sp, vr->fib4); |
781 | vr->fib4 = NULL; | 833 | vr->fib4 = NULL; |
782 | } | 834 | } |
783 | 835 | ||
@@ -793,12 +845,12 @@ static struct mlxsw_sp_vr *mlxsw_sp_vr_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, | |||
793 | return vr; | 845 | return vr; |
794 | } | 846 | } |
795 | 847 | ||
796 | static void mlxsw_sp_vr_put(struct mlxsw_sp_vr *vr) | 848 | static void mlxsw_sp_vr_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_vr *vr) |
797 | { | 849 | { |
798 | if (!vr->rif_count && list_empty(&vr->fib4->node_list) && | 850 | if (!vr->rif_count && list_empty(&vr->fib4->node_list) && |
799 | list_empty(&vr->fib6->node_list) && | 851 | list_empty(&vr->fib6->node_list) && |
800 | mlxsw_sp_mr_table_empty(vr->mr4_table)) | 852 | mlxsw_sp_mr_table_empty(vr->mr4_table)) |
801 | mlxsw_sp_vr_destroy(vr); | 853 | mlxsw_sp_vr_destroy(mlxsw_sp, vr); |
802 | } | 854 | } |
803 | 855 | ||
804 | static bool | 856 | static bool |
@@ -809,7 +861,7 @@ mlxsw_sp_vr_lpm_tree_should_replace(struct mlxsw_sp_vr *vr, | |||
809 | 861 | ||
810 | if (!mlxsw_sp_vr_is_used(vr)) | 862 | if (!mlxsw_sp_vr_is_used(vr)) |
811 | return false; | 863 | return false; |
812 | if (fib->lpm_tree && fib->lpm_tree->id == tree_id) | 864 | if (fib->lpm_tree->id == tree_id) |
813 | return true; | 865 | return true; |
814 | return false; | 866 | return false; |
815 | } | 867 | } |
@@ -839,14 +891,13 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, | |||
839 | struct mlxsw_sp_fib *fib, | 891 | struct mlxsw_sp_fib *fib, |
840 | struct mlxsw_sp_lpm_tree *new_tree) | 892 | struct mlxsw_sp_lpm_tree *new_tree) |
841 | { | 893 | { |
842 | struct mlxsw_sp_lpm_tree *old_tree = fib->lpm_tree; | ||
843 | enum mlxsw_sp_l3proto proto = fib->proto; | 894 | enum mlxsw_sp_l3proto proto = fib->proto; |
895 | struct mlxsw_sp_lpm_tree *old_tree; | ||
844 | u8 old_id, new_id = new_tree->id; | 896 | u8 old_id, new_id = new_tree->id; |
845 | struct mlxsw_sp_vr *vr; | 897 | struct mlxsw_sp_vr *vr; |
846 | int i, err; | 898 | int i, err; |
847 | 899 | ||
848 | if (!old_tree) | 900 | old_tree = mlxsw_sp->router->lpm.proto_trees[proto]; |
849 | goto no_replace; | ||
850 | old_id = old_tree->id; | 901 | old_id = old_tree->id; |
851 | 902 | ||
852 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { | 903 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { |
@@ -860,6 +911,11 @@ static int mlxsw_sp_vrs_lpm_tree_replace(struct mlxsw_sp *mlxsw_sp, | |||
860 | goto err_tree_replace; | 911 | goto err_tree_replace; |
861 | } | 912 | } |
862 | 913 | ||
914 | memcpy(new_tree->prefix_ref_count, old_tree->prefix_ref_count, | ||
915 | sizeof(new_tree->prefix_ref_count)); | ||
916 | mlxsw_sp->router->lpm.proto_trees[proto] = new_tree; | ||
917 | mlxsw_sp_lpm_tree_put(mlxsw_sp, old_tree); | ||
918 | |||
863 | return 0; | 919 | return 0; |
864 | 920 | ||
865 | err_tree_replace: | 921 | err_tree_replace: |
@@ -871,36 +927,6 @@ err_tree_replace: | |||
871 | old_tree); | 927 | old_tree); |
872 | } | 928 | } |
873 | return err; | 929 | return err; |
874 | |||
875 | no_replace: | ||
876 | fib->lpm_tree = new_tree; | ||
877 | mlxsw_sp_lpm_tree_hold(new_tree); | ||
878 | err = mlxsw_sp_vr_lpm_tree_bind(mlxsw_sp, fib, new_tree->id); | ||
879 | if (err) { | ||
880 | mlxsw_sp_lpm_tree_put(mlxsw_sp, new_tree); | ||
881 | fib->lpm_tree = NULL; | ||
882 | return err; | ||
883 | } | ||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | static void | ||
888 | mlxsw_sp_vrs_prefixes(struct mlxsw_sp *mlxsw_sp, | ||
889 | enum mlxsw_sp_l3proto proto, | ||
890 | struct mlxsw_sp_prefix_usage *req_prefix_usage) | ||
891 | { | ||
892 | int i; | ||
893 | |||
894 | for (i = 0; i < MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_VRS); i++) { | ||
895 | struct mlxsw_sp_vr *vr = &mlxsw_sp->router->vrs[i]; | ||
896 | struct mlxsw_sp_fib *fib = mlxsw_sp_vr_fib(vr, proto); | ||
897 | unsigned char prefix; | ||
898 | |||
899 | if (!mlxsw_sp_vr_is_used(vr)) | ||
900 | continue; | ||
901 | mlxsw_sp_prefix_usage_for_each(prefix, &fib->prefix_usage) | ||
902 | mlxsw_sp_prefix_usage_set(req_prefix_usage, prefix); | ||
903 | } | ||
904 | } | 930 | } |
905 | 931 | ||
906 | static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) | 932 | static int mlxsw_sp_vrs_init(struct mlxsw_sp *mlxsw_sp) |
@@ -4201,68 +4227,66 @@ mlxsw_sp_fib_node_entry_is_first(const struct mlxsw_sp_fib_node *fib_node, | |||
4201 | } | 4227 | } |
4202 | 4228 | ||
4203 | static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, | 4229 | static int mlxsw_sp_fib_lpm_tree_link(struct mlxsw_sp *mlxsw_sp, |
4204 | struct mlxsw_sp_fib *fib, | ||
4205 | struct mlxsw_sp_fib_node *fib_node) | 4230 | struct mlxsw_sp_fib_node *fib_node) |
4206 | { | 4231 | { |
4207 | struct mlxsw_sp_prefix_usage req_prefix_usage = {{ 0 } }; | 4232 | struct mlxsw_sp_prefix_usage req_prefix_usage; |
4233 | struct mlxsw_sp_fib *fib = fib_node->fib; | ||
4208 | struct mlxsw_sp_lpm_tree *lpm_tree; | 4234 | struct mlxsw_sp_lpm_tree *lpm_tree; |
4209 | int err; | 4235 | int err; |
4210 | 4236 | ||
4211 | /* Since the tree is shared between all virtual routers we must | 4237 | lpm_tree = mlxsw_sp->router->lpm.proto_trees[fib->proto]; |
4212 | * make sure it contains all the required prefix lengths. This | 4238 | if (lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0) |
4213 | * can be computed by either adding the new prefix length to the | 4239 | goto out; |
4214 | * existing prefix usage of a bound tree, or by aggregating the | ||
4215 | * prefix lengths across all virtual routers and adding the new | ||
4216 | * one as well. | ||
4217 | */ | ||
4218 | if (fib->lpm_tree) | ||
4219 | mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, | ||
4220 | &fib->lpm_tree->prefix_usage); | ||
4221 | else | ||
4222 | mlxsw_sp_vrs_prefixes(mlxsw_sp, fib->proto, &req_prefix_usage); | ||
4223 | mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); | ||
4224 | 4240 | ||
4241 | mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage); | ||
4242 | mlxsw_sp_prefix_usage_set(&req_prefix_usage, fib_node->key.prefix_len); | ||
4225 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | 4243 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, |
4226 | fib->proto); | 4244 | fib->proto); |
4227 | if (IS_ERR(lpm_tree)) | 4245 | if (IS_ERR(lpm_tree)) |
4228 | return PTR_ERR(lpm_tree); | 4246 | return PTR_ERR(lpm_tree); |
4229 | 4247 | ||
4230 | if (fib->lpm_tree && fib->lpm_tree->id == lpm_tree->id) | ||
4231 | return 0; | ||
4232 | |||
4233 | err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); | 4248 | err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); |
4234 | if (err) | 4249 | if (err) |
4235 | return err; | 4250 | goto err_lpm_tree_replace; |
4236 | 4251 | ||
4252 | out: | ||
4253 | lpm_tree->prefix_ref_count[fib_node->key.prefix_len]++; | ||
4237 | return 0; | 4254 | return 0; |
4238 | } | ||
4239 | 4255 | ||
4240 | static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp, | 4256 | err_lpm_tree_replace: |
4241 | struct mlxsw_sp_fib *fib) | 4257 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); |
4242 | { | 4258 | return err; |
4243 | if (!mlxsw_sp_prefix_usage_none(&fib->prefix_usage)) | ||
4244 | return; | ||
4245 | mlxsw_sp_vr_lpm_tree_unbind(mlxsw_sp, fib); | ||
4246 | mlxsw_sp_lpm_tree_put(mlxsw_sp, fib->lpm_tree); | ||
4247 | fib->lpm_tree = NULL; | ||
4248 | } | 4259 | } |
4249 | 4260 | ||
4250 | static void mlxsw_sp_fib_node_prefix_inc(struct mlxsw_sp_fib_node *fib_node) | 4261 | static void mlxsw_sp_fib_lpm_tree_unlink(struct mlxsw_sp *mlxsw_sp, |
4262 | struct mlxsw_sp_fib_node *fib_node) | ||
4251 | { | 4263 | { |
4252 | unsigned char prefix_len = fib_node->key.prefix_len; | 4264 | struct mlxsw_sp_lpm_tree *lpm_tree = fib_node->fib->lpm_tree; |
4265 | struct mlxsw_sp_prefix_usage req_prefix_usage; | ||
4253 | struct mlxsw_sp_fib *fib = fib_node->fib; | 4266 | struct mlxsw_sp_fib *fib = fib_node->fib; |
4267 | int err; | ||
4254 | 4268 | ||
4255 | if (fib->prefix_ref_count[prefix_len]++ == 0) | 4269 | if (--lpm_tree->prefix_ref_count[fib_node->key.prefix_len] != 0) |
4256 | mlxsw_sp_prefix_usage_set(&fib->prefix_usage, prefix_len); | 4270 | return; |
4257 | } | 4271 | /* Try to construct a new LPM tree from the current prefix usage |
4272 | * minus the unused one. If we fail, continue using the old one. | ||
4273 | */ | ||
4274 | mlxsw_sp_prefix_usage_cpy(&req_prefix_usage, &lpm_tree->prefix_usage); | ||
4275 | mlxsw_sp_prefix_usage_clear(&req_prefix_usage, | ||
4276 | fib_node->key.prefix_len); | ||
4277 | lpm_tree = mlxsw_sp_lpm_tree_get(mlxsw_sp, &req_prefix_usage, | ||
4278 | fib->proto); | ||
4279 | if (IS_ERR(lpm_tree)) | ||
4280 | return; | ||
4258 | 4281 | ||
4259 | static void mlxsw_sp_fib_node_prefix_dec(struct mlxsw_sp_fib_node *fib_node) | 4282 | err = mlxsw_sp_vrs_lpm_tree_replace(mlxsw_sp, fib, lpm_tree); |
4260 | { | 4283 | if (err) |
4261 | unsigned char prefix_len = fib_node->key.prefix_len; | 4284 | goto err_lpm_tree_replace; |
4262 | struct mlxsw_sp_fib *fib = fib_node->fib; | ||
4263 | 4285 | ||
4264 | if (--fib->prefix_ref_count[prefix_len] == 0) | 4286 | return; |
4265 | mlxsw_sp_prefix_usage_clear(&fib->prefix_usage, prefix_len); | 4287 | |
4288 | err_lpm_tree_replace: | ||
4289 | mlxsw_sp_lpm_tree_put(mlxsw_sp, lpm_tree); | ||
4266 | } | 4290 | } |
4267 | 4291 | ||
4268 | static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, | 4292 | static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, |
@@ -4276,12 +4300,10 @@ static int mlxsw_sp_fib_node_init(struct mlxsw_sp *mlxsw_sp, | |||
4276 | return err; | 4300 | return err; |
4277 | fib_node->fib = fib; | 4301 | fib_node->fib = fib; |
4278 | 4302 | ||
4279 | err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib, fib_node); | 4303 | err = mlxsw_sp_fib_lpm_tree_link(mlxsw_sp, fib_node); |
4280 | if (err) | 4304 | if (err) |
4281 | goto err_fib_lpm_tree_link; | 4305 | goto err_fib_lpm_tree_link; |
4282 | 4306 | ||
4283 | mlxsw_sp_fib_node_prefix_inc(fib_node); | ||
4284 | |||
4285 | return 0; | 4307 | return 0; |
4286 | 4308 | ||
4287 | err_fib_lpm_tree_link: | 4309 | err_fib_lpm_tree_link: |
@@ -4295,8 +4317,7 @@ static void mlxsw_sp_fib_node_fini(struct mlxsw_sp *mlxsw_sp, | |||
4295 | { | 4317 | { |
4296 | struct mlxsw_sp_fib *fib = fib_node->fib; | 4318 | struct mlxsw_sp_fib *fib = fib_node->fib; |
4297 | 4319 | ||
4298 | mlxsw_sp_fib_node_prefix_dec(fib_node); | 4320 | mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib_node); |
4299 | mlxsw_sp_fib_lpm_tree_unlink(mlxsw_sp, fib); | ||
4300 | fib_node->fib = NULL; | 4321 | fib_node->fib = NULL; |
4301 | mlxsw_sp_fib_node_remove(fib, fib_node); | 4322 | mlxsw_sp_fib_node_remove(fib, fib_node); |
4302 | } | 4323 | } |
@@ -4335,7 +4356,7 @@ mlxsw_sp_fib_node_get(struct mlxsw_sp *mlxsw_sp, u32 tb_id, const void *addr, | |||
4335 | err_fib_node_init: | 4356 | err_fib_node_init: |
4336 | mlxsw_sp_fib_node_destroy(fib_node); | 4357 | mlxsw_sp_fib_node_destroy(fib_node); |
4337 | err_fib_node_create: | 4358 | err_fib_node_create: |
4338 | mlxsw_sp_vr_put(vr); | 4359 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
4339 | return ERR_PTR(err); | 4360 | return ERR_PTR(err); |
4340 | } | 4361 | } |
4341 | 4362 | ||
@@ -4348,7 +4369,7 @@ static void mlxsw_sp_fib_node_put(struct mlxsw_sp *mlxsw_sp, | |||
4348 | return; | 4369 | return; |
4349 | mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node); | 4370 | mlxsw_sp_fib_node_fini(mlxsw_sp, fib_node); |
4350 | mlxsw_sp_fib_node_destroy(fib_node); | 4371 | mlxsw_sp_fib_node_destroy(fib_node); |
4351 | mlxsw_sp_vr_put(vr); | 4372 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
4352 | } | 4373 | } |
4353 | 4374 | ||
4354 | static struct mlxsw_sp_fib4_entry * | 4375 | static struct mlxsw_sp_fib4_entry * |
@@ -5371,7 +5392,7 @@ static void mlxsw_sp_router_fibmr_del(struct mlxsw_sp *mlxsw_sp, | |||
5371 | return; | 5392 | return; |
5372 | 5393 | ||
5373 | mlxsw_sp_mr_route4_del(vr->mr4_table, men_info->mfc); | 5394 | mlxsw_sp_mr_route4_del(vr->mr4_table, men_info->mfc); |
5374 | mlxsw_sp_vr_put(vr); | 5395 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
5375 | } | 5396 | } |
5376 | 5397 | ||
5377 | static int | 5398 | static int |
@@ -5408,7 +5429,7 @@ mlxsw_sp_router_fibmr_vif_del(struct mlxsw_sp *mlxsw_sp, | |||
5408 | return; | 5429 | return; |
5409 | 5430 | ||
5410 | mlxsw_sp_mr_vif_del(vr->mr4_table, ven_info->vif_index); | 5431 | mlxsw_sp_mr_vif_del(vr->mr4_table, ven_info->vif_index); |
5411 | mlxsw_sp_vr_put(vr); | 5432 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
5412 | } | 5433 | } |
5413 | 5434 | ||
5414 | static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) | 5435 | static int mlxsw_sp_router_set_abort_trap(struct mlxsw_sp *mlxsw_sp) |
@@ -6057,7 +6078,7 @@ err_fid_get: | |||
6057 | err_rif_alloc: | 6078 | err_rif_alloc: |
6058 | err_rif_index_alloc: | 6079 | err_rif_index_alloc: |
6059 | vr->rif_count--; | 6080 | vr->rif_count--; |
6060 | mlxsw_sp_vr_put(vr); | 6081 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
6061 | return ERR_PTR(err); | 6082 | return ERR_PTR(err); |
6062 | } | 6083 | } |
6063 | 6084 | ||
@@ -6080,7 +6101,7 @@ void mlxsw_sp_rif_destroy(struct mlxsw_sp_rif *rif) | |||
6080 | mlxsw_sp_fid_put(fid); | 6101 | mlxsw_sp_fid_put(fid); |
6081 | kfree(rif); | 6102 | kfree(rif); |
6082 | vr->rif_count--; | 6103 | vr->rif_count--; |
6083 | mlxsw_sp_vr_put(vr); | 6104 | mlxsw_sp_vr_put(mlxsw_sp, vr); |
6084 | } | 6105 | } |
6085 | 6106 | ||
6086 | static void | 6107 | static void |
@@ -6870,7 +6891,7 @@ mlxsw_sp_rif_ipip_lb_configure(struct mlxsw_sp_rif *rif) | |||
6870 | return 0; | 6891 | return 0; |
6871 | 6892 | ||
6872 | err_loopback_op: | 6893 | err_loopback_op: |
6873 | mlxsw_sp_vr_put(ul_vr); | 6894 | mlxsw_sp_vr_put(mlxsw_sp, ul_vr); |
6874 | return err; | 6895 | return err; |
6875 | } | 6896 | } |
6876 | 6897 | ||
@@ -6884,7 +6905,7 @@ static void mlxsw_sp_rif_ipip_lb_deconfigure(struct mlxsw_sp_rif *rif) | |||
6884 | mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false); | 6905 | mlxsw_sp_rif_ipip_lb_op(lb_rif, ul_vr, false); |
6885 | 6906 | ||
6886 | --ul_vr->rif_count; | 6907 | --ul_vr->rif_count; |
6887 | mlxsw_sp_vr_put(ul_vr); | 6908 | mlxsw_sp_vr_put(mlxsw_sp, ul_vr); |
6888 | } | 6909 | } |
6889 | 6910 | ||
6890 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = { | 6911 | static const struct mlxsw_sp_rif_ops mlxsw_sp_rif_ipip_lb_ops = { |