#include <net/net_namespace.h>
 #include <net/netns/generic.h>
 
+
+struct tcf_hashinfo {
+       struct hlist_head       *htab;
+       unsigned int            hmask;
+       spinlock_t              lock;
+       u32                     index;
+};
+
+struct tc_action_ops;
+
+struct tc_action {
+       const struct tc_action_ops      *ops;
+       __u32                   type; /* for backward compat(TCA_OLD_COMPAT) */
+       __u32                   order;
+       struct list_head        list;
+       struct tcf_hashinfo     *hinfo;
+};
+
 struct tcf_common {
+       struct tc_action                tcfc_act;
        struct hlist_node               tcfc_head;
        u32                             tcfc_index;
        int                             tcfc_refcnt;
        struct gnet_stats_basic_cpu __percpu *cpu_bstats;
        struct gnet_stats_queue __percpu *cpu_qstats;
 };
+#define tcf_act                common.tcfc_act
 #define tcf_head       common.tcfc_head
 #define tcf_index      common.tcfc_index
 #define tcf_refcnt     common.tcfc_refcnt
 #define tcf_lock       common.tcfc_lock
 #define tcf_rcu                common.tcfc_rcu
 
-struct tcf_hashinfo {
-       struct hlist_head       *htab;
-       unsigned int            hmask;
-       spinlock_t              lock;
-       u32                     index;
-};
-
 static inline unsigned int tcf_hash(u32 index, unsigned int hmask)
 {
        return index & hmask;
        dtm->expires = jiffies_to_clock_t(stm->expires);
 }
 
-struct tc_action {
-       void                    *priv;
-       const struct tc_action_ops      *ops;
-       __u32                   type; /* for backward compat(TCA_OLD_COMPAT) */
-       __u32                   order;
-       struct list_head        list;
-       struct tcf_hashinfo     *hinfo;
-};
-
 #ifdef CONFIG_NET_CLS_ACT
 
 #define ACT_P_CREATED 1
        struct list_head head;
        char    kind[IFNAMSIZ];
        __u32   type; /* TBD to match kind */
+       size_t  size;
        struct module           *owner;
        int     (*act)(struct sk_buff *, const struct tc_action *,
                       struct tcf_result *);
        int     (*dump)(struct sk_buff *, struct tc_action *, int, int);
        void    (*cleanup)(struct tc_action *, int bind);
-       int     (*lookup)(struct net *, struct tc_action *, u32);
+       int     (*lookup)(struct net *, struct tc_action **, u32);
        int     (*init)(struct net *net, struct nlattr *nla,
-                       struct nlattr *est, struct tc_action *act, int ovr,
+                       struct nlattr *est, struct tc_action **act, int ovr,
                        int bind);
        int     (*walk)(struct net *, struct sk_buff *,
-                       struct netlink_callback *, int, struct tc_action *);
+                       struct netlink_callback *, int, const struct tc_action_ops *);
        void    (*stats_update)(struct tc_action *, u64, u32, u64);
 };
 
 
 int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
                       struct netlink_callback *cb, int type,
-                      struct tc_action *a);
-int tcf_hash_search(struct tc_action_net *tn, struct tc_action *a, u32 index);
+                      const struct tc_action_ops *ops);
+int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index);
 u32 tcf_hash_new_index(struct tc_action_net *tn);
-bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action *a,
+bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a,
                    int bind);
 int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
-                   struct tc_action *a, int size, int bind, bool cpustats);
+                   struct tc_action **a, const struct tc_action_ops *ops, int bind,
+                   bool cpustats);
 void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est);
 void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a);
 
 
        struct sock_filter      *bpf_ops;
        const char              *bpf_name;
 };
-#define to_bpf(a) \
-       container_of(a->priv, struct tcf_bpf, common)
+#define to_bpf(a) ((struct tcf_bpf *)a)
 
 #endif /* __NET_TC_BPF_H */
 
        u16 zone;
 };
 
-#define to_connmark(a) \
-       container_of(a->priv, struct tcf_connmark_info, common)
+#define to_connmark(a) ((struct tcf_connmark_info *)a)
 
 #endif /* __NET_TC_CONNMARK_H */
 
 
        u32 update_flags;
 };
-#define to_tcf_csum(a) \
-       container_of(a->priv,struct tcf_csum,common)
+#define to_tcf_csum(a) ((struct tcf_csum *)a)
 
 #endif /* __NET_TC_CSUM_H */
 
        u32             tcfd_datalen;
        void            *tcfd_defdata;
 };
-#define to_defact(a) \
-       container_of(a->priv, struct tcf_defact, common)
+#define to_defact(a) ((struct tcf_defact *)a)
 
 #endif /* __NET_TC_DEF_H */
 
        atomic_t                packets;
 #endif
 };
-#define to_gact(a) \
-       container_of(a->priv, struct tcf_gact, common)
+#define to_gact(a) ((struct tcf_gact *)a)
 
 static inline bool is_tcf_gact_shot(const struct tc_action *a)
 {
        if (a->ops && a->ops->type != TCA_ACT_GACT)
                return false;
 
-       gact = a->priv;
+       gact = to_gact(a);
        if (gact->tcf_action == TC_ACT_SHOT)
                return true;
 
 
        /* list of metaids allowed */
        struct list_head metalist;
 };
-#define to_ife(a) \
-       container_of(a->priv, struct tcf_ife_info, common)
+#define to_ife(a) ((struct tcf_ife_info *)a)
 
 struct tcf_meta_info {
        const struct tcf_meta_ops *ops;
 
        char                    *tcfi_tname;
        struct xt_entry_target  *tcfi_t;
 };
-#define to_ipt(a) \
-       container_of(a->priv, struct tcf_ipt, common)
+#define to_ipt(a) ((struct tcf_ipt *)a)
 
 #endif /* __NET_TC_IPT_H */
 
        struct net_device __rcu *tcfm_dev;
        struct list_head        tcfm_list;
 };
-#define to_mirred(a) \
-       container_of(a->priv, struct tcf_mirred, common)
+#define to_mirred(a) ((struct tcf_mirred *)a)
 
 static inline bool is_tcf_mirred_redirect(const struct tc_action *a)
 {
 
        u32 flags;
 };
 
-static inline struct tcf_nat *to_tcf_nat(struct tc_action *a)
-{
-       return container_of(a->priv, struct tcf_nat, common);
-}
+#define to_tcf_nat(a) ((struct tcf_nat *)a)
 
 #endif /* __NET_TC_NAT_H */
 
        unsigned char           tcfp_flags;
        struct tc_pedit_key     *tcfp_keys;
 };
-#define to_pedit(a) \
-       container_of(a->priv, struct tcf_pedit, common)
+#define to_pedit(a) ((struct tcf_pedit *)a)
 
 #endif /* __NET_TC_PED_H */
 
        u16             queue_mapping;
        u16             ptype;
 };
-#define to_skbedit(a) \
-       container_of(a->priv, struct tcf_skbedit, common)
+#define to_skbedit(a) ((struct tcf_skbedit *)a)
 
 /* Return true iff action is mark */
 static inline bool is_tcf_skbedit_mark(const struct tc_action *a)
 
        u16                     tcfv_push_vid;
        __be16                  tcfv_push_proto;
 };
