Commit e846efe2 authored by Amit Cohen's avatar Amit Cohen Committed by David S. Miller
Browse files

mlxsw: spectrum: Add hash table for IPv6 address mapping



The device supports forwarding entries such as routes and FDBs that
perform tunnel (e.g., VXLAN, IP-in-IP) encapsulation or decapsulation.
When the underlay is IPv6, these entries do not encode the 128 bit IPv6
address used for encapsulation / decapsulation. Instead, these entries
encode a 24 bit pointer to an array called KVDL where the IPv6 address
is stored.

Currently, only IP-in-IP with IPv6 underlay is supported, but subsequent
patches will add support for VxLAN with IPv6 underlay. To avoid
duplicating the logic required to store and retrieve these IPv6
addresses, introduce a hash table that will store the mapping between
IPv6 addresses and their KVDL index.

Signed-off-by: default avatarAmit Cohen <amcohen@nvidia.com>
Signed-off-by: default avatarIdo Schimmel <idosch@nvidia.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent f71f1bcb
Loading
Loading
Loading
Loading
+143 −0
Original line number Diff line number Diff line
@@ -2755,6 +2755,140 @@ static void mlxsw_sp_parsing_fini(struct mlxsw_sp *mlxsw_sp)
	mutex_destroy(&mlxsw_sp->parsing.lock);
}

struct mlxsw_sp_ipv6_addr_node {
	struct in6_addr key;
	struct rhash_head ht_node;
	u32 kvdl_index;
	refcount_t refcount;
};

static const struct rhashtable_params mlxsw_sp_ipv6_addr_ht_params = {
	.key_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, key),
	.head_offset = offsetof(struct mlxsw_sp_ipv6_addr_node, ht_node),
	.key_len = sizeof(struct in6_addr),
	.automatic_shrinking = true,
};

static int
mlxsw_sp_ipv6_addr_init(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6,
			u32 *p_kvdl_index)
{
	struct mlxsw_sp_ipv6_addr_node *node;
	char rips_pl[MLXSW_REG_RIPS_LEN];
	int err;

	err = mlxsw_sp_kvdl_alloc(mlxsw_sp,
				  MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
				  p_kvdl_index);
	if (err)
		return err;

	mlxsw_reg_rips_pack(rips_pl, *p_kvdl_index, addr6);
	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(rips), rips_pl);
	if (err)
		goto err_rips_write;

	node = kzalloc(sizeof(*node), GFP_KERNEL);
	if (!node) {
		err = -ENOMEM;
		goto err_node_alloc;
	}

	node->key = *addr6;
	node->kvdl_index = *p_kvdl_index;
	refcount_set(&node->refcount, 1);

	err = rhashtable_insert_fast(&mlxsw_sp->ipv6_addr_ht,
				     &node->ht_node,
				     mlxsw_sp_ipv6_addr_ht_params);
	if (err)
		goto err_rhashtable_insert;

	return 0;

err_rhashtable_insert:
	kfree(node);
err_node_alloc:
err_rips_write:
	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
			   *p_kvdl_index);
	return err;
}

static void mlxsw_sp_ipv6_addr_fini(struct mlxsw_sp *mlxsw_sp,
				    struct mlxsw_sp_ipv6_addr_node *node)
{
	u32 kvdl_index = node->kvdl_index;

	rhashtable_remove_fast(&mlxsw_sp->ipv6_addr_ht, &node->ht_node,
			       mlxsw_sp_ipv6_addr_ht_params);
	kfree(node);
	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_IPV6_ADDRESS, 1,
			   kvdl_index);
}

int mlxsw_sp_ipv6_addr_kvdl_index_get(struct mlxsw_sp *mlxsw_sp,
				      const struct in6_addr *addr6,
				      u32 *p_kvdl_index)
{
	struct mlxsw_sp_ipv6_addr_node *node;
	int err = 0;

	mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
	node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
				      mlxsw_sp_ipv6_addr_ht_params);
	if (node) {
		refcount_inc(&node->refcount);
		*p_kvdl_index = node->kvdl_index;
		goto out_unlock;
	}

	err = mlxsw_sp_ipv6_addr_init(mlxsw_sp, addr6, p_kvdl_index);

out_unlock:
	mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
	return err;
}

