Author: kib
Date: Wed Mar 18 22:30:56 2020
New Revision: 359100
URL: https://svnweb.freebsd.org/changeset/base/359100

Log:
  mlx5: Restore eswitch management code from attic.
  
  Reviewed by:  hselasky
  Sponsored by: Mellanox Technologies
  MFC after:    2 weeks

Added:
  head/sys/dev/mlx5/mlx5_core/eswitch.h   (contents, props changed)
  head/sys/dev/mlx5/mlx5_core/flow_table.h   (contents, props changed)
  head/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c   (contents, props changed)
Modified:
  head/sys/dev/mlx5/driver.h
  head/sys/modules/mlx5/Makefile

Modified: head/sys/dev/mlx5/driver.h
==============================================================================
--- head/sys/dev/mlx5/driver.h  Wed Mar 18 22:21:42 2020        (r359099)
+++ head/sys/dev/mlx5/driver.h  Wed Mar 18 22:30:56 2020        (r359100)
@@ -628,6 +628,8 @@ struct mlx5_priv {
        struct mlx5_rl_table    rl_table;
 #endif
        struct mlx5_pme_stats pme_stats;
+
+       struct mlx5_eswitch     *eswitch;
 };
 
 enum mlx5_device_state {

Added: head/sys/dev/mlx5/mlx5_core/eswitch.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mlx5/mlx5_core/eswitch.h       Wed Mar 18 22:30:56 2020        
(r359100)
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __MLX5_ESWITCH_H__
+#define __MLX5_ESWITCH_H__
+
+#include <linux/if_ether.h>
+#include <dev/mlx5/device.h>
+
+#define MLX5_MAX_UC_PER_VPORT(dev) \
+       (1 << MLX5_CAP_GEN(dev, log_max_current_uc_list))
+
+#define MLX5_MAX_MC_PER_VPORT(dev) \
+       (1 << MLX5_CAP_GEN(dev, log_max_current_mc_list))
+
+#define MLX5_L2_ADDR_HASH_SIZE (BIT(BITS_PER_BYTE))
+#define MLX5_L2_ADDR_HASH(addr) (addr[5])
+
+/* L2 -mac address based- hash helpers */
+struct l2addr_node {
+       struct hlist_node hlist;
+       u8                addr[ETH_ALEN];
+};
+
+#define for_each_l2hash_node(hn, tmp, hash, i) \
+       for (i = 0; i < MLX5_L2_ADDR_HASH_SIZE; i++) \
+               hlist_for_each_entry_safe(hn, tmp, &hash[i], hlist)
+
+#define l2addr_hash_find(hash, mac, type) ({                \
+       int ix = MLX5_L2_ADDR_HASH(mac);                    \
+       bool found = false;                                 \
+       type *ptr = NULL;                                   \
+                                                           \
+       hlist_for_each_entry(ptr, &hash[ix], node.hlist)    \
+               if (ether_addr_equal(ptr->node.addr, mac)) {\
+                       found = true;                       \
+                       break;                              \
+               }                                           \
+       if (!found)                                         \
+               ptr = NULL;                                 \
+       ptr;                                                \
+})
+
+#define l2addr_hash_add(hash, mac, type, gfp) ({            \
+       int ix = MLX5_L2_ADDR_HASH(mac);                    \
+       type *ptr = NULL;                                   \
+                                                           \
+       ptr = kzalloc(sizeof(type), gfp);                   \
+       if (ptr) {                                          \
+               ether_addr_copy(ptr->node.addr, mac);       \
+               hlist_add_head(&ptr->node.hlist, &hash[ix]);\
+       }                                                   \
+       ptr;                                                \
+})
+
+#define l2addr_hash_del(ptr) ({                             \
+       hlist_del(&ptr->node.hlist);                        \
+       kfree(ptr);                                         \
+})
+
+struct vport_ingress {
+       struct mlx5_flow_table *acl;
+       struct mlx5_flow_group *drop_grp;
+       struct mlx5_flow_rule  *drop_rule;
+};
+
+struct vport_egress {
+       struct mlx5_flow_table *acl;
+       struct mlx5_flow_group *allowed_vlans_grp;
+       struct mlx5_flow_group *drop_grp;
+       struct mlx5_flow_rule  *allowed_vlan;
+       struct mlx5_flow_rule  *drop_rule;
+};
+
+struct mlx5_vport {
+       struct mlx5_core_dev    *dev;
+       int                     vport;
+       struct hlist_head       uc_list[MLX5_L2_ADDR_HASH_SIZE];
+       struct hlist_head       mc_list[MLX5_L2_ADDR_HASH_SIZE];
+       struct work_struct      vport_change_handler;
+
+       struct vport_ingress    ingress;
+       struct vport_egress     egress;
+
+       u16                     vlan;
+       u8                      qos;
+       struct mutex    state_lock; /* protect dynamic state changes */
+       /* This spinlock protects access to vport data, between
+        * "esw_vport_disable" and ongoing interrupt "mlx5_eswitch_vport_event"
+        * once vport marked as disabled new interrupts are discarded.
+        */
+       spinlock_t              lock; /* vport events sync */
+       bool                    enabled;
+       u16                     enabled_events;
+};
+
+struct mlx5_l2_table {
+       struct hlist_head l2_hash[MLX5_L2_ADDR_HASH_SIZE];
+       u32                  size;
+       unsigned long        *bitmap;
+};
+
+struct mlx5_eswitch_fdb {
+       void *fdb;
+       struct mlx5_flow_group *addr_grp;
+};
+
+struct mlx5_eswitch {
+       struct mlx5_core_dev    *dev;
+       struct mlx5_l2_table    l2_table;
+       struct mlx5_eswitch_fdb fdb_table;
+       struct hlist_head       mc_table[MLX5_L2_ADDR_HASH_SIZE];
+       struct workqueue_struct *work_queue;
+       struct mlx5_vport       *vports;
+       int                     total_vports;
+       int                     enabled_vports;
+};
+
+struct mlx5_esw_vport_info {
+       __u32 vf;
+       __u8 mac[32];
+       __u32 vlan;
+       __u32 qos;
+       __u32 spoofchk;
+       __u32 linkstate;
+       __u32 min_tx_rate;
+       __u32 max_tx_rate;
+};
+
+/* E-Switch API */
+int mlx5_eswitch_init(struct mlx5_core_dev *dev);
+void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw);
+void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe);
+int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs);
+void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw);
+int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
+                              int vport, u8 mac[ETH_ALEN]);
+int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
+                                int vport, int link_state);
+int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
+                               int vport, u16 vlan, u8 qos);
+int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
+                                 int vport, struct mlx5_esw_vport_info *evi);
+
+#endif /* __MLX5_ESWITCH_H__ */

