static int esw_vport_ingress_prio_tag_config(struct mlx5_eswitch *esw,
                                             struct mlx5_vport *vport)
 {
-       struct mlx5_core_dev *dev = esw->dev;
        struct mlx5_flow_act flow_act = {0};
        struct mlx5_flow_spec *spec;
        int err = 0;
 
        /* For prio tag mode, there is only 1 FTEs:
-        * 1) Untagged packets - push prio tag VLAN, allow
+        * 1) Untagged packets - push prio tag VLAN and modify metadata if
+        * required, allow
         * Unmatched traffic is allowed by default
         */
 
-       if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
-               return -EOPNOTSUPP;
-
-       esw_vport_cleanup_ingress_rules(esw, vport);
-
-       err = esw_vport_enable_ingress_acl(esw, vport);
-       if (err) {
-               mlx5_core_warn(esw->dev,
-                              "failed to enable prio tag ingress acl (%d) on vport[%d]\n",
-                              err, vport->vport);
-               return err;
-       }
-
-       esw_debug(esw->dev,
-                 "vport[%d] configure ingress rules\n", vport->vport);
-
        spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
        if (!spec) {
                err = -ENOMEM;
        flow_act.vlan[0].ethtype = ETH_P_8021Q;
        flow_act.vlan[0].vid = 0;
        flow_act.vlan[0].prio = 0;
+
+       if (vport->ingress.modify_metadata_rule) {
+               flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
+               flow_act.modify_id = vport->ingress.modify_metadata_id;
+       }
+
        vport->ingress.allow_rule =
                mlx5_add_flow_rules(vport->ingress.acl, spec,
                                    &flow_act, NULL, 0);
        return err;
 }
 
+static int esw_vport_add_ingress_acl_modify_metadata(struct mlx5_eswitch *esw,
+                                                    struct mlx5_vport *vport)
+{
+       u8 action[MLX5_UN_SZ_BYTES(set_action_in_add_action_in_auto)] = {};
+       struct mlx5_flow_act flow_act = {};
+       struct mlx5_flow_spec spec = {};
+       int err = 0;
+
+       MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
+       MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);
+       MLX5_SET(set_action_in, action, data,
+                mlx5_eswitch_get_vport_metadata_for_match(esw, vport->vport));
+
+       err = mlx5_modify_header_alloc(esw->dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS,
+                                      1, action, &vport->ingress.modify_metadata_id);
+       if (err) {
+               esw_warn(esw->dev,
+                        "failed to alloc modify header for vport %d ingress acl (%d)\n",
+                        vport->vport, err);
+               return err;
+       }
+
+       flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | MLX5_FLOW_CONTEXT_ACTION_ALLOW;
+       flow_act.modify_id = vport->ingress.modify_metadata_id;
+       vport->ingress.modify_metadata_rule = mlx5_add_flow_rules(vport->ingress.acl,
+                                                                 &spec, &flow_act, NULL, 0);
+       if (IS_ERR(vport->ingress.modify_metadata_rule)) {
+               err = PTR_ERR(vport->ingress.modify_metadata_rule);
+               esw_warn(esw->dev,
+                        "failed to add setting metadata rule for vport %d ingress acl, err(%d)\n",
+                        vport->vport, err);
+               vport->ingress.modify_metadata_rule = NULL;
+               goto out;
+       }
+
+out:
+       if (err)
+               mlx5_modify_header_dealloc(esw->dev, vport->ingress.modify_metadata_id);
+       return err;
+}
+
+void esw_vport_del_ingress_acl_modify_metadata(struct mlx5_eswitch *esw,
+                                              struct mlx5_vport *vport)
+{
+       if (vport->ingress.modify_metadata_rule) {
+               mlx5_del_flow_rules(vport->ingress.modify_metadata_rule);
+               mlx5_modify_header_dealloc(esw->dev, vport->ingress.modify_metadata_id);
+
+               vport->ingress.modify_metadata_rule = NULL;
+       }
+}
+
 static int esw_vport_egress_prio_tag_config(struct mlx5_eswitch *esw,
                                            struct mlx5_vport *vport)
 {
        struct mlx5_flow_spec *spec;
        int err = 0;
 
+       if (!MLX5_CAP_GEN(esw->dev, prio_tag_required))
+               return 0;
+
        /* For prio tag mode, there is only 1 FTEs:
         * 1) prio tag packets - pop the prio tag VLAN, allow
         * Unmatched traffic is allowed by default
        return err;
 }
 
-static int esw_prio_tag_acls_config(struct mlx5_eswitch *esw, int nvports)
+static int esw_vport_ingress_common_config(struct mlx5_eswitch *esw,
+                                          struct mlx5_vport *vport)
 {
-       struct mlx5_vport *vport = NULL;
-       int i, j;
        int err;
 
-       mlx5_esw_for_each_vf_vport(esw, i, vport, nvports) {
+       if (!mlx5_eswitch_vport_match_metadata_enabled(esw) &&
+           !MLX5_CAP_GEN(esw->dev, prio_tag_required))
+               return 0;
+
+       esw_vport_cleanup_ingress_rules(esw, vport);
+
+       err = esw_vport_enable_ingress_acl(esw, vport);
+       if (err) {
+               esw_warn(esw->dev,
+                        "failed to enable ingress acl (%d) on vport[%d]\n",
+                        err, vport->vport);
+               return err;
+       }
+
+       esw_debug(esw->dev,
+                 "vport[%d] configure ingress rules\n", vport->vport);
+
+       if (mlx5_eswitch_vport_match_metadata_enabled(esw)) {
+               err = esw_vport_add_ingress_acl_modify_metadata(esw, vport);
+               if (err)
+                       goto out;
+       }
+
+       if (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
+           mlx5_eswitch_is_vf_vport(esw, vport->vport)) {
                err = esw_vport_ingress_prio_tag_config(esw, vport);
                if (err)
-                       goto err_ingress;
-               err = esw_vport_egress_prio_tag_config(esw, vport);
+                       goto out;
+       }
+
+out:
+       if (err)
+               esw_vport_disable_ingress_acl(esw, vport);
+       return err;
+}
+
+static int esw_create_offloads_acl_tables(struct mlx5_eswitch *esw)
+{
+       struct mlx5_vport *vport;
+       int i, j;
+       int err;
+
+       mlx5_esw_for_all_vports(esw, i, vport) {
+               err = esw_vport_ingress_common_config(esw, vport);
                if (err)
-                       goto err_egress;
+                       goto err_ingress;
+
+               if (mlx5_eswitch_is_vf_vport(esw, vport->vport)) {
+                       err = esw_vport_egress_prio_tag_config(esw, vport);
+                       if (err)
+                               goto err_egress;
+               }
        }
 
+       if (mlx5_eswitch_vport_match_metadata_enabled(esw))
+               esw_info(esw->dev, "Use metadata reg_c as source vport to match\n");
+
        return 0;
 
 err_egress:
        esw_vport_disable_ingress_acl(esw, vport);
 err_ingress:
-       mlx5_esw_for_each_vf_vport_reverse(esw, j, vport, i - 1) {
+       for (j = MLX5_VPORT_PF; j < i; j++) {
+               vport = &esw->vports[j];
                esw_vport_disable_egress_acl(esw, vport);
                esw_vport_disable_ingress_acl(esw, vport);
        }
        return err;
 }
 
-static void esw_prio_tag_acls_cleanup(struct mlx5_eswitch *esw)
+static void esw_destroy_offloads_acl_tables(struct mlx5_eswitch *esw)
 {
        struct mlx5_vport *vport;
        int i;
 
-       mlx5_esw_for_each_vf_vport(esw, i, vport, esw->nvports) {
+       mlx5_esw_for_all_vports(esw, i, vport) {
                esw_vport_disable_egress_acl(esw, vport);
                esw_vport_disable_ingress_acl(esw, vport);
        }
+
+       esw->flags &= ~MLX5_ESWITCH_VPORT_MATCH_METADATA;
 }
 
 static int esw_offloads_steering_init(struct mlx5_eswitch *esw, int nvports)
        memset(&esw->fdb_table.offloads, 0, sizeof(struct offloads_fdb));
        mutex_init(&esw->fdb_table.offloads.fdb_prio_lock);
 
-       if (MLX5_CAP_GEN(esw->dev, prio_tag_required)) {
-               err = esw_prio_tag_acls_config(esw, nvports);
-               if (err)
-                       return err;
-       }
+       err = esw_create_offloads_acl_tables(esw);
+       if (err)
+               return err;
 
        err = esw_create_offloads_fdb_tables(esw, nvports);
        if (err)
-               return err;
+               goto create_fdb_err;
 
        err = esw_create_offloads_table(esw, nvports);
        if (err)
 create_ft_err:
        esw_destroy_offloads_fdb_tables(esw);
 
+create_fdb_err:
+       esw_destroy_offloads_acl_tables(esw);
+
        return err;
 }
 
        esw_destroy_vport_rx_group(esw);
        esw_destroy_offloads_table(esw);
        esw_destroy_offloads_fdb_tables(esw);
-       if (MLX5_CAP_GEN(esw->dev, prio_tag_required))
-               esw_prio_tag_acls_cleanup(esw);
+       esw_destroy_offloads_acl_tables(esw);
 }
 
 static void esw_functions_changed_event_handler(struct work_struct *work)
        return vport_num >= MLX5_VPORT_FIRST_VF &&
               vport_num <= esw->dev->priv.sriov.max_vfs;
 }
+
+bool mlx5_eswitch_vport_match_metadata_enabled(const struct mlx5_eswitch *esw)
+{
+       return !!(esw->flags & MLX5_ESWITCH_VPORT_MATCH_METADATA);
+}
+EXPORT_SYMBOL(mlx5_eswitch_vport_match_metadata_enabled);
+
+u32 mlx5_eswitch_get_vport_metadata_for_match(const struct mlx5_eswitch *esw,
+                                             u16 vport_num)
+{
+       return ((MLX5_CAP_GEN(esw->dev, vhca_id) & 0xffff) << 16) | vport_num;
+}
+EXPORT_SYMBOL(mlx5_eswitch_get_vport_metadata_for_match);