net/mlx5: E-Switch, Vport ingress/egress ACLs rules for spoofchk
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx5 / core / eswitch.c
index bc3d9f8..17d093c 100644 (file)
@@ -401,7 +401,7 @@ static int esw_create_fdb_table(struct mlx5_eswitch *esw, int nvports)
        memset(flow_group_in, 0, inlen);
 
        table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
-       fdb = mlx5_create_flow_table(root_ns, 0, table_size);
+       fdb = mlx5_create_flow_table(root_ns, 0, table_size, 0);
        if (IS_ERR_OR_NULL(fdb)) {
                err = PTR_ERR(fdb);
                esw_warn(dev, "Failed to create FDB Table err %d\n", err);
@@ -646,7 +646,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
        err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
                                            mac_list, &size);
        if (err)
-               return;
+               goto out;
        esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
                  vport_num, is_uc ? "UC" : "MC", size);
 
@@ -674,6 +674,7 @@ static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
                addr->vport = vport_num;
                addr->action = MLX5_ACTION_ADD;
        }
+out:
        kfree(mac_list);
 }
 
@@ -709,15 +710,471 @@ static void esw_vport_change_handler(struct work_struct *work)
                                             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;
+       u32 *flow_group_in;
+       /* The egress acl table contains 2 rules:
+        * 1)Allow traffic with vlan_tag=vst_vlan_id
+        * 2)Drop all other traffic.
+        */
+       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;
+
+       acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
+       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.vlan_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;
+out:
+       kfree(flow_group_in);
+       if (err && !IS_ERR_OR_NULL(vlan_grp))
+               mlx5_destroy_flow_group(vlan_grp);
+       if (err && !IS_ERR_OR_NULL(acl))
+               mlx5_destroy_flow_table(acl);
+}
+
+static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
+                                          struct mlx5_vport *vport)
+{
+       if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
+               mlx5_del_flow_rule(vport->egress.allowed_vlan);
+
+       if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
+               mlx5_del_flow_rule(vport->egress.drop_rule);
+
+       vport->egress.allowed_vlan = NULL;
+       vport->egress.drop_rule = NULL;
+}
+
+static void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
+                                        struct mlx5_vport *vport)
+{
+       if (IS_ERR_OR_NULL(vport->egress.acl))
+               return;
+
+       esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport);
+
+       esw_vport_cleanup_egress_rules(esw, vport);
+       mlx5_destroy_flow_group(vport->egress.allowed_vlans_grp);
+       mlx5_destroy_flow_group(vport->egress.drop_grp);
+       mlx5_destroy_flow_table(vport->egress.acl);
+       vport->egress.allowed_vlans_grp = NULL;
+       vport->egress.drop_grp = NULL;
+       vport->egress.acl = NULL;
+}
+
+static void esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
+                                        struct mlx5_vport *vport)
+{
+       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 *acl;
+       struct mlx5_flow_group *g;
+       void *match_criteria;
+       u32 *flow_group_in;
+       /* The ingress acl table contains 4 groups
+        * (2 active rules at the same time -
+        *      1 allow rule from one of the first 3 groups.
+        *      1 drop rule from the last group):
+        * 1)Allow untagged traffic with smac=original mac.
+        * 2)Allow untagged traffic.
+        * 3)Allow traffic with smac=original mac.
+        * 4)Drop all other traffic.
+        */
+       int table_size = 4;
+       int err = 0;
+
+       if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
+               return;
+
+       esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n",
+                 vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size));
+
+       root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS);
+       if (!root_ns) {
+               esw_warn(dev, "Failed to get E-Switch ingress flow namespace\n");
+               return;
+       }
+
+       flow_group_in = mlx5_vzalloc(inlen);
+       if (!flow_group_in)
+               return;
+
+       acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
+       if (IS_ERR_OR_NULL(acl)) {
+               err = PTR_ERR(acl);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow Table, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+       vport->ingress.acl = acl;
+
+       match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
+
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.vlan_tag);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0);
+       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);
+
+       g = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged spoofchk flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+       vport->ingress.allow_untagged_spoofchk_grp = g;
+
+       memset(flow_group_in, 0, inlen);
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.vlan_tag);
+       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);
+
+       g = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+       vport->ingress.allow_untagged_only_grp = g;
+
+       memset(flow_group_in, 0, inlen);
+       MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16);
+       MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0);
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 2);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 2);
+
+       g = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] ingress spoofchk flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+       vport->ingress.allow_spoofchk_only_grp = g;
+
+       memset(flow_group_in, 0, inlen);
+       MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 3);
+       MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 3);
+
+       g = mlx5_create_flow_group(acl, flow_group_in);
+       if (IS_ERR_OR_NULL(g)) {
+               err = PTR_ERR(g);
+               esw_warn(dev, "Failed to create E-Switch vport[%d] ingress drop flow group, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+       vport->ingress.drop_grp = g;
+
+out:
+       if (err) {
+               if (!IS_ERR_OR_NULL(vport->ingress.allow_spoofchk_only_grp))
+                       mlx5_destroy_flow_group(
+                                       vport->ingress.allow_spoofchk_only_grp);
+               if (!IS_ERR_OR_NULL(vport->ingress.allow_untagged_only_grp))
+                       mlx5_destroy_flow_group(
+                                       vport->ingress.allow_untagged_only_grp);
+               if (!IS_ERR_OR_NULL(vport->ingress.allow_untagged_spoofchk_grp))
+                       mlx5_destroy_flow_group(
+                               vport->ingress.allow_untagged_spoofchk_grp);
+               if (!IS_ERR_OR_NULL(vport->ingress.acl))
+                       mlx5_destroy_flow_table(vport->ingress.acl);
+       }
+
+       kfree(flow_group_in);
+}
+
+static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw,
+                                           struct mlx5_vport *vport)
+{
+       if (!IS_ERR_OR_NULL(vport->ingress.drop_rule))
+               mlx5_del_flow_rule(vport->ingress.drop_rule);
+
+       if (!IS_ERR_OR_NULL(vport->ingress.allow_rule))
+               mlx5_del_flow_rule(vport->ingress.allow_rule);
+
+       vport->ingress.drop_rule = NULL;
+       vport->ingress.allow_rule = NULL;
+}
+
+static void esw_vport_disable_ingress_acl(struct mlx5_eswitch *esw,
+                                         struct mlx5_vport *vport)
+{
+       if (IS_ERR_OR_NULL(vport->ingress.acl))
+               return;
+
+       esw_debug(esw->dev, "Destroy vport[%d] E-Switch ingress ACL\n", vport->vport);
+
+       esw_vport_cleanup_ingress_rules(esw, vport);
+       mlx5_destroy_flow_group(vport->ingress.allow_spoofchk_only_grp);
+       mlx5_destroy_flow_group(vport->ingress.allow_untagged_only_grp);
+       mlx5_destroy_flow_group(vport->ingress.allow_untagged_spoofchk_grp);
+       mlx5_destroy_flow_group(vport->ingress.drop_grp);
+       mlx5_destroy_flow_table(vport->ingress.acl);
+       vport->ingress.acl = NULL;
+       vport->ingress.drop_grp = NULL;
+       vport->ingress.allow_spoofchk_only_grp = NULL;
+       vport->ingress.allow_untagged_only_grp = NULL;
+       vport->ingress.allow_untagged_spoofchk_grp = NULL;
+}
+
+static int esw_vport_ingress_config(struct mlx5_eswitch *esw,
+                                   struct mlx5_vport *vport)
+{
+       u8 smac[ETH_ALEN];
+       u32 *match_v;
+       u32 *match_c;
+       int err = 0;
+       u8 *smac_v;
+
+       if (IS_ERR_OR_NULL(vport->ingress.acl)) {
+               esw_warn(esw->dev,
+                        "vport[%d] configure ingress rules failed, ingress acl is not initialized!\n",
+                        vport->vport);
+               return -EPERM;
+       }
+
+       if (vport->spoofchk) {
+               err = mlx5_query_nic_vport_mac_address(esw->dev, vport->vport, smac);
+               if (err) {
+                       esw_warn(esw->dev,
+                                "vport[%d] configure ingress rules failed, query smac failed, err(%d)\n",
+                                vport->vport, err);
+                       return err;
+               }
+
+               if (!is_valid_ether_addr(smac)) {
+                       mlx5_core_warn(esw->dev,
+                                      "vport[%d] configure ingress rules failed, illegal mac with spoofchk\n",
+                                      vport->vport);
+                       return -EPERM;
+               }
+       }
+
+       esw_vport_cleanup_ingress_rules(esw, vport);
+
+       if (!vport->vlan && !vport->qos && !vport->spoofchk)
+               return 0;
+
+       esw_debug(esw->dev,
+                 "vport[%d] configure ingress rules, vlan(%d) qos(%d)\n",
+                 vport->vport, vport->vlan, vport->qos);
+
+       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) {
+               err = -ENOMEM;
+               esw_warn(esw->dev, "vport[%d] configure ingress rules failed, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+
+       if (vport->vlan || vport->qos)
+               MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.vlan_tag);
+
+       if (vport->spoofchk) {
+               MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.smac_47_16);
+               MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.smac_15_0);
+               smac_v = MLX5_ADDR_OF(fte_match_param,
+                                     match_v,
+                                     outer_headers.smac_47_16);
+               ether_addr_copy(smac_v, smac);
+       }
+
+       vport->ingress.allow_rule =
+               mlx5_add_flow_rule(vport->ingress.acl,
+                                  MLX5_MATCH_OUTER_HEADERS,
+                                  match_c,
+                                  match_v,
+                                  MLX5_FLOW_CONTEXT_ACTION_ALLOW,
+                                  0, NULL);
+       if (IS_ERR_OR_NULL(vport->ingress.allow_rule)) {
+               err = PTR_ERR(vport->ingress.allow_rule);
+               pr_warn("vport[%d] configure ingress allow rule, err(%d)\n",
+                       vport->vport, err);
+               vport->ingress.allow_rule = NULL;
+               goto out;
+       }
+
+       memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param));
+       memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param));
+       vport->ingress.drop_rule =
+               mlx5_add_flow_rule(vport->ingress.acl,
+                                  0,
+                                  match_c,
+                                  match_v,
+                                  MLX5_FLOW_CONTEXT_ACTION_DROP,
+                                  0, NULL);
+       if (IS_ERR_OR_NULL(vport->ingress.drop_rule)) {
+               err = PTR_ERR(vport->ingress.drop_rule);
+               pr_warn("vport[%d] configure ingress drop rule, err(%d)\n",
+                       vport->vport, err);
+               vport->ingress.drop_rule = NULL;
+               goto out;
+       }
+
+out:
+       if (err)
+               esw_vport_cleanup_ingress_rules(esw, vport);
+
+       kfree(match_v);
+       kfree(match_c);
+       return err;
+}
+
+static int esw_vport_egress_config(struct mlx5_eswitch *esw,
+                                  struct mlx5_vport *vport)
+{
+       u32 *match_v;
+       u32 *match_c;
+       int err = 0;
+
+       if (IS_ERR_OR_NULL(vport->egress.acl)) {
+               esw_warn(esw->dev, "vport[%d] configure rgress rules failed, egress acl is not initialized!\n",
+                        vport->vport);
+               return -EPERM;
+       }
+
+       esw_vport_cleanup_egress_rules(esw, vport);
+
+       if (!vport->vlan && !vport->qos)
+               return 0;
+
+       esw_debug(esw->dev,
+                 "vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
+                 vport->vport, vport->vlan, vport->qos);
+
+       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) {
+               err = -ENOMEM;
+               esw_warn(esw->dev, "vport[%d] configure egress rules failed, err(%d)\n",
+                        vport->vport, err);
+               goto out;
+       }
+
+       /* Allowed vlan rule */
+       MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.vlan_tag);
+       MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.vlan_tag);
+       MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.first_vid);
+       MLX5_SET(fte_match_param, match_v, outer_headers.first_vid, vport->vlan);
+
+       vport->egress.allowed_vlan =
+               mlx5_add_flow_rule(vport->egress.acl,
+                                  MLX5_MATCH_OUTER_HEADERS,
+                                  match_c,
+                                  match_v,
+                                  MLX5_FLOW_CONTEXT_ACTION_ALLOW,
+                                  0, NULL);
+       if (IS_ERR_OR_NULL(vport->egress.allowed_vlan)) {
+               err = PTR_ERR(vport->egress.allowed_vlan);
+               pr_warn("vport[%d] configure egress allowed vlan rule failed, err(%d)\n",
+                       vport->vport, err);
+               vport->egress.allowed_vlan = NULL;
+               goto out;
+       }
+
+       /* Drop others rule (star rule) */
+       memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param));
+       memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param));
+       vport->egress.drop_rule =
+               mlx5_add_flow_rule(vport->egress.acl,
+                                  0,
+                                  match_c,
+                                  match_v,
+                                  MLX5_FLOW_CONTEXT_ACTION_DROP,
+                                  0, NULL);
+       if (IS_ERR_OR_NULL(vport->egress.drop_rule)) {
+               err = PTR_ERR(vport->egress.drop_rule);
+               pr_warn("vport[%d] configure egress drop rule failed, err(%d)\n",
+                       vport->vport, err);
+               vport->egress.drop_rule = NULL;
+       }
+out:
+       kfree(match_v);
+       kfree(match_c);
+       return err;
+}
+
 static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
                             int enable_events)
 {
        struct mlx5_vport *vport = &esw->vports[vport_num];
-       unsigned long flags;
 
+       mutex_lock(&esw->state_lock);
        WARN_ON(vport->enabled);
 
        esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num);