Added: head/sys/dev/mlx5/mlx5_core/flow_table.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mlx5/mlx5_core/flow_table.h    Wed Mar 18 22:30:56 2020        
(r359100)
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef MLX5_FLOW_TABLE_H
+#define MLX5_FLOW_TABLE_H
+
+#include <dev/mlx5/driver.h>
+
+#define MLX5_SET_FLOW_TABLE_ROOT_OPMOD_SET      0x0
+#define MLX5_SET_FLOW_TABLE_ROOT_OPMOD_RESET    0x1
+
+struct mlx5_flow_table_group {
+       u8      log_sz;
+       u8      match_criteria_enable;
+       u32     match_criteria[MLX5_ST_SZ_DW(fte_match_param)];
+};
+
+void *mlx5_create_flow_table(struct mlx5_core_dev *dev, u8 level, u8 
table_type,
+                            u16 vport,
+                            u16 num_groups,
+                            struct mlx5_flow_table_group *group);
+void mlx5_destroy_flow_table(void *flow_table);
+int mlx5_add_flow_table_entry(void *flow_table, u8 match_criteria_enable,
+                             void *match_criteria, void *flow_context,
+                             u32 *flow_index);
+int mlx5_del_flow_table_entry(void *flow_table, u32 flow_index);
+u32 mlx5_get_flow_table_id(void *flow_table);
+int mlx5_set_flow_table_root(struct mlx5_core_dev *mdev, u16 op_mod,
+                            u8 vport_num, u8 table_type, u32 table_id,
+                            u32 underlay_qpn);
+void *mlx5_get_flow_table_properties(void *flow_table);
+u32 mlx5_set_flow_table_miss_id(void *flow_table, u32 miss_ft_id);
+
+int mlx5_create_flow_counter(struct mlx5_core_dev *dev, u16 *cnt_id);
+void mlx5_destroy_flow_counter(struct mlx5_core_dev *dev, u16 cnt_id);
+int mlx5_query_flow_counters(struct mlx5_core_dev *dev,
+                            u32 num_counters, u16 *cnt_ids,
+                            struct mlx5_traffic_counter *cnt_data);
+int mlx5_reset_flow_counter(struct mlx5_core_dev *dev, u16 cnt_id);
+
+#endif /* MLX5_FLOW_TABLE_H */

