119e9bfa0SVlad Buslov // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
219e9bfa0SVlad Buslov /* Copyright (c) 2021 Mellanox Technologies. */
319e9bfa0SVlad Buslov 
455d3654cSVlad Buslov #include <linux/build_bug.h>
519e9bfa0SVlad Buslov #include <linux/list.h>
69724fd5dSVlad Buslov #include <linux/notifier.h>
79724fd5dSVlad Buslov #include <net/netevent.h>
819e9bfa0SVlad Buslov #include <net/switchdev.h>
9ff9b7521SVlad Buslov #include "lib/devcom.h"
1019e9bfa0SVlad Buslov #include "bridge.h"
1119e9bfa0SVlad Buslov #include "eswitch.h"
129724fd5dSVlad Buslov #include "bridge_priv.h"
139724fd5dSVlad Buslov #define CREATE_TRACE_POINTS
149724fd5dSVlad Buslov #include "diag/bridge_tracepoint.h"
1519e9bfa0SVlad Buslov 
167cd6a54aSVlad Buslov static const struct rhashtable_params fdb_ht_params = {
177cd6a54aSVlad Buslov 	.key_offset = offsetof(struct mlx5_esw_bridge_fdb_entry, key),
187cd6a54aSVlad Buslov 	.key_len = sizeof(struct mlx5_esw_bridge_fdb_key),
197cd6a54aSVlad Buslov 	.head_offset = offsetof(struct mlx5_esw_bridge_fdb_entry, ht_node),
207cd6a54aSVlad Buslov 	.automatic_shrinking = true,
217cd6a54aSVlad Buslov };
227cd6a54aSVlad Buslov 
237cd6a54aSVlad Buslov static void
mlx5_esw_bridge_fdb_offload_notify(struct net_device * dev,const unsigned char * addr,u16 vid,unsigned long val)247cd6a54aSVlad Buslov mlx5_esw_bridge_fdb_offload_notify(struct net_device *dev, const unsigned char *addr, u16 vid,
257cd6a54aSVlad Buslov 				   unsigned long val)
267cd6a54aSVlad Buslov {
27c35b57ceSVladimir Oltean 	struct switchdev_notifier_fdb_info send_info = {};
287cd6a54aSVlad Buslov 
297cd6a54aSVlad Buslov 	send_info.addr = addr;
307cd6a54aSVlad Buslov 	send_info.vid = vid;
317cd6a54aSVlad Buslov 	send_info.offloaded = true;
327cd6a54aSVlad Buslov 	call_switchdev_notifiers(val, dev, &send_info.info, NULL);
337cd6a54aSVlad Buslov }
347cd6a54aSVlad Buslov 
35bf3d56d8SVlad Buslov static void
mlx5_esw_bridge_fdb_del_notify(struct mlx5_esw_bridge_fdb_entry * entry)36bf3d56d8SVlad Buslov mlx5_esw_bridge_fdb_del_notify(struct mlx5_esw_bridge_fdb_entry *entry)
37bf3d56d8SVlad Buslov {
38c358ea17SVlad Buslov 	if (!(entry->flags & (MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER | MLX5_ESW_BRIDGE_FLAG_PEER)))
39bf3d56d8SVlad Buslov 		mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr,
40bf3d56d8SVlad Buslov 						   entry->key.vid,
41bf3d56d8SVlad Buslov 						   SWITCHDEV_FDB_DEL_TO_BRIDGE);
42bf3d56d8SVlad Buslov }
43bf3d56d8SVlad Buslov 
mlx5_esw_bridge_pkt_reformat_vlan_pop_supported(struct mlx5_eswitch * esw)4464fc4b35SVlad Buslov static bool mlx5_esw_bridge_pkt_reformat_vlan_pop_supported(struct mlx5_eswitch *esw)
4564fc4b35SVlad Buslov {
4664fc4b35SVlad Buslov 	return BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, reformat_remove)) &&
4764fc4b35SVlad Buslov 		MLX5_CAP_GEN_2(esw->dev, max_reformat_remove_size) >= sizeof(struct vlan_hdr) &&
4864fc4b35SVlad Buslov 		MLX5_CAP_GEN_2(esw->dev, max_reformat_remove_offset) >=
4964fc4b35SVlad Buslov 		offsetof(struct vlan_ethhdr, h_vlan_proto);
5064fc4b35SVlad Buslov }
5164fc4b35SVlad Buslov 
5264fc4b35SVlad Buslov static struct mlx5_pkt_reformat *
mlx5_esw_bridge_pkt_reformat_vlan_pop_create(struct mlx5_eswitch * esw)5364fc4b35SVlad Buslov mlx5_esw_bridge_pkt_reformat_vlan_pop_create(struct mlx5_eswitch *esw)
5464fc4b35SVlad Buslov {
5564fc4b35SVlad Buslov 	struct mlx5_pkt_reformat_params reformat_params = {};
5664fc4b35SVlad Buslov 
5764fc4b35SVlad Buslov 	reformat_params.type = MLX5_REFORMAT_TYPE_REMOVE_HDR;
5864fc4b35SVlad Buslov 	reformat_params.param_0 = MLX5_REFORMAT_CONTEXT_ANCHOR_MAC_START;
5964fc4b35SVlad Buslov 	reformat_params.param_1 = offsetof(struct vlan_ethhdr, h_vlan_proto);
6064fc4b35SVlad Buslov 	reformat_params.size = sizeof(struct vlan_hdr);
6164fc4b35SVlad Buslov 	return mlx5_packet_reformat_alloc(esw->dev, &reformat_params, MLX5_FLOW_NAMESPACE_FDB);
6264fc4b35SVlad Buslov }
6364fc4b35SVlad Buslov 
64272ecfc9SVlad Buslov struct mlx5_flow_table *
mlx5_esw_bridge_table_create(int max_fte,u32 level,struct mlx5_eswitch * esw)6519e9bfa0SVlad Buslov mlx5_esw_bridge_table_create(int max_fte, u32 level, struct mlx5_eswitch *esw)
6619e9bfa0SVlad Buslov {
6719e9bfa0SVlad Buslov 	struct mlx5_flow_table_attr ft_attr = {};
6819e9bfa0SVlad Buslov 	struct mlx5_core_dev *dev = esw->dev;
6919e9bfa0SVlad Buslov 	struct mlx5_flow_namespace *ns;
7019e9bfa0SVlad Buslov 	struct mlx5_flow_table *fdb;
7119e9bfa0SVlad Buslov 
7219e9bfa0SVlad Buslov 	ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
7319e9bfa0SVlad Buslov 	if (!ns) {
7419e9bfa0SVlad Buslov 		esw_warn(dev, "Failed to get FDB namespace\n");
7519e9bfa0SVlad Buslov 		return ERR_PTR(-ENOENT);
7619e9bfa0SVlad Buslov 	}
7719e9bfa0SVlad Buslov 
7836e55079SVlad Buslov 	ft_attr.flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT;
7919e9bfa0SVlad Buslov 	ft_attr.max_fte = max_fte;
8019e9bfa0SVlad Buslov 	ft_attr.level = level;
8119e9bfa0SVlad Buslov 	ft_attr.prio = FDB_BR_OFFLOAD;
8219e9bfa0SVlad Buslov 	fdb = mlx5_create_flow_table(ns, &ft_attr);
8319e9bfa0SVlad Buslov 	if (IS_ERR(fdb))
8419e9bfa0SVlad Buslov 		esw_warn(dev, "Failed to create bridge FDB Table (err=%ld)\n", PTR_ERR(fdb));
8519e9bfa0SVlad Buslov 
8619e9bfa0SVlad Buslov 	return fdb;
8719e9bfa0SVlad Buslov }
8819e9bfa0SVlad Buslov 
8919e9bfa0SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_vlan_proto_fg_create(unsigned int from,unsigned int to,u16 vlan_proto,struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)90c5fcac93SVlad Buslov mlx5_esw_bridge_ingress_vlan_proto_fg_create(unsigned int from, unsigned int to, u16 vlan_proto,
91c5fcac93SVlad Buslov 					     struct mlx5_eswitch *esw,
92cc2987c4SVlad Buslov 					     struct mlx5_flow_table *ingress_ft)
93cc2987c4SVlad Buslov {
94cc2987c4SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
95cc2987c4SVlad Buslov 	struct mlx5_flow_group *fg;
96cc2987c4SVlad Buslov 	u32 *in, *match;
97cc2987c4SVlad Buslov 
98cc2987c4SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
99cc2987c4SVlad Buslov 	if (!in)
100cc2987c4SVlad Buslov 		return ERR_PTR(-ENOMEM);
101cc2987c4SVlad Buslov 
102cc2987c4SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable,
103cc2987c4SVlad Buslov 		 MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_2);
104cc2987c4SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
105cc2987c4SVlad Buslov 
106cc2987c4SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_47_16);
107cc2987c4SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_15_0);
108c5fcac93SVlad Buslov 	if (vlan_proto == ETH_P_8021Q)
109cc2987c4SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.cvlan_tag);
1109c0ca9baSVlad Buslov 	else if (vlan_proto == ETH_P_8021AD)
1119c0ca9baSVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.svlan_tag);
112c5fcac93SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.first_vid);
113cc2987c4SVlad Buslov 
114cc2987c4SVlad Buslov 	MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_0,
115cc2987c4SVlad Buslov 		 mlx5_eswitch_get_vport_metadata_mask());
116cc2987c4SVlad Buslov 
117c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index, from);
118c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index, to);
119c5fcac93SVlad Buslov 
120c5fcac93SVlad Buslov 	fg = mlx5_create_flow_group(ingress_ft, in);
121c5fcac93SVlad Buslov 	kvfree(in);
122c5fcac93SVlad Buslov 	if (IS_ERR(fg))
123c5fcac93SVlad Buslov 		esw_warn(esw->dev,
124c5fcac93SVlad Buslov 			 "Failed to create VLAN(proto=%x) flow group for bridge ingress table (err=%ld)\n",
125c5fcac93SVlad Buslov 			 vlan_proto, PTR_ERR(fg));
126c5fcac93SVlad Buslov 
127c5fcac93SVlad Buslov 	return fg;
128c5fcac93SVlad Buslov }
129c5fcac93SVlad Buslov 
130c5fcac93SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_vlan_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)131c5fcac93SVlad Buslov mlx5_esw_bridge_ingress_vlan_fg_create(struct mlx5_eswitch *esw,
132c5fcac93SVlad Buslov 				       struct mlx5_flow_table *ingress_ft)
133c5fcac93SVlad Buslov {
134c5fcac93SVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_FROM;
135c5fcac93SVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_GRP_IDX_TO;
136c5fcac93SVlad Buslov 
137c5fcac93SVlad Buslov 	return mlx5_esw_bridge_ingress_vlan_proto_fg_create(from, to, ETH_P_8021Q, esw, ingress_ft);
138c5fcac93SVlad Buslov }
139c5fcac93SVlad Buslov 
140c5fcac93SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_qinq_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)1419c0ca9baSVlad Buslov mlx5_esw_bridge_ingress_qinq_fg_create(struct mlx5_eswitch *esw,
1429c0ca9baSVlad Buslov 				       struct mlx5_flow_table *ingress_ft)
1439c0ca9baSVlad Buslov {
1449c0ca9baSVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_FROM;
1459c0ca9baSVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_GRP_IDX_TO;
1469c0ca9baSVlad Buslov 
1479c0ca9baSVlad Buslov 	return mlx5_esw_bridge_ingress_vlan_proto_fg_create(from, to, ETH_P_8021AD, esw,
1489c0ca9baSVlad Buslov 							    ingress_ft);
1499c0ca9baSVlad Buslov }
1509c0ca9baSVlad Buslov 
1519c0ca9baSVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_vlan_proto_filter_fg_create(unsigned int from,unsigned int to,u16 vlan_proto,struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)152c5fcac93SVlad Buslov mlx5_esw_bridge_ingress_vlan_proto_filter_fg_create(unsigned int from, unsigned int to,
153c5fcac93SVlad Buslov 						    u16 vlan_proto, struct mlx5_eswitch *esw,
154c5fcac93SVlad Buslov 						    struct mlx5_flow_table *ingress_ft)
155c5fcac93SVlad Buslov {
156c5fcac93SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
157c5fcac93SVlad Buslov 	struct mlx5_flow_group *fg;
158c5fcac93SVlad Buslov 	u32 *in, *match;
159c5fcac93SVlad Buslov 
160c5fcac93SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
161c5fcac93SVlad Buslov 	if (!in)
162c5fcac93SVlad Buslov 		return ERR_PTR(-ENOMEM);
163c5fcac93SVlad Buslov 
164c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable,
165c5fcac93SVlad Buslov 		 MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_2);
166c5fcac93SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
167c5fcac93SVlad Buslov 
168c5fcac93SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_47_16);
169c5fcac93SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_15_0);
170c5fcac93SVlad Buslov 	if (vlan_proto == ETH_P_8021Q)
171c5fcac93SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.cvlan_tag);
1729c0ca9baSVlad Buslov 	else if (vlan_proto == ETH_P_8021AD)
1739c0ca9baSVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.svlan_tag);
174c5fcac93SVlad Buslov 	MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_0,
175c5fcac93SVlad Buslov 		 mlx5_eswitch_get_vport_metadata_mask());
176c5fcac93SVlad Buslov 
177c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index, from);
178c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index, to);
179cc2987c4SVlad Buslov 
180cc2987c4SVlad Buslov 	fg = mlx5_create_flow_group(ingress_ft, in);
181cc2987c4SVlad Buslov 	if (IS_ERR(fg))
182cc2987c4SVlad Buslov 		esw_warn(esw->dev,
183cc2987c4SVlad Buslov 			 "Failed to create bridge ingress table VLAN filter flow group (err=%ld)\n",
184cc2987c4SVlad Buslov 			 PTR_ERR(fg));
185cc2987c4SVlad Buslov 	kvfree(in);
186cc2987c4SVlad Buslov 	return fg;
187cc2987c4SVlad Buslov }
188cc2987c4SVlad Buslov 
189cc2987c4SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_vlan_filter_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)190c5fcac93SVlad Buslov mlx5_esw_bridge_ingress_vlan_filter_fg_create(struct mlx5_eswitch *esw,
191c5fcac93SVlad Buslov 					      struct mlx5_flow_table *ingress_ft)
192c5fcac93SVlad Buslov {
193c5fcac93SVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_FROM;
194c5fcac93SVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_INGRESS_TABLE_VLAN_FILTER_GRP_IDX_TO;
195c5fcac93SVlad Buslov 
196c5fcac93SVlad Buslov 	return mlx5_esw_bridge_ingress_vlan_proto_filter_fg_create(from, to, ETH_P_8021Q, esw,
197c5fcac93SVlad Buslov 								   ingress_ft);
198c5fcac93SVlad Buslov }
199c5fcac93SVlad Buslov 
200c5fcac93SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_qinq_filter_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)2019c0ca9baSVlad Buslov mlx5_esw_bridge_ingress_qinq_filter_fg_create(struct mlx5_eswitch *esw,
2029c0ca9baSVlad Buslov 					      struct mlx5_flow_table *ingress_ft)
2039c0ca9baSVlad Buslov {
2049c0ca9baSVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_FROM;
2059c0ca9baSVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_INGRESS_TABLE_QINQ_FILTER_GRP_IDX_TO;
2069c0ca9baSVlad Buslov 
2079c0ca9baSVlad Buslov 	return mlx5_esw_bridge_ingress_vlan_proto_filter_fg_create(from, to, ETH_P_8021AD, esw,
2089c0ca9baSVlad Buslov 								   ingress_ft);
2099c0ca9baSVlad Buslov }
2109c0ca9baSVlad Buslov 
2119c0ca9baSVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_ingress_mac_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * ingress_ft)21219e9bfa0SVlad Buslov mlx5_esw_bridge_ingress_mac_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *ingress_ft)
21319e9bfa0SVlad Buslov {
21419e9bfa0SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
21519e9bfa0SVlad Buslov 	struct mlx5_flow_group *fg;
21619e9bfa0SVlad Buslov 	u32 *in, *match;
21719e9bfa0SVlad Buslov 
21819e9bfa0SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
21919e9bfa0SVlad Buslov 	if (!in)
22019e9bfa0SVlad Buslov 		return ERR_PTR(-ENOMEM);
22119e9bfa0SVlad Buslov 
22219e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable,
22319e9bfa0SVlad Buslov 		 MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_2);
22419e9bfa0SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
22519e9bfa0SVlad Buslov 
22619e9bfa0SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_47_16);
22719e9bfa0SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.smac_15_0);
22819e9bfa0SVlad Buslov 
22919e9bfa0SVlad Buslov 	MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_0,
23019e9bfa0SVlad Buslov 		 mlx5_eswitch_get_vport_metadata_mask());
23119e9bfa0SVlad Buslov 
23219e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index,
23319e9bfa0SVlad Buslov 		 MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_FROM);
23419e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index,
23519e9bfa0SVlad Buslov 		 MLX5_ESW_BRIDGE_INGRESS_TABLE_MAC_GRP_IDX_TO);
23619e9bfa0SVlad Buslov 
23719e9bfa0SVlad Buslov 	fg = mlx5_create_flow_group(ingress_ft, in);
23819e9bfa0SVlad Buslov 	if (IS_ERR(fg))
23919e9bfa0SVlad Buslov 		esw_warn(esw->dev,
240ffc89ee5SVlad Buslov 			 "Failed to create MAC flow group for bridge ingress table (err=%ld)\n",
24119e9bfa0SVlad Buslov 			 PTR_ERR(fg));
24219e9bfa0SVlad Buslov 
24319e9bfa0SVlad Buslov 	kvfree(in);
24419e9bfa0SVlad Buslov 	return fg;
24519e9bfa0SVlad Buslov }
24619e9bfa0SVlad Buslov 
24719e9bfa0SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_egress_vlan_proto_fg_create(unsigned int from,unsigned int to,u16 vlan_proto,struct mlx5_eswitch * esw,struct mlx5_flow_table * egress_ft)248c5fcac93SVlad Buslov mlx5_esw_bridge_egress_vlan_proto_fg_create(unsigned int from, unsigned int to, u16 vlan_proto,
249c5fcac93SVlad Buslov 					    struct mlx5_eswitch *esw,
250c5fcac93SVlad Buslov 					    struct mlx5_flow_table *egress_ft)
251ffc89ee5SVlad Buslov {
252ffc89ee5SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
253ffc89ee5SVlad Buslov 	struct mlx5_flow_group *fg;
254ffc89ee5SVlad Buslov 	u32 *in, *match;
255ffc89ee5SVlad Buslov 
256ffc89ee5SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
257ffc89ee5SVlad Buslov 	if (!in)
258ffc89ee5SVlad Buslov 		return ERR_PTR(-ENOMEM);
259ffc89ee5SVlad Buslov 
260ffc89ee5SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
261ffc89ee5SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
262ffc89ee5SVlad Buslov 
263ffc89ee5SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.dmac_47_16);
264ffc89ee5SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.dmac_15_0);
265c5fcac93SVlad Buslov 	if (vlan_proto == ETH_P_8021Q)
266ffc89ee5SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.cvlan_tag);
2679c0ca9baSVlad Buslov 	else if (vlan_proto == ETH_P_8021AD)
2689c0ca9baSVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.svlan_tag);
269ffc89ee5SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.first_vid);
270ffc89ee5SVlad Buslov 
271c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index, from);
272c5fcac93SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index, to);
273ffc89ee5SVlad Buslov 
274ffc89ee5SVlad Buslov 	fg = mlx5_create_flow_group(egress_ft, in);
275ffc89ee5SVlad Buslov 	if (IS_ERR(fg))
276ffc89ee5SVlad Buslov 		esw_warn(esw->dev,
277ffc89ee5SVlad Buslov 			 "Failed to create VLAN flow group for bridge egress table (err=%ld)\n",
278ffc89ee5SVlad Buslov 			 PTR_ERR(fg));
279ffc89ee5SVlad Buslov 	kvfree(in);
280ffc89ee5SVlad Buslov 	return fg;
281ffc89ee5SVlad Buslov }
282ffc89ee5SVlad Buslov 
283ffc89ee5SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_egress_vlan_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * egress_ft)284c5fcac93SVlad Buslov mlx5_esw_bridge_egress_vlan_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *egress_ft)
285c5fcac93SVlad Buslov {
286c5fcac93SVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_FROM;
287c5fcac93SVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_EGRESS_TABLE_VLAN_GRP_IDX_TO;
288c5fcac93SVlad Buslov 
289c5fcac93SVlad Buslov 	return mlx5_esw_bridge_egress_vlan_proto_fg_create(from, to, ETH_P_8021Q, esw, egress_ft);
290c5fcac93SVlad Buslov }
291c5fcac93SVlad Buslov 
292c5fcac93SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_egress_qinq_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * egress_ft)2939c0ca9baSVlad Buslov mlx5_esw_bridge_egress_qinq_fg_create(struct mlx5_eswitch *esw,
2949c0ca9baSVlad Buslov 				      struct mlx5_flow_table *egress_ft)
2959c0ca9baSVlad Buslov {
2969c0ca9baSVlad Buslov 	unsigned int from = MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_FROM;
2979c0ca9baSVlad Buslov 	unsigned int to = MLX5_ESW_BRIDGE_EGRESS_TABLE_QINQ_GRP_IDX_TO;
2989c0ca9baSVlad Buslov 
2999c0ca9baSVlad Buslov 	return mlx5_esw_bridge_egress_vlan_proto_fg_create(from, to, ETH_P_8021AD, esw, egress_ft);
3009c0ca9baSVlad Buslov }
3019c0ca9baSVlad Buslov 
3029c0ca9baSVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_egress_mac_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * egress_ft)30319e9bfa0SVlad Buslov mlx5_esw_bridge_egress_mac_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *egress_ft)
30419e9bfa0SVlad Buslov {
30519e9bfa0SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
30619e9bfa0SVlad Buslov 	struct mlx5_flow_group *fg;
30719e9bfa0SVlad Buslov 	u32 *in, *match;
30819e9bfa0SVlad Buslov 
30919e9bfa0SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
31019e9bfa0SVlad Buslov 	if (!in)
31119e9bfa0SVlad Buslov 		return ERR_PTR(-ENOMEM);
31219e9bfa0SVlad Buslov 
31319e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
31419e9bfa0SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
31519e9bfa0SVlad Buslov 
31619e9bfa0SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.dmac_47_16);
31719e9bfa0SVlad Buslov 	MLX5_SET_TO_ONES(fte_match_param, match, outer_headers.dmac_15_0);
31819e9bfa0SVlad Buslov 
31919e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index,
32019e9bfa0SVlad Buslov 		 MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_FROM);
32119e9bfa0SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index,
32219e9bfa0SVlad Buslov 		 MLX5_ESW_BRIDGE_EGRESS_TABLE_MAC_GRP_IDX_TO);
32319e9bfa0SVlad Buslov 
32419e9bfa0SVlad Buslov 	fg = mlx5_create_flow_group(egress_ft, in);
32519e9bfa0SVlad Buslov 	if (IS_ERR(fg))
32619e9bfa0SVlad Buslov 		esw_warn(esw->dev,
32719e9bfa0SVlad Buslov 			 "Failed to create bridge egress table MAC flow group (err=%ld)\n",
32819e9bfa0SVlad Buslov 			 PTR_ERR(fg));
32919e9bfa0SVlad Buslov 	kvfree(in);
33019e9bfa0SVlad Buslov 	return fg;
33119e9bfa0SVlad Buslov }
33219e9bfa0SVlad Buslov 
333575baa92SVlad Buslov static struct mlx5_flow_group *
mlx5_esw_bridge_egress_miss_fg_create(struct mlx5_eswitch * esw,struct mlx5_flow_table * egress_ft)334575baa92SVlad Buslov mlx5_esw_bridge_egress_miss_fg_create(struct mlx5_eswitch *esw, struct mlx5_flow_table *egress_ft)
335575baa92SVlad Buslov {
336575baa92SVlad Buslov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
337575baa92SVlad Buslov 	struct mlx5_flow_group *fg;
338575baa92SVlad Buslov 	u32 *in, *match;
339575baa92SVlad Buslov 
340575baa92SVlad Buslov 	in = kvzalloc(inlen, GFP_KERNEL);
341575baa92SVlad Buslov 	if (!in)
342575baa92SVlad Buslov 		return ERR_PTR(-ENOMEM);
343575baa92SVlad Buslov 
344575baa92SVlad Buslov 	MLX5_SET(create_flow_group_in, in, match_criteria_enable, MLX5_MATCH_MISC_PARAMETERS_2);
345575baa92SVlad Buslov 	match = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria);
346575baa92SVlad Buslov 
347575baa92SVlad Buslov 	MLX5_SET(fte_match_param, match, misc_parameters_2.metadata_reg_c_1, ESW_TUN_MASK);
348575baa92SVlad Buslov 
349575baa92SVlad Buslov 	MLX5_SET(create_flow_group_in, in, start_flow_index,
350575baa92SVlad Buslov 		 MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_FROM);
351575baa92SVlad Buslov 	MLX5_SET(create_flow_group_in, in, end_flow_index,
352575baa92SVlad Buslov 		 MLX5_ESW_BRIDGE_EGRESS_TABLE_MISS_GRP_IDX_TO);
353575baa92SVlad Buslov 
354575baa92SVlad Buslov 	fg = mlx5_create_flow_group(egress_ft, in);
355575baa92SVlad Buslov 	if (IS_ERR(fg))
356575baa92SVlad Buslov 		esw_warn(esw->dev,
357575baa92SVlad Buslov 			 "Failed to create bridge egress table miss flow group (err=%ld)\n",
358575baa92SVlad Buslov 			 PTR_ERR(fg));
359575baa92SVlad Buslov 	kvfree(in);
360575baa92SVlad Buslov 	return fg;
361575baa92SVlad Buslov }
362575baa92SVlad Buslov 
36319e9bfa0SVlad Buslov static int
mlx5_esw_bridge_ingress_table_init(struct mlx5_esw_bridge_offloads * br_offloads)36419e9bfa0SVlad Buslov mlx5_esw_bridge_ingress_table_init(struct mlx5_esw_bridge_offloads *br_offloads)
36519e9bfa0SVlad Buslov {
3669c0ca9baSVlad Buslov 	struct mlx5_flow_group *mac_fg, *qinq_filter_fg, *qinq_fg, *vlan_filter_fg, *vlan_fg;
367cc2987c4SVlad Buslov 	struct mlx5_flow_table *ingress_ft, *skip_ft;
368a1a6e721SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
36919e9bfa0SVlad Buslov 	int err;
37019e9bfa0SVlad Buslov 
371a1a6e721SVlad Buslov 	if (!mlx5_eswitch_vport_match_metadata_enabled(esw))
3727cd6a54aSVlad Buslov 		return -EOPNOTSUPP;
3737cd6a54aSVlad Buslov 
37419e9bfa0SVlad Buslov 	ingress_ft = mlx5_esw_bridge_table_create(MLX5_ESW_BRIDGE_INGRESS_TABLE_SIZE,
37519e9bfa0SVlad Buslov 						  MLX5_ESW_BRIDGE_LEVEL_INGRESS_TABLE,
376a1a6e721SVlad Buslov 						  esw);
37719e9bfa0SVlad Buslov 	if (IS_ERR(ingress_ft))
37819e9bfa0SVlad Buslov 		return PTR_ERR(ingress_ft);
37919e9bfa0SVlad Buslov 
380cc2987c4SVlad Buslov 	skip_ft = mlx5_esw_bridge_table_create(MLX5_ESW_BRIDGE_SKIP_TABLE_SIZE,
381cc2987c4SVlad Buslov 					       MLX5_ESW_BRIDGE_LEVEL_SKIP_TABLE,
382a1a6e721SVlad Buslov 					       esw);
383cc2987c4SVlad Buslov 	if (IS_ERR(skip_ft)) {
384cc2987c4SVlad Buslov 		err = PTR_ERR(skip_ft);
385cc2987c4SVlad Buslov 		goto err_skip_tbl;
386cc2987c4SVlad Buslov 	}
387cc2987c4SVlad Buslov 
388a1a6e721SVlad Buslov 	vlan_fg = mlx5_esw_bridge_ingress_vlan_fg_create(esw, ingress_ft);
389ffc89ee5SVlad Buslov 	if (IS_ERR(vlan_fg)) {
390ffc89ee5SVlad Buslov 		err = PTR_ERR(vlan_fg);
391ffc89ee5SVlad Buslov 		goto err_vlan_fg;
392ffc89ee5SVlad Buslov 	}
393ffc89ee5SVlad Buslov 
394d4893978SVlad Buslov 	vlan_filter_fg = mlx5_esw_bridge_ingress_vlan_filter_fg_create(esw, ingress_ft);
395d4893978SVlad Buslov 	if (IS_ERR(vlan_filter_fg)) {
396d4893978SVlad Buslov 		err = PTR_ERR(vlan_filter_fg);
397d4893978SVlad Buslov 		goto err_vlan_filter_fg;
398cc2987c4SVlad Buslov 	}
399cc2987c4SVlad Buslov 
4009c0ca9baSVlad Buslov 	qinq_fg = mlx5_esw_bridge_ingress_qinq_fg_create(esw, ingress_ft);
4019c0ca9baSVlad Buslov 	if (IS_ERR(qinq_fg)) {
4029c0ca9baSVlad Buslov 		err = PTR_ERR(qinq_fg);
4039c0ca9baSVlad Buslov 		goto err_qinq_fg;
4049c0ca9baSVlad Buslov 	}
4059c0ca9baSVlad Buslov 
4069c0ca9baSVlad Buslov 	qinq_filter_fg = mlx5_esw_bridge_ingress_qinq_filter_fg_create(esw, ingress_ft);
4079c0ca9baSVlad Buslov 	if (IS_ERR(qinq_filter_fg)) {
4089c0ca9baSVlad Buslov 		err = PTR_ERR(qinq_filter_fg);
4099c0ca9baSVlad Buslov 		goto err_qinq_filter_fg;
4109c0ca9baSVlad Buslov 	}
4119c0ca9baSVlad Buslov 
412a1a6e721SVlad Buslov 	mac_fg = mlx5_esw_bridge_ingress_mac_fg_create(esw, ingress_ft);
41319e9bfa0SVlad Buslov 	if (IS_ERR(mac_fg)) {
41419e9bfa0SVlad Buslov 		err = PTR_ERR(mac_fg);
41519e9bfa0SVlad Buslov 		goto err_mac_fg;
41619e9bfa0SVlad Buslov 	}
41719e9bfa0SVlad Buslov 
41819e9bfa0SVlad Buslov 	br_offloads->ingress_ft = ingress_ft;
419cc2987c4SVlad Buslov 	br_offloads->skip_ft = skip_ft;
420ffc89ee5SVlad Buslov 	br_offloads->ingress_vlan_fg = vlan_fg;
421d4893978SVlad Buslov 	br_offloads->ingress_vlan_filter_fg = vlan_filter_fg;
4229c0ca9baSVlad Buslov 	br_offloads->ingress_qinq_fg = qinq_fg;
4239c0ca9baSVlad Buslov 	br_offloads->ingress_qinq_filter_fg = qinq_filter_fg;
42419e9bfa0SVlad Buslov 	br_offloads->ingress_mac_fg = mac_fg;
42519e9bfa0SVlad Buslov 	return 0;
42619e9bfa0SVlad Buslov 
42719e9bfa0SVlad Buslov err_mac_fg:
4289c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(qinq_filter_fg);
4299c0ca9baSVlad Buslov err_qinq_filter_fg:
4309c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(qinq_fg);
4319c0ca9baSVlad Buslov err_qinq_fg:
432d4893978SVlad Buslov 	mlx5_destroy_flow_group(vlan_filter_fg);
433d4893978SVlad Buslov err_vlan_filter_fg:
434ffc89ee5SVlad Buslov 	mlx5_destroy_flow_group(vlan_fg);
435ffc89ee5SVlad Buslov err_vlan_fg:
436cc2987c4SVlad Buslov 	mlx5_destroy_flow_table(skip_ft);
437cc2987c4SVlad Buslov err_skip_tbl:
43819e9bfa0SVlad Buslov 	mlx5_destroy_flow_table(ingress_ft);
43919e9bfa0SVlad Buslov 	return err;
44019e9bfa0SVlad Buslov }
44119e9bfa0SVlad Buslov 
44219e9bfa0SVlad Buslov static void
mlx5_esw_bridge_ingress_table_cleanup(struct mlx5_esw_bridge_offloads * br_offloads)44319e9bfa0SVlad Buslov mlx5_esw_bridge_ingress_table_cleanup(struct mlx5_esw_bridge_offloads *br_offloads)
44419e9bfa0SVlad Buslov {
44519e9bfa0SVlad Buslov 	mlx5_destroy_flow_group(br_offloads->ingress_mac_fg);
44619e9bfa0SVlad Buslov 	br_offloads->ingress_mac_fg = NULL;
4479c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(br_offloads->ingress_qinq_filter_fg);
4489c0ca9baSVlad Buslov 	br_offloads->ingress_qinq_filter_fg = NULL;
4499c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(br_offloads->ingress_qinq_fg);
4509c0ca9baSVlad Buslov 	br_offloads->ingress_qinq_fg = NULL;
451d4893978SVlad Buslov 	mlx5_destroy_flow_group(br_offloads->ingress_vlan_filter_fg);
452d4893978SVlad Buslov 	br_offloads->ingress_vlan_filter_fg = NULL;
453ffc89ee5SVlad Buslov 	mlx5_destroy_flow_group(br_offloads->ingress_vlan_fg);
454ffc89ee5SVlad Buslov 	br_offloads->ingress_vlan_fg = NULL;
455cc2987c4SVlad Buslov 	mlx5_destroy_flow_table(br_offloads->skip_ft);
456cc2987c4SVlad Buslov 	br_offloads->skip_ft = NULL;
45719e9bfa0SVlad Buslov 	mlx5_destroy_flow_table(br_offloads->ingress_ft);
45819e9bfa0SVlad Buslov 	br_offloads->ingress_ft = NULL;
45919e9bfa0SVlad Buslov }
46019e9bfa0SVlad Buslov 
461575baa92SVlad Buslov static struct mlx5_flow_handle *
462575baa92SVlad Buslov mlx5_esw_bridge_egress_miss_flow_create(struct mlx5_flow_table *egress_ft,
463575baa92SVlad Buslov 					struct mlx5_flow_table *skip_ft,
464575baa92SVlad Buslov 					struct mlx5_pkt_reformat *pkt_reformat);
465575baa92SVlad Buslov 
46619e9bfa0SVlad Buslov static int
mlx5_esw_bridge_egress_table_init(struct mlx5_esw_bridge_offloads * br_offloads,struct mlx5_esw_bridge * bridge)46719e9bfa0SVlad Buslov mlx5_esw_bridge_egress_table_init(struct mlx5_esw_bridge_offloads *br_offloads,
46819e9bfa0SVlad Buslov 				  struct mlx5_esw_bridge *bridge)
46919e9bfa0SVlad Buslov {
4709c0ca9baSVlad Buslov 	struct mlx5_flow_group *miss_fg = NULL, *mac_fg, *vlan_fg, *qinq_fg;
471575baa92SVlad Buslov 	struct mlx5_pkt_reformat *miss_pkt_reformat = NULL;
472575baa92SVlad Buslov 	struct mlx5_flow_handle *miss_handle = NULL;
473a1a6e721SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
47419e9bfa0SVlad Buslov 	struct mlx5_flow_table *egress_ft;
47519e9bfa0SVlad Buslov 	int err;
47619e9bfa0SVlad Buslov 
47719e9bfa0SVlad Buslov 	egress_ft = mlx5_esw_bridge_table_create(MLX5_ESW_BRIDGE_EGRESS_TABLE_SIZE,
47819e9bfa0SVlad Buslov 						 MLX5_ESW_BRIDGE_LEVEL_EGRESS_TABLE,
479a1a6e721SVlad Buslov 						 esw);
48019e9bfa0SVlad Buslov 	if (IS_ERR(egress_ft))
48119e9bfa0SVlad Buslov 		return PTR_ERR(egress_ft);
48219e9bfa0SVlad Buslov 
483a1a6e721SVlad Buslov 	vlan_fg = mlx5_esw_bridge_egress_vlan_fg_create(esw, egress_ft);
484ffc89ee5SVlad Buslov 	if (IS_ERR(vlan_fg)) {
485ffc89ee5SVlad Buslov 		err = PTR_ERR(vlan_fg);
486ffc89ee5SVlad Buslov 		goto err_vlan_fg;
487ffc89ee5SVlad Buslov 	}
488ffc89ee5SVlad Buslov 
4899c0ca9baSVlad Buslov 	qinq_fg = mlx5_esw_bridge_egress_qinq_fg_create(esw, egress_ft);
4909c0ca9baSVlad Buslov 	if (IS_ERR(qinq_fg)) {
4919c0ca9baSVlad Buslov 		err = PTR_ERR(qinq_fg);
4929c0ca9baSVlad Buslov 		goto err_qinq_fg;
4939c0ca9baSVlad Buslov 	}
4949c0ca9baSVlad Buslov 
495a1a6e721SVlad Buslov 	mac_fg = mlx5_esw_bridge_egress_mac_fg_create(esw, egress_ft);
49619e9bfa0SVlad Buslov 	if (IS_ERR(mac_fg)) {
49719e9bfa0SVlad Buslov 		err = PTR_ERR(mac_fg);
49819e9bfa0SVlad Buslov 		goto err_mac_fg;
49919e9bfa0SVlad Buslov 	}
50019e9bfa0SVlad Buslov 
501575baa92SVlad Buslov 	if (mlx5_esw_bridge_pkt_reformat_vlan_pop_supported(esw)) {
502575baa92SVlad Buslov 		miss_fg = mlx5_esw_bridge_egress_miss_fg_create(esw, egress_ft);
503575baa92SVlad Buslov 		if (IS_ERR(miss_fg)) {
504575baa92SVlad Buslov 			esw_warn(esw->dev, "Failed to create miss flow group (err=%ld)\n",
505575baa92SVlad Buslov 				 PTR_ERR(miss_fg));
506575baa92SVlad Buslov 			miss_fg = NULL;
507575baa92SVlad Buslov 			goto skip_miss_flow;
508575baa92SVlad Buslov 		}
509575baa92SVlad Buslov 
510575baa92SVlad Buslov 		miss_pkt_reformat = mlx5_esw_bridge_pkt_reformat_vlan_pop_create(esw);
511575baa92SVlad Buslov 		if (IS_ERR(miss_pkt_reformat)) {
512575baa92SVlad Buslov 			esw_warn(esw->dev,
513575baa92SVlad Buslov 				 "Failed to alloc packet reformat REMOVE_HEADER (err=%ld)\n",
514575baa92SVlad Buslov 				 PTR_ERR(miss_pkt_reformat));
515575baa92SVlad Buslov 			miss_pkt_reformat = NULL;
516575baa92SVlad Buslov 			mlx5_destroy_flow_group(miss_fg);
517575baa92SVlad Buslov 			miss_fg = NULL;
518575baa92SVlad Buslov 			goto skip_miss_flow;
519575baa92SVlad Buslov 		}
520575baa92SVlad Buslov 
521575baa92SVlad Buslov 		miss_handle = mlx5_esw_bridge_egress_miss_flow_create(egress_ft,
522575baa92SVlad Buslov 								      br_offloads->skip_ft,
523575baa92SVlad Buslov 								      miss_pkt_reformat);
524575baa92SVlad Buslov 		if (IS_ERR(miss_handle)) {
525575baa92SVlad Buslov 			esw_warn(esw->dev, "Failed to create miss flow (err=%ld)\n",
526575baa92SVlad Buslov 				 PTR_ERR(miss_handle));
527575baa92SVlad Buslov 			miss_handle = NULL;
528575baa92SVlad Buslov 			mlx5_packet_reformat_dealloc(esw->dev, miss_pkt_reformat);
529575baa92SVlad Buslov 			miss_pkt_reformat = NULL;
530575baa92SVlad Buslov 			mlx5_destroy_flow_group(miss_fg);
531575baa92SVlad Buslov 			miss_fg = NULL;
532575baa92SVlad Buslov 			goto skip_miss_flow;
533575baa92SVlad Buslov 		}
534575baa92SVlad Buslov 	}
535575baa92SVlad Buslov skip_miss_flow:
536575baa92SVlad Buslov 
53719e9bfa0SVlad Buslov 	bridge->egress_ft = egress_ft;
538ffc89ee5SVlad Buslov 	bridge->egress_vlan_fg = vlan_fg;
5399c0ca9baSVlad Buslov 	bridge->egress_qinq_fg = qinq_fg;
54019e9bfa0SVlad Buslov 	bridge->egress_mac_fg = mac_fg;
541575baa92SVlad Buslov 	bridge->egress_miss_fg = miss_fg;
542575baa92SVlad Buslov 	bridge->egress_miss_pkt_reformat = miss_pkt_reformat;
543575baa92SVlad Buslov 	bridge->egress_miss_handle = miss_handle;
54419e9bfa0SVlad Buslov 	return 0;
54519e9bfa0SVlad Buslov 
54619e9bfa0SVlad Buslov err_mac_fg:
5479c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(qinq_fg);
5489c0ca9baSVlad Buslov err_qinq_fg:
549ffc89ee5SVlad Buslov 	mlx5_destroy_flow_group(vlan_fg);
550ffc89ee5SVlad Buslov err_vlan_fg:
55119e9bfa0SVlad Buslov 	mlx5_destroy_flow_table(egress_ft);
55219e9bfa0SVlad Buslov 	return err;
55319e9bfa0SVlad Buslov }
55419e9bfa0SVlad Buslov 
55519e9bfa0SVlad Buslov static void
mlx5_esw_bridge_egress_table_cleanup(struct mlx5_esw_bridge * bridge)55619e9bfa0SVlad Buslov mlx5_esw_bridge_egress_table_cleanup(struct mlx5_esw_bridge *bridge)
55719e9bfa0SVlad Buslov {
558575baa92SVlad Buslov 	if (bridge->egress_miss_handle)
559575baa92SVlad Buslov 		mlx5_del_flow_rules(bridge->egress_miss_handle);
560575baa92SVlad Buslov 	if (bridge->egress_miss_pkt_reformat)
561575baa92SVlad Buslov 		mlx5_packet_reformat_dealloc(bridge->br_offloads->esw->dev,
562575baa92SVlad Buslov 					     bridge->egress_miss_pkt_reformat);
563575baa92SVlad Buslov 	if (bridge->egress_miss_fg)
564575baa92SVlad Buslov 		mlx5_destroy_flow_group(bridge->egress_miss_fg);
56519e9bfa0SVlad Buslov 	mlx5_destroy_flow_group(bridge->egress_mac_fg);
5669c0ca9baSVlad Buslov 	mlx5_destroy_flow_group(bridge->egress_qinq_fg);
567ffc89ee5SVlad Buslov 	mlx5_destroy_flow_group(bridge->egress_vlan_fg);
56819e9bfa0SVlad Buslov 	mlx5_destroy_flow_table(bridge->egress_ft);
56919e9bfa0SVlad Buslov }
57019e9bfa0SVlad Buslov 
5717cd6a54aSVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_flow_with_esw_create(u16 vport_num,const unsigned char * addr,struct mlx5_esw_bridge_vlan * vlan,u32 counter_id,struct mlx5_esw_bridge * bridge,struct mlx5_eswitch * esw)572ff9b7521SVlad Buslov mlx5_esw_bridge_ingress_flow_with_esw_create(u16 vport_num, const unsigned char *addr,
573ffc89ee5SVlad Buslov 					     struct mlx5_esw_bridge_vlan *vlan, u32 counter_id,
574ff9b7521SVlad Buslov 					     struct mlx5_esw_bridge *bridge,
575ff9b7521SVlad Buslov 					     struct mlx5_eswitch *esw)
5767cd6a54aSVlad Buslov {
5777cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
5787cd6a54aSVlad Buslov 	struct mlx5_flow_act flow_act = {
579c636a0f0SVlad Buslov 		.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_COUNT,
5807cd6a54aSVlad Buslov 		.flags = FLOW_ACT_NO_APPEND,
5817cd6a54aSVlad Buslov 	};
582c636a0f0SVlad Buslov 	struct mlx5_flow_destination dests[2] = {};
5837cd6a54aSVlad Buslov 	struct mlx5_flow_spec *rule_spec;
5847cd6a54aSVlad Buslov 	struct mlx5_flow_handle *handle;
5857cd6a54aSVlad Buslov 	u8 *smac_v, *smac_c;
5867cd6a54aSVlad Buslov 
5877cd6a54aSVlad Buslov 	rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
5887cd6a54aSVlad Buslov 	if (!rule_spec)
5897cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
5907cd6a54aSVlad Buslov 
5917cd6a54aSVlad Buslov 	rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_2;
5927cd6a54aSVlad Buslov 
5937cd6a54aSVlad Buslov 	smac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value,
5947cd6a54aSVlad Buslov 			      outer_headers.smac_47_16);
5957cd6a54aSVlad Buslov 	ether_addr_copy(smac_v, addr);
5967cd6a54aSVlad Buslov 	smac_c = MLX5_ADDR_OF(fte_match_param, rule_spec->match_criteria,
5977cd6a54aSVlad Buslov 			      outer_headers.smac_47_16);
5987cd6a54aSVlad Buslov 	eth_broadcast_addr(smac_c);
5997cd6a54aSVlad Buslov 
6007cd6a54aSVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_criteria,
6017cd6a54aSVlad Buslov 		 misc_parameters_2.metadata_reg_c_0, mlx5_eswitch_get_vport_metadata_mask());
6027cd6a54aSVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_2.metadata_reg_c_0,
603ff9b7521SVlad Buslov 		 mlx5_eswitch_get_vport_metadata_for_match(esw, vport_num));
6047cd6a54aSVlad Buslov 
60536e55079SVlad Buslov 	if (vlan && vlan->pkt_reformat_push) {
6065249001dSVlad Buslov 		flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT |
6075249001dSVlad Buslov 			MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
60836e55079SVlad Buslov 		flow_act.pkt_reformat = vlan->pkt_reformat_push;
6095249001dSVlad Buslov 		flow_act.modify_hdr = vlan->pkt_mod_hdr_push_mark;
61036e55079SVlad Buslov 	} else if (vlan) {
611c5fcac93SVlad Buslov 		if (bridge->vlan_proto == ETH_P_8021Q) {
612ffc89ee5SVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
613ffc89ee5SVlad Buslov 					 outer_headers.cvlan_tag);
614ffc89ee5SVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
615ffc89ee5SVlad Buslov 					 outer_headers.cvlan_tag);
6169c0ca9baSVlad Buslov 		} else if (bridge->vlan_proto == ETH_P_8021AD) {
6179c0ca9baSVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
6189c0ca9baSVlad Buslov 					 outer_headers.svlan_tag);
6199c0ca9baSVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
6209c0ca9baSVlad Buslov 					 outer_headers.svlan_tag);
621c5fcac93SVlad Buslov 		}
622ffc89ee5SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
623ffc89ee5SVlad Buslov 				 outer_headers.first_vid);
624ffc89ee5SVlad Buslov 		MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid,
625ffc89ee5SVlad Buslov 			 vlan->vid);
626ffc89ee5SVlad Buslov 	}
627ffc89ee5SVlad Buslov 
628c636a0f0SVlad Buslov 	dests[0].type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
629c636a0f0SVlad Buslov 	dests[0].ft = bridge->egress_ft;
630c636a0f0SVlad Buslov 	dests[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER;
631c636a0f0SVlad Buslov 	dests[1].counter_id = counter_id;
632c636a0f0SVlad Buslov 
633c636a0f0SVlad Buslov 	handle = mlx5_add_flow_rules(br_offloads->ingress_ft, rule_spec, &flow_act, dests,
634c636a0f0SVlad Buslov 				     ARRAY_SIZE(dests));
6357cd6a54aSVlad Buslov 
6367cd6a54aSVlad Buslov 	kvfree(rule_spec);
6377cd6a54aSVlad Buslov 	return handle;
6387cd6a54aSVlad Buslov }
6397cd6a54aSVlad Buslov 
6407cd6a54aSVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_flow_create(u16 vport_num,const unsigned char * addr,struct mlx5_esw_bridge_vlan * vlan,u32 counter_id,struct mlx5_esw_bridge * bridge)641ff9b7521SVlad Buslov mlx5_esw_bridge_ingress_flow_create(u16 vport_num, const unsigned char *addr,
642ff9b7521SVlad Buslov 				    struct mlx5_esw_bridge_vlan *vlan, u32 counter_id,
643ff9b7521SVlad Buslov 				    struct mlx5_esw_bridge *bridge)
644ff9b7521SVlad Buslov {
645ff9b7521SVlad Buslov 	return mlx5_esw_bridge_ingress_flow_with_esw_create(vport_num, addr, vlan, counter_id,
646ff9b7521SVlad Buslov 							    bridge, bridge->br_offloads->esw);
647ff9b7521SVlad Buslov }
648ff9b7521SVlad Buslov 
649ff9b7521SVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_flow_peer_create(u16 vport_num,u16 esw_owner_vhca_id,const unsigned char * addr,struct mlx5_esw_bridge_vlan * vlan,u32 counter_id,struct mlx5_esw_bridge * bridge)65090ca127cSSaeed Mahameed mlx5_esw_bridge_ingress_flow_peer_create(u16 vport_num, u16 esw_owner_vhca_id,
65190ca127cSSaeed Mahameed 					 const unsigned char *addr,
652ff9b7521SVlad Buslov 					 struct mlx5_esw_bridge_vlan *vlan, u32 counter_id,
653ff9b7521SVlad Buslov 					 struct mlx5_esw_bridge *bridge)
654ff9b7521SVlad Buslov {
65588d162b4SRoi Dayan 	struct mlx5_devcom_comp_dev *devcom = bridge->br_offloads->esw->devcom, *pos;
65690ca127cSSaeed Mahameed 	struct mlx5_eswitch *tmp, *peer_esw = NULL;
657ff9b7521SVlad Buslov 	static struct mlx5_flow_handle *handle;
658ff9b7521SVlad Buslov 
65988d162b4SRoi Dayan 	if (!mlx5_devcom_for_each_peer_begin(devcom))
660ff9b7521SVlad Buslov 		return ERR_PTR(-ENODEV);
661ff9b7521SVlad Buslov 
66288d162b4SRoi Dayan 	mlx5_devcom_for_each_peer_entry(devcom, tmp, pos) {
66390ca127cSSaeed Mahameed 		if (mlx5_esw_is_owner(tmp, vport_num, esw_owner_vhca_id)) {
66490ca127cSSaeed Mahameed 			peer_esw = tmp;
66590ca127cSSaeed Mahameed 			break;
66690ca127cSSaeed Mahameed 		}
66790ca127cSSaeed Mahameed 	}
66888d162b4SRoi Dayan 
66990ca127cSSaeed Mahameed 	if (!peer_esw) {
67088d162b4SRoi Dayan 		handle = ERR_PTR(-ENODEV);
67188d162b4SRoi Dayan 		goto out;
67290ca127cSSaeed Mahameed 	}
67390ca127cSSaeed Mahameed 
674ff9b7521SVlad Buslov 	handle = mlx5_esw_bridge_ingress_flow_with_esw_create(vport_num, addr, vlan, counter_id,
675ff9b7521SVlad Buslov 							      bridge, peer_esw);
67688d162b4SRoi Dayan 
67788d162b4SRoi Dayan out:
67888d162b4SRoi Dayan 	mlx5_devcom_for_each_peer_end(devcom);
679ff9b7521SVlad Buslov 	return handle;
680ff9b7521SVlad Buslov }
681ff9b7521SVlad Buslov 
682ff9b7521SVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_ingress_filter_flow_create(u16 vport_num,const unsigned char * addr,struct mlx5_esw_bridge * bridge)683cc2987c4SVlad Buslov mlx5_esw_bridge_ingress_filter_flow_create(u16 vport_num, const unsigned char *addr,
684cc2987c4SVlad Buslov 					   struct mlx5_esw_bridge *bridge)
685cc2987c4SVlad Buslov {
686cc2987c4SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
687cc2987c4SVlad Buslov 	struct mlx5_flow_destination dest = {
688cc2987c4SVlad Buslov 		.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
689cc2987c4SVlad Buslov 		.ft = br_offloads->skip_ft,
690cc2987c4SVlad Buslov 	};
691cc2987c4SVlad Buslov 	struct mlx5_flow_act flow_act = {
692cc2987c4SVlad Buslov 		.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
693cc2987c4SVlad Buslov 		.flags = FLOW_ACT_NO_APPEND,
694cc2987c4SVlad Buslov 	};
695cc2987c4SVlad Buslov 	struct mlx5_flow_spec *rule_spec;
696cc2987c4SVlad Buslov 	struct mlx5_flow_handle *handle;
697cc2987c4SVlad Buslov 	u8 *smac_v, *smac_c;
698cc2987c4SVlad Buslov 
699cc2987c4SVlad Buslov 	rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
700cc2987c4SVlad Buslov 	if (!rule_spec)
701cc2987c4SVlad Buslov 		return ERR_PTR(-ENOMEM);
702cc2987c4SVlad Buslov 
703cc2987c4SVlad Buslov 	rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS | MLX5_MATCH_MISC_PARAMETERS_2;
704cc2987c4SVlad Buslov 
705cc2987c4SVlad Buslov 	smac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value,
706cc2987c4SVlad Buslov 			      outer_headers.smac_47_16);
707cc2987c4SVlad Buslov 	ether_addr_copy(smac_v, addr);
708cc2987c4SVlad Buslov 	smac_c = MLX5_ADDR_OF(fte_match_param, rule_spec->match_criteria,
709cc2987c4SVlad Buslov 			      outer_headers.smac_47_16);
710cc2987c4SVlad Buslov 	eth_broadcast_addr(smac_c);
711cc2987c4SVlad Buslov 
712cc2987c4SVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_criteria,
713cc2987c4SVlad Buslov 		 misc_parameters_2.metadata_reg_c_0, mlx5_eswitch_get_vport_metadata_mask());
714cc2987c4SVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_2.metadata_reg_c_0,
715cc2987c4SVlad Buslov 		 mlx5_eswitch_get_vport_metadata_for_match(br_offloads->esw, vport_num));
716cc2987c4SVlad Buslov 
717c5fcac93SVlad Buslov 	if (bridge->vlan_proto == ETH_P_8021Q) {
718cc2987c4SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
719cc2987c4SVlad Buslov 				 outer_headers.cvlan_tag);
720cc2987c4SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
721cc2987c4SVlad Buslov 				 outer_headers.cvlan_tag);
7229c0ca9baSVlad Buslov 	} else if (bridge->vlan_proto == ETH_P_8021AD) {
7239c0ca9baSVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
7249c0ca9baSVlad Buslov 				 outer_headers.svlan_tag);
7259c0ca9baSVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
7269c0ca9baSVlad Buslov 				 outer_headers.svlan_tag);
727c5fcac93SVlad Buslov 	}
728cc2987c4SVlad Buslov 
729cc2987c4SVlad Buslov 	handle = mlx5_add_flow_rules(br_offloads->ingress_ft, rule_spec, &flow_act, &dest, 1);
730cc2987c4SVlad Buslov 
731cc2987c4SVlad Buslov 	kvfree(rule_spec);
732cc2987c4SVlad Buslov 	return handle;
733cc2987c4SVlad Buslov }
734cc2987c4SVlad Buslov 
735cc2987c4SVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_egress_flow_create(u16 vport_num,u16 esw_owner_vhca_id,const unsigned char * addr,struct mlx5_esw_bridge_vlan * vlan,struct mlx5_esw_bridge * bridge)736c358ea17SVlad Buslov mlx5_esw_bridge_egress_flow_create(u16 vport_num, u16 esw_owner_vhca_id, const unsigned char *addr,
737ffc89ee5SVlad Buslov 				   struct mlx5_esw_bridge_vlan *vlan,
7387cd6a54aSVlad Buslov 				   struct mlx5_esw_bridge *bridge)
7397cd6a54aSVlad Buslov {
7407cd6a54aSVlad Buslov 	struct mlx5_flow_destination dest = {
7417cd6a54aSVlad Buslov 		.type = MLX5_FLOW_DESTINATION_TYPE_VPORT,
7427cd6a54aSVlad Buslov 		.vport.num = vport_num,
7437cd6a54aSVlad Buslov 	};
7447cd6a54aSVlad Buslov 	struct mlx5_flow_act flow_act = {
7457cd6a54aSVlad Buslov 		.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
7467cd6a54aSVlad Buslov 		.flags = FLOW_ACT_NO_APPEND,
7477cd6a54aSVlad Buslov 	};
7487cd6a54aSVlad Buslov 	struct mlx5_flow_spec *rule_spec;
7497cd6a54aSVlad Buslov 	struct mlx5_flow_handle *handle;
7507cd6a54aSVlad Buslov 	u8 *dmac_v, *dmac_c;
7517cd6a54aSVlad Buslov 
7527cd6a54aSVlad Buslov 	rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
7537cd6a54aSVlad Buslov 	if (!rule_spec)
7547cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
7557cd6a54aSVlad Buslov 
75617ac528dSVlad Buslov 	if (MLX5_CAP_ESW_FLOWTABLE(bridge->br_offloads->esw->dev, flow_source) &&
75717ac528dSVlad Buslov 	    vport_num == MLX5_VPORT_UPLINK)
75817ac528dSVlad Buslov 		rule_spec->flow_context.flow_source =
75917ac528dSVlad Buslov 			MLX5_FLOW_CONTEXT_FLOW_SOURCE_LOCAL_VPORT;
7607cd6a54aSVlad Buslov 	rule_spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
7617cd6a54aSVlad Buslov 
7627cd6a54aSVlad Buslov 	dmac_v = MLX5_ADDR_OF(fte_match_param, rule_spec->match_value,
7637cd6a54aSVlad Buslov 			      outer_headers.dmac_47_16);
7647cd6a54aSVlad Buslov 	ether_addr_copy(dmac_v, addr);
7657cd6a54aSVlad Buslov 	dmac_c = MLX5_ADDR_OF(fte_match_param, rule_spec->match_criteria,
7667cd6a54aSVlad Buslov 			      outer_headers.dmac_47_16);
7677cd6a54aSVlad Buslov 	eth_broadcast_addr(dmac_c);
7687cd6a54aSVlad Buslov 
769ffc89ee5SVlad Buslov 	if (vlan) {
77036e55079SVlad Buslov 		if (vlan->pkt_reformat_pop) {
77136e55079SVlad Buslov 			flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
77236e55079SVlad Buslov 			flow_act.pkt_reformat = vlan->pkt_reformat_pop;
77336e55079SVlad Buslov 		}
77436e55079SVlad Buslov 
775c5fcac93SVlad Buslov 		if (bridge->vlan_proto == ETH_P_8021Q) {
776ffc89ee5SVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
777ffc89ee5SVlad Buslov 					 outer_headers.cvlan_tag);
778ffc89ee5SVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
779ffc89ee5SVlad Buslov 					 outer_headers.cvlan_tag);
7809c0ca9baSVlad Buslov 		} else if (bridge->vlan_proto == ETH_P_8021AD) {
7819c0ca9baSVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
7829c0ca9baSVlad Buslov 					 outer_headers.svlan_tag);
7839c0ca9baSVlad Buslov 			MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_value,
7849c0ca9baSVlad Buslov 					 outer_headers.svlan_tag);
785c5fcac93SVlad Buslov 		}
786ffc89ee5SVlad Buslov 		MLX5_SET_TO_ONES(fte_match_param, rule_spec->match_criteria,
787ffc89ee5SVlad Buslov 				 outer_headers.first_vid);
788ffc89ee5SVlad Buslov 		MLX5_SET(fte_match_param, rule_spec->match_value, outer_headers.first_vid,
789ffc89ee5SVlad Buslov 			 vlan->vid);
790ffc89ee5SVlad Buslov 	}
791ffc89ee5SVlad Buslov 
792c358ea17SVlad Buslov 	if (MLX5_CAP_ESW(bridge->br_offloads->esw->dev, merged_eswitch)) {
793c358ea17SVlad Buslov 		dest.vport.flags = MLX5_FLOW_DEST_VPORT_VHCA_ID;
794c358ea17SVlad Buslov 		dest.vport.vhca_id = esw_owner_vhca_id;
795c358ea17SVlad Buslov 	}
7967cd6a54aSVlad Buslov 	handle = mlx5_add_flow_rules(bridge->egress_ft, rule_spec, &flow_act, &dest, 1);
7977cd6a54aSVlad Buslov 
7987cd6a54aSVlad Buslov 	kvfree(rule_spec);
7997cd6a54aSVlad Buslov 	return handle;
8007cd6a54aSVlad Buslov }
8017cd6a54aSVlad Buslov 
802575baa92SVlad Buslov static struct mlx5_flow_handle *
mlx5_esw_bridge_egress_miss_flow_create(struct mlx5_flow_table * egress_ft,struct mlx5_flow_table * skip_ft,struct mlx5_pkt_reformat * pkt_reformat)803575baa92SVlad Buslov mlx5_esw_bridge_egress_miss_flow_create(struct mlx5_flow_table *egress_ft,
804575baa92SVlad Buslov 					struct mlx5_flow_table *skip_ft,
805575baa92SVlad Buslov 					struct mlx5_pkt_reformat *pkt_reformat)
806575baa92SVlad Buslov {
807575baa92SVlad Buslov 	struct mlx5_flow_destination dest = {
808575baa92SVlad Buslov 		.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
809575baa92SVlad Buslov 		.ft = skip_ft,
810575baa92SVlad Buslov 	};
811575baa92SVlad Buslov 	struct mlx5_flow_act flow_act = {
812575baa92SVlad Buslov 		.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST |
813575baa92SVlad Buslov 		MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT,
814575baa92SVlad Buslov 		.flags = FLOW_ACT_NO_APPEND,
815575baa92SVlad Buslov 		.pkt_reformat = pkt_reformat,
816575baa92SVlad Buslov 	};
817575baa92SVlad Buslov 	struct mlx5_flow_spec *rule_spec;
818575baa92SVlad Buslov 	struct mlx5_flow_handle *handle;
819575baa92SVlad Buslov 
820575baa92SVlad Buslov 	rule_spec = kvzalloc(sizeof(*rule_spec), GFP_KERNEL);
821575baa92SVlad Buslov 	if (!rule_spec)
822575baa92SVlad Buslov 		return ERR_PTR(-ENOMEM);
823575baa92SVlad Buslov 
824575baa92SVlad Buslov 	rule_spec->match_criteria_enable = MLX5_MATCH_MISC_PARAMETERS_2;
825575baa92SVlad Buslov 
826575baa92SVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_criteria,
827575baa92SVlad Buslov 		 misc_parameters_2.metadata_reg_c_1, ESW_TUN_MASK);
828575baa92SVlad Buslov 	MLX5_SET(fte_match_param, rule_spec->match_value, misc_parameters_2.metadata_reg_c_1,
829575baa92SVlad Buslov 		 ESW_TUN_BRIDGE_INGRESS_PUSH_VLAN_MARK);
830575baa92SVlad Buslov 
831575baa92SVlad Buslov 	handle = mlx5_add_flow_rules(egress_ft, rule_spec, &flow_act, &dest, 1);
832575baa92SVlad Buslov 
833575baa92SVlad Buslov 	kvfree(rule_spec);
834575baa92SVlad Buslov 	return handle;
835575baa92SVlad Buslov }
836575baa92SVlad Buslov 
mlx5_esw_bridge_create(struct net_device * br_netdev,struct mlx5_esw_bridge_offloads * br_offloads)837ade19f0dSVlad Buslov static struct mlx5_esw_bridge *mlx5_esw_bridge_create(struct net_device *br_netdev,
83819e9bfa0SVlad Buslov 						      struct mlx5_esw_bridge_offloads *br_offloads)
83919e9bfa0SVlad Buslov {
84019e9bfa0SVlad Buslov 	struct mlx5_esw_bridge *bridge;
84119e9bfa0SVlad Buslov 	int err;
84219e9bfa0SVlad Buslov 
84319e9bfa0SVlad Buslov 	bridge = kvzalloc(sizeof(*bridge), GFP_KERNEL);
84419e9bfa0SVlad Buslov 	if (!bridge)
84519e9bfa0SVlad Buslov 		return ERR_PTR(-ENOMEM);
84619e9bfa0SVlad Buslov 
8477cd6a54aSVlad Buslov 	bridge->br_offloads = br_offloads;
84819e9bfa0SVlad Buslov 	err = mlx5_esw_bridge_egress_table_init(br_offloads, bridge);
84919e9bfa0SVlad Buslov 	if (err)
85019e9bfa0SVlad Buslov 		goto err_egress_tbl;
85119e9bfa0SVlad Buslov 
8527cd6a54aSVlad Buslov 	err = rhashtable_init(&bridge->fdb_ht, &fdb_ht_params);
8537cd6a54aSVlad Buslov 	if (err)
8547cd6a54aSVlad Buslov 		goto err_fdb_ht;
8557cd6a54aSVlad Buslov 
85670f0302bSVlad Buslov 	err = mlx5_esw_bridge_mdb_init(bridge);
85770f0302bSVlad Buslov 	if (err)
85870f0302bSVlad Buslov 		goto err_mdb_ht;
85970f0302bSVlad Buslov 
8607cd6a54aSVlad Buslov 	INIT_LIST_HEAD(&bridge->fdb_list);
861ade19f0dSVlad Buslov 	bridge->ifindex = br_netdev->ifindex;
86219e9bfa0SVlad Buslov 	bridge->refcnt = 1;
8636d8680daSVlad Buslov 	bridge->ageing_time = clock_t_to_jiffies(BR_DEFAULT_AGEING_TIME);
864c5fcac93SVlad Buslov 	bridge->vlan_proto = ETH_P_8021Q;
86519e9bfa0SVlad Buslov 	list_add(&bridge->list, &br_offloads->bridges);
866791eb782SVlad Buslov 	mlx5_esw_bridge_debugfs_init(br_netdev, bridge);
86719e9bfa0SVlad Buslov 
86819e9bfa0SVlad Buslov 	return bridge;
86919e9bfa0SVlad Buslov 
87070f0302bSVlad Buslov err_mdb_ht:
87170f0302bSVlad Buslov 	rhashtable_destroy(&bridge->fdb_ht);
8727cd6a54aSVlad Buslov err_fdb_ht:
8737cd6a54aSVlad Buslov 	mlx5_esw_bridge_egress_table_cleanup(bridge);
87419e9bfa0SVlad Buslov err_egress_tbl:
87519e9bfa0SVlad Buslov 	kvfree(bridge);
87619e9bfa0SVlad Buslov 	return ERR_PTR(err);
87719e9bfa0SVlad Buslov }
87819e9bfa0SVlad Buslov 
mlx5_esw_bridge_get(struct mlx5_esw_bridge * bridge)87919e9bfa0SVlad Buslov static void mlx5_esw_bridge_get(struct mlx5_esw_bridge *bridge)
88019e9bfa0SVlad Buslov {
88119e9bfa0SVlad Buslov 	bridge->refcnt++;
88219e9bfa0SVlad Buslov }
88319e9bfa0SVlad Buslov 
mlx5_esw_bridge_put(struct mlx5_esw_bridge_offloads * br_offloads,struct mlx5_esw_bridge * bridge)88419e9bfa0SVlad Buslov static void mlx5_esw_bridge_put(struct mlx5_esw_bridge_offloads *br_offloads,
88519e9bfa0SVlad Buslov 				struct mlx5_esw_bridge *bridge)
88619e9bfa0SVlad Buslov {
88719e9bfa0SVlad Buslov 	if (--bridge->refcnt)
88819e9bfa0SVlad Buslov 		return;
88919e9bfa0SVlad Buslov 
890791eb782SVlad Buslov 	mlx5_esw_bridge_debugfs_cleanup(bridge);
89119e9bfa0SVlad Buslov 	mlx5_esw_bridge_egress_table_cleanup(bridge);
89218c2916cSVlad Buslov 	mlx5_esw_bridge_mcast_disable(bridge);
89319e9bfa0SVlad Buslov 	list_del(&bridge->list);
89470f0302bSVlad Buslov 	mlx5_esw_bridge_mdb_cleanup(bridge);
8957cd6a54aSVlad Buslov 	rhashtable_destroy(&bridge->fdb_ht);
89619e9bfa0SVlad Buslov 	kvfree(bridge);
89719e9bfa0SVlad Buslov 
89819e9bfa0SVlad Buslov 	if (list_empty(&br_offloads->bridges))
89919e9bfa0SVlad Buslov 		mlx5_esw_bridge_ingress_table_cleanup(br_offloads);
90019e9bfa0SVlad Buslov }
90119e9bfa0SVlad Buslov 
90219e9bfa0SVlad Buslov static struct mlx5_esw_bridge *
mlx5_esw_bridge_lookup(struct net_device * br_netdev,struct mlx5_esw_bridge_offloads * br_offloads)903ade19f0dSVlad Buslov mlx5_esw_bridge_lookup(struct net_device *br_netdev, struct mlx5_esw_bridge_offloads *br_offloads)
90419e9bfa0SVlad Buslov {
90519e9bfa0SVlad Buslov 	struct mlx5_esw_bridge *bridge;
90619e9bfa0SVlad Buslov 
90719e9bfa0SVlad Buslov 	ASSERT_RTNL();
90819e9bfa0SVlad Buslov 
90919e9bfa0SVlad Buslov 	list_for_each_entry(bridge, &br_offloads->bridges, list) {
910ade19f0dSVlad Buslov 		if (bridge->ifindex == br_netdev->ifindex) {
91119e9bfa0SVlad Buslov 			mlx5_esw_bridge_get(bridge);
91219e9bfa0SVlad Buslov 			return bridge;
91319e9bfa0SVlad Buslov 		}
91419e9bfa0SVlad Buslov 	}
91519e9bfa0SVlad Buslov 
91619e9bfa0SVlad Buslov 	if (!br_offloads->ingress_ft) {
91719e9bfa0SVlad Buslov 		int err = mlx5_esw_bridge_ingress_table_init(br_offloads);
91819e9bfa0SVlad Buslov 
91919e9bfa0SVlad Buslov 		if (err)
92019e9bfa0SVlad Buslov 			return ERR_PTR(err);
92119e9bfa0SVlad Buslov 	}
92219e9bfa0SVlad Buslov 
923ade19f0dSVlad Buslov 	bridge = mlx5_esw_bridge_create(br_netdev, br_offloads);
92419e9bfa0SVlad Buslov 	if (IS_ERR(bridge) && list_empty(&br_offloads->bridges))
92519e9bfa0SVlad Buslov 		mlx5_esw_bridge_ingress_table_cleanup(br_offloads);
92619e9bfa0SVlad Buslov 	return bridge;
92719e9bfa0SVlad Buslov }
92819e9bfa0SVlad Buslov 
mlx5_esw_bridge_port_key_from_data(u16 vport_num,u16 esw_owner_vhca_id)9293ee6233eSVlad Buslov static unsigned long mlx5_esw_bridge_port_key_from_data(u16 vport_num, u16 esw_owner_vhca_id)
930d75b9e80SVlad Buslov {
9313ee6233eSVlad Buslov 	return vport_num | (unsigned long)esw_owner_vhca_id << sizeof(vport_num) * BITS_PER_BYTE;
9323ee6233eSVlad Buslov }
9333ee6233eSVlad Buslov 
mlx5_esw_bridge_port_key(struct mlx5_esw_bridge_port * port)93470f0302bSVlad Buslov unsigned long mlx5_esw_bridge_port_key(struct mlx5_esw_bridge_port *port)
9353ee6233eSVlad Buslov {
9363ee6233eSVlad Buslov 	return mlx5_esw_bridge_port_key_from_data(port->vport_num, port->esw_owner_vhca_id);
9373ee6233eSVlad Buslov }
9383ee6233eSVlad Buslov 
mlx5_esw_bridge_port_insert(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_offloads * br_offloads)9393ee6233eSVlad Buslov static int mlx5_esw_bridge_port_insert(struct mlx5_esw_bridge_port *port,
9403ee6233eSVlad Buslov 				       struct mlx5_esw_bridge_offloads *br_offloads)
9413ee6233eSVlad Buslov {
9423ee6233eSVlad Buslov 	return xa_insert(&br_offloads->ports, mlx5_esw_bridge_port_key(port), port, GFP_KERNEL);
943d75b9e80SVlad Buslov }
944d75b9e80SVlad Buslov 
945d75b9e80SVlad Buslov static struct mlx5_esw_bridge_port *
mlx5_esw_bridge_port_lookup(u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads)9463ee6233eSVlad Buslov mlx5_esw_bridge_port_lookup(u16 vport_num, u16 esw_owner_vhca_id,
9473ee6233eSVlad Buslov 			    struct mlx5_esw_bridge_offloads *br_offloads)
948d75b9e80SVlad Buslov {
9493ee6233eSVlad Buslov 	return xa_load(&br_offloads->ports, mlx5_esw_bridge_port_key_from_data(vport_num,
9503ee6233eSVlad Buslov 									       esw_owner_vhca_id));
951d75b9e80SVlad Buslov }
952d75b9e80SVlad Buslov 
mlx5_esw_bridge_port_erase(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_offloads * br_offloads)953d75b9e80SVlad Buslov static void mlx5_esw_bridge_port_erase(struct mlx5_esw_bridge_port *port,
9543ee6233eSVlad Buslov 				       struct mlx5_esw_bridge_offloads *br_offloads)
955d75b9e80SVlad Buslov {
9563ee6233eSVlad Buslov 	xa_erase(&br_offloads->ports, mlx5_esw_bridge_port_key(port));
957d75b9e80SVlad Buslov }
958d75b9e80SVlad Buslov 
959b99c4ef2SVlad Buslov static struct mlx5_esw_bridge *
mlx5_esw_bridge_from_port_lookup(u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads)960b99c4ef2SVlad Buslov mlx5_esw_bridge_from_port_lookup(u16 vport_num, u16 esw_owner_vhca_id,
961b99c4ef2SVlad Buslov 				 struct mlx5_esw_bridge_offloads *br_offloads)
962b99c4ef2SVlad Buslov {
963b99c4ef2SVlad Buslov 	struct mlx5_esw_bridge_port *port;
964b99c4ef2SVlad Buslov 
965b99c4ef2SVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
966b99c4ef2SVlad Buslov 	if (!port)
967b99c4ef2SVlad Buslov 		return NULL;
968b99c4ef2SVlad Buslov 
969b99c4ef2SVlad Buslov 	return port->bridge;
970b99c4ef2SVlad Buslov }
971b99c4ef2SVlad Buslov 
mlx5_esw_bridge_fdb_entry_refresh(struct mlx5_esw_bridge_fdb_entry * entry)972ff9b7521SVlad Buslov static void mlx5_esw_bridge_fdb_entry_refresh(struct mlx5_esw_bridge_fdb_entry *entry)
9739724fd5dSVlad Buslov {
9749724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_fdb_entry_refresh(entry);
9759724fd5dSVlad Buslov 
9769724fd5dSVlad Buslov 	mlx5_esw_bridge_fdb_offload_notify(entry->dev, entry->key.addr,
9779724fd5dSVlad Buslov 					   entry->key.vid,
9789724fd5dSVlad Buslov 					   SWITCHDEV_FDB_ADD_TO_BRIDGE);
9799724fd5dSVlad Buslov }
9809724fd5dSVlad Buslov 
9817cd6a54aSVlad Buslov static void
mlx5_esw_bridge_fdb_entry_cleanup(struct mlx5_esw_bridge_fdb_entry * entry,struct mlx5_esw_bridge * bridge)9827cd6a54aSVlad Buslov mlx5_esw_bridge_fdb_entry_cleanup(struct mlx5_esw_bridge_fdb_entry *entry,
9837cd6a54aSVlad Buslov 				  struct mlx5_esw_bridge *bridge)
9847cd6a54aSVlad Buslov {
9859724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_fdb_entry_cleanup(entry);
9869724fd5dSVlad Buslov 
9877cd6a54aSVlad Buslov 	rhashtable_remove_fast(&bridge->fdb_ht, &entry->ht_node, fdb_ht_params);
9887cd6a54aSVlad Buslov 	mlx5_del_flow_rules(entry->egress_handle);
989cc2987c4SVlad Buslov 	if (entry->filter_handle)
990cc2987c4SVlad Buslov 		mlx5_del_flow_rules(entry->filter_handle);
9917cd6a54aSVlad Buslov 	mlx5_del_flow_rules(entry->ingress_handle);
992c636a0f0SVlad Buslov 	mlx5_fc_destroy(bridge->br_offloads->esw->dev, entry->ingress_counter);
993cc2987c4SVlad Buslov 	list_del(&entry->vlan_list);
9947cd6a54aSVlad Buslov 	list_del(&entry->list);
9957cd6a54aSVlad Buslov 	kvfree(entry);
9967cd6a54aSVlad Buslov }
9977cd6a54aSVlad Buslov 
9982deda2f1SVlad Buslov static void
mlx5_esw_bridge_fdb_entry_notify_and_cleanup(struct mlx5_esw_bridge_fdb_entry * entry,struct mlx5_esw_bridge * bridge)9992deda2f1SVlad Buslov mlx5_esw_bridge_fdb_entry_notify_and_cleanup(struct mlx5_esw_bridge_fdb_entry *entry,
10002deda2f1SVlad Buslov 					     struct mlx5_esw_bridge *bridge)
10012deda2f1SVlad Buslov {
10022deda2f1SVlad Buslov 	mlx5_esw_bridge_fdb_del_notify(entry);
10032deda2f1SVlad Buslov 	mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge);
10042deda2f1SVlad Buslov }
10052deda2f1SVlad Buslov 
mlx5_esw_bridge_fdb_flush(struct mlx5_esw_bridge * bridge)1006d75b9e80SVlad Buslov static void mlx5_esw_bridge_fdb_flush(struct mlx5_esw_bridge *bridge)
1007d75b9e80SVlad Buslov {
1008d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry, *tmp;
1009d75b9e80SVlad Buslov 
10102deda2f1SVlad Buslov 	list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list)
10112deda2f1SVlad Buslov 		mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
1012d75b9e80SVlad Buslov }
1013d75b9e80SVlad Buslov 
1014d75b9e80SVlad Buslov static struct mlx5_esw_bridge_vlan *
mlx5_esw_bridge_vlan_lookup(u16 vid,struct mlx5_esw_bridge_port * port)1015d75b9e80SVlad Buslov mlx5_esw_bridge_vlan_lookup(u16 vid, struct mlx5_esw_bridge_port *port)
1016d75b9e80SVlad Buslov {
1017d75b9e80SVlad Buslov 	return xa_load(&port->vlans, vid);
1018d75b9e80SVlad Buslov }
1019d75b9e80SVlad Buslov 
102036e55079SVlad Buslov static int
mlx5_esw_bridge_vlan_push_create(u16 vlan_proto,struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)1021c5fcac93SVlad Buslov mlx5_esw_bridge_vlan_push_create(u16 vlan_proto, struct mlx5_esw_bridge_vlan *vlan,
1022c5fcac93SVlad Buslov 				 struct mlx5_eswitch *esw)
102336e55079SVlad Buslov {
102436e55079SVlad Buslov 	struct {
102536e55079SVlad Buslov 		__be16	h_vlan_proto;
102636e55079SVlad Buslov 		__be16	h_vlan_TCI;
1027c5fcac93SVlad Buslov 	} vlan_hdr = { htons(vlan_proto), htons(vlan->vid) };
102836e55079SVlad Buslov 	struct mlx5_pkt_reformat_params reformat_params = {};
102936e55079SVlad Buslov 	struct mlx5_pkt_reformat *pkt_reformat;
103036e55079SVlad Buslov 
103136e55079SVlad Buslov 	if (!BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, reformat_insert)) ||
103236e55079SVlad Buslov 	    MLX5_CAP_GEN_2(esw->dev, max_reformat_insert_size) < sizeof(vlan_hdr) ||
103336e55079SVlad Buslov 	    MLX5_CAP_GEN_2(esw->dev, max_reformat_insert_offset) <
103436e55079SVlad Buslov 	    offsetof(struct vlan_ethhdr, h_vlan_proto)) {
103536e55079SVlad Buslov 		esw_warn(esw->dev, "Packet reformat INSERT_HEADER is not supported\n");
103636e55079SVlad Buslov 		return -EOPNOTSUPP;
103736e55079SVlad Buslov 	}
103836e55079SVlad Buslov 
103936e55079SVlad Buslov 	reformat_params.type = MLX5_REFORMAT_TYPE_INSERT_HDR;
104036e55079SVlad Buslov 	reformat_params.param_0 = MLX5_REFORMAT_CONTEXT_ANCHOR_MAC_START;
104136e55079SVlad Buslov 	reformat_params.param_1 = offsetof(struct vlan_ethhdr, h_vlan_proto);
104236e55079SVlad Buslov 	reformat_params.size = sizeof(vlan_hdr);
104336e55079SVlad Buslov 	reformat_params.data = &vlan_hdr;
104436e55079SVlad Buslov 	pkt_reformat = mlx5_packet_reformat_alloc(esw->dev,
104536e55079SVlad Buslov 						  &reformat_params,
104636e55079SVlad Buslov 						  MLX5_FLOW_NAMESPACE_FDB);
104736e55079SVlad Buslov 	if (IS_ERR(pkt_reformat)) {
104836e55079SVlad Buslov 		esw_warn(esw->dev, "Failed to alloc packet reformat INSERT_HEADER (err=%ld)\n",
104936e55079SVlad Buslov 			 PTR_ERR(pkt_reformat));
105036e55079SVlad Buslov 		return PTR_ERR(pkt_reformat);
105136e55079SVlad Buslov 	}
105236e55079SVlad Buslov 
105336e55079SVlad Buslov 	vlan->pkt_reformat_push = pkt_reformat;
105436e55079SVlad Buslov 	return 0;
105536e55079SVlad Buslov }
105636e55079SVlad Buslov 
105736e55079SVlad Buslov static void
mlx5_esw_bridge_vlan_push_cleanup(struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)105836e55079SVlad Buslov mlx5_esw_bridge_vlan_push_cleanup(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
105936e55079SVlad Buslov {
106036e55079SVlad Buslov 	mlx5_packet_reformat_dealloc(esw->dev, vlan->pkt_reformat_push);
106136e55079SVlad Buslov 	vlan->pkt_reformat_push = NULL;
106236e55079SVlad Buslov }
106336e55079SVlad Buslov 
106436e55079SVlad Buslov static int
mlx5_esw_bridge_vlan_pop_create(struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)106536e55079SVlad Buslov mlx5_esw_bridge_vlan_pop_create(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
106636e55079SVlad Buslov {
106736e55079SVlad Buslov 	struct mlx5_pkt_reformat *pkt_reformat;
106836e55079SVlad Buslov 
106964fc4b35SVlad Buslov 	if (!mlx5_esw_bridge_pkt_reformat_vlan_pop_supported(esw)) {
107036e55079SVlad Buslov 		esw_warn(esw->dev, "Packet reformat REMOVE_HEADER is not supported\n");
107136e55079SVlad Buslov 		return -EOPNOTSUPP;
107236e55079SVlad Buslov 	}
107336e55079SVlad Buslov 
107464fc4b35SVlad Buslov 	pkt_reformat = mlx5_esw_bridge_pkt_reformat_vlan_pop_create(esw);
107536e55079SVlad Buslov 	if (IS_ERR(pkt_reformat)) {
107636e55079SVlad Buslov 		esw_warn(esw->dev, "Failed to alloc packet reformat REMOVE_HEADER (err=%ld)\n",
107736e55079SVlad Buslov 			 PTR_ERR(pkt_reformat));
107836e55079SVlad Buslov 		return PTR_ERR(pkt_reformat);
107936e55079SVlad Buslov 	}
108036e55079SVlad Buslov 
108136e55079SVlad Buslov 	vlan->pkt_reformat_pop = pkt_reformat;
108236e55079SVlad Buslov 	return 0;
108336e55079SVlad Buslov }
108436e55079SVlad Buslov 
108536e55079SVlad Buslov static void
mlx5_esw_bridge_vlan_pop_cleanup(struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)108636e55079SVlad Buslov mlx5_esw_bridge_vlan_pop_cleanup(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
108736e55079SVlad Buslov {
108836e55079SVlad Buslov 	mlx5_packet_reformat_dealloc(esw->dev, vlan->pkt_reformat_pop);
108936e55079SVlad Buslov 	vlan->pkt_reformat_pop = NULL;
109036e55079SVlad Buslov }
109136e55079SVlad Buslov 
10925249001dSVlad Buslov static int
mlx5_esw_bridge_vlan_push_mark_create(struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)10935249001dSVlad Buslov mlx5_esw_bridge_vlan_push_mark_create(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
10945249001dSVlad Buslov {
10955249001dSVlad Buslov 	u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
10965249001dSVlad Buslov 	struct mlx5_modify_hdr *pkt_mod_hdr;
10975249001dSVlad Buslov 
10985249001dSVlad Buslov 	MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
10995249001dSVlad Buslov 	MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_C_1);
11005249001dSVlad Buslov 	MLX5_SET(set_action_in, action, offset, 8);
11015249001dSVlad Buslov 	MLX5_SET(set_action_in, action, length, ESW_TUN_OPTS_BITS + ESW_TUN_ID_BITS);
11025249001dSVlad Buslov 	MLX5_SET(set_action_in, action, data, ESW_TUN_BRIDGE_INGRESS_PUSH_VLAN);
11035249001dSVlad Buslov 
11045249001dSVlad Buslov 	pkt_mod_hdr = mlx5_modify_header_alloc(esw->dev, MLX5_FLOW_NAMESPACE_FDB, 1, action);
11055249001dSVlad Buslov 	if (IS_ERR(pkt_mod_hdr))
11065249001dSVlad Buslov 		return PTR_ERR(pkt_mod_hdr);
11075249001dSVlad Buslov 
11085249001dSVlad Buslov 	vlan->pkt_mod_hdr_push_mark = pkt_mod_hdr;
11095249001dSVlad Buslov 	return 0;
11105249001dSVlad Buslov }
11115249001dSVlad Buslov 
11125249001dSVlad Buslov static void
mlx5_esw_bridge_vlan_push_mark_cleanup(struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)11135249001dSVlad Buslov mlx5_esw_bridge_vlan_push_mark_cleanup(struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
11145249001dSVlad Buslov {
11155249001dSVlad Buslov 	mlx5_modify_header_dealloc(esw->dev, vlan->pkt_mod_hdr_push_mark);
11165249001dSVlad Buslov 	vlan->pkt_mod_hdr_push_mark = NULL;
11175249001dSVlad Buslov }
11185249001dSVlad Buslov 
11195a9db8d4SVlad Buslov static int
mlx5_esw_bridge_vlan_push_pop_fhs_create(u16 vlan_proto,struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan)1120b5e80625SVlad Buslov mlx5_esw_bridge_vlan_push_pop_fhs_create(u16 vlan_proto, struct mlx5_esw_bridge_port *port,
1121b5e80625SVlad Buslov 					 struct mlx5_esw_bridge_vlan *vlan)
1122b5e80625SVlad Buslov {
1123b5e80625SVlad Buslov 	return mlx5_esw_bridge_vlan_mcast_init(vlan_proto, port, vlan);
1124b5e80625SVlad Buslov }
1125b5e80625SVlad Buslov 
1126b5e80625SVlad Buslov static void
mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(struct mlx5_esw_bridge_vlan * vlan)1127b5e80625SVlad Buslov mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(struct mlx5_esw_bridge_vlan *vlan)
1128b5e80625SVlad Buslov {
1129b5e80625SVlad Buslov 	mlx5_esw_bridge_vlan_mcast_cleanup(vlan);
1130b5e80625SVlad Buslov }
1131b5e80625SVlad Buslov 
1132b5e80625SVlad Buslov static int
mlx5_esw_bridge_vlan_push_pop_create(u16 vlan_proto,u16 flags,struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan,struct mlx5_eswitch * esw)1133b5e80625SVlad Buslov mlx5_esw_bridge_vlan_push_pop_create(u16 vlan_proto, u16 flags, struct mlx5_esw_bridge_port *port,
1134b5e80625SVlad Buslov 				     struct mlx5_esw_bridge_vlan *vlan, struct mlx5_eswitch *esw)
11355a9db8d4SVlad Buslov {
11365a9db8d4SVlad Buslov 	int err;
11375a9db8d4SVlad Buslov 
11385a9db8d4SVlad Buslov 	if (flags & BRIDGE_VLAN_INFO_PVID) {
1139c5fcac93SVlad Buslov 		err = mlx5_esw_bridge_vlan_push_create(vlan_proto, vlan, esw);
11405a9db8d4SVlad Buslov 		if (err)
11415a9db8d4SVlad Buslov 			return err;
11425a9db8d4SVlad Buslov 
11435a9db8d4SVlad Buslov 		err = mlx5_esw_bridge_vlan_push_mark_create(vlan, esw);
11445a9db8d4SVlad Buslov 		if (err)
11455a9db8d4SVlad Buslov 			goto err_vlan_push_mark;
11465a9db8d4SVlad Buslov 	}
11475a9db8d4SVlad Buslov 
11485a9db8d4SVlad Buslov 	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
11495a9db8d4SVlad Buslov 		err = mlx5_esw_bridge_vlan_pop_create(vlan, esw);
11505a9db8d4SVlad Buslov 		if (err)
11515a9db8d4SVlad Buslov 			goto err_vlan_pop;
1152b5e80625SVlad Buslov 
1153b5e80625SVlad Buslov 		err = mlx5_esw_bridge_vlan_push_pop_fhs_create(vlan_proto, port, vlan);
1154b5e80625SVlad Buslov 		if (err)
1155b5e80625SVlad Buslov 			goto err_vlan_pop_fhs;
11565a9db8d4SVlad Buslov 	}
11575a9db8d4SVlad Buslov 
11585a9db8d4SVlad Buslov 	return 0;
11595a9db8d4SVlad Buslov 
1160b5e80625SVlad Buslov err_vlan_pop_fhs:
1161b5e80625SVlad Buslov 	mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
11625a9db8d4SVlad Buslov err_vlan_pop:
11635a9db8d4SVlad Buslov 	if (vlan->pkt_mod_hdr_push_mark)
11645a9db8d4SVlad Buslov 		mlx5_esw_bridge_vlan_push_mark_cleanup(vlan, esw);
11655a9db8d4SVlad Buslov err_vlan_push_mark:
11665a9db8d4SVlad Buslov 	if (vlan->pkt_reformat_push)
11675a9db8d4SVlad Buslov 		mlx5_esw_bridge_vlan_push_cleanup(vlan, esw);
11685a9db8d4SVlad Buslov 	return err;
11695a9db8d4SVlad Buslov }
11705a9db8d4SVlad Buslov 
1171d75b9e80SVlad Buslov static struct mlx5_esw_bridge_vlan *
mlx5_esw_bridge_vlan_create(u16 vlan_proto,u16 vid,u16 flags,struct mlx5_esw_bridge_port * port,struct mlx5_eswitch * esw)1172c5fcac93SVlad Buslov mlx5_esw_bridge_vlan_create(u16 vlan_proto, u16 vid, u16 flags, struct mlx5_esw_bridge_port *port,
117336e55079SVlad Buslov 			    struct mlx5_eswitch *esw)
1174d75b9e80SVlad Buslov {
1175d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1176d75b9e80SVlad Buslov 	int err;
1177d75b9e80SVlad Buslov 
1178d75b9e80SVlad Buslov 	vlan = kvzalloc(sizeof(*vlan), GFP_KERNEL);
1179d75b9e80SVlad Buslov 	if (!vlan)
1180d75b9e80SVlad Buslov 		return ERR_PTR(-ENOMEM);
1181d75b9e80SVlad Buslov 
1182d75b9e80SVlad Buslov 	vlan->vid = vid;
1183d75b9e80SVlad Buslov 	vlan->flags = flags;
118436e55079SVlad Buslov 	INIT_LIST_HEAD(&vlan->fdb_list);
118536e55079SVlad Buslov 
1186b5e80625SVlad Buslov 	err = mlx5_esw_bridge_vlan_push_pop_create(vlan_proto, flags, port, vlan, esw);
118736e55079SVlad Buslov 	if (err)
11885a9db8d4SVlad Buslov 		goto err_vlan_push_pop;
1189d75b9e80SVlad Buslov 
119036e55079SVlad Buslov 	err = xa_insert(&port->vlans, vid, vlan, GFP_KERNEL);
119136e55079SVlad Buslov 	if (err)
119236e55079SVlad Buslov 		goto err_xa_insert;
119336e55079SVlad Buslov 
11949724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_vlan_create(vlan);
1195d75b9e80SVlad Buslov 	return vlan;
119636e55079SVlad Buslov 
119736e55079SVlad Buslov err_xa_insert:
1198b5e80625SVlad Buslov 	if (vlan->mcast_handle)
1199b5e80625SVlad Buslov 		mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(vlan);
120036e55079SVlad Buslov 	if (vlan->pkt_reformat_pop)
120136e55079SVlad Buslov 		mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
12025249001dSVlad Buslov 	if (vlan->pkt_mod_hdr_push_mark)
12035249001dSVlad Buslov 		mlx5_esw_bridge_vlan_push_mark_cleanup(vlan, esw);
120436e55079SVlad Buslov 	if (vlan->pkt_reformat_push)
120536e55079SVlad Buslov 		mlx5_esw_bridge_vlan_push_cleanup(vlan, esw);
12065a9db8d4SVlad Buslov err_vlan_push_pop:
120736e55079SVlad Buslov 	kvfree(vlan);
120836e55079SVlad Buslov 	return ERR_PTR(err);
1209d75b9e80SVlad Buslov }
1210d75b9e80SVlad Buslov 
mlx5_esw_bridge_vlan_erase(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan)1211d75b9e80SVlad Buslov static void mlx5_esw_bridge_vlan_erase(struct mlx5_esw_bridge_port *port,
1212d75b9e80SVlad Buslov 				       struct mlx5_esw_bridge_vlan *vlan)
1213d75b9e80SVlad Buslov {
1214d75b9e80SVlad Buslov 	xa_erase(&port->vlans, vlan->vid);
1215d75b9e80SVlad Buslov }
1216d75b9e80SVlad Buslov 
mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan,struct mlx5_esw_bridge * bridge)121770f0302bSVlad Buslov static void mlx5_esw_bridge_vlan_flush(struct mlx5_esw_bridge_port *port,
121870f0302bSVlad Buslov 				       struct mlx5_esw_bridge_vlan *vlan,
121936e55079SVlad Buslov 				       struct mlx5_esw_bridge *bridge)
1220d75b9e80SVlad Buslov {
1221a1a6e721SVlad Buslov 	struct mlx5_eswitch *esw = bridge->br_offloads->esw;
122236e55079SVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry, *tmp;
122336e55079SVlad Buslov 
12242deda2f1SVlad Buslov 	list_for_each_entry_safe(entry, tmp, &vlan->fdb_list, vlan_list)
12252deda2f1SVlad Buslov 		mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
122670f0302bSVlad Buslov 	mlx5_esw_bridge_port_mdb_vlan_flush(port, vlan);
122736e55079SVlad Buslov 
1228b5e80625SVlad Buslov 	if (vlan->mcast_handle)
1229b5e80625SVlad Buslov 		mlx5_esw_bridge_vlan_push_pop_fhs_cleanup(vlan);
123036e55079SVlad Buslov 	if (vlan->pkt_reformat_pop)
1231a1a6e721SVlad Buslov 		mlx5_esw_bridge_vlan_pop_cleanup(vlan, esw);
12325249001dSVlad Buslov 	if (vlan->pkt_mod_hdr_push_mark)
12335249001dSVlad Buslov 		mlx5_esw_bridge_vlan_push_mark_cleanup(vlan, esw);
123436e55079SVlad Buslov 	if (vlan->pkt_reformat_push)
1235a1a6e721SVlad Buslov 		mlx5_esw_bridge_vlan_push_cleanup(vlan, esw);
123636e55079SVlad Buslov }
123736e55079SVlad Buslov 
mlx5_esw_bridge_vlan_cleanup(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge_vlan * vlan,struct mlx5_esw_bridge * bridge)123836e55079SVlad Buslov static void mlx5_esw_bridge_vlan_cleanup(struct mlx5_esw_bridge_port *port,
123936e55079SVlad Buslov 					 struct mlx5_esw_bridge_vlan *vlan,
124036e55079SVlad Buslov 					 struct mlx5_esw_bridge *bridge)
124136e55079SVlad Buslov {
12429724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_vlan_cleanup(vlan);
124370f0302bSVlad Buslov 	mlx5_esw_bridge_vlan_flush(port, vlan, bridge);
1244d75b9e80SVlad Buslov 	mlx5_esw_bridge_vlan_erase(port, vlan);
1245d75b9e80SVlad Buslov 	kvfree(vlan);
1246d75b9e80SVlad Buslov }
1247d75b9e80SVlad Buslov 
mlx5_esw_bridge_port_vlans_flush(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge * bridge)124836e55079SVlad Buslov static void mlx5_esw_bridge_port_vlans_flush(struct mlx5_esw_bridge_port *port,
124936e55079SVlad Buslov 					     struct mlx5_esw_bridge *bridge)
1250d75b9e80SVlad Buslov {
1251d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1252d75b9e80SVlad Buslov 	unsigned long index;
1253d75b9e80SVlad Buslov 
1254d75b9e80SVlad Buslov 	xa_for_each(&port->vlans, index, vlan)
125536e55079SVlad Buslov 		mlx5_esw_bridge_vlan_cleanup(port, vlan, bridge);
1256d75b9e80SVlad Buslov }
1257d75b9e80SVlad Buslov 
mlx5_esw_bridge_port_vlans_recreate(struct mlx5_esw_bridge_port * port,struct mlx5_esw_bridge * bridge)1258c5fcac93SVlad Buslov static int mlx5_esw_bridge_port_vlans_recreate(struct mlx5_esw_bridge_port *port,
1259c5fcac93SVlad Buslov 					       struct mlx5_esw_bridge *bridge)
1260c5fcac93SVlad Buslov {
1261c5fcac93SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
1262c5fcac93SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1263c5fcac93SVlad Buslov 	unsigned long i;
1264c5fcac93SVlad Buslov 	int err;
1265c5fcac93SVlad Buslov 
1266c5fcac93SVlad Buslov 	xa_for_each(&port->vlans, i, vlan) {
126770f0302bSVlad Buslov 		mlx5_esw_bridge_vlan_flush(port, vlan, bridge);
1268b5e80625SVlad Buslov 		err = mlx5_esw_bridge_vlan_push_pop_create(bridge->vlan_proto, vlan->flags, port,
1269b5e80625SVlad Buslov 							   vlan, br_offloads->esw);
1270c5fcac93SVlad Buslov 		if (err) {
1271c5fcac93SVlad Buslov 			esw_warn(br_offloads->esw->dev,
1272c5fcac93SVlad Buslov 				 "Failed to create VLAN=%u(proto=%x) push/pop actions (vport=%u,err=%d)\n",
1273c5fcac93SVlad Buslov 				 vlan->vid, bridge->vlan_proto, port->vport_num,
1274c5fcac93SVlad Buslov 				 err);
1275c5fcac93SVlad Buslov 			return err;
1276c5fcac93SVlad Buslov 		}
1277c5fcac93SVlad Buslov 	}
1278c5fcac93SVlad Buslov 
1279c5fcac93SVlad Buslov 	return 0;
1280c5fcac93SVlad Buslov }
1281c5fcac93SVlad Buslov 
1282c5fcac93SVlad Buslov static int
mlx5_esw_bridge_vlans_recreate(struct mlx5_esw_bridge * bridge)1283c5fcac93SVlad Buslov mlx5_esw_bridge_vlans_recreate(struct mlx5_esw_bridge *bridge)
1284c5fcac93SVlad Buslov {
1285c5fcac93SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = bridge->br_offloads;
1286c5fcac93SVlad Buslov 	struct mlx5_esw_bridge_port *port;
1287c5fcac93SVlad Buslov 	unsigned long i;
1288c5fcac93SVlad Buslov 	int err;
1289c5fcac93SVlad Buslov 
1290c5fcac93SVlad Buslov 	xa_for_each(&br_offloads->ports, i, port) {
1291c5fcac93SVlad Buslov 		if (port->bridge != bridge)
1292c5fcac93SVlad Buslov 			continue;
1293c5fcac93SVlad Buslov 
1294c5fcac93SVlad Buslov 		err = mlx5_esw_bridge_port_vlans_recreate(port, bridge);
1295c5fcac93SVlad Buslov 		if (err)
1296c5fcac93SVlad Buslov 			return err;
1297c5fcac93SVlad Buslov 	}
1298c5fcac93SVlad Buslov 
1299c5fcac93SVlad Buslov 	return 0;
1300c5fcac93SVlad Buslov }
1301c5fcac93SVlad Buslov 
1302ffc89ee5SVlad Buslov static struct mlx5_esw_bridge_vlan *
mlx5_esw_bridge_port_vlan_lookup(u16 vid,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge * bridge,struct mlx5_eswitch * esw)13033ee6233eSVlad Buslov mlx5_esw_bridge_port_vlan_lookup(u16 vid, u16 vport_num, u16 esw_owner_vhca_id,
13043ee6233eSVlad Buslov 				 struct mlx5_esw_bridge *bridge, struct mlx5_eswitch *esw)
1305ffc89ee5SVlad Buslov {
1306ffc89ee5SVlad Buslov 	struct mlx5_esw_bridge_port *port;
1307ffc89ee5SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1308ffc89ee5SVlad Buslov 
13093ee6233eSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, bridge->br_offloads);
1310ffc89ee5SVlad Buslov 	if (!port) {
1311ffc89ee5SVlad Buslov 		/* FDB is added asynchronously on wq while port might have been deleted
1312ffc89ee5SVlad Buslov 		 * concurrently. Report on 'info' logging level and skip the FDB offload.
1313ffc89ee5SVlad Buslov 		 */
1314ffc89ee5SVlad Buslov 		esw_info(esw->dev, "Failed to lookup bridge port (vport=%u)\n", vport_num);
1315ffc89ee5SVlad Buslov 		return ERR_PTR(-EINVAL);
1316ffc89ee5SVlad Buslov 	}
1317ffc89ee5SVlad Buslov 
1318ffc89ee5SVlad Buslov 	vlan = mlx5_esw_bridge_vlan_lookup(vid, port);
1319ffc89ee5SVlad Buslov 	if (!vlan) {
1320ffc89ee5SVlad Buslov 		/* FDB is added asynchronously on wq while vlan might have been deleted
1321ffc89ee5SVlad Buslov 		 * concurrently. Report on 'info' logging level and skip the FDB offload.
1322ffc89ee5SVlad Buslov 		 */
1323ffc89ee5SVlad Buslov 		esw_info(esw->dev, "Failed to lookup bridge port vlan metadata (vport=%u)\n",
1324ffc89ee5SVlad Buslov 			 vport_num);
1325ffc89ee5SVlad Buslov 		return ERR_PTR(-EINVAL);
1326ffc89ee5SVlad Buslov 	}
1327ffc89ee5SVlad Buslov 
1328ffc89ee5SVlad Buslov 	return vlan;
1329ffc89ee5SVlad Buslov }
1330ffc89ee5SVlad Buslov 
13317cd6a54aSVlad Buslov static struct mlx5_esw_bridge_fdb_entry *
mlx5_esw_bridge_fdb_lookup(struct mlx5_esw_bridge * bridge,const unsigned char * addr,u16 vid)13322deda2f1SVlad Buslov mlx5_esw_bridge_fdb_lookup(struct mlx5_esw_bridge *bridge,
13332deda2f1SVlad Buslov 			   const unsigned char *addr, u16 vid)
13342deda2f1SVlad Buslov {
13352deda2f1SVlad Buslov 	struct mlx5_esw_bridge_fdb_key key = {};
13362deda2f1SVlad Buslov 
13372deda2f1SVlad Buslov 	ether_addr_copy(key.addr, addr);
13382deda2f1SVlad Buslov 	key.vid = vid;
13392deda2f1SVlad Buslov 	return rhashtable_lookup_fast(&bridge->fdb_ht, &key, fdb_ht_params);
13402deda2f1SVlad Buslov }
13412deda2f1SVlad Buslov 
13422deda2f1SVlad Buslov static struct mlx5_esw_bridge_fdb_entry *
mlx5_esw_bridge_fdb_entry_init(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,const unsigned char * addr,u16 vid,bool added_by_user,bool peer,struct mlx5_eswitch * esw,struct mlx5_esw_bridge * bridge)13433ee6233eSVlad Buslov mlx5_esw_bridge_fdb_entry_init(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
1344c358ea17SVlad Buslov 			       const unsigned char *addr, u16 vid, bool added_by_user, bool peer,
13453ee6233eSVlad Buslov 			       struct mlx5_eswitch *esw, struct mlx5_esw_bridge *bridge)
13467cd6a54aSVlad Buslov {
1347ffc89ee5SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan = NULL;
13487cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry;
13497cd6a54aSVlad Buslov 	struct mlx5_flow_handle *handle;
1350c636a0f0SVlad Buslov 	struct mlx5_fc *counter;
13517cd6a54aSVlad Buslov 	int err;
13527cd6a54aSVlad Buslov 
1353ffc89ee5SVlad Buslov 	if (bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG && vid) {
13543ee6233eSVlad Buslov 		vlan = mlx5_esw_bridge_port_vlan_lookup(vid, vport_num, esw_owner_vhca_id, bridge,
13553ee6233eSVlad Buslov 							esw);
1356ffc89ee5SVlad Buslov 		if (IS_ERR(vlan))
1357ffc89ee5SVlad Buslov 			return ERR_CAST(vlan);
1358ffc89ee5SVlad Buslov 	}
1359ffc89ee5SVlad Buslov 
13603518c83fSVlad Buslov 	entry = mlx5_esw_bridge_fdb_lookup(bridge, addr, vid);
13613518c83fSVlad Buslov 	if (entry)
13623518c83fSVlad Buslov 		mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
13633518c83fSVlad Buslov 
13647cd6a54aSVlad Buslov 	entry = kvzalloc(sizeof(*entry), GFP_KERNEL);
13657cd6a54aSVlad Buslov 	if (!entry)
13667cd6a54aSVlad Buslov 		return ERR_PTR(-ENOMEM);
13677cd6a54aSVlad Buslov 
13687cd6a54aSVlad Buslov 	ether_addr_copy(entry->key.addr, addr);
13697cd6a54aSVlad Buslov 	entry->key.vid = vid;
1370c636a0f0SVlad Buslov 	entry->dev = dev;
13717cd6a54aSVlad Buslov 	entry->vport_num = vport_num;
13723ee6233eSVlad Buslov 	entry->esw_owner_vhca_id = esw_owner_vhca_id;
1373c636a0f0SVlad Buslov 	entry->lastuse = jiffies;
1374c636a0f0SVlad Buslov 	if (added_by_user)
1375c636a0f0SVlad Buslov 		entry->flags |= MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER;
1376c358ea17SVlad Buslov 	if (peer)
1377c358ea17SVlad Buslov 		entry->flags |= MLX5_ESW_BRIDGE_FLAG_PEER;
13787cd6a54aSVlad Buslov 
1379a514d173SVlad Buslov 	counter = mlx5_fc_create(esw->dev, true);
1380c636a0f0SVlad Buslov 	if (IS_ERR(counter)) {
1381c636a0f0SVlad Buslov 		err = PTR_ERR(counter);
1382c636a0f0SVlad Buslov 		goto err_ingress_fc_create;
1383c636a0f0SVlad Buslov 	}
1384c636a0f0SVlad Buslov 	entry->ingress_counter = counter;
1385c636a0f0SVlad Buslov 
1386ff9b7521SVlad Buslov 	handle = peer ?
138790ca127cSSaeed Mahameed 		mlx5_esw_bridge_ingress_flow_peer_create(vport_num, esw_owner_vhca_id,
138890ca127cSSaeed Mahameed 							 addr, vlan, mlx5_fc_id(counter),
138990ca127cSSaeed Mahameed 							 bridge) :
1390ff9b7521SVlad Buslov 		mlx5_esw_bridge_ingress_flow_create(vport_num, addr, vlan,
1391ff9b7521SVlad Buslov 						    mlx5_fc_id(counter), bridge);
13927cd6a54aSVlad Buslov 	if (IS_ERR(handle)) {
13937cd6a54aSVlad Buslov 		err = PTR_ERR(handle);
139488d162b4SRoi Dayan 		esw_warn(esw->dev, "Failed to create ingress flow(vport=%u,err=%d,peer=%d)\n",
139588d162b4SRoi Dayan 			 vport_num, err, peer);
13967cd6a54aSVlad Buslov 		goto err_ingress_flow_create;
13977cd6a54aSVlad Buslov 	}
13987cd6a54aSVlad Buslov 	entry->ingress_handle = handle;
13997cd6a54aSVlad Buslov 
1400cc2987c4SVlad Buslov 	if (bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG) {
1401cc2987c4SVlad Buslov 		handle = mlx5_esw_bridge_ingress_filter_flow_create(vport_num, addr, bridge);
1402cc2987c4SVlad Buslov 		if (IS_ERR(handle)) {
1403cc2987c4SVlad Buslov 			err = PTR_ERR(handle);
1404cc2987c4SVlad Buslov 			esw_warn(esw->dev, "Failed to create ingress filter(vport=%u,err=%d)\n",
1405cc2987c4SVlad Buslov 				 vport_num, err);
1406cc2987c4SVlad Buslov 			goto err_ingress_filter_flow_create;
1407cc2987c4SVlad Buslov 		}
1408cc2987c4SVlad Buslov 		entry->filter_handle = handle;
1409cc2987c4SVlad Buslov 	}
1410cc2987c4SVlad Buslov 
1411c358ea17SVlad Buslov 	handle = mlx5_esw_bridge_egress_flow_create(vport_num, esw_owner_vhca_id, addr, vlan,
1412c358ea17SVlad Buslov 						    bridge);
14137cd6a54aSVlad Buslov 	if (IS_ERR(handle)) {
14147cd6a54aSVlad Buslov 		err = PTR_ERR(handle);
14157cd6a54aSVlad Buslov 		esw_warn(esw->dev, "Failed to create egress flow(vport=%u,err=%d)\n",
14167cd6a54aSVlad Buslov 			 vport_num, err);
14177cd6a54aSVlad Buslov 		goto err_egress_flow_create;
14187cd6a54aSVlad Buslov 	}
14197cd6a54aSVlad Buslov 	entry->egress_handle = handle;
14207cd6a54aSVlad Buslov 
14217cd6a54aSVlad Buslov 	err = rhashtable_insert_fast(&bridge->fdb_ht, &entry->ht_node, fdb_ht_params);
14227cd6a54aSVlad Buslov 	if (err) {
14237cd6a54aSVlad Buslov 		esw_warn(esw->dev, "Failed to insert FDB flow(vport=%u,err=%d)\n", vport_num, err);
14247cd6a54aSVlad Buslov 		goto err_ht_init;
14257cd6a54aSVlad Buslov 	}
14267cd6a54aSVlad Buslov 
142736e55079SVlad Buslov 	if (vlan)
142836e55079SVlad Buslov 		list_add(&entry->vlan_list, &vlan->fdb_list);
142936e55079SVlad Buslov 	else
143036e55079SVlad Buslov 		INIT_LIST_HEAD(&entry->vlan_list);
14317cd6a54aSVlad Buslov 	list_add(&entry->list, &bridge->fdb_list);
14329724fd5dSVlad Buslov 
14339724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_fdb_entry_init(entry);
14347cd6a54aSVlad Buslov 	return entry;
14357cd6a54aSVlad Buslov 
14367cd6a54aSVlad Buslov err_ht_init:
14377cd6a54aSVlad Buslov 	mlx5_del_flow_rules(entry->egress_handle);
14387cd6a54aSVlad Buslov err_egress_flow_create:
1439cc2987c4SVlad Buslov 	if (entry->filter_handle)
1440cc2987c4SVlad Buslov 		mlx5_del_flow_rules(entry->filter_handle);
1441cc2987c4SVlad Buslov err_ingress_filter_flow_create:
14427cd6a54aSVlad Buslov 	mlx5_del_flow_rules(entry->ingress_handle);
14437cd6a54aSVlad Buslov err_ingress_flow_create:
1444a514d173SVlad Buslov 	mlx5_fc_destroy(esw->dev, entry->ingress_counter);
1445c636a0f0SVlad Buslov err_ingress_fc_create:
14467cd6a54aSVlad Buslov 	kvfree(entry);
14477cd6a54aSVlad Buslov 	return ERR_PTR(err);
14487cd6a54aSVlad Buslov }
14497cd6a54aSVlad Buslov 
mlx5_esw_bridge_ageing_time_set(u16 vport_num,u16 esw_owner_vhca_id,unsigned long ageing_time,struct mlx5_esw_bridge_offloads * br_offloads)14503ee6233eSVlad Buslov int mlx5_esw_bridge_ageing_time_set(u16 vport_num, u16 esw_owner_vhca_id, unsigned long ageing_time,
14513ee6233eSVlad Buslov 				    struct mlx5_esw_bridge_offloads *br_offloads)
1452c636a0f0SVlad Buslov {
1453b99c4ef2SVlad Buslov 	struct mlx5_esw_bridge *bridge;
14543ee6233eSVlad Buslov 
1455b99c4ef2SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1456b99c4ef2SVlad Buslov 	if (!bridge)
1457c636a0f0SVlad Buslov 		return -EINVAL;
1458c636a0f0SVlad Buslov 
1459b99c4ef2SVlad Buslov 	bridge->ageing_time = clock_t_to_jiffies(ageing_time);
1460c636a0f0SVlad Buslov 	return 0;
1461c636a0f0SVlad Buslov }
1462c636a0f0SVlad Buslov 
mlx5_esw_bridge_vlan_filtering_set(u16 vport_num,u16 esw_owner_vhca_id,bool enable,struct mlx5_esw_bridge_offloads * br_offloads)14633ee6233eSVlad Buslov int mlx5_esw_bridge_vlan_filtering_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
14643ee6233eSVlad Buslov 				       struct mlx5_esw_bridge_offloads *br_offloads)
146519e9bfa0SVlad Buslov {
1466d75b9e80SVlad Buslov 	struct mlx5_esw_bridge *bridge;
1467d75b9e80SVlad Buslov 	bool filtering;
1468d75b9e80SVlad Buslov 
1469b99c4ef2SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1470b99c4ef2SVlad Buslov 	if (!bridge)
1471d75b9e80SVlad Buslov 		return -EINVAL;
1472d75b9e80SVlad Buslov 
1473d75b9e80SVlad Buslov 	filtering = bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG;
1474d75b9e80SVlad Buslov 	if (filtering == enable)
1475d75b9e80SVlad Buslov 		return 0;
1476d75b9e80SVlad Buslov 
1477d75b9e80SVlad Buslov 	mlx5_esw_bridge_fdb_flush(bridge);
147870f0302bSVlad Buslov 	mlx5_esw_bridge_mdb_flush(bridge);
1479d75b9e80SVlad Buslov 	if (enable)
1480d75b9e80SVlad Buslov 		bridge->flags |= MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG;
1481d75b9e80SVlad Buslov 	else
1482d75b9e80SVlad Buslov 		bridge->flags &= ~MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG;
1483d75b9e80SVlad Buslov 
1484d75b9e80SVlad Buslov 	return 0;
1485d75b9e80SVlad Buslov }
1486d75b9e80SVlad Buslov 
mlx5_esw_bridge_vlan_proto_set(u16 vport_num,u16 esw_owner_vhca_id,u16 proto,struct mlx5_esw_bridge_offloads * br_offloads)1487c5fcac93SVlad Buslov int mlx5_esw_bridge_vlan_proto_set(u16 vport_num, u16 esw_owner_vhca_id, u16 proto,
1488c5fcac93SVlad Buslov 				   struct mlx5_esw_bridge_offloads *br_offloads)
1489c5fcac93SVlad Buslov {
1490c5fcac93SVlad Buslov 	struct mlx5_esw_bridge *bridge;
1491c5fcac93SVlad Buslov 
1492b99c4ef2SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id,
1493c5fcac93SVlad Buslov 						  br_offloads);
1494b99c4ef2SVlad Buslov 	if (!bridge)
1495c5fcac93SVlad Buslov 		return -EINVAL;
1496c5fcac93SVlad Buslov 
1497c5fcac93SVlad Buslov 	if (bridge->vlan_proto == proto)
1498c5fcac93SVlad Buslov 		return 0;
14999c0ca9baSVlad Buslov 	if (proto != ETH_P_8021Q && proto != ETH_P_8021AD) {
1500c5fcac93SVlad Buslov 		esw_warn(br_offloads->esw->dev, "Can't set unsupported VLAN protocol %x", proto);
1501c5fcac93SVlad Buslov 		return -EOPNOTSUPP;
1502c5fcac93SVlad Buslov 	}
1503c5fcac93SVlad Buslov 
1504c5fcac93SVlad Buslov 	mlx5_esw_bridge_fdb_flush(bridge);
150570f0302bSVlad Buslov 	mlx5_esw_bridge_mdb_flush(bridge);
1506c5fcac93SVlad Buslov 	bridge->vlan_proto = proto;
1507c5fcac93SVlad Buslov 	mlx5_esw_bridge_vlans_recreate(bridge);
1508c5fcac93SVlad Buslov 
1509c5fcac93SVlad Buslov 	return 0;
1510c5fcac93SVlad Buslov }
1511c5fcac93SVlad Buslov 
mlx5_esw_bridge_mcast_set(u16 vport_num,u16 esw_owner_vhca_id,bool enable,struct mlx5_esw_bridge_offloads * br_offloads)151218c2916cSVlad Buslov int mlx5_esw_bridge_mcast_set(u16 vport_num, u16 esw_owner_vhca_id, bool enable,
151318c2916cSVlad Buslov 			      struct mlx5_esw_bridge_offloads *br_offloads)
151418c2916cSVlad Buslov {
151518c2916cSVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
151618c2916cSVlad Buslov 	struct mlx5_esw_bridge *bridge;
151718c2916cSVlad Buslov 	int err = 0;
151818c2916cSVlad Buslov 	bool mcast;
151918c2916cSVlad Buslov 
152018c2916cSVlad Buslov 	if (!(MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_multi_path_any_table) ||
152118c2916cSVlad Buslov 	      MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_multi_path_any_table_limit_regc)) ||
152218c2916cSVlad Buslov 	    !MLX5_CAP_ESW_FLOWTABLE((esw)->dev, fdb_uplink_hairpin) ||
152318c2916cSVlad Buslov 	    !MLX5_CAP_ESW_FLOWTABLE_FDB((esw)->dev, ignore_flow_level))
152418c2916cSVlad Buslov 		return -EOPNOTSUPP;
152518c2916cSVlad Buslov 
152618c2916cSVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
152718c2916cSVlad Buslov 	if (!bridge)
152818c2916cSVlad Buslov 		return -EINVAL;
152918c2916cSVlad Buslov 
153018c2916cSVlad Buslov 	mcast = bridge->flags & MLX5_ESW_BRIDGE_MCAST_FLAG;
153118c2916cSVlad Buslov 	if (mcast == enable)
153218c2916cSVlad Buslov 		return 0;
153318c2916cSVlad Buslov 
153418c2916cSVlad Buslov 	if (enable)
153518c2916cSVlad Buslov 		err = mlx5_esw_bridge_mcast_enable(bridge);
153618c2916cSVlad Buslov 	else
153718c2916cSVlad Buslov 		mlx5_esw_bridge_mcast_disable(bridge);
153818c2916cSVlad Buslov 
153918c2916cSVlad Buslov 	return err;
154018c2916cSVlad Buslov }
154118c2916cSVlad Buslov 
mlx5_esw_bridge_vport_init(u16 vport_num,u16 esw_owner_vhca_id,u16 flags,struct mlx5_esw_bridge_offloads * br_offloads,struct mlx5_esw_bridge * bridge)1542c358ea17SVlad Buslov static int mlx5_esw_bridge_vport_init(u16 vport_num, u16 esw_owner_vhca_id, u16 flags,
15433ee6233eSVlad Buslov 				      struct mlx5_esw_bridge_offloads *br_offloads,
15443ee6233eSVlad Buslov 				      struct mlx5_esw_bridge *bridge)
1545d75b9e80SVlad Buslov {
1546d75b9e80SVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
1547d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_port *port;
1548d75b9e80SVlad Buslov 	int err;
1549d75b9e80SVlad Buslov 
1550d75b9e80SVlad Buslov 	port = kvzalloc(sizeof(*port), GFP_KERNEL);
15514de20e9aSVlad Buslov 	if (!port)
15524de20e9aSVlad Buslov 		return -ENOMEM;
1553d75b9e80SVlad Buslov 
15543ee6233eSVlad Buslov 	port->vport_num = vport_num;
15553ee6233eSVlad Buslov 	port->esw_owner_vhca_id = esw_owner_vhca_id;
15563ee6233eSVlad Buslov 	port->bridge = bridge;
1557c358ea17SVlad Buslov 	port->flags |= flags;
1558d75b9e80SVlad Buslov 	xa_init(&port->vlans);
1559272ecfc9SVlad Buslov 
1560272ecfc9SVlad Buslov 	err = mlx5_esw_bridge_port_mcast_init(port);
1561272ecfc9SVlad Buslov 	if (err) {
1562272ecfc9SVlad Buslov 		esw_warn(esw->dev,
1563272ecfc9SVlad Buslov 			 "Failed to initialize port multicast (vport=%u,esw_owner_vhca_id=%u,err=%d)\n",
1564272ecfc9SVlad Buslov 			 port->vport_num, port->esw_owner_vhca_id, err);
1565272ecfc9SVlad Buslov 		goto err_port_mcast;
1566272ecfc9SVlad Buslov 	}
1567272ecfc9SVlad Buslov 
15683ee6233eSVlad Buslov 	err = mlx5_esw_bridge_port_insert(port, br_offloads);
1569d75b9e80SVlad Buslov 	if (err) {
15703ee6233eSVlad Buslov 		esw_warn(esw->dev,
15713ee6233eSVlad Buslov 			 "Failed to insert port metadata (vport=%u,esw_owner_vhca_id=%u,err=%d)\n",
15723ee6233eSVlad Buslov 			 port->vport_num, port->esw_owner_vhca_id, err);
1573d75b9e80SVlad Buslov 		goto err_port_insert;
1574d75b9e80SVlad Buslov 	}
15759724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_vport_init(port);
1576d75b9e80SVlad Buslov 
157719e9bfa0SVlad Buslov 	return 0;
1578d75b9e80SVlad Buslov 
1579d75b9e80SVlad Buslov err_port_insert:
1580272ecfc9SVlad Buslov 	mlx5_esw_bridge_port_mcast_cleanup(port);
1581272ecfc9SVlad Buslov err_port_mcast:
1582d75b9e80SVlad Buslov 	kvfree(port);
1583d75b9e80SVlad Buslov 	return err;
158419e9bfa0SVlad Buslov }
158519e9bfa0SVlad Buslov 
mlx5_esw_bridge_vport_cleanup(struct mlx5_esw_bridge_offloads * br_offloads,struct mlx5_esw_bridge_port * port)158619e9bfa0SVlad Buslov static int mlx5_esw_bridge_vport_cleanup(struct mlx5_esw_bridge_offloads *br_offloads,
15873ee6233eSVlad Buslov 					 struct mlx5_esw_bridge_port *port)
158819e9bfa0SVlad Buslov {
15893ee6233eSVlad Buslov 	u16 vport_num = port->vport_num, esw_owner_vhca_id = port->esw_owner_vhca_id;
15903ee6233eSVlad Buslov 	struct mlx5_esw_bridge *bridge = port->bridge;
15917cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry, *tmp;
15927cd6a54aSVlad Buslov 
15937cd6a54aSVlad Buslov 	list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list)
15943ee6233eSVlad Buslov 		if (entry->vport_num == vport_num && entry->esw_owner_vhca_id == esw_owner_vhca_id)
15957cd6a54aSVlad Buslov 			mlx5_esw_bridge_fdb_entry_cleanup(entry, bridge);
15967cd6a54aSVlad Buslov 
15979724fd5dSVlad Buslov 	trace_mlx5_esw_bridge_vport_cleanup(port);
159836e55079SVlad Buslov 	mlx5_esw_bridge_port_vlans_flush(port, bridge);
1599272ecfc9SVlad Buslov 	mlx5_esw_bridge_port_mcast_cleanup(port);
16003ee6233eSVlad Buslov 	mlx5_esw_bridge_port_erase(port, br_offloads);
1601d75b9e80SVlad Buslov 	kvfree(port);
16027cd6a54aSVlad Buslov 	mlx5_esw_bridge_put(br_offloads, bridge);
160319e9bfa0SVlad Buslov 	return 0;
160419e9bfa0SVlad Buslov }
160519e9bfa0SVlad Buslov 
mlx5_esw_bridge_vport_link_with_flags(struct net_device * br_netdev,u16 vport_num,u16 esw_owner_vhca_id,u16 flags,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)1606ade19f0dSVlad Buslov static int mlx5_esw_bridge_vport_link_with_flags(struct net_device *br_netdev, u16 vport_num,
1607ade19f0dSVlad Buslov 						 u16 esw_owner_vhca_id, u16 flags,
16083ee6233eSVlad Buslov 						 struct mlx5_esw_bridge_offloads *br_offloads,
16093ee6233eSVlad Buslov 						 struct netlink_ext_ack *extack)
161019e9bfa0SVlad Buslov {
161119e9bfa0SVlad Buslov 	struct mlx5_esw_bridge *bridge;
1612d75b9e80SVlad Buslov 	int err;
161319e9bfa0SVlad Buslov 
1614ade19f0dSVlad Buslov 	bridge = mlx5_esw_bridge_lookup(br_netdev, br_offloads);
161519e9bfa0SVlad Buslov 	if (IS_ERR(bridge)) {
161619e9bfa0SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Error checking for existing bridge with same ifindex");
161719e9bfa0SVlad Buslov 		return PTR_ERR(bridge);
161819e9bfa0SVlad Buslov 	}
161919e9bfa0SVlad Buslov 
1620c358ea17SVlad Buslov 	err = mlx5_esw_bridge_vport_init(vport_num, esw_owner_vhca_id, flags, br_offloads, bridge);
16214de20e9aSVlad Buslov 	if (err) {
1622d75b9e80SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Error initializing port");
16234de20e9aSVlad Buslov 		goto err_vport;
16244de20e9aSVlad Buslov 	}
16254de20e9aSVlad Buslov 	return 0;
16264de20e9aSVlad Buslov 
16274de20e9aSVlad Buslov err_vport:
16284de20e9aSVlad Buslov 	mlx5_esw_bridge_put(br_offloads, bridge);
1629d75b9e80SVlad Buslov 	return err;
163019e9bfa0SVlad Buslov }
163119e9bfa0SVlad Buslov 
mlx5_esw_bridge_vport_link(struct net_device * br_netdev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)1632ade19f0dSVlad Buslov int mlx5_esw_bridge_vport_link(struct net_device *br_netdev, u16 vport_num, u16 esw_owner_vhca_id,
1633c358ea17SVlad Buslov 			       struct mlx5_esw_bridge_offloads *br_offloads,
1634c358ea17SVlad Buslov 			       struct netlink_ext_ack *extack)
1635c358ea17SVlad Buslov {
1636ade19f0dSVlad Buslov 	return mlx5_esw_bridge_vport_link_with_flags(br_netdev, vport_num, esw_owner_vhca_id, 0,
1637c358ea17SVlad Buslov 						     br_offloads, extack);
1638c358ea17SVlad Buslov }
1639c358ea17SVlad Buslov 
mlx5_esw_bridge_vport_unlink(struct net_device * br_netdev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)1640ade19f0dSVlad Buslov int mlx5_esw_bridge_vport_unlink(struct net_device *br_netdev, u16 vport_num,
1641ade19f0dSVlad Buslov 				 u16 esw_owner_vhca_id,
16423ee6233eSVlad Buslov 				 struct mlx5_esw_bridge_offloads *br_offloads,
16433ee6233eSVlad Buslov 				 struct netlink_ext_ack *extack)
164419e9bfa0SVlad Buslov {
16453ee6233eSVlad Buslov 	struct mlx5_esw_bridge_port *port;
1646d75b9e80SVlad Buslov 	int err;
16477cd6a54aSVlad Buslov 
16483ee6233eSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
16493ee6233eSVlad Buslov 	if (!port) {
165019e9bfa0SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Port is not attached to any bridge");
165119e9bfa0SVlad Buslov 		return -EINVAL;
165219e9bfa0SVlad Buslov 	}
1653ade19f0dSVlad Buslov 	if (port->bridge->ifindex != br_netdev->ifindex) {
165419e9bfa0SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Port is attached to another bridge");
165519e9bfa0SVlad Buslov 		return -EINVAL;
165619e9bfa0SVlad Buslov 	}
165719e9bfa0SVlad Buslov 
16583ee6233eSVlad Buslov 	err = mlx5_esw_bridge_vport_cleanup(br_offloads, port);
1659d75b9e80SVlad Buslov 	if (err)
1660d75b9e80SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Port cleanup failed");
1661d75b9e80SVlad Buslov 	return err;
1662d75b9e80SVlad Buslov }
1663d75b9e80SVlad Buslov 
mlx5_esw_bridge_vport_peer_link(struct net_device * br_netdev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)1664ade19f0dSVlad Buslov int mlx5_esw_bridge_vport_peer_link(struct net_device *br_netdev, u16 vport_num,
1665ade19f0dSVlad Buslov 				    u16 esw_owner_vhca_id,
1666c358ea17SVlad Buslov 				    struct mlx5_esw_bridge_offloads *br_offloads,
1667c358ea17SVlad Buslov 				    struct netlink_ext_ack *extack)
1668c358ea17SVlad Buslov {
1669c358ea17SVlad Buslov 	if (!MLX5_CAP_ESW(br_offloads->esw->dev, merged_eswitch))
1670c358ea17SVlad Buslov 		return 0;
1671c358ea17SVlad Buslov 
1672ade19f0dSVlad Buslov 	return mlx5_esw_bridge_vport_link_with_flags(br_netdev, vport_num, esw_owner_vhca_id,
1673c358ea17SVlad Buslov 						     MLX5_ESW_BRIDGE_PORT_FLAG_PEER,
1674c358ea17SVlad Buslov 						     br_offloads, extack);
1675c358ea17SVlad Buslov }
1676c358ea17SVlad Buslov 
mlx5_esw_bridge_vport_peer_unlink(struct net_device * br_netdev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)1677ade19f0dSVlad Buslov int mlx5_esw_bridge_vport_peer_unlink(struct net_device *br_netdev, u16 vport_num,
1678ade19f0dSVlad Buslov 				      u16 esw_owner_vhca_id,
1679c358ea17SVlad Buslov 				      struct mlx5_esw_bridge_offloads *br_offloads,
1680c358ea17SVlad Buslov 				      struct netlink_ext_ack *extack)
1681c358ea17SVlad Buslov {
1682ade19f0dSVlad Buslov 	return mlx5_esw_bridge_vport_unlink(br_netdev, vport_num, esw_owner_vhca_id, br_offloads,
1683c358ea17SVlad Buslov 					    extack);
1684c358ea17SVlad Buslov }
1685c358ea17SVlad Buslov 
mlx5_esw_bridge_port_vlan_add(u16 vport_num,u16 esw_owner_vhca_id,u16 vid,u16 flags,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)16863ee6233eSVlad Buslov int mlx5_esw_bridge_port_vlan_add(u16 vport_num, u16 esw_owner_vhca_id, u16 vid, u16 flags,
16873ee6233eSVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads,
16883ee6233eSVlad Buslov 				  struct netlink_ext_ack *extack)
1689d75b9e80SVlad Buslov {
1690d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_port *port;
1691d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1692d75b9e80SVlad Buslov 
16933ee6233eSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1694d75b9e80SVlad Buslov 	if (!port)
1695d75b9e80SVlad Buslov 		return -EINVAL;
1696d75b9e80SVlad Buslov 
1697d75b9e80SVlad Buslov 	vlan = mlx5_esw_bridge_vlan_lookup(vid, port);
1698d75b9e80SVlad Buslov 	if (vlan) {
169936e55079SVlad Buslov 		if (vlan->flags == flags)
1700d75b9e80SVlad Buslov 			return 0;
17013ee6233eSVlad Buslov 		mlx5_esw_bridge_vlan_cleanup(port, vlan, port->bridge);
1702d75b9e80SVlad Buslov 	}
1703d75b9e80SVlad Buslov 
1704c5fcac93SVlad Buslov 	vlan = mlx5_esw_bridge_vlan_create(port->bridge->vlan_proto, vid, flags, port,
1705c5fcac93SVlad Buslov 					   br_offloads->esw);
1706d75b9e80SVlad Buslov 	if (IS_ERR(vlan)) {
1707d75b9e80SVlad Buslov 		NL_SET_ERR_MSG_MOD(extack, "Failed to create VLAN entry");
1708d75b9e80SVlad Buslov 		return PTR_ERR(vlan);
1709d75b9e80SVlad Buslov 	}
1710d75b9e80SVlad Buslov 	return 0;
1711d75b9e80SVlad Buslov }
1712d75b9e80SVlad Buslov 
mlx5_esw_bridge_port_vlan_del(u16 vport_num,u16 esw_owner_vhca_id,u16 vid,struct mlx5_esw_bridge_offloads * br_offloads)17133ee6233eSVlad Buslov void mlx5_esw_bridge_port_vlan_del(u16 vport_num, u16 esw_owner_vhca_id, u16 vid,
17143ee6233eSVlad Buslov 				   struct mlx5_esw_bridge_offloads *br_offloads)
1715d75b9e80SVlad Buslov {
1716d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_port *port;
1717d75b9e80SVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
1718d75b9e80SVlad Buslov 
17193ee6233eSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1720d75b9e80SVlad Buslov 	if (!port)
1721d75b9e80SVlad Buslov 		return;
1722d75b9e80SVlad Buslov 
1723d75b9e80SVlad Buslov 	vlan = mlx5_esw_bridge_vlan_lookup(vid, port);
1724d75b9e80SVlad Buslov 	if (!vlan)
1725d75b9e80SVlad Buslov 		return;
17263ee6233eSVlad Buslov 	mlx5_esw_bridge_vlan_cleanup(port, vlan, port->bridge);
172719e9bfa0SVlad Buslov }
172819e9bfa0SVlad Buslov 
mlx5_esw_bridge_fdb_update_used(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct switchdev_notifier_fdb_info * fdb_info)1729ff9b7521SVlad Buslov void mlx5_esw_bridge_fdb_update_used(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
1730ff9b7521SVlad Buslov 				     struct mlx5_esw_bridge_offloads *br_offloads,
1731ff9b7521SVlad Buslov 				     struct switchdev_notifier_fdb_info *fdb_info)
1732ff9b7521SVlad Buslov {
1733ff9b7521SVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry;
1734ff9b7521SVlad Buslov 	struct mlx5_esw_bridge *bridge;
1735ff9b7521SVlad Buslov 
1736b99c4ef2SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1737b99c4ef2SVlad Buslov 	if (!bridge)
1738ff9b7521SVlad Buslov 		return;
1739ff9b7521SVlad Buslov 
17402deda2f1SVlad Buslov 	entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid);
1741ff9b7521SVlad Buslov 	if (!entry) {
1742ff9b7521SVlad Buslov 		esw_debug(br_offloads->esw->dev,
1743ea645f97SRoi Dayan 			  "FDB update entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n",
17442deda2f1SVlad Buslov 			  fdb_info->addr, fdb_info->vid, vport_num);
1745ff9b7521SVlad Buslov 		return;
1746ff9b7521SVlad Buslov 	}
1747ff9b7521SVlad Buslov 
1748ff9b7521SVlad Buslov 	entry->lastuse = jiffies;
1749ff9b7521SVlad Buslov }
1750ff9b7521SVlad Buslov 
mlx5_esw_bridge_fdb_mark_deleted(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct switchdev_notifier_fdb_info * fdb_info)17517a3ce807SVlad Buslov void mlx5_esw_bridge_fdb_mark_deleted(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
17527a3ce807SVlad Buslov 				      struct mlx5_esw_bridge_offloads *br_offloads,
17537a3ce807SVlad Buslov 				      struct switchdev_notifier_fdb_info *fdb_info)
17547a3ce807SVlad Buslov {
17557a3ce807SVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry;
17567a3ce807SVlad Buslov 	struct mlx5_esw_bridge *bridge;
17577a3ce807SVlad Buslov 
17587a3ce807SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
17597a3ce807SVlad Buslov 	if (!bridge)
17607a3ce807SVlad Buslov 		return;
17617a3ce807SVlad Buslov 
17627a3ce807SVlad Buslov 	entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid);
17637a3ce807SVlad Buslov 	if (!entry) {
17647a3ce807SVlad Buslov 		esw_debug(br_offloads->esw->dev,
17657a3ce807SVlad Buslov 			  "FDB mark deleted entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n",
17667a3ce807SVlad Buslov 			  fdb_info->addr, fdb_info->vid, vport_num);
17677a3ce807SVlad Buslov 		return;
17687a3ce807SVlad Buslov 	}
17697a3ce807SVlad Buslov 
17707a3ce807SVlad Buslov 	entry->flags |= MLX5_ESW_BRIDGE_FLAG_DELETED;
17717a3ce807SVlad Buslov }
17727a3ce807SVlad Buslov 
mlx5_esw_bridge_fdb_create(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct switchdev_notifier_fdb_info * fdb_info)17733ee6233eSVlad Buslov void mlx5_esw_bridge_fdb_create(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
17743ee6233eSVlad Buslov 				struct mlx5_esw_bridge_offloads *br_offloads,
17757cd6a54aSVlad Buslov 				struct switchdev_notifier_fdb_info *fdb_info)
17767cd6a54aSVlad Buslov {
17777cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry;
17783ee6233eSVlad Buslov 	struct mlx5_esw_bridge_port *port;
17793ee6233eSVlad Buslov 	struct mlx5_esw_bridge *bridge;
17807cd6a54aSVlad Buslov 
17813ee6233eSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
17823ee6233eSVlad Buslov 	if (!port)
17837cd6a54aSVlad Buslov 		return;
17847cd6a54aSVlad Buslov 
17853ee6233eSVlad Buslov 	bridge = port->bridge;
17863ee6233eSVlad Buslov 	entry = mlx5_esw_bridge_fdb_entry_init(dev, vport_num, esw_owner_vhca_id, fdb_info->addr,
17873ee6233eSVlad Buslov 					       fdb_info->vid, fdb_info->added_by_user,
1788c358ea17SVlad Buslov 					       port->flags & MLX5_ESW_BRIDGE_PORT_FLAG_PEER,
17893ee6233eSVlad Buslov 					       br_offloads->esw, bridge);
17907cd6a54aSVlad Buslov 	if (IS_ERR(entry))
17917cd6a54aSVlad Buslov 		return;
17927cd6a54aSVlad Buslov 
1793c636a0f0SVlad Buslov 	if (entry->flags & MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER)
17947cd6a54aSVlad Buslov 		mlx5_esw_bridge_fdb_offload_notify(dev, entry->key.addr, entry->key.vid,
17957cd6a54aSVlad Buslov 						   SWITCHDEV_FDB_OFFLOADED);
1796c358ea17SVlad Buslov 	else if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_PEER))
1797c636a0f0SVlad Buslov 		/* Take over dynamic entries to prevent kernel bridge from aging them out. */
1798c636a0f0SVlad Buslov 		mlx5_esw_bridge_fdb_offload_notify(dev, entry->key.addr, entry->key.vid,
1799c636a0f0SVlad Buslov 						   SWITCHDEV_FDB_ADD_TO_BRIDGE);
18007cd6a54aSVlad Buslov }
18017cd6a54aSVlad Buslov 
mlx5_esw_bridge_fdb_remove(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,struct mlx5_esw_bridge_offloads * br_offloads,struct switchdev_notifier_fdb_info * fdb_info)18023ee6233eSVlad Buslov void mlx5_esw_bridge_fdb_remove(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
18033ee6233eSVlad Buslov 				struct mlx5_esw_bridge_offloads *br_offloads,
18047cd6a54aSVlad Buslov 				struct switchdev_notifier_fdb_info *fdb_info)
18057cd6a54aSVlad Buslov {
18063ee6233eSVlad Buslov 	struct mlx5_eswitch *esw = br_offloads->esw;
18077cd6a54aSVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry;
18083ee6233eSVlad Buslov 	struct mlx5_esw_bridge *bridge;
18097cd6a54aSVlad Buslov 
1810b99c4ef2SVlad Buslov 	bridge = mlx5_esw_bridge_from_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
1811b99c4ef2SVlad Buslov 	if (!bridge)
18127cd6a54aSVlad Buslov 		return;
18137cd6a54aSVlad Buslov 
18142deda2f1SVlad Buslov 	entry = mlx5_esw_bridge_fdb_lookup(bridge, fdb_info->addr, fdb_info->vid);
18157cd6a54aSVlad Buslov 	if (!entry) {
1816ea645f97SRoi Dayan 		esw_debug(esw->dev,
1817ea645f97SRoi Dayan 			  "FDB remove entry with specified key not found (MAC=%pM,vid=%u,vport=%u)\n",
18182deda2f1SVlad Buslov 			  fdb_info->addr, fdb_info->vid, vport_num);
18197cd6a54aSVlad Buslov 		return;
18207cd6a54aSVlad Buslov 	}
18217cd6a54aSVlad Buslov 
18222deda2f1SVlad Buslov 	mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
18237cd6a54aSVlad Buslov }
18247cd6a54aSVlad Buslov 
mlx5_esw_bridge_update(struct mlx5_esw_bridge_offloads * br_offloads)1825c636a0f0SVlad Buslov void mlx5_esw_bridge_update(struct mlx5_esw_bridge_offloads *br_offloads)
1826c636a0f0SVlad Buslov {
1827c636a0f0SVlad Buslov 	struct mlx5_esw_bridge_fdb_entry *entry, *tmp;
1828c636a0f0SVlad Buslov 	struct mlx5_esw_bridge *bridge;
1829c636a0f0SVlad Buslov 
1830c636a0f0SVlad Buslov 	list_for_each_entry(bridge, &br_offloads->bridges, list) {
1831c636a0f0SVlad Buslov 		list_for_each_entry_safe(entry, tmp, &bridge->fdb_list, list) {
1832c636a0f0SVlad Buslov 			unsigned long lastuse =
1833c636a0f0SVlad Buslov 				(unsigned long)mlx5_fc_query_lastuse(entry->ingress_counter);
1834c636a0f0SVlad Buslov 
18357a3ce807SVlad Buslov 			if (entry->flags & (MLX5_ESW_BRIDGE_FLAG_ADDED_BY_USER |
18367a3ce807SVlad Buslov 					    MLX5_ESW_BRIDGE_FLAG_DELETED))
1837c636a0f0SVlad Buslov 				continue;
1838c636a0f0SVlad Buslov 
18392deda2f1SVlad Buslov 			if (time_after(lastuse, entry->lastuse))
1840ff9b7521SVlad Buslov 				mlx5_esw_bridge_fdb_entry_refresh(entry);
18412deda2f1SVlad Buslov 			else if (!(entry->flags & MLX5_ESW_BRIDGE_FLAG_PEER) &&
18422deda2f1SVlad Buslov 				 time_is_before_jiffies(entry->lastuse + bridge->ageing_time))
18432deda2f1SVlad Buslov 				mlx5_esw_bridge_fdb_entry_notify_and_cleanup(entry, bridge);
1844c636a0f0SVlad Buslov 		}
1845c636a0f0SVlad Buslov 	}
1846c636a0f0SVlad Buslov }
1847c636a0f0SVlad Buslov 
mlx5_esw_bridge_port_mdb_add(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,const unsigned char * addr,u16 vid,struct mlx5_esw_bridge_offloads * br_offloads,struct netlink_ext_ack * extack)184855f3e740SVlad Buslov int mlx5_esw_bridge_port_mdb_add(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
184955f3e740SVlad Buslov 				 const unsigned char *addr, u16 vid,
185055f3e740SVlad Buslov 				 struct mlx5_esw_bridge_offloads *br_offloads,
185170f0302bSVlad Buslov 				 struct netlink_ext_ack *extack)
185270f0302bSVlad Buslov {
185370f0302bSVlad Buslov 	struct mlx5_esw_bridge_vlan *vlan;
185470f0302bSVlad Buslov 	struct mlx5_esw_bridge_port *port;
185570f0302bSVlad Buslov 	struct mlx5_esw_bridge *bridge;
185670f0302bSVlad Buslov 	int err;
185770f0302bSVlad Buslov 
185870f0302bSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
185970f0302bSVlad Buslov 	if (!port) {
186070f0302bSVlad Buslov 		esw_warn(br_offloads->esw->dev,
186170f0302bSVlad Buslov 			 "Failed to lookup bridge port to add MDB (MAC=%pM,vport=%u)\n",
186270f0302bSVlad Buslov 			 addr, vport_num);
186370f0302bSVlad Buslov 		NL_SET_ERR_MSG_FMT_MOD(extack,
186470f0302bSVlad Buslov 				       "Failed to lookup bridge port to add MDB (MAC=%pM,vport=%u)\n",
186570f0302bSVlad Buslov 				       addr, vport_num);
186670f0302bSVlad Buslov 		return -EINVAL;
186770f0302bSVlad Buslov 	}
186870f0302bSVlad Buslov 
186970f0302bSVlad Buslov 	bridge = port->bridge;
187070f0302bSVlad Buslov 	if (bridge->flags & MLX5_ESW_BRIDGE_VLAN_FILTERING_FLAG && vid) {
187170f0302bSVlad Buslov 		vlan = mlx5_esw_bridge_vlan_lookup(vid, port);
187270f0302bSVlad Buslov 		if (!vlan) {
187370f0302bSVlad Buslov 			esw_warn(br_offloads->esw->dev,
187470f0302bSVlad Buslov 				 "Failed to lookup bridge port vlan metadata to create MDB (MAC=%pM,vid=%u,vport=%u)\n",
187570f0302bSVlad Buslov 				 addr, vid, vport_num);
187670f0302bSVlad Buslov 			NL_SET_ERR_MSG_FMT_MOD(extack,
18777dd2a9bbSArnd Bergmann 					       "Failed to lookup vlan metadata for MDB (MAC=%pM,vid=%u,vport=%u)\n",
187870f0302bSVlad Buslov 					       addr, vid, vport_num);
187970f0302bSVlad Buslov 			return -EINVAL;
188070f0302bSVlad Buslov 		}
188170f0302bSVlad Buslov 	}
188270f0302bSVlad Buslov 
188355f3e740SVlad Buslov 	err = mlx5_esw_bridge_port_mdb_attach(dev, port, addr, vid);
188470f0302bSVlad Buslov 	if (err) {
188570f0302bSVlad Buslov 		NL_SET_ERR_MSG_FMT_MOD(extack, "Failed to add MDB (MAC=%pM,vid=%u,vport=%u)\n",
188670f0302bSVlad Buslov 				       addr, vid, vport_num);
188770f0302bSVlad Buslov 		return err;
188870f0302bSVlad Buslov 	}
188970f0302bSVlad Buslov 
189070f0302bSVlad Buslov 	return 0;
189170f0302bSVlad Buslov }
189270f0302bSVlad Buslov 
mlx5_esw_bridge_port_mdb_del(struct net_device * dev,u16 vport_num,u16 esw_owner_vhca_id,const unsigned char * addr,u16 vid,struct mlx5_esw_bridge_offloads * br_offloads)189355f3e740SVlad Buslov void mlx5_esw_bridge_port_mdb_del(struct net_device *dev, u16 vport_num, u16 esw_owner_vhca_id,
189455f3e740SVlad Buslov 				  const unsigned char *addr, u16 vid,
189555f3e740SVlad Buslov 				  struct mlx5_esw_bridge_offloads *br_offloads)
189670f0302bSVlad Buslov {
189770f0302bSVlad Buslov 	struct mlx5_esw_bridge_port *port;
189870f0302bSVlad Buslov 
189970f0302bSVlad Buslov 	port = mlx5_esw_bridge_port_lookup(vport_num, esw_owner_vhca_id, br_offloads);
190070f0302bSVlad Buslov 	if (!port)
190170f0302bSVlad Buslov 		return;
190270f0302bSVlad Buslov 
190355f3e740SVlad Buslov 	mlx5_esw_bridge_port_mdb_detach(dev, port, addr, vid);
190470f0302bSVlad Buslov }
190570f0302bSVlad Buslov 
mlx5_esw_bridge_flush(struct mlx5_esw_bridge_offloads * br_offloads)190619e9bfa0SVlad Buslov static void mlx5_esw_bridge_flush(struct mlx5_esw_bridge_offloads *br_offloads)
190719e9bfa0SVlad Buslov {
19083ee6233eSVlad Buslov 	struct mlx5_esw_bridge_port *port;
190919e9bfa0SVlad Buslov 	unsigned long i;
191019e9bfa0SVlad Buslov 
19113ee6233eSVlad Buslov 	xa_for_each(&br_offloads->ports, i, port)
19123ee6233eSVlad Buslov 		mlx5_esw_bridge_vport_cleanup(br_offloads, port);
191319e9bfa0SVlad Buslov 
191419e9bfa0SVlad Buslov 	WARN_ONCE(!list_empty(&br_offloads->bridges),
191519e9bfa0SVlad Buslov 		  "Cleaning up bridge offloads while still having bridges attached\n");
191619e9bfa0SVlad Buslov }
191719e9bfa0SVlad Buslov 
mlx5_esw_bridge_init(struct mlx5_eswitch * esw)191819e9bfa0SVlad Buslov struct mlx5_esw_bridge_offloads *mlx5_esw_bridge_init(struct mlx5_eswitch *esw)
191919e9bfa0SVlad Buslov {
192019e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads;
192119e9bfa0SVlad Buslov 
192204f8c12fSVlad Buslov 	ASSERT_RTNL();
192304f8c12fSVlad Buslov 
192419e9bfa0SVlad Buslov 	br_offloads = kvzalloc(sizeof(*br_offloads), GFP_KERNEL);
192519e9bfa0SVlad Buslov 	if (!br_offloads)
192619e9bfa0SVlad Buslov 		return ERR_PTR(-ENOMEM);
192719e9bfa0SVlad Buslov 
192819e9bfa0SVlad Buslov 	INIT_LIST_HEAD(&br_offloads->bridges);
19293ee6233eSVlad Buslov 	xa_init(&br_offloads->ports);
193019e9bfa0SVlad Buslov 	br_offloads->esw = esw;
193119e9bfa0SVlad Buslov 	esw->br_offloads = br_offloads;
1932791eb782SVlad Buslov 	mlx5_esw_bridge_debugfs_offloads_init(br_offloads);
193319e9bfa0SVlad Buslov 
193419e9bfa0SVlad Buslov 	return br_offloads;
193519e9bfa0SVlad Buslov }
193619e9bfa0SVlad Buslov 
mlx5_esw_bridge_cleanup(struct mlx5_eswitch * esw)193719e9bfa0SVlad Buslov void mlx5_esw_bridge_cleanup(struct mlx5_eswitch *esw)
193819e9bfa0SVlad Buslov {
193919e9bfa0SVlad Buslov 	struct mlx5_esw_bridge_offloads *br_offloads = esw->br_offloads;
194019e9bfa0SVlad Buslov 
194104f8c12fSVlad Buslov 	ASSERT_RTNL();
194204f8c12fSVlad Buslov 
194319e9bfa0SVlad Buslov 	if (!br_offloads)
194419e9bfa0SVlad Buslov 		return;
194519e9bfa0SVlad Buslov 
194619e9bfa0SVlad Buslov 	mlx5_esw_bridge_flush(br_offloads);
19473ee6233eSVlad Buslov 	WARN_ON(!xa_empty(&br_offloads->ports));
1948791eb782SVlad Buslov 	mlx5_esw_bridge_debugfs_offloads_cleanup(br_offloads);
194919e9bfa0SVlad Buslov 
195019e9bfa0SVlad Buslov 	esw->br_offloads = NULL;
195119e9bfa0SVlad Buslov 	kvfree(br_offloads);
195219e9bfa0SVlad Buslov }
1953