+
+       if (vport_num) { /* Only VFs need ACLs for VST and spoofchk filtering */
+               esw_vport_enable_ingress_acl(esw, vport);
+               esw_vport_enable_egress_acl(esw, vport);
+               esw_vport_ingress_config(esw, vport);
+               esw_vport_egress_config(esw, vport);
+       }
+
        mlx5_modify_vport_admin_state(esw->dev,
                                      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
                                      vport_num,
@@ -727,14 +1184,13 @@ static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
        vport->enabled_events = enable_events;
        esw_vport_change_handler(&vport->vport_change_handler);
 
-       spin_lock_irqsave(&vport->lock, flags);
        vport->enabled = true;
-       spin_unlock_irqrestore(&vport->lock, flags);
 
        arm_vport_context_events_cmd(esw->dev, vport_num, enable_events);
 
        esw->enabled_vports++;
        esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
+       mutex_unlock(&esw->state_lock);
 }
 
 static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
@@ -761,17 +1217,16 @@ static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
 static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
 {
        struct mlx5_vport *vport = &esw->vports[vport_num];
-       unsigned long flags;
 
        if (!vport->enabled)
                return;
 
        esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num);
        /* Mark this vport as disabled to discard new events */
-       spin_lock_irqsave(&vport->lock, flags);
        vport->enabled = false;
        vport->enabled_events = 0;