Added: head/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/mlx5/mlx5_core/mlx5_eswitch.c  Wed Mar 18 22:30:56 2020        
(r359100)
@@ -0,0 +1,1382 @@
+/*-
+ * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <linux/etherdevice.h>
+#include <dev/mlx5/driver.h>
+#include <dev/mlx5/mlx5_ifc.h>
+#include <dev/mlx5/vport.h>
+#include <dev/mlx5/fs.h>
+#include "mlx5_core.h"
+#include "eswitch.h"
+
+#define UPLINK_VPORT 0xFFFF
+
+#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
+
+#define esw_info(dev, format, ...)                             \
+       printf("mlx5_core: INFO: ""(%s): E-Switch: " format, (dev)->priv.name, 
##__VA_ARGS__)
+
+#define esw_warn(dev, format, ...)                             \
+       printf("mlx5_core: WARN: ""(%s): E-Switch: " format, (dev)->priv.name, 
##__VA_ARGS__)
+
+#define esw_debug(dev, format, ...)                            \
+       mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
+
+enum {
+       MLX5_ACTION_NONE = 0,
+       MLX5_ACTION_ADD  = 1,
+       MLX5_ACTION_DEL  = 2,
+};
+
+/* E-Switch UC L2 table hash node */
+struct esw_uc_addr {
+       struct l2addr_node node;
+       u32                table_index;
+       u32                vport;
+};
+
+/* E-Switch MC FDB table hash node */
+struct esw_mc_addr { /* SRIOV only */
+       struct l2addr_node     node;
+       struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */
+       u32                    refcnt;
+};
+
+/* Vport UC/MC hash node */
+struct vport_addr {
+       struct l2addr_node     node;
+       u8                     action;
+       u32                    vport;
+       struct mlx5_flow_rule *flow_rule; /* SRIOV only */
+};
+
+enum {
+       UC_ADDR_CHANGE = BIT(0),
+       MC_ADDR_CHANGE = BIT(1),
+};
+
+/* Vport context events */
+#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
+                           MC_ADDR_CHANGE)
+
+static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
+                                       u32 events_mask)
+{
+       int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {0};
+       int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
+       void *nic_vport_ctx;
+
+       MLX5_SET(modify_nic_vport_context_in, in,
+                opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
+       MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
+       MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
+       if (vport)
+               MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
+       nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
+                                    in, nic_vport_context);
+
+       MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
+
+       if (events_mask & UC_ADDR_CHANGE)
+               MLX5_SET(nic_vport_context, nic_vport_ctx,
+                        event_on_uc_address_change, 1);
+       if (events_mask & MC_ADDR_CHANGE)
+               MLX5_SET(nic_vport_context, nic_vport_ctx,
+                        event_on_mc_address_change, 1);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+/* E-Switch vport context HW commands */
+static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
+                                      u32 *out, int outlen)
+{
+       u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {0};
+
+       MLX5_SET(query_nic_vport_context_in, in, opcode,
+                MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
+
+       MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
+       if (vport)
+               MLX5_SET(query_esw_vport_context_in, in, other_vport, 1);
+
+       return mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
+}
+
+static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+                                u16 *vlan, u8 *qos)
+{
+       u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {0};
+       int err;
+       bool cvlan_strip;
+       bool cvlan_insert;
+
+       *vlan = 0;
+       *qos = 0;
+
+       if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+           !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+               return -ENOTSUPP;
+
+       err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out));
+       if (err)
+               goto out;
+
+       cvlan_strip = MLX5_GET(query_esw_vport_context_out, out,
+                              esw_vport_context.vport_cvlan_strip);
+
+       cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
+                               esw_vport_context.vport_cvlan_insert);
+
+       if (cvlan_strip || cvlan_insert) {
+               *vlan = MLX5_GET(query_esw_vport_context_out, out,
+                                esw_vport_context.cvlan_id);
+               *qos = MLX5_GET(query_esw_vport_context_out, out,
+                               esw_vport_context.cvlan_pcp);
+       }
+
+       esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n",
+                 vport, *vlan, *qos);
+out:
+       return err;
+}
+
+static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
+                                       void *in, int inlen)
+{
+       u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
+
+       MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
+       if (vport)
+               MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
+
+       MLX5_SET(modify_esw_vport_context_in, in, opcode,
+                MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
+
+       return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
+}
+
+static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
+                                 u16 vlan, u8 qos, bool set)
+{
+       u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
+
+       if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
+           !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
+               return -ENOTSUPP;
+
+       esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n",
+                 vport, vlan, qos, set);
+
+       if (set) {
+               MLX5_SET(modify_esw_vport_context_in, in,
+                        esw_vport_context.vport_cvlan_strip, 1);
+               /* insert only if no vlan in packet */
+               MLX5_SET(modify_esw_vport_context_in, in,
+                        esw_vport_context.vport_cvlan_insert, 1);
+               MLX5_SET(modify_esw_vport_context_in, in,
+                        esw_vport_context.cvlan_pcp, qos);
+               MLX5_SET(modify_esw_vport_context_in, in,
+                        esw_vport_context.cvlan_id, vlan);
+       }
+
+       MLX5_SET(modify_esw_vport_context_in, in,
+                field_select.vport_cvlan_strip, 1);
+       MLX5_SET(modify_esw_vport_context_in, in,
+                field_select.vport_cvlan_insert, 1);
+
+       return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
+}
+
+/* HW L2 Table (MPFS) management */
+static int set_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index,
+                                 u8 *mac, u8 vlan_valid, u16 vlan)
+{
+       u32 in[MLX5_ST_SZ_DW(set_l2_table_entry_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(set_l2_table_entry_out)] = {0};
+       u8 *in_mac_addr;
+
+       MLX5_SET(set_l2_table_entry_in, in, opcode,
+                MLX5_CMD_OP_SET_L2_TABLE_ENTRY);
+       MLX5_SET(set_l2_table_entry_in, in, table_index, index);
+       MLX5_SET(set_l2_table_entry_in, in, vlan_valid, vlan_valid);
+       MLX5_SET(set_l2_table_entry_in, in, vlan, vlan);
+
+       in_mac_addr = MLX5_ADDR_OF(set_l2_table_entry_in, in, mac_address);
+       ether_addr_copy(&in_mac_addr[2], mac);
+
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+static int del_l2_table_entry_cmd(struct mlx5_core_dev *dev, u32 index)
+{
+       u32 in[MLX5_ST_SZ_DW(delete_l2_table_entry_in)] = {0};
+       u32 out[MLX5_ST_SZ_DW(delete_l2_table_entry_out)] = {0};
+
+       MLX5_SET(delete_l2_table_entry_in, in, opcode,
+                MLX5_CMD_OP_DELETE_L2_TABLE_ENTRY);
+       MLX5_SET(delete_l2_table_entry_in, in, table_index, index);
+       return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
+}
+
+static int alloc_l2_table_index(struct mlx5_l2_table *l2_table, u32 *ix)
+{
+       int err = 0;
+
+       *ix = find_first_zero_bit(l2_table->bitmap, l2_table->size);
+       if (*ix >= l2_table->size)
+               err = -ENOSPC;
+       else
+               __set_bit(*ix, l2_table->bitmap);
+
+       return err;
+}
+
+static void free_l2_table_index(struct mlx5_l2_table *l2_table, u32 ix)
+{
+       __clear_bit(ix, l2_table->bitmap);
+}
+
+static int set_l2_table_entry(struct mlx5_core_dev *dev, u8 *mac,
+                             u8 vlan_valid, u16 vlan,
+                             u32 *index)
+{
+       struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+       int err;
+
+       err = alloc_l2_table_index(l2_table, index);
+       if (err)
+               return err;
+
+       err = set_l2_table_entry_cmd(dev, *index, mac, vlan_valid, vlan);
+       if (err)
+               free_l2_table_index(l2_table, *index);
+
+       return err;
+}
+
+static void del_l2_table_entry(struct mlx5_core_dev *dev, u32 index)
+{
+       struct mlx5_l2_table *l2_table = &dev->priv.eswitch->l2_table;
+
+       del_l2_table_entry_cmd(dev, index);
+       free_l2_table_index(l2_table, index);
+}
+
+/* E-Switch FDB */
+static struct mlx5_flow_rule *
+esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
+{
+       int match_header = MLX5_MATCH_OUTER_HEADERS;
+       struct mlx5_flow_destination dest;
+       struct mlx5_flow_rule *flow_rule = NULL;
+       u32 *match_v;
+       u32 *match_c;
+       u8 *dmac_v;
+       u8 *dmac_c;
+
+       match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+       match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
+       if (!match_v || !match_c) {
+               printf("mlx5_core: WARN: ""FDB: Failed to alloc match 
parameters\n");
+               goto out;
+       }
+       dmac_v = MLX5_ADDR_OF(fte_match_param, match_v,
+                             outer_headers.dmac_47_16);
+       dmac_c = MLX5_ADDR_OF(fte_match_param, match_c,
+                             outer_headers.dmac_47_16);
+
+       ether_addr_copy(dmac_v, mac);
+       /* Match criteria mask */
+       memset(dmac_c, 0xff, 6);
+
+       dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
+       dest.vport_num = vport;
+
+       esw_debug(esw->dev,
+                 "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
+                 dmac_v, dmac_c, vport);
+       flow_rule =
+               mlx5_add_flow_rule(esw->fdb_table.fdb,
+                                  match_header,
+                                  match_c,
+                                  match_v,
+                                  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
+                                  0, &dest);
+       if (IS_ERR_OR_NULL(flow_rule)) {
+               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));
+               flow_rule = NULL;
+       }
+out:
+       kfree(match_v);
+       kfree(match_c);
+       return flow_rule;
+}
+
+static int esw_create_fdb_table(struct mlx5_eswitch *esw)
+{
+       int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+       struct mlx5_core_dev *dev = esw->dev;
+       struct mlx5_flow_namespace *root_ns;
+       struct mlx5_flow_table *fdb;
+       struct mlx5_flow_group *g;
+       void *match_criteria;
+       int table_size;
+       u32 *flow_group_in;
+       u8 *dmac;
+       int err = 0;
+
+       esw_debug(dev, "Create FDB log_max_size(%d)\n",
+                 MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
+
+       root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
+       if (!root_ns) {
+               esw_warn(dev, "Failed to get FDB flow namespace\n");
+               return -ENOMEM;
+       }
+
+       flow_group_in = mlx5_vzalloc(inlen);
+       if (!flow_group_in)
+               return -ENOMEM;
+       memset(flow_group_in, 0, inlen);
+
+       /* (-2) Since MaorG said so .. */
+       table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)) - 2;
+
+       fdb = mlx5_create_flow_table(root_ns, 0, "FDB", table_size);
+       if (IS_ERR_OR_NULL(fdb)) {
+               err = PTR_ERR(fdb);
+               esw_warn(dev, "Failed to create FDB Table err %d\n", err);
+               goto out;
+       }
+
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
+                MLX5_MATCH_OUTER_HEADERS);
+       match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, 
match_criteria);
+       dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, 
outer_headers.dmac_47_16);
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 
table_size - 1);
+       eth_broadcast_addr(dmac);
+
+       g = mlx5_create_flow_group(fdb, flow_group_in);
+       if (IS_ERR_OR_NULL(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create flow group err(%d)\n", err);
+               goto out;
+       }
+
+       esw->fdb_table.addr_grp = g;
+       esw->fdb_table.fdb = fdb;
+out:
+       kfree(flow_group_in);
+       if (err && !IS_ERR_OR_NULL(fdb))
+               mlx5_destroy_flow_table(fdb);
+       return err;
+}
+
+static void esw_destroy_fdb_table(struct mlx5_eswitch *esw)
+{
+       if (!esw->fdb_table.fdb)
+               return;
+
+       esw_debug(esw->dev, "Destroy FDB Table\n");
+       mlx5_destroy_flow_group(esw->fdb_table.addr_grp);
+       mlx5_destroy_flow_table(esw->fdb_table.fdb);
+       esw->fdb_table.fdb = NULL;
+       esw->fdb_table.addr_grp = NULL;
+}
+
+/* E-Switch vport UC/MC lists management */
+typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
+                                struct vport_addr *vaddr);
+
+static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+       struct hlist_head *hash = esw->l2_table.l2_hash;
+       struct esw_uc_addr *esw_uc;
+       u8 *mac = vaddr->node.addr;
+       u32 vport = vaddr->vport;
+       int err;
+
+       esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+       if (esw_uc) {
+               esw_warn(esw->dev,
+                        "Failed to set L2 mac(%pM) for vport(%d), mac is 
already in use by vport(%d)\n",
+                        mac, vport, esw_uc->vport);
+               return -EEXIST;
+       }
+
+       esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL);
+       if (!esw_uc)
+               return -ENOMEM;
+       esw_uc->vport = vport;
+
+       err = set_l2_table_entry(esw->dev, mac, 0, 0, &esw_uc->table_index);
+       if (err)
+               goto abort;
+
+       if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */
+               vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+
+       esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+                 vport, mac, esw_uc->table_index, vaddr->flow_rule);
+       return err;
+abort:
+       l2addr_hash_del(esw_uc);
+       return err;
+}
+
+static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+       struct hlist_head *hash = esw->l2_table.l2_hash;
+       struct esw_uc_addr *esw_uc;
+       u8 *mac = vaddr->node.addr;
+       u32 vport = vaddr->vport;
+
+       esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
+       if (!esw_uc || esw_uc->vport != vport) {
+               esw_debug(esw->dev,
+                         "MAC(%pM) doesn't belong to vport (%d)\n",
+                         mac, vport);
+               return -EINVAL;
+       }
+       esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n",
+                 vport, mac, esw_uc->table_index, vaddr->flow_rule);
+
+       del_l2_table_entry(esw->dev, esw_uc->table_index);
+
+       if (vaddr->flow_rule)
+               mlx5_del_flow_rule(vaddr->flow_rule);
+       vaddr->flow_rule = NULL;
+
+       l2addr_hash_del(esw_uc);
+       return 0;
+}
+
+static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+       struct hlist_head *hash = esw->mc_table;
+       struct esw_mc_addr *esw_mc;
+       u8 *mac = vaddr->node.addr;
+       u32 vport = vaddr->vport;
+
+       if (!esw->fdb_table.fdb)
+               return 0;
+
+       esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+       if (esw_mc)
+               goto add;
+
+       esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
+       if (!esw_mc)
+               return -ENOMEM;
+
+       esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
+               esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
+add:
+       esw_mc->refcnt++;
+       /* Forward MC MAC to vport */
+       vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
+       esw_debug(esw->dev,
+                 "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) 
uplinkfr(%p)\n",
+                 vport, mac, vaddr->flow_rule,
+                 esw_mc->refcnt, esw_mc->uplink_rule);
+       return 0;
+}
+
+static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
+{
+       struct hlist_head *hash = esw->mc_table;
+       struct esw_mc_addr *esw_mc;
+       u8 *mac = vaddr->node.addr;
+       u32 vport = vaddr->vport;
+
+       if (!esw->fdb_table.fdb)
+               return 0;
+
+       esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
+       if (!esw_mc) {
+               esw_warn(esw->dev,
+                        "Failed to find eswitch MC addr for MAC(%pM) 
vport(%d)",
+                        mac, vport);
+               return -EINVAL;
+       }
+       esw_debug(esw->dev,
+                 "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) 
uplinkfr(%p)\n",
+                 vport, mac, vaddr->flow_rule, esw_mc->refcnt,
+                 esw_mc->uplink_rule);
+
+       if (vaddr->flow_rule)
+               mlx5_del_flow_rule(vaddr->flow_rule);
+       vaddr->flow_rule = NULL;
+
+       if (--esw_mc->refcnt)
+               return 0;
+
+       if (esw_mc->uplink_rule)
+               mlx5_del_flow_rule(esw_mc->uplink_rule);
+
+       l2addr_hash_del(esw_mc);
+       return 0;
+}
+
+/* Apply vport UC/MC list to HW l2 table and FDB table */
+static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
+                                     u32 vport_num, int list_type)
+{
+       struct mlx5_vport *vport = &esw->vports[vport_num];
+       bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
+       vport_addr_action vport_addr_add;
+       vport_addr_action vport_addr_del;
+       struct vport_addr *addr;
+       struct l2addr_node *node;
+       struct hlist_head *hash;
+       struct hlist_node *tmp;
+       int hi;
+
+       vport_addr_add = is_uc ? esw_add_uc_addr :
+                                esw_add_mc_addr;
+       vport_addr_del = is_uc ? esw_del_uc_addr :
+                                esw_del_mc_addr;
+
+       hash = is_uc ? vport->uc_list : vport->mc_list;
+       for_each_l2hash_node(node, tmp, hash, hi) {
+               addr = container_of(node, struct vport_addr, node);
+               switch (addr->action) {
+               case MLX5_ACTION_ADD:
+                       vport_addr_add(esw, addr);
+                       addr->action = MLX5_ACTION_NONE;
+                       break;
+               case MLX5_ACTION_DEL:
+                       vport_addr_del(esw, addr);
+                       l2addr_hash_del(addr);
+                       break;
+               }
+       }
+}
+
+/* Sync vport UC/MC list from vport context */
+static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
+                                      u32 vport_num, int list_type)
+{
+       struct mlx5_vport *vport = &esw->vports[vport_num];
+       bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
+       u8 (*mac_list)[ETH_ALEN];
+       struct l2addr_node *node;
+       struct vport_addr *addr;
+       struct hlist_head *hash;
+       struct hlist_node *tmp;
+       int size;
+       int err;
+       int hi;
+       int i;
+
+       size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
+                      MLX5_MAX_MC_PER_VPORT(esw->dev);
+
+       mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
+       if (!mac_list)
+               return;
+
+       hash = is_uc ? vport->uc_list : vport->mc_list;
+
+       for_each_l2hash_node(node, tmp, hash, hi) {
+               addr = container_of(node, struct vport_addr, node);
+               addr->action = MLX5_ACTION_DEL;
+       }
+
+       err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
+                                           mac_list, &size);
+       if (err)
+               return;
+       esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
+                 vport_num, is_uc ? "UC" : "MC", size);
+
+       for (i = 0; i < size; i++) {
+               if (is_uc && !is_valid_ether_addr(mac_list[i]))
+                       continue;
+
+               if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
+                       continue;
+
+               addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
+               if (addr) {
+                       addr->action = MLX5_ACTION_NONE;
+                       continue;
+               }
+
+               addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
+                                      GFP_KERNEL);
+               if (!addr) {
+                       esw_warn(esw->dev,
+                                "Failed to add MAC(%pM) to vport[%d] DB\n",
+                                mac_list[i], vport_num);
+                       continue;
+               }
+               addr->vport = vport_num;
+               addr->action = MLX5_ACTION_ADD;
+       }
+       kfree(mac_list);
+}
+
+static void esw_vport_change_handler(struct work_struct *work)
+{
+       struct mlx5_vport *vport =
+               container_of(work, struct mlx5_vport, vport_change_handler);
+       struct mlx5_core_dev *dev = vport->dev;
+       struct mlx5_eswitch *esw = dev->priv.eswitch;
+       u8 mac[ETH_ALEN];
+
+       mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
+       esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
+                 vport->vport, mac);
+
+       if (vport->enabled_events & UC_ADDR_CHANGE) {
+               esw_update_vport_addr_list(esw, vport->vport,
+                                          MLX5_NIC_VPORT_LIST_TYPE_UC);
+               esw_apply_vport_addr_list(esw, vport->vport,
+                                         MLX5_NIC_VPORT_LIST_TYPE_UC);
+       }
+
+       if (vport->enabled_events & MC_ADDR_CHANGE) {
+               esw_update_vport_addr_list(esw, vport->vport,
+                                          MLX5_NIC_VPORT_LIST_TYPE_MC);
+               esw_apply_vport_addr_list(esw, vport->vport,
+                                         MLX5_NIC_VPORT_LIST_TYPE_MC);
+       }
+
+       esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
+       if (vport->enabled)
+               arm_vport_context_events_cmd(dev, vport->vport,
+                                            vport->enabled_events);
+}
+
+static void esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
+                                       struct mlx5_vport *vport)
+{
+       int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
+       struct mlx5_flow_group *vlan_grp = NULL;
+       struct mlx5_flow_group *drop_grp = NULL;
+       struct mlx5_core_dev *dev = esw->dev;
+       struct mlx5_flow_namespace *root_ns;
+       struct mlx5_flow_table *acl;
+       void *match_criteria;
+       char table_name[32];
+       u32 *flow_group_in;
+       int table_size = 2;
+       int err = 0;
+
+       if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
+               return;
+
+       esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n",
+                 vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size));
+
+       root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS);
+       if (!root_ns) {
+               esw_warn(dev, "Failed to get E-Switch egress flow namespace\n");
+               return;
+       }
+
+       flow_group_in = mlx5_vzalloc(inlen);
+       if (!flow_group_in)
+               return;
+
+       snprintf(table_name, 32, "egress_%d", vport->vport);
+       acl = mlx5_create_vport_flow_table(root_ns, vport->vport, 0, 
table_name, table_size);
+       if (IS_ERR_OR_NULL(acl)) {
+               err = PTR_ERR(acl);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] egress flow 
Table, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, 
MLX5_MATCH_OUTER_HEADERS);
+       match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, 
match_criteria);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, 
outer_headers.cvlan_tag);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, 
outer_headers.first_vid);
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
+
+       vlan_grp = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(vlan_grp)) {
+               err = PTR_ERR(vlan_grp);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] egress 
allowed vlans flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+
+       memset(flow_group_in, 0, inlen);
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
+       drop_grp = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(drop_grp)) {
+               err = PTR_ERR(drop_grp);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop 
flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+
+       vport->egress.acl = acl;
+       vport->egress.drop_grp = drop_grp;
+       vport->egress.allowed_vlans_grp = vlan_grp;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to