-#define to_vlan(a) \
-       container_of(a->priv, struct tcf_vlan, common)
+#define to_vlan(a) ((struct tcf_vlan *)a)
 
 #endif /* __NET_TC_VLAN_H */
 
 
 static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *a)
 {
-       struct tcf_common *p = a->priv;
+       struct tcf_common *p = (struct tcf_common *)a;
 
        spin_lock_bh(&hinfo->lock);
        hlist_del(&p->tcfc_head);
 
 int __tcf_hash_release(struct tc_action *a, bool bind, bool strict)
 {
-       struct tcf_common *p = a->priv;
+       struct tcf_common *p = (struct tcf_common *)a;
        int ret = 0;
 
        if (p) {
                if (p->tcfc_bindcnt <= 0 && p->tcfc_refcnt <= 0) {
                        if (a->ops->cleanup)
                                a->ops->cleanup(a, bind);
+                       list_del(&a->list);
                        tcf_hash_destroy(a->hinfo, a);
                        ret = ACT_P_DELETED;
                }
 EXPORT_SYMBOL(__tcf_hash_release);
 
 static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb,
-                          struct netlink_callback *cb, struct tc_action *a)
+                          struct netlink_callback *cb)
 {
-       struct hlist_head *head;
-       struct tcf_common *p;
        int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
        struct nlattr *nest;
 
        s_i = cb->args[0];
 
        for (i = 0; i < (hinfo->hmask + 1); i++) {
+               struct hlist_head *head;
+               struct tcf_common *p;
+
                head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
 
                hlist_for_each_entry_rcu(p, head, tcfc_head) {
                        index++;
                        if (index < s_i)
                                continue;
-                       a->priv = p;
-                       a->order = n_i;
 
-                       nest = nla_nest_start(skb, a->order);
+                       nest = nla_nest_start(skb, n_i);
                        if (nest == NULL)
                                goto nla_put_failure;
-                       err = tcf_action_dump_1(skb, a, 0, 0);
+                       err = tcf_action_dump_1(skb, (struct tc_action *)p, 0, 0);
                        if (err < 0) {
                                index--;
                                nlmsg_trim(skb, nest);
 }
 
 static int tcf_del_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb,
-                         struct tc_action *a)
+                         const struct tc_action_ops *ops)
 {
-       struct hlist_head *head;
-       struct hlist_node *n;
-       struct tcf_common *p;
        struct nlattr *nest;
        int i = 0, n_i = 0;
        int ret = -EINVAL;
 
-       nest = nla_nest_start(skb, a->order);
+       nest = nla_nest_start(skb, 0);
        if (nest == NULL)
                goto nla_put_failure;
-       if (nla_put_string(skb, TCA_KIND, a->ops->kind))
+       if (nla_put_string(skb, TCA_KIND, ops->kind))
                goto nla_put_failure;
        for (i = 0; i < (hinfo->hmask + 1); i++) {
+               struct hlist_head *head;
+               struct hlist_node *n;
+               struct tcf_common *p;
+
                head = &hinfo->htab[tcf_hash(i, hinfo->hmask)];
                hlist_for_each_entry_safe(p, n, head, tcfc_head) {
-                       a->priv = p;
-                       ret = __tcf_hash_release(a, false, true);
+                       ret = __tcf_hash_release((struct tc_action *)p, false, true);
                        if (ret == ACT_P_DELETED) {
-                               module_put(a->ops->owner);
+                               module_put(p->tcfc_act.ops->owner);
                                n_i++;
                        } else if (ret < 0)
                                goto nla_put_failure;
 
 int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
                       struct netlink_callback *cb, int type,
-                      struct tc_action *a)
+                      const struct tc_action_ops *ops)
 {
        struct tcf_hashinfo *hinfo = tn->hinfo;
 
-       a->hinfo = hinfo;
-
        if (type == RTM_DELACTION) {
-               return tcf_del_walker(hinfo, skb, a);
+               return tcf_del_walker(hinfo, skb, ops);
        } else if (type == RTM_GETACTION) {
-               return tcf_dump_walker(hinfo, skb, cb, a);
+               return tcf_dump_walker(hinfo, skb, cb);
        } else {
                WARN(1, "tcf_generic_walker: unknown action %d\n", type);
                return -EINVAL;
 }
 EXPORT_SYMBOL(tcf_hash_new_index);
 
-int tcf_hash_search(struct tc_action_net *tn, struct tc_action *a, u32 index)
+int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index)
 {
        struct tcf_hashinfo *hinfo = tn->hinfo;
        struct tcf_common *p = tcf_hash_lookup(index, hinfo);
 
        if (p) {
-               a->priv = p;
-               a->hinfo = hinfo;
+               *a = &p->tcfc_act;
                return 1;
        }
        return 0;
 }
 EXPORT_SYMBOL(tcf_hash_search);
 
-bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action *a,
+bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a,
                    int bind)
 {
        struct tcf_hashinfo *hinfo = tn->hinfo;
                if (bind)
                        p->tcfc_bindcnt++;
                p->tcfc_refcnt++;
-               a->priv = p;
-               a->hinfo = hinfo;
+               *a = &p->tcfc_act;
                return true;
        }
        return false;
 
 void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est)
 {
-       struct tcf_common *pc = a->priv;
+       struct tcf_common *pc = (struct tcf_common *)a;
        if (est)
                gen_kill_estimator(&pc->tcfc_bstats,
                                   &pc->tcfc_rate_est);
 EXPORT_SYMBOL(tcf_hash_cleanup);
 
 int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
-                   struct tc_action *a, int size, int bind, bool cpustats)
+                   struct tc_action **a, const struct tc_action_ops *ops,
+                   int bind, bool cpustats)
 {
-       struct tcf_common *p = kzalloc(size, GFP_KERNEL);
+       struct tcf_common *p = kzalloc(ops->size, GFP_KERNEL);
        struct tcf_hashinfo *hinfo = tn->hinfo;
        int err = -ENOMEM;
 
                }
        }
 
-       a->priv = (void *) p;
-       a->hinfo = hinfo;
+       p->tcfc_act.hinfo = hinfo;
+       p->tcfc_act.ops = ops;
+       INIT_LIST_HEAD(&p->tcfc_act.list);
+       *a = &p->tcfc_act;
        return 0;
 }
 EXPORT_SYMBOL(tcf_hash_create);
 
 void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a)
 {
-       struct tcf_common *p = a->priv;
+       struct tcf_common *p = (struct tcf_common *)a;
        struct tcf_hashinfo *hinfo = tn->hinfo;
        unsigned int h = tcf_hash(p->tcfc_index, hinfo->hmask);
 
 void tcf_hashinfo_destroy(const struct tc_action_ops *ops,
                          struct tcf_hashinfo *hinfo)
 {
-       struct tc_action a = {
-               .ops = ops,
-               .hinfo = hinfo,
-       };
        int i;
 
        for (i = 0; i < hinfo->hmask + 1; i++) {
                hlist_for_each_entry_safe(p, n, &hinfo->htab[i], tcfc_head) {
                        int ret;
 
-                       a.priv = p;
-                       ret = __tcf_hash_release(&a, false, true);
+                       ret = __tcf_hash_release((struct tc_action *)p, false, true);
                        if (ret == ACT_P_DELETED)
                                module_put(ops->owner);
                        else if (ret < 0)
                        module_put(a->ops->owner);
                else if (ret < 0)
                        return ret;
-               list_del(&a->list);
-               kfree(a);
        }
        return ret;
 }
                goto err_out;
        }
 
-       err = -ENOMEM;
-       a = kzalloc(sizeof(*a), GFP_KERNEL);
-       if (a == NULL)
-               goto err_mod;
-
-       a->ops = a_o;
-       INIT_LIST_HEAD(&a->list);
        /* backward compatibility for policer */
        if (name == NULL)
-               err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, a, ovr, bind);
+               err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, ovr, bind);
        else
