--- /dev/null
+// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
+// Copyright (c) 2021, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+#include <linux/if_vlan.h>
+#include "act.h"
+#include "pedit.h"
+#include "en/tc_priv.h"
+#include "en/mod_hdr.h"
+
+static int pedit_header_offsets[] = {
+       [FLOW_ACT_MANGLE_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
+       [FLOW_ACT_MANGLE_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
+       [FLOW_ACT_MANGLE_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
+       [FLOW_ACT_MANGLE_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
+       [FLOW_ACT_MANGLE_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
+};
+
+#define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype])
+
+static int
+set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset,
+             struct pedit_headers_action *hdrs,
+             struct netlink_ext_ack *extack)
+{
+       u32 *curr_pmask, *curr_pval;
+
+       curr_pmask = (u32 *)(pedit_header(&hdrs->masks, hdr_type) + offset);
+       curr_pval  = (u32 *)(pedit_header(&hdrs->vals, hdr_type) + offset);
+
+       if (*curr_pmask & mask) { /* disallow acting twice on the same location */
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "curr_pmask and new mask same. Acting twice on same location");
+               goto out_err;
+       }
+
+       *curr_pmask |= mask;
+       *curr_pval  |= (val & mask);
+
+       return 0;
+
+out_err:
+       return -EOPNOTSUPP;
+}
+
+static int
+parse_pedit_to_modify_hdr(struct mlx5e_priv *priv,
+                         const struct flow_action_entry *act, int namespace,
+                         struct mlx5e_tc_flow_parse_attr *parse_attr,
+                         struct pedit_headers_action *hdrs,
+                         struct netlink_ext_ack *extack)
+{
+       u8 cmd = (act->id == FLOW_ACTION_MANGLE) ? 0 : 1;
+       u8 htype = act->mangle.htype;
+       int err = -EOPNOTSUPP;
+       u32 mask, val, offset;
+
+       if (htype == FLOW_ACT_MANGLE_UNSPEC) {
+               NL_SET_ERR_MSG_MOD(extack, "legacy pedit isn't offloaded");
+               goto out_err;
+       }
+
+       if (!mlx5e_mod_hdr_max_actions(priv->mdev, namespace)) {
+               NL_SET_ERR_MSG_MOD(extack, "The pedit offload action is not supported");
+               goto out_err;
+       }
+
+       mask = act->mangle.mask;
+       val = act->mangle.val;
+       offset = act->mangle.offset;
+
+       err = set_pedit_val(htype, ~mask, val, offset, &hdrs[cmd], extack);
+       if (err)
+               goto out_err;
+
+       hdrs[cmd].pedits++;
+
+       return 0;
+out_err:
+       return err;
+}
+
+static int
+parse_pedit_to_reformat(const struct flow_action_entry *act,
+                       struct mlx5e_tc_flow_parse_attr *parse_attr,
+                       struct netlink_ext_ack *extack)
+{
+       u32 mask, val, offset;
+       u32 *p;
+
+       if (act->id != FLOW_ACTION_MANGLE) {
+               NL_SET_ERR_MSG_MOD(extack, "Unsupported action id");
+               return -EOPNOTSUPP;
+       }
+
+       if (act->mangle.htype != FLOW_ACT_MANGLE_HDR_TYPE_ETH) {
+               NL_SET_ERR_MSG_MOD(extack, "Only Ethernet modification is supported");
+               return -EOPNOTSUPP;
+       }
+
+       mask = ~act->mangle.mask;
+       val = act->mangle.val;
+       offset = act->mangle.offset;
+       p = (u32 *)&parse_attr->eth;
+       *(p + (offset >> 2)) |= (val & mask);
+
+       return 0;
+}
+
+int
+mlx5e_tc_act_pedit_parse_action(struct mlx5e_priv *priv,
+                               const struct flow_action_entry *act, int namespace,
+                               struct mlx5e_tc_flow_parse_attr *parse_attr,
+                               struct pedit_headers_action *hdrs,
+                               struct mlx5e_tc_flow *flow,
+                               struct netlink_ext_ack *extack)
+{
+       if (flow && flow_flag_test(flow, L3_TO_L2_DECAP))
+               return parse_pedit_to_reformat(act, parse_attr, extack);
+
+       return parse_pedit_to_modify_hdr(priv, act, namespace, parse_attr, hdrs, extack);
+}
+
+static bool
+tc_act_can_offload_pedit(struct mlx5e_tc_act_parse_state *parse_state,
+                        const struct flow_action_entry *act,
+                        int act_index)
+{
+       return true;
+}
+
+static int
+tc_act_parse_pedit(struct mlx5e_tc_act_parse_state *parse_state,
+                  const struct flow_action_entry *act,
+                  struct mlx5e_priv *priv,
+                  struct mlx5_flow_attr *attr)
+{
+       struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
+       struct mlx5e_tc_flow *flow = parse_state->flow;
+       enum mlx5_flow_namespace_type ns_type;
+       int err;
+
+       ns_type = mlx5e_get_flow_namespace(flow);
+
+       err = mlx5e_tc_act_pedit_parse_action(flow->priv, act, ns_type,
+                                             attr->parse_attr, parse_state->hdrs,
+                                             flow, parse_state->extack);
+       if (err)
+               return err;
+
+       if (flow_flag_test(flow, L3_TO_L2_DECAP))
+               goto out;
+
+       attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
+
+       if (ns_type == MLX5_FLOW_NAMESPACE_FDB)
+               esw_attr->split_count = esw_attr->out_count;
+
+out:
+       return 0;
+}
+
+struct mlx5e_tc_act mlx5e_tc_act_pedit = {
+       .can_offload = tc_act_can_offload_pedit,
+       .parse_action = tc_act_parse_pedit,
+};
 
 #include <linux/refcount.h>
 #include <linux/completion.h>
 #include <linux/if_macvlan.h>
-#include <net/tc_act/tc_pedit.h>
 #include <net/psample.h>
 #include <net/arp.h>
 #include <net/ipv6_stubs.h>
 #include "en/tc_tun_encap.h"
 #include "en/tc/sample.h"
 #include "en/tc/act/act.h"
+#include "en/tc/act/pedit.h"
 #include "lib/devcom.h"
 #include "lib/geneve.h"
 #include "lib/fs_chains.h"
        return flow_flag_test(flow, OFFLOADED);
 }
 
-static int get_flow_name_space(struct mlx5e_tc_flow *flow)
+int mlx5e_get_flow_namespace(struct mlx5e_tc_flow *flow)
 {
        return mlx5e_is_eswitch_flow(flow) ?
                MLX5_FLOW_NAMESPACE_FDB : MLX5_FLOW_NAMESPACE_KERNEL;
 {
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
 
-       return get_flow_name_space(flow) == MLX5_FLOW_NAMESPACE_FDB ?
+       return mlx5e_get_flow_namespace(flow) == MLX5_FLOW_NAMESPACE_FDB ?
                &esw->offloads.mod_hdr :
                &priv->fs.tc.mod_hdr;
 }
        struct mlx5e_mod_hdr_handle *mh;
 
        mh = mlx5e_mod_hdr_attach(priv->mdev, get_mod_hdr_table(priv, flow),
-                                 get_flow_name_space(flow),
+                                 mlx5e_get_flow_namespace(flow),
                                  &parse_attr->mod_hdr_acts);
        if (IS_ERR(mh))
                return PTR_ERR(mh);
        struct mlx5_modify_hdr *mod_hdr;
 
        mod_hdr = mlx5_modify_header_alloc(priv->mdev,
-                                          get_flow_name_space(flow),
+                                          mlx5e_get_flow_namespace(flow),
                                           mod_hdr_acts->num_actions,
                                           mod_hdr_acts->actions);
        if (IS_ERR(mod_hdr))
        return err;
 }
 
-struct pedit_headers {
-       struct ethhdr  eth;
-       struct vlan_hdr vlan;
-       struct iphdr   ip4;
-       struct ipv6hdr ip6;
-       struct tcphdr  tcp;
-       struct udphdr  udp;
-};
-
-struct pedit_headers_action {
-       struct pedit_headers    vals;
-       struct pedit_headers    masks;
-       u32                     pedits;
-};
-
-static int pedit_header_offsets[] = {
-       [FLOW_ACT_MANGLE_HDR_TYPE_ETH] = offsetof(struct pedit_headers, eth),
-       [FLOW_ACT_MANGLE_HDR_TYPE_IP4] = offsetof(struct pedit_headers, ip4),
-       [FLOW_ACT_MANGLE_HDR_TYPE_IP6] = offsetof(struct pedit_headers, ip6),
-       [FLOW_ACT_MANGLE_HDR_TYPE_TCP] = offsetof(struct pedit_headers, tcp),
-       [FLOW_ACT_MANGLE_HDR_TYPE_UDP] = offsetof(struct pedit_headers, udp),
-};
-
-#define pedit_header(_ph, _htype) ((void *)(_ph) + pedit_header_offsets[_htype])
-
-static int set_pedit_val(u8 hdr_type, u32 mask, u32 val, u32 offset,
-                        struct pedit_headers_action *hdrs,
-                        struct netlink_ext_ack *extack)
-{
-       u32 *curr_pmask, *curr_pval;
-
-       curr_pmask = (u32 *)(pedit_header(&hdrs->masks, hdr_type) + offset);
-       curr_pval  = (u32 *)(pedit_header(&hdrs->vals, hdr_type) + offset);
-
-       if (*curr_pmask & mask) { /* disallow acting twice on the same location */
-               NL_SET_ERR_MSG_MOD(extack,
-                                  "curr_pmask and new mask same. Acting twice on same location");
-               goto out_err;
-       }
-
-       *curr_pmask |= mask;
-       *curr_pval  |= (val & mask);
-
-       return 0;
-
-out_err:
-       return -EOPNOTSUPP;
-}
-
 struct mlx5_fields {
        u8  field;
        u8  field_bsize;
 
 static const struct pedit_headers zero_masks = {};
 
-static int
-parse_pedit_to_modify_hdr(struct mlx5e_priv *priv,
-                         const struct flow_action_entry *act, int namespace,
-                         struct mlx5e_tc_flow_parse_attr *parse_attr,
-                         struct pedit_headers_action *hdrs,
-                         struct netlink_ext_ack *extack)
-{
-       u8 cmd = (act->id == FLOW_ACTION_MANGLE) ? 0 : 1;
-       int err = -EOPNOTSUPP;
-       u32 mask, val, offset;
-       u8 htype;
-
-       htype = act->mangle.htype;
-       err = -EOPNOTSUPP; /* can't be all optimistic */
-
-       if (htype == FLOW_ACT_MANGLE_UNSPEC) {
-               NL_SET_ERR_MSG_MOD(extack, "legacy pedit isn't offloaded");
-               goto out_err;
-       }
-
-       if (!mlx5e_mod_hdr_max_actions(priv->mdev, namespace)) {
-               NL_SET_ERR_MSG_MOD(extack,
-                                  "The pedit offload action is not supported");
-               goto out_err;
-       }
-
-       mask = act->mangle.mask;
-       val = act->mangle.val;
-       offset = act->mangle.offset;
-
-       err = set_pedit_val(htype, ~mask, val, offset, &hdrs[cmd], extack);
-       if (err)
-               goto out_err;
-
-       hdrs[cmd].pedits++;
-
-       return 0;
-out_err:
-       return err;
-}
-
-static int
-parse_pedit_to_reformat(const struct flow_action_entry *act,
-                       struct mlx5e_tc_flow_parse_attr *parse_attr,
-                       struct netlink_ext_ack *extack)
-{
-       u32 mask, val, offset;
-       u32 *p;
-
-       if (act->id != FLOW_ACTION_MANGLE) {
-               NL_SET_ERR_MSG_MOD(extack, "Unsupported action id");
-               return -EOPNOTSUPP;
-       }
-
-       if (act->mangle.htype != FLOW_ACT_MANGLE_HDR_TYPE_ETH) {
-               NL_SET_ERR_MSG_MOD(extack, "Only Ethernet modification is supported");
-               return -EOPNOTSUPP;
-       }
-
-       mask = ~act->mangle.mask;
-       val = act->mangle.val;
-       offset = act->mangle.offset;
-       p = (u32 *)&parse_attr->eth;
-       *(p + (offset >> 2)) |= (val & mask);
-
-       return 0;
-}
-
-static int parse_tc_pedit_action(struct mlx5e_priv *priv,
-                                const struct flow_action_entry *act, int namespace,
-                                struct mlx5e_tc_flow_parse_attr *parse_attr,
-                                struct pedit_headers_action *hdrs,
-                                struct mlx5e_tc_flow *flow,
-                                struct netlink_ext_ack *extack)
-{
-       if (flow && flow_flag_test(flow, L3_TO_L2_DECAP))
-               return parse_pedit_to_reformat(act, parse_attr, extack);
-
-       return parse_pedit_to_modify_hdr(priv, act, namespace,
-                                        parse_attr, hdrs, extack);
-}
-
 static int alloc_tc_pedit_action(struct mlx5e_priv *priv, int namespace,
                                 struct mlx5e_tc_flow_parse_attr *parse_attr,
                                 struct pedit_headers_action *hdrs,
                return -EOPNOTSUPP;
        }
 
-       err = parse_tc_pedit_action(priv, &pedit_act, namespace, parse_attr, hdrs, NULL, extack);
+       err = mlx5e_tc_act_pedit_parse_action(priv, &pedit_act, namespace, parse_attr, hdrs,
+                                             NULL, extack);
        *action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
 
        return err;
            !hdrs[TCA_PEDIT_KEY_EX_CMD_ADD].pedits)
                return 0;
 
-       ns_type = get_flow_name_space(flow);
+       ns_type = mlx5e_get_flow_namespace(flow);
 
        err = alloc_tc_pedit_action(priv, ns_type, parse_attr, hdrs,
                                    &attr->action, extack);
        struct mlx5e_tc_act_parse_state *parse_state;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
        struct mlx5_flow_attr *attr = flow->attr;
-       struct pedit_headers_action hdrs[2] = {};
        enum mlx5_flow_namespace_type ns_type;
        const struct flow_action_entry *act;
+       struct pedit_headers_action *hdrs;
        struct mlx5e_tc_act *tc_act;
        int err, i;
 
        parse_attr = attr->parse_attr;
        parse_state = &parse_attr->parse_state;
        mlx5e_tc_act_init_parse_state(parse_state, flow, flow_action, extack);
-       ns_type = get_flow_name_space(flow);
+       ns_type = mlx5e_get_flow_namespace(flow);
+       hdrs = parse_state->hdrs;
 
        flow_action_for_each(i, act, flow_action) {
                switch (act->id) {
-               case FLOW_ACTION_MANGLE:
-               case FLOW_ACTION_ADD:
-                       err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_KERNEL,
-                                                   parse_attr, hdrs, NULL, extack);
-                       if (err)
-                               return err;
-
-                       attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
-                       break;
                case FLOW_ACTION_VLAN_MANGLE:
                        err = add_vlan_rewrite_action(priv,
                                                      MLX5_FLOW_NAMESPACE_KERNEL,
                                struct mlx5e_tc_flow *flow,
                                struct netlink_ext_ack *extack)
 {
-       struct pedit_headers_action hdrs[2] = {};
        struct mlx5_eswitch *esw = priv->mdev->priv.eswitch;
        struct mlx5e_tc_act_parse_state *parse_state;
        struct mlx5e_tc_flow_parse_attr *parse_attr;
        enum mlx5_flow_namespace_type ns_type;
        const struct flow_action_entry *act;
        struct mlx5_esw_flow_attr *esw_attr;
+       struct pedit_headers_action *hdrs;
        struct mlx5e_tc_act *tc_act;
        int err, i, if_count = 0;
        bool ptype_host = false;
        parse_attr = attr->parse_attr;
        parse_state = &parse_attr->parse_state;
        mlx5e_tc_act_init_parse_state(parse_state, flow, flow_action, extack);
-       ns_type = get_flow_name_space(flow);
+       ns_type = mlx5e_get_flow_namespace(flow);
+       hdrs = parse_state->hdrs;
 
        flow_action_for_each(i, act, flow_action) {
                switch (act->id) {
                        attr->action |= MLX5_FLOW_CONTEXT_ACTION_PACKET_REFORMAT;
                        flow_flag_set(flow, L3_TO_L2_DECAP);
                        break;
-               case FLOW_ACTION_MANGLE:
-               case FLOW_ACTION_ADD:
-                       err = parse_tc_pedit_action(priv, act, MLX5_FLOW_NAMESPACE_FDB,
-                                                   parse_attr, hdrs, flow, extack);
-                       if (err)
-                               return err;
-
-                       if (!flow_flag_test(flow, L3_TO_L2_DECAP)) {
-                               attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
-                               esw_attr->split_count = esw_attr->out_count;
-                       }
-                       break;
                case FLOW_ACTION_REDIRECT_INGRESS: {
                        struct net_device *out_dev;
 
        flow->cookie = f->cookie;
        flow->priv = priv;
 
-       attr = mlx5_alloc_flow_attr(get_flow_name_space(flow));
+       attr = mlx5_alloc_flow_attr(mlx5e_get_flow_namespace(flow));
        if (!attr)
                goto err_free;