mlxsw: spectrum_nve: Keep track of IPv6 addresses used by FDB entries

FDB entries that perform VxLAN encapsulation with an IPv6 underlay hold
a reference on a resource. Namely, the KVDL entry where the IPv6
underlay destination IP is stored. When such an FDB entry is deleted, it
needs to drop the reference from the corresponding KVDL entry.

To that end, maintain a hash table that maps an FDB entry (i.e., {MAC,
FID}) to the IPv6 address used by it.

Signed-off-by: Amit Cohen <amcohen@nvidia.com>
Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Amit Cohen 2021-12-14 16:25:49 +02:00 committed by David S. Miller
parent 4b08c3e676
commit 0860c76416
3 changed files with 162 additions and 2 deletions

View File

@ -1317,6 +1317,17 @@ void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_fid *fid,
enum mlxsw_sp_l3proto proto,
union mlxsw_sp_l3addr *addr);
int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index);
void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6);
int
mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index,
const struct in6_addr *new_addr6);
void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index);
int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack);

View File

@ -787,6 +787,142 @@ static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
ops->fdb_clear_offload(nve_dev, vni);
}
struct mlxsw_sp_nve_ipv6_ht_key {
u8 mac[ETH_ALEN];
u16 fid_index;
};
struct mlxsw_sp_nve_ipv6_ht_node {
struct rhash_head ht_node;
struct list_head list;
struct mlxsw_sp_nve_ipv6_ht_key key;
struct in6_addr addr6;
};
static const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = {
.key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key),
.key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key),
.head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node),
};
int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6,
u32 *p_kvdl_index)
{
return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index);
}
void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
const struct in6_addr *addr6)
{
mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6);
}
static struct mlxsw_sp_nve_ipv6_ht_node *
mlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_key key = {};
ether_addr_copy(key.mac, mac);
key.fid_index = fid_index;
return rhashtable_lookup_fast(&mlxsw_sp->nve->ipv6_ht, &key,
mlxsw_sp_nve_ipv6_ht_params);
}
static int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp,
const char *mac, u16 fid_index,
const struct in6_addr *addr6)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
int err;
ipv6_ht_node = kzalloc(sizeof(*ipv6_ht_node), GFP_KERNEL);
if (!ipv6_ht_node)
return -ENOMEM;
ether_addr_copy(ipv6_ht_node->key.mac, mac);
ipv6_ht_node->key.fid_index = fid_index;
ipv6_ht_node->addr6 = *addr6;
err = rhashtable_insert_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
mlxsw_sp_nve_ipv6_ht_params);
if (err)
goto err_rhashtable_insert;
list_add(&ipv6_ht_node->list, &nve->ipv6_addr_list);
return 0;
err_rhashtable_insert:
kfree(ipv6_ht_node);
return err;
}
static void
mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node)
{
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
list_del(&ipv6_ht_node->list);
rhashtable_remove_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
mlxsw_sp_nve_ipv6_ht_params);
kfree(ipv6_ht_node);
}
int
mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index,
const struct in6_addr *new_addr6)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
ASSERT_RTNL();
ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
fid_index);
if (!ipv6_ht_node)
return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index,
new_addr6);
mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
ipv6_ht_node->addr6 = *new_addr6;
return 0;
}
void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
ASSERT_RTNL();
ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
fid_index);
if (WARN_ON(!ipv6_ht_node))
return;
mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
}
static void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
u16 fid_index)
{
struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp;
struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list,
list) {
if (ipv6_ht_node->key.fid_index != fid_index)
continue;
mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
}
}
int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
struct mlxsw_sp_nve_params *params,
struct netlink_ext_ack *extack)
@ -845,6 +981,7 @@ void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index);
if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
mlxsw_sp_fid_vni(fid, &vni)))
@ -981,7 +1118,13 @@ int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
err = rhashtable_init(&nve->mc_list_ht,
&mlxsw_sp_nve_mc_list_ht_params);
if (err)
goto err_rhashtable_init;
goto err_mc_rhashtable_init;
err = rhashtable_init(&nve->ipv6_ht, &mlxsw_sp_nve_ipv6_ht_params);
if (err)
goto err_ipv6_rhashtable_init;
INIT_LIST_HEAD(&nve->ipv6_addr_list);
err = mlxsw_sp_nve_qos_init(mlxsw_sp);
if (err)
@ -1000,8 +1143,10 @@ int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
err_nve_resources_query:
err_nve_ecn_init:
err_nve_qos_init:
rhashtable_destroy(&nve->ipv6_ht);
err_ipv6_rhashtable_init:
rhashtable_destroy(&nve->mc_list_ht);
err_rhashtable_init:
err_mc_rhashtable_init:
mlxsw_sp->nve = NULL;
kfree(nve);
return err;
@ -1010,6 +1155,8 @@ err_rhashtable_init:
void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
{
WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list));
rhashtable_destroy(&mlxsw_sp->nve->ipv6_ht);
rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
kfree(mlxsw_sp->nve);
mlxsw_sp->nve = NULL;

View File

@ -23,6 +23,8 @@ struct mlxsw_sp_nve_config {
struct mlxsw_sp_nve {
struct mlxsw_sp_nve_config config;
struct rhashtable mc_list_ht;
struct rhashtable ipv6_ht;
struct list_head ipv6_addr_list; /* Saves hash table nodes. */
struct mlxsw_sp *mlxsw_sp;
const struct mlxsw_sp_nve_ops **nve_ops_arr;
unsigned int num_nve_tunnels; /* Protected by RTNL */