-               err = a_o->init(net, nla, est, a, ovr, bind);
+               err = a_o->init(net, nla, est, &a, ovr, bind);
        if (err < 0)
-               goto err_free;
+               goto err_mod;
 
        /* module count goes up only when brand new policy is created
         * if it exists and is only bound to in a_o->init() then
 
        return a;
 
-err_free:
-       kfree(a);
 err_mod:
        module_put(a_o->owner);
 err_out:
 {
        int err = 0;
        struct gnet_dump d;
-       struct tcf_common *p = a->priv;
+       struct tcf_common *p = (struct tcf_common *)a;
 
        if (p == NULL)
                goto errout;
        return rtnl_unicast(skb, net, portid);
 }
 
-static struct tc_action *create_a(int i)
-{
-       struct tc_action *act;
-
-       act = kzalloc(sizeof(*act), GFP_KERNEL);
-       if (act == NULL) {
-               pr_debug("create_a: failed to alloc!\n");
-               return NULL;
-       }
-       act->order = i;
-       INIT_LIST_HEAD(&act->list);
-       return act;
-}
-
 static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
                                          struct nlmsghdr *n, u32 portid)
 {
        struct nlattr *tb[TCA_ACT_MAX + 1];
+       const struct tc_action_ops *ops;
        struct tc_action *a;
        int index;
        int err;
                goto err_out;
        index = nla_get_u32(tb[TCA_ACT_INDEX]);
 
-       err = -ENOMEM;
-       a = create_a(0);
-       if (a == NULL)
-               goto err_out;
-
        err = -EINVAL;
-       a->ops = tc_lookup_action(tb[TCA_ACT_KIND]);
-       if (a->ops == NULL) /* could happen in batch of actions */
-               goto err_free;
+       ops = tc_lookup_action(tb[TCA_ACT_KIND]);
+       if (!ops) /* could happen in batch of actions */
+               goto err_out;
        err = -ENOENT;
-       if (a->ops->lookup(net, a, index) == 0)
+       if (ops->lookup(net, &a, index) == 0)
                goto err_mod;
 
-       module_put(a->ops->owner);
+       module_put(ops->owner);
        return a;
 
 err_mod:
-       module_put(a->ops->owner);
-err_free:
-       kfree(a);
+       module_put(ops->owner);
 err_out:
        return ERR_PTR(err);
 }
        struct netlink_callback dcb;
        struct nlattr *nest;
        struct nlattr *tb[TCA_ACT_MAX + 1];
+       const struct tc_action_ops *ops;
        struct nlattr *kind;
-       struct tc_action a;
        int err = -ENOMEM;
 
        skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
 
        err = -EINVAL;
        kind = tb[TCA_ACT_KIND];
-       memset(&a, 0, sizeof(struct tc_action));
-       INIT_LIST_HEAD(&a.list);
-       a.ops = tc_lookup_action(kind);
-       if (a.ops == NULL) /*some idjot trying to flush unknown action */
+       ops = tc_lookup_action(kind);
+       if (!ops) /*some idjot trying to flush unknown action */
                goto err_out;
 
        nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION,
        if (nest == NULL)
                goto out_module_put;
 
-       err = a.ops->walk(net, skb, &dcb, RTM_DELACTION, &a);
+       err = ops->walk(net, skb, &dcb, RTM_DELACTION, ops);
        if (err < 0)
                goto out_module_put;
        if (err == 0)
 
        nlh->nlmsg_len = skb_tail_pointer(skb) - b;
        nlh->nlmsg_flags |= NLM_F_ROOT;
-       module_put(a.ops->owner);
+       module_put(ops->owner);
        err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
                             n->nlmsg_flags & NLM_F_ECHO);
        if (err > 0)
        return err;
 
 out_module_put:
-       module_put(a.ops->owner);
+       module_put(ops->owner);
 err_out:
 noflush_out:
        kfree_skb(skb);
        unsigned char *b = skb_tail_pointer(skb);
        struct nlattr *nest;
        struct tc_action_ops *a_o;
-       struct tc_action a;
        int ret = 0;
        struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh);
        struct nlattr *kind = find_dump_kind(cb->nlh);
        if (a_o == NULL)
                return 0;
 
-       memset(&a, 0, sizeof(struct tc_action));
-       a.ops = a_o;
-
        nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
                        cb->nlh->nlmsg_type, sizeof(*t), 0);
        if (!nlh)
        if (nest == NULL)
                goto out_module_put;
 
-       ret = a_o->walk(net, skb, cb, RTM_GETACTION, &a);
+       ret = a_o->walk(net, skb, cb, RTM_GETACTION, a_o);
        if (ret < 0)
                goto out_module_put;
 
 
 };
 
 static int bpf_net_id;
+static struct tc_action_ops act_bpf_ops;
 
 static int tcf_bpf(struct sk_buff *skb, const struct tc_action *act,
                   struct tcf_result *res)
 {
-       struct tcf_bpf *prog = act->priv;
+       struct tcf_bpf *prog = to_bpf(act);
        struct bpf_prog *filter;
        int action, filter_res;
        bool at_ingress = G_TC_AT(skb->tc_verd) & AT_INGRESS;
                        int bind, int ref)
 {
        unsigned char *tp = skb_tail_pointer(skb);
-       struct tcf_bpf *prog = act->priv;
+       struct tcf_bpf *prog = to_bpf(act);
        struct tc_act_bpf opt = {
                .index   = prog->tcf_index,
                .refcnt  = prog->tcf_refcnt - ref,
 }
 
 static int tcf_bpf_init(struct net *net, struct nlattr *nla,
-                       struct nlattr *est, struct tc_action *act,
+                       struct nlattr *est, struct tc_action **act,
                        int replace, int bind)
 {
        struct tc_action_net *tn = net_generic(net, bpf_net_id);
 
        if (!tcf_hash_check(tn, parm->index, act, bind)) {
                ret = tcf_hash_create(tn, parm->index, est, act,
-                                     sizeof(*prog), bind, true);
+                                     &act_bpf_ops, bind, true);
                if (ret < 0)
                        return ret;
 
                if (bind)
                        return 0;
 
-               tcf_hash_release(act, bind);
+               tcf_hash_release(*act, bind);
                if (!replace)
                        return -EEXIST;
        }
        if (ret < 0)
                goto out;
 
-       prog = to_bpf(act);
+       prog = to_bpf(*act);
        ASSERT_RTNL();
 
        if (res != ACT_P_CREATED)
        rcu_assign_pointer(prog->filter, cfg.filter);
 
        if (res == ACT_P_CREATED) {
-               tcf_hash_insert(tn, act);
+               tcf_hash_insert(tn, *act);
        } else {
                /* make sure the program being replaced is no longer executing */
                synchronize_rcu();
        return res;
 out:
        if (res == ACT_P_CREATED)
-               tcf_hash_cleanup(act, est);
+               tcf_hash_cleanup(*act, est);
 
        return ret;
 }
 {
        struct tcf_bpf_cfg tmp;
 
-       tcf_bpf_prog_fill_cfg(act->priv, &tmp);
+       tcf_bpf_prog_fill_cfg(to_bpf(act), &tmp);
        tcf_bpf_cfg_cleanup(&tmp);
 }
 
 static int tcf_bpf_walker(struct net *net, struct sk_buff *skb,
                          struct netlink_callback *cb, int type,
-                         struct tc_action *a)
+                         const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, bpf_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_bpf_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_bpf_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, bpf_net_id);
 
        .init           =       tcf_bpf_init,
        .walk           =       tcf_bpf_walker,
        .lookup         =       tcf_bpf_search,