-       spin_unlock_irqrestore(&vport->lock, flags);
+
+       synchronize_irq(mlx5_get_msix_vec(esw->dev, MLX5_EQ_VEC_ASYNC));
 
        mlx5_modify_vport_admin_state(esw->dev,
                                      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
@@ -781,9 +1236,15 @@ static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
        flush_workqueue(esw->work_queue);
        /* Disable events from this vport */
        arm_vport_context_events_cmd(esw->dev, vport->vport, 0);
+       mutex_lock(&esw->state_lock);
        /* We don't assume VFs will cleanup after themselves */
        esw_cleanup_vport(esw, vport_num);
+       if (vport_num) {
+               esw_vport_disable_egress_acl(esw, vport);
+               esw_vport_disable_ingress_acl(esw, vport);
+       }
        esw->enabled_vports--;
+       mutex_unlock(&esw->state_lock);
 }
 
 /* Public E-Switch API */
@@ -802,6 +1263,12 @@ int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs)
                return -ENOTSUPP;
        }
 
+       if (!MLX5_CAP_ESW_INGRESS_ACL(esw->dev, ft_support))
+               esw_warn(esw->dev, "E-Switch ingress ACL is not supported by FW\n");
+
+       if (!MLX5_CAP_ESW_EGRESS_ACL(esw->dev, ft_support))
+               esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n");
+
        esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs);
 
        esw_disable_vport(esw, 0);
