flow_offload: allow user to offload tc action to net device
authorBaowen Zheng <baowen.zheng@corigine.com>
Fri, 17 Dec 2021 18:16:22 +0000 (19:16 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 19 Dec 2021 14:08:48 +0000 (14:08 +0000)
Use flow_indr_dev_register/flow_indr_dev_setup_offload to
offload tc action.

We need to call tc_cleanup_flow_action to clean up tc action entry since
in tc_setup_action, some actions may hold dev refcnt, especially the mirror
action.

Signed-off-by: Baowen Zheng <baowen.zheng@corigine.com>
Signed-off-by: Louis Peens <louis.peens@corigine.com>
Signed-off-by: Simon Horman <simon.horman@corigine.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
17 files changed:
include/linux/netdevice.h
include/net/flow_offload.h
include/net/pkt_cls.h
net/core/flow_offload.c
net/sched/act_api.c
net/sched/act_csum.c
net/sched/act_ct.c
net/sched/act_gact.c
net/sched/act_gate.c
net/sched/act_mirred.c
net/sched/act_mpls.c
net/sched/act_police.c
net/sched/act_sample.c
net/sched/act_skbedit.c
net/sched/act_tunnel_key.c
net/sched/act_vlan.c
net/sched/cls_api.c

index a419718612c6f82530d67a4540cc86c4bf326f98..8b0bdeb4734e7050319900b3e32e7c54f930a9b4 100644 (file)
@@ -920,6 +920,7 @@ enum tc_setup_type {
        TC_SETUP_QDISC_TBF,
        TC_SETUP_QDISC_FIFO,
        TC_SETUP_QDISC_HTB,
+       TC_SETUP_ACT,
 };
 
 /* These structures hold the attributes of bpf state that are being passed
index 2271da5aa8ee398a6ee2523a88c5505cbe6d6c59..5b8c54eb7a6b8a6e35d7f14e41f49898b585b456 100644 (file)
@@ -551,6 +551,23 @@ struct flow_cls_offload {
        u32 classid;
 };
 
+enum offload_act_command  {
+       FLOW_ACT_REPLACE,
+       FLOW_ACT_DESTROY,
+       FLOW_ACT_STATS,
+};
+
+struct flow_offload_action {
+       struct netlink_ext_ack *extack; /* NULL in FLOW_ACT_STATS process*/
+       enum offload_act_command  command;
+       enum flow_action_id id;
+       u32 index;
+       struct flow_stats stats;
+       struct flow_action action;
+};
+
+struct flow_offload_action *offload_action_alloc(unsigned int num_actions);
+
 static inline struct flow_rule *
 flow_cls_offload_flow_rule(struct flow_cls_offload *flow_cmd)
 {
index 5d4ff76d37e28aa30f6ddb1af732cb51485c52be..1bfb616ea7596301717b9117b3551b30a7a7af2c 100644 (file)
@@ -262,6 +262,9 @@ static inline void tcf_exts_put_net(struct tcf_exts *exts)
        for (; 0; (void)(i), (void)(a), (void)(exts))
 #endif
 
+#define tcf_act_for_each_action(i, a, actions) \
+       for (i = 0; i < TCA_ACT_MAX_PRIO && ((a) = actions[i]); i++)
+
 static inline void
 tcf_exts_stats_update(const struct tcf_exts *exts,
                      u64 bytes, u64 packets, u64 drops, u64 lastuse,
@@ -539,6 +542,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
 int tc_setup_offload_action(struct flow_action *flow_action,
                            const struct tcf_exts *exts);
 void tc_cleanup_offload_action(struct flow_action *flow_action);
+int tc_setup_action(struct flow_action *flow_action,
+                   struct tc_action *actions[]);
 
 int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
                     void *type_data, bool err_stop, bool rtnl_held);
index 6beaea13564a8208f66004df24796c947065f08f..022c945817facbee6da415201b6b1ce6ff5d2aa7 100644 (file)
@@ -27,6 +27,26 @@ struct flow_rule *flow_rule_alloc(unsigned int num_actions)
 }
 EXPORT_SYMBOL(flow_rule_alloc);
 
+struct flow_offload_action *offload_action_alloc(unsigned int num_actions)
+{
+       struct flow_offload_action *fl_action;
+       int i;
+
+       fl_action = kzalloc(struct_size(fl_action, action.entries, num_actions),
+                           GFP_KERNEL);
+       if (!fl_action)
+               return NULL;
+
+       fl_action->action.num_entries = num_actions;
+       /* Pre-fill each action hw_stats with DONT_CARE.
+        * Caller can override this if it wants stats for a given action.
+        */
+       for (i = 0; i < num_actions; i++)
+               fl_action->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE;
+
+       return fl_action;
+}
+
 #define FLOW_DISSECTOR_MATCH(__rule, __type, __out)                            \
        const struct flow_match *__m = &(__rule)->match;                        \
        struct flow_dissector *__d = (__m)->dissector;                          \
