diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 312 |
1 files changed, 282 insertions, 30 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 87617df694..40ba314fbc 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -19,6 +19,7 @@ #include <linux/net_namespace.h> #include <linux/mutex.h> #include <linux/genalloc.h> +#include <linux/xarray.h> #include <net/netevent.h> #include <net/neighbour.h> #include <net/arp.h> @@ -2251,7 +2252,7 @@ int mlxsw_sp_neigh_counter_get(struct mlxsw_sp *mlxsw_sp, return -EINVAL; return mlxsw_sp_flow_counter_get(mlxsw_sp, neigh_entry->counter_index, - p_counter, NULL); + false, p_counter, NULL); } static struct mlxsw_sp_neigh_entry * @@ -3049,6 +3050,8 @@ struct mlxsw_sp_nexthop_key { struct fib_nh *fib_nh; }; +struct mlxsw_sp_nexthop_counter; + struct mlxsw_sp_nexthop { struct list_head neigh_list_node; /* member of neigh entry list */ struct list_head crif_list_node; @@ -3080,8 +3083,8 @@ struct mlxsw_sp_nexthop { struct mlxsw_sp_neigh_entry *neigh_entry; struct mlxsw_sp_ipip_entry *ipip_entry; }; - unsigned int counter_index; - bool counter_valid; + struct mlxsw_sp_nexthop_counter *counter; + u32 id; /* NH ID for members of a NH object group. */ }; static struct net_device * @@ -3106,8 +3109,10 @@ struct mlxsw_sp_nexthop_group_info { int sum_norm_weight; u8 adj_index_valid:1, gateway:1, /* routes using the group use a gateway */ - is_resilient:1; + is_resilient:1, + hw_stats:1; struct list_head list; /* member in nh_res_grp_list */ + struct xarray nexthop_counters; struct mlxsw_sp_nexthop nexthops[] __counted_by(count); }; @@ -3151,39 +3156,148 @@ struct mlxsw_sp_nexthop_group { bool can_destroy; }; -void mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp, +struct mlxsw_sp_nexthop_counter { + unsigned int counter_index; + refcount_t ref_count; +}; + +static struct mlxsw_sp_nexthop_counter * +mlxsw_sp_nexthop_counter_alloc(struct mlxsw_sp *mlxsw_sp) +{ + struct mlxsw_sp_nexthop_counter *nhct; + int err; + + nhct = kzalloc(sizeof(*nhct), GFP_KERNEL); + if (!nhct) + return ERR_PTR(-ENOMEM); + + err = mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nhct->counter_index); + if (err) + goto err_counter_alloc; + + refcount_set(&nhct->ref_count, 1); + return nhct; + +err_counter_alloc: + kfree(nhct); + return ERR_PTR(err); +} + +static void +mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_counter *nhct) +{ + mlxsw_sp_flow_counter_free(mlxsw_sp, nhct->counter_index); + kfree(nhct); +} + +static struct mlxsw_sp_nexthop_counter * +mlxsw_sp_nexthop_sh_counter_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + struct mlxsw_sp_nexthop_group *nh_grp = nh->nhgi->nh_grp; + struct mlxsw_sp_nexthop_counter *nhct; + void *ptr; + int err; + + nhct = xa_load(&nh_grp->nhgi->nexthop_counters, nh->id); + if (nhct) { + refcount_inc(&nhct->ref_count); + return nhct; + } + + nhct = mlxsw_sp_nexthop_counter_alloc(mlxsw_sp); + if (IS_ERR(nhct)) + return nhct; + + ptr = xa_store(&nh_grp->nhgi->nexthop_counters, nh->id, nhct, + GFP_KERNEL); + if (IS_ERR(ptr)) { + err = PTR_ERR(ptr); + goto err_store; + } + + return nhct; + +err_store: + mlxsw_sp_nexthop_counter_free(mlxsw_sp, nhct); + return ERR_PTR(err); +} + +static void mlxsw_sp_nexthop_sh_counter_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + struct mlxsw_sp_nexthop_group *nh_grp = nh->nhgi->nh_grp; + struct mlxsw_sp_nexthop_counter *nhct; + + nhct = xa_load(&nh_grp->nhgi->nexthop_counters, nh->id); + if (WARN_ON(!nhct)) + return; + + if (!refcount_dec_and_test(&nhct->ref_count)) + return; + + xa_erase(&nh_grp->nhgi->nexthop_counters, nh->id); + mlxsw_sp_nexthop_counter_free(mlxsw_sp, nhct); +} + +int mlxsw_sp_nexthop_counter_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh) { + const char *table_adj = MLXSW_SP_DPIPE_TABLE_NAME_ADJ; + struct mlxsw_sp_nexthop_counter *nhct; struct devlink *devlink; + bool dpipe_stats; + + if (nh->counter) + return 0; devlink = priv_to_devlink(mlxsw_sp->core); - if (!devlink_dpipe_table_counter_enabled(devlink, - MLXSW_SP_DPIPE_TABLE_NAME_ADJ)) - return; + dpipe_stats = devlink_dpipe_table_counter_enabled(devlink, table_adj); + if (!(nh->nhgi->hw_stats || dpipe_stats)) + return 0; - if (mlxsw_sp_flow_counter_alloc(mlxsw_sp, &nh->counter_index)) - return; + if (nh->id) + nhct = mlxsw_sp_nexthop_sh_counter_get(mlxsw_sp, nh); + else + nhct = mlxsw_sp_nexthop_counter_alloc(mlxsw_sp); + if (IS_ERR(nhct)) + return PTR_ERR(nhct); - nh->counter_valid = true; + nh->counter = nhct; + return 0; } -void mlxsw_sp_nexthop_counter_free(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_nexthop *nh) +void mlxsw_sp_nexthop_counter_disable(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) { - if (!nh->counter_valid) + if (!nh->counter) return; - mlxsw_sp_flow_counter_free(mlxsw_sp, nh->counter_index); - nh->counter_valid = false; + + if (nh->id) + mlxsw_sp_nexthop_sh_counter_put(mlxsw_sp, nh); + else + mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh->counter); + nh->counter = NULL; +} + +static int mlxsw_sp_nexthop_counter_update(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop *nh) +{ + if (nh->nhgi->hw_stats) + return mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); + return 0; } int mlxsw_sp_nexthop_counter_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop *nh, u64 *p_counter) { - if (!nh->counter_valid) + if (!nh->counter) return -EINVAL; - return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter_index, - p_counter, NULL); + return mlxsw_sp_flow_counter_get(mlxsw_sp, nh->counter->counter_index, + true, p_counter, NULL); } struct mlxsw_sp_nexthop *mlxsw_sp_nexthop_next(struct mlxsw_sp_router *router, @@ -3656,8 +3770,9 @@ static int __mlxsw_sp_nexthop_eth_update(struct mlxsw_sp *mlxsw_sp, WARN_ON_ONCE(1); return -EINVAL; } - if (nh->counter_valid) - mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter_index, true); + if (nh->counter) + mlxsw_reg_ratr_counter_pack(ratr_pl, nh->counter->counter_index, + true); else mlxsw_reg_ratr_counter_pack(ratr_pl, 0, false); @@ -3744,6 +3859,7 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, nh = &nhgi->nexthops[i]; if (!nh->should_offload) { + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); nh->offloaded = 0; continue; } @@ -3751,6 +3867,10 @@ mlxsw_sp_nexthop_group_update(struct mlxsw_sp *mlxsw_sp, if (nh->update || reallocate) { int err = 0; + err = mlxsw_sp_nexthop_counter_update(mlxsw_sp, nh); + if (err) + return err; + err = mlxsw_sp_nexthop_update(mlxsw_sp, adj_index, nh, true, ratr_pl); if (err) @@ -4507,7 +4627,10 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, if (err) return err; - mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh); + err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh); + if (err) + goto err_counter_enable; + list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list); if (!dev) @@ -4531,7 +4654,8 @@ static int mlxsw_sp_nexthop4_init(struct mlxsw_sp *mlxsw_sp, err_nexthop_neigh_init: list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); +err_counter_enable: mlxsw_sp_nexthop_remove(mlxsw_sp, nh); return err; } @@ -4541,7 +4665,7 @@ static void mlxsw_sp_nexthop4_fini(struct mlxsw_sp *mlxsw_sp, { mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); mlxsw_sp_nexthop_remove(mlxsw_sp, nh); } @@ -5006,9 +5130,9 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp, break; } - mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh); list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list); nh->ifindex = dev->ifindex; + nh->id = nh_obj->id; err = mlxsw_sp_nexthop_type_init(mlxsw_sp, nh, dev); if (err) @@ -5030,7 +5154,6 @@ mlxsw_sp_nexthop_obj_init(struct mlxsw_sp *mlxsw_sp, err_type_init: list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); return err; } @@ -5041,7 +5164,7 @@ static void mlxsw_sp_nexthop_obj_fini(struct mlxsw_sp *mlxsw_sp, mlxsw_sp_nexthop_obj_blackhole_fini(mlxsw_sp, nh); mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); nh->should_offload = 0; } @@ -5053,6 +5176,7 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_nexthop_group_info *nhgi; struct mlxsw_sp_nexthop *nh; bool is_resilient = false; + bool hw_stats = false; unsigned int nhs; int err, i; @@ -5062,9 +5186,11 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp, break; case NH_NOTIFIER_INFO_TYPE_GRP: nhs = info->nh_grp->num_nh; + hw_stats = info->nh_grp->hw_stats; break; case NH_NOTIFIER_INFO_TYPE_RES_TABLE: nhs = info->nh_res_table->num_nh_buckets; + hw_stats = info->nh_res_table->hw_stats; is_resilient = true; break; default: @@ -5079,6 +5205,10 @@ mlxsw_sp_nexthop_obj_group_info_init(struct mlxsw_sp *mlxsw_sp, nhgi->gateway = mlxsw_sp_nexthop_obj_is_gateway(mlxsw_sp, info); nhgi->is_resilient = is_resilient; nhgi->count = nhs; + nhgi->hw_stats = hw_stats; + + xa_init_flags(&nhgi->nexthop_counters, XA_FLAGS_ALLOC1); + for (i = 0; i < nhgi->count; i++) { struct nh_notifier_single_info *nh_obj; int weight; @@ -5161,6 +5291,8 @@ mlxsw_sp_nexthop_obj_group_info_fini(struct mlxsw_sp *mlxsw_sp, } mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); WARN_ON_ONCE(nhgi->adj_index_valid); + WARN_ON(!xa_empty(&nhgi->nexthop_counters)); + xa_destroy(&nhgi->nexthop_counters); kfree(nhgi); } @@ -5300,6 +5432,43 @@ err_out: return err; } +static int mlxsw_sp_nexthop_obj_res_group_pre(struct mlxsw_sp *mlxsw_sp, + struct nh_notifier_info *info) +{ + struct nh_notifier_grp_info *grp_info = info->nh_grp; + struct mlxsw_sp_nexthop_group_info *nhgi; + struct mlxsw_sp_nexthop_group *nh_grp; + int err; + int i; + + nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id); + if (!nh_grp) + return 0; + nhgi = nh_grp->nhgi; + + if (nhgi->hw_stats == grp_info->hw_stats) + return 0; + + nhgi->hw_stats = grp_info->hw_stats; + + for (i = 0; i < nhgi->count; i++) { + struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[i]; + + if (nh->offloaded) + nh->update = 1; + } + + err = mlxsw_sp_nexthop_group_refresh(mlxsw_sp, nh_grp); + if (err) + goto err_group_refresh; + + return 0; + +err_group_refresh: + nhgi->hw_stats = !grp_info->hw_stats; + return err; +} + static int mlxsw_sp_nexthop_obj_new(struct mlxsw_sp *mlxsw_sp, struct nh_notifier_info *info) { @@ -5476,6 +5645,79 @@ err_nexthop_obj_init: return err; } +static void +mlxsw_sp_nexthop_obj_mp_hw_stats_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group_info *nhgi, + struct nh_notifier_grp_hw_stats_info *info) +{ + int nhi; + + for (nhi = 0; nhi < info->num_nh; nhi++) { + struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[nhi]; + u64 packets; + int err; + + err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &packets); + if (err) + continue; + + nh_grp_hw_stats_report_delta(info, nhi, packets); + } +} + +static void +mlxsw_sp_nexthop_obj_res_hw_stats_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_nexthop_group_info *nhgi, + struct nh_notifier_grp_hw_stats_info *info) +{ + int nhi = -1; + int bucket; + + for (bucket = 0; bucket < nhgi->count; bucket++) { + struct mlxsw_sp_nexthop *nh = &nhgi->nexthops[bucket]; + u64 packets; + int err; + + if (nhi == -1 || info->stats[nhi].id != nh->id) { + for (nhi = 0; nhi < info->num_nh; nhi++) + if (info->stats[nhi].id == nh->id) + break; + if (WARN_ON_ONCE(nhi == info->num_nh)) { + nhi = -1; + continue; + } + } + + err = mlxsw_sp_nexthop_counter_get(mlxsw_sp, nh, &packets); + if (err) + continue; + + nh_grp_hw_stats_report_delta(info, nhi, packets); + } +} + +static void mlxsw_sp_nexthop_obj_hw_stats_get(struct mlxsw_sp *mlxsw_sp, + struct nh_notifier_info *info) +{ + struct mlxsw_sp_nexthop_group_info *nhgi; + struct mlxsw_sp_nexthop_group *nh_grp; + + if (info->type != NH_NOTIFIER_INFO_TYPE_GRP_HW_STATS) + return; + + nh_grp = mlxsw_sp_nexthop_obj_group_lookup(mlxsw_sp, info->id); + if (!nh_grp) + return; + nhgi = nh_grp->nhgi; + + if (nhgi->is_resilient) + mlxsw_sp_nexthop_obj_res_hw_stats_get(mlxsw_sp, nhgi, + info->nh_grp_hw_stats); + else + mlxsw_sp_nexthop_obj_mp_hw_stats_get(mlxsw_sp, nhgi, + info->nh_grp_hw_stats); +} + static int mlxsw_sp_nexthop_obj_event(struct notifier_block *nb, unsigned long event, void *ptr) { @@ -5491,6 +5733,10 @@ static int mlxsw_sp_nexthop_obj_event(struct notifier_block *nb, mutex_lock(&router->lock); switch (event) { + case NEXTHOP_EVENT_RES_TABLE_PRE_REPLACE: + err = mlxsw_sp_nexthop_obj_res_group_pre(router->mlxsw_sp, + info); + break; case NEXTHOP_EVENT_REPLACE: err = mlxsw_sp_nexthop_obj_new(router->mlxsw_sp, info); break; @@ -5501,6 +5747,9 @@ static int mlxsw_sp_nexthop_obj_event(struct notifier_block *nb, err = mlxsw_sp_nexthop_obj_bucket_replace(router->mlxsw_sp, info); break; + case NEXTHOP_EVENT_HW_STATS_REPORT_DELTA: + mlxsw_sp_nexthop_obj_hw_stats_get(router->mlxsw_sp, info); + break; default: break; } @@ -6734,7 +6983,10 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, #if IS_ENABLED(CONFIG_IPV6) nh->neigh_tbl = &nd_tbl; #endif - mlxsw_sp_nexthop_counter_alloc(mlxsw_sp, nh); + + err = mlxsw_sp_nexthop_counter_enable(mlxsw_sp, nh); + if (err) + return err; list_add_tail(&nh->router_list_node, &mlxsw_sp->router->nexthop_list); @@ -6750,7 +7002,7 @@ static int mlxsw_sp_nexthop6_init(struct mlxsw_sp *mlxsw_sp, err_nexthop_type_init: list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); return err; } @@ -6759,7 +7011,7 @@ static void mlxsw_sp_nexthop6_fini(struct mlxsw_sp *mlxsw_sp, { mlxsw_sp_nexthop_type_fini(mlxsw_sp, nh); list_del(&nh->router_list_node); - mlxsw_sp_nexthop_counter_free(mlxsw_sp, nh); + mlxsw_sp_nexthop_counter_disable(mlxsw_sp, nh); } static bool mlxsw_sp_rt6_is_gateway(const struct mlxsw_sp *mlxsw_sp, |