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