+       .size           =       sizeof(struct tcf_bpf),
 };
 
 static __net_init int bpf_init_net(struct net *net)
 
 #define CONNMARK_TAB_MASK     3
 
 static int connmark_net_id;
+static struct tc_action_ops act_connmark_ops;
 
 static int tcf_connmark(struct sk_buff *skb, const struct tc_action *a,
                        struct tcf_result *res)
        const struct nf_conntrack_tuple_hash *thash;
        struct nf_conntrack_tuple tuple;
        enum ip_conntrack_info ctinfo;
-       struct tcf_connmark_info *ca = a->priv;
+       struct tcf_connmark_info *ca = to_connmark(a);
        struct nf_conntrack_zone zone;
        struct nf_conn *c;
        int proto;
 };
 
 static int tcf_connmark_init(struct net *net, struct nlattr *nla,
-                            struct nlattr *est, struct tc_action *a,
+                            struct nlattr *est, struct tc_action **a,
                             int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, connmark_net_id);
 
        if (!tcf_hash_check(tn, parm->index, a, bind)) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*ci), bind, false);
+                                     &act_connmark_ops, bind, false);
                if (ret)
                        return ret;
 
-               ci = to_connmark(a);
+               ci = to_connmark(*a);
                ci->tcf_action = parm->action;
                ci->net = net;
                ci->zone = parm->zone;
 
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
                ret = ACT_P_CREATED;
        } else {
-               ci = to_connmark(a);
+               ci = to_connmark(*a);
                if (bind)
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
                /* replacing action and zone */
                                    int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_connmark_info *ci = a->priv;
+       struct tcf_connmark_info *ci = to_connmark(a);
 
        struct tc_connmark opt = {
                .index   = ci->tcf_index,
 
 static int tcf_connmark_walker(struct net *net, struct sk_buff *skb,
                               struct netlink_callback *cb, int type,
-                              struct tc_action *a)
+                              const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, connmark_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_connmark_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_connmark_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, connmark_net_id);
 
        .init           =       tcf_connmark_init,
        .walk           =       tcf_connmark_walker,
        .lookup         =       tcf_connmark_search,
+       .size           =       sizeof(struct tcf_connmark_info),
 };
 
 static __net_init int connmark_init_net(struct net *net)
 
 };
 
 static int csum_net_id;
+static struct tc_action_ops act_csum_ops;
 
 static int tcf_csum_init(struct net *net, struct nlattr *nla,
-                        struct nlattr *est, struct tc_action *a, int ovr,
+                        struct nlattr *est, struct tc_action **a, int ovr,
                         int bind)
 {
        struct tc_action_net *tn = net_generic(net, csum_net_id);
 
        if (!tcf_hash_check(tn, parm->index, a, bind)) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*p), bind, false);
+                                     &act_csum_ops, bind, false);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        } else {
                if (bind)/* dont override defaults */
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
 
-       p = to_tcf_csum(a);
+       p = to_tcf_csum(*a);
        spin_lock_bh(&p->tcf_lock);
        p->tcf_action = parm->action;
        p->update_flags = parm->update_flags;
        spin_unlock_bh(&p->tcf_lock);
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
 
        return ret;
 }
 static int tcf_csum(struct sk_buff *skb,
                    const struct tc_action *a, struct tcf_result *res)
 {
-       struct tcf_csum *p = a->priv;
+       struct tcf_csum *p = to_tcf_csum(a);
        int action;
        u32 update_flags;
 
                         struct tc_action *a, int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_csum *p = a->priv;
+       struct tcf_csum *p = to_tcf_csum(a);
        struct tc_csum opt = {
                .update_flags = p->update_flags,
                .index   = p->tcf_index,
 
 static int tcf_csum_walker(struct net *net, struct sk_buff *skb,
                           struct netlink_callback *cb, int type,
-                          struct tc_action *a)
+                          const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, csum_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_csum_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_csum_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, csum_net_id);
 
        .init           = tcf_csum_init,
        .walk           = tcf_csum_walker,
        .lookup         = tcf_csum_search,
+       .size           = sizeof(struct tcf_csum),
 };
 
 static __net_init int csum_init_net(struct net *net)
 
 #define GACT_TAB_MASK  15
 
 static int gact_net_id;
+static struct tc_action_ops act_gact_ops;
 
 #ifdef CONFIG_GACT_PROB
 static int gact_net_rand(struct tcf_gact *gact)
 };
 
 static int tcf_gact_init(struct net *net, struct nlattr *nla,
-                        struct nlattr *est, struct tc_action *a,
+                        struct nlattr *est, struct tc_action **a,
                         int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, gact_net_id);
 
        if (!tcf_hash_check(tn, parm->index, a, bind)) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*gact), bind, true);
