summaryrefslogtreecommitdiffstats
path: root/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c')
-rw-r--r--sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c804
1 files changed, 804 insertions, 0 deletions
diff --git a/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c b/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c
new file mode 100644
index 0000000..95d90a3
--- /dev/null
+++ b/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c
@@ -0,0 +1,804 @@
+/*-
+ * Copyright (c) 2013-2015, 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/if_ether.h>
+#include <linux/etherdevice.h>
+#include <dev/mlx5/driver.h>
+#include <dev/mlx5/flow_table.h>
+#include <dev/mlx5/eswitch_vacl.h>
+#include "mlx5_core.h"
+
+enum {
+ MLX5_ACL_LOOPBACK_GROUP_IDX = 0,
+ MLX5_ACL_UNTAGGED_GROUP_IDX = 1,
+ MLX5_ACL_VLAN_GROUP_IDX = 2,
+ MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX = 3,
+ MLX5_ACL_DEFAULT_GROUP_IDX = 4,
+ MLX5_ACL_GROUPS_NUM,
+};
+
+struct mlx_vacl_fr {
+ bool applied;
+ u32 fi;
+ u16 action;
+};
+
+struct mlx5_vacl_table {
+ struct mlx5_core_dev *dev;
+ u16 vport;
+ void *ft;
+ int max_ft_size;
+ int acl_type;
+
+ struct mlx_vacl_fr loopback_fr;
+ struct mlx_vacl_fr untagged_fr;
+ struct mlx_vacl_fr unknown_vlan_fr;
+ struct mlx_vacl_fr default_fr;
+
+ bool vlan_filter_enabled;
+ bool vlan_filter_applied;
+ unsigned long *vlan_allowed_bitmap;
+ u32 vlan_fi_table[4096];
+
+ bool spoofchk_enabled;
+ u8 smac[ETH_ALEN];
+};
+
+static int mlx5_vacl_table_allow_vlan(void *acl_t, u16 vlan)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ u32 *flow_context = NULL;
+ void *in_match_criteria = NULL;
+ void *in_match_value = NULL;
+ u8 *smac;
+ int vlan_mc_enable = MLX5_MATCH_OUTER_HEADERS;
+ int err = 0;
+
+ if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
+ return -EINVAL;
+
+ flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
+ if (!flow_context) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!in_match_criteria) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ /* Apply vlan rule */
+ MLX5_SET(flow_context, flow_context, action,
+ MLX5_FLOW_CONTEXT_ACTION_ALLOW);
+ in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
+ MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 1);
+ MLX5_SET(fte_match_param, in_match_value, outer_headers.first_vid,
+ vlan);
+ MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
+ MLX5_SET(fte_match_param, in_match_criteria, outer_headers.first_vid,
+ 0xfff);
+ if (acl_table->spoofchk_enabled) {
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_value,
+ outer_headers.smac_47_16);
+ ether_addr_copy(smac, acl_table->smac);
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+ err = mlx5_add_flow_table_entry(acl_table->ft, vlan_mc_enable,
+ in_match_criteria, flow_context,
+ &acl_table->vlan_fi_table[vlan]);
+out:
+ if (flow_context)
+ vfree(flow_context);
+ if (in_match_criteria)
+ vfree(in_match_criteria);
+ return err;
+}
+
+static int mlx5_vacl_table_apply_loopback_filter(void *acl_t, u16 new_action)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ u8 loopback_mc_enable = MLX5_MATCH_MISC_PARAMETERS;
+ u32 *flow_context = NULL;
+ void *in_match_criteria = NULL;
+ void *in_match_value = NULL;
+ void *mv_misc = NULL;
+ void *mc_misc = NULL;
+ int err = 0;
+
+ flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
+ if (!flow_context) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!in_match_criteria) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (acl_table->loopback_fr.applied)
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->loopback_fr.fi);
+
+ /* Apply new loopback rule */
+ MLX5_SET(flow_context, flow_context, action, new_action);
+ in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
+ mv_misc = MLX5_ADDR_OF(fte_match_param, in_match_value,
+ misc_parameters);
+ mc_misc = MLX5_ADDR_OF(fte_match_param, in_match_criteria,
+ misc_parameters);
+ MLX5_SET(fte_match_set_misc, mv_misc, source_port, acl_table->vport);
+
+ MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
+
+ err = mlx5_add_flow_table_entry(acl_table->ft, loopback_mc_enable,
+ in_match_criteria, flow_context,
+ &acl_table->loopback_fr.fi);
+ if (err) {
+ acl_table->loopback_fr.applied = false;
+ } else {
+ acl_table->loopback_fr.applied = true;
+ acl_table->loopback_fr.action = new_action;
+ }
+
+out:
+ if (flow_context)
+ vfree(flow_context);
+ if (in_match_criteria)
+ vfree(in_match_criteria);
+ return err;
+}
+
+static int mlx5_vacl_table_apply_default(void *acl_t, u16 new_action)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ u8 default_mc_enable = 0;
+ u32 *flow_context = NULL;
+ void *in_match_criteria = NULL;
+ int err = 0;
+
+ if (!acl_table->spoofchk_enabled)
+ return -EINVAL;
+
+ flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
+ if (!flow_context) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!in_match_criteria) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (acl_table->default_fr.applied)
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->default_fr.fi);
+
+ /* Apply new default rule */
+ MLX5_SET(flow_context, flow_context, action, new_action);
+ err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
+ in_match_criteria, flow_context,
+ &acl_table->default_fr.fi);
+ if (err) {
+ acl_table->default_fr.applied = false;
+ } else {
+ acl_table->default_fr.applied = true;
+ acl_table->default_fr.action = new_action;
+ }
+
+out:
+ if (flow_context)
+ vfree(flow_context);
+ if (in_match_criteria)
+ vfree(in_match_criteria);
+ return err;
+}
+
+static int mlx5_vacl_table_apply_untagged(void *acl_t, u16 new_action)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ u8 untagged_mc_enable = MLX5_MATCH_OUTER_HEADERS;
+ u8 *smac;
+ u32 *flow_context = NULL;
+ void *in_match_criteria = NULL;
+ void *in_match_value = NULL;
+ int err = 0;
+
+ flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
+ if (!flow_context) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!in_match_criteria) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (acl_table->untagged_fr.applied)
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->untagged_fr.fi);
+
+ /* Apply new untagged rule */
+ MLX5_SET(flow_context, flow_context, action, new_action);
+ in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
+ MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 0);
+ MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
+ if (acl_table->spoofchk_enabled) {
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_value,
+ outer_headers.smac_47_16);
+ ether_addr_copy(smac, acl_table->smac);
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+ err = mlx5_add_flow_table_entry(acl_table->ft, untagged_mc_enable,
+ in_match_criteria, flow_context,
+ &acl_table->untagged_fr.fi);
+ if (err) {
+ acl_table->untagged_fr.applied = false;
+ } else {
+ acl_table->untagged_fr.applied = true;
+ acl_table->untagged_fr.action = new_action;
+ }
+
+out:
+ if (flow_context)
+ vfree(flow_context);
+ if (in_match_criteria)
+ vfree(in_match_criteria);
+ return err;
+}
+
+static int mlx5_vacl_table_apply_unknown_vlan(void *acl_t, u16 new_action)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ u8 default_mc_enable = (!acl_table->spoofchk_enabled) ? 0 :
+ MLX5_MATCH_OUTER_HEADERS;
+ u32 *flow_context = NULL;
+ void *in_match_criteria = NULL;
+ void *in_match_value = NULL;
+ u8 *smac;
+ int err = 0;
+
+ flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
+ if (!flow_context) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
+ if (!in_match_criteria) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ if (acl_table->unknown_vlan_fr.applied)
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->unknown_vlan_fr.fi);
+
+ /* Apply new unknown vlan rule */
+ MLX5_SET(flow_context, flow_context, action, new_action);
+ if (acl_table->spoofchk_enabled) {
+ in_match_value = MLX5_ADDR_OF(flow_context, flow_context,
+ match_value);
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_value,
+ outer_headers.smac_47_16);
+ ether_addr_copy(smac, acl_table->smac);
+ smac = MLX5_ADDR_OF(fte_match_param,
+ in_match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+ err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
+ in_match_criteria, flow_context,
+ &acl_table->unknown_vlan_fr.fi);
+ if (err) {
+ acl_table->unknown_vlan_fr.applied = false;
+ } else {
+ acl_table->unknown_vlan_fr.applied = true;
+ acl_table->unknown_vlan_fr.action = new_action;
+ }
+
+out:
+ if (flow_context)
+ vfree(flow_context);
+ if (in_match_criteria)
+ vfree(in_match_criteria);
+ return err;
+}
+
+static int mlx5_vacl_table_apply_vlan_filter(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int index = 0;
+ int err_index = 0;
+ int err = 0;
+
+ if (acl_table->vlan_filter_applied)
+ return 0;
+
+ for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
+ index < 4096;
+ index = find_next_bit(acl_table->vlan_allowed_bitmap,
+ 4096, ++index)) {
+ err = mlx5_vacl_table_allow_vlan(acl_t, index);
+ if (err)
+ goto err_disable_vlans;
+ }
+
+ acl_table->vlan_filter_applied = true;
+ return 0;
+
+err_disable_vlans:
+ for (err_index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
+ err_index < index;
+ err_index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
+ ++err_index)) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->vlan_fi_table[err_index]);
+ }
+ return err;
+}
+
+static void mlx5_vacl_table_disapply_vlan_filter(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int index = 0;
+
+ if (!acl_table->vlan_filter_applied)
+ return;
+
+ for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
+ index < 4096;
+ index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
+ ++index)) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->vlan_fi_table[index]);
+ }
+
+ acl_table->vlan_filter_applied = false;
+}
+
+static void mlx5_vacl_table_disapply_all_filters(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ if (acl_table->default_fr.applied) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->default_fr.fi);
+ acl_table->default_fr.applied = false;
+ }
+ if (acl_table->unknown_vlan_fr.applied) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->unknown_vlan_fr.fi);
+ acl_table->unknown_vlan_fr.applied = false;
+ }
+ if (acl_table->loopback_fr.applied) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->loopback_fr.fi);
+ acl_table->loopback_fr.applied = false;
+ }
+ if (acl_table->untagged_fr.applied) {
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->untagged_fr.fi);
+ acl_table->untagged_fr.applied = false;
+ }
+ if (acl_table->vlan_filter_applied) {
+ mlx5_vacl_table_disapply_vlan_filter(acl_t);
+ acl_table->vlan_filter_applied = false;
+ }
+}
+
+static int mlx5_vacl_table_apply_all_filters(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int err = 0;
+
+ if (!acl_table->default_fr.applied && acl_table->spoofchk_enabled) {
+ err = mlx5_vacl_table_apply_default(acl_table,
+ acl_table->default_fr.action);
+ if (err)
+ goto err_disapply_all;
+ }
+
+ if (!acl_table->unknown_vlan_fr.applied) {
+ err = mlx5_vacl_table_apply_unknown_vlan(acl_table,
+ acl_table->unknown_vlan_fr.action);
+ if (err)
+ goto err_disapply_all;
+ }
+
+ if (!acl_table->loopback_fr.applied &&
+ acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
+ err = mlx5_vacl_table_apply_loopback_filter(
+ acl_table,
+ acl_table->loopback_fr.action);
+ if (err)
+ goto err_disapply_all;
+ }
+
+ if (!acl_table->untagged_fr.applied) {
+ err = mlx5_vacl_table_apply_untagged(acl_table,
+ acl_table->untagged_fr.action);
+ if (err)
+ goto err_disapply_all;
+ }
+
+ if (!acl_table->vlan_filter_applied && acl_table->vlan_filter_enabled) {
+ err = mlx5_vacl_table_apply_vlan_filter(acl_t);
+ if (err)
+ goto err_disapply_all;
+ }
+
+ goto out;
+
+err_disapply_all:
+ mlx5_vacl_table_disapply_all_filters(acl_t);
+
+out:
+ return err;
+}
+
+static void mlx5_vacl_table_destroy_ft(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ mlx5_vacl_table_disapply_all_filters(acl_t);
+ if (acl_table->ft)
+ mlx5_destroy_flow_table(acl_table->ft);
+ acl_table->ft = NULL;
+}
+
+static int mlx5_vacl_table_create_ft(void *acl_t, bool spoofchk)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int log_acl_ft_size;
+ int err = 0;
+ int groups_num = MLX5_ACL_GROUPS_NUM - 1;
+ int shift_idx = MLX5_ACL_UNTAGGED_GROUP_IDX;
+ u8 *smac;
+ struct mlx5_flow_table_group *g;
+
+ if (acl_table->ft)
+ return -EINVAL;
+
+ g = kcalloc(MLX5_ACL_GROUPS_NUM, sizeof(*g), GFP_KERNEL);
+ if (!g)
+ goto out;
+
+ acl_table->spoofchk_enabled = spoofchk;
+
+ /*
+ * for vlan group
+ */
+ log_acl_ft_size = 4096;
+ /*
+ * for loopback filter rule
+ */
+ log_acl_ft_size += 1;
+ /*
+ * for untagged rule
+ */
+ log_acl_ft_size += 1;
+ /*
+ * for unknown vlan rule
+ */
+ log_acl_ft_size += 1;
+ /*
+ * for default rule
+ */
+ log_acl_ft_size += 1;
+
+ log_acl_ft_size = order_base_2(log_acl_ft_size);
+ log_acl_ft_size = min_t(int, log_acl_ft_size, acl_table->max_ft_size);
+
+ if (log_acl_ft_size < 2)
+ goto out;
+
+ if (acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
+ /* Loopback filter group */
+ g[MLX5_ACL_LOOPBACK_GROUP_IDX].log_sz = 0;
+ g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria_enable =
+ MLX5_MATCH_MISC_PARAMETERS;
+ MLX5_SET_TO_ONES(fte_match_param,
+ g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria,
+ misc_parameters.source_port);
+ groups_num++;
+ shift_idx = MLX5_ACL_LOOPBACK_GROUP_IDX;
+ }
+ /* Untagged traffic group */
+ g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].log_sz = 0;
+ g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria_enable =
+ MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET(fte_match_param,
+ g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria,
+ outer_headers.vlan_tag, 1);
+ if (spoofchk) {
+ smac = MLX5_ADDR_OF(fte_match_param,
+ g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx]
+ .match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+
+ /* Allowed vlans group */
+ g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].log_sz = log_acl_ft_size - 1;
+ g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
+ MLX5_MATCH_OUTER_HEADERS;
+ MLX5_SET(fte_match_param,
+ g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
+ outer_headers.vlan_tag, 1);
+ MLX5_SET(fte_match_param,
+ g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
+ outer_headers.first_vid, 0xfff);
+ if (spoofchk) {
+ smac = MLX5_ADDR_OF(fte_match_param,
+ g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx]
+ .match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+
+ /* Unknown vlan traffic group */
+ g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].log_sz = 0;
+ g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
+ (spoofchk ? MLX5_MATCH_OUTER_HEADERS : 0);
+ if (spoofchk) {
+ smac = MLX5_ADDR_OF(
+ fte_match_param,
+ g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx]
+ .match_criteria,
+ outer_headers.smac_47_16);
+ memset(smac, 0xff, ETH_ALEN);
+ }
+
+ /*
+ * Default group - for spoofchk only.
+ */
+ g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].log_sz = 0;
+ g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].match_criteria_enable = 0;
+
+ acl_table->ft = mlx5_create_flow_table(acl_table->dev,
+ 0,
+ acl_table->acl_type,
+ acl_table->vport,
+ groups_num,
+ g);
+ if (!acl_table->ft) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = mlx5_vacl_table_apply_all_filters(acl_t);
+ if (err)
+ goto err_destroy_ft;
+
+ goto out;
+
+err_destroy_ft:
+ mlx5_vacl_table_destroy_ft(acl_table->ft);
+ acl_table->ft = NULL;
+
+out:
+ kfree(g);
+ return err;
+}
+
+void *mlx5_vacl_table_create(struct mlx5_core_dev *dev,
+ u16 vport, bool is_egress)
+{
+ struct mlx5_vacl_table *acl_table;
+ int err = 0;
+
+ if (is_egress && !MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev, ft_support))
+ return NULL;
+
+ if (!is_egress && !MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev, ft_support))
+ return NULL;
+
+ acl_table = kzalloc(sizeof(*acl_table), GFP_KERNEL);
+ if (!acl_table)
+ return NULL;
+
+ acl_table->acl_type = is_egress ? MLX5_FLOW_TABLE_TYPE_EGRESS_ACL :
+ MLX5_FLOW_TABLE_TYPE_INGRESS_ACL;
+ acl_table->max_ft_size = (is_egress ?
+ MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev,
+ log_max_ft_size) :
+ MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev,
+ log_max_ft_size));
+ acl_table->dev = dev;
+ acl_table->vport = vport;
+
+ /*
+ * default behavior : Allow and if spoofchk drop the default
+ */
+ acl_table->default_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+ acl_table->loopback_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
+ acl_table->unknown_vlan_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+ acl_table->untagged_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+ err = mlx5_vacl_table_create_ft(acl_table, false);
+ if (err)
+ goto err_free_acl_table;
+
+ acl_table->vlan_allowed_bitmap = kcalloc(BITS_TO_LONGS(4096),
+ sizeof(uintptr_t),
+ GFP_KERNEL);
+ if (!acl_table->vlan_allowed_bitmap)
+ goto err_destroy_ft;
+
+ goto out;
+
+err_destroy_ft:
+ mlx5_vacl_table_destroy_ft(acl_table->ft);
+ acl_table->ft = NULL;
+
+err_free_acl_table:
+ kfree(acl_table);
+ acl_table = NULL;
+
+out:
+ return (void *)acl_table;
+}
+EXPORT_SYMBOL(mlx5_vacl_table_create);
+
+void mlx5_vacl_table_cleanup(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ mlx5_vacl_table_destroy_ft(acl_t);
+ kfree(acl_table->vlan_allowed_bitmap);
+ kfree(acl_table);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_cleanup);
+
+int mlx5_vacl_table_add_vlan(void *acl_t, u16 vlan)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int err = 0;
+
+ if (test_bit(vlan, acl_table->vlan_allowed_bitmap))
+ return 0;
+ __set_bit(vlan, acl_table->vlan_allowed_bitmap);
+ if (!acl_table->vlan_filter_applied)
+ return 0;
+
+ err = mlx5_vacl_table_allow_vlan(acl_t, vlan);
+ if (err)
+ goto err_clear_vbit;
+
+ goto out;
+
+err_clear_vbit:
+ __clear_bit(vlan, acl_table->vlan_allowed_bitmap);
+
+out:
+ return err;
+}
+EXPORT_SYMBOL(mlx5_vacl_table_add_vlan);
+
+void mlx5_vacl_table_del_vlan(void *acl_t, u16 vlan)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
+ return;
+
+ __clear_bit(vlan, acl_table->vlan_allowed_bitmap);
+
+ if (!acl_table->vlan_filter_applied)
+ return;
+
+ mlx5_del_flow_table_entry(acl_table->ft,
+ acl_table->vlan_fi_table[vlan]);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_del_vlan);
+
+int mlx5_vacl_table_enable_vlan_filter(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ acl_table->vlan_filter_enabled = true;
+ return mlx5_vacl_table_apply_vlan_filter(acl_t);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_enable_vlan_filter);
+
+void mlx5_vacl_table_disable_vlan_filter(void *acl_t)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+
+ acl_table->vlan_filter_enabled = false;
+ mlx5_vacl_table_disapply_vlan_filter(acl_t);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_disable_vlan_filter);
+
+int mlx5_vacl_table_drop_untagged(void *acl_t)
+{
+ return mlx5_vacl_table_apply_untagged(acl_t,
+ MLX5_FLOW_CONTEXT_ACTION_DROP);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_drop_untagged);
+
+int mlx5_vacl_table_allow_untagged(void *acl_t)
+{
+ return mlx5_vacl_table_apply_untagged(acl_t,
+ MLX5_FLOW_CONTEXT_ACTION_ALLOW);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_allow_untagged);
+
+int mlx5_vacl_table_drop_unknown_vlan(void *acl_t)
+{
+ return mlx5_vacl_table_apply_unknown_vlan(acl_t,
+ MLX5_FLOW_CONTEXT_ACTION_DROP);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_drop_unknown_vlan);
+
+int mlx5_vacl_table_allow_unknown_vlan(void *acl_t)
+{
+ return mlx5_vacl_table_apply_unknown_vlan(acl_t,
+ MLX5_FLOW_CONTEXT_ACTION_ALLOW);
+}
+EXPORT_SYMBOL(mlx5_vacl_table_allow_unknown_vlan);
+
+int mlx5_vacl_table_set_spoofchk(void *acl_t, bool spoofchk, u8 *vport_mac)
+{
+ struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
+ int err = 0;
+
+ if (spoofchk == acl_table->spoofchk_enabled) {
+ if (!spoofchk ||
+ (spoofchk && !memcmp(acl_table->smac, vport_mac, ETH_ALEN)))
+ return 0;
+ }
+
+ ether_addr_copy(acl_table->smac, vport_mac);
+ if (spoofchk != acl_table->spoofchk_enabled) {
+ mlx5_vacl_table_destroy_ft(acl_t);
+ err = mlx5_vacl_table_create_ft(acl_t, spoofchk);
+ } else {
+ mlx5_vacl_table_disapply_all_filters(acl_t);
+ err = mlx5_vacl_table_apply_all_filters(acl_t);
+ }
+
+ return err;
+}
+EXPORT_SYMBOL(mlx5_vacl_table_set_spoofchk);
+
OpenPOWER on IntegriCloud