@@ -549,19 +569,25 @@ int flow_indr_dev_setup_offload(struct net_device *dev,   struct Qdisc *sch,
                                void (*cleanup)(struct flow_block_cb *block_cb))
 {
        struct flow_indr_dev *this;
+       u32 count = 0;
+       int err;
 
        mutex_lock(&flow_indr_block_lock);
+       if (bo) {
+               if (bo->command == FLOW_BLOCK_BIND)
+                       indir_dev_add(data, dev, sch, type, cleanup, bo);
+               else if (bo->command == FLOW_BLOCK_UNBIND)
+                       indir_dev_remove(data);
+       }
 
-       if (bo->command == FLOW_BLOCK_BIND)
-               indir_dev_add(data, dev, sch, type, cleanup, bo);
-       else if (bo->command == FLOW_BLOCK_UNBIND)
-               indir_dev_remove(data);
-
-       list_for_each_entry(this, &flow_block_indr_dev_list, list)
-               this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);
+       list_for_each_entry(this, &flow_block_indr_dev_list, list) {
+               err = this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);
+               if (!err)
+                       count++;
+       }
 
        mutex_unlock(&flow_indr_block_lock);
 
-       return list_empty(&bo->cb_list) ? -EOPNOTSUPP : 0;
+       return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count;
 }
 EXPORT_SYMBOL(flow_indr_dev_setup_offload);
index 3258da3d5bed5127780eafe6027b9f75e1fa87e1..5c21401b0555bb5b1758253338cbd7a9a32f33c1 100644 (file)
 #include <net/sock.h>
 #include <net/sch_generic.h>
 #include <net/pkt_cls.h>
+#include <net/tc_act/tc_pedit.h>
 #include <net/act_api.h>
 #include <net/netlink.h>
+#include <net/flow_offload.h>
 
 #ifdef CONFIG_INET
 DEFINE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
@@ -129,8 +131,92 @@ static void free_tcf(struct tc_action *p)
        kfree(p);
 }
 
+static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act)
+{
+       if (is_tcf_pedit(act))
+               return tcf_pedit_nkeys(act);
+       else
+               return 1;
+}
+
+static int offload_action_init(struct flow_offload_action *fl_action,
+                              struct tc_action *act,
+                              enum offload_act_command  cmd,
+                              struct netlink_ext_ack *extack)
+{
+       fl_action->extack = extack;
+       fl_action->command = cmd;
+       fl_action->index = act->tcfa_index;
+
+       if (act->ops->offload_act_setup)
+               return act->ops->offload_act_setup(act, fl_action, NULL, false);
+
+       return -EOPNOTSUPP;
+}
+
+static int tcf_action_offload_cmd(struct flow_offload_action *fl_act,
+                                 struct netlink_ext_ack *extack)
+{
+       int err;
+
+       err = flow_indr_dev_setup_offload(NULL, NULL, TC_SETUP_ACT,
+                                         fl_act, NULL, NULL);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+/* offload the tc action after it is inserted */
+static int tcf_action_offload_add(struct tc_action *action,
+                                 struct netlink_ext_ack *extack)
+{
+       struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
+               [0] = action,
+       };
+       struct flow_offload_action *fl_action;
+       int num, err = 0;
+
+       num = tcf_offload_act_num_actions_single(action);
+       fl_action = offload_action_alloc(num);
+       if (!fl_action)
+               return -ENOMEM;
+
+       err = offload_action_init(fl_action, action, FLOW_ACT_REPLACE, extack);
+       if (err)
+               goto fl_err;
+
+       err = tc_setup_action(&fl_action->action, actions);
+       if (err) {
+               NL_SET_ERR_MSG_MOD(extack,
+                                  "Failed to setup tc actions for offload\n");
+               goto fl_err;
+       }
+
+       err = tcf_action_offload_cmd(fl_action, extack);
+       tc_cleanup_offload_action(&fl_action->action);
+
+fl_err:
+       kfree(fl_action);
+
+       return err;
+}
+
+static int tcf_action_offload_del(struct tc_action *action)
+{
+       struct flow_offload_action fl_act = {};
+       int err = 0;
+
+       err = offload_action_init(&fl_act, action, FLOW_ACT_DESTROY, NULL);
+       if (err)
+               return err;
+
+       return tcf_action_offload_cmd(&fl_act, NULL);
+}
+
 static void tcf_action_cleanup(struct tc_action *p)
 {
+       tcf_action_offload_del(p);
        if (p->ops->cleanup)
                p->ops->cleanup(p);
 
@@ -1061,6 +1147,11 @@ err_out:
        return ERR_PTR(err);
 }
 
