diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c | 292 |
1 files changed, 180 insertions, 112 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c index d20b143de3b4..a82539609d49 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_mr.c @@ -33,6 +33,7 @@ */ #include <linux/rhashtable.h> +#include <net/ipv6.h> #include "spectrum_mr.h" #include "spectrum_router.h" @@ -47,6 +48,11 @@ struct mlxsw_sp_mr { /* priv has to be always the last item */ }; +struct mlxsw_sp_mr_vif; +struct mlxsw_sp_mr_vif_ops { + bool (*is_regular)(const struct mlxsw_sp_mr_vif *vif); +}; + struct mlxsw_sp_mr_vif { struct net_device *dev; const struct mlxsw_sp_rif *rif; @@ -61,6 +67,9 @@ struct mlxsw_sp_mr_vif { * instance is used as an ingress VIF */ struct list_head route_ivif_list; + + /* Protocol specific operations for a VIF */ + const struct mlxsw_sp_mr_vif_ops *ops; }; struct mlxsw_sp_mr_route_vif_entry { @@ -70,6 +79,17 @@ struct mlxsw_sp_mr_route_vif_entry { struct mlxsw_sp_mr_route *mr_route; }; +struct mlxsw_sp_mr_table; +struct mlxsw_sp_mr_table_ops { + bool (*is_route_valid)(const struct mlxsw_sp_mr_table *mr_table, + const struct mr_mfc *mfc); + void (*key_create)(struct mlxsw_sp_mr_table *mr_table, + struct mlxsw_sp_mr_route_key *key, + struct mr_mfc *mfc); + bool (*is_route_starg)(const struct mlxsw_sp_mr_table *mr_table, + const struct mlxsw_sp_mr_route *mr_route); +}; + struct mlxsw_sp_mr_table { struct list_head node; enum mlxsw_sp_l3proto proto; @@ -78,6 +98,7 @@ struct mlxsw_sp_mr_table { struct mlxsw_sp_mr_vif vifs[MAXVIFS]; struct list_head route_list; struct rhashtable route_ht; + const struct mlxsw_sp_mr_table_ops *ops; char catchall_route_priv[0]; /* catchall_route_priv has to be always the last item */ }; @@ -88,7 +109,7 @@ struct mlxsw_sp_mr_route { struct mlxsw_sp_mr_route_key key; enum mlxsw_sp_mr_route_action route_action; u16 min_mtu; - struct mfc_cache *mfc4; + struct mr_mfc *mfc; void *route_priv; const struct mlxsw_sp_mr_table *mr_table; /* A list of route_vif_entry structs that point to the egress VIFs */ @@ -104,14 +125,9 @@ static const struct rhashtable_params mlxsw_sp_mr_route_ht_params = { .automatic_shrinking = true, }; -static bool mlxsw_sp_mr_vif_regular(const struct mlxsw_sp_mr_vif *vif) -{ - return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER)); -} - static bool mlxsw_sp_mr_vif_valid(const struct mlxsw_sp_mr_vif *vif) { - return mlxsw_sp_mr_vif_regular(vif) && vif->dev && vif->rif; + return vif->ops->is_regular(vif) && vif->dev && vif->rif; } static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif) @@ -122,18 +138,9 @@ static bool mlxsw_sp_mr_vif_exists(const struct mlxsw_sp_mr_vif *vif) static bool mlxsw_sp_mr_route_ivif_in_evifs(const struct mlxsw_sp_mr_route *mr_route) { - vifi_t ivif; + vifi_t ivif = mr_route->mfc->mfc_parent; - switch (mr_route->mr_table->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - ivif = mr_route->mfc4->mfc_parent; - return mr_route->mfc4->mfc_un.res.ttls[ivif] != 255; - case MLXSW_SP_L3_PROTO_IPV6: - /* fall through */ - default: - WARN_ON_ONCE(1); - } - return false; + return mr_route->mfc->mfc_un.res.ttls[ivif] != 255; } static int @@ -149,19 +156,6 @@ mlxsw_sp_mr_route_valid_evifs_num(const struct mlxsw_sp_mr_route *mr_route) return valid_evifs; } -static bool mlxsw_sp_mr_route_starg(const struct mlxsw_sp_mr_route *mr_route) -{ - switch (mr_route->mr_table->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY); - case MLXSW_SP_L3_PROTO_IPV6: - /* fall through */ - default: - WARN_ON_ONCE(1); - } - return false; -} - static enum mlxsw_sp_mr_route_action mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route) { @@ -174,7 +168,8 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route) /* The kernel does not match a (*,G) route that the ingress interface is * not one of the egress interfaces, so trap these kind of routes. */ - if (mlxsw_sp_mr_route_starg(mr_route) && + if (mr_route->mr_table->ops->is_route_starg(mr_route->mr_table, + mr_route) && !mlxsw_sp_mr_route_ivif_in_evifs(mr_route)) return MLXSW_SP_MR_ROUTE_ACTION_TRAP; @@ -195,25 +190,11 @@ mlxsw_sp_mr_route_action(const struct mlxsw_sp_mr_route *mr_route) static enum mlxsw_sp_mr_route_prio mlxsw_sp_mr_route_prio(const struct mlxsw_sp_mr_route *mr_route) { - return mlxsw_sp_mr_route_starg(mr_route) ? + return mr_route->mr_table->ops->is_route_starg(mr_route->mr_table, + mr_route) ? MLXSW_SP_MR_ROUTE_PRIO_STARG : MLXSW_SP_MR_ROUTE_PRIO_SG; } -static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table, - struct mlxsw_sp_mr_route_key *key, - const struct mfc_cache *mfc) -{ - bool starg = (mfc->mfc_origin == htonl(INADDR_ANY)); - - memset(key, 0, sizeof(*key)); - key->vrid = mr_table->vr_id; - key->proto = mr_table->proto; - key->group.addr4 = mfc->mfc_mcastgrp; - key->group_mask.addr4 = htonl(0xffffffff); - key->source.addr4 = mfc->mfc_origin; - key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff); -} - static int mlxsw_sp_mr_route_evif_link(struct mlxsw_sp_mr_route *mr_route, struct mlxsw_sp_mr_vif *mr_vif) { @@ -343,8 +324,8 @@ static void mlxsw_sp_mr_route_erase(struct mlxsw_sp_mr_table *mr_table, } static struct mlxsw_sp_mr_route * -mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table, - struct mfc_cache *mfc) +mlxsw_sp_mr_route_create(struct mlxsw_sp_mr_table *mr_table, + struct mr_mfc *mfc) { struct mlxsw_sp_mr_route_vif_entry *rve, *tmp; struct mlxsw_sp_mr_route *mr_route; @@ -356,12 +337,13 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table, if (!mr_route) return ERR_PTR(-ENOMEM); INIT_LIST_HEAD(&mr_route->evif_list); - mlxsw_sp_mr_route4_key(mr_table, &mr_route->key, mfc); /* Find min_mtu and link iVIF and eVIFs */ mr_route->min_mtu = ETH_MAX_MTU; - ipmr_cache_hold(mfc); - mr_route->mfc4 = mfc; + mr_cache_hold(mfc); + mr_route->mfc = mfc; + mr_table->ops->key_create(mr_table, &mr_route->key, mr_route->mfc); + mr_route->mr_table = mr_table; for (i = 0; i < MAXVIFS; i++) { if (mfc->mfc_un.res.ttls[i] != 255) { @@ -374,59 +356,38 @@ mlxsw_sp_mr_route4_create(struct mlxsw_sp_mr_table *mr_table, mr_route->min_mtu = mr_table->vifs[i].dev->mtu; } } - mlxsw_sp_mr_route_ivif_link(mr_route, &mr_table->vifs[mfc->mfc_parent]); + mlxsw_sp_mr_route_ivif_link(mr_route, + &mr_table->vifs[mfc->mfc_parent]); mr_route->route_action = mlxsw_sp_mr_route_action(mr_route); return mr_route; err: - ipmr_cache_put(mfc); + mr_cache_put(mfc); list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node) mlxsw_sp_mr_route_evif_unlink(rve); kfree(mr_route); return ERR_PTR(err); } -static void mlxsw_sp_mr_route4_destroy(struct mlxsw_sp_mr_table *mr_table, - struct mlxsw_sp_mr_route *mr_route) +static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table, + struct mlxsw_sp_mr_route *mr_route) { struct mlxsw_sp_mr_route_vif_entry *rve, *tmp; mlxsw_sp_mr_route_ivif_unlink(mr_route); - ipmr_cache_put(mr_route->mfc4); + mr_cache_put(mr_route->mfc); list_for_each_entry_safe(rve, tmp, &mr_route->evif_list, route_node) mlxsw_sp_mr_route_evif_unlink(rve); kfree(mr_route); } -static void mlxsw_sp_mr_route_destroy(struct mlxsw_sp_mr_table *mr_table, - struct mlxsw_sp_mr_route *mr_route) -{ - switch (mr_table->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - mlxsw_sp_mr_route4_destroy(mr_table, mr_route); - break; - case MLXSW_SP_L3_PROTO_IPV6: - /* fall through */ - default: - WARN_ON_ONCE(1); - } -} - static void mlxsw_sp_mr_mfc_offload_set(struct mlxsw_sp_mr_route *mr_route, bool offload) { - switch (mr_route->mr_table->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - if (offload) - mr_route->mfc4->mfc_flags |= MFC_OFFLOAD; - else - mr_route->mfc4->mfc_flags &= ~MFC_OFFLOAD; - break; - case MLXSW_SP_L3_PROTO_IPV6: - /* fall through */ - default: - WARN_ON_ONCE(1); - } + if (offload) + mr_route->mfc->mfc_flags |= MFC_OFFLOAD; + else + mr_route->mfc->mfc_flags &= ~MFC_OFFLOAD; } static void mlxsw_sp_mr_mfc_offload_update(struct mlxsw_sp_mr_route *mr_route) @@ -448,25 +409,18 @@ static void __mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table, mlxsw_sp_mr_route_destroy(mr_table, mr_route); } -int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table, - struct mfc_cache *mfc, bool replace) +int mlxsw_sp_mr_route_add(struct mlxsw_sp_mr_table *mr_table, + struct mr_mfc *mfc, bool replace) { struct mlxsw_sp_mr_route *mr_orig_route = NULL; struct mlxsw_sp_mr_route *mr_route; int err; - /* If the route is a (*,*) route, abort, as these kind of routes are - * used for proxy routes. - */ - if (mfc->mfc_origin == htonl(INADDR_ANY) && - mfc->mfc_mcastgrp == htonl(INADDR_ANY)) { - dev_warn(mr_table->mlxsw_sp->bus_info->dev, - "Offloading proxy routes is not supported.\n"); + if (!mr_table->ops->is_route_valid(mr_table, mfc)) return -EINVAL; - } /* Create a new route */ - mr_route = mlxsw_sp_mr_route4_create(mr_table, mfc); + mr_route = mlxsw_sp_mr_route_create(mr_table, mfc); if (IS_ERR(mr_route)) return PTR_ERR(mr_route); @@ -511,7 +465,7 @@ int mlxsw_sp_mr_route4_add(struct mlxsw_sp_mr_table *mr_table, &mr_orig_route->ht_node, mlxsw_sp_mr_route_ht_params); list_del(&mr_orig_route->node); - mlxsw_sp_mr_route4_destroy(mr_table, mr_orig_route); + mlxsw_sp_mr_route_destroy(mr_table, mr_orig_route); } mlxsw_sp_mr_mfc_offload_update(mr_route); @@ -524,17 +478,17 @@ err_rhashtable_insert: list_del(&mr_route->node); err_no_orig_route: err_duplicate_route: - mlxsw_sp_mr_route4_destroy(mr_table, mr_route); + mlxsw_sp_mr_route_destroy(mr_table, mr_route); return err; } -void mlxsw_sp_mr_route4_del(struct mlxsw_sp_mr_table *mr_table, - struct mfc_cache *mfc) +void mlxsw_sp_mr_route_del(struct mlxsw_sp_mr_table *mr_table, + struct mr_mfc *mfc) { struct mlxsw_sp_mr_route *mr_route; struct mlxsw_sp_mr_route_key key; - mlxsw_sp_mr_route4_key(mr_table, &key, mfc); + mr_table->ops->key_create(mr_table, &key, mfc); mr_route = rhashtable_lookup_fast(&mr_table->route_ht, &key, mlxsw_sp_mr_route_ht_params); if (mr_route) @@ -839,6 +793,125 @@ void mlxsw_sp_mr_rif_mtu_update(struct mlxsw_sp_mr_table *mr_table, } } +/* Protocol specific functions */ +static bool +mlxsw_sp_mr_route4_validate(const struct mlxsw_sp_mr_table *mr_table, + const struct mr_mfc *c) +{ + struct mfc_cache *mfc = (struct mfc_cache *) c; + + /* If the route is a (*,*) route, abort, as these kind of routes are + * used for proxy routes. + */ + if (mfc->mfc_origin == htonl(INADDR_ANY) && + mfc->mfc_mcastgrp == htonl(INADDR_ANY)) { + dev_warn(mr_table->mlxsw_sp->bus_info->dev, + "Offloading proxy routes is not supported.\n"); + return false; + } + return true; +} + +static void mlxsw_sp_mr_route4_key(struct mlxsw_sp_mr_table *mr_table, + struct mlxsw_sp_mr_route_key *key, + struct mr_mfc *c) +{ + const struct mfc_cache *mfc = (struct mfc_cache *) c; + bool starg; + + starg = (mfc->mfc_origin == htonl(INADDR_ANY)); + + memset(key, 0, sizeof(*key)); + key->vrid = mr_table->vr_id; + key->proto = MLXSW_SP_L3_PROTO_IPV4; + key->group.addr4 = mfc->mfc_mcastgrp; + key->group_mask.addr4 = htonl(0xffffffff); + key->source.addr4 = mfc->mfc_origin; + key->source_mask.addr4 = htonl(starg ? 0 : 0xffffffff); +} + +static bool mlxsw_sp_mr_route4_starg(const struct mlxsw_sp_mr_table *mr_table, + const struct mlxsw_sp_mr_route *mr_route) +{ + return mr_route->key.source_mask.addr4 == htonl(INADDR_ANY); +} + +static bool mlxsw_sp_mr_vif4_is_regular(const struct mlxsw_sp_mr_vif *vif) +{ + return !(vif->vif_flags & (VIFF_TUNNEL | VIFF_REGISTER)); +} + +static bool +mlxsw_sp_mr_route6_validate(const struct mlxsw_sp_mr_table *mr_table, + const struct mr_mfc *c) +{ + struct mfc6_cache *mfc = (struct mfc6_cache *) c; + + /* If the route is a (*,*) route, abort, as these kind of routes are + * used for proxy routes. + */ + if (ipv6_addr_any(&mfc->mf6c_origin) && + ipv6_addr_any(&mfc->mf6c_mcastgrp)) { + dev_warn(mr_table->mlxsw_sp->bus_info->dev, + "Offloading proxy routes is not supported.\n"); + return false; + } + return true; +} + +static void mlxsw_sp_mr_route6_key(struct mlxsw_sp_mr_table *mr_table, + struct mlxsw_sp_mr_route_key *key, + struct mr_mfc *c) +{ + const struct mfc6_cache *mfc = (struct mfc6_cache *) c; + + memset(key, 0, sizeof(*key)); + key->vrid = mr_table->vr_id; + key->proto = MLXSW_SP_L3_PROTO_IPV6; + key->group.addr6 = mfc->mf6c_mcastgrp; + memset(&key->group_mask.addr6, 0xff, sizeof(key->group_mask.addr6)); + key->source.addr6 = mfc->mf6c_origin; + if (!ipv6_addr_any(&mfc->mf6c_origin)) + memset(&key->source_mask.addr6, 0xff, + sizeof(key->source_mask.addr6)); +} + +static bool mlxsw_sp_mr_route6_starg(const struct mlxsw_sp_mr_table *mr_table, + const struct mlxsw_sp_mr_route *mr_route) +{ + return ipv6_addr_any(&mr_route->key.source_mask.addr6); +} + +static bool mlxsw_sp_mr_vif6_is_regular(const struct mlxsw_sp_mr_vif *vif) +{ + return !(vif->vif_flags & MIFF_REGISTER); +} + +static struct +mlxsw_sp_mr_vif_ops mlxsw_sp_mr_vif_ops_arr[] = { + { + .is_regular = mlxsw_sp_mr_vif4_is_regular, + }, + { + .is_regular = mlxsw_sp_mr_vif6_is_regular, + }, +}; + +static struct +mlxsw_sp_mr_table_ops mlxsw_sp_mr_table_ops_arr[] = { + { + .is_route_valid = mlxsw_sp_mr_route4_validate, + .key_create = mlxsw_sp_mr_route4_key, + .is_route_starg = mlxsw_sp_mr_route4_starg, + }, + { + .is_route_valid = mlxsw_sp_mr_route6_validate, + .key_create = mlxsw_sp_mr_route6_key, + .is_route_starg = mlxsw_sp_mr_route6_starg, + }, + +}; + struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp, u32 vr_id, enum mlxsw_sp_l3proto proto) @@ -847,6 +920,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp, .prio = MLXSW_SP_MR_ROUTE_PRIO_CATCHALL, .key = { .vrid = vr_id, + .proto = proto, }, .value = { .route_action = MLXSW_SP_MR_ROUTE_ACTION_TRAP, @@ -865,6 +939,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp, mr_table->vr_id = vr_id; mr_table->mlxsw_sp = mlxsw_sp; mr_table->proto = proto; + mr_table->ops = &mlxsw_sp_mr_table_ops_arr[proto]; INIT_LIST_HEAD(&mr_table->route_list); err = rhashtable_init(&mr_table->route_ht, @@ -875,6 +950,7 @@ struct mlxsw_sp_mr_table *mlxsw_sp_mr_table_create(struct mlxsw_sp *mlxsw_sp, for (i = 0; i < MAXVIFS; i++) { INIT_LIST_HEAD(&mr_table->vifs[i].route_evif_list); INIT_LIST_HEAD(&mr_table->vifs[i].route_ivif_list); + mr_table->vifs[i].ops = &mlxsw_sp_mr_vif_ops_arr[proto]; } err = mr->mr_ops->route_create(mlxsw_sp, mr->priv, @@ -941,18 +1017,10 @@ static void mlxsw_sp_mr_route_stats_update(struct mlxsw_sp *mlxsw_sp, mr->mr_ops->route_stats(mlxsw_sp, mr_route->route_priv, &packets, &bytes); - switch (mr_route->mr_table->proto) { - case MLXSW_SP_L3_PROTO_IPV4: - if (mr_route->mfc4->mfc_un.res.pkt != packets) - mr_route->mfc4->mfc_un.res.lastuse = jiffies; - mr_route->mfc4->mfc_un.res.pkt = packets; - mr_route->mfc4->mfc_un.res.bytes = bytes; - break; - case MLXSW_SP_L3_PROTO_IPV6: - /* fall through */ - default: - WARN_ON_ONCE(1); - } + if (mr_route->mfc->mfc_un.res.pkt != packets) + mr_route->mfc->mfc_un.res.lastuse = jiffies; + mr_route->mfc->mfc_un.res.pkt = packets; + mr_route->mfc->mfc_un.res.bytes = bytes; } static void mlxsw_sp_mr_stats_update(struct work_struct *work) |