+                                     &act_gact_ops, bind, true);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        } else {
                if (bind)/* dont override defaults */
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
 
-       gact = to_gact(a);
+       gact = to_gact(*a);
 
        ASSERT_RTNL();
        gact->tcf_action = parm->action;
        }
 #endif
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 }
 
 static int tcf_gact(struct sk_buff *skb, const struct tc_action *a,
                    struct tcf_result *res)
 {
-       struct tcf_gact *gact = a->priv;
+       struct tcf_gact *gact = to_gact(a);
        int action = READ_ONCE(gact->tcf_action);
 
 #ifdef CONFIG_GACT_PROB
 static void tcf_gact_stats_update(struct tc_action *a, u64 bytes, u32 packets,
                                  u64 lastuse)
 {
-       struct tcf_gact *gact = a->priv;
+       struct tcf_gact *gact = to_gact(a);
        int action = READ_ONCE(gact->tcf_action);
        struct tcf_t *tm = &gact->tcf_tm;
 
                         int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_gact *gact = a->priv;
+       struct tcf_gact *gact = to_gact(a);
        struct tc_gact opt = {
                .index   = gact->tcf_index,
                .refcnt  = gact->tcf_refcnt - ref,
 
 static int tcf_gact_walker(struct net *net, struct sk_buff *skb,
                           struct netlink_callback *cb, int type,
-                          struct tc_action *a)
+                          const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, gact_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_gact_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_gact_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, gact_net_id);
 
        .init           =       tcf_gact_init,
        .walk           =       tcf_gact_walker,
        .lookup         =       tcf_gact_search,
+       .size           =       sizeof(struct tcf_gact),
 };
 
 static __net_init int gact_init_net(struct net *net)
 
 
 static int ife_net_id;
 static int max_metacnt = IFE_META_MAX + 1;
+static struct tc_action_ops act_ife_ops;
 
 static const struct nla_policy ife_policy[TCA_IFE_MAX + 1] = {
        [TCA_IFE_PARMS] = { .len = sizeof(struct tc_ife)},
 /* under ife->tcf_lock */
 static void _tcf_ife_cleanup(struct tc_action *a, int bind)
 {
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
        struct tcf_meta_info *e, *n;
 
        list_for_each_entry_safe(e, n, &ife->metalist, metalist) {
 
 static void tcf_ife_cleanup(struct tc_action *a, int bind)
 {
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
 
        spin_lock_bh(&ife->tcf_lock);
        _tcf_ife_cleanup(a, bind);
 }
 
 static int tcf_ife_init(struct net *net, struct nlattr *nla,
-                       struct nlattr *est, struct tc_action *a,
+                       struct nlattr *est, struct tc_action **a,
                        int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, ife_net_id);
                **/
                if (!tb[TCA_IFE_TYPE]) {
                        if (exists)
-                               tcf_hash_release(a, bind);
+                               tcf_hash_release(*a, bind);
                        pr_info("You MUST pass etherype for encoding\n");
                        return -EINVAL;
                }
        }
 
        if (!exists) {
-               ret = tcf_hash_create(tn, parm->index, est, a, sizeof(*ife),
+               ret = tcf_hash_create(tn, parm->index, est, a, &act_ife_ops,
                                      bind, false);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        } else {
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
 
-       ife = to_ife(a);
+       ife = to_ife(*a);
        ife->flags = parm->flags;
 
        if (parm->flags & IFE_ENCODE) {
                if (err) {
 metadata_parse_err:
                        if (exists)
-                               tcf_hash_release(a, bind);
+                               tcf_hash_release(*a, bind);
                        if (ret == ACT_P_CREATED)
-                               _tcf_ife_cleanup(a, bind);
+                               _tcf_ife_cleanup(*a, bind);
 
                        if (exists)
                                spin_unlock_bh(&ife->tcf_lock);
                err = use_all_metadata(ife);
                if (err) {
                        if (ret == ACT_P_CREATED)
-                               _tcf_ife_cleanup(a, bind);
+                               _tcf_ife_cleanup(*a, bind);
 
                        if (exists)
                                spin_unlock_bh(&ife->tcf_lock);
                spin_unlock_bh(&ife->tcf_lock);
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
 
        return ret;
 }
                        int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
        struct tc_ife opt = {
                .index = ife->tcf_index,
                .refcnt = ife->tcf_refcnt - ref,
 static int tcf_ife_decode(struct sk_buff *skb, const struct tc_action *a,
                          struct tcf_result *res)
 {
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
        int action = ife->tcf_action;
        struct ifeheadr *ifehdr = (struct ifeheadr *)skb->data;
        u16 ifehdrln = ifehdr->metalen;
 static int tcf_ife_encode(struct sk_buff *skb, const struct tc_action *a,
                          struct tcf_result *res)
 {
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
        int action = ife->tcf_action;
        struct ethhdr *oethh;   /* outer ether header */
        struct ethhdr *iethh;   /* inner eth header */
 static int tcf_ife_act(struct sk_buff *skb, const struct tc_action *a,
                       struct tcf_result *res)
 {
-       struct tcf_ife_info *ife = a->priv;
+       struct tcf_ife_info *ife = to_ife(a);
 
        if (ife->flags & IFE_ENCODE)
                return tcf_ife_encode(skb, a, res);
 
 static int tcf_ife_walker(struct net *net, struct sk_buff *skb,
                          struct netlink_callback *cb, int type,
-                         struct tc_action *a)
+                         const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, ife_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_ife_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_ife_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, ife_net_id);
 
        .init = tcf_ife_init,
        .walk = tcf_ife_walker,
        .lookup = tcf_ife_search,
+       .size = sizeof(struct tcf_ife_info),
 };
 
 static __net_init int ife_init_net(struct net *net)
 
 #define IPT_TAB_MASK     15
 
 static int ipt_net_id;
+static struct tc_action_ops act_ipt_ops;
 
 static int xt_net_id;
+static struct tc_action_ops act_xt_ops;
 
 static int ipt_init_target(struct xt_entry_target *t, char *table,
                           unsigned int hook)
 };
 
 static int __tcf_ipt_init(struct tc_action_net *tn, struct nlattr *nla,
-                         struct nlattr *est, struct tc_action *a, int ovr,
-                         int bind)
+                         struct nlattr *est, struct tc_action **a,
+                         const struct tc_action_ops *ops, int ovr, int bind)
 {
        struct nlattr *tb[TCA_IPT_MAX + 1];
        struct tcf_ipt *ipt;
 
        if (tb[TCA_IPT_HOOK] == NULL || tb[TCA_IPT_TARG] == NULL) {
                if (exists)
-                       tcf_hash_release(a, bind);
+                       tcf_hash_release(*a, bind);
                return -EINVAL;
        }
 
        td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]);
        if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) {
                if (exists)
-                       tcf_hash_release(a, bind);
+                       tcf_hash_release(*a, bind);
                return -EINVAL;
        }
 
        if (!exists) {
-               ret = tcf_hash_create(tn, index, est, a, sizeof(*ipt), bind,
+               ret = tcf_hash_create(tn, index, est, a, ops, bind,
                                      false);
                if (ret)
                        return ret;
        } else {
                if (bind)/* dont override defaults */
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
 
                if (!ovr)
                        return -EEXIST;
        }
-       ipt = to_ipt(a);
-
        hook = nla_get_u32(tb[TCA_IPT_HOOK]);
 
        err = -ENOMEM;
        if (err < 0)
                goto err3;
 
+       ipt = to_ipt(*a);
+
        spin_lock_bh(&ipt->tcf_lock);
        if (ret != ACT_P_CREATED) {
                ipt_destroy_target(ipt->tcfi_t);
        ipt->tcfi_hook  = hook;
        spin_unlock_bh(&ipt->tcf_lock);
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 
 err3:
        kfree(tname);
 err1:
        if (ret == ACT_P_CREATED)
-               tcf_hash_cleanup(a, est);
+               tcf_hash_cleanup(*a, est);
        return err;
 }
 
 static int tcf_ipt_init(struct net *net, struct nlattr *nla,
-                       struct nlattr *est, struct tc_action *a, int ovr,
+                       struct nlattr *est, struct tc_action **a, int ovr,
                        int bind)
 {
        struct tc_action_net *tn = net_generic(net, ipt_net_id);
 
-       return __tcf_ipt_init(tn, nla, est, a, ovr, bind);
+       return __tcf_ipt_init(tn, nla, est, a, &act_ipt_ops, ovr, bind);
 }
 
 static int tcf_xt_init(struct net *net, struct nlattr *nla,
-                      struct nlattr *est, struct tc_action *a, int ovr,
+                      struct nlattr *est, struct tc_action **a, int ovr,
                       int bind)
 {
        struct tc_action_net *tn = net_generic(net, xt_net_id);
 
-       return __tcf_ipt_init(tn, nla, est, a, ovr, bind);
+       return __tcf_ipt_init(tn, nla, est, a, &act_xt_ops, ovr, bind);
 }
 
 static int tcf_ipt(struct sk_buff *skb, const struct tc_action *a,
                   struct tcf_result *res)
 {
        int ret = 0, result = 0;
-       struct tcf_ipt *ipt = a->priv;
+       struct tcf_ipt *ipt = to_ipt(a);
        struct xt_action_param par;
 
        if (skb_unclone(skb, GFP_ATOMIC))
                        int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_ipt *ipt = a->priv;
+       struct tcf_ipt *ipt = to_ipt(a);
        struct xt_entry_target *t;
        struct tcf_t tm;
        struct tc_cnt c;
 
 static int tcf_ipt_walker(struct net *net, struct sk_buff *skb,
                          struct netlink_callback *cb, int type,
-                         struct tc_action *a)
+                         const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, ipt_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_ipt_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_ipt_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, ipt_net_id);
 
        .init           =       tcf_ipt_init,
        .walk           =       tcf_ipt_walker,
        .lookup         =       tcf_ipt_search,
+       .size           =       sizeof(struct tcf_ipt),
 };
 
 static __net_init int ipt_init_net(struct net *net)
 
 static int tcf_xt_walker(struct net *net, struct sk_buff *skb,
                         struct netlink_callback *cb, int type,
-                        struct tc_action *a)
+                        const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, xt_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_xt_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_xt_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, xt_net_id);
 
        .init           =       tcf_xt_init,
        .walk           =       tcf_xt_walker,
        .lookup         =       tcf_xt_search,
+       .size           =       sizeof(struct tcf_ipt),
 };
 
 static __net_init int xt_init_net(struct net *net)
 
 };
 
 static int mirred_net_id;
+static struct tc_action_ops act_mirred_ops;
 
 static int tcf_mirred_init(struct net *net, struct nlattr *nla,
-                          struct nlattr *est, struct tc_action *a, int ovr,
+                          struct nlattr *est, struct tc_action **a, int ovr,
                           int bind)
 {
        struct tc_action_net *tn = net_generic(net, mirred_net_id);
                break;
        default:
                if (exists)
-                       tcf_hash_release(a, bind);
+                       tcf_hash_release(*a, bind);
                return -EINVAL;
        }
        if (parm->ifindex) {
                dev = __dev_get_by_index(net, parm->ifindex);
                if (dev == NULL) {
                        if (exists)
-                               tcf_hash_release(a, bind);
+                               tcf_hash_release(*a, bind);
                        return -ENODEV;
                }
                switch (dev->type) {
                if (dev == NULL)
                        return -EINVAL;
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*m), bind, true);
+                                     &act_mirred_ops, bind, true);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        } else {
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
-       m = to_mirred(a);
+       m = to_mirred(*a);
 
        ASSERT_RTNL();
        m->tcf_action = parm->action;
                spin_lock_bh(&mirred_list_lock);
                list_add(&m->tcfm_list, &mirred_list);
                spin_unlock_bh(&mirred_list_lock);
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        }
 
        return ret;
 static int tcf_mirred(struct sk_buff *skb, const struct tc_action *a,
                      struct tcf_result *res)
 {
-       struct tcf_mirred *m = a->priv;
+       struct tcf_mirred *m = to_mirred(a);
        struct net_device *dev;
        struct sk_buff *skb2;
        int retval, err;
 static int tcf_mirred_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_mirred *m = a->priv;
+       struct tcf_mirred *m = to_mirred(a);
        struct tc_mirred opt = {
                .index   = m->tcf_index,
                .action  = m->tcf_action,
 
 static int tcf_mirred_walker(struct net *net, struct sk_buff *skb,
                             struct netlink_callback *cb, int type,
-                            struct tc_action *a)
+                            const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, mirred_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_mirred_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, mirred_net_id);
 
        .init           =       tcf_mirred_init,
        .walk           =       tcf_mirred_walker,
        .lookup         =       tcf_mirred_search,
+       .size           =       sizeof(struct tcf_mirred),
 };
 
 static __net_init int mirred_init_net(struct net *net)
 
 #define NAT_TAB_MASK   15
 
 static int nat_net_id;