+static bool tc_act_bind(u32 flags)
+{
+       return !!(flags & TCA_ACT_FLAGS_BIND);
+}
+
 /* Returns numbers of initialized actions or negative error. */
 
 int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
@@ -1103,6 +1194,8 @@ int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
                sz += tcf_action_fill_size(act);
                /* Start from index 0 */
                actions[i - 1] = act;
+               if (!tc_act_bind(flags))
+                       tcf_action_offload_add(act, extack);
        }
 
        /* We have to commit them all together, because if any error happened in
index 4428852a03d7812c58f1d40763567077e066740f..e0f515b774cad280f13c776a89e0fd0d818b912c 100644 (file)
@@ -705,7 +705,9 @@ static int tcf_csum_offload_act_setup(struct tc_action *act, void *entry_data,
                entry->csum_flags = tcf_csum_update_flags(act);
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               fl_action->id = FLOW_ACTION_CSUM;
        }
 
        return 0;
index dc64f31e5191f1e18718662cc4158bbd2604f734..1c537913a189a9dd69ad46901840fab58002ff20 100644 (file)
@@ -1505,7 +1505,9 @@ static int tcf_ct_offload_act_setup(struct tc_action *act, void *entry_data,
                entry->ct.flow_table = tcf_ct_ft(act);
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               fl_action->id = FLOW_ACTION_CT;
        }
 
        return 0;
index f77be22069f4f42f52b611d82ab1fcf21e63bd59..bde6a6c01e64cb92567bfcde1e3eb7241ee57594 100644 (file)
@@ -272,7 +272,18 @@ static int tcf_gact_offload_act_setup(struct tc_action *act, void *entry_data,
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               if (is_tcf_gact_ok(act))
+                       fl_action->id = FLOW_ACTION_ACCEPT;
+               else if (is_tcf_gact_shot(act))
+                       fl_action->id = FLOW_ACTION_DROP;
+               else if (is_tcf_gact_trap(act))
+                       fl_action->id = FLOW_ACTION_TRAP;
+               else if (is_tcf_gact_goto_chain(act))
+                       fl_action->id = FLOW_ACTION_GOTO;
+               else
+                       return -EOPNOTSUPP;
        }
 
        return 0;
index 1d8297497692fa319a944a1f0c1ad6ca8a7fbbff..d56e73843a4b7fe0bd9dfc0aa84960a59c2d1de2 100644 (file)
@@ -637,7 +637,9 @@ static int tcf_gate_offload_act_setup(struct tc_action *act, void *entry_data,
                        return err;
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               fl_action->id = FLOW_ACTION_GATE;
        }
 
        return 0;
index 8eecf55be0a250172727363c82e8a65171901e1d..39acd1d186098bbb958822f3c087276ee0ea84e4 100644 (file)
@@ -482,7 +482,18 @@ static int tcf_mirred_offload_act_setup(struct tc_action *act, void *entry_data,
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               if (is_tcf_mirred_egress_redirect(act))
+                       fl_action->id = FLOW_ACTION_REDIRECT;
+               else if (is_tcf_mirred_egress_mirror(act))
+                       fl_action->id = FLOW_ACTION_MIRRED;
+               else if (is_tcf_mirred_ingress_redirect(act))
+                       fl_action->id = FLOW_ACTION_REDIRECT_INGRESS;
+               else if (is_tcf_mirred_ingress_mirror(act))
+                       fl_action->id = FLOW_ACTION_MIRRED_INGRESS;
+               else
+                       return -EOPNOTSUPP;
        }
 
        return 0;
index a4615e1331e0283136351cfd2a7e9db41c87a81e..b9ff3459fdab97381f059d8c476e5c214c5d58a4 100644 (file)
@@ -415,7 +415,21 @@ static int tcf_mpls_offload_act_setup(struct tc_action *act, void *entry_data,
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               switch (tcf_mpls_action(act)) {
+               case TCA_MPLS_ACT_PUSH:
+                       fl_action->id = FLOW_ACTION_MPLS_PUSH;
+                       break;
+               case TCA_MPLS_ACT_POP:
+                       fl_action->id = FLOW_ACTION_MPLS_POP;
+                       break;
+               case TCA_MPLS_ACT_MODIFY:
+                       fl_action->id = FLOW_ACTION_MPLS_MANGLE;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
        }
 
        return 0;
index abb6d16a20b21b9e23e3b4e1eba07ed1e650961f..0923aa2b8f8a7137f3c15083ced5b3d0640910e1 100644 (file)
@@ -421,7 +421,9 @@ static int tcf_police_offload_act_setup(struct tc_action *act, void *entry_data,
                entry->police.mtu = tcf_police_tcfp_mtu(act);
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               fl_action->id = FLOW_ACTION_POLICE;
        }
 
        return 0;
index 07e56903211e7e827d27066b76b523b5dea94001..9a22cdda6bbdcd38abc0d0c4d5365fad2f4088dd 100644 (file)
@@ -303,7 +303,9 @@ static int tcf_sample_offload_act_setup(struct tc_action *act, void *entry_data,
                tcf_offload_sample_get_group(entry, act);
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               fl_action->id = FLOW_ACTION_SAMPLE;
        }
 
        return 0;
index c380f9e6cc95e263aa7072348fa2b2159d52af2f..ceba11b198bbab11f9d648d155f618a664c16923 100644 (file)
@@ -347,7 +347,16 @@ static int tcf_skbedit_offload_act_setup(struct tc_action *act, void *entry_data
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               if (is_tcf_skbedit_mark(act))
+                       fl_action->id = FLOW_ACTION_MARK;
+               else if (is_tcf_skbedit_ptype(act))
+                       fl_action->id = FLOW_ACTION_PTYPE;
+               else if (is_tcf_skbedit_priority(act))
+                       fl_action->id = FLOW_ACTION_PRIORITY;
+               else
+                       return -EOPNOTSUPP;
        }
 
        return 0;
index e96a65a5323ec5f4751617d1376c6719212d8e33..23aba03d26a8d1a0e3e393bc78df3e71a18c27d1 100644 (file)
@@ -827,7 +827,14 @@ static int tcf_tunnel_key_offload_act_setup(struct tc_action *act,
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               if (is_tcf_tunnel_set(act))
+                       fl_action->id = FLOW_ACTION_TUNNEL_ENCAP;
+               else if (is_tcf_tunnel_release(act))
+                       fl_action->id = FLOW_ACTION_TUNNEL_DECAP;
+               else
+                       return -EOPNOTSUPP;
        }
 
        return 0;
index 0300792084f094650cceaf7cde94f071683af211..756e2dcde1cd8a4371e71cbbf49e79a30460dc5d 100644 (file)
@@ -395,7 +395,21 @@ static int tcf_vlan_offload_act_setup(struct tc_action *act, void *entry_data,
                }
                *index_inc = 1;
        } else {
-               return -EOPNOTSUPP;
+               struct flow_offload_action *fl_action = entry_data;
+
+               switch (tcf_vlan_action(act)) {
+               case TCA_VLAN_ACT_PUSH:
+                       fl_action->id = FLOW_ACTION_VLAN_PUSH;
+                       break;
+               case TCA_VLAN_ACT_POP:
+                       fl_action->id = FLOW_ACTION_VLAN_POP;
+                       break;
+               case TCA_VLAN_ACT_MODIFY:
+                       fl_action->id = FLOW_ACTION_VLAN_MANGLE;
+                       break;
+               default:
+                       return -EOPNOTSUPP;
+               }
        }
 
        return 0;
index 53f263c9a7259c04bf011489f16cd0ef3f22bba1..353e1eed48be89c083342babab81d5a0451c733d 100644 (file)
@@ -3488,8 +3488,8 @@ static int tc_setup_offload_act(struct tc_action *act,
 #endif
 }
 
-int tc_setup_offload_action(struct flow_action *flow_action,
-                           const struct tcf_exts *exts)
+int tc_setup_action(struct flow_action *flow_action,
+                   struct tc_action *actions[])
 {
        int i, j, index, err = 0;
        struct tc_action *act;
@@ -3498,11 +3498,11 @@ int tc_setup_offload_action(struct flow_action *flow_action,
        BUILD_BUG_ON(TCA_ACT_HW_STATS_IMMEDIATE != FLOW_ACTION_HW_STATS_IMMEDIATE);
        BUILD_BUG_ON(TCA_ACT_HW_STATS_DELAYED != FLOW_ACTION_HW_STATS_DELAYED);
 
-       if (!exts)
+       if (!actions)
                return 0;
 
        j = 0;
-       tcf_exts_for_each_action(i, act, exts) {
+       tcf_act_for_each_action(i, act, actions) {
                struct flow_action_entry *entry;
 
                entry = &flow_action->entries[j];
@@ -3531,6 +3531,19 @@ err_out_locked:
        spin_unlock_bh(&act->tcfa_lock);
        goto err_out;
 }
+
+int tc_setup_offload_action(struct flow_action *flow_action,
+                           const struct tcf_exts *exts)
+{
+#ifdef CONFIG_NET_CLS_ACT
+       if (!exts)
+               return 0;
+
+       return tc_setup_action(flow_action, exts->actions);
+#else
+       return 0;
+#endif
+}
 EXPORT_SYMBOL(tc_setup_offload_action);
 
 unsigned int tcf_exts_num_actions(struct tcf_exts *exts)