xref: /openbmc/linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_nve.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
16e6030bdSIdo Schimmel // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
26e6030bdSIdo Schimmel /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
36e6030bdSIdo Schimmel 
46e6030bdSIdo Schimmel #include <linux/err.h>
56e6030bdSIdo Schimmel #include <linux/gfp.h>
66e6030bdSIdo Schimmel #include <linux/kernel.h>
76e6030bdSIdo Schimmel #include <linux/list.h>
86e6030bdSIdo Schimmel #include <linux/netlink.h>
96e6030bdSIdo Schimmel #include <linux/rtnetlink.h>
106e6030bdSIdo Schimmel #include <linux/slab.h>
116e6030bdSIdo Schimmel #include <net/inet_ecn.h>
126e6030bdSIdo Schimmel #include <net/ipv6.h>
136e6030bdSIdo Schimmel 
146e6030bdSIdo Schimmel #include "reg.h"
156e6030bdSIdo Schimmel #include "spectrum.h"
166e6030bdSIdo Schimmel #include "spectrum_nve.h"
176e6030bdSIdo Schimmel 
186e6030bdSIdo Schimmel const struct mlxsw_sp_nve_ops *mlxsw_sp1_nve_ops_arr[] = {
196e6030bdSIdo Schimmel 	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp1_nve_vxlan_ops,
206e6030bdSIdo Schimmel };
216e6030bdSIdo Schimmel 
226e6030bdSIdo Schimmel const struct mlxsw_sp_nve_ops *mlxsw_sp2_nve_ops_arr[] = {
236e6030bdSIdo Schimmel 	[MLXSW_SP_NVE_TYPE_VXLAN]	= &mlxsw_sp2_nve_vxlan_ops,
246e6030bdSIdo Schimmel };
256e6030bdSIdo Schimmel 
266e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_entry;
276e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_record;
286e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_list;
296e6030bdSIdo Schimmel 
306e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_record_ops {
316e6030bdSIdo Schimmel 	enum mlxsw_reg_tnumt_record_type type;
326e6030bdSIdo Schimmel 	int (*entry_add)(struct mlxsw_sp_nve_mc_record *mc_record,
336e6030bdSIdo Schimmel 			 struct mlxsw_sp_nve_mc_entry *mc_entry,
346e6030bdSIdo Schimmel 			 const union mlxsw_sp_l3addr *addr);
356e6030bdSIdo Schimmel 	void (*entry_del)(const struct mlxsw_sp_nve_mc_record *mc_record,
366e6030bdSIdo Schimmel 			  const struct mlxsw_sp_nve_mc_entry *mc_entry);
376e6030bdSIdo Schimmel 	void (*entry_set)(const struct mlxsw_sp_nve_mc_record *mc_record,
386e6030bdSIdo Schimmel 			  const struct mlxsw_sp_nve_mc_entry *mc_entry,
396e6030bdSIdo Schimmel 			  char *tnumt_pl, unsigned int entry_index);
406e6030bdSIdo Schimmel 	bool (*entry_compare)(const struct mlxsw_sp_nve_mc_record *mc_record,
416e6030bdSIdo Schimmel 			      const struct mlxsw_sp_nve_mc_entry *mc_entry,
426e6030bdSIdo Schimmel 			      const union mlxsw_sp_l3addr *addr);
436e6030bdSIdo Schimmel };
446e6030bdSIdo Schimmel 
456e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_list_key {
466e6030bdSIdo Schimmel 	u16 fid_index;
476e6030bdSIdo Schimmel };
486e6030bdSIdo Schimmel 
496e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_ipv6_entry {
506e6030bdSIdo Schimmel 	struct in6_addr addr6;
516e6030bdSIdo Schimmel 	u32 addr6_kvdl_index;
526e6030bdSIdo Schimmel };
536e6030bdSIdo Schimmel 
546e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_entry {
556e6030bdSIdo Schimmel 	union {
566e6030bdSIdo Schimmel 		__be32 addr4;
576e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_ipv6_entry ipv6_entry;
586e6030bdSIdo Schimmel 	};
596e6030bdSIdo Schimmel 	u8 valid:1;
606e6030bdSIdo Schimmel };
616e6030bdSIdo Schimmel 
626e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_record {
636e6030bdSIdo Schimmel 	struct list_head list;
646e6030bdSIdo Schimmel 	enum mlxsw_sp_l3proto proto;
656e6030bdSIdo Schimmel 	unsigned int num_entries;
666e6030bdSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp;
676e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
686e6030bdSIdo Schimmel 	const struct mlxsw_sp_nve_mc_record_ops *ops;
696e6030bdSIdo Schimmel 	u32 kvdl_index;
70e99f8e7fSGustavo A. R. Silva 	struct mlxsw_sp_nve_mc_entry entries[];
716e6030bdSIdo Schimmel };
726e6030bdSIdo Schimmel 
736e6030bdSIdo Schimmel struct mlxsw_sp_nve_mc_list {
746e6030bdSIdo Schimmel 	struct list_head records_list;
756e6030bdSIdo Schimmel 	struct rhash_head ht_node;
766e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list_key key;
776e6030bdSIdo Schimmel };
786e6030bdSIdo Schimmel 
796e6030bdSIdo Schimmel static const struct rhashtable_params mlxsw_sp_nve_mc_list_ht_params = {
806e6030bdSIdo Schimmel 	.key_len = sizeof(struct mlxsw_sp_nve_mc_list_key),
816e6030bdSIdo Schimmel 	.key_offset = offsetof(struct mlxsw_sp_nve_mc_list, key),
826e6030bdSIdo Schimmel 	.head_offset = offsetof(struct mlxsw_sp_nve_mc_list, ht_node),
836e6030bdSIdo Schimmel };
846e6030bdSIdo Schimmel 
856e6030bdSIdo Schimmel static int
mlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record * mc_record,struct mlxsw_sp_nve_mc_entry * mc_entry,const union mlxsw_sp_l3addr * addr)866e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv4_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
876e6030bdSIdo Schimmel 				      struct mlxsw_sp_nve_mc_entry *mc_entry,
886e6030bdSIdo Schimmel 				      const union mlxsw_sp_l3addr *addr)
896e6030bdSIdo Schimmel {
906e6030bdSIdo Schimmel 	mc_entry->addr4 = addr->addr4;
916e6030bdSIdo Schimmel 
926e6030bdSIdo Schimmel 	return 0;
936e6030bdSIdo Schimmel }
946e6030bdSIdo Schimmel 
956e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry)966e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv4_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
976e6030bdSIdo Schimmel 				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
986e6030bdSIdo Schimmel {
996e6030bdSIdo Schimmel }
1006e6030bdSIdo Schimmel 
1016e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry,char * tnumt_pl,unsigned int entry_index)1026e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv4_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
1036e6030bdSIdo Schimmel 				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
1046e6030bdSIdo Schimmel 				      char *tnumt_pl, unsigned int entry_index)
1056e6030bdSIdo Schimmel {
1066e6030bdSIdo Schimmel 	u32 udip = be32_to_cpu(mc_entry->addr4);
1076e6030bdSIdo Schimmel 
1086e6030bdSIdo Schimmel 	mlxsw_reg_tnumt_udip_set(tnumt_pl, entry_index, udip);
1096e6030bdSIdo Schimmel }
1106e6030bdSIdo Schimmel 
1116e6030bdSIdo Schimmel static bool
mlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry,const union mlxsw_sp_l3addr * addr)1126e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv4_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
1136e6030bdSIdo Schimmel 					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
1146e6030bdSIdo Schimmel 					  const union mlxsw_sp_l3addr *addr)
1156e6030bdSIdo Schimmel {
1166e6030bdSIdo Schimmel 	return mc_entry->addr4 == addr->addr4;
1176e6030bdSIdo Schimmel }
1186e6030bdSIdo Schimmel 
1196e6030bdSIdo Schimmel static const struct mlxsw_sp_nve_mc_record_ops
1206e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv4_ops = {
1216e6030bdSIdo Schimmel 	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV4,
1226e6030bdSIdo Schimmel 	.entry_add	= &mlxsw_sp_nve_mc_record_ipv4_entry_add,
1236e6030bdSIdo Schimmel 	.entry_del	= &mlxsw_sp_nve_mc_record_ipv4_entry_del,
1246e6030bdSIdo Schimmel 	.entry_set	= &mlxsw_sp_nve_mc_record_ipv4_entry_set,
1256e6030bdSIdo Schimmel 	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv4_entry_compare,
1266e6030bdSIdo Schimmel };
1276e6030bdSIdo Schimmel 
1286e6030bdSIdo Schimmel static int
mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record * mc_record,struct mlxsw_sp_nve_mc_entry * mc_entry,const union mlxsw_sp_l3addr * addr)1296e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv6_entry_add(struct mlxsw_sp_nve_mc_record *mc_record,
1306e6030bdSIdo Schimmel 				      struct mlxsw_sp_nve_mc_entry *mc_entry,
1316e6030bdSIdo Schimmel 				      const union mlxsw_sp_l3addr *addr)
1326e6030bdSIdo Schimmel {
13306c08f86SAmit Cohen 	u32 kvdl_index;
13406c08f86SAmit Cohen 	int err;
1356e6030bdSIdo Schimmel 
13606c08f86SAmit Cohen 	err = mlxsw_sp_ipv6_addr_kvdl_index_get(mc_record->mlxsw_sp,
13706c08f86SAmit Cohen 						&addr->addr6, &kvdl_index);
13806c08f86SAmit Cohen 	if (err)
13906c08f86SAmit Cohen 		return err;
14006c08f86SAmit Cohen 
14106c08f86SAmit Cohen 	mc_entry->ipv6_entry.addr6 = addr->addr6;
14206c08f86SAmit Cohen 	mc_entry->ipv6_entry.addr6_kvdl_index = kvdl_index;
14306c08f86SAmit Cohen 	return 0;
1446e6030bdSIdo Schimmel }
1456e6030bdSIdo Schimmel 
1466e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry)1476e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv6_entry_del(const struct mlxsw_sp_nve_mc_record *mc_record,
1486e6030bdSIdo Schimmel 				      const struct mlxsw_sp_nve_mc_entry *mc_entry)
1496e6030bdSIdo Schimmel {
15006c08f86SAmit Cohen 	mlxsw_sp_ipv6_addr_put(mc_record->mlxsw_sp,
15106c08f86SAmit Cohen 			       &mc_entry->ipv6_entry.addr6);
1526e6030bdSIdo Schimmel }
1536e6030bdSIdo Schimmel 
1546e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry,char * tnumt_pl,unsigned int entry_index)1556e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv6_entry_set(const struct mlxsw_sp_nve_mc_record *mc_record,
1566e6030bdSIdo Schimmel 				      const struct mlxsw_sp_nve_mc_entry *mc_entry,
1576e6030bdSIdo Schimmel 				      char *tnumt_pl, unsigned int entry_index)
1586e6030bdSIdo Schimmel {
1596e6030bdSIdo Schimmel 	u32 udip_ptr = mc_entry->ipv6_entry.addr6_kvdl_index;
1606e6030bdSIdo Schimmel 
1616e6030bdSIdo Schimmel 	mlxsw_reg_tnumt_udip_ptr_set(tnumt_pl, entry_index, udip_ptr);
1626e6030bdSIdo Schimmel }
1636e6030bdSIdo Schimmel 
1646e6030bdSIdo Schimmel static bool
mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record * mc_record,const struct mlxsw_sp_nve_mc_entry * mc_entry,const union mlxsw_sp_l3addr * addr)1656e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv6_entry_compare(const struct mlxsw_sp_nve_mc_record *mc_record,
1666e6030bdSIdo Schimmel 					  const struct mlxsw_sp_nve_mc_entry *mc_entry,
1676e6030bdSIdo Schimmel 					  const union mlxsw_sp_l3addr *addr)
1686e6030bdSIdo Schimmel {
1696e6030bdSIdo Schimmel 	return ipv6_addr_equal(&mc_entry->ipv6_entry.addr6, &addr->addr6);
1706e6030bdSIdo Schimmel }
1716e6030bdSIdo Schimmel 
1726e6030bdSIdo Schimmel static const struct mlxsw_sp_nve_mc_record_ops
1736e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ipv6_ops = {
1746e6030bdSIdo Schimmel 	.type		= MLXSW_REG_TNUMT_RECORD_TYPE_IPV6,
1756e6030bdSIdo Schimmel 	.entry_add	= &mlxsw_sp_nve_mc_record_ipv6_entry_add,
1766e6030bdSIdo Schimmel 	.entry_del	= &mlxsw_sp_nve_mc_record_ipv6_entry_del,
1776e6030bdSIdo Schimmel 	.entry_set	= &mlxsw_sp_nve_mc_record_ipv6_entry_set,
1786e6030bdSIdo Schimmel 	.entry_compare	= &mlxsw_sp_nve_mc_record_ipv6_entry_compare,
1796e6030bdSIdo Schimmel };
1806e6030bdSIdo Schimmel 
1816e6030bdSIdo Schimmel static const struct mlxsw_sp_nve_mc_record_ops *
1826e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ops_arr[] = {
1836e6030bdSIdo Schimmel 	[MLXSW_SP_L3_PROTO_IPV4] = &mlxsw_sp_nve_mc_record_ipv4_ops,
1846e6030bdSIdo Schimmel 	[MLXSW_SP_L3_PROTO_IPV6] = &mlxsw_sp_nve_mc_record_ipv6_ops,
1856e6030bdSIdo Schimmel };
1866e6030bdSIdo Schimmel 
mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp * mlxsw_sp,u32 uip,enum mlxsw_sp_l3proto proto,union mlxsw_sp_l3addr * addr)1873c55bdacSIdo Schimmel int mlxsw_sp_nve_learned_ip_resolve(struct mlxsw_sp *mlxsw_sp, u32 uip,
1883c55bdacSIdo Schimmel 				    enum mlxsw_sp_l3proto proto,
1893c55bdacSIdo Schimmel 				    union mlxsw_sp_l3addr *addr)
1903c55bdacSIdo Schimmel {
1913c55bdacSIdo Schimmel 	switch (proto) {
1923c55bdacSIdo Schimmel 	case MLXSW_SP_L3_PROTO_IPV4:
1933c55bdacSIdo Schimmel 		addr->addr4 = cpu_to_be32(uip);
1943c55bdacSIdo Schimmel 		return 0;
1953c55bdacSIdo Schimmel 	default:
1963c55bdacSIdo Schimmel 		WARN_ON(1);
1973c55bdacSIdo Schimmel 		return -EINVAL;
1983c55bdacSIdo Schimmel 	}
1993c55bdacSIdo Schimmel }
2003c55bdacSIdo Schimmel 
2016e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_list *
mlxsw_sp_nve_mc_list_find(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_nve_mc_list_key * key)2026e6030bdSIdo Schimmel mlxsw_sp_nve_mc_list_find(struct mlxsw_sp *mlxsw_sp,
2036e6030bdSIdo Schimmel 			  const struct mlxsw_sp_nve_mc_list_key *key)
2046e6030bdSIdo Schimmel {
2056e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
2066e6030bdSIdo Schimmel 
2076e6030bdSIdo Schimmel 	return rhashtable_lookup_fast(&nve->mc_list_ht, key,
2086e6030bdSIdo Schimmel 				      mlxsw_sp_nve_mc_list_ht_params);
2096e6030bdSIdo Schimmel }
2106e6030bdSIdo Schimmel 
2116e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_list *
mlxsw_sp_nve_mc_list_create(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_nve_mc_list_key * key)2126e6030bdSIdo Schimmel mlxsw_sp_nve_mc_list_create(struct mlxsw_sp *mlxsw_sp,
2136e6030bdSIdo Schimmel 			    const struct mlxsw_sp_nve_mc_list_key *key)
2146e6030bdSIdo Schimmel {
2156e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
2166e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
2176e6030bdSIdo Schimmel 	int err;
2186e6030bdSIdo Schimmel 
2196e6030bdSIdo Schimmel 	mc_list = kmalloc(sizeof(*mc_list), GFP_KERNEL);
2206e6030bdSIdo Schimmel 	if (!mc_list)
2216e6030bdSIdo Schimmel 		return ERR_PTR(-ENOMEM);
2226e6030bdSIdo Schimmel 
2236e6030bdSIdo Schimmel 	INIT_LIST_HEAD(&mc_list->records_list);
2246e6030bdSIdo Schimmel 	mc_list->key = *key;
2256e6030bdSIdo Schimmel 
2266e6030bdSIdo Schimmel 	err = rhashtable_insert_fast(&nve->mc_list_ht, &mc_list->ht_node,
2276e6030bdSIdo Schimmel 				     mlxsw_sp_nve_mc_list_ht_params);
2286e6030bdSIdo Schimmel 	if (err)
2296e6030bdSIdo Schimmel 		goto err_rhashtable_insert;
2306e6030bdSIdo Schimmel 
2316e6030bdSIdo Schimmel 	return mc_list;
2326e6030bdSIdo Schimmel 
2336e6030bdSIdo Schimmel err_rhashtable_insert:
2346e6030bdSIdo Schimmel 	kfree(mc_list);
2356e6030bdSIdo Schimmel 	return ERR_PTR(err);
2366e6030bdSIdo Schimmel }
2376e6030bdSIdo Schimmel 
mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list)2386e6030bdSIdo Schimmel static void mlxsw_sp_nve_mc_list_destroy(struct mlxsw_sp *mlxsw_sp,
2396e6030bdSIdo Schimmel 					 struct mlxsw_sp_nve_mc_list *mc_list)
2406e6030bdSIdo Schimmel {
2416e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
2426e6030bdSIdo Schimmel 
2436e6030bdSIdo Schimmel 	rhashtable_remove_fast(&nve->mc_list_ht, &mc_list->ht_node,
2446e6030bdSIdo Schimmel 			       mlxsw_sp_nve_mc_list_ht_params);
2456e6030bdSIdo Schimmel 	WARN_ON(!list_empty(&mc_list->records_list));
2466e6030bdSIdo Schimmel 	kfree(mc_list);
2476e6030bdSIdo Schimmel }
2486e6030bdSIdo Schimmel 
2496e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_list *
mlxsw_sp_nve_mc_list_get(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_nve_mc_list_key * key)2506e6030bdSIdo Schimmel mlxsw_sp_nve_mc_list_get(struct mlxsw_sp *mlxsw_sp,
2516e6030bdSIdo Schimmel 			 const struct mlxsw_sp_nve_mc_list_key *key)
2526e6030bdSIdo Schimmel {
2536e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
2546e6030bdSIdo Schimmel 
2556e6030bdSIdo Schimmel 	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, key);
2566e6030bdSIdo Schimmel 	if (mc_list)
2576e6030bdSIdo Schimmel 		return mc_list;
2586e6030bdSIdo Schimmel 
2596e6030bdSIdo Schimmel 	return mlxsw_sp_nve_mc_list_create(mlxsw_sp, key);
2606e6030bdSIdo Schimmel }
2616e6030bdSIdo Schimmel 
2626e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_list_put(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list)2636e6030bdSIdo Schimmel mlxsw_sp_nve_mc_list_put(struct mlxsw_sp *mlxsw_sp,
2646e6030bdSIdo Schimmel 			 struct mlxsw_sp_nve_mc_list *mc_list)
2656e6030bdSIdo Schimmel {
2666e6030bdSIdo Schimmel 	if (!list_empty(&mc_list->records_list))
2676e6030bdSIdo Schimmel 		return;
2686e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_destroy(mlxsw_sp, mc_list);
2696e6030bdSIdo Schimmel }
2706e6030bdSIdo Schimmel 
2716e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_record *
mlxsw_sp_nve_mc_record_create(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list,enum mlxsw_sp_l3proto proto)2726e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_create(struct mlxsw_sp *mlxsw_sp,
2736e6030bdSIdo Schimmel 			      struct mlxsw_sp_nve_mc_list *mc_list,
2746e6030bdSIdo Schimmel 			      enum mlxsw_sp_l3proto proto)
2756e6030bdSIdo Schimmel {
2766e6030bdSIdo Schimmel 	unsigned int num_max_entries = mlxsw_sp->nve->num_max_mc_entries[proto];
2776e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
2786e6030bdSIdo Schimmel 	int err;
2796e6030bdSIdo Schimmel 
280faa311e9SGustavo A. R. Silva 	mc_record = kzalloc(struct_size(mc_record, entries, num_max_entries),
281faa311e9SGustavo A. R. Silva 			    GFP_KERNEL);
2826e6030bdSIdo Schimmel 	if (!mc_record)
2836e6030bdSIdo Schimmel 		return ERR_PTR(-ENOMEM);
2846e6030bdSIdo Schimmel 
2856e6030bdSIdo Schimmel 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
2866e6030bdSIdo Schimmel 				  &mc_record->kvdl_index);
2876e6030bdSIdo Schimmel 	if (err)
2886e6030bdSIdo Schimmel 		goto err_kvdl_alloc;
2896e6030bdSIdo Schimmel 
2906e6030bdSIdo Schimmel 	mc_record->ops = mlxsw_sp_nve_mc_record_ops_arr[proto];
2916e6030bdSIdo Schimmel 	mc_record->mlxsw_sp = mlxsw_sp;
2926e6030bdSIdo Schimmel 	mc_record->mc_list = mc_list;
2936e6030bdSIdo Schimmel 	mc_record->proto = proto;
2946e6030bdSIdo Schimmel 	list_add_tail(&mc_record->list, &mc_list->records_list);
2956e6030bdSIdo Schimmel 
2966e6030bdSIdo Schimmel 	return mc_record;
2976e6030bdSIdo Schimmel 
2986e6030bdSIdo Schimmel err_kvdl_alloc:
2996e6030bdSIdo Schimmel 	kfree(mc_record);
3006e6030bdSIdo Schimmel 	return ERR_PTR(err);
3016e6030bdSIdo Schimmel }
3026e6030bdSIdo Schimmel 
3036e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record * mc_record)3046e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_destroy(struct mlxsw_sp_nve_mc_record *mc_record)
3056e6030bdSIdo Schimmel {
3066e6030bdSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
3076e6030bdSIdo Schimmel 
3086e6030bdSIdo Schimmel 	list_del(&mc_record->list);
3096e6030bdSIdo Schimmel 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_TNUMT, 1,
3106e6030bdSIdo Schimmel 			   mc_record->kvdl_index);
3116e6030bdSIdo Schimmel 	WARN_ON(mc_record->num_entries);
3126e6030bdSIdo Schimmel 	kfree(mc_record);
3136e6030bdSIdo Schimmel }
3146e6030bdSIdo Schimmel 
3156e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_record *
mlxsw_sp_nve_mc_record_get(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list,enum mlxsw_sp_l3proto proto)3166e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_get(struct mlxsw_sp *mlxsw_sp,
3176e6030bdSIdo Schimmel 			   struct mlxsw_sp_nve_mc_list *mc_list,
3186e6030bdSIdo Schimmel 			   enum mlxsw_sp_l3proto proto)
3196e6030bdSIdo Schimmel {
3206e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
3216e6030bdSIdo Schimmel 
3226e6030bdSIdo Schimmel 	list_for_each_entry_reverse(mc_record, &mc_list->records_list, list) {
3236e6030bdSIdo Schimmel 		unsigned int num_entries = mc_record->num_entries;
3246e6030bdSIdo Schimmel 		struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
3256e6030bdSIdo Schimmel 
3266e6030bdSIdo Schimmel 		if (mc_record->proto == proto &&
3276e6030bdSIdo Schimmel 		    num_entries < nve->num_max_mc_entries[proto])
3286e6030bdSIdo Schimmel 			return mc_record;
3296e6030bdSIdo Schimmel 	}
3306e6030bdSIdo Schimmel 
3316e6030bdSIdo Schimmel 	return mlxsw_sp_nve_mc_record_create(mlxsw_sp, mc_list, proto);
3326e6030bdSIdo Schimmel }
3336e6030bdSIdo Schimmel 
3346e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record * mc_record)3356e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_put(struct mlxsw_sp_nve_mc_record *mc_record)
3366e6030bdSIdo Schimmel {
3376e6030bdSIdo Schimmel 	if (mc_record->num_entries != 0)
3386e6030bdSIdo Schimmel 		return;
3396e6030bdSIdo Schimmel 
3406e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_record_destroy(mc_record);
3416e6030bdSIdo Schimmel }
3426e6030bdSIdo Schimmel 
3436e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_entry *
mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record * mc_record)3446e6030bdSIdo Schimmel mlxsw_sp_nve_mc_free_entry_find(struct mlxsw_sp_nve_mc_record *mc_record)
3456e6030bdSIdo Schimmel {
3466e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
3476e6030bdSIdo Schimmel 	unsigned int num_max_entries;
3486e6030bdSIdo Schimmel 	int i;
3496e6030bdSIdo Schimmel 
3506e6030bdSIdo Schimmel 	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
3516e6030bdSIdo Schimmel 	for (i = 0; i < num_max_entries; i++) {
3526e6030bdSIdo Schimmel 		if (mc_record->entries[i].valid)
3536e6030bdSIdo Schimmel 			continue;
3546e6030bdSIdo Schimmel 		return &mc_record->entries[i];
3556e6030bdSIdo Schimmel 	}
3566e6030bdSIdo Schimmel 
3576e6030bdSIdo Schimmel 	return NULL;
3586e6030bdSIdo Schimmel }
3596e6030bdSIdo Schimmel 
3606e6030bdSIdo Schimmel static int
mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record * mc_record)3616e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_refresh(struct mlxsw_sp_nve_mc_record *mc_record)
3626e6030bdSIdo Schimmel {
3636e6030bdSIdo Schimmel 	enum mlxsw_reg_tnumt_record_type type = mc_record->ops->type;
3646e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
3656e6030bdSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mc_record->mlxsw_sp;
3666e6030bdSIdo Schimmel 	char tnumt_pl[MLXSW_REG_TNUMT_LEN];
3676e6030bdSIdo Schimmel 	unsigned int num_max_entries;
3686e6030bdSIdo Schimmel 	unsigned int num_entries = 0;
3696e6030bdSIdo Schimmel 	u32 next_kvdl_index = 0;
3706e6030bdSIdo Schimmel 	bool next_valid = false;
3716e6030bdSIdo Schimmel 	int i;
3726e6030bdSIdo Schimmel 
3736e6030bdSIdo Schimmel 	if (!list_is_last(&mc_record->list, &mc_list->records_list)) {
3746e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_record *next_record;
3756e6030bdSIdo Schimmel 
3766e6030bdSIdo Schimmel 		next_record = list_next_entry(mc_record, list);
3776e6030bdSIdo Schimmel 		next_kvdl_index = next_record->kvdl_index;
3786e6030bdSIdo Schimmel 		next_valid = true;
3796e6030bdSIdo Schimmel 	}
3806e6030bdSIdo Schimmel 
38102c3b5c5SAmit Cohen 	mlxsw_reg_tnumt_pack(tnumt_pl, type, MLXSW_REG_TUNNEL_PORT_NVE,
3826e6030bdSIdo Schimmel 			     mc_record->kvdl_index, next_valid,
3836e6030bdSIdo Schimmel 			     next_kvdl_index, mc_record->num_entries);
3846e6030bdSIdo Schimmel 
3856e6030bdSIdo Schimmel 	num_max_entries = mlxsw_sp->nve->num_max_mc_entries[mc_record->proto];
3866e6030bdSIdo Schimmel 	for (i = 0; i < num_max_entries; i++) {
3876e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_entry *mc_entry;
3886e6030bdSIdo Schimmel 
3896e6030bdSIdo Schimmel 		mc_entry = &mc_record->entries[i];
3906e6030bdSIdo Schimmel 		if (!mc_entry->valid)
3916e6030bdSIdo Schimmel 			continue;
3926e6030bdSIdo Schimmel 		mc_record->ops->entry_set(mc_record, mc_entry, tnumt_pl,
3936e6030bdSIdo Schimmel 					  num_entries++);
3946e6030bdSIdo Schimmel 	}
3956e6030bdSIdo Schimmel 
3966e6030bdSIdo Schimmel 	WARN_ON(num_entries != mc_record->num_entries);
3976e6030bdSIdo Schimmel 
3986e6030bdSIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnumt), tnumt_pl);
3996e6030bdSIdo Schimmel }
4006e6030bdSIdo Schimmel 
4016e6030bdSIdo Schimmel static bool
mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record * mc_record)4026e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_is_first(struct mlxsw_sp_nve_mc_record *mc_record)
4036e6030bdSIdo Schimmel {
4046e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
4056e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *first_record;
4066e6030bdSIdo Schimmel 
4076e6030bdSIdo Schimmel 	first_record = list_first_entry(&mc_list->records_list,
4086e6030bdSIdo Schimmel 					struct mlxsw_sp_nve_mc_record, list);
4096e6030bdSIdo Schimmel 
4106e6030bdSIdo Schimmel 	return mc_record == first_record;
4116e6030bdSIdo Schimmel }
4126e6030bdSIdo Schimmel 
4136e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_entry *
mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record * mc_record,union mlxsw_sp_l3addr * addr)4146e6030bdSIdo Schimmel mlxsw_sp_nve_mc_entry_find(struct mlxsw_sp_nve_mc_record *mc_record,
4156e6030bdSIdo Schimmel 			   union mlxsw_sp_l3addr *addr)
4166e6030bdSIdo Schimmel {
4176e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
4186e6030bdSIdo Schimmel 	unsigned int num_max_entries;
4196e6030bdSIdo Schimmel 	int i;
4206e6030bdSIdo Schimmel 
4216e6030bdSIdo Schimmel 	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
4226e6030bdSIdo Schimmel 	for (i = 0; i < num_max_entries; i++) {
4236e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_entry *mc_entry;
4246e6030bdSIdo Schimmel 
4256e6030bdSIdo Schimmel 		mc_entry = &mc_record->entries[i];
4266e6030bdSIdo Schimmel 		if (!mc_entry->valid)
4276e6030bdSIdo Schimmel 			continue;
4286e6030bdSIdo Schimmel 		if (mc_record->ops->entry_compare(mc_record, mc_entry, addr))
4296e6030bdSIdo Schimmel 			return mc_entry;
4306e6030bdSIdo Schimmel 	}
4316e6030bdSIdo Schimmel 
4326e6030bdSIdo Schimmel 	return NULL;
4336e6030bdSIdo Schimmel }
4346e6030bdSIdo Schimmel 
4356e6030bdSIdo Schimmel static int
mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record * mc_record,union mlxsw_sp_l3addr * addr)4366e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_ip_add(struct mlxsw_sp_nve_mc_record *mc_record,
4376e6030bdSIdo Schimmel 			      union mlxsw_sp_l3addr *addr)
4386e6030bdSIdo Schimmel {
4396e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_entry *mc_entry = NULL;
4406e6030bdSIdo Schimmel 	int err;
4416e6030bdSIdo Schimmel 
4426e6030bdSIdo Schimmel 	mc_entry = mlxsw_sp_nve_mc_free_entry_find(mc_record);
4436e6030bdSIdo Schimmel 	if (WARN_ON(!mc_entry))
4446e6030bdSIdo Schimmel 		return -EINVAL;
4456e6030bdSIdo Schimmel 
4466e6030bdSIdo Schimmel 	err = mc_record->ops->entry_add(mc_record, mc_entry, addr);
4476e6030bdSIdo Schimmel 	if (err)
4486e6030bdSIdo Schimmel 		return err;
4496e6030bdSIdo Schimmel 	mc_record->num_entries++;
4506e6030bdSIdo Schimmel 	mc_entry->valid = true;
4516e6030bdSIdo Schimmel 
4526e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_mc_record_refresh(mc_record);
4536e6030bdSIdo Schimmel 	if (err)
4546e6030bdSIdo Schimmel 		goto err_record_refresh;
4556e6030bdSIdo Schimmel 
4566e6030bdSIdo Schimmel 	/* If this is a new record and not the first one, then we need to
4576e6030bdSIdo Schimmel 	 * update the next pointer of the previous entry
4586e6030bdSIdo Schimmel 	 */
4596e6030bdSIdo Schimmel 	if (mc_record->num_entries != 1 ||
4606e6030bdSIdo Schimmel 	    mlxsw_sp_nve_mc_record_is_first(mc_record))
4616e6030bdSIdo Schimmel 		return 0;
4626e6030bdSIdo Schimmel 
4636e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_mc_record_refresh(list_prev_entry(mc_record, list));
4646e6030bdSIdo Schimmel 	if (err)
4656e6030bdSIdo Schimmel 		goto err_prev_record_refresh;
4666e6030bdSIdo Schimmel 
4676e6030bdSIdo Schimmel 	return 0;
4686e6030bdSIdo Schimmel 
4696e6030bdSIdo Schimmel err_prev_record_refresh:
4706e6030bdSIdo Schimmel err_record_refresh:
4716e6030bdSIdo Schimmel 	mc_entry->valid = false;
4726e6030bdSIdo Schimmel 	mc_record->num_entries--;
4736e6030bdSIdo Schimmel 	mc_record->ops->entry_del(mc_record, mc_entry);
4746e6030bdSIdo Schimmel 	return err;
4756e6030bdSIdo Schimmel }
4766e6030bdSIdo Schimmel 
4776e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record * mc_record,struct mlxsw_sp_nve_mc_entry * mc_entry)4786e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_entry_del(struct mlxsw_sp_nve_mc_record *mc_record,
4796e6030bdSIdo Schimmel 				 struct mlxsw_sp_nve_mc_entry *mc_entry)
4806e6030bdSIdo Schimmel {
4816e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list = mc_record->mc_list;
4826e6030bdSIdo Schimmel 
4836e6030bdSIdo Schimmel 	mc_entry->valid = false;
4846e6030bdSIdo Schimmel 	mc_record->num_entries--;
4856e6030bdSIdo Schimmel 
4866e6030bdSIdo Schimmel 	/* When the record continues to exist we only need to invalidate
4876e6030bdSIdo Schimmel 	 * the requested entry
4886e6030bdSIdo Schimmel 	 */
4896e6030bdSIdo Schimmel 	if (mc_record->num_entries != 0) {
4906e6030bdSIdo Schimmel 		mlxsw_sp_nve_mc_record_refresh(mc_record);
4916e6030bdSIdo Schimmel 		mc_record->ops->entry_del(mc_record, mc_entry);
4926e6030bdSIdo Schimmel 		return;
4936e6030bdSIdo Schimmel 	}
4946e6030bdSIdo Schimmel 
4956e6030bdSIdo Schimmel 	/* If the record needs to be deleted, but it is not the first,
4966e6030bdSIdo Schimmel 	 * then we need to make sure that the previous record no longer
4976e6030bdSIdo Schimmel 	 * points to it. Remove deleted record from the list to reflect
4986e6030bdSIdo Schimmel 	 * that and then re-add it at the end, so that it could be
4996e6030bdSIdo Schimmel 	 * properly removed by the record destruction code
5006e6030bdSIdo Schimmel 	 */
5016e6030bdSIdo Schimmel 	if (!mlxsw_sp_nve_mc_record_is_first(mc_record)) {
5026e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_record *prev_record;
5036e6030bdSIdo Schimmel 
5046e6030bdSIdo Schimmel 		prev_record = list_prev_entry(mc_record, list);
5056e6030bdSIdo Schimmel 		list_del(&mc_record->list);
5066e6030bdSIdo Schimmel 		mlxsw_sp_nve_mc_record_refresh(prev_record);
5076e6030bdSIdo Schimmel 		list_add_tail(&mc_record->list, &mc_list->records_list);
5086e6030bdSIdo Schimmel 		mc_record->ops->entry_del(mc_record, mc_entry);
5096e6030bdSIdo Schimmel 		return;
5106e6030bdSIdo Schimmel 	}
5116e6030bdSIdo Schimmel 
5126e6030bdSIdo Schimmel 	/* If the first record needs to be deleted, but the list is not
5136e6030bdSIdo Schimmel 	 * singular, then the second record needs to be written in the
5146e6030bdSIdo Schimmel 	 * first record's address, as this address is stored as a property
5156e6030bdSIdo Schimmel 	 * of the FID
5166e6030bdSIdo Schimmel 	 */
5176e6030bdSIdo Schimmel 	if (mlxsw_sp_nve_mc_record_is_first(mc_record) &&
5186e6030bdSIdo Schimmel 	    !list_is_singular(&mc_list->records_list)) {
5196e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_record *next_record;
5206e6030bdSIdo Schimmel 
5216e6030bdSIdo Schimmel 		next_record = list_next_entry(mc_record, list);
5226e6030bdSIdo Schimmel 		swap(mc_record->kvdl_index, next_record->kvdl_index);
5236e6030bdSIdo Schimmel 		mlxsw_sp_nve_mc_record_refresh(next_record);
5246e6030bdSIdo Schimmel 		mc_record->ops->entry_del(mc_record, mc_entry);
5256e6030bdSIdo Schimmel 		return;
5266e6030bdSIdo Schimmel 	}
5276e6030bdSIdo Schimmel 
5286e6030bdSIdo Schimmel 	/* This is the last case where the last remaining record needs to
5296e6030bdSIdo Schimmel 	 * be deleted. Simply delete the entry
5306e6030bdSIdo Schimmel 	 */
5316e6030bdSIdo Schimmel 	mc_record->ops->entry_del(mc_record, mc_entry);
5326e6030bdSIdo Schimmel }
5336e6030bdSIdo Schimmel 
5346e6030bdSIdo Schimmel static struct mlxsw_sp_nve_mc_record *
mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list * mc_list,enum mlxsw_sp_l3proto proto,union mlxsw_sp_l3addr * addr,struct mlxsw_sp_nve_mc_entry ** mc_entry)5356e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_find(struct mlxsw_sp_nve_mc_list *mc_list,
5366e6030bdSIdo Schimmel 			    enum mlxsw_sp_l3proto proto,
5376e6030bdSIdo Schimmel 			    union mlxsw_sp_l3addr *addr,
5386e6030bdSIdo Schimmel 			    struct mlxsw_sp_nve_mc_entry **mc_entry)
5396e6030bdSIdo Schimmel {
5406e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
5416e6030bdSIdo Schimmel 
5426e6030bdSIdo Schimmel 	list_for_each_entry(mc_record, &mc_list->records_list, list) {
5436e6030bdSIdo Schimmel 		if (mc_record->proto != proto)
5446e6030bdSIdo Schimmel 			continue;
5456e6030bdSIdo Schimmel 
5466e6030bdSIdo Schimmel 		*mc_entry = mlxsw_sp_nve_mc_entry_find(mc_record, addr);
5476e6030bdSIdo Schimmel 		if (*mc_entry)
5486e6030bdSIdo Schimmel 			return mc_record;
5496e6030bdSIdo Schimmel 	}
5506e6030bdSIdo Schimmel 
5516e6030bdSIdo Schimmel 	return NULL;
5526e6030bdSIdo Schimmel }
5536e6030bdSIdo Schimmel 
mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list,enum mlxsw_sp_l3proto proto,union mlxsw_sp_l3addr * addr)5546e6030bdSIdo Schimmel static int mlxsw_sp_nve_mc_list_ip_add(struct mlxsw_sp *mlxsw_sp,
5556e6030bdSIdo Schimmel 				       struct mlxsw_sp_nve_mc_list *mc_list,
5566e6030bdSIdo Schimmel 				       enum mlxsw_sp_l3proto proto,
5576e6030bdSIdo Schimmel 				       union mlxsw_sp_l3addr *addr)
5586e6030bdSIdo Schimmel {
5596e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
5606e6030bdSIdo Schimmel 	int err;
5616e6030bdSIdo Schimmel 
5626e6030bdSIdo Schimmel 	mc_record = mlxsw_sp_nve_mc_record_get(mlxsw_sp, mc_list, proto);
5636e6030bdSIdo Schimmel 	if (IS_ERR(mc_record))
5646e6030bdSIdo Schimmel 		return PTR_ERR(mc_record);
5656e6030bdSIdo Schimmel 
5666e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_mc_record_ip_add(mc_record, addr);
5676e6030bdSIdo Schimmel 	if (err)
5686e6030bdSIdo Schimmel 		goto err_ip_add;
5696e6030bdSIdo Schimmel 
5706e6030bdSIdo Schimmel 	return 0;
5716e6030bdSIdo Schimmel 
5726e6030bdSIdo Schimmel err_ip_add:
5736e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_record_put(mc_record);
5746e6030bdSIdo Schimmel 	return err;
5756e6030bdSIdo Schimmel }
5766e6030bdSIdo Schimmel 
mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_mc_list * mc_list,enum mlxsw_sp_l3proto proto,union mlxsw_sp_l3addr * addr)5776e6030bdSIdo Schimmel static void mlxsw_sp_nve_mc_list_ip_del(struct mlxsw_sp *mlxsw_sp,
5786e6030bdSIdo Schimmel 					struct mlxsw_sp_nve_mc_list *mc_list,
5796e6030bdSIdo Schimmel 					enum mlxsw_sp_l3proto proto,
5806e6030bdSIdo Schimmel 					union mlxsw_sp_l3addr *addr)
5816e6030bdSIdo Schimmel {
5826e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
5836e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_entry *mc_entry;
5846e6030bdSIdo Schimmel 
5856e6030bdSIdo Schimmel 	mc_record = mlxsw_sp_nve_mc_record_find(mc_list, proto, addr,
5866e6030bdSIdo Schimmel 						&mc_entry);
587050fc01fSIdo Schimmel 	if (!mc_record)
5886e6030bdSIdo Schimmel 		return;
5896e6030bdSIdo Schimmel 
5906e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
5916e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_record_put(mc_record);
5926e6030bdSIdo Schimmel }
5936e6030bdSIdo Schimmel 
5946e6030bdSIdo Schimmel static int
mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid * fid,struct mlxsw_sp_nve_mc_list * mc_list)5956e6030bdSIdo Schimmel mlxsw_sp_nve_fid_flood_index_set(struct mlxsw_sp_fid *fid,
5966e6030bdSIdo Schimmel 				 struct mlxsw_sp_nve_mc_list *mc_list)
5976e6030bdSIdo Schimmel {
5986e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
5996e6030bdSIdo Schimmel 
6006e6030bdSIdo Schimmel 	/* The address of the first record in the list is a property of
6016e6030bdSIdo Schimmel 	 * the FID and we never change it. It only needs to be set when
6026e6030bdSIdo Schimmel 	 * a new list is created
6036e6030bdSIdo Schimmel 	 */
6046e6030bdSIdo Schimmel 	if (mlxsw_sp_fid_nve_flood_index_is_set(fid))
6056e6030bdSIdo Schimmel 		return 0;
6066e6030bdSIdo Schimmel 
6076e6030bdSIdo Schimmel 	mc_record = list_first_entry(&mc_list->records_list,
6086e6030bdSIdo Schimmel 				     struct mlxsw_sp_nve_mc_record, list);
6096e6030bdSIdo Schimmel 
6106e6030bdSIdo Schimmel 	return mlxsw_sp_fid_nve_flood_index_set(fid, mc_record->kvdl_index);
6116e6030bdSIdo Schimmel }
6126e6030bdSIdo Schimmel 
6136e6030bdSIdo Schimmel static void
mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid * fid,struct mlxsw_sp_nve_mc_list * mc_list)6146e6030bdSIdo Schimmel mlxsw_sp_nve_fid_flood_index_clear(struct mlxsw_sp_fid *fid,
6156e6030bdSIdo Schimmel 				   struct mlxsw_sp_nve_mc_list *mc_list)
6166e6030bdSIdo Schimmel {
6176e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record;
6186e6030bdSIdo Schimmel 
6196e6030bdSIdo Schimmel 	/* The address of the first record needs to be invalidated only when
6206e6030bdSIdo Schimmel 	 * the last record is about to be removed
6216e6030bdSIdo Schimmel 	 */
6226e6030bdSIdo Schimmel 	if (!list_is_singular(&mc_list->records_list))
6236e6030bdSIdo Schimmel 		return;
6246e6030bdSIdo Schimmel 
6256e6030bdSIdo Schimmel 	mc_record = list_first_entry(&mc_list->records_list,
6266e6030bdSIdo Schimmel 				     struct mlxsw_sp_nve_mc_record, list);
6276e6030bdSIdo Schimmel 	if (mc_record->num_entries != 1)
6286e6030bdSIdo Schimmel 		return;
6296e6030bdSIdo Schimmel 
6306e6030bdSIdo Schimmel 	return mlxsw_sp_fid_nve_flood_index_clear(fid);
6316e6030bdSIdo Schimmel }
6326e6030bdSIdo Schimmel 
mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fid * fid,enum mlxsw_sp_l3proto proto,union mlxsw_sp_l3addr * addr)6336e6030bdSIdo Schimmel int mlxsw_sp_nve_flood_ip_add(struct mlxsw_sp *mlxsw_sp,
6346e6030bdSIdo Schimmel 			      struct mlxsw_sp_fid *fid,
6356e6030bdSIdo Schimmel 			      enum mlxsw_sp_l3proto proto,
6366e6030bdSIdo Schimmel 			      union mlxsw_sp_l3addr *addr)
6376e6030bdSIdo Schimmel {
6386e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list_key key = { 0 };
6396e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
6406e6030bdSIdo Schimmel 	int err;
6416e6030bdSIdo Schimmel 
6426e6030bdSIdo Schimmel 	key.fid_index = mlxsw_sp_fid_index(fid);
6436e6030bdSIdo Schimmel 	mc_list = mlxsw_sp_nve_mc_list_get(mlxsw_sp, &key);
6446e6030bdSIdo Schimmel 	if (IS_ERR(mc_list))
6456e6030bdSIdo Schimmel 		return PTR_ERR(mc_list);
6466e6030bdSIdo Schimmel 
6476e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_mc_list_ip_add(mlxsw_sp, mc_list, proto, addr);
6486e6030bdSIdo Schimmel 	if (err)
6496e6030bdSIdo Schimmel 		goto err_add_ip;
6506e6030bdSIdo Schimmel 
6516e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_fid_flood_index_set(fid, mc_list);
6526e6030bdSIdo Schimmel 	if (err)
6536e6030bdSIdo Schimmel 		goto err_fid_flood_index_set;
6546e6030bdSIdo Schimmel 
6556e6030bdSIdo Schimmel 	return 0;
6566e6030bdSIdo Schimmel 
6576e6030bdSIdo Schimmel err_fid_flood_index_set:
6586e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
6596e6030bdSIdo Schimmel err_add_ip:
6606e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
6616e6030bdSIdo Schimmel 	return err;
6626e6030bdSIdo Schimmel }
6636e6030bdSIdo Schimmel 
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)6646e6030bdSIdo Schimmel void mlxsw_sp_nve_flood_ip_del(struct mlxsw_sp *mlxsw_sp,
6656e6030bdSIdo Schimmel 			       struct mlxsw_sp_fid *fid,
6666e6030bdSIdo Schimmel 			       enum mlxsw_sp_l3proto proto,
6676e6030bdSIdo Schimmel 			       union mlxsw_sp_l3addr *addr)
6686e6030bdSIdo Schimmel {
6696e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list_key key = { 0 };
6706e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
6716e6030bdSIdo Schimmel 
6726e6030bdSIdo Schimmel 	key.fid_index = mlxsw_sp_fid_index(fid);
6736e6030bdSIdo Schimmel 	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
674050fc01fSIdo Schimmel 	if (!mc_list)
6756e6030bdSIdo Schimmel 		return;
6766e6030bdSIdo Schimmel 
6776e6030bdSIdo Schimmel 	mlxsw_sp_nve_fid_flood_index_clear(fid, mc_list);
6786e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_ip_del(mlxsw_sp, mc_list, proto, addr);
6796e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
6806e6030bdSIdo Schimmel }
6816e6030bdSIdo Schimmel 
6826e6030bdSIdo Schimmel static void
mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record * mc_record)6836e6030bdSIdo Schimmel mlxsw_sp_nve_mc_record_delete(struct mlxsw_sp_nve_mc_record *mc_record)
6846e6030bdSIdo Schimmel {
6856e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mc_record->mlxsw_sp->nve;
6866e6030bdSIdo Schimmel 	unsigned int num_max_entries;
6876e6030bdSIdo Schimmel 	int i;
6886e6030bdSIdo Schimmel 
6896e6030bdSIdo Schimmel 	num_max_entries = nve->num_max_mc_entries[mc_record->proto];
6906e6030bdSIdo Schimmel 	for (i = 0; i < num_max_entries; i++) {
6916e6030bdSIdo Schimmel 		struct mlxsw_sp_nve_mc_entry *mc_entry = &mc_record->entries[i];
6926e6030bdSIdo Schimmel 
6936e6030bdSIdo Schimmel 		if (!mc_entry->valid)
6946e6030bdSIdo Schimmel 			continue;
6956e6030bdSIdo Schimmel 		mlxsw_sp_nve_mc_record_entry_del(mc_record, mc_entry);
6966e6030bdSIdo Schimmel 	}
6976e6030bdSIdo Schimmel 
6986e6030bdSIdo Schimmel 	WARN_ON(mc_record->num_entries);
6996e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_record_put(mc_record);
7006e6030bdSIdo Schimmel }
7016e6030bdSIdo Schimmel 
mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fid * fid)7026e6030bdSIdo Schimmel static void mlxsw_sp_nve_flood_ip_flush(struct mlxsw_sp *mlxsw_sp,
7036e6030bdSIdo Schimmel 					struct mlxsw_sp_fid *fid)
7046e6030bdSIdo Schimmel {
7056e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_record *mc_record, *tmp;
7066e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list_key key = { 0 };
7076e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_mc_list *mc_list;
7086e6030bdSIdo Schimmel 
7096e6030bdSIdo Schimmel 	if (!mlxsw_sp_fid_nve_flood_index_is_set(fid))
7106e6030bdSIdo Schimmel 		return;
7116e6030bdSIdo Schimmel 
7126e6030bdSIdo Schimmel 	mlxsw_sp_fid_nve_flood_index_clear(fid);
7136e6030bdSIdo Schimmel 
7146e6030bdSIdo Schimmel 	key.fid_index = mlxsw_sp_fid_index(fid);
7156e6030bdSIdo Schimmel 	mc_list = mlxsw_sp_nve_mc_list_find(mlxsw_sp, &key);
7166e6030bdSIdo Schimmel 	if (WARN_ON(!mc_list))
7176e6030bdSIdo Schimmel 		return;
7186e6030bdSIdo Schimmel 
7196e6030bdSIdo Schimmel 	list_for_each_entry_safe(mc_record, tmp, &mc_list->records_list, list)
7206e6030bdSIdo Schimmel 		mlxsw_sp_nve_mc_record_delete(mc_record);
7216e6030bdSIdo Schimmel 
7226e6030bdSIdo Schimmel 	WARN_ON(!list_empty(&mc_list->records_list));
7236e6030bdSIdo Schimmel 	mlxsw_sp_nve_mc_list_put(mlxsw_sp, mc_list);
7246e6030bdSIdo Schimmel }
7256e6030bdSIdo Schimmel 
mlxsw_sp_nve_tunnel_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_config * config)7266e6030bdSIdo Schimmel static int mlxsw_sp_nve_tunnel_init(struct mlxsw_sp *mlxsw_sp,
7276e6030bdSIdo Schimmel 				    struct mlxsw_sp_nve_config *config)
7286e6030bdSIdo Schimmel {
7296e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
7306e6030bdSIdo Schimmel 	const struct mlxsw_sp_nve_ops *ops;
7316e6030bdSIdo Schimmel 	int err;
7326e6030bdSIdo Schimmel 
7336e6030bdSIdo Schimmel 	if (nve->num_nve_tunnels++ != 0)
7346e6030bdSIdo Schimmel 		return 0;
7356e6030bdSIdo Schimmel 
7369ef87b24SIdo Schimmel 	nve->config = *config;
7379ef87b24SIdo Schimmel 
7386e6030bdSIdo Schimmel 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
7396e6030bdSIdo Schimmel 				  &nve->tunnel_index);
7406e6030bdSIdo Schimmel 	if (err)
7416e6030bdSIdo Schimmel 		goto err_kvdl_alloc;
7426e6030bdSIdo Schimmel 
7436e6030bdSIdo Schimmel 	ops = nve->nve_ops_arr[config->type];
7446e6030bdSIdo Schimmel 	err = ops->init(nve, config);
7456e6030bdSIdo Schimmel 	if (err)
7466e6030bdSIdo Schimmel 		goto err_ops_init;
7476e6030bdSIdo Schimmel 
7486e6030bdSIdo Schimmel 	return 0;
7496e6030bdSIdo Schimmel 
7506e6030bdSIdo Schimmel err_ops_init:
7516e6030bdSIdo Schimmel 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
7526e6030bdSIdo Schimmel 			   nve->tunnel_index);
7536e6030bdSIdo Schimmel err_kvdl_alloc:
7549ef87b24SIdo Schimmel 	memset(&nve->config, 0, sizeof(nve->config));
7556e6030bdSIdo Schimmel 	nve->num_nve_tunnels--;
7566e6030bdSIdo Schimmel 	return err;
7576e6030bdSIdo Schimmel }
7586e6030bdSIdo Schimmel 
mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp * mlxsw_sp)7596e6030bdSIdo Schimmel static void mlxsw_sp_nve_tunnel_fini(struct mlxsw_sp *mlxsw_sp)
7606e6030bdSIdo Schimmel {
7616e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
7626e6030bdSIdo Schimmel 	const struct mlxsw_sp_nve_ops *ops;
7636e6030bdSIdo Schimmel 
7646e6030bdSIdo Schimmel 	ops = nve->nve_ops_arr[nve->config.type];
7656e6030bdSIdo Schimmel 
7666e6030bdSIdo Schimmel 	if (mlxsw_sp->nve->num_nve_tunnels == 1) {
7676e6030bdSIdo Schimmel 		ops->fini(nve);
7686e6030bdSIdo Schimmel 		mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ADJ, 1,
7696e6030bdSIdo Schimmel 				   nve->tunnel_index);
770c6b36bddSPetr Machata 		memset(&nve->config, 0, sizeof(nve->config));
7716e6030bdSIdo Schimmel 	}
7726e6030bdSIdo Schimmel 	nve->num_nve_tunnels--;
7736e6030bdSIdo Schimmel }
7746e6030bdSIdo Schimmel 
mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp * mlxsw_sp,u16 fid_index)7756e6030bdSIdo Schimmel static void mlxsw_sp_nve_fdb_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
7766e6030bdSIdo Schimmel 					  u16 fid_index)
7776e6030bdSIdo Schimmel {
7786e6030bdSIdo Schimmel 	char sfdf_pl[MLXSW_REG_SFDF_LEN];
7796e6030bdSIdo Schimmel 
7806e6030bdSIdo Schimmel 	mlxsw_reg_sfdf_pack(sfdf_pl, MLXSW_REG_SFDF_FLUSH_PER_NVE_AND_FID);
7816e6030bdSIdo Schimmel 	mlxsw_reg_sfdf_fid_set(sfdf_pl, fid_index);
7826e6030bdSIdo Schimmel 	mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdf), sfdf_pl);
7836e6030bdSIdo Schimmel }
7846e6030bdSIdo Schimmel 
mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp * mlxsw_sp,const struct mlxsw_sp_fid * fid,const struct net_device * nve_dev,__be32 vni)7858a5969d8SPetr Machata static void mlxsw_sp_nve_fdb_clear_offload(struct mlxsw_sp *mlxsw_sp,
7868a5969d8SPetr Machata 					   const struct mlxsw_sp_fid *fid,
7878a5969d8SPetr Machata 					   const struct net_device *nve_dev,
7888a5969d8SPetr Machata 					   __be32 vni)
7898a5969d8SPetr Machata {
7908a5969d8SPetr Machata 	const struct mlxsw_sp_nve_ops *ops;
7918a5969d8SPetr Machata 	enum mlxsw_sp_nve_type type;
7928a5969d8SPetr Machata 
7938a5969d8SPetr Machata 	if (WARN_ON(mlxsw_sp_fid_nve_type(fid, &type)))
7948a5969d8SPetr Machata 		return;
7958a5969d8SPetr Machata 
7968a5969d8SPetr Machata 	ops = mlxsw_sp->nve->nve_ops_arr[type];
7978a5969d8SPetr Machata 	ops->fdb_clear_offload(nve_dev, vni);
7988a5969d8SPetr Machata }
7998a5969d8SPetr Machata 
8000860c764SAmit Cohen struct mlxsw_sp_nve_ipv6_ht_key {
8010860c764SAmit Cohen 	u8 mac[ETH_ALEN];
8020860c764SAmit Cohen 	u16 fid_index;
8030860c764SAmit Cohen };
8040860c764SAmit Cohen 
8050860c764SAmit Cohen struct mlxsw_sp_nve_ipv6_ht_node {
8060860c764SAmit Cohen 	struct rhash_head ht_node;
8070860c764SAmit Cohen 	struct list_head list;
8080860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_key key;
8090860c764SAmit Cohen 	struct in6_addr addr6;
8100860c764SAmit Cohen };
8110860c764SAmit Cohen 
8120860c764SAmit Cohen static const struct rhashtable_params mlxsw_sp_nve_ipv6_ht_params = {
8130860c764SAmit Cohen 	.key_len = sizeof(struct mlxsw_sp_nve_ipv6_ht_key),
8140860c764SAmit Cohen 	.key_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, key),
8150860c764SAmit Cohen 	.head_offset = offsetof(struct mlxsw_sp_nve_ipv6_ht_node, ht_node),
8160860c764SAmit Cohen };
8170860c764SAmit Cohen 
mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp * mlxsw_sp,const struct in6_addr * addr6,u32 * p_kvdl_index)8180860c764SAmit Cohen int mlxsw_sp_nve_ipv6_addr_kvdl_set(struct mlxsw_sp *mlxsw_sp,
8190860c764SAmit Cohen 				    const struct in6_addr *addr6,
8200860c764SAmit Cohen 				    u32 *p_kvdl_index)
8210860c764SAmit Cohen {
8220860c764SAmit Cohen 	return mlxsw_sp_ipv6_addr_kvdl_index_get(mlxsw_sp, addr6, p_kvdl_index);
8230860c764SAmit Cohen }
8240860c764SAmit Cohen 
mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp * mlxsw_sp,const struct in6_addr * addr6)8250860c764SAmit Cohen void mlxsw_sp_nve_ipv6_addr_kvdl_unset(struct mlxsw_sp *mlxsw_sp,
8260860c764SAmit Cohen 				       const struct in6_addr *addr6)
8270860c764SAmit Cohen {
8280860c764SAmit Cohen 	mlxsw_sp_ipv6_addr_put(mlxsw_sp, addr6);
8290860c764SAmit Cohen }
8300860c764SAmit Cohen 
8310860c764SAmit Cohen 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)8320860c764SAmit Cohen mlxsw_sp_nve_ipv6_ht_node_lookup(struct mlxsw_sp *mlxsw_sp, const char *mac,
8330860c764SAmit Cohen 				 u16 fid_index)
8340860c764SAmit Cohen {
8350860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_key key = {};
8360860c764SAmit Cohen 
8370860c764SAmit Cohen 	ether_addr_copy(key.mac, mac);
8380860c764SAmit Cohen 	key.fid_index = fid_index;
8390860c764SAmit Cohen 	return rhashtable_lookup_fast(&mlxsw_sp->nve->ipv6_ht, &key,
8400860c764SAmit Cohen 				      mlxsw_sp_nve_ipv6_ht_params);
8410860c764SAmit Cohen }
8420860c764SAmit Cohen 
mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp * mlxsw_sp,const char * mac,u16 fid_index,const struct in6_addr * addr6)8430860c764SAmit Cohen static int mlxsw_sp_nve_ipv6_ht_insert(struct mlxsw_sp *mlxsw_sp,
8440860c764SAmit Cohen 				       const char *mac, u16 fid_index,
8450860c764SAmit Cohen 				       const struct in6_addr *addr6)
8460860c764SAmit Cohen {
8470860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
8480860c764SAmit Cohen 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
8490860c764SAmit Cohen 	int err;
8500860c764SAmit Cohen 
8510860c764SAmit Cohen 	ipv6_ht_node = kzalloc(sizeof(*ipv6_ht_node), GFP_KERNEL);
8520860c764SAmit Cohen 	if (!ipv6_ht_node)
8530860c764SAmit Cohen 		return -ENOMEM;
8540860c764SAmit Cohen 
8550860c764SAmit Cohen 	ether_addr_copy(ipv6_ht_node->key.mac, mac);
8560860c764SAmit Cohen 	ipv6_ht_node->key.fid_index = fid_index;
8570860c764SAmit Cohen 	ipv6_ht_node->addr6 = *addr6;
8580860c764SAmit Cohen 
8590860c764SAmit Cohen 	err = rhashtable_insert_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
8600860c764SAmit Cohen 				     mlxsw_sp_nve_ipv6_ht_params);
8610860c764SAmit Cohen 	if (err)
8620860c764SAmit Cohen 		goto err_rhashtable_insert;
8630860c764SAmit Cohen 
8640860c764SAmit Cohen 	list_add(&ipv6_ht_node->list, &nve->ipv6_addr_list);
8650860c764SAmit Cohen 
8660860c764SAmit Cohen 	return 0;
8670860c764SAmit Cohen 
8680860c764SAmit Cohen err_rhashtable_insert:
8690860c764SAmit Cohen 	kfree(ipv6_ht_node);
8700860c764SAmit Cohen 	return err;
8710860c764SAmit Cohen }
8720860c764SAmit Cohen 
8730860c764SAmit Cohen static void
mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_nve_ipv6_ht_node * ipv6_ht_node)8740860c764SAmit Cohen mlxsw_sp_nve_ipv6_ht_remove(struct mlxsw_sp *mlxsw_sp,
8750860c764SAmit Cohen 			    struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node)
8760860c764SAmit Cohen {
8770860c764SAmit Cohen 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
8780860c764SAmit Cohen 
8790860c764SAmit Cohen 	list_del(&ipv6_ht_node->list);
8800860c764SAmit Cohen 	rhashtable_remove_fast(&nve->ipv6_ht, &ipv6_ht_node->ht_node,
8810860c764SAmit Cohen 			       mlxsw_sp_nve_ipv6_ht_params);
8820860c764SAmit Cohen 	kfree(ipv6_ht_node);
8830860c764SAmit Cohen }
8840860c764SAmit Cohen 
8850860c764SAmit Cohen 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)8860860c764SAmit Cohen mlxsw_sp_nve_ipv6_addr_map_replace(struct mlxsw_sp *mlxsw_sp, const char *mac,
8870860c764SAmit Cohen 				   u16 fid_index,
8880860c764SAmit Cohen 				   const struct in6_addr *new_addr6)
8890860c764SAmit Cohen {
8900860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
8910860c764SAmit Cohen 
8920860c764SAmit Cohen 	ASSERT_RTNL();
8930860c764SAmit Cohen 
8940860c764SAmit Cohen 	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
8950860c764SAmit Cohen 							fid_index);
8960860c764SAmit Cohen 	if (!ipv6_ht_node)
8970860c764SAmit Cohen 		return mlxsw_sp_nve_ipv6_ht_insert(mlxsw_sp, mac, fid_index,
8980860c764SAmit Cohen 						   new_addr6);
8990860c764SAmit Cohen 
9000860c764SAmit Cohen 	mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
9010860c764SAmit Cohen 	ipv6_ht_node->addr6 = *new_addr6;
9020860c764SAmit Cohen 	return 0;
9030860c764SAmit Cohen }
9040860c764SAmit Cohen 
mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp * mlxsw_sp,const char * mac,u16 fid_index)9050860c764SAmit Cohen void mlxsw_sp_nve_ipv6_addr_map_del(struct mlxsw_sp *mlxsw_sp, const char *mac,
9060860c764SAmit Cohen 				    u16 fid_index)
9070860c764SAmit Cohen {
9080860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node;
9090860c764SAmit Cohen 
9100860c764SAmit Cohen 	ASSERT_RTNL();
9110860c764SAmit Cohen 
9120860c764SAmit Cohen 	ipv6_ht_node = mlxsw_sp_nve_ipv6_ht_node_lookup(mlxsw_sp, mac,
9130860c764SAmit Cohen 							fid_index);
9140860c764SAmit Cohen 	if (WARN_ON(!ipv6_ht_node))
9150860c764SAmit Cohen 		return;
9160860c764SAmit Cohen 
9170860c764SAmit Cohen 	mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
9180860c764SAmit Cohen }
9190860c764SAmit Cohen 
mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp * mlxsw_sp,u16 fid_index)9200860c764SAmit Cohen static void mlxsw_sp_nve_ipv6_addr_flush_by_fid(struct mlxsw_sp *mlxsw_sp,
9210860c764SAmit Cohen 						u16 fid_index)
9220860c764SAmit Cohen {
9230860c764SAmit Cohen 	struct mlxsw_sp_nve_ipv6_ht_node *ipv6_ht_node, *tmp;
9240860c764SAmit Cohen 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
9250860c764SAmit Cohen 
9260860c764SAmit Cohen 	list_for_each_entry_safe(ipv6_ht_node, tmp, &nve->ipv6_addr_list,
9270860c764SAmit Cohen 				 list) {
9280860c764SAmit Cohen 		if (ipv6_ht_node->key.fid_index != fid_index)
9290860c764SAmit Cohen 			continue;
9300860c764SAmit Cohen 
9310860c764SAmit Cohen 		mlxsw_sp_ipv6_addr_put(mlxsw_sp, &ipv6_ht_node->addr6);
9320860c764SAmit Cohen 		mlxsw_sp_nve_ipv6_ht_remove(mlxsw_sp, ipv6_ht_node);
9330860c764SAmit Cohen 	}
9340860c764SAmit Cohen }
9350860c764SAmit Cohen 
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)9366e6030bdSIdo Schimmel int mlxsw_sp_nve_fid_enable(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *fid,
9376e6030bdSIdo Schimmel 			    struct mlxsw_sp_nve_params *params,
9386e6030bdSIdo Schimmel 			    struct netlink_ext_ack *extack)
9396e6030bdSIdo Schimmel {
9406e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve = mlxsw_sp->nve;
9416e6030bdSIdo Schimmel 	const struct mlxsw_sp_nve_ops *ops;
9426e6030bdSIdo Schimmel 	struct mlxsw_sp_nve_config config;
9436e6030bdSIdo Schimmel 	int err;
9446e6030bdSIdo Schimmel 
9456e6030bdSIdo Schimmel 	ops = nve->nve_ops_arr[params->type];
9466e6030bdSIdo Schimmel 
947efbcb673SAmit Cohen 	if (!ops->can_offload(nve, params, extack))
948412283eeSIdo Schimmel 		return -EINVAL;
9496e6030bdSIdo Schimmel 
9506e6030bdSIdo Schimmel 	memset(&config, 0, sizeof(config));
95149d18964SAmit Cohen 	ops->nve_config(nve, params, &config);
9526e6030bdSIdo Schimmel 	if (nve->num_nve_tunnels &&
9536e6030bdSIdo Schimmel 	    memcmp(&config, &nve->config, sizeof(config))) {
9546e6030bdSIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Conflicting NVE tunnels configuration");
955412283eeSIdo Schimmel 		return -EINVAL;
9566e6030bdSIdo Schimmel 	}
9576e6030bdSIdo Schimmel 
9586e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_tunnel_init(mlxsw_sp, &config);
9596e6030bdSIdo Schimmel 	if (err) {
9606e6030bdSIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to initialize NVE tunnel");
9616e6030bdSIdo Schimmel 		return err;
9626e6030bdSIdo Schimmel 	}
9636e6030bdSIdo Schimmel 
9642a36c125SPetr Machata 	err = mlxsw_sp_fid_vni_set(fid, params->type, params->vni,
9652a36c125SPetr Machata 				   params->dev->ifindex);
9666e6030bdSIdo Schimmel 	if (err) {
9676e6030bdSIdo Schimmel 		NL_SET_ERR_MSG_MOD(extack, "Failed to set VNI on FID");
9686e6030bdSIdo Schimmel 		goto err_fid_vni_set;
9696e6030bdSIdo Schimmel 	}
9706e6030bdSIdo Schimmel 
971d907f58fSPetr Machata 	err = ops->fdb_replay(params->dev, params->vni, extack);
972a40313d9SPetr Machata 	if (err)
9738a5969d8SPetr Machata 		goto err_fdb_replay;
9748a5969d8SPetr Machata 
9756e6030bdSIdo Schimmel 	return 0;
9766e6030bdSIdo Schimmel 
9778a5969d8SPetr Machata err_fdb_replay:
9788a5969d8SPetr Machata 	mlxsw_sp_fid_vni_clear(fid);
9796e6030bdSIdo Schimmel err_fid_vni_set:
9806e6030bdSIdo Schimmel 	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
9816e6030bdSIdo Schimmel 	return err;
9826e6030bdSIdo Schimmel }
9836e6030bdSIdo Schimmel 
mlxsw_sp_nve_fid_disable(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_fid * fid)9846e6030bdSIdo Schimmel void mlxsw_sp_nve_fid_disable(struct mlxsw_sp *mlxsw_sp,
9856e6030bdSIdo Schimmel 			      struct mlxsw_sp_fid *fid)
9866e6030bdSIdo Schimmel {
9876e6030bdSIdo Schimmel 	u16 fid_index = mlxsw_sp_fid_index(fid);
9888a5969d8SPetr Machata 	struct net_device *nve_dev;
9898a5969d8SPetr Machata 	int nve_ifindex;
9908a5969d8SPetr Machata 	__be32 vni;
9916e6030bdSIdo Schimmel 
992*16f8c846SPetr Machata 	/* Necessary for __dev_get_by_index() below. */
993*16f8c846SPetr Machata 	ASSERT_RTNL();
994*16f8c846SPetr Machata 
9956e6030bdSIdo Schimmel 	mlxsw_sp_nve_flood_ip_flush(mlxsw_sp, fid);
9966e6030bdSIdo Schimmel 	mlxsw_sp_nve_fdb_flush_by_fid(mlxsw_sp, fid_index);
9970860c764SAmit Cohen 	mlxsw_sp_nve_ipv6_addr_flush_by_fid(mlxsw_sp, fid_index);
9988a5969d8SPetr Machata 
9998a5969d8SPetr Machata 	if (WARN_ON(mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex) ||
10008a5969d8SPetr Machata 		    mlxsw_sp_fid_vni(fid, &vni)))
10018a5969d8SPetr Machata 		goto out;
10028a5969d8SPetr Machata 
1003*16f8c846SPetr Machata 	nve_dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex);
10048a5969d8SPetr Machata 	if (!nve_dev)
10058a5969d8SPetr Machata 		goto out;
10068a5969d8SPetr Machata 
10078a5969d8SPetr Machata 	mlxsw_sp_nve_fdb_clear_offload(mlxsw_sp, fid, nve_dev, vni);
10088a5969d8SPetr Machata 	mlxsw_sp_fid_fdb_clear_offload(fid, nve_dev);
10098a5969d8SPetr Machata 
10108a5969d8SPetr Machata out:
10116e6030bdSIdo Schimmel 	mlxsw_sp_fid_vni_clear(fid);
10126e6030bdSIdo Schimmel 	mlxsw_sp_nve_tunnel_fini(mlxsw_sp);
10136e6030bdSIdo Schimmel }
10146e6030bdSIdo Schimmel 
mlxsw_sp_port_nve_init(struct mlxsw_sp_port * mlxsw_sp_port)10156e6030bdSIdo Schimmel int mlxsw_sp_port_nve_init(struct mlxsw_sp_port *mlxsw_sp_port)
10166e6030bdSIdo Schimmel {
10176e6030bdSIdo Schimmel 	struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
10186e6030bdSIdo Schimmel 	char tnqdr_pl[MLXSW_REG_TNQDR_LEN];
10196e6030bdSIdo Schimmel 
10206e6030bdSIdo Schimmel 	mlxsw_reg_tnqdr_pack(tnqdr_pl, mlxsw_sp_port->local_port);
10216e6030bdSIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqdr), tnqdr_pl);
10226e6030bdSIdo Schimmel }
10236e6030bdSIdo Schimmel 
mlxsw_sp_port_nve_fini(struct mlxsw_sp_port * mlxsw_sp_port)10246e6030bdSIdo Schimmel void mlxsw_sp_port_nve_fini(struct mlxsw_sp_port *mlxsw_sp_port)
10256e6030bdSIdo Schimmel {
10266e6030bdSIdo Schimmel }
10276e6030bdSIdo Schimmel 
mlxsw_sp_nve_qos_init(struct mlxsw_sp * mlxsw_sp)10286e6030bdSIdo Schimmel static int mlxsw_sp_nve_qos_init(struct mlxsw_sp *mlxsw_sp)
10296e6030bdSIdo Schimmel {
10306e6030bdSIdo Schimmel 	char tnqcr_pl[MLXSW_REG_TNQCR_LEN];
10316e6030bdSIdo Schimmel 
10326e6030bdSIdo Schimmel 	mlxsw_reg_tnqcr_pack(tnqcr_pl);
10336e6030bdSIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tnqcr), tnqcr_pl);
10346e6030bdSIdo Schimmel }
10356e6030bdSIdo Schimmel 
mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp * mlxsw_sp)10366e6030bdSIdo Schimmel static int mlxsw_sp_nve_ecn_encap_init(struct mlxsw_sp *mlxsw_sp)
10376e6030bdSIdo Schimmel {
10386e6030bdSIdo Schimmel 	int i;
10396e6030bdSIdo Schimmel 
10406e6030bdSIdo Schimmel 	/* Iterate over inner ECN values */
10416e6030bdSIdo Schimmel 	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
10426e6030bdSIdo Schimmel 		u8 outer_ecn = INET_ECN_encapsulate(0, i);
10436e6030bdSIdo Schimmel 		char tneem_pl[MLXSW_REG_TNEEM_LEN];
10446e6030bdSIdo Schimmel 		int err;
10456e6030bdSIdo Schimmel 
10466e6030bdSIdo Schimmel 		mlxsw_reg_tneem_pack(tneem_pl, i, outer_ecn);
10476e6030bdSIdo Schimmel 		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tneem),
10486e6030bdSIdo Schimmel 				      tneem_pl);
10496e6030bdSIdo Schimmel 		if (err)
10506e6030bdSIdo Schimmel 			return err;
10516e6030bdSIdo Schimmel 	}
10526e6030bdSIdo Schimmel 
10536e6030bdSIdo Schimmel 	return 0;
10546e6030bdSIdo Schimmel }
10556e6030bdSIdo Schimmel 
__mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp * mlxsw_sp,u8 inner_ecn,u8 outer_ecn)10566e6030bdSIdo Schimmel static int __mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp,
10576e6030bdSIdo Schimmel 					 u8 inner_ecn, u8 outer_ecn)
10586e6030bdSIdo Schimmel {
10596e6030bdSIdo Schimmel 	char tndem_pl[MLXSW_REG_TNDEM_LEN];
10606e6030bdSIdo Schimmel 	u8 new_inner_ecn;
106166167c31SIdo Schimmel 	bool trap_en;
10626e6030bdSIdo Schimmel 
106366167c31SIdo Schimmel 	new_inner_ecn = mlxsw_sp_tunnel_ecn_decap(outer_ecn, inner_ecn,
106466167c31SIdo Schimmel 						  &trap_en);
10656e6030bdSIdo Schimmel 	mlxsw_reg_tndem_pack(tndem_pl, outer_ecn, inner_ecn, new_inner_ecn,
10666e6030bdSIdo Schimmel 			     trap_en, trap_en ? MLXSW_TRAP_ID_DECAP_ECN0 : 0);
10676e6030bdSIdo Schimmel 	return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(tndem), tndem_pl);
10686e6030bdSIdo Schimmel }
10696e6030bdSIdo Schimmel 
mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp * mlxsw_sp)10706e6030bdSIdo Schimmel static int mlxsw_sp_nve_ecn_decap_init(struct mlxsw_sp *mlxsw_sp)
10716e6030bdSIdo Schimmel {
10726e6030bdSIdo Schimmel 	int i;
10736e6030bdSIdo Schimmel 
10746e6030bdSIdo Schimmel 	/* Iterate over inner ECN values */
10756e6030bdSIdo Schimmel 	for (i = INET_ECN_NOT_ECT; i <= INET_ECN_CE; i++) {
10766e6030bdSIdo Schimmel 		int j;
10776e6030bdSIdo Schimmel 
10786e6030bdSIdo Schimmel 		/* Iterate over outer ECN values */
10796e6030bdSIdo Schimmel 		for (j = INET_ECN_NOT_ECT; j <= INET_ECN_CE; j++) {
10806e6030bdSIdo Schimmel 			int err;
10816e6030bdSIdo Schimmel 
10826e6030bdSIdo Schimmel 			err = __mlxsw_sp_nve_ecn_decap_init(mlxsw_sp, i, j);
10836e6030bdSIdo Schimmel 			if (err)
10846e6030bdSIdo Schimmel 				return err;
10856e6030bdSIdo Schimmel 		}
10866e6030bdSIdo Schimmel 	}
10876e6030bdSIdo Schimmel 
10886e6030bdSIdo Schimmel 	return 0;
10896e6030bdSIdo Schimmel }
10906e6030bdSIdo Schimmel 
mlxsw_sp_nve_ecn_init(struct mlxsw_sp * mlxsw_sp)10916e6030bdSIdo Schimmel static int mlxsw_sp_nve_ecn_init(struct mlxsw_sp *mlxsw_sp)
10926e6030bdSIdo Schimmel {
10936e6030bdSIdo Schimmel 	int err;
10946e6030bdSIdo Schimmel 
10956e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_ecn_encap_init(mlxsw_sp);
10966e6030bdSIdo Schimmel 	if (err)
10976e6030bdSIdo Schimmel 		return err;
10986e6030bdSIdo Schimmel 
10996e6030bdSIdo Schimmel 	return mlxsw_sp_nve_ecn_decap_init(mlxsw_sp);
11006e6030bdSIdo Schimmel }
11016e6030bdSIdo Schimmel 
mlxsw_sp_nve_resources_query(struct mlxsw_sp * mlxsw_sp)11026e6030bdSIdo Schimmel static int mlxsw_sp_nve_resources_query(struct mlxsw_sp *mlxsw_sp)
11036e6030bdSIdo Schimmel {
11046e6030bdSIdo Schimmel 	unsigned int max;
11056e6030bdSIdo Schimmel 
11066e6030bdSIdo Schimmel 	if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4) ||
11076e6030bdSIdo Schimmel 	    !MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6))
11086e6030bdSIdo Schimmel 		return -EIO;
11096e6030bdSIdo Schimmel 	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV4);
11106e6030bdSIdo Schimmel 	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV4] = max;
11116e6030bdSIdo Schimmel 	max = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_MC_ENTRIES_IPV6);
11126e6030bdSIdo Schimmel 	mlxsw_sp->nve->num_max_mc_entries[MLXSW_SP_L3_PROTO_IPV6] = max;
11136e6030bdSIdo Schimmel 
11146e6030bdSIdo Schimmel 	return 0;
11156e6030bdSIdo Schimmel }
11166e6030bdSIdo Schimmel 
mlxsw_sp_nve_init(struct mlxsw_sp * mlxsw_sp)11176e6030bdSIdo Schimmel int mlxsw_sp_nve_init(struct mlxsw_sp *mlxsw_sp)
11186e6030bdSIdo Schimmel {
11196e6030bdSIdo Schimmel 	struct mlxsw_sp_nve *nve;
11206e6030bdSIdo Schimmel 	int err;
11216e6030bdSIdo Schimmel 
11226e6030bdSIdo Schimmel 	nve = kzalloc(sizeof(*mlxsw_sp->nve), GFP_KERNEL);
11236e6030bdSIdo Schimmel 	if (!nve)
11246e6030bdSIdo Schimmel 		return -ENOMEM;
11256e6030bdSIdo Schimmel 	mlxsw_sp->nve = nve;
11266e6030bdSIdo Schimmel 	nve->mlxsw_sp = mlxsw_sp;
11276e6030bdSIdo Schimmel 	nve->nve_ops_arr = mlxsw_sp->nve_ops_arr;
11286e6030bdSIdo Schimmel 
11296e6030bdSIdo Schimmel 	err = rhashtable_init(&nve->mc_list_ht,
11306e6030bdSIdo Schimmel 			      &mlxsw_sp_nve_mc_list_ht_params);
11316e6030bdSIdo Schimmel 	if (err)
11320860c764SAmit Cohen 		goto err_mc_rhashtable_init;
11330860c764SAmit Cohen 
11340860c764SAmit Cohen 	err = rhashtable_init(&nve->ipv6_ht, &mlxsw_sp_nve_ipv6_ht_params);
11350860c764SAmit Cohen 	if (err)
11360860c764SAmit Cohen 		goto err_ipv6_rhashtable_init;
11370860c764SAmit Cohen 
11380860c764SAmit Cohen 	INIT_LIST_HEAD(&nve->ipv6_addr_list);
11396e6030bdSIdo Schimmel 
11406e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_qos_init(mlxsw_sp);
11416e6030bdSIdo Schimmel 	if (err)
11426e6030bdSIdo Schimmel 		goto err_nve_qos_init;
11436e6030bdSIdo Schimmel 
11446e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_ecn_init(mlxsw_sp);
11456e6030bdSIdo Schimmel 	if (err)
11466e6030bdSIdo Schimmel 		goto err_nve_ecn_init;
11476e6030bdSIdo Schimmel 
11486e6030bdSIdo Schimmel 	err = mlxsw_sp_nve_resources_query(mlxsw_sp);
11496e6030bdSIdo Schimmel 	if (err)
11506e6030bdSIdo Schimmel 		goto err_nve_resources_query;
11516e6030bdSIdo Schimmel 
11526e6030bdSIdo Schimmel 	return 0;
11536e6030bdSIdo Schimmel 
11546e6030bdSIdo Schimmel err_nve_resources_query:
11556e6030bdSIdo Schimmel err_nve_ecn_init:
11566e6030bdSIdo Schimmel err_nve_qos_init:
11570860c764SAmit Cohen 	rhashtable_destroy(&nve->ipv6_ht);
11580860c764SAmit Cohen err_ipv6_rhashtable_init:
11596e6030bdSIdo Schimmel 	rhashtable_destroy(&nve->mc_list_ht);
11600860c764SAmit Cohen err_mc_rhashtable_init:
11616e6030bdSIdo Schimmel 	mlxsw_sp->nve = NULL;
11626e6030bdSIdo Schimmel 	kfree(nve);
11636e6030bdSIdo Schimmel 	return err;
11646e6030bdSIdo Schimmel }
11656e6030bdSIdo Schimmel 
mlxsw_sp_nve_fini(struct mlxsw_sp * mlxsw_sp)11666e6030bdSIdo Schimmel void mlxsw_sp_nve_fini(struct mlxsw_sp *mlxsw_sp)
11676e6030bdSIdo Schimmel {
11686e6030bdSIdo Schimmel 	WARN_ON(mlxsw_sp->nve->num_nve_tunnels);
11690860c764SAmit Cohen 	WARN_ON(!list_empty(&mlxsw_sp->nve->ipv6_addr_list));
11700860c764SAmit Cohen 	rhashtable_destroy(&mlxsw_sp->nve->ipv6_ht);
11716e6030bdSIdo Schimmel 	rhashtable_destroy(&mlxsw_sp->nve->mc_list_ht);
11726e6030bdSIdo Schimmel 	kfree(mlxsw_sp->nve);
11735edb7e8bSIdo Schimmel 	mlxsw_sp->nve = NULL;
11746e6030bdSIdo Schimmel }
1175