+static struct tc_action_ops act_nat_ops;
 
 static const struct nla_policy nat_policy[TCA_NAT_MAX + 1] = {
        [TCA_NAT_PARMS] = { .len = sizeof(struct tc_nat) },
 };
 
 static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est,
-                       struct tc_action *a, int ovr, int bind)
+                       struct tc_action **a, int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, nat_net_id);
        struct nlattr *tb[TCA_NAT_MAX + 1];
 
        if (!tcf_hash_check(tn, parm->index, a, bind)) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*p), bind, false);
+                                     &act_nat_ops, bind, false);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        } else {
                if (bind)
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
-       p = to_tcf_nat(a);
+       p = to_tcf_nat(*a);
 
        spin_lock_bh(&p->tcf_lock);
        p->old_addr = parm->old_addr;
        spin_unlock_bh(&p->tcf_lock);
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
 
        return ret;
 }
 static int tcf_nat(struct sk_buff *skb, const struct tc_action *a,
                   struct tcf_result *res)
 {
-       struct tcf_nat *p = a->priv;
+       struct tcf_nat *p = to_tcf_nat(a);
        struct iphdr *iph;
        __be32 old_addr;
        __be32 new_addr;
                        int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_nat *p = a->priv;
+       struct tcf_nat *p = to_tcf_nat(a);
        struct tc_nat opt = {
                .old_addr = p->old_addr,
                .new_addr = p->new_addr,
 
 static int tcf_nat_walker(struct net *net, struct sk_buff *skb,
                          struct netlink_callback *cb, int type,
-                         struct tc_action *a)
+                         const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, nat_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_nat_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, nat_net_id);
 
        .init           =       tcf_nat_init,
        .walk           =       tcf_nat_walker,
        .lookup         =       tcf_nat_search,
+       .size           =       sizeof(struct tcf_nat),
 };
 
 static __net_init int nat_init_net(struct net *net)
 
 #define PEDIT_TAB_MASK 15
 
 static int pedit_net_id;
+static struct tc_action_ops act_pedit_ops;
 
 static const struct nla_policy pedit_policy[TCA_PEDIT_MAX + 1] = {
        [TCA_PEDIT_PARMS]       = { .len = sizeof(struct tc_pedit) },
 };
 
 static int tcf_pedit_init(struct net *net, struct nlattr *nla,
-                         struct nlattr *est, struct tc_action *a,
+                         struct nlattr *est, struct tc_action **a,
                          int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, pedit_net_id);
                if (!parm->nkeys)
                        return -EINVAL;
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*p), bind, false);
+                                     &act_pedit_ops, bind, false);
                if (ret)
                        return ret;