@@ -845,7 +1312,7 @@ void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
 int mlx5_eswitch_init(struct mlx5_core_dev *dev)
 {
        int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
-       int total_vports = 1 + pci_sriov_get_totalvfs(dev->pdev);
+       int total_vports = MLX5_TOTAL_VPORTS(dev);
        struct mlx5_eswitch *esw;
        int vport_num;
        int err;
@@ -887,6 +1354,8 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
                goto abort;
        }
 
+       mutex_init(&esw->state_lock);
+
        for (vport_num = 0; vport_num < total_vports; vport_num++) {
                struct mlx5_vport *vport = &esw->vports[vport_num];
 
@@ -894,7 +1363,6 @@ int mlx5_eswitch_init(struct mlx5_core_dev *dev)
                vport->dev = dev;
                INIT_WORK(&vport->vport_change_handler,
                          esw_vport_change_handler);
-               spin_lock_init(&vport->lock);
        }
 
        esw->total_vports = total_vports;
@@ -942,10 +1410,8 @@ void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
        }
 
        vport = &esw->vports[vport_num];
-       spin_lock(&vport->lock);
        if (vport->enabled)
                queue_work(esw->work_queue, &vport->vport_change_handler);
-       spin_unlock(&vport->lock);
 }
 
 /* Vport Administration */
