162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/err.h>
562306a36Sopenharmony_ci#include <linux/gfp.h>
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/list.h>
862306a36Sopenharmony_ci#include <linux/netlink.h>
962306a36Sopenharmony_ci#include <linux/rtnetlink.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <net/inet_ecn.h>
1262306a36Sopenharmony_ci#include <net/ipv6.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include "reg.h"
1562306a36Sopenharmony_ci#include "spectrum.h"
1662306a36Sopenharmony_ci#include "spectrum_nve.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciconst struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
1962306a36Sopenharmony_ci	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp1_nve_vxlan_ops,
2062306a36Sopenharmony_ci};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ciconst struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
2362306a36Sopenharmony_ci	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp2_nve_vxlan_ops,
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_entry;
2762306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_record;
2862306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_list;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_record_ops {
3162306a36Sopenharmony_ci	enum mlxsw_reg_tnumt_record_type type;
3262306a36Sopenharmony_ci	int (*entry_add)(struct mlxsw_sp_nve_mc_record *mc_record,
3362306a36Sopenharmony_ci			 struct mlxsw_sp_nve_mc_entry *mc_entry,
3462306a36Sopenharmony_ci			 const union mlxsw_sp_l3addr *addr);
3562306a36Sopenharmony_ci	void (*entry_del)(const struct mlxsw_sp_nve_mc_record *mc_record,
3662306a36Sopenharmony_ci			  const struct mlxsw_sp_nve_mc_entry *mc_entry);
3762306a36Sopenharmony_ci	void (*entry_set)(const struct mlxsw_sp_nve_mc_record *mc_record,
3862306a36Sopenharmony_ci			  const struct mlxsw_sp_nve_mc_entry *mc_entry,
3962306a36Sopenharmony_ci			  char *tnumt_pl, unsigned int entry_index);
4062306a36Sopenharmony_ci	bool (*entry_compare)(const struct mlxsw_sp_nve_mc_record *mc_record,
4162306a36Sopenharmony_ci			      const struct mlxsw_sp_nve_mc_entry *mc_entry,
4262306a36Sopenharmony_ci			      const union mlxsw_sp_l3addr *addr);
4362306a36Sopenharmony_ci};
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_list_key {
4662306a36Sopenharmony_ci	u16 fid_index;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_ipv6_entry {
5062306a36Sopenharmony_ci	struct in6_addr addr6;
5162306a36Sopenharmony_ci	u32 addr6_kvdl_index;
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_entry {
5562306a36Sopenharmony_ci	union {
5662306a36Sopenharmony_ci		__be32 addr4;
5762306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_ipv6_entry ipv6_entry;
5862306a36Sopenharmony_ci	};
5962306a36Sopenharmony_ci	u8 valid:1;
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_record {
6362306a36Sopenharmony_ci	struct list_head list;
6462306a36Sopenharmony_ci	enum mlxsw_sp_l3proto proto;
6562306a36Sopenharmony_ci	unsigned int num_entries;
6662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp;
6762306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
6862306a36Sopenharmony_ci	const struct mlxsw_sp_nve_mc_record_ops *ops;
6962306a36Sopenharmony_ci	u32 kvdl_index;
7062306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_entry entries[];
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistruct mlxsw_sp_nve_mc_list {
7462306a36Sopenharmony_ci	struct list_head records_list;
7562306a36Sopenharmony_ci	struct rhash_head ht_node;
7662306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list_key key;
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_nve_mc_list_ht_params = {
8062306a36Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_nve_mc_list_key),
8162306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_nve_mc_list, key),
8262306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_nve_mc_list, ht_node),
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int
8662306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
8762306a36Sopenharmony_ci				      struct mlxsw_sp_nve_mc_entry *mc_entry,
8862306a36Sopenharmony_ci				      const union mlxsw_sp_l3addr *addr)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	mc_entry->addr4 = addr->addr4;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return 0;
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void
9662306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
9762306a36Sopenharmony_ci				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void
10262306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
10362306a36Sopenharmony_ci				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
10462306a36Sopenharmony_ci				      char *tnumt_pl, unsigned int entry_index)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	u32 udip = be32_to_cpu(mc_entry->addr4);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	mlxsw_reg_tnumt_udip_set(tnumt_pl, entry_index, udip);
10962306a36Sopenharmony_ci}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic bool
11262306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
11362306a36Sopenharmony_ci					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
11462306a36Sopenharmony_ci					  const union mlxsw_sp_l3addr *addr)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	return mc_entry->addr4 == addr->addr4;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic const struct mlxsw_sp_nve_mc_record_ops
12062306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv4_ops = {
12162306a36Sopenharmony_ci	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV4,
12262306a36Sopenharmony_ci	.entry_add	= &mlxsw_sp_nve_mc_record_ipv4_entry_add,
12362306a36Sopenharmony_ci	.entry_del	= &mlxsw_sp_nve_mc_record_ipv4_entry_del,
12462306a36Sopenharmony_ci	.entry_set	= &mlxsw_sp_nve_mc_record_ipv4_entry_set,
12562306a36Sopenharmony_ci	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv4_entry_compare,
12662306a36Sopenharmony_ci};
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_cistatic int
12962306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
13062306a36Sopenharmony_ci				      struct mlxsw_sp_nve_mc_entry *mc_entry,
13162306a36Sopenharmony_ci				      const union mlxsw_sp_l3addr *addr)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	u32 kvdl_index;
13462306a36Sopenharmony_ci	int err;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	err = mlxsw_sp_ipv6_addr_kvdl_index_get(mc_record->mlxsw_sp,
13762306a36Sopenharmony_ci						&addr->addr6, &kvdl_index);
13862306a36Sopenharmony_ci	if (err)
13962306a36Sopenharmony_ci		return err;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	mc_entry->ipv6_entry.addr6 = addr->addr6;
14262306a36Sopenharmony_ci	mc_entry->ipv6_entry.addr6_kvdl_index = kvdl_index;
14362306a36Sopenharmony_ci	return 0;
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic void
14762306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
14862306a36Sopenharmony_ci				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
14962306a36Sopenharmony_ci{
15062306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_put(mc_record->mlxsw_sp,
15162306a36Sopenharmony_ci			       &mc_entry->ipv6_entry.addr6);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void
15562306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
15662306a36Sopenharmony_ci				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
15762306a36Sopenharmony_ci				      char *tnumt_pl, unsigned int entry_index)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	mlxsw_reg_tnumt_udip_ptr_set(tnumt_pl, entry_index, udip_ptr);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic bool
16562306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
16662306a36Sopenharmony_ci					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
16762306a36Sopenharmony_ci					  const union mlxsw_sp_l3addr *addr)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	return ipv6_addr_equal(&mc_entry->ipv6_entry.addr6, &addr->addr6);
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic const struct mlxsw_sp_nve_mc_record_ops
17362306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ipv6_ops = {
17462306a36Sopenharmony_ci	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV6,
17562306a36Sopenharmony_ci	.entry_add	= &mlxsw_sp_nve_mc_record_ipv6_entry_add,
17662306a36Sopenharmony_ci	.entry_del	= &mlxsw_sp_nve_mc_record_ipv6_entry_del,
17762306a36Sopenharmony_ci	.entry_set	= &mlxsw_sp_nve_mc_record_ipv6_entry_set,
17862306a36Sopenharmony_ci	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv6_entry_compare,
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic const struct mlxsw_sp_nve_mc_record_ops *
18262306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ops_arr[] = {
18362306a36Sopenharmony_ci	[MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops,
18462306a36Sopenharmony_ci	[MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ciint mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
18862306a36Sopenharmony_ci				    enum mlxsw_sp_l3proto proto,
18962306a36Sopenharmony_ci				    union mlxsw_sp_l3addr *addr)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	switch (proto) {
19262306a36Sopenharmony_ci	case MLXSW_SP_L3_PROTO_IPV4:
19362306a36Sopenharmony_ci		addr->addr4 = cpu_to_be32(uip);
19462306a36Sopenharmony_ci		return 0;
19562306a36Sopenharmony_ci	default:
19662306a36Sopenharmony_ci		WARN_ON(1);
19762306a36Sopenharmony_ci		return -EINVAL;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_list *
20262306a36Sopenharmony_cimlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
20362306a36Sopenharmony_ci			  const struct mlxsw_sp_nve_mc_list_key *key)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	return rhashtable_lookup_fast(&nve->mc_list_ht, key,
20862306a36Sopenharmony_ci				      mlxsw_sp_nve_mc_list_ht_params);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_list *
21262306a36Sopenharmony_cimlxsw_sp_nve_mc_list_create(struct mlxsw_sp *mlxsw_sp,
21362306a36Sopenharmony_ci			    const struct mlxsw_sp_nve_mc_list_key *key)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
21662306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
21762306a36Sopenharmony_ci	int err;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mc_list = kmalloc(sizeof(*mc_list), GFP_KERNEL);
22062306a36Sopenharmony_ci	if (!mc_list)
22162306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	INIT_LIST_HEAD(&mc_list->records_list);
22462306a36Sopenharmony_ci	mc_list->key = *key;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	err = rhashtable_insert_fast(&nve->mc_list_ht, &mc_list->ht_node,
22762306a36Sopenharmony_ci				     mlxsw_sp_nve_mc_list_ht_params);
22862306a36Sopenharmony_ci	if (err)
22962306a36Sopenharmony_ci		goto err_rhashtable_insert;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	return mc_list;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cierr_rhashtable_insert:
23462306a36Sopenharmony_ci	kfree(mc_list);
23562306a36Sopenharmony_ci	return ERR_PTR(err);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp,
23962306a36Sopenharmony_ci					 struct mlxsw_sp_nve_mc_list *mc_list)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	rhashtable_remove_fast(&nve->mc_list_ht, &mc_list->ht_node,
24462306a36Sopenharmony_ci			       mlxsw_sp_nve_mc_list_ht_params);
24562306a36Sopenharmony_ci	WARN_ON(!list_empty(&mc_list->records_list));
24662306a36Sopenharmony_ci	kfree(mc_list);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_list *
25062306a36Sopenharmony_cimlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp,
25162306a36Sopenharmony_ci			 const struct mlxsw_sp_nve_mc_list_key *key)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key);
25662306a36Sopenharmony_ci	if (mc_list)
25762306a36Sopenharmony_ci		return mc_list;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic void
26362306a36Sopenharmony_cimlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp,
26462306a36Sopenharmony_ci			 struct mlxsw_sp_nve_mc_list *mc_list)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	if (!list_empty(&mc_list->records_list))
26762306a36Sopenharmony_ci		return;
26862306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list);
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_record *
27262306a36Sopenharmony_cimlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp,
27362306a36Sopenharmony_ci			      struct mlxsw_sp_nve_mc_list *mc_list,
27462306a36Sopenharmony_ci			      enum mlxsw_sp_l3proto proto)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto];
27762306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
27862306a36Sopenharmony_ci	int err;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries),
28162306a36Sopenharmony_ci			    GFP_KERNEL);
28262306a36Sopenharmony_ci	if (!mc_record)
28362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
28662306a36Sopenharmony_ci				  &mc_record->kvdl_index);
28762306a36Sopenharmony_ci	if (err)
28862306a36Sopenharmony_ci		goto err_kvdl_alloc;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto];
29162306a36Sopenharmony_ci	mc_record->mlxsw_sp = mlxsw_sp;
29262306a36Sopenharmony_ci	mc_record->mc_list = mc_list;
29362306a36Sopenharmony_ci	mc_record->proto = proto;
29462306a36Sopenharmony_ci	list_add_tail(&mc_record->list, &mc_list->records_list);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return mc_record;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cierr_kvdl_alloc:
29962306a36Sopenharmony_ci	kfree(mc_record);
30062306a36Sopenharmony_ci	return ERR_PTR(err);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic void
30462306a36Sopenharmony_cimlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	list_del(&mc_record->list);
30962306a36Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
31062306a36Sopenharmony_ci			   mc_record->kvdl_index);
31162306a36Sopenharmony_ci	WARN_ON(mc_record->num_entries);
31262306a36Sopenharmony_ci	kfree(mc_record);
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_record *
31662306a36Sopenharmony_cimlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp,
31762306a36Sopenharmony_ci			   struct mlxsw_sp_nve_mc_list *mc_list,
31862306a36Sopenharmony_ci			   enum mlxsw_sp_l3proto proto)
31962306a36Sopenharmony_ci{
32062306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) {
32362306a36Sopenharmony_ci		unsigned int num_entries = mc_record->num_entries;
32462306a36Sopenharmony_ci		struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		if (mc_record->proto == proto &&
32762306a36Sopenharmony_ci		    num_entries < nve->num_max_mc_entries[proto])
32862306a36Sopenharmony_ci			return mc_record;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto);
33262306a36Sopenharmony_ci}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_cistatic void
33562306a36Sopenharmony_cimlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	if (mc_record->num_entries != 0)
33862306a36Sopenharmony_ci		return;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	mlxsw_sp_nve_mc_record_destroy(mc_record);
34162306a36Sopenharmony_ci}
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_entry *
34462306a36Sopenharmony_cimlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
34762306a36Sopenharmony_ci	unsigned int num_max_entries;
34862306a36Sopenharmony_ci	int i;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
35162306a36Sopenharmony_ci	for (i = 0; i < num_max_entries; i++) {
35262306a36Sopenharmony_ci		if (mc_record->entries[i].valid)
35362306a36Sopenharmony_ci			continue;
35462306a36Sopenharmony_ci		return &mc_record->entries[i];
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	return NULL;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic int
36162306a36Sopenharmony_cimlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type;
36462306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
36562306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
36662306a36Sopenharmony_ci	char tnumt_pl[MLXSW_REG_TNUMT_LEN];
36762306a36Sopenharmony_ci	unsigned int num_max_entries;
36862306a36Sopenharmony_ci	unsigned int num_entries = 0;
36962306a36Sopenharmony_ci	u32 next_kvdl_index = 0;
37062306a36Sopenharmony_ci	bool next_valid = false;
37162306a36Sopenharmony_ci	int i;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!list_is_last(&mc_record->list, &mc_list->records_list)) {
37462306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_record *next_record;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci		next_record = list_next_entry(mc_record, list);
37762306a36Sopenharmony_ci		next_kvdl_index = next_record->kvdl_index;
37862306a36Sopenharmony_ci		next_valid = true;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	mlxsw_reg_tnumt_pack(tnumt_pl, type, MLXSW_REG_TUNNEL_PORT_NVE,
38262306a36Sopenharmony_ci			     mc_record->kvdl_index, next_valid,
38362306a36Sopenharmony_ci			     next_kvdl_index, mc_record->num_entries);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto];
38662306a36Sopenharmony_ci	for (i = 0; i < num_max_entries; i++) {
38762306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_entry *mc_entry;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci		mc_entry = &mc_record->entries[i];
39062306a36Sopenharmony_ci		if (!mc_entry->valid)
39162306a36Sopenharmony_ci			continue;
39262306a36Sopenharmony_ci		mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl,
39362306a36Sopenharmony_ci					  num_entries++);
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	WARN_ON(num_entries != mc_record->num_entries);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnumt), tnumt_pl);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic bool
40262306a36Sopenharmony_cimlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
40562306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *first_record;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	first_record = list_first_entry(&mc_list->records_list,
40862306a36Sopenharmony_ci					struct mlxsw_sp_nve_mc_record, list);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return mc_record == first_record;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_entry *
41462306a36Sopenharmony_cimlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record,
41562306a36Sopenharmony_ci			   union mlxsw_sp_l3addr *addr)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
41862306a36Sopenharmony_ci	unsigned int num_max_entries;
41962306a36Sopenharmony_ci	int i;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
42262306a36Sopenharmony_ci	for (i = 0; i < num_max_entries; i++) {
42362306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_entry *mc_entry;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		mc_entry = &mc_record->entries[i];
42662306a36Sopenharmony_ci		if (!mc_entry->valid)
42762306a36Sopenharmony_ci			continue;
42862306a36Sopenharmony_ci		if (mc_record->ops->entry_compare(mc_record, mc_entry, addr))
42962306a36Sopenharmony_ci			return mc_entry;
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	return NULL;
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_cistatic int
43662306a36Sopenharmony_cimlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record,
43762306a36Sopenharmony_ci			      union mlxsw_sp_l3addr *addr)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_entry *mc_entry = NULL;
44062306a36Sopenharmony_ci	int err;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record);
44362306a36Sopenharmony_ci	if (WARN_ON(!mc_entry))
44462306a36Sopenharmony_ci		return -EINVAL;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	err = mc_record->ops->entry_add(mc_record, mc_entry, addr);
44762306a36Sopenharmony_ci	if (err)
44862306a36Sopenharmony_ci		return err;
44962306a36Sopenharmony_ci	mc_record->num_entries++;
45062306a36Sopenharmony_ci	mc_entry->valid = true;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	err = mlxsw_sp_nve_mc_record_refresh(mc_record);
45362306a36Sopenharmony_ci	if (err)
45462306a36Sopenharmony_ci		goto err_record_refresh;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	/* If this is a new record and not the first one, then we need to
45762306a36Sopenharmony_ci	 * update the next pointer of the previous entry
45862306a36Sopenharmony_ci	 */
45962306a36Sopenharmony_ci	if (mc_record->num_entries != 1 ||
46062306a36Sopenharmony_ci	    mlxsw_sp_nve_mc_record_is_first(mc_record))
46162306a36Sopenharmony_ci		return 0;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list));
46462306a36Sopenharmony_ci	if (err)
46562306a36Sopenharmony_ci		goto err_prev_record_refresh;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	return 0;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cierr_prev_record_refresh:
47062306a36Sopenharmony_cierr_record_refresh:
47162306a36Sopenharmony_ci	mc_entry->valid = false;
47262306a36Sopenharmony_ci	mc_record->num_entries--;
47362306a36Sopenharmony_ci	mc_record->ops->entry_del(mc_record, mc_entry);
47462306a36Sopenharmony_ci	return err;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void
47862306a36Sopenharmony_cimlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record,
47962306a36Sopenharmony_ci				 struct mlxsw_sp_nve_mc_entry *mc_entry)
48062306a36Sopenharmony_ci{
48162306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	mc_entry->valid = false;
48462306a36Sopenharmony_ci	mc_record->num_entries--;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	/* When the record continues to exist we only need to invalidate
48762306a36Sopenharmony_ci	 * the requested entry
48862306a36Sopenharmony_ci	 */
48962306a36Sopenharmony_ci	if (mc_record->num_entries != 0) {
49062306a36Sopenharmony_ci		mlxsw_sp_nve_mc_record_refresh(mc_record);
49162306a36Sopenharmony_ci		mc_record->ops->entry_del(mc_record, mc_entry);
49262306a36Sopenharmony_ci		return;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/* If the record needs to be deleted, but it is not the first,
49662306a36Sopenharmony_ci	 * then we need to make sure that the previous record no longer
49762306a36Sopenharmony_ci	 * points to it. Remove deleted record from the list to reflect
49862306a36Sopenharmony_ci	 * that and then re-add it at the end, so that it could be
49962306a36Sopenharmony_ci	 * properly removed by the record destruction code
50062306a36Sopenharmony_ci	 */
50162306a36Sopenharmony_ci	if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) {
50262306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_record *prev_record;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci		prev_record = list_prev_entry(mc_record, list);
50562306a36Sopenharmony_ci		list_del(&mc_record->list);
50662306a36Sopenharmony_ci		mlxsw_sp_nve_mc_record_refresh(prev_record);
50762306a36Sopenharmony_ci		list_add_tail(&mc_record->list, &mc_list->records_list);
50862306a36Sopenharmony_ci		mc_record->ops->entry_del(mc_record, mc_entry);
50962306a36Sopenharmony_ci		return;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	/* If the first record needs to be deleted, but the list is not
51362306a36Sopenharmony_ci	 * singular, then the second record needs to be written in the
51462306a36Sopenharmony_ci	 * first record's address, as this address is stored as a property
51562306a36Sopenharmony_ci	 * of the FID
51662306a36Sopenharmony_ci	 */
51762306a36Sopenharmony_ci	if (mlxsw_sp_nve_mc_record_is_first(mc_record) &&
51862306a36Sopenharmony_ci	    !list_is_singular(&mc_list->records_list)) {
51962306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_record *next_record;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		next_record = list_next_entry(mc_record, list);
52262306a36Sopenharmony_ci		swap(mc_record->kvdl_index, next_record->kvdl_index);
52362306a36Sopenharmony_ci		mlxsw_sp_nve_mc_record_refresh(next_record);
52462306a36Sopenharmony_ci		mc_record->ops->entry_del(mc_record, mc_entry);
52562306a36Sopenharmony_ci		return;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* This is the last case where the last remaining record needs to
52962306a36Sopenharmony_ci	 * be deleted. Simply delete the entry
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	mc_record->ops->entry_del(mc_record, mc_entry);
53262306a36Sopenharmony_ci}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_cistatic struct mlxsw_sp_nve_mc_record *
53562306a36Sopenharmony_cimlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list,
53662306a36Sopenharmony_ci			    enum mlxsw_sp_l3proto proto,
53762306a36Sopenharmony_ci			    union mlxsw_sp_l3addr *addr,
53862306a36Sopenharmony_ci			    struct mlxsw_sp_nve_mc_entry **mc_entry)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	list_for_each_entry(mc_record, &mc_list->records_list, list) {
54362306a36Sopenharmony_ci		if (mc_record->proto != proto)
54462306a36Sopenharmony_ci			continue;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci		*mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr);
54762306a36Sopenharmony_ci		if (*mc_entry)
54862306a36Sopenharmony_ci			return mc_record;
54962306a36Sopenharmony_ci	}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	return NULL;
55262306a36Sopenharmony_ci}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cistatic int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp,
55562306a36Sopenharmony_ci				       struct mlxsw_sp_nve_mc_list *mc_list,
55662306a36Sopenharmony_ci				       enum mlxsw_sp_l3proto proto,
55762306a36Sopenharmony_ci				       union mlxsw_sp_l3addr *addr)
55862306a36Sopenharmony_ci{
55962306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
56062306a36Sopenharmony_ci	int err;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto);
56362306a36Sopenharmony_ci	if (IS_ERR(mc_record))
56462306a36Sopenharmony_ci		return PTR_ERR(mc_record);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr);
56762306a36Sopenharmony_ci	if (err)
56862306a36Sopenharmony_ci		goto err_ip_add;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	return 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_cierr_ip_add:
57362306a36Sopenharmony_ci	mlxsw_sp_nve_mc_record_put(mc_record);
57462306a36Sopenharmony_ci	return err;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
57862306a36Sopenharmony_ci					struct mlxsw_sp_nve_mc_list *mc_list,
57962306a36Sopenharmony_ci					enum mlxsw_sp_l3proto proto,
58062306a36Sopenharmony_ci					union mlxsw_sp_l3addr *addr)
58162306a36Sopenharmony_ci{
58262306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
58362306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_entry *mc_entry;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
58662306a36Sopenharmony_ci						&mc_entry);
58762306a36Sopenharmony_ci	if (!mc_record)
58862306a36Sopenharmony_ci		return;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
59162306a36Sopenharmony_ci	mlxsw_sp_nve_mc_record_put(mc_record);
59262306a36Sopenharmony_ci}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_cistatic int
59562306a36Sopenharmony_cimlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid,
59662306a36Sopenharmony_ci				 struct mlxsw_sp_nve_mc_list *mc_list)
59762306a36Sopenharmony_ci{
59862306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* The address of the first record in the list is a property of
60162306a36Sopenharmony_ci	 * the FID and we never change it. It only needs to be set when
60262306a36Sopenharmony_ci	 * a new list is created
60362306a36Sopenharmony_ci	 */
60462306a36Sopenharmony_ci	if (mlxsw_sp_fid_nve_flood_index_is_set(fid))
60562306a36Sopenharmony_ci		return 0;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	mc_record = list_first_entry(&mc_list->records_list,
60862306a36Sopenharmony_ci				     struct mlxsw_sp_nve_mc_record, list);
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	return mlxsw_sp_fid_nve_flood_index_set(fid, mc_record->kvdl_index);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic void
61462306a36Sopenharmony_cimlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid,
61562306a36Sopenharmony_ci				   struct mlxsw_sp_nve_mc_list *mc_list)
61662306a36Sopenharmony_ci{
61762306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record;
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	/* The address of the first record needs to be invalidated only when
62062306a36Sopenharmony_ci	 * the last record is about to be removed
62162306a36Sopenharmony_ci	 */
62262306a36Sopenharmony_ci	if (!list_is_singular(&mc_list->records_list))
62362306a36Sopenharmony_ci		return;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	mc_record = list_first_entry(&mc_list->records_list,
62662306a36Sopenharmony_ci				     struct mlxsw_sp_nve_mc_record, list);
62762306a36Sopenharmony_ci	if (mc_record->num_entries != 1)
62862306a36Sopenharmony_ci		return;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	return mlxsw_sp_fid_nve_flood_index_clear(fid);
63162306a36Sopenharmony_ci}
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ciint mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
63462306a36Sopenharmony_ci			      struct mlxsw_sp_fid *fid,
63562306a36Sopenharmony_ci			      enum mlxsw_sp_l3proto proto,
63662306a36Sopenharmony_ci			      union mlxsw_sp_l3addr *addr)
63762306a36Sopenharmony_ci{
63862306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list_key key = { 0 };
63962306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
64062306a36Sopenharmony_ci	int err;
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	key.fid_index = mlxsw_sp_fid_index(fid);
64362306a36Sopenharmony_ci	mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, &key);
64462306a36Sopenharmony_ci	if (IS_ERR(mc_list))
64562306a36Sopenharmony_ci		return PTR_ERR(mc_list);
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr);
64862306a36Sopenharmony_ci	if (err)
64962306a36Sopenharmony_ci		goto err_add_ip;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list);
65262306a36Sopenharmony_ci	if (err)
65362306a36Sopenharmony_ci		goto err_fid_flood_index_set;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return 0;
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_cierr_fid_flood_index_set:
65862306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
65962306a36Sopenharmony_cierr_add_ip:
66062306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
66162306a36Sopenharmony_ci	return err;
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_civoid mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
66562306a36Sopenharmony_ci			       struct mlxsw_sp_fid *fid,
66662306a36Sopenharmony_ci			       enum mlxsw_sp_l3proto proto,
66762306a36Sopenharmony_ci			       union mlxsw_sp_l3addr *addr)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list_key key = { 0 };
67062306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	key.fid_index = mlxsw_sp_fid_index(fid);
67362306a36Sopenharmony_ci	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
67462306a36Sopenharmony_ci	if (!mc_list)
67562306a36Sopenharmony_ci		return;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
67862306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
67962306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
68062306a36Sopenharmony_ci}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic void
68362306a36Sopenharmony_cimlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
68662306a36Sopenharmony_ci	unsigned int num_max_entries;
68762306a36Sopenharmony_ci	int i;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
69062306a36Sopenharmony_ci	for (i = 0; i < num_max_entries; i++) {
69162306a36Sopenharmony_ci		struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i];
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		if (!mc_entry->valid)
69462306a36Sopenharmony_ci			continue;
69562306a36Sopenharmony_ci		mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	WARN_ON(mc_record->num_entries);
69962306a36Sopenharmony_ci	mlxsw_sp_nve_mc_record_put(mc_record);
70062306a36Sopenharmony_ci}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_cistatic void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp,
70362306a36Sopenharmony_ci					struct mlxsw_sp_fid *fid)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_record *mc_record, *tmp;
70662306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list_key key = { 0 };
70762306a36Sopenharmony_ci	struct mlxsw_sp_nve_mc_list *mc_list;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (!mlxsw_sp_fid_nve_flood_index_is_set(fid))
71062306a36Sopenharmony_ci		return;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	mlxsw_sp_fid_nve_flood_index_clear(fid);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	key.fid_index = mlxsw_sp_fid_index(fid);
71562306a36Sopenharmony_ci	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
71662306a36Sopenharmony_ci	if (WARN_ON(!mc_list))
71762306a36Sopenharmony_ci		return;
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list)
72062306a36Sopenharmony_ci		mlxsw_sp_nve_mc_record_delete(mc_record);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	WARN_ON(!list_empty(&mc_list->records_list));
72362306a36Sopenharmony_ci	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
72762306a36Sopenharmony_ci				    struct mlxsw_sp_nve_config *config)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
73062306a36Sopenharmony_ci	const struct mlxsw_sp_nve_ops *ops;
73162306a36Sopenharmony_ci	int err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	if (nve->num_nve_tunnels++ != 0)
73462306a36Sopenharmony_ci		return 0;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	nve->config = *config;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
73962306a36Sopenharmony_ci				  &nve->tunnel_index);
74062306a36Sopenharmony_ci	if (err)
74162306a36Sopenharmony_ci		goto err_kvdl_alloc;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	ops = nve->nve_ops_arr[config->type];
74462306a36Sopenharmony_ci	err = ops->init(nve, config);
74562306a36Sopenharmony_ci	if (err)
74662306a36Sopenharmony_ci		goto err_ops_init;
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	return 0;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_cierr_ops_init:
75162306a36Sopenharmony_ci	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
75262306a36Sopenharmony_ci			   nve->tunnel_index);
75362306a36Sopenharmony_cierr_kvdl_alloc:
75462306a36Sopenharmony_ci	memset(&nve->config, 0, sizeof(nve->config));
75562306a36Sopenharmony_ci	nve->num_nve_tunnels--;
75662306a36Sopenharmony_ci	return err;
75762306a36Sopenharmony_ci}
75862306a36Sopenharmony_ci
75962306a36Sopenharmony_cistatic void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
76062306a36Sopenharmony_ci{
76162306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
76262306a36Sopenharmony_ci	const struct mlxsw_sp_nve_ops *ops;
76362306a36Sopenharmony_ci
76462306a36Sopenharmony_ci	ops = nve->nve_ops_arr[nve->config.type];
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	if (mlxsw_sp->nve->num_nve_tunnels == 1) {
76762306a36Sopenharmony_ci		ops->fini(nve);
76862306a36Sopenharmony_ci		mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
76962306a36Sopenharmony_ci				   nve->tunnel_index);
77062306a36Sopenharmony_ci		memset(&nve->config, 0, sizeof(nve->config));
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	nve->num_nve_tunnels--;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
77662306a36Sopenharmony_ci					  u16 fid_index)
77762306a36Sopenharmony_ci{
77862306a36Sopenharmony_ci	char sfdf_pl[MLXSW_REG_SFDF_LEN];
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
78162306a36Sopenharmony_ci	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
78262306a36Sopenharmony_ci	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
78662306a36Sopenharmony_ci					   const struct mlxsw_sp_fid *fid,
78762306a36Sopenharmony_ci					   const struct net_device *nve_dev,
78862306a36Sopenharmony_ci					   __be32 vni)
78962306a36Sopenharmony_ci{
79062306a36Sopenharmony_ci	const struct mlxsw_sp_nve_ops *ops;
79162306a36Sopenharmony_ci	enum mlxsw_sp_nve_type type;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	ops = mlxsw_sp->nve->nve_ops_arr[type];
79762306a36Sopenharmony_ci	ops->fdb_clear_offload(nve_dev, vni);
79862306a36Sopenharmony_ci}
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_cistruct mlxsw_sp_nve_ipv6_ht_key {
80162306a36Sopenharmony_ci	u8 mac[ETH_ALEN];
80262306a36Sopenharmony_ci	u16 fid_index;
80362306a36Sopenharmony_ci};
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_cistruct mlxsw_sp_nve_ipv6_ht_node {
80662306a36Sopenharmony_ci	struct rhash_head ht_node;
80762306a36Sopenharmony_ci	struct list_head list;
80862306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_key key;
80962306a36Sopenharmony_ci	struct in6_addr addr6;
81062306a36Sopenharmony_ci};
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = {
81362306a36Sopenharmony_ci	.key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key),
81462306a36Sopenharmony_ci	.key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key),
81562306a36Sopenharmony_ci	.head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node),
81662306a36Sopenharmony_ci};
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ciint mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
81962306a36Sopenharmony_ci				    const struct in6_addr *addr6,
82062306a36Sopenharmony_ci				    u32 *p_kvdl_index)
82162306a36Sopenharmony_ci{
82262306a36Sopenharmony_ci	return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_civoid mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
82662306a36Sopenharmony_ci				       const struct in6_addr *addr6)
82762306a36Sopenharmony_ci{
82862306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6);
82962306a36Sopenharmony_ci}
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_cistatic struct mlxsw_sp_nve_ipv6_ht_node *
83262306a36Sopenharmony_cimlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac,
83362306a36Sopenharmony_ci				 u16 fid_index)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_key key = {};
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	ether_addr_copy(key.mac, mac);
83862306a36Sopenharmony_ci	key.fid_index = fid_index;
83962306a36Sopenharmony_ci	return rhashtable_lookup_fast(&mlxsw_sp->nve->ipv6_ht, &key,
84062306a36Sopenharmony_ci				      mlxsw_sp_nve_ipv6_ht_params);
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_cistatic int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp,
84462306a36Sopenharmony_ci				       const char *mac, u16 fid_index,
84562306a36Sopenharmony_ci				       const struct in6_addr *addr6)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
84862306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
84962306a36Sopenharmony_ci	int err;
85062306a36Sopenharmony_ci
85162306a36Sopenharmony_ci	ipv6_ht_node = kzalloc(sizeof(*ipv6_ht_node), GFP_KERNEL);
85262306a36Sopenharmony_ci	if (!ipv6_ht_node)
85362306a36Sopenharmony_ci		return -ENOMEM;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	ether_addr_copy(ipv6_ht_node->key.mac, mac);
85662306a36Sopenharmony_ci	ipv6_ht_node->key.fid_index = fid_index;
85762306a36Sopenharmony_ci	ipv6_ht_node->addr6 = *addr6;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	err = rhashtable_insert_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
86062306a36Sopenharmony_ci				     mlxsw_sp_nve_ipv6_ht_params);
86162306a36Sopenharmony_ci	if (err)
86262306a36Sopenharmony_ci		goto err_rhashtable_insert;
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	list_add(&ipv6_ht_node->list, &nve->ipv6_addr_list);
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	return 0;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_cierr_rhashtable_insert:
86962306a36Sopenharmony_ci	kfree(ipv6_ht_node);
87062306a36Sopenharmony_ci	return err;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic void
87462306a36Sopenharmony_cimlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp,
87562306a36Sopenharmony_ci			    struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	list_del(&ipv6_ht_node->list);
88062306a36Sopenharmony_ci	rhashtable_remove_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
88162306a36Sopenharmony_ci			       mlxsw_sp_nve_ipv6_ht_params);
88262306a36Sopenharmony_ci	kfree(ipv6_ht_node);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ciint
88662306a36Sopenharmony_cimlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
88762306a36Sopenharmony_ci				   u16 fid_index,
88862306a36Sopenharmony_ci				   const struct in6_addr *new_addr6)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	ASSERT_RTNL();
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
89562306a36Sopenharmony_ci							fid_index);
89662306a36Sopenharmony_ci	if (!ipv6_ht_node)
89762306a36Sopenharmony_ci		return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index,
89862306a36Sopenharmony_ci						   new_addr6);
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
90162306a36Sopenharmony_ci	ipv6_ht_node->addr6 = *new_addr6;
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_civoid mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
90662306a36Sopenharmony_ci				    u16 fid_index)
90762306a36Sopenharmony_ci{
90862306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	ASSERT_RTNL();
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
91362306a36Sopenharmony_ci							fid_index);
91462306a36Sopenharmony_ci	if (WARN_ON(!ipv6_ht_node))
91562306a36Sopenharmony_ci		return;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
91862306a36Sopenharmony_ci}
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_cistatic void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
92162306a36Sopenharmony_ci						u16 fid_index)
92262306a36Sopenharmony_ci{
92362306a36Sopenharmony_ci	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp;
92462306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list,
92762306a36Sopenharmony_ci				 list) {
92862306a36Sopenharmony_ci		if (ipv6_ht_node->key.fid_index != fid_index)
92962306a36Sopenharmony_ci			continue;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci		mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
93262306a36Sopenharmony_ci		mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
93362306a36Sopenharmony_ci	}
93462306a36Sopenharmony_ci}
93562306a36Sopenharmony_ci
93662306a36Sopenharmony_ciint mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
93762306a36Sopenharmony_ci			    struct mlxsw_sp_nve_params *params,
93862306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
93962306a36Sopenharmony_ci{
94062306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
94162306a36Sopenharmony_ci	const struct mlxsw_sp_nve_ops *ops;
94262306a36Sopenharmony_ci	struct mlxsw_sp_nve_config config;
94362306a36Sopenharmony_ci	int err;
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	ops = nve->nve_ops_arr[params->type];
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_ci	if (!ops->can_offload(nve, params, extack))
94862306a36Sopenharmony_ci		return -EINVAL;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	memset(&config, 0, sizeof(config));
95162306a36Sopenharmony_ci	ops->nve_config(nve, params, &config);
95262306a36Sopenharmony_ci	if (nve->num_nve_tunnels &&
95362306a36Sopenharmony_ci	    memcmp(&config, &nve->config, sizeof(config))) {
95462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
95562306a36Sopenharmony_ci		return -EINVAL;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
95962306a36Sopenharmony_ci	if (err) {
96062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
96162306a36Sopenharmony_ci		return err;
96262306a36Sopenharmony_ci	}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
96562306a36Sopenharmony_ci				   params->dev->ifindex);
96662306a36Sopenharmony_ci	if (err) {
96762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
96862306a36Sopenharmony_ci		goto err_fid_vni_set;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	err = ops->fdb_replay(params->dev, params->vni, extack);
97262306a36Sopenharmony_ci	if (err)
97362306a36Sopenharmony_ci		goto err_fdb_replay;
97462306a36Sopenharmony_ci
97562306a36Sopenharmony_ci	return 0;
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_cierr_fdb_replay:
97862306a36Sopenharmony_ci	mlxsw_sp_fid_vni_clear(fid);
97962306a36Sopenharmony_cierr_fid_vni_set:
98062306a36Sopenharmony_ci	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
98162306a36Sopenharmony_ci	return err;
98262306a36Sopenharmony_ci}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_civoid mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
98562306a36Sopenharmony_ci			      struct mlxsw_sp_fid *fid)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	u16 fid_index = mlxsw_sp_fid_index(fid);
98862306a36Sopenharmony_ci	struct net_device *nve_dev;
98962306a36Sopenharmony_ci	int nve_ifindex;
99062306a36Sopenharmony_ci	__be32 vni;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	/* Necessary for __dev_get_by_index() below. */
99362306a36Sopenharmony_ci	ASSERT_RTNL();
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
99662306a36Sopenharmony_ci	mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
99762306a36Sopenharmony_ci	mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
100062306a36Sopenharmony_ci		    mlxsw_sp_fid_vni(fid, &vni)))
100162306a36Sopenharmony_ci		goto out;
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci	nve_dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
100462306a36Sopenharmony_ci	if (!nve_dev)
100562306a36Sopenharmony_ci		goto out;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
100862306a36Sopenharmony_ci	mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
100962306a36Sopenharmony_ci
101062306a36Sopenharmony_ciout:
101162306a36Sopenharmony_ci	mlxsw_sp_fid_vni_clear(fid);
101262306a36Sopenharmony_ci	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
101362306a36Sopenharmony_ci}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ciint mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
101862306a36Sopenharmony_ci	char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
102162306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_civoid mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
102562306a36Sopenharmony_ci{
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	mlxsw_reg_tnqcr_pack(tnqcr_pl);
103362306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistatic int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int i;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	/* Iterate over inner ECN values */
104162306a36Sopenharmony_ci	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
104262306a36Sopenharmony_ci		u8 outer_ecn = INET_ECN_encapsulate(0, i);
104362306a36Sopenharmony_ci		char tneem_pl[MLXSW_REG_TNEEM_LEN];
104462306a36Sopenharmony_ci		int err;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci		mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
104762306a36Sopenharmony_ci		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
104862306a36Sopenharmony_ci				      tneem_pl);
104962306a36Sopenharmony_ci		if (err)
105062306a36Sopenharmony_ci			return err;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	return 0;
105462306a36Sopenharmony_ci}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_cistatic int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
105762306a36Sopenharmony_ci					 u8 inner_ecn, u8 outer_ecn)
105862306a36Sopenharmony_ci{
105962306a36Sopenharmony_ci	char tndem_pl[MLXSW_REG_TNDEM_LEN];
106062306a36Sopenharmony_ci	u8 new_inner_ecn;
106162306a36Sopenharmony_ci	bool trap_en;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
106462306a36Sopenharmony_ci						  &trap_en);
106562306a36Sopenharmony_ci	mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
106662306a36Sopenharmony_ci			     trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
106762306a36Sopenharmony_ci	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
106862306a36Sopenharmony_ci}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_cistatic int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
107162306a36Sopenharmony_ci{
107262306a36Sopenharmony_ci	int i;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* Iterate over inner ECN values */
107562306a36Sopenharmony_ci	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
107662306a36Sopenharmony_ci		int j;
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci		/* Iterate over outer ECN values */
107962306a36Sopenharmony_ci		for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
108062306a36Sopenharmony_ci			int err;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci			err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
108362306a36Sopenharmony_ci			if (err)
108462306a36Sopenharmony_ci				return err;
108562306a36Sopenharmony_ci		}
108662306a36Sopenharmony_ci	}
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	return 0;
108962306a36Sopenharmony_ci}
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_cistatic int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
109262306a36Sopenharmony_ci{
109362306a36Sopenharmony_ci	int err;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
109662306a36Sopenharmony_ci	if (err)
109762306a36Sopenharmony_ci		return err;
109862306a36Sopenharmony_ci
109962306a36Sopenharmony_ci	return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
110062306a36Sopenharmony_ci}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
110362306a36Sopenharmony_ci{
110462306a36Sopenharmony_ci	unsigned int max;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
110762306a36Sopenharmony_ci	    !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
110862306a36Sopenharmony_ci		return -EIO;
110962306a36Sopenharmony_ci	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
111062306a36Sopenharmony_ci	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
111162306a36Sopenharmony_ci	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
111262306a36Sopenharmony_ci	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci	return 0;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ciint mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct mlxsw_sp_nve *nve;
112062306a36Sopenharmony_ci	int err;
112162306a36Sopenharmony_ci
112262306a36Sopenharmony_ci	nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
112362306a36Sopenharmony_ci	if (!nve)
112462306a36Sopenharmony_ci		return -ENOMEM;
112562306a36Sopenharmony_ci	mlxsw_sp->nve = nve;
112662306a36Sopenharmony_ci	nve->mlxsw_sp = mlxsw_sp;
112762306a36Sopenharmony_ci	nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	err = rhashtable_init(&nve->mc_list_ht,
113062306a36Sopenharmony_ci			      &mlxsw_sp_nve_mc_list_ht_params);
113162306a36Sopenharmony_ci	if (err)
113262306a36Sopenharmony_ci		goto err_mc_rhashtable_init;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci	err = rhashtable_init(&nve->ipv6_ht, &mlxsw_sp_nve_ipv6_ht_params);
113562306a36Sopenharmony_ci	if (err)
113662306a36Sopenharmony_ci		goto err_ipv6_rhashtable_init;
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci	INIT_LIST_HEAD(&nve->ipv6_addr_list);
113962306a36Sopenharmony_ci
114062306a36Sopenharmony_ci	err = mlxsw_sp_nve_qos_init(mlxsw_sp);
114162306a36Sopenharmony_ci	if (err)
114262306a36Sopenharmony_ci		goto err_nve_qos_init;
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
114562306a36Sopenharmony_ci	if (err)
114662306a36Sopenharmony_ci		goto err_nve_ecn_init;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	err = mlxsw_sp_nve_resources_query(mlxsw_sp);
114962306a36Sopenharmony_ci	if (err)
115062306a36Sopenharmony_ci		goto err_nve_resources_query;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_cierr_nve_resources_query:
115562306a36Sopenharmony_cierr_nve_ecn_init:
115662306a36Sopenharmony_cierr_nve_qos_init:
115762306a36Sopenharmony_ci	rhashtable_destroy(&nve->ipv6_ht);
115862306a36Sopenharmony_cierr_ipv6_rhashtable_init:
115962306a36Sopenharmony_ci	rhashtable_destroy(&nve->mc_list_ht);
116062306a36Sopenharmony_cierr_mc_rhashtable_init:
116162306a36Sopenharmony_ci	mlxsw_sp->nve = NULL;
116262306a36Sopenharmony_ci	kfree(nve);
116362306a36Sopenharmony_ci	return err;
116462306a36Sopenharmony_ci}
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_civoid mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
116762306a36Sopenharmony_ci{
116862306a36Sopenharmony_ci	WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
116962306a36Sopenharmony_ci	WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list));
117062306a36Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->nve->ipv6_ht);
117162306a36Sopenharmony_ci	rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
117262306a36Sopenharmony_ci	kfree(mlxsw_sp->nve);
117362306a36Sopenharmony_ci	mlxsw_sp->nve = NULL;
117462306a36Sopenharmony_ci}
1175