-               p = to_pedit(a);
+               p = to_pedit(*a);
                keys = kmalloc(ksize, GFP_KERNEL);
                if (keys == NULL) {
-                       tcf_hash_cleanup(a, est);
+                       tcf_hash_cleanup(*a, est);
                        return -ENOMEM;
                }
                ret = ACT_P_CREATED;
        } else {
                if (bind)
                        return 0;
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
-               p = to_pedit(a);
+               p = to_pedit(*a);
                if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) {
                        keys = kmalloc(ksize, GFP_KERNEL);
                        if (keys == NULL)
        memcpy(p->tcfp_keys, parm->keys, ksize);
        spin_unlock_bh(&p->tcf_lock);
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 }
 
 static void tcf_pedit_cleanup(struct tc_action *a, int bind)
 {
-       struct tcf_pedit *p = a->priv;
+       struct tcf_pedit *p = to_pedit(a);
        struct tc_pedit_key *keys = p->tcfp_keys;
        kfree(keys);
 }
 static int tcf_pedit(struct sk_buff *skb, const struct tc_action *a,
                     struct tcf_result *res)
 {
-       struct tcf_pedit *p = a->priv;
+       struct tcf_pedit *p = to_pedit(a);
        int i;
        unsigned int off;
 
                          int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_pedit *p = a->priv;
+       struct tcf_pedit *p = to_pedit(a);
        struct tc_pedit *opt;
        struct tcf_t t;
        int s;
 
 static int tcf_pedit_walker(struct net *net, struct sk_buff *skb,
                            struct netlink_callback *cb, int type,
-                           struct tc_action *a)
+                           const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, pedit_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_pedit_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, pedit_net_id);
 
        .init           =       tcf_pedit_init,
        .walk           =       tcf_pedit_walker,
        .lookup         =       tcf_pedit_search,
+       .size           =       sizeof(struct tcf_pedit),
 };
 
 static __net_init int pedit_init_net(struct net *net)
 
        struct psched_ratecfg   peak;
        bool                    peak_present;
 };
-#define to_police(pc)  \
-       container_of(pc->priv, struct tcf_police, common)
+
+#define to_police(pc) ((struct tcf_police *)pc)
 
 #define POL_TAB_MASK     15
 
 /* Each policer is serialized by its individual spinlock */
 
 static int police_net_id;
+static struct tc_action_ops act_police_ops;
 
 static int tcf_act_police_walker(struct net *net, struct sk_buff *skb,
                                 struct netlink_callback *cb, int type,
-                                struct tc_action *a)
+                                const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, police_net_id);
        struct tcf_hashinfo *hinfo = tn->hinfo;
-       struct hlist_head *head;
-       struct tcf_common *p;
        int err = 0, index = -1, i = 0, s_i = 0, n_i = 0;
        struct nlattr *nest;
 
        s_i = cb->args[0];
 
        for (i = 0; i < (POL_TAB_MASK + 1); i++) {
+               struct hlist_head *head;
+               struct tcf_common *p;
+
                head = &hinfo->htab[tcf_hash(i, POL_TAB_MASK)];
 
                hlist_for_each_entry_rcu(p, head, tcfc_head) {
                        index++;
                        if (index < s_i)
                                continue;
-                       a->priv = p;
-                       a->order = index;
-                       nest = nla_nest_start(skb, a->order);
+                       nest = nla_nest_start(skb, index);
                        if (nest == NULL)
                                goto nla_put_failure;
                        if (type == RTM_DELACTION)
-                               err = tcf_action_dump_1(skb, a, 0, 1);
+                               err = tcf_action_dump_1(skb, (struct tc_action *)p, 0, 1);
                        else
-                               err = tcf_action_dump_1(skb, a, 0, 0);
+                               err = tcf_action_dump_1(skb, (struct tc_action *)p, 0, 0);
                        if (err < 0) {
                                index--;
                                nla_nest_cancel(skb, nest);
 };
 
 static int tcf_act_police_init(struct net *net, struct nlattr *nla,
-                              struct nlattr *est, struct tc_action *a,
+                              struct nlattr *est, struct tc_action **a,
                               int ovr, int bind)
 {
        int ret = 0, err;
        parm = nla_data(tb[TCA_POLICE_TBF]);
 
        if (parm->index) {
-               if (tcf_hash_search(tn, a, parm->index)) {
-                       police = to_police(a);
-                       if (bind) {
-                               police->tcf_bindcnt += 1;
-                               police->tcf_refcnt += 1;
-                               return 0;
-                       }
+               if (tcf_hash_check(tn, parm->index, a, bind)) {
                        if (ovr)
                                goto override;
                        /* not replacing */
                }
        } else {
                ret = tcf_hash_create(tn, parm->index, NULL, a,
-                                     sizeof(*police), bind, false);
+                                     &act_police_ops, bind, false);
                if (ret)
                        return ret;
                ret = ACT_P_CREATED;
        }
 
-       police = to_police(a);
 override:
+       police = to_police(*a);
        if (parm->rate.rate) {
                err = -ENOMEM;
                R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE]);
                return ret;
 
        police->tcfp_t_c = ktime_get_ns();
-       tcf_hash_insert(tn, a);
+       tcf_hash_insert(tn, *a);
 
        return ret;
 
        qdisc_put_rtab(P_tab);
        qdisc_put_rtab(R_tab);
        if (ret == ACT_P_CREATED)
-               tcf_hash_cleanup(a, est);
+               tcf_hash_cleanup(*a, est);
        return err;
 }
 
 static int tcf_act_police(struct sk_buff *skb, const struct tc_action *a,
                          struct tcf_result *res)
 {
-       struct tcf_police *police = a->priv;
+       struct tcf_police *police = to_police(a);
        s64 now;
        s64 toks;
        s64 ptoks = 0;
 tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_police *police = a->priv;
+       struct tcf_police *police = to_police(a);
        struct tc_police opt = {
                .index = police->tcf_index,
                .action = police->tcf_action,
        return -1;
 }
 
-static int tcf_police_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_police_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, police_net_id);
 
        .init           =       tcf_act_police_init,
        .walk           =       tcf_act_police_walker,
        .lookup         =       tcf_police_search,
+       .size           =       sizeof(struct tcf_police),
 };
 
 static __net_init int police_init_net(struct net *net)
 
 #define SIMP_TAB_MASK     7
 
 static int simp_net_id;
+static struct tc_action_ops act_simp_ops;
 
 #define SIMP_MAX_DATA  32
 static int tcf_simp(struct sk_buff *skb, const struct tc_action *a,
                    struct tcf_result *res)
 {
-       struct tcf_defact *d = a->priv;
+       struct tcf_defact *d = to_defact(a);
 
        spin_lock(&d->tcf_lock);
        tcf_lastuse_update(&d->tcf_tm);
 };
 
 static int tcf_simp_init(struct net *net, struct nlattr *nla,
-                        struct nlattr *est, struct tc_action *a,
+                        struct nlattr *est, struct tc_action **a,
                         int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, simp_net_id);
        if (tb[TCA_DEF_PARMS] == NULL)
                return -EINVAL;
 