void
mlxsw_sp_ipv6_addr_put(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6)
{
	struct mlxsw_sp_ipv6_addr_node *node;

	mutex_lock(&mlxsw_sp->ipv6_addr_ht_lock);
	node = rhashtable_lookup_fast(&mlxsw_sp->ipv6_addr_ht, addr6,
				      mlxsw_sp_ipv6_addr_ht_params);
	if (WARN_ON(!node))
		goto out_unlock;

	if (!refcount_dec_and_test(&node->refcount))
		goto out_unlock;

	mlxsw_sp_ipv6_addr_fini(mlxsw_sp, node);

out_unlock:
	mutex_unlock(&mlxsw_sp->ipv6_addr_ht_lock);
}

static int mlxsw_sp_ipv6_addr_ht_init(struct mlxsw_sp *mlxsw_sp)
{
	int err;

	err = rhashtable_init(&mlxsw_sp->ipv6_addr_ht,
			      &mlxsw_sp_ipv6_addr_ht_params);
	if (err)
		return err;

	mutex_init(&mlxsw_sp->ipv6_addr_ht_lock);
	return 0;
}

static void mlxsw_sp_ipv6_addr_ht_fini(struct mlxsw_sp *mlxsw_sp)
{
	mutex_destroy(&mlxsw_sp->ipv6_addr_ht_lock);
	rhashtable_destroy(&mlxsw_sp->ipv6_addr_ht);
}

static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
			 const struct mlxsw_bus_info *mlxsw_bus_info,
			 struct netlink_ext_ack *extack)
@@ -2843,6 +2977,12 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
		goto err_afa_init;
	}

	err = mlxsw_sp_ipv6_addr_ht_init(mlxsw_sp);
	if (err) {
		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize hash table for IPv6 addresses\n");
		goto err_ipv6_addr_ht_init;
	}

	err = mlxsw_sp_nve_init(mlxsw_sp);
	if (err) {
		dev_err(mlxsw_sp->bus_info->dev, "Failed to initialize NVE\n");
@@ -2944,6 +3084,8 @@ static int mlxsw_sp_init(struct mlxsw_core *mlxsw_core,
err_acl_init:
	mlxsw_sp_nve_fini(mlxsw_sp);
err_nve_init:
	mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
err_ipv6_addr_ht_init:
	mlxsw_sp_afa_fini(mlxsw_sp);
err_afa_init:
	mlxsw_sp_counter_pool_fini(mlxsw_sp);
@@ -3075,6 +3217,7 @@ static void mlxsw_sp_fini(struct mlxsw_core *mlxsw_core)
	mlxsw_sp_router_fini(mlxsw_sp);
	mlxsw_sp_acl_fini(mlxsw_sp);
	mlxsw_sp_nve_fini(mlxsw_sp);
	mlxsw_sp_ipv6_addr_ht_fini(mlxsw_sp);
	mlxsw_sp_afa_fini(mlxsw_sp);
	mlxsw_sp_counter_pool_fini(mlxsw_sp);
	mlxsw_sp_switchdev_fini(mlxsw_sp);
+7 −0
Original line number Diff line number Diff line
@@ -203,6 +203,8 @@ struct mlxsw_sp {
	const struct mlxsw_listener *listeners;
	size_t listeners_count;
	u32 lowest_shaper_bs;
	struct rhashtable ipv6_addr_ht;
	struct mutex ipv6_addr_ht_lock; /* Protects ipv6_addr_ht */
};

struct mlxsw_sp_ptp_ops {
@@ -587,6 +589,11 @@ mlxsw_sp_sample_trigger_params_set(struct mlxsw_sp *mlxsw_sp,
void
mlxsw_sp_sample_trigger_params_unset(struct mlxsw_sp *mlxsw_sp,
				     const struct mlxsw_sp_sample_trigger *trigger);
int mlxsw_sp_ipv6_addr_kvdl_index_get(struct mlxsw_sp *mlxsw_sp,
				      const struct in6_addr *addr6,
				      u32 *p_kvdl_index);
void
mlxsw_sp_ipv6_addr_put(struct mlxsw_sp *mlxsw_sp, const struct in6_addr *addr6);

extern const struct mlxsw_sp_sb_vals mlxsw_sp1_sb_vals;
extern const struct mlxsw_sp_sb_vals mlxsw_sp2_sb_vals;