xref: /freebsd/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c (revision 91ad1bd9)
191ad1bd9SKonstantin Belousov /*-
291ad1bd9SKonstantin Belousov  * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
391ad1bd9SKonstantin Belousov  *
491ad1bd9SKonstantin Belousov  * Redistribution and use in source and binary forms, with or without
591ad1bd9SKonstantin Belousov  * modification, are permitted provided that the following conditions
691ad1bd9SKonstantin Belousov  * are met:
791ad1bd9SKonstantin Belousov  * 1. Redistributions of source code must retain the above copyright
891ad1bd9SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer.
991ad1bd9SKonstantin Belousov  * 2. Redistributions in binary form must reproduce the above copyright
1091ad1bd9SKonstantin Belousov  *    notice, this list of conditions and the following disclaimer in the
1191ad1bd9SKonstantin Belousov  *    documentation and/or other materials provided with the distribution.
1291ad1bd9SKonstantin Belousov  *
1391ad1bd9SKonstantin Belousov  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
1491ad1bd9SKonstantin Belousov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1591ad1bd9SKonstantin Belousov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1691ad1bd9SKonstantin Belousov  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
1791ad1bd9SKonstantin Belousov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1891ad1bd9SKonstantin Belousov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1991ad1bd9SKonstantin Belousov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2091ad1bd9SKonstantin Belousov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2191ad1bd9SKonstantin Belousov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2291ad1bd9SKonstantin Belousov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2391ad1bd9SKonstantin Belousov  * SUCH DAMAGE.
2491ad1bd9SKonstantin Belousov  *
2591ad1bd9SKonstantin Belousov  * $FreeBSD$
2691ad1bd9SKonstantin Belousov  */
2791ad1bd9SKonstantin Belousov 
2891ad1bd9SKonstantin Belousov #include <linux/etherdevice.h>
2991ad1bd9SKonstantin Belousov #include <dev/mlx5/driver.h>
3091ad1bd9SKonstantin Belousov #include <dev/mlx5/mlx5_ifc.h>
3191ad1bd9SKonstantin Belousov #include <dev/mlx5/vport.h>
3291ad1bd9SKonstantin Belousov #include <dev/mlx5/fs.h>
3391ad1bd9SKonstantin Belousov #include "mlx5_core.h"
3491ad1bd9SKonstantin Belousov #include "eswitch.h"
3591ad1bd9SKonstantin Belousov 
3691ad1bd9SKonstantin Belousov #define UPLINK_VPORT 0xFFFF
3791ad1bd9SKonstantin Belousov 
3891ad1bd9SKonstantin Belousov #define MLX5_DEBUG_ESWITCH_MASK BIT(3)
3991ad1bd9SKonstantin Belousov 
4091ad1bd9SKonstantin Belousov #define esw_info(dev, format, ...)				\
4191ad1bd9SKonstantin Belousov 	printf("mlx5_core: INFO: ""(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
4291ad1bd9SKonstantin Belousov 
4391ad1bd9SKonstantin Belousov #define esw_warn(dev, format, ...)				\
4491ad1bd9SKonstantin Belousov 	printf("mlx5_core: WARN: ""(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
4591ad1bd9SKonstantin Belousov 
4691ad1bd9SKonstantin Belousov #define esw_debug(dev, format, ...)				\
4791ad1bd9SKonstantin Belousov 	mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
4891ad1bd9SKonstantin Belousov 
4991ad1bd9SKonstantin Belousov enum {
5091ad1bd9SKonstantin Belousov 	MLX5_ACTION_NONE = 0,
5191ad1bd9SKonstantin Belousov 	MLX5_ACTION_ADD  = 1,
5291ad1bd9SKonstantin Belousov 	MLX5_ACTION_DEL  = 2,
5391ad1bd9SKonstantin Belousov };
5491ad1bd9SKonstantin Belousov 
5591ad1bd9SKonstantin Belousov /* E-Switch UC L2 table hash node */
5691ad1bd9SKonstantin Belousov struct esw_uc_addr {
5791ad1bd9SKonstantin Belousov 	struct l2addr_node node;
5891ad1bd9SKonstantin Belousov 	u32                table_index;
5991ad1bd9SKonstantin Belousov 	u32                vport;
6091ad1bd9SKonstantin Belousov };
6191ad1bd9SKonstantin Belousov 
6291ad1bd9SKonstantin Belousov /* E-Switch MC FDB table hash node */
6391ad1bd9SKonstantin Belousov struct esw_mc_addr { /* SRIOV only */
6491ad1bd9SKonstantin Belousov 	struct l2addr_node     node;
6591ad1bd9SKonstantin Belousov 	struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */
6691ad1bd9SKonstantin Belousov 	u32                    refcnt;
6791ad1bd9SKonstantin Belousov };
6891ad1bd9SKonstantin Belousov 
6991ad1bd9SKonstantin Belousov /* Vport UC/MC hash node */
7091ad1bd9SKonstantin Belousov struct vport_addr {
7191ad1bd9SKonstantin Belousov 	struct l2addr_node     node;
7291ad1bd9SKonstantin Belousov 	u8                     action;
7391ad1bd9SKonstantin Belousov 	u32                    vport;
7491ad1bd9SKonstantin Belousov 	struct mlx5_flow_rule *flow_rule; /* SRIOV only */
7591ad1bd9SKonstantin Belousov };
7691ad1bd9SKonstantin Belousov 
7791ad1bd9SKonstantin Belousov enum {
7891ad1bd9SKonstantin Belousov 	UC_ADDR_CHANGE = BIT(0),
7991ad1bd9SKonstantin Belousov 	MC_ADDR_CHANGE = BIT(1),
8091ad1bd9SKonstantin Belousov };
8191ad1bd9SKonstantin Belousov 
8291ad1bd9SKonstantin Belousov /* Vport context events */
8391ad1bd9SKonstantin Belousov #define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
8491ad1bd9SKonstantin Belousov 			    MC_ADDR_CHANGE)
8591ad1bd9SKonstantin Belousov 
8691ad1bd9SKonstantin Belousov static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
8791ad1bd9SKonstantin Belousov 					u32 events_mask)
8891ad1bd9SKonstantin Belousov {
8991ad1bd9SKonstantin Belousov 	int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {0};
9091ad1bd9SKonstantin Belousov 	int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
9191ad1bd9SKonstantin Belousov 	void *nic_vport_ctx;
9291ad1bd9SKonstantin Belousov 
9391ad1bd9SKonstantin Belousov 	MLX5_SET(modify_nic_vport_context_in, in,
9491ad1bd9SKonstantin Belousov 		 opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
9591ad1bd9SKonstantin Belousov 	MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
9691ad1bd9SKonstantin Belousov 	MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
9791ad1bd9SKonstantin Belousov 	if (vport)
9891ad1bd9SKonstantin Belousov 		MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
9991ad1bd9SKonstantin Belousov 	nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
10091ad1bd9SKonstantin Belousov 				     in, nic_vport_context);
10191ad1bd9SKonstantin Belousov 
10291ad1bd9SKonstantin Belousov 	MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
10391ad1bd9SKonstantin Belousov 
10491ad1bd9SKonstantin Belousov 	if (events_mask & UC_ADDR_CHANGE)
10591ad1bd9SKonstantin Belousov 		MLX5_SET(nic_vport_context, nic_vport_ctx,
10691ad1bd9SKonstantin Belousov 			 event_on_uc_address_change, 1);
10791ad1bd9SKonstantin Belousov 	if (events_mask & MC_ADDR_CHANGE)
10891ad1bd9SKonstantin Belousov 		MLX5_SET(nic_vport_context, nic_vport_ctx,
10991ad1bd9SKonstantin Belousov 			 event_on_mc_address_change, 1);
11091ad1bd9SKonstantin Belousov 
11191ad1bd9SKonstantin Belousov 	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
11291ad1bd9SKonstantin Belousov }
11391ad1bd9SKonstantin Belousov 
11491ad1bd9SKonstantin Belousov /* E-Switch vport context HW commands */
11591ad1bd9SKonstantin Belousov static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
11691ad1bd9SKonstantin Belousov 				       u32 *out, int outlen)
11791ad1bd9SKonstantin Belousov {
11891ad1bd9SKonstantin Belousov 	u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {0};
11991ad1bd9SKonstantin Belousov 
12091ad1bd9SKonstantin Belousov 	MLX5_SET(query_nic_vport_context_in, in, opcode,
12191ad1bd9SKonstantin Belousov 		 MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
12291ad1bd9SKonstantin Belousov 
12391ad1bd9SKonstantin Belousov 	MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
12491ad1bd9SKonstantin Belousov 	if (vport)
12591ad1bd9SKonstantin Belousov 		MLX5_SET(query_esw_vport_context_in, in, other_vport, 1);
12691ad1bd9SKonstantin Belousov 
12791ad1bd9SKonstantin Belousov 	return mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
12891ad1bd9SKonstantin Belousov }
12991ad1bd9SKonstantin Belousov 
13091ad1bd9SKonstantin Belousov static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
13191ad1bd9SKonstantin Belousov 				 u16 *vlan, u8 *qos)
13291ad1bd9SKonstantin Belousov {
13391ad1bd9SKonstantin Belousov 	u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {0};
13491ad1bd9SKonstantin Belousov 	int err;
13591ad1bd9SKonstantin Belousov 	bool cvlan_strip;
13691ad1bd9SKonstantin Belousov 	bool cvlan_insert;
13791ad1bd9SKonstantin Belousov 
13891ad1bd9SKonstantin Belousov 	*vlan = 0;
13991ad1bd9SKonstantin Belousov 	*qos = 0;
14091ad1bd9SKonstantin Belousov 
14191ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
14291ad1bd9SKonstantin Belousov 	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
14391ad1bd9SKonstantin Belousov 		return -ENOTSUPP;
14491ad1bd9SKonstantin Belousov 
14591ad1bd9SKonstantin Belousov 	err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out));
14691ad1bd9SKonstantin Belousov 	if (err)
14791ad1bd9SKonstantin Belousov 		goto out;
14891ad1bd9SKonstantin Belousov 
14991ad1bd9SKonstantin Belousov 	cvlan_strip = MLX5_GET(query_esw_vport_context_out, out,
15091ad1bd9SKonstantin Belousov 			       esw_vport_context.vport_cvlan_strip);
15191ad1bd9SKonstantin Belousov 
15291ad1bd9SKonstantin Belousov 	cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
15391ad1bd9SKonstantin Belousov 				esw_vport_context.vport_cvlan_insert);
15491ad1bd9SKonstantin Belousov 
15591ad1bd9SKonstantin Belousov 	if (cvlan_strip || cvlan_insert) {
15691ad1bd9SKonstantin Belousov 		*vlan = MLX5_GET(query_esw_vport_context_out, out,
15791ad1bd9SKonstantin Belousov 				 esw_vport_context.cvlan_id);
15891ad1bd9SKonstantin Belousov 		*qos = MLX5_GET(query_esw_vport_context_out, out,
15991ad1bd9SKonstantin Belousov 				esw_vport_context.cvlan_pcp);
16091ad1bd9SKonstantin Belousov 	}
16191ad1bd9SKonstantin Belousov 
16291ad1bd9SKonstantin Belousov 	esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n",
16391ad1bd9SKonstantin Belousov 		  vport, *vlan, *qos);
16491ad1bd9SKonstantin Belousov out:
16591ad1bd9SKonstantin Belousov 	return err;
16691ad1bd9SKonstantin Belousov }
16791ad1bd9SKonstantin Belousov 
16891ad1bd9SKonstantin Belousov static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
16991ad1bd9SKonstantin Belousov 					void *in, int inlen)
17091ad1bd9SKonstantin Belousov {
17191ad1bd9SKonstantin Belousov 	u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
17291ad1bd9SKonstantin Belousov 
17391ad1bd9SKonstantin Belousov 	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
17491ad1bd9SKonstantin Belousov 	if (vport)
17591ad1bd9SKonstantin Belousov 		MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
17691ad1bd9SKonstantin Belousov 
17791ad1bd9SKonstantin Belousov 	MLX5_SET(modify_esw_vport_context_in, in, opcode,
17891ad1bd9SKonstantin Belousov 		 MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
17991ad1bd9SKonstantin Belousov 
18091ad1bd9SKonstantin Belousov 	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
18191ad1bd9SKonstantin Belousov }
18291ad1bd9SKonstantin Belousov 
18391ad1bd9SKonstantin Belousov static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
18491ad1bd9SKonstantin Belousov 				  u16 vlan, u8 qos, bool set)
18591ad1bd9SKonstantin Belousov {
18691ad1bd9SKonstantin Belousov 	u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
18791ad1bd9SKonstantin Belousov 
18891ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
18991ad1bd9SKonstantin Belousov 	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
19091ad1bd9SKonstantin Belousov 		return -ENOTSUPP;
19191ad1bd9SKonstantin Belousov 
19291ad1bd9SKonstantin Belousov 	esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n",
19391ad1bd9SKonstantin Belousov 		  vport, vlan, qos, set);
19491ad1bd9SKonstantin Belousov 
19591ad1bd9SKonstantin Belousov 	if (set) {
19691ad1bd9SKonstantin Belousov 		MLX5_SET(modify_esw_vport_context_in, in,
19791ad1bd9SKonstantin Belousov 			 esw_vport_context.vport_cvlan_strip, 1);
19891ad1bd9SKonstantin Belousov 		/* insert only if no vlan in packet */
19991ad1bd9SKonstantin Belousov 		MLX5_SET(modify_esw_vport_context_in, in,
20091ad1bd9SKonstantin Belousov 			 esw_vport_context.vport_cvlan_insert, 1);
20191ad1bd9SKonstantin Belousov 		MLX5_SET(modify_esw_vport_context_in, in,
20291ad1bd9SKonstantin Belousov 			 esw_vport_context.cvlan_pcp, qos);
20391ad1bd9SKonstantin Belousov 		MLX5_SET(modify_esw_vport_context_in, in,
20491ad1bd9SKonstantin Belousov 			 esw_vport_context.cvlan_id, vlan);
20591ad1bd9SKonstantin Belousov 	}
20691ad1bd9SKonstantin Belousov 
20791ad1bd9SKonstantin Belousov 	MLX5_SET(modify_esw_vport_context_in, in,
20891ad1bd9SKonstantin Belousov 		 field_select.vport_cvlan_strip, 1);
20991ad1bd9SKonstantin Belousov 	MLX5_SET(modify_esw_vport_context_in, in,
21091ad1bd9SKonstantin Belousov 		 field_select.vport_cvlan_insert, 1);
21191ad1bd9SKonstantin Belousov 
21291ad1bd9SKonstantin Belousov 	return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
21391ad1bd9SKonstantin Belousov }
21491ad1bd9SKonstantin Belousov 
21591ad1bd9SKonstantin Belousov /* HW L2 Table (MPFS) management */
21691ad1bd9SKonstantin Belousov static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index,
21791ad1bd9SKonstantin Belousov 				  u8 *mac, u8 vlan_valid, u16 vlan)
21891ad1bd9SKonstantin Belousov {
21991ad1bd9SKonstantin Belousov 	u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0};
22091ad1bd9SKonstantin Belousov 	u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0};
22191ad1bd9SKonstantin Belousov 	u8 *in_mac_addr;
22291ad1bd9SKonstantin Belousov 
22391ad1bd9SKonstantin Belousov 	MLX5_SET(set_l2_table_entry_in, in, opcode,
22491ad1bd9SKonstantin Belousov 		 MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
22591ad1bd9SKonstantin Belousov 	MLX5_SET(set_l2_table_entry_in, in, table_index, index);
22691ad1bd9SKonstantin Belousov 	MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid);
22791ad1bd9SKonstantin Belousov 	MLX5_SET(set_l2_table_entry_in, in, vlan, vlan);
22891ad1bd9SKonstantin Belousov 
22991ad1bd9SKonstantin Belousov 	in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
23091ad1bd9SKonstantin Belousov 	ether_addr_copy(&in_mac_addr[2], mac);
23191ad1bd9SKonstantin Belousov 
23291ad1bd9SKonstantin Belousov 	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
23391ad1bd9SKonstantin Belousov }
23491ad1bd9SKonstantin Belousov 
23591ad1bd9SKonstantin Belousov static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
23691ad1bd9SKonstantin Belousov {
23791ad1bd9SKonstantin Belousov 	u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0};
23891ad1bd9SKonstantin Belousov 	u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0};
23991ad1bd9SKonstantin Belousov 
24091ad1bd9SKonstantin Belousov 	MLX5_SET(delete_l2_table_entry_in, in, opcode,
24191ad1bd9SKonstantin Belousov 		 MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
24291ad1bd9SKonstantin Belousov 	MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
24391ad1bd9SKonstantin Belousov 	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
24491ad1bd9SKonstantin Belousov }
24591ad1bd9SKonstantin Belousov 
24691ad1bd9SKonstantin Belousov static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix)
24791ad1bd9SKonstantin Belousov {
24891ad1bd9SKonstantin Belousov 	int err = 0;
24991ad1bd9SKonstantin Belousov 
25091ad1bd9SKonstantin Belousov 	*ix = find_first_zero_bit(l2_table->bitmap, l2_table->size);
25191ad1bd9SKonstantin Belousov 	if (*ix >= l2_table->size)
25291ad1bd9SKonstantin Belousov 		err = -ENOSPC;
25391ad1bd9SKonstantin Belousov 	else
25491ad1bd9SKonstantin Belousov 		__set_bit(*ix, l2_table->bitmap);
25591ad1bd9SKonstantin Belousov 
25691ad1bd9SKonstantin Belousov 	return err;
25791ad1bd9SKonstantin Belousov }
25891ad1bd9SKonstantin Belousov 
25991ad1bd9SKonstantin Belousov static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix)
26091ad1bd9SKonstantin Belousov {
26191ad1bd9SKonstantin Belousov 	__clear_bit(ix, l2_table->bitmap);
26291ad1bd9SKonstantin Belousov }
26391ad1bd9SKonstantin Belousov 
26491ad1bd9SKonstantin Belousov static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac,
26591ad1bd9SKonstantin Belousov 			      u8 vlan_valid, u16 vlan,
26691ad1bd9SKonstantin Belousov 			      u32 *index)
26791ad1bd9SKonstantin Belousov {
26891ad1bd9SKonstantin Belousov 	struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
26991ad1bd9SKonstantin Belousov 	int err;
27091ad1bd9SKonstantin Belousov 
27191ad1bd9SKonstantin Belousov 	err = alloc_l2_table_index(l2_table, index);
27291ad1bd9SKonstantin Belousov 	if (err)
27391ad1bd9SKonstantin Belousov 		return err;
27491ad1bd9SKonstantin Belousov 
27591ad1bd9SKonstantin Belousov 	err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan);
27691ad1bd9SKonstantin Belousov 	if (err)
27791ad1bd9SKonstantin Belousov 		free_l2_table_index(l2_table, *index);
27891ad1bd9SKonstantin Belousov 
27991ad1bd9SKonstantin Belousov 	return err;
28091ad1bd9SKonstantin Belousov }
28191ad1bd9SKonstantin Belousov 
28291ad1bd9SKonstantin Belousov static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index)
28391ad1bd9SKonstantin Belousov {
28491ad1bd9SKonstantin Belousov 	struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
28591ad1bd9SKonstantin Belousov 
28691ad1bd9SKonstantin Belousov 	del_l2_table_entry_cmd(dev, index);
28791ad1bd9SKonstantin Belousov 	free_l2_table_index(l2_table, index);
28891ad1bd9SKonstantin Belousov }
28991ad1bd9SKonstantin Belousov 
29091ad1bd9SKonstantin Belousov /* E-Switch FDB */
29191ad1bd9SKonstantin Belousov static struct mlx5_flow_rule *
29291ad1bd9SKonstantin Belousov esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
29391ad1bd9SKonstantin Belousov {
29491ad1bd9SKonstantin Belousov 	int match_header = MLX5_MATCH_OUTER_HEADERS;
29591ad1bd9SKonstantin Belousov 	struct mlx5_flow_destination dest;
29691ad1bd9SKonstantin Belousov 	struct mlx5_flow_rule *flow_rule = NULL;
29791ad1bd9SKonstantin Belousov 	u32 *match_v;
29891ad1bd9SKonstantin Belousov 	u32 *match_c;
29991ad1bd9SKonstantin Belousov 	u8 *dmac_v;
30091ad1bd9SKonstantin Belousov 	u8 *dmac_c;
30191ad1bd9SKonstantin Belousov 
30291ad1bd9SKonstantin Belousov 	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
30391ad1bd9SKonstantin Belousov 	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
30491ad1bd9SKonstantin Belousov 	if (!match_v || !match_c) {
30591ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""FDB: Failed to alloc match parameters\n");
30691ad1bd9SKonstantin Belousov 		goto out;
30791ad1bd9SKonstantin Belousov 	}
30891ad1bd9SKonstantin Belousov 	dmac_v = MLX5_ADDR_OF(fte_match_param, match_v,
30991ad1bd9SKonstantin Belousov 			      outer_headers.dmac_47_16);
31091ad1bd9SKonstantin Belousov 	dmac_c = MLX5_ADDR_OF(fte_match_param, match_c,
31191ad1bd9SKonstantin Belousov 			      outer_headers.dmac_47_16);
31291ad1bd9SKonstantin Belousov 
31391ad1bd9SKonstantin Belousov 	ether_addr_copy(dmac_v, mac);
31491ad1bd9SKonstantin Belousov 	/* Match criteria mask */
31591ad1bd9SKonstantin Belousov 	memset(dmac_c, 0xff, 6);
31691ad1bd9SKonstantin Belousov 
31791ad1bd9SKonstantin Belousov 	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
31891ad1bd9SKonstantin Belousov 	dest.vport_num = vport;
31991ad1bd9SKonstantin Belousov 
32091ad1bd9SKonstantin Belousov 	esw_debug(esw->dev,
32191ad1bd9SKonstantin Belousov 		  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
32291ad1bd9SKonstantin Belousov 		  dmac_v, dmac_c, vport);
32391ad1bd9SKonstantin Belousov 	flow_rule =
32491ad1bd9SKonstantin Belousov 		mlx5_add_flow_rule(esw->fdb_table.fdb,
32591ad1bd9SKonstantin Belousov 				   match_header,
32691ad1bd9SKonstantin Belousov 				   match_c,
32791ad1bd9SKonstantin Belousov 				   match_v,
32891ad1bd9SKonstantin Belousov 				   MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
32991ad1bd9SKonstantin Belousov 				   0, &dest);
33091ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(flow_rule)) {
33191ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n", dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
33291ad1bd9SKonstantin Belousov 		flow_rule = NULL;
33391ad1bd9SKonstantin Belousov 	}
33491ad1bd9SKonstantin Belousov out:
33591ad1bd9SKonstantin Belousov 	kfree(match_v);
33691ad1bd9SKonstantin Belousov 	kfree(match_c);
33791ad1bd9SKonstantin Belousov 	return flow_rule;
33891ad1bd9SKonstantin Belousov }
33991ad1bd9SKonstantin Belousov 
34091ad1bd9SKonstantin Belousov static int esw_create_fdb_table(struct mlx5_eswitch *esw)
34191ad1bd9SKonstantin Belousov {
34291ad1bd9SKonstantin Belousov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
34391ad1bd9SKonstantin Belousov 	struct mlx5_core_dev *dev = esw->dev;
34491ad1bd9SKonstantin Belousov 	struct mlx5_flow_namespace *root_ns;
34591ad1bd9SKonstantin Belousov 	struct mlx5_flow_table *fdb;
34691ad1bd9SKonstantin Belousov 	struct mlx5_flow_group *g;
34791ad1bd9SKonstantin Belousov 	void *match_criteria;
34891ad1bd9SKonstantin Belousov 	int table_size;
34991ad1bd9SKonstantin Belousov 	u32 *flow_group_in;
35091ad1bd9SKonstantin Belousov 	u8 *dmac;
35191ad1bd9SKonstantin Belousov 	int err = 0;
35291ad1bd9SKonstantin Belousov 
35391ad1bd9SKonstantin Belousov 	esw_debug(dev, "Create FDB log_max_size(%d)\n",
35491ad1bd9SKonstantin Belousov 		  MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
35591ad1bd9SKonstantin Belousov 
35691ad1bd9SKonstantin Belousov 	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
35791ad1bd9SKonstantin Belousov 	if (!root_ns) {
35891ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to get FDB flow namespace\n");
35991ad1bd9SKonstantin Belousov 		return -ENOMEM;
36091ad1bd9SKonstantin Belousov 	}
36191ad1bd9SKonstantin Belousov 
36291ad1bd9SKonstantin Belousov 	flow_group_in = mlx5_vzalloc(inlen);
36391ad1bd9SKonstantin Belousov 	if (!flow_group_in)
36491ad1bd9SKonstantin Belousov 		return -ENOMEM;
36591ad1bd9SKonstantin Belousov 	memset(flow_group_in, 0, inlen);
36691ad1bd9SKonstantin Belousov 
36791ad1bd9SKonstantin Belousov 	/* (-2) Since MaorG said so .. */
36891ad1bd9SKonstantin Belousov 	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)) - 2;
36991ad1bd9SKonstantin Belousov 
37091ad1bd9SKonstantin Belousov 	fdb = mlx5_create_flow_table(root_ns, 0, "FDB", table_size);
37191ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(fdb)) {
37291ad1bd9SKonstantin Belousov 		err = PTR_ERR(fdb);
37391ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
37491ad1bd9SKonstantin Belousov 		goto out;
37591ad1bd9SKonstantin Belousov 	}
37691ad1bd9SKonstantin Belousov 
37791ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
37891ad1bd9SKonstantin Belousov 		 MLX5_MATCH_OUTER_HEADERS);
37991ad1bd9SKonstantin Belousov 	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
38091ad1bd9SKonstantin Belousov 	dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
38191ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
38291ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
38391ad1bd9SKonstantin Belousov 	eth_broadcast_addr(dmac);
38491ad1bd9SKonstantin Belousov 
38591ad1bd9SKonstantin Belousov 	g = mlx5_create_flow_group(fdb, flow_group_in);
38691ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(g)) {
38791ad1bd9SKonstantin Belousov 		err = PTR_ERR(g);
38891ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
38991ad1bd9SKonstantin Belousov 		goto out;
39091ad1bd9SKonstantin Belousov 	}
39191ad1bd9SKonstantin Belousov 
39291ad1bd9SKonstantin Belousov 	esw->fdb_table.addr_grp = g;
39391ad1bd9SKonstantin Belousov 	esw->fdb_table.fdb = fdb;
39491ad1bd9SKonstantin Belousov out:
39591ad1bd9SKonstantin Belousov 	kfree(flow_group_in);
39691ad1bd9SKonstantin Belousov 	if (err && !IS_ERR_OR_NULL(fdb))
39791ad1bd9SKonstantin Belousov 		mlx5_destroy_flow_table(fdb);
39891ad1bd9SKonstantin Belousov 	return err;
39991ad1bd9SKonstantin Belousov }
40091ad1bd9SKonstantin Belousov 
40191ad1bd9SKonstantin Belousov static void esw_destroy_fdb_table(struct mlx5_eswitch *esw)
40291ad1bd9SKonstantin Belousov {
40391ad1bd9SKonstantin Belousov 	if (!esw->fdb_table.fdb)
40491ad1bd9SKonstantin Belousov 		return;
40591ad1bd9SKonstantin Belousov 
40691ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Destroy FDB Table\n");
40791ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_group(esw->fdb_table.addr_grp);
40891ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_table(esw->fdb_table.fdb);
40991ad1bd9SKonstantin Belousov 	esw->fdb_table.fdb = NULL;
41091ad1bd9SKonstantin Belousov 	esw->fdb_table.addr_grp = NULL;
41191ad1bd9SKonstantin Belousov }
41291ad1bd9SKonstantin Belousov 
41391ad1bd9SKonstantin Belousov /* E-Switch vport UC/MC lists management */
41491ad1bd9SKonstantin Belousov typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
41591ad1bd9SKonstantin Belousov 				 struct vport_addr *vaddr);
41691ad1bd9SKonstantin Belousov 
41791ad1bd9SKonstantin Belousov static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
41891ad1bd9SKonstantin Belousov {
41991ad1bd9SKonstantin Belousov 	struct hlist_head *hash = esw->l2_table.l2_hash;
42091ad1bd9SKonstantin Belousov 	struct esw_uc_addr *esw_uc;
42191ad1bd9SKonstantin Belousov 	u8 *mac = vaddr->node.addr;
42291ad1bd9SKonstantin Belousov 	u32 vport = vaddr->vport;
42391ad1bd9SKonstantin Belousov 	int err;
42491ad1bd9SKonstantin Belousov 
42591ad1bd9SKonstantin Belousov 	esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
42691ad1bd9SKonstantin Belousov 	if (esw_uc) {
42791ad1bd9SKonstantin Belousov 		esw_warn(esw->dev,
42891ad1bd9SKonstantin Belousov 			 "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n",
42991ad1bd9SKonstantin Belousov 			 mac, vport, esw_uc->vport);
43091ad1bd9SKonstantin Belousov 		return -EEXIST;
43191ad1bd9SKonstantin Belousov 	}
43291ad1bd9SKonstantin Belousov 
43391ad1bd9SKonstantin Belousov 	esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL);
43491ad1bd9SKonstantin Belousov 	if (!esw_uc)
43591ad1bd9SKonstantin Belousov 		return -ENOMEM;
43691ad1bd9SKonstantin Belousov 	esw_uc->vport = vport;
43791ad1bd9SKonstantin Belousov 
43891ad1bd9SKonstantin Belousov 	err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index);
43991ad1bd9SKonstantin Belousov 	if (err)
44091ad1bd9SKonstantin Belousov 		goto abort;
44191ad1bd9SKonstantin Belousov 
44291ad1bd9SKonstantin Belousov 	if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */
44391ad1bd9SKonstantin Belousov 		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
44491ad1bd9SKonstantin Belousov 
44591ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n",
44691ad1bd9SKonstantin Belousov 		  vport, mac, esw_uc->table_index, vaddr->flow_rule);
44791ad1bd9SKonstantin Belousov 	return err;
44891ad1bd9SKonstantin Belousov abort:
44991ad1bd9SKonstantin Belousov 	l2addr_hash_del(esw_uc);
45091ad1bd9SKonstantin Belousov 	return err;
45191ad1bd9SKonstantin Belousov }
45291ad1bd9SKonstantin Belousov 
45391ad1bd9SKonstantin Belousov static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
45491ad1bd9SKonstantin Belousov {
45591ad1bd9SKonstantin Belousov 	struct hlist_head *hash = esw->l2_table.l2_hash;
45691ad1bd9SKonstantin Belousov 	struct esw_uc_addr *esw_uc;
45791ad1bd9SKonstantin Belousov 	u8 *mac = vaddr->node.addr;
45891ad1bd9SKonstantin Belousov 	u32 vport = vaddr->vport;
45991ad1bd9SKonstantin Belousov 
46091ad1bd9SKonstantin Belousov 	esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
46191ad1bd9SKonstantin Belousov 	if (!esw_uc || esw_uc->vport != vport) {
46291ad1bd9SKonstantin Belousov 		esw_debug(esw->dev,
46391ad1bd9SKonstantin Belousov 			  "MAC(%pM) doesn't belong to vport (%d)\n",
46491ad1bd9SKonstantin Belousov 			  mac, vport);
46591ad1bd9SKonstantin Belousov 		return -EINVAL;
46691ad1bd9SKonstantin Belousov 	}
46791ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n",
46891ad1bd9SKonstantin Belousov 		  vport, mac, esw_uc->table_index, vaddr->flow_rule);
46991ad1bd9SKonstantin Belousov 
47091ad1bd9SKonstantin Belousov 	del_l2_table_entry(esw->dev, esw_uc->table_index);
47191ad1bd9SKonstantin Belousov 
47291ad1bd9SKonstantin Belousov 	if (vaddr->flow_rule)
47391ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(vaddr->flow_rule);
47491ad1bd9SKonstantin Belousov 	vaddr->flow_rule = NULL;
47591ad1bd9SKonstantin Belousov 
47691ad1bd9SKonstantin Belousov 	l2addr_hash_del(esw_uc);
47791ad1bd9SKonstantin Belousov 	return 0;
47891ad1bd9SKonstantin Belousov }
47991ad1bd9SKonstantin Belousov 
48091ad1bd9SKonstantin Belousov static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
48191ad1bd9SKonstantin Belousov {
48291ad1bd9SKonstantin Belousov 	struct hlist_head *hash = esw->mc_table;
48391ad1bd9SKonstantin Belousov 	struct esw_mc_addr *esw_mc;
48491ad1bd9SKonstantin Belousov 	u8 *mac = vaddr->node.addr;
48591ad1bd9SKonstantin Belousov 	u32 vport = vaddr->vport;
48691ad1bd9SKonstantin Belousov 
48791ad1bd9SKonstantin Belousov 	if (!esw->fdb_table.fdb)
48891ad1bd9SKonstantin Belousov 		return 0;
48991ad1bd9SKonstantin Belousov 
49091ad1bd9SKonstantin Belousov 	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
49191ad1bd9SKonstantin Belousov 	if (esw_mc)
49291ad1bd9SKonstantin Belousov 		goto add;
49391ad1bd9SKonstantin Belousov 
49491ad1bd9SKonstantin Belousov 	esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
49591ad1bd9SKonstantin Belousov 	if (!esw_mc)
49691ad1bd9SKonstantin Belousov 		return -ENOMEM;
49791ad1bd9SKonstantin Belousov 
49891ad1bd9SKonstantin Belousov 	esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
49991ad1bd9SKonstantin Belousov 		esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
50091ad1bd9SKonstantin Belousov add:
50191ad1bd9SKonstantin Belousov 	esw_mc->refcnt++;
50291ad1bd9SKonstantin Belousov 	/* Forward MC MAC to vport */
50391ad1bd9SKonstantin Belousov 	vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
50491ad1bd9SKonstantin Belousov 	esw_debug(esw->dev,
50591ad1bd9SKonstantin Belousov 		  "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
50691ad1bd9SKonstantin Belousov 		  vport, mac, vaddr->flow_rule,
50791ad1bd9SKonstantin Belousov 		  esw_mc->refcnt, esw_mc->uplink_rule);
50891ad1bd9SKonstantin Belousov 	return 0;
50991ad1bd9SKonstantin Belousov }
51091ad1bd9SKonstantin Belousov 
51191ad1bd9SKonstantin Belousov static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
51291ad1bd9SKonstantin Belousov {
51391ad1bd9SKonstantin Belousov 	struct hlist_head *hash = esw->mc_table;
51491ad1bd9SKonstantin Belousov 	struct esw_mc_addr *esw_mc;
51591ad1bd9SKonstantin Belousov 	u8 *mac = vaddr->node.addr;
51691ad1bd9SKonstantin Belousov 	u32 vport = vaddr->vport;
51791ad1bd9SKonstantin Belousov 
51891ad1bd9SKonstantin Belousov 	if (!esw->fdb_table.fdb)
51991ad1bd9SKonstantin Belousov 		return 0;
52091ad1bd9SKonstantin Belousov 
52191ad1bd9SKonstantin Belousov 	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
52291ad1bd9SKonstantin Belousov 	if (!esw_mc) {
52391ad1bd9SKonstantin Belousov 		esw_warn(esw->dev,
52491ad1bd9SKonstantin Belousov 			 "Failed to find eswitch MC addr for MAC(%pM) vport(%d)",
52591ad1bd9SKonstantin Belousov 			 mac, vport);
52691ad1bd9SKonstantin Belousov 		return -EINVAL;
52791ad1bd9SKonstantin Belousov 	}
52891ad1bd9SKonstantin Belousov 	esw_debug(esw->dev,
52991ad1bd9SKonstantin Belousov 		  "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
53091ad1bd9SKonstantin Belousov 		  vport, mac, vaddr->flow_rule, esw_mc->refcnt,
53191ad1bd9SKonstantin Belousov 		  esw_mc->uplink_rule);
53291ad1bd9SKonstantin Belousov 
53391ad1bd9SKonstantin Belousov 	if (vaddr->flow_rule)
53491ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(vaddr->flow_rule);
53591ad1bd9SKonstantin Belousov 	vaddr->flow_rule = NULL;
53691ad1bd9SKonstantin Belousov 
53791ad1bd9SKonstantin Belousov 	if (--esw_mc->refcnt)
53891ad1bd9SKonstantin Belousov 		return 0;
53991ad1bd9SKonstantin Belousov 
54091ad1bd9SKonstantin Belousov 	if (esw_mc->uplink_rule)
54191ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(esw_mc->uplink_rule);
54291ad1bd9SKonstantin Belousov 
54391ad1bd9SKonstantin Belousov 	l2addr_hash_del(esw_mc);
54491ad1bd9SKonstantin Belousov 	return 0;
54591ad1bd9SKonstantin Belousov }
54691ad1bd9SKonstantin Belousov 
54791ad1bd9SKonstantin Belousov /* Apply vport UC/MC list to HW l2 table and FDB table */
54891ad1bd9SKonstantin Belousov static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
54991ad1bd9SKonstantin Belousov 				      u32 vport_num, int list_type)
55091ad1bd9SKonstantin Belousov {
55191ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport = &esw->vports[vport_num];
55291ad1bd9SKonstantin Belousov 	bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
55391ad1bd9SKonstantin Belousov 	vport_addr_action vport_addr_add;
55491ad1bd9SKonstantin Belousov 	vport_addr_action vport_addr_del;
55591ad1bd9SKonstantin Belousov 	struct vport_addr *addr;
55691ad1bd9SKonstantin Belousov 	struct l2addr_node *node;
55791ad1bd9SKonstantin Belousov 	struct hlist_head *hash;
55891ad1bd9SKonstantin Belousov 	struct hlist_node *tmp;
55991ad1bd9SKonstantin Belousov 	int hi;
56091ad1bd9SKonstantin Belousov 
56191ad1bd9SKonstantin Belousov 	vport_addr_add = is_uc ? esw_add_uc_addr :
56291ad1bd9SKonstantin Belousov 				 esw_add_mc_addr;
56391ad1bd9SKonstantin Belousov 	vport_addr_del = is_uc ? esw_del_uc_addr :
56491ad1bd9SKonstantin Belousov 				 esw_del_mc_addr;
56591ad1bd9SKonstantin Belousov 
56691ad1bd9SKonstantin Belousov 	hash = is_uc ? vport->uc_list : vport->mc_list;
56791ad1bd9SKonstantin Belousov 	for_each_l2hash_node(node, tmp, hash, hi) {
56891ad1bd9SKonstantin Belousov 		addr = container_of(node, struct vport_addr, node);
56991ad1bd9SKonstantin Belousov 		switch (addr->action) {
57091ad1bd9SKonstantin Belousov 		case MLX5_ACTION_ADD:
57191ad1bd9SKonstantin Belousov 			vport_addr_add(esw, addr);
57291ad1bd9SKonstantin Belousov 			addr->action = MLX5_ACTION_NONE;
57391ad1bd9SKonstantin Belousov 			break;
57491ad1bd9SKonstantin Belousov 		case MLX5_ACTION_DEL:
57591ad1bd9SKonstantin Belousov 			vport_addr_del(esw, addr);
57691ad1bd9SKonstantin Belousov 			l2addr_hash_del(addr);
57791ad1bd9SKonstantin Belousov 			break;
57891ad1bd9SKonstantin Belousov 		}
57991ad1bd9SKonstantin Belousov 	}
58091ad1bd9SKonstantin Belousov }
58191ad1bd9SKonstantin Belousov 
58291ad1bd9SKonstantin Belousov /* Sync vport UC/MC list from vport context */
58391ad1bd9SKonstantin Belousov static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
58491ad1bd9SKonstantin Belousov 				       u32 vport_num, int list_type)
58591ad1bd9SKonstantin Belousov {
58691ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport = &esw->vports[vport_num];
58791ad1bd9SKonstantin Belousov 	bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
58891ad1bd9SKonstantin Belousov 	u8 (*mac_list)[ETH_ALEN];
58991ad1bd9SKonstantin Belousov 	struct l2addr_node *node;
59091ad1bd9SKonstantin Belousov 	struct vport_addr *addr;
59191ad1bd9SKonstantin Belousov 	struct hlist_head *hash;
59291ad1bd9SKonstantin Belousov 	struct hlist_node *tmp;
59391ad1bd9SKonstantin Belousov 	int size;
59491ad1bd9SKonstantin Belousov 	int err;
59591ad1bd9SKonstantin Belousov 	int hi;
59691ad1bd9SKonstantin Belousov 	int i;
59791ad1bd9SKonstantin Belousov 
59891ad1bd9SKonstantin Belousov 	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
59991ad1bd9SKonstantin Belousov 		       MLX5_MAX_MC_PER_VPORT(esw->dev);
60091ad1bd9SKonstantin Belousov 
60191ad1bd9SKonstantin Belousov 	mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
60291ad1bd9SKonstantin Belousov 	if (!mac_list)
60391ad1bd9SKonstantin Belousov 		return;
60491ad1bd9SKonstantin Belousov 
60591ad1bd9SKonstantin Belousov 	hash = is_uc ? vport->uc_list : vport->mc_list;
60691ad1bd9SKonstantin Belousov 
60791ad1bd9SKonstantin Belousov 	for_each_l2hash_node(node, tmp, hash, hi) {
60891ad1bd9SKonstantin Belousov 		addr = container_of(node, struct vport_addr, node);
60991ad1bd9SKonstantin Belousov 		addr->action = MLX5_ACTION_DEL;
61091ad1bd9SKonstantin Belousov 	}
61191ad1bd9SKonstantin Belousov 
61291ad1bd9SKonstantin Belousov 	err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
61391ad1bd9SKonstantin Belousov 					    mac_list, &size);
61491ad1bd9SKonstantin Belousov 	if (err)
61591ad1bd9SKonstantin Belousov 		return;
61691ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
61791ad1bd9SKonstantin Belousov 		  vport_num, is_uc ? "UC" : "MC", size);
61891ad1bd9SKonstantin Belousov 
61991ad1bd9SKonstantin Belousov 	for (i = 0; i < size; i++) {
62091ad1bd9SKonstantin Belousov 		if (is_uc && !is_valid_ether_addr(mac_list[i]))
62191ad1bd9SKonstantin Belousov 			continue;
62291ad1bd9SKonstantin Belousov 
62391ad1bd9SKonstantin Belousov 		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
62491ad1bd9SKonstantin Belousov 			continue;
62591ad1bd9SKonstantin Belousov 
62691ad1bd9SKonstantin Belousov 		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
62791ad1bd9SKonstantin Belousov 		if (addr) {
62891ad1bd9SKonstantin Belousov 			addr->action = MLX5_ACTION_NONE;
62991ad1bd9SKonstantin Belousov 			continue;
63091ad1bd9SKonstantin Belousov 		}
63191ad1bd9SKonstantin Belousov 
63291ad1bd9SKonstantin Belousov 		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
63391ad1bd9SKonstantin Belousov 				       GFP_KERNEL);
63491ad1bd9SKonstantin Belousov 		if (!addr) {
63591ad1bd9SKonstantin Belousov 			esw_warn(esw->dev,
63691ad1bd9SKonstantin Belousov 				 "Failed to add MAC(%pM) to vport[%d] DB\n",
63791ad1bd9SKonstantin Belousov 				 mac_list[i], vport_num);
63891ad1bd9SKonstantin Belousov 			continue;
63991ad1bd9SKonstantin Belousov 		}
64091ad1bd9SKonstantin Belousov 		addr->vport = vport_num;
64191ad1bd9SKonstantin Belousov 		addr->action = MLX5_ACTION_ADD;
64291ad1bd9SKonstantin Belousov 	}
64391ad1bd9SKonstantin Belousov 	kfree(mac_list);
64491ad1bd9SKonstantin Belousov }
64591ad1bd9SKonstantin Belousov 
64691ad1bd9SKonstantin Belousov static void esw_vport_change_handler(struct work_struct *work)
64791ad1bd9SKonstantin Belousov {
64891ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport =
64991ad1bd9SKonstantin Belousov 		container_of(work, struct mlx5_vport, vport_change_handler);
65091ad1bd9SKonstantin Belousov 	struct mlx5_core_dev *dev = vport->dev;
65191ad1bd9SKonstantin Belousov 	struct mlx5_eswitch *esw = dev->priv.eswitch;
65291ad1bd9SKonstantin Belousov 	u8 mac[ETH_ALEN];
65391ad1bd9SKonstantin Belousov 
65491ad1bd9SKonstantin Belousov 	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
65591ad1bd9SKonstantin Belousov 	esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
65691ad1bd9SKonstantin Belousov 		  vport->vport, mac);
65791ad1bd9SKonstantin Belousov 
65891ad1bd9SKonstantin Belousov 	if (vport->enabled_events & UC_ADDR_CHANGE) {
65991ad1bd9SKonstantin Belousov 		esw_update_vport_addr_list(esw, vport->vport,
66091ad1bd9SKonstantin Belousov 					   MLX5_NIC_VPORT_LIST_TYPE_UC);
66191ad1bd9SKonstantin Belousov 		esw_apply_vport_addr_list(esw, vport->vport,
66291ad1bd9SKonstantin Belousov 					  MLX5_NIC_VPORT_LIST_TYPE_UC);
66391ad1bd9SKonstantin Belousov 	}
66491ad1bd9SKonstantin Belousov 
66591ad1bd9SKonstantin Belousov 	if (vport->enabled_events & MC_ADDR_CHANGE) {
66691ad1bd9SKonstantin Belousov 		esw_update_vport_addr_list(esw, vport->vport,
66791ad1bd9SKonstantin Belousov 					   MLX5_NIC_VPORT_LIST_TYPE_MC);
66891ad1bd9SKonstantin Belousov 		esw_apply_vport_addr_list(esw, vport->vport,
66991ad1bd9SKonstantin Belousov 					  MLX5_NIC_VPORT_LIST_TYPE_MC);
67091ad1bd9SKonstantin Belousov 	}
67191ad1bd9SKonstantin Belousov 
67291ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
67391ad1bd9SKonstantin Belousov 	if (vport->enabled)
67491ad1bd9SKonstantin Belousov 		arm_vport_context_events_cmd(dev, vport->vport,
67591ad1bd9SKonstantin Belousov 					     vport->enabled_events);
67691ad1bd9SKonstantin Belousov }
67791ad1bd9SKonstantin Belousov 
67891ad1bd9SKonstantin Belousov static void esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
67991ad1bd9SKonstantin Belousov 					struct mlx5_vport *vport)
68091ad1bd9SKonstantin Belousov {
68191ad1bd9SKonstantin Belousov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
68291ad1bd9SKonstantin Belousov 	struct mlx5_flow_group *vlan_grp = NULL;
68391ad1bd9SKonstantin Belousov 	struct mlx5_flow_group *drop_grp = NULL;
68491ad1bd9SKonstantin Belousov 	struct mlx5_core_dev *dev = esw->dev;
68591ad1bd9SKonstantin Belousov 	struct mlx5_flow_namespace *root_ns;
68691ad1bd9SKonstantin Belousov 	struct mlx5_flow_table *acl;
68791ad1bd9SKonstantin Belousov 	void *match_criteria;
68891ad1bd9SKonstantin Belousov 	char table_name[32];
68991ad1bd9SKonstantin Belousov 	u32 *flow_group_in;
69091ad1bd9SKonstantin Belousov 	int table_size = 2;
69191ad1bd9SKonstantin Belousov 	int err = 0;
69291ad1bd9SKonstantin Belousov 
69391ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
69491ad1bd9SKonstantin Belousov 		return;
69591ad1bd9SKonstantin Belousov 
69691ad1bd9SKonstantin Belousov 	esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n",
69791ad1bd9SKonstantin Belousov 		  vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size));
69891ad1bd9SKonstantin Belousov 
69991ad1bd9SKonstantin Belousov 	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS);
70091ad1bd9SKonstantin Belousov 	if (!root_ns) {
70191ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to get E-Switch egress flow namespace\n");
70291ad1bd9SKonstantin Belousov 		return;
70391ad1bd9SKonstantin Belousov 	}
70491ad1bd9SKonstantin Belousov 
70591ad1bd9SKonstantin Belousov 	flow_group_in = mlx5_vzalloc(inlen);
70691ad1bd9SKonstantin Belousov 	if (!flow_group_in)
70791ad1bd9SKonstantin Belousov 		return;
70891ad1bd9SKonstantin Belousov 
70991ad1bd9SKonstantin Belousov 	snprintf(table_name, 32, "egress_%d", vport->vport);
71091ad1bd9SKonstantin Belousov 	acl = mlx5_create_vport_flow_table(root_ns, vport->vport, 0, table_name, table_size);
71191ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(acl)) {
71291ad1bd9SKonstantin Belousov 		err = PTR_ERR(acl);
71391ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create E-Switch vport[%d] egress flow Table, err(%d)\n",
71491ad1bd9SKonstantin Belousov 			 vport->vport, err);
71591ad1bd9SKonstantin Belousov 		goto out;
71691ad1bd9SKonstantin Belousov 	}
71791ad1bd9SKonstantin Belousov 
71891ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
71991ad1bd9SKonstantin Belousov 	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
72091ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
72191ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.first_vid);
72291ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
72391ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
72491ad1bd9SKonstantin Belousov 
72591ad1bd9SKonstantin Belousov 	vlan_grp = mlx5_create_flow_group(acl, flow_group_in);
72691ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vlan_grp)) {
72791ad1bd9SKonstantin Belousov 		err = PTR_ERR(vlan_grp);
72891ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create E-Switch vport[%d] egress allowed vlans flow group, err(%d)\n",
72991ad1bd9SKonstantin Belousov 			 vport->vport, err);
73091ad1bd9SKonstantin Belousov 		goto out;
73191ad1bd9SKonstantin Belousov 	}
73291ad1bd9SKonstantin Belousov 
73391ad1bd9SKonstantin Belousov 	memset(flow_group_in, 0, inlen);
73491ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
73591ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
73691ad1bd9SKonstantin Belousov 	drop_grp = mlx5_create_flow_group(acl, flow_group_in);
73791ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(drop_grp)) {
73891ad1bd9SKonstantin Belousov 		err = PTR_ERR(drop_grp);
73991ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop flow group, err(%d)\n",
74091ad1bd9SKonstantin Belousov 			 vport->vport, err);
74191ad1bd9SKonstantin Belousov 		goto out;
74291ad1bd9SKonstantin Belousov 	}
74391ad1bd9SKonstantin Belousov 
74491ad1bd9SKonstantin Belousov 	vport->egress.acl = acl;
74591ad1bd9SKonstantin Belousov 	vport->egress.drop_grp = drop_grp;
74691ad1bd9SKonstantin Belousov 	vport->egress.allowed_vlans_grp = vlan_grp;
74791ad1bd9SKonstantin Belousov out:
74891ad1bd9SKonstantin Belousov 	kfree(flow_group_in);
74991ad1bd9SKonstantin Belousov 	if (err && !IS_ERR_OR_NULL(vlan_grp))
75091ad1bd9SKonstantin Belousov 		mlx5_destroy_flow_group(vlan_grp);
75191ad1bd9SKonstantin Belousov 	if (err && !IS_ERR_OR_NULL(acl))
75291ad1bd9SKonstantin Belousov 		mlx5_destroy_flow_table(acl);
75391ad1bd9SKonstantin Belousov }
75491ad1bd9SKonstantin Belousov 
75591ad1bd9SKonstantin Belousov static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
75691ad1bd9SKonstantin Belousov 					   struct mlx5_vport *vport)
75791ad1bd9SKonstantin Belousov {
75891ad1bd9SKonstantin Belousov 	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
75991ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(vport->egress.allowed_vlan);
76091ad1bd9SKonstantin Belousov 
76191ad1bd9SKonstantin Belousov 	if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
76291ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(vport->egress.drop_rule);
76391ad1bd9SKonstantin Belousov 
76491ad1bd9SKonstantin Belousov 	vport->egress.allowed_vlan = NULL;
76591ad1bd9SKonstantin Belousov 	vport->egress.drop_rule = NULL;
76691ad1bd9SKonstantin Belousov }
76791ad1bd9SKonstantin Belousov 
76891ad1bd9SKonstantin Belousov static void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
76991ad1bd9SKonstantin Belousov 					 struct mlx5_vport *vport)
77091ad1bd9SKonstantin Belousov {
77191ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->egress.acl))
77291ad1bd9SKonstantin Belousov 		return;
77391ad1bd9SKonstantin Belousov 
77491ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport);
77591ad1bd9SKonstantin Belousov 
77691ad1bd9SKonstantin Belousov 	esw_vport_cleanup_egress_rules(esw, vport);
77791ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_group(vport->egress.allowed_vlans_grp);
77891ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_group(vport->egress.drop_grp);
77991ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_table(vport->egress.acl);
78091ad1bd9SKonstantin Belousov 	vport->egress.allowed_vlans_grp = NULL;
78191ad1bd9SKonstantin Belousov 	vport->egress.drop_grp = NULL;
78291ad1bd9SKonstantin Belousov 	vport->egress.acl = NULL;
78391ad1bd9SKonstantin Belousov }
78491ad1bd9SKonstantin Belousov 
78591ad1bd9SKonstantin Belousov static void esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
78691ad1bd9SKonstantin Belousov 					 struct mlx5_vport *vport)
78791ad1bd9SKonstantin Belousov {
78891ad1bd9SKonstantin Belousov 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
78991ad1bd9SKonstantin Belousov 	struct mlx5_core_dev *dev = esw->dev;
79091ad1bd9SKonstantin Belousov 	struct mlx5_flow_namespace *root_ns;
79191ad1bd9SKonstantin Belousov 	struct mlx5_flow_table *acl;
79291ad1bd9SKonstantin Belousov 	struct mlx5_flow_group *g;
79391ad1bd9SKonstantin Belousov 	void *match_criteria;
79491ad1bd9SKonstantin Belousov 	char table_name[32];
79591ad1bd9SKonstantin Belousov 	u32 *flow_group_in;
79691ad1bd9SKonstantin Belousov 	int table_size = 1;
79791ad1bd9SKonstantin Belousov 	int err = 0;
79891ad1bd9SKonstantin Belousov 
79991ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
80091ad1bd9SKonstantin Belousov 		return;
80191ad1bd9SKonstantin Belousov 
80291ad1bd9SKonstantin Belousov 	esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n",
80391ad1bd9SKonstantin Belousov 		  vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size));
80491ad1bd9SKonstantin Belousov 
80591ad1bd9SKonstantin Belousov 	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS);
80691ad1bd9SKonstantin Belousov 	if (!root_ns) {
80791ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to get E-Switch ingress flow namespace\n");
80891ad1bd9SKonstantin Belousov 		return;
80991ad1bd9SKonstantin Belousov 	}
81091ad1bd9SKonstantin Belousov 
81191ad1bd9SKonstantin Belousov 	flow_group_in = mlx5_vzalloc(inlen);
81291ad1bd9SKonstantin Belousov 	if (!flow_group_in)
81391ad1bd9SKonstantin Belousov 		return;
81491ad1bd9SKonstantin Belousov 
81591ad1bd9SKonstantin Belousov 	snprintf(table_name, 32, "ingress_%d", vport->vport);
81691ad1bd9SKonstantin Belousov 	acl = mlx5_create_vport_flow_table(root_ns, vport->vport, 0, table_name, table_size);
81791ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(acl)) {
81891ad1bd9SKonstantin Belousov 		err = PTR_ERR(acl);
81991ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow Table, err(%d)\n",
82091ad1bd9SKonstantin Belousov 			 vport->vport, err);
82191ad1bd9SKonstantin Belousov 		goto out;
82291ad1bd9SKonstantin Belousov 	}
82391ad1bd9SKonstantin Belousov 
82491ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
82591ad1bd9SKonstantin Belousov 	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
82691ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
82791ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
82891ad1bd9SKonstantin Belousov 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
82991ad1bd9SKonstantin Belousov 
83091ad1bd9SKonstantin Belousov 	g = mlx5_create_flow_group(acl, flow_group_in);
83191ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(g)) {
83291ad1bd9SKonstantin Belousov 		err = PTR_ERR(g);
83391ad1bd9SKonstantin Belousov 		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow group, err(%d)\n",
83491ad1bd9SKonstantin Belousov 			 vport->vport, err);
83591ad1bd9SKonstantin Belousov 		goto out;
83691ad1bd9SKonstantin Belousov 	}
83791ad1bd9SKonstantin Belousov 
83891ad1bd9SKonstantin Belousov 	vport->ingress.acl = acl;
83991ad1bd9SKonstantin Belousov 	vport->ingress.drop_grp = g;
84091ad1bd9SKonstantin Belousov out:
84191ad1bd9SKonstantin Belousov 	kfree(flow_group_in);
84291ad1bd9SKonstantin Belousov 	if (err && !IS_ERR_OR_NULL(acl))
84391ad1bd9SKonstantin Belousov 		mlx5_destroy_flow_table(acl);
84491ad1bd9SKonstantin Belousov }
84591ad1bd9SKonstantin Belousov 
84691ad1bd9SKonstantin Belousov static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw,
84791ad1bd9SKonstantin Belousov 					    struct mlx5_vport *vport)
84891ad1bd9SKonstantin Belousov {
84991ad1bd9SKonstantin Belousov 	if (!IS_ERR_OR_NULL(vport->ingress.drop_rule))
85091ad1bd9SKonstantin Belousov 		mlx5_del_flow_rule(vport->ingress.drop_rule);
85191ad1bd9SKonstantin Belousov 	vport->ingress.drop_rule = NULL;
85291ad1bd9SKonstantin Belousov }
85391ad1bd9SKonstantin Belousov 
85491ad1bd9SKonstantin Belousov static void esw_vport_disable_ingress_acl(struct mlx5_eswitch *esw,
85591ad1bd9SKonstantin Belousov 					  struct mlx5_vport *vport)
85691ad1bd9SKonstantin Belousov {
85791ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->ingress.acl))
85891ad1bd9SKonstantin Belousov 		return;
85991ad1bd9SKonstantin Belousov 
86091ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Destroy vport[%d] E-Switch ingress ACL\n", vport->vport);
86191ad1bd9SKonstantin Belousov 
86291ad1bd9SKonstantin Belousov 	esw_vport_cleanup_ingress_rules(esw, vport);
86391ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_group(vport->ingress.drop_grp);
86491ad1bd9SKonstantin Belousov 	mlx5_destroy_flow_table(vport->ingress.acl);
86591ad1bd9SKonstantin Belousov 	vport->ingress.acl = NULL;
86691ad1bd9SKonstantin Belousov 	vport->ingress.drop_grp = NULL;
86791ad1bd9SKonstantin Belousov }
86891ad1bd9SKonstantin Belousov 
86991ad1bd9SKonstantin Belousov static int esw_vport_ingress_config(struct mlx5_eswitch *esw,
87091ad1bd9SKonstantin Belousov 				    struct mlx5_vport *vport)
87191ad1bd9SKonstantin Belousov {
87291ad1bd9SKonstantin Belousov 	struct mlx5_flow_destination dest;
87391ad1bd9SKonstantin Belousov 	u32 *match_v;
87491ad1bd9SKonstantin Belousov 	u32 *match_c;
87591ad1bd9SKonstantin Belousov 	int err = 0;
87691ad1bd9SKonstantin Belousov 
87791ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->ingress.acl)) {
87891ad1bd9SKonstantin Belousov 		esw_warn(esw->dev,
87991ad1bd9SKonstantin Belousov 			 "vport[%d] configure ingress rules failed, ingress acl is not initialized!\n",
88091ad1bd9SKonstantin Belousov 			 vport->vport);
88191ad1bd9SKonstantin Belousov 		return -EPERM;
88291ad1bd9SKonstantin Belousov 	}
88391ad1bd9SKonstantin Belousov 
88491ad1bd9SKonstantin Belousov 	esw_vport_cleanup_ingress_rules(esw, vport);
88591ad1bd9SKonstantin Belousov 
88691ad1bd9SKonstantin Belousov 	if (!vport->vlan && !vport->qos)
88791ad1bd9SKonstantin Belousov 		return 0;
88891ad1bd9SKonstantin Belousov 
88991ad1bd9SKonstantin Belousov 	esw_debug(esw->dev,
89091ad1bd9SKonstantin Belousov 		  "vport[%d] configure ingress rules, vlan(%d) qos(%d)\n",
89191ad1bd9SKonstantin Belousov 		  vport->vport, vport->vlan, vport->qos);
89291ad1bd9SKonstantin Belousov 
89391ad1bd9SKonstantin Belousov 	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
89491ad1bd9SKonstantin Belousov 	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
89591ad1bd9SKonstantin Belousov 	if (!match_v || !match_c) {
89691ad1bd9SKonstantin Belousov 		err = -ENOMEM;
89791ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "vport[%d] configure ingress rules failed, err(%d)\n",
89891ad1bd9SKonstantin Belousov 			 vport->vport, err);
89991ad1bd9SKonstantin Belousov 		goto out;
90091ad1bd9SKonstantin Belousov 	}
90191ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.cvlan_tag);
90291ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.cvlan_tag);
90391ad1bd9SKonstantin Belousov 
90491ad1bd9SKonstantin Belousov 	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
90591ad1bd9SKonstantin Belousov 	dest.vport_num = vport->vport;
90691ad1bd9SKonstantin Belousov 
90791ad1bd9SKonstantin Belousov 	vport->ingress.drop_rule =
90891ad1bd9SKonstantin Belousov 		mlx5_add_flow_rule(vport->ingress.acl,
90991ad1bd9SKonstantin Belousov 				   MLX5_MATCH_OUTER_HEADERS,
91091ad1bd9SKonstantin Belousov 				   match_c,
91191ad1bd9SKonstantin Belousov 				   match_v,
91291ad1bd9SKonstantin Belousov 				   MLX5_FLOW_CONTEXT_ACTION_DROP,
91391ad1bd9SKonstantin Belousov 				   0, &dest);
91491ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->ingress.drop_rule)) {
91591ad1bd9SKonstantin Belousov 		err = PTR_ERR(vport->ingress.drop_rule);
91691ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""vport[%d] configure ingress rules, err(%d)\n", vport->vport, err);
91791ad1bd9SKonstantin Belousov 		vport->ingress.drop_rule = NULL;
91891ad1bd9SKonstantin Belousov 	}
91991ad1bd9SKonstantin Belousov out:
92091ad1bd9SKonstantin Belousov 	kfree(match_v);
92191ad1bd9SKonstantin Belousov 	kfree(match_c);
92291ad1bd9SKonstantin Belousov 	return err;
92391ad1bd9SKonstantin Belousov }
92491ad1bd9SKonstantin Belousov 
92591ad1bd9SKonstantin Belousov static int esw_vport_egress_config(struct mlx5_eswitch *esw,
92691ad1bd9SKonstantin Belousov 				   struct mlx5_vport *vport)
92791ad1bd9SKonstantin Belousov {
92891ad1bd9SKonstantin Belousov 	struct mlx5_flow_destination dest;
92991ad1bd9SKonstantin Belousov 	u32 *match_v;
93091ad1bd9SKonstantin Belousov 	u32 *match_c;
93191ad1bd9SKonstantin Belousov 	int err = 0;
93291ad1bd9SKonstantin Belousov 
93391ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->egress.acl)) {
93491ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "vport[%d] configure rgress rules failed, egress acl is not initialized!\n",
93591ad1bd9SKonstantin Belousov 			 vport->vport);
93691ad1bd9SKonstantin Belousov 		return -EPERM;
93791ad1bd9SKonstantin Belousov 	}
93891ad1bd9SKonstantin Belousov 
93991ad1bd9SKonstantin Belousov 	esw_vport_cleanup_egress_rules(esw, vport);
94091ad1bd9SKonstantin Belousov 
94191ad1bd9SKonstantin Belousov 	if (!vport->vlan && !vport->qos)
94291ad1bd9SKonstantin Belousov 		return 0;
94391ad1bd9SKonstantin Belousov 
94491ad1bd9SKonstantin Belousov 	esw_debug(esw->dev,
94591ad1bd9SKonstantin Belousov 		  "vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
94691ad1bd9SKonstantin Belousov 		  vport->vport, vport->vlan, vport->qos);
94791ad1bd9SKonstantin Belousov 
94891ad1bd9SKonstantin Belousov 	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
94991ad1bd9SKonstantin Belousov 	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
95091ad1bd9SKonstantin Belousov 	if (!match_v || !match_c) {
95191ad1bd9SKonstantin Belousov 		err = -ENOMEM;
95291ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "vport[%d] configure egress rules failed, err(%d)\n",
95391ad1bd9SKonstantin Belousov 			 vport->vport, err);
95491ad1bd9SKonstantin Belousov 		goto out;
95591ad1bd9SKonstantin Belousov 	}
95691ad1bd9SKonstantin Belousov 
95791ad1bd9SKonstantin Belousov 	/* Allowed vlan rule */
95891ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.cvlan_tag);
95991ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.cvlan_tag);
96091ad1bd9SKonstantin Belousov 	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.first_vid);
96191ad1bd9SKonstantin Belousov 	MLX5_SET(fte_match_param, match_v, outer_headers.first_vid, vport->vlan);
96291ad1bd9SKonstantin Belousov 
96391ad1bd9SKonstantin Belousov 	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
96491ad1bd9SKonstantin Belousov 	dest.vport_num = vport->vport;
96591ad1bd9SKonstantin Belousov 
96691ad1bd9SKonstantin Belousov 	vport->egress.allowed_vlan =
96791ad1bd9SKonstantin Belousov 		mlx5_add_flow_rule(vport->egress.acl,
96891ad1bd9SKonstantin Belousov 				   MLX5_MATCH_OUTER_HEADERS,
96991ad1bd9SKonstantin Belousov 				   match_c,
97091ad1bd9SKonstantin Belousov 				   match_v,
97191ad1bd9SKonstantin Belousov 				   MLX5_FLOW_CONTEXT_ACTION_ALLOW,
97291ad1bd9SKonstantin Belousov 				   0, &dest);
97391ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->egress.allowed_vlan)) {
97491ad1bd9SKonstantin Belousov 		err = PTR_ERR(vport->egress.allowed_vlan);
97591ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""vport[%d] configure egress allowed vlan rule failed, err(%d)\n", vport->vport, err);
97691ad1bd9SKonstantin Belousov 		vport->egress.allowed_vlan = NULL;
97791ad1bd9SKonstantin Belousov 		goto out;
97891ad1bd9SKonstantin Belousov 	}
97991ad1bd9SKonstantin Belousov 
98091ad1bd9SKonstantin Belousov 	/* Drop others rule (star rule) */
98191ad1bd9SKonstantin Belousov 	memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param));
98291ad1bd9SKonstantin Belousov 	memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param));
98391ad1bd9SKonstantin Belousov 	vport->egress.drop_rule =
98491ad1bd9SKonstantin Belousov 		mlx5_add_flow_rule(vport->egress.acl,
98591ad1bd9SKonstantin Belousov 				   0,
98691ad1bd9SKonstantin Belousov 				   match_c,
98791ad1bd9SKonstantin Belousov 				   match_v,
98891ad1bd9SKonstantin Belousov 				   MLX5_FLOW_CONTEXT_ACTION_DROP,
98991ad1bd9SKonstantin Belousov 				   0, &dest);
99091ad1bd9SKonstantin Belousov 	if (IS_ERR_OR_NULL(vport->egress.drop_rule)) {
99191ad1bd9SKonstantin Belousov 		err = PTR_ERR(vport->egress.drop_rule);
99291ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""vport[%d] configure egress drop rule failed, err(%d)\n", vport->vport, err);
99391ad1bd9SKonstantin Belousov 		vport->egress.drop_rule = NULL;
99491ad1bd9SKonstantin Belousov 	}
99591ad1bd9SKonstantin Belousov out:
99691ad1bd9SKonstantin Belousov 	kfree(match_v);
99791ad1bd9SKonstantin Belousov 	kfree(match_c);
99891ad1bd9SKonstantin Belousov 	return err;
99991ad1bd9SKonstantin Belousov }
100091ad1bd9SKonstantin Belousov 
100191ad1bd9SKonstantin Belousov static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
100291ad1bd9SKonstantin Belousov 			     int enable_events)
100391ad1bd9SKonstantin Belousov {
100491ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport = &esw->vports[vport_num];
100591ad1bd9SKonstantin Belousov 	unsigned long flags;
100691ad1bd9SKonstantin Belousov 
100791ad1bd9SKonstantin Belousov 	mutex_lock(&vport->state_lock);
100891ad1bd9SKonstantin Belousov 	WARN_ON(vport->enabled);
100991ad1bd9SKonstantin Belousov 
101091ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num);
101191ad1bd9SKonstantin Belousov 
101291ad1bd9SKonstantin Belousov 	if (vport_num) { /* Only VFs need ACLs for VST and spoofchk filtering */
101391ad1bd9SKonstantin Belousov 		esw_vport_enable_ingress_acl(esw, vport);
101491ad1bd9SKonstantin Belousov 		esw_vport_enable_egress_acl(esw, vport);
101591ad1bd9SKonstantin Belousov 		esw_vport_ingress_config(esw, vport);
101691ad1bd9SKonstantin Belousov 		esw_vport_egress_config(esw, vport);
101791ad1bd9SKonstantin Belousov 	}
101891ad1bd9SKonstantin Belousov 
101991ad1bd9SKonstantin Belousov 	mlx5_modify_vport_admin_state(esw->dev,
102091ad1bd9SKonstantin Belousov 				      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
102191ad1bd9SKonstantin Belousov 				      vport_num,
102291ad1bd9SKonstantin Belousov 				      MLX5_ESW_VPORT_ADMIN_STATE_AUTO);
102391ad1bd9SKonstantin Belousov 
102491ad1bd9SKonstantin Belousov 	/* Sync with current vport context */
102591ad1bd9SKonstantin Belousov 	vport->enabled_events = enable_events;
102691ad1bd9SKonstantin Belousov 	esw_vport_change_handler(&vport->vport_change_handler);
102791ad1bd9SKonstantin Belousov 
102891ad1bd9SKonstantin Belousov 	spin_lock_irqsave(&vport->lock, flags);
102991ad1bd9SKonstantin Belousov 	vport->enabled = true;
103091ad1bd9SKonstantin Belousov 	spin_unlock_irqrestore(&vport->lock, flags);
103191ad1bd9SKonstantin Belousov 
103291ad1bd9SKonstantin Belousov 	arm_vport_context_events_cmd(esw->dev, vport_num, enable_events);
103391ad1bd9SKonstantin Belousov 
103491ad1bd9SKonstantin Belousov 	esw->enabled_vports++;
103591ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
103691ad1bd9SKonstantin Belousov 	mutex_unlock(&vport->state_lock);
103791ad1bd9SKonstantin Belousov }
103891ad1bd9SKonstantin Belousov 
103991ad1bd9SKonstantin Belousov static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
104091ad1bd9SKonstantin Belousov {
104191ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport = &esw->vports[vport_num];
104291ad1bd9SKonstantin Belousov 	struct l2addr_node *node;
104391ad1bd9SKonstantin Belousov 	struct vport_addr *addr;
104491ad1bd9SKonstantin Belousov 	struct hlist_node *tmp;
104591ad1bd9SKonstantin Belousov 	int hi;
104691ad1bd9SKonstantin Belousov 
104791ad1bd9SKonstantin Belousov 	for_each_l2hash_node(node, tmp, vport->uc_list, hi) {
104891ad1bd9SKonstantin Belousov 		addr = container_of(node, struct vport_addr, node);
104991ad1bd9SKonstantin Belousov 		addr->action = MLX5_ACTION_DEL;
105091ad1bd9SKonstantin Belousov 	}
105191ad1bd9SKonstantin Belousov 	esw_apply_vport_addr_list(esw, vport_num, MLX5_NIC_VPORT_LIST_TYPE_UC);
105291ad1bd9SKonstantin Belousov 
105391ad1bd9SKonstantin Belousov 	for_each_l2hash_node(node, tmp, vport->mc_list, hi) {
105491ad1bd9SKonstantin Belousov 		addr = container_of(node, struct vport_addr, node);
105591ad1bd9SKonstantin Belousov 		addr->action = MLX5_ACTION_DEL;
105691ad1bd9SKonstantin Belousov 	}
105791ad1bd9SKonstantin Belousov 	esw_apply_vport_addr_list(esw, vport_num, MLX5_NIC_VPORT_LIST_TYPE_MC);
105891ad1bd9SKonstantin Belousov }
105991ad1bd9SKonstantin Belousov 
106091ad1bd9SKonstantin Belousov static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
106191ad1bd9SKonstantin Belousov {
106291ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport = &esw->vports[vport_num];
106391ad1bd9SKonstantin Belousov 	unsigned long flags;
106491ad1bd9SKonstantin Belousov 
106591ad1bd9SKonstantin Belousov 	mutex_lock(&vport->state_lock);
106691ad1bd9SKonstantin Belousov 	if (!vport->enabled) {
106791ad1bd9SKonstantin Belousov 		mutex_unlock(&vport->state_lock);
106891ad1bd9SKonstantin Belousov 		return;
106991ad1bd9SKonstantin Belousov 	}
107091ad1bd9SKonstantin Belousov 
107191ad1bd9SKonstantin Belousov 	esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num);
107291ad1bd9SKonstantin Belousov 	/* Mark this vport as disabled to discard new events */
107391ad1bd9SKonstantin Belousov 	spin_lock_irqsave(&vport->lock, flags);
107491ad1bd9SKonstantin Belousov 	vport->enabled = false;
107591ad1bd9SKonstantin Belousov 	vport->enabled_events = 0;
107691ad1bd9SKonstantin Belousov 	spin_unlock_irqrestore(&vport->lock, flags);
107791ad1bd9SKonstantin Belousov 
107891ad1bd9SKonstantin Belousov 	mlx5_modify_vport_admin_state(esw->dev,
107991ad1bd9SKonstantin Belousov 				      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
108091ad1bd9SKonstantin Belousov 				      vport_num,
108191ad1bd9SKonstantin Belousov 				      MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
108291ad1bd9SKonstantin Belousov 	/* Wait for current already scheduled events to complete */
108391ad1bd9SKonstantin Belousov 	flush_workqueue(esw->work_queue);
108491ad1bd9SKonstantin Belousov 	/* Disable events from this vport */
108591ad1bd9SKonstantin Belousov 	arm_vport_context_events_cmd(esw->dev, vport->vport, 0);
108691ad1bd9SKonstantin Belousov 	/* We don't assume VFs will cleanup after themselves */
108791ad1bd9SKonstantin Belousov 	esw_cleanup_vport(esw, vport_num);
108891ad1bd9SKonstantin Belousov 	if (vport_num) {
108991ad1bd9SKonstantin Belousov 		esw_vport_disable_egress_acl(esw, vport);
109091ad1bd9SKonstantin Belousov 		esw_vport_disable_ingress_acl(esw, vport);
109191ad1bd9SKonstantin Belousov 	}
109291ad1bd9SKonstantin Belousov 	esw->enabled_vports--;
109391ad1bd9SKonstantin Belousov 	mutex_unlock(&vport->state_lock);
109491ad1bd9SKonstantin Belousov }
109591ad1bd9SKonstantin Belousov 
109691ad1bd9SKonstantin Belousov /* Public E-Switch API */
109791ad1bd9SKonstantin Belousov int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs)
109891ad1bd9SKonstantin Belousov {
109991ad1bd9SKonstantin Belousov 	int err;
110091ad1bd9SKonstantin Belousov 	int i;
110191ad1bd9SKonstantin Belousov 
110291ad1bd9SKonstantin Belousov 	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
110391ad1bd9SKonstantin Belousov 	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
110491ad1bd9SKonstantin Belousov 		return 0;
110591ad1bd9SKonstantin Belousov 
110691ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) ||
110791ad1bd9SKonstantin Belousov 	    !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) {
110891ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n");
110991ad1bd9SKonstantin Belousov 		return -ENOTSUPP;
111091ad1bd9SKonstantin Belousov 	}
111191ad1bd9SKonstantin Belousov 
111291ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW_INGRESS_ACL(esw->dev, ft_support))
111391ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "E-Switch ingress ACL is not supported by FW\n");
111491ad1bd9SKonstantin Belousov 
111591ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_ESW_EGRESS_ACL(esw->dev, ft_support))
111691ad1bd9SKonstantin Belousov 		esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n");
111791ad1bd9SKonstantin Belousov 
111891ad1bd9SKonstantin Belousov 	esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs);
111991ad1bd9SKonstantin Belousov 
112091ad1bd9SKonstantin Belousov 	esw_disable_vport(esw, 0);
112191ad1bd9SKonstantin Belousov 
112291ad1bd9SKonstantin Belousov 	err = esw_create_fdb_table(esw);
112391ad1bd9SKonstantin Belousov 	if (err)
112491ad1bd9SKonstantin Belousov 		goto abort;
112591ad1bd9SKonstantin Belousov 
112691ad1bd9SKonstantin Belousov 	for (i = 0; i <= nvfs; i++)
112791ad1bd9SKonstantin Belousov 		esw_enable_vport(esw, i, SRIOV_VPORT_EVENTS);
112891ad1bd9SKonstantin Belousov 
112991ad1bd9SKonstantin Belousov 	esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n",
113091ad1bd9SKonstantin Belousov 		 esw->enabled_vports);
113191ad1bd9SKonstantin Belousov 	return 0;
113291ad1bd9SKonstantin Belousov 
113391ad1bd9SKonstantin Belousov abort:
113491ad1bd9SKonstantin Belousov 	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
113591ad1bd9SKonstantin Belousov 	return err;
113691ad1bd9SKonstantin Belousov }
113791ad1bd9SKonstantin Belousov 
113891ad1bd9SKonstantin Belousov void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
113991ad1bd9SKonstantin Belousov {
114091ad1bd9SKonstantin Belousov 	int i;
114191ad1bd9SKonstantin Belousov 
114291ad1bd9SKonstantin Belousov 	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
114391ad1bd9SKonstantin Belousov 	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
114491ad1bd9SKonstantin Belousov 		return;
114591ad1bd9SKonstantin Belousov 
114691ad1bd9SKonstantin Belousov 	esw_info(esw->dev, "disable SRIOV: active vports(%d)\n",
114791ad1bd9SKonstantin Belousov 		 esw->enabled_vports);
114891ad1bd9SKonstantin Belousov 
114991ad1bd9SKonstantin Belousov 	for (i = 0; i < esw->total_vports; i++)
115091ad1bd9SKonstantin Belousov 		esw_disable_vport(esw, i);
115191ad1bd9SKonstantin Belousov 
115291ad1bd9SKonstantin Belousov 	esw_destroy_fdb_table(esw);
115391ad1bd9SKonstantin Belousov 
115491ad1bd9SKonstantin Belousov 	/* VPORT 0 (PF) must be enabled back with non-sriov configuration */
115591ad1bd9SKonstantin Belousov 	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
115691ad1bd9SKonstantin Belousov }
115791ad1bd9SKonstantin Belousov 
115891ad1bd9SKonstantin Belousov int mlx5_eswitch_init(struct mlx5_core_dev *dev)
115991ad1bd9SKonstantin Belousov {
116091ad1bd9SKonstantin Belousov 	int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
116191ad1bd9SKonstantin Belousov 	int total_vports = 1;
116291ad1bd9SKonstantin Belousov 	struct mlx5_eswitch *esw;
116391ad1bd9SKonstantin Belousov 	int vport_num;
116491ad1bd9SKonstantin Belousov 	int err;
116591ad1bd9SKonstantin Belousov 
116691ad1bd9SKonstantin Belousov 	if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
116791ad1bd9SKonstantin Belousov 	    MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
116891ad1bd9SKonstantin Belousov 		return 0;
116991ad1bd9SKonstantin Belousov 
117091ad1bd9SKonstantin Belousov 	esw_info(dev,
117191ad1bd9SKonstantin Belousov 		 "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n",
117291ad1bd9SKonstantin Belousov 		 total_vports, l2_table_size,
117391ad1bd9SKonstantin Belousov 		 MLX5_MAX_UC_PER_VPORT(dev),
117491ad1bd9SKonstantin Belousov 		 MLX5_MAX_MC_PER_VPORT(dev));
117591ad1bd9SKonstantin Belousov 
117691ad1bd9SKonstantin Belousov 	esw = kzalloc(sizeof(*esw), GFP_KERNEL);
117791ad1bd9SKonstantin Belousov 	if (!esw)
117891ad1bd9SKonstantin Belousov 		return -ENOMEM;
117991ad1bd9SKonstantin Belousov 
118091ad1bd9SKonstantin Belousov 	esw->dev = dev;
118191ad1bd9SKonstantin Belousov 
118291ad1bd9SKonstantin Belousov 	esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size),
118391ad1bd9SKonstantin Belousov 				   sizeof(uintptr_t), GFP_KERNEL);
118491ad1bd9SKonstantin Belousov 	if (!esw->l2_table.bitmap) {
118591ad1bd9SKonstantin Belousov 		err = -ENOMEM;
118691ad1bd9SKonstantin Belousov 		goto abort;
118791ad1bd9SKonstantin Belousov 	}
118891ad1bd9SKonstantin Belousov 	esw->l2_table.size = l2_table_size;
118991ad1bd9SKonstantin Belousov 
119091ad1bd9SKonstantin Belousov 	esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq");
119191ad1bd9SKonstantin Belousov 	if (!esw->work_queue) {
119291ad1bd9SKonstantin Belousov 		err = -ENOMEM;
119391ad1bd9SKonstantin Belousov 		goto abort;
119491ad1bd9SKonstantin Belousov 	}
119591ad1bd9SKonstantin Belousov 
119691ad1bd9SKonstantin Belousov 	esw->vports = kcalloc(total_vports, sizeof(struct mlx5_vport),
119791ad1bd9SKonstantin Belousov 			      GFP_KERNEL);
119891ad1bd9SKonstantin Belousov 	if (!esw->vports) {
119991ad1bd9SKonstantin Belousov 		err = -ENOMEM;
120091ad1bd9SKonstantin Belousov 		goto abort;
120191ad1bd9SKonstantin Belousov 	}
120291ad1bd9SKonstantin Belousov 
120391ad1bd9SKonstantin Belousov 	for (vport_num = 0; vport_num < total_vports; vport_num++) {
120491ad1bd9SKonstantin Belousov 		struct mlx5_vport *vport = &esw->vports[vport_num];
120591ad1bd9SKonstantin Belousov 
120691ad1bd9SKonstantin Belousov 		vport->vport = vport_num;
120791ad1bd9SKonstantin Belousov 		vport->dev = dev;
120891ad1bd9SKonstantin Belousov 		INIT_WORK(&vport->vport_change_handler,
120991ad1bd9SKonstantin Belousov 			  esw_vport_change_handler);
121091ad1bd9SKonstantin Belousov 		spin_lock_init(&vport->lock);
121191ad1bd9SKonstantin Belousov 		mutex_init(&vport->state_lock);
121291ad1bd9SKonstantin Belousov 	}
121391ad1bd9SKonstantin Belousov 
121491ad1bd9SKonstantin Belousov 	esw->total_vports = total_vports;
121591ad1bd9SKonstantin Belousov 	esw->enabled_vports = 0;
121691ad1bd9SKonstantin Belousov 
121791ad1bd9SKonstantin Belousov 	dev->priv.eswitch = esw;
121891ad1bd9SKonstantin Belousov 	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
121991ad1bd9SKonstantin Belousov 	/* VF Vports will be enabled when SRIOV is enabled */
122091ad1bd9SKonstantin Belousov 	return 0;
122191ad1bd9SKonstantin Belousov abort:
122291ad1bd9SKonstantin Belousov 	if (esw->work_queue)
122391ad1bd9SKonstantin Belousov 		destroy_workqueue(esw->work_queue);
122491ad1bd9SKonstantin Belousov 	kfree(esw->l2_table.bitmap);
122591ad1bd9SKonstantin Belousov 	kfree(esw->vports);
122691ad1bd9SKonstantin Belousov 	kfree(esw);
122791ad1bd9SKonstantin Belousov 	return err;
122891ad1bd9SKonstantin Belousov }
122991ad1bd9SKonstantin Belousov 
123091ad1bd9SKonstantin Belousov void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
123191ad1bd9SKonstantin Belousov {
123291ad1bd9SKonstantin Belousov 	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
123391ad1bd9SKonstantin Belousov 	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
123491ad1bd9SKonstantin Belousov 		return;
123591ad1bd9SKonstantin Belousov 
123691ad1bd9SKonstantin Belousov 	esw_info(esw->dev, "cleanup\n");
123791ad1bd9SKonstantin Belousov 	esw_disable_vport(esw, 0);
123891ad1bd9SKonstantin Belousov 
123991ad1bd9SKonstantin Belousov 	esw->dev->priv.eswitch = NULL;
124091ad1bd9SKonstantin Belousov 	destroy_workqueue(esw->work_queue);
124191ad1bd9SKonstantin Belousov 	kfree(esw->l2_table.bitmap);
124291ad1bd9SKonstantin Belousov 	kfree(esw->vports);
124391ad1bd9SKonstantin Belousov 	kfree(esw);
124491ad1bd9SKonstantin Belousov }
124591ad1bd9SKonstantin Belousov 
124691ad1bd9SKonstantin Belousov void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
124791ad1bd9SKonstantin Belousov {
124891ad1bd9SKonstantin Belousov 	struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change;
124991ad1bd9SKonstantin Belousov 	u16 vport_num = be16_to_cpu(vc_eqe->vport_num);
125091ad1bd9SKonstantin Belousov 	struct mlx5_vport *vport;
125191ad1bd9SKonstantin Belousov 
125291ad1bd9SKonstantin Belousov 	if (!esw) {
125391ad1bd9SKonstantin Belousov 		printf("mlx5_core: WARN: ""MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n", vport_num);
125491ad1bd9SKonstantin Belousov 		return;
125591ad1bd9SKonstantin Belousov 	}
125691ad1bd9SKonstantin Belousov 
125791ad1bd9SKonstantin Belousov 	vport = &esw->vports[vport_num];
125891ad1bd9SKonstantin Belousov 	spin_lock(&vport->lock);
125991ad1bd9SKonstantin Belousov 	if (vport->enabled)
126091ad1bd9SKonstantin Belousov 		queue_work(esw->work_queue, &vport->vport_change_handler);
126191ad1bd9SKonstantin Belousov 	spin_unlock(&vport->lock);
126291ad1bd9SKonstantin Belousov }
126391ad1bd9SKonstantin Belousov 
126491ad1bd9SKonstantin Belousov /* Vport Administration */
126591ad1bd9SKonstantin Belousov #define ESW_ALLOWED(esw) \
126691ad1bd9SKonstantin Belousov 	(esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev))
126791ad1bd9SKonstantin Belousov #define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports)
126891ad1bd9SKonstantin Belousov 
126991ad1bd9SKonstantin Belousov static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN])
127091ad1bd9SKonstantin Belousov {
127191ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[7] = mac[0];
127291ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[6] = mac[1];
127391ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[5] = mac[2];
127491ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[4] = 0xff;
127591ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[3] = 0xfe;
127691ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[2] = mac[3];
127791ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[1] = mac[4];
127891ad1bd9SKonstantin Belousov 	((u8 *)node_guid)[0] = mac[5];
127991ad1bd9SKonstantin Belousov }
128091ad1bd9SKonstantin Belousov 
128191ad1bd9SKonstantin Belousov int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
128291ad1bd9SKonstantin Belousov 			       int vport, u8 mac[ETH_ALEN])
128391ad1bd9SKonstantin Belousov {
128491ad1bd9SKonstantin Belousov 	int err = 0;
128591ad1bd9SKonstantin Belousov 	u64 node_guid;
128691ad1bd9SKonstantin Belousov 
128791ad1bd9SKonstantin Belousov 	if (!ESW_ALLOWED(esw))
128891ad1bd9SKonstantin Belousov 		return -EPERM;
128991ad1bd9SKonstantin Belousov 	if (!LEGAL_VPORT(esw, vport))
129091ad1bd9SKonstantin Belousov 		return -EINVAL;
129191ad1bd9SKonstantin Belousov 
129291ad1bd9SKonstantin Belousov 	err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
129391ad1bd9SKonstantin Belousov 	if (err) {
129491ad1bd9SKonstantin Belousov 		mlx5_core_warn(esw->dev,
129591ad1bd9SKonstantin Belousov 			       "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
129691ad1bd9SKonstantin Belousov 			       vport, err);
129791ad1bd9SKonstantin Belousov 		return err;
129891ad1bd9SKonstantin Belousov 	}
129991ad1bd9SKonstantin Belousov 
130091ad1bd9SKonstantin Belousov 	node_guid_gen_from_mac(&node_guid, mac);
130191ad1bd9SKonstantin Belousov 	err = mlx5_modify_nic_vport_node_guid(esw->dev, vport, node_guid);
130291ad1bd9SKonstantin Belousov 	if (err) {
130391ad1bd9SKonstantin Belousov 		mlx5_core_warn(esw->dev,
130491ad1bd9SKonstantin Belousov 			       "Failed to mlx5_modify_nic_vport_node_guid vport(%d) err=(%d)\n",
130591ad1bd9SKonstantin Belousov 			       vport, err);
130691ad1bd9SKonstantin Belousov 		return err;
130791ad1bd9SKonstantin Belousov 	}
130891ad1bd9SKonstantin Belousov 
130991ad1bd9SKonstantin Belousov 	return err;
131091ad1bd9SKonstantin Belousov }
131191ad1bd9SKonstantin Belousov 
131291ad1bd9SKonstantin Belousov int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
131391ad1bd9SKonstantin Belousov 				 int vport, int link_state)
131491ad1bd9SKonstantin Belousov {
131591ad1bd9SKonstantin Belousov 	if (!ESW_ALLOWED(esw))
131691ad1bd9SKonstantin Belousov 		return -EPERM;
131791ad1bd9SKonstantin Belousov 	if (!LEGAL_VPORT(esw, vport))
131891ad1bd9SKonstantin Belousov 		return -EINVAL;
131991ad1bd9SKonstantin Belousov 
132091ad1bd9SKonstantin Belousov 	return mlx5_modify_vport_admin_state(esw->dev,
132191ad1bd9SKonstantin Belousov 					     MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
132291ad1bd9SKonstantin Belousov 					     vport, link_state);
132391ad1bd9SKonstantin Belousov }
132491ad1bd9SKonstantin Belousov 
132591ad1bd9SKonstantin Belousov int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
132691ad1bd9SKonstantin Belousov 				  int vport, struct mlx5_esw_vport_info *ivi)
132791ad1bd9SKonstantin Belousov {
132891ad1bd9SKonstantin Belousov 	u16 vlan;
132991ad1bd9SKonstantin Belousov 	u8 qos;
133091ad1bd9SKonstantin Belousov 
133191ad1bd9SKonstantin Belousov 	if (!ESW_ALLOWED(esw))
133291ad1bd9SKonstantin Belousov 		return -EPERM;
133391ad1bd9SKonstantin Belousov 	if (!LEGAL_VPORT(esw, vport))
133491ad1bd9SKonstantin Belousov 		return -EINVAL;
133591ad1bd9SKonstantin Belousov 
133691ad1bd9SKonstantin Belousov 	memset(ivi, 0, sizeof(*ivi));
133791ad1bd9SKonstantin Belousov 	ivi->vf = vport - 1;
133891ad1bd9SKonstantin Belousov 
133991ad1bd9SKonstantin Belousov 	mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
134091ad1bd9SKonstantin Belousov 	ivi->linkstate = mlx5_query_vport_admin_state(esw->dev,
134191ad1bd9SKonstantin Belousov 						      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
134291ad1bd9SKonstantin Belousov 						      vport);
134391ad1bd9SKonstantin Belousov 	query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
134491ad1bd9SKonstantin Belousov 	ivi->vlan = vlan;
134591ad1bd9SKonstantin Belousov 	ivi->qos = qos;
134691ad1bd9SKonstantin Belousov 	ivi->spoofchk = 0;
134791ad1bd9SKonstantin Belousov 
134891ad1bd9SKonstantin Belousov 	return 0;
134991ad1bd9SKonstantin Belousov }
135091ad1bd9SKonstantin Belousov 
135191ad1bd9SKonstantin Belousov int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
135291ad1bd9SKonstantin Belousov 				int vport, u16 vlan, u8 qos)
135391ad1bd9SKonstantin Belousov {
135491ad1bd9SKonstantin Belousov 	struct mlx5_vport *evport;
135591ad1bd9SKonstantin Belousov 	int err = 0;
135691ad1bd9SKonstantin Belousov 	int set = 0;
135791ad1bd9SKonstantin Belousov 
135891ad1bd9SKonstantin Belousov 	if (!ESW_ALLOWED(esw))
135991ad1bd9SKonstantin Belousov 		return -EPERM;
136091ad1bd9SKonstantin Belousov 	if (!LEGAL_VPORT(esw, vport) || (vlan > 4095) || (qos > 7))
136191ad1bd9SKonstantin Belousov 		return -EINVAL;
136291ad1bd9SKonstantin Belousov 
136391ad1bd9SKonstantin Belousov 	if (vlan || qos)
136491ad1bd9SKonstantin Belousov 		set = 1;
136591ad1bd9SKonstantin Belousov 
136691ad1bd9SKonstantin Belousov 	evport = &esw->vports[vport];
136791ad1bd9SKonstantin Belousov 
136891ad1bd9SKonstantin Belousov 	err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
136991ad1bd9SKonstantin Belousov 	if (err)
137091ad1bd9SKonstantin Belousov 		return err;
137191ad1bd9SKonstantin Belousov 
137291ad1bd9SKonstantin Belousov 	mutex_lock(&evport->state_lock);
137391ad1bd9SKonstantin Belousov 	evport->vlan = vlan;
137491ad1bd9SKonstantin Belousov 	evport->qos = qos;
137591ad1bd9SKonstantin Belousov 	if (evport->enabled) {
137691ad1bd9SKonstantin Belousov 		esw_vport_ingress_config(esw, evport);
137791ad1bd9SKonstantin Belousov 		esw_vport_egress_config(esw, evport);
137891ad1bd9SKonstantin Belousov 	}
137991ad1bd9SKonstantin Belousov 	mutex_unlock(&evport->state_lock);
138091ad1bd9SKonstantin Belousov 	return err;
138191ad1bd9SKonstantin Belousov }
138291ad1bd9SKonstantin Belousov 
1383