-
        parm = nla_data(tb[TCA_DEF_PARMS]);
        exists = tcf_hash_check(tn, parm->index, a, bind);
        if (exists && bind)
 
        if (tb[TCA_DEF_DATA] == NULL) {
                if (exists)
-                       tcf_hash_release(a, bind);
+                       tcf_hash_release(*a, bind);
                return -EINVAL;
        }
 
 
        if (!exists) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*d), bind, false);
+                                     &act_simp_ops, bind, false);
                if (ret)
                        return ret;
 
-               d = to_defact(a);
+               d = to_defact(*a);
                ret = alloc_defdata(d, defdata);
                if (ret < 0) {
-                       tcf_hash_cleanup(a, est);
+                       tcf_hash_cleanup(*a, est);
                        return ret;
                }
                d->tcf_action = parm->action;
                ret = ACT_P_CREATED;
        } else {
-               d = to_defact(a);
+               d = to_defact(*a);
 
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
 
        }
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 }
 
                         int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_defact *d = a->priv;
+       struct tcf_defact *d = to_defact(a);
        struct tc_defact opt = {
                .index   = d->tcf_index,
                .refcnt  = d->tcf_refcnt - ref,
 
 static int tcf_simp_walker(struct net *net, struct sk_buff *skb,
                           struct netlink_callback *cb, int type,
-                          struct tc_action *a)
+                          const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, simp_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_simp_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_simp_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, simp_net_id);
 
        .init           =       tcf_simp_init,
        .walk           =       tcf_simp_walker,
        .lookup         =       tcf_simp_search,
+       .size           =       sizeof(struct tcf_defact),
 };
 
 static __net_init int simp_init_net(struct net *net)
 
 #define SKBEDIT_TAB_MASK     15
 
 static int skbedit_net_id;
+static struct tc_action_ops act_skbedit_ops;
 
 static int tcf_skbedit(struct sk_buff *skb, const struct tc_action *a,
                       struct tcf_result *res)
 {
-       struct tcf_skbedit *d = a->priv;
+       struct tcf_skbedit *d = to_skbedit(a);
 
        spin_lock(&d->tcf_lock);
        tcf_lastuse_update(&d->tcf_tm);
 };
 
 static int tcf_skbedit_init(struct net *net, struct nlattr *nla,
-                           struct nlattr *est, struct tc_action *a,
+                           struct nlattr *est, struct tc_action **a,
                            int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, skbedit_net_id);
                return 0;
 
        if (!flags) {
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                return -EINVAL;
        }
 
        if (!exists) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*d), bind, false);
+                                     &act_skbedit_ops, bind, false);
                if (ret)
                        return ret;
 
-               d = to_skbedit(a);
+               d = to_skbedit(*a);
                ret = ACT_P_CREATED;
        } else {
-               d = to_skbedit(a);
-               tcf_hash_release(a, bind);
+               d = to_skbedit(*a);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
        spin_unlock_bh(&d->tcf_lock);
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 }
 
                            int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_skbedit *d = a->priv;
+       struct tcf_skbedit *d = to_skbedit(a);
        struct tc_skbedit opt = {
                .index   = d->tcf_index,
                .refcnt  = d->tcf_refcnt - ref,
 
 static int tcf_skbedit_walker(struct net *net, struct sk_buff *skb,
                              struct netlink_callback *cb, int type,
-                             struct tc_action *a)
+                             const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, skbedit_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_skbedit_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, skbedit_net_id);
 
        .init           =       tcf_skbedit_init,
        .walk           =       tcf_skbedit_walker,
        .lookup         =       tcf_skbedit_search,
+       .size           =       sizeof(struct tcf_skbedit),
 };
 
 static __net_init int skbedit_init_net(struct net *net)
 
 #define VLAN_TAB_MASK     15
 
 static int vlan_net_id;
+static struct tc_action_ops act_vlan_ops;
 
 static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a,
                    struct tcf_result *res)
 {
-       struct tcf_vlan *v = a->priv;
+       struct tcf_vlan *v = to_vlan(a);
        int action;
        int err;
 
 };
 
 static int tcf_vlan_init(struct net *net, struct nlattr *nla,
-                        struct nlattr *est, struct tc_action *a,
+                        struct nlattr *est, struct tc_action **a,
                         int ovr, int bind)
 {
        struct tc_action_net *tn = net_generic(net, vlan_net_id);
        case TCA_VLAN_ACT_PUSH:
                if (!tb[TCA_VLAN_PUSH_VLAN_ID]) {
                        if (exists)
-                               tcf_hash_release(a, bind);
+                               tcf_hash_release(*a, bind);
                        return -EINVAL;
                }
                push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]);
                if (push_vid >= VLAN_VID_MASK) {
                        if (exists)
-                               tcf_hash_release(a, bind);
+                               tcf_hash_release(*a, bind);
                        return -ERANGE;
                }
 
                break;
        default:
                if (exists)
-                       tcf_hash_release(a, bind);
+                       tcf_hash_release(*a, bind);
                return -EINVAL;
        }
        action = parm->v_action;
 
        if (!exists) {
                ret = tcf_hash_create(tn, parm->index, est, a,
-                                     sizeof(*v), bind, false);
+                                     &act_vlan_ops, bind, false);
                if (ret)
                        return ret;
 
                ret = ACT_P_CREATED;
        } else {
-               tcf_hash_release(a, bind);
+               tcf_hash_release(*a, bind);
                if (!ovr)
                        return -EEXIST;
        }
 
-       v = to_vlan(a);
+       v = to_vlan(*a);
 
        spin_lock_bh(&v->tcf_lock);
 
        spin_unlock_bh(&v->tcf_lock);
 
        if (ret == ACT_P_CREATED)
-               tcf_hash_insert(tn, a);
+               tcf_hash_insert(tn, *a);
        return ret;
 }
 
                         int bind, int ref)
 {
        unsigned char *b = skb_tail_pointer(skb);
-       struct tcf_vlan *v = a->priv;
+       struct tcf_vlan *v = to_vlan(a);
        struct tc_vlan opt = {
                .index    = v->tcf_index,
                .refcnt   = v->tcf_refcnt - ref,
 
 static int tcf_vlan_walker(struct net *net, struct sk_buff *skb,
                           struct netlink_callback *cb, int type,
-                          struct tc_action *a)
+                          const struct tc_action_ops *ops)
 {
        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 
-       return tcf_generic_walker(tn, skb, cb, type, a);
+       return tcf_generic_walker(tn, skb, cb, type, ops);
 }
 
-static int tcf_vlan_search(struct net *net, struct tc_action *a, u32 index)
+static int tcf_vlan_search(struct net *net, struct tc_action **a, u32 index)
 {
        struct tc_action_net *tn = net_generic(net, vlan_net_id);
 
        .init           =       tcf_vlan_init,
        .walk           =       tcf_vlan_walker,
        .lookup         =       tcf_vlan_search,
+       .size           =       sizeof(struct tcf_vlan),
 };
 
 static __net_init int vlan_init_net(struct net *net)