@@ -957,12 +1423,22 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
                               int vport, u8 mac[ETH_ALEN])
 {
        int err = 0;
+       struct mlx5_vport *evport;
 
        if (!ESW_ALLOWED(esw))
                return -EPERM;
        if (!LEGAL_VPORT(esw, vport))
                return -EINVAL;
 
+       evport = &esw->vports[vport];
+
+       if (evport->spoofchk && !is_valid_ether_addr(mac)) {
+               mlx5_core_warn(esw->dev,
+                              "MAC invalidation is not allowed when spoofchk is on, vport(%d)\n",
+                              vport);
+               return -EPERM;
+       }
+
        err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
        if (err) {
                mlx5_core_warn(esw->dev,
@@ -971,6 +1447,11 @@ int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
                return err;
        }
 
+       mutex_lock(&esw->state_lock);
+       if (evport->enabled)
+               err = esw_vport_ingress_config(esw, evport);
+       mutex_unlock(&esw->state_lock);
+
        return err;
 }
 
@@ -990,6 +1471,7 @@ int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
 int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
                                  int vport, struct ifla_vf_info *ivi)
 {
+       struct mlx5_vport *evport;
        u16 vlan;
        u8 qos;
 
@@ -998,6 +1480,8 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
        if (!LEGAL_VPORT(esw, vport))
                return -EINVAL;
 
+       evport = &esw->vports[vport];
+
        memset(ivi, 0, sizeof(*ivi));
        ivi->vf = vport - 1;
 
@@ -1008,7 +1492,7 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
        query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
        ivi->vlan = vlan;
        ivi->qos = qos;
-       ivi->spoofchk = 0;
+       ivi->spoofchk = evport->spoofchk;
 
        return 0;
 }
@@ -1016,6 +1500,8 @@ int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
 int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
                                int vport, u16 vlan, u8 qos)
 {
+       struct mlx5_vport *evport;
+       int err = 0;
        int set = 0;
 
        if (!ESW_ALLOWED(esw))
@@ -1026,7 +1512,51 @@ int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
        if (vlan || qos)
                set = 1;
 
-       return modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
+       evport = &esw->vports[vport];
+
+       err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
+       if (err)
+               return err;
+
+       mutex_lock(&esw->state_lock);
+       evport->vlan = vlan;
+       evport->qos = qos;
+       if (evport->enabled) {
+               err = esw_vport_ingress_config(esw, evport);
+               if (err)
+                       goto out;
+               err = esw_vport_egress_config(esw, evport);
+       }
+
+out:
+       mutex_unlock(&esw->state_lock);
+       return err;
+}
+
+int mlx5_eswitch_set_vport_spoofchk(struct mlx5_eswitch *esw,
+                                   int vport, bool spoofchk)
+{
+       struct mlx5_vport *evport;
+       bool pschk;
+       int err = 0;
+
+       if (!ESW_ALLOWED(esw))
+               return -EPERM;
+       if (!LEGAL_VPORT(esw, vport))
+               return -EINVAL;
+
+       evport = &esw->vports[vport];
+
+       mutex_lock(&esw->state_lock);
+       pschk = evport->spoofchk;
+       evport->spoofchk = spoofchk;
+       if (evport->enabled)
+               err = esw_vport_ingress_config(esw, evport);
+       if (err)
+               evport->spoofchk = pschk;
+       mutex_unlock(&esw->state_lock);
+
+       return err;
 }
 
 int mlx5_eswitch_get_vport_stats(struct mlx5_eswitch *esw,