12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * net/sched/act_api.c Packet action API.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Author: Jamal Hadi Salim
61da177e4SLinus Torvalds */
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds #include <linux/types.h>
91da177e4SLinus Torvalds #include <linux/kernel.h>
101da177e4SLinus Torvalds #include <linux/string.h>
111da177e4SLinus Torvalds #include <linux/errno.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
131da177e4SLinus Torvalds #include <linux/skbuff.h>
141da177e4SLinus Torvalds #include <linux/init.h>
151da177e4SLinus Torvalds #include <linux/kmod.h>
16ab27cfb8SPatrick McHardy #include <linux/err.h>
173a9a231dSPaul Gortmaker #include <linux/module.h>
18b854272bSDenis V. Lunev #include <net/net_namespace.h>
19b854272bSDenis V. Lunev #include <net/sock.h>
201da177e4SLinus Torvalds #include <net/sch_generic.h>
211045ba77SJamal Hadi Salim #include <net/pkt_cls.h>
228cbfe939SBaowen Zheng #include <net/tc_act/tc_pedit.h>
231da177e4SLinus Torvalds #include <net/act_api.h>
24dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h>
258cbfe939SBaowen Zheng #include <net/flow_offload.h>
26871cf386SPedro Tammela #include <net/tc_wrapper.h>
271da177e4SLinus Torvalds
28c129412fSwenxu #ifdef CONFIG_INET
29c129412fSwenxu DEFINE_STATIC_KEY_FALSE(tcf_frag_xmit_count);
30c129412fSwenxu EXPORT_SYMBOL_GPL(tcf_frag_xmit_count);
31c129412fSwenxu #endif
32c129412fSwenxu
tcf_dev_queue_xmit(struct sk_buff * skb,int (* xmit)(struct sk_buff * skb))33c129412fSwenxu int tcf_dev_queue_xmit(struct sk_buff *skb, int (*xmit)(struct sk_buff *skb))
34c129412fSwenxu {
35c129412fSwenxu #ifdef CONFIG_INET
36c129412fSwenxu if (static_branch_unlikely(&tcf_frag_xmit_count))
37c129412fSwenxu return sch_frag_xmit_hook(skb, xmit);
38c129412fSwenxu #endif
39c129412fSwenxu
40c129412fSwenxu return xmit(skb);
41c129412fSwenxu }
42c129412fSwenxu EXPORT_SYMBOL_GPL(tcf_dev_queue_xmit);
43c129412fSwenxu
tcf_action_goto_chain_exec(const struct tc_action * a,struct tcf_result * res)44db50514fSJiri Pirko static void tcf_action_goto_chain_exec(const struct tc_action *a,
45db50514fSJiri Pirko struct tcf_result *res)
46db50514fSJiri Pirko {
47ee3bbfe8SDavide Caratti const struct tcf_chain *chain = rcu_dereference_bh(a->goto_chain);
48db50514fSJiri Pirko
49db50514fSJiri Pirko res->goto_tp = rcu_dereference_bh(chain->filter_chain);
50db50514fSJiri Pirko }
51db50514fSJiri Pirko
tcf_free_cookie_rcu(struct rcu_head * p)52eec94fdbSVlad Buslov static void tcf_free_cookie_rcu(struct rcu_head *p)
53eec94fdbSVlad Buslov {
54eec94fdbSVlad Buslov struct tc_cookie *cookie = container_of(p, struct tc_cookie, rcu);
55eec94fdbSVlad Buslov
56eec94fdbSVlad Buslov kfree(cookie->data);
57eec94fdbSVlad Buslov kfree(cookie);
58eec94fdbSVlad Buslov }
59eec94fdbSVlad Buslov
tcf_set_action_cookie(struct tc_cookie __rcu ** old_cookie,struct tc_cookie * new_cookie)60eec94fdbSVlad Buslov static void tcf_set_action_cookie(struct tc_cookie __rcu **old_cookie,
61eec94fdbSVlad Buslov struct tc_cookie *new_cookie)
62eec94fdbSVlad Buslov {
63eec94fdbSVlad Buslov struct tc_cookie *old;
64eec94fdbSVlad Buslov
65*70530a2fSEric Dumazet old = unrcu_pointer(xchg(old_cookie, RCU_INITIALIZER(new_cookie)));
66eec94fdbSVlad Buslov if (old)
67eec94fdbSVlad Buslov call_rcu(&old->rcu, tcf_free_cookie_rcu);
68eec94fdbSVlad Buslov }
69eec94fdbSVlad Buslov
tcf_action_check_ctrlact(int action,struct tcf_proto * tp,struct tcf_chain ** newchain,struct netlink_ext_ack * extack)7085d0966fSDavide Caratti int tcf_action_check_ctrlact(int action, struct tcf_proto *tp,
7185d0966fSDavide Caratti struct tcf_chain **newchain,
7285d0966fSDavide Caratti struct netlink_ext_ack *extack)
7385d0966fSDavide Caratti {
7485d0966fSDavide Caratti int opcode = TC_ACT_EXT_OPCODE(action), ret = -EINVAL;
7585d0966fSDavide Caratti u32 chain_index;
7685d0966fSDavide Caratti
7785d0966fSDavide Caratti if (!opcode)
7885d0966fSDavide Caratti ret = action > TC_ACT_VALUE_MAX ? -EINVAL : 0;
7985d0966fSDavide Caratti else if (opcode <= TC_ACT_EXT_OPCODE_MAX || action == TC_ACT_UNSPEC)
8085d0966fSDavide Caratti ret = 0;
8185d0966fSDavide Caratti if (ret) {
8285d0966fSDavide Caratti NL_SET_ERR_MSG(extack, "invalid control action");
8385d0966fSDavide Caratti goto end;
8485d0966fSDavide Caratti }
8585d0966fSDavide Caratti
8685d0966fSDavide Caratti if (TC_ACT_EXT_CMP(action, TC_ACT_GOTO_CHAIN)) {
8785d0966fSDavide Caratti chain_index = action & TC_ACT_EXT_VAL_MASK;
8885d0966fSDavide Caratti if (!tp || !newchain) {
8985d0966fSDavide Caratti ret = -EINVAL;
9085d0966fSDavide Caratti NL_SET_ERR_MSG(extack,
9185d0966fSDavide Caratti "can't goto NULL proto/chain");
9285d0966fSDavide Caratti goto end;
9385d0966fSDavide Caratti }
9485d0966fSDavide Caratti *newchain = tcf_chain_get_by_act(tp->chain->block, chain_index);
9585d0966fSDavide Caratti if (!*newchain) {
9685d0966fSDavide Caratti ret = -ENOMEM;
9785d0966fSDavide Caratti NL_SET_ERR_MSG(extack,
9885d0966fSDavide Caratti "can't allocate goto_chain");
9985d0966fSDavide Caratti }
10085d0966fSDavide Caratti }
10185d0966fSDavide Caratti end:
10285d0966fSDavide Caratti return ret;
10385d0966fSDavide Caratti }
10485d0966fSDavide Caratti EXPORT_SYMBOL(tcf_action_check_ctrlact);
10585d0966fSDavide Caratti
tcf_action_set_ctrlact(struct tc_action * a,int action,struct tcf_chain * goto_chain)10685d0966fSDavide Caratti struct tcf_chain *tcf_action_set_ctrlact(struct tc_action *a, int action,
107ee3bbfe8SDavide Caratti struct tcf_chain *goto_chain)
10885d0966fSDavide Caratti {
10985d0966fSDavide Caratti a->tcfa_action = action;
110445d3749SPaul E. McKenney goto_chain = rcu_replace_pointer(a->goto_chain, goto_chain, 1);
111ee3bbfe8SDavide Caratti return goto_chain;
11285d0966fSDavide Caratti }
11385d0966fSDavide Caratti EXPORT_SYMBOL(tcf_action_set_ctrlact);
11485d0966fSDavide Caratti
115d7fb60b9SCong Wang /* XXX: For standalone actions, we don't need a RCU grace period either, because
116d7fb60b9SCong Wang * actions are always connected to filters and filters are already destroyed in
117d7fb60b9SCong Wang * RCU callbacks, so after a RCU grace period actions are already disconnected
118d7fb60b9SCong Wang * from filters. Readers later can not find us.
119d7fb60b9SCong Wang */
free_tcf(struct tc_action * p)120d7fb60b9SCong Wang static void free_tcf(struct tc_action *p)
121519c818eSEric Dumazet {
122ee3bbfe8SDavide Caratti struct tcf_chain *chain = rcu_dereference_protected(p->goto_chain, 1);
12385d0966fSDavide Caratti
124519c818eSEric Dumazet free_percpu(p->cpu_bstats);
12528169abaSEelco Chaudron free_percpu(p->cpu_bstats_hw);
126519c818eSEric Dumazet free_percpu(p->cpu_qstats);
1271045ba77SJamal Hadi Salim
128db4b4902SPaul Blakey tcf_set_action_cookie(&p->user_cookie, NULL);
12985d0966fSDavide Caratti if (chain)
13085d0966fSDavide Caratti tcf_chain_put_by_act(chain);
1311045ba77SJamal Hadi Salim
132519c818eSEric Dumazet kfree(p);
133519c818eSEric Dumazet }
134519c818eSEric Dumazet
offload_action_hw_count_set(struct tc_action * act,u32 hw_count)1357adc5765SBaowen Zheng static void offload_action_hw_count_set(struct tc_action *act,
1367adc5765SBaowen Zheng u32 hw_count)
1377adc5765SBaowen Zheng {
1387adc5765SBaowen Zheng act->in_hw_count = hw_count;
1397adc5765SBaowen Zheng }
1407adc5765SBaowen Zheng
offload_action_hw_count_inc(struct tc_action * act,u32 hw_count)14113926d19SBaowen Zheng static void offload_action_hw_count_inc(struct tc_action *act,
14213926d19SBaowen Zheng u32 hw_count)
14313926d19SBaowen Zheng {
14413926d19SBaowen Zheng act->in_hw_count += hw_count;
14513926d19SBaowen Zheng }
14613926d19SBaowen Zheng
offload_action_hw_count_dec(struct tc_action * act,u32 hw_count)14713926d19SBaowen Zheng static void offload_action_hw_count_dec(struct tc_action *act,
14813926d19SBaowen Zheng u32 hw_count)
14913926d19SBaowen Zheng {
15013926d19SBaowen Zheng act->in_hw_count = act->in_hw_count > hw_count ?
15113926d19SBaowen Zheng act->in_hw_count - hw_count : 0;
15213926d19SBaowen Zheng }
15313926d19SBaowen Zheng
tcf_offload_act_num_actions_single(struct tc_action * act)1548cbfe939SBaowen Zheng static unsigned int tcf_offload_act_num_actions_single(struct tc_action *act)
1558cbfe939SBaowen Zheng {
1568cbfe939SBaowen Zheng if (is_tcf_pedit(act))
1578cbfe939SBaowen Zheng return tcf_pedit_nkeys(act);
1588cbfe939SBaowen Zheng else
1598cbfe939SBaowen Zheng return 1;
1608cbfe939SBaowen Zheng }
1618cbfe939SBaowen Zheng
tc_act_skip_hw(u32 flags)1627adc5765SBaowen Zheng static bool tc_act_skip_hw(u32 flags)
1637adc5765SBaowen Zheng {
1647adc5765SBaowen Zheng return (flags & TCA_ACT_FLAGS_SKIP_HW) ? true : false;
1657adc5765SBaowen Zheng }
1667adc5765SBaowen Zheng
tc_act_skip_sw(u32 flags)1677adc5765SBaowen Zheng static bool tc_act_skip_sw(u32 flags)
1687adc5765SBaowen Zheng {
1697adc5765SBaowen Zheng return (flags & TCA_ACT_FLAGS_SKIP_SW) ? true : false;
1707adc5765SBaowen Zheng }
1717adc5765SBaowen Zheng
1727adc5765SBaowen Zheng /* SKIP_HW and SKIP_SW are mutually exclusive flags. */
tc_act_flags_valid(u32 flags)1737adc5765SBaowen Zheng static bool tc_act_flags_valid(u32 flags)
1747adc5765SBaowen Zheng {
1757adc5765SBaowen Zheng flags &= TCA_ACT_FLAGS_SKIP_HW | TCA_ACT_FLAGS_SKIP_SW;
1767adc5765SBaowen Zheng
1777adc5765SBaowen Zheng return flags ^ (TCA_ACT_FLAGS_SKIP_HW | TCA_ACT_FLAGS_SKIP_SW);
1787adc5765SBaowen Zheng }
1797adc5765SBaowen Zheng
offload_action_init(struct flow_offload_action * fl_action,struct tc_action * act,enum offload_act_command cmd,struct netlink_ext_ack * extack)1808cbfe939SBaowen Zheng static int offload_action_init(struct flow_offload_action *fl_action,
1818cbfe939SBaowen Zheng struct tc_action *act,
1828cbfe939SBaowen Zheng enum offload_act_command cmd,
1838cbfe939SBaowen Zheng struct netlink_ext_ack *extack)
1848cbfe939SBaowen Zheng {
185963178a0SBaowen Zheng int err;
186963178a0SBaowen Zheng
1878cbfe939SBaowen Zheng fl_action->extack = extack;
1888cbfe939SBaowen Zheng fl_action->command = cmd;
1898cbfe939SBaowen Zheng fl_action->index = act->tcfa_index;
190d307b2c6SOz Shlomo fl_action->cookie = (unsigned long)act;
1918cbfe939SBaowen Zheng
192963178a0SBaowen Zheng if (act->ops->offload_act_setup) {
193963178a0SBaowen Zheng spin_lock_bh(&act->tcfa_lock);
194963178a0SBaowen Zheng err = act->ops->offload_act_setup(act, fl_action, NULL,
195c2ccf84eSIdo Schimmel false, extack);
196963178a0SBaowen Zheng spin_unlock_bh(&act->tcfa_lock);
197963178a0SBaowen Zheng return err;
198963178a0SBaowen Zheng }
1998cbfe939SBaowen Zheng
2008cbfe939SBaowen Zheng return -EOPNOTSUPP;
2018cbfe939SBaowen Zheng }
2028cbfe939SBaowen Zheng
tcf_action_offload_cmd_ex(struct flow_offload_action * fl_act,u32 * hw_count)20313926d19SBaowen Zheng static int tcf_action_offload_cmd_ex(struct flow_offload_action *fl_act,
20413926d19SBaowen Zheng u32 *hw_count)
2058cbfe939SBaowen Zheng {
2068cbfe939SBaowen Zheng int err;
2078cbfe939SBaowen Zheng
2088cbfe939SBaowen Zheng err = flow_indr_dev_setup_offload(NULL, NULL, TC_SETUP_ACT,
2098cbfe939SBaowen Zheng fl_act, NULL, NULL);
2108cbfe939SBaowen Zheng if (err < 0)
2118cbfe939SBaowen Zheng return err;
2128cbfe939SBaowen Zheng
2137adc5765SBaowen Zheng if (hw_count)
2147adc5765SBaowen Zheng *hw_count = err;
2157adc5765SBaowen Zheng
2168cbfe939SBaowen Zheng return 0;
2178cbfe939SBaowen Zheng }
2188cbfe939SBaowen Zheng
tcf_action_offload_cmd_cb_ex(struct flow_offload_action * fl_act,u32 * hw_count,flow_indr_block_bind_cb_t * cb,void * cb_priv)21913926d19SBaowen Zheng static int tcf_action_offload_cmd_cb_ex(struct flow_offload_action *fl_act,
22013926d19SBaowen Zheng u32 *hw_count,
22113926d19SBaowen Zheng flow_indr_block_bind_cb_t *cb,
22213926d19SBaowen Zheng void *cb_priv)
22313926d19SBaowen Zheng {
22413926d19SBaowen Zheng int err;
22513926d19SBaowen Zheng
22613926d19SBaowen Zheng err = cb(NULL, NULL, cb_priv, TC_SETUP_ACT, NULL, fl_act, NULL);
22713926d19SBaowen Zheng if (err < 0)
22813926d19SBaowen Zheng return err;
22913926d19SBaowen Zheng
23013926d19SBaowen Zheng if (hw_count)
23113926d19SBaowen Zheng *hw_count = 1;
23213926d19SBaowen Zheng
23313926d19SBaowen Zheng return 0;
23413926d19SBaowen Zheng }
23513926d19SBaowen Zheng
tcf_action_offload_cmd(struct flow_offload_action * fl_act,u32 * hw_count,flow_indr_block_bind_cb_t * cb,void * cb_priv)23613926d19SBaowen Zheng static int tcf_action_offload_cmd(struct flow_offload_action *fl_act,
23713926d19SBaowen Zheng u32 *hw_count,
23813926d19SBaowen Zheng flow_indr_block_bind_cb_t *cb,
23913926d19SBaowen Zheng void *cb_priv)
24013926d19SBaowen Zheng {
24113926d19SBaowen Zheng return cb ? tcf_action_offload_cmd_cb_ex(fl_act, hw_count,
24213926d19SBaowen Zheng cb, cb_priv) :
24313926d19SBaowen Zheng tcf_action_offload_cmd_ex(fl_act, hw_count);
24413926d19SBaowen Zheng }
24513926d19SBaowen Zheng
tcf_action_offload_add_ex(struct tc_action * action,struct netlink_ext_ack * extack,flow_indr_block_bind_cb_t * cb,void * cb_priv)24613926d19SBaowen Zheng static int tcf_action_offload_add_ex(struct tc_action *action,
24713926d19SBaowen Zheng struct netlink_ext_ack *extack,
24813926d19SBaowen Zheng flow_indr_block_bind_cb_t *cb,
24913926d19SBaowen Zheng void *cb_priv)
2508cbfe939SBaowen Zheng {
2517adc5765SBaowen Zheng bool skip_sw = tc_act_skip_sw(action->tcfa_flags);
2528cbfe939SBaowen Zheng struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
2538cbfe939SBaowen Zheng [0] = action,
2548cbfe939SBaowen Zheng };
2558cbfe939SBaowen Zheng struct flow_offload_action *fl_action;
2567adc5765SBaowen Zheng u32 in_hw_count = 0;
2578cbfe939SBaowen Zheng int num, err = 0;
2588cbfe939SBaowen Zheng
2597adc5765SBaowen Zheng if (tc_act_skip_hw(action->tcfa_flags))
2607adc5765SBaowen Zheng return 0;
2617adc5765SBaowen Zheng
2628cbfe939SBaowen Zheng num = tcf_offload_act_num_actions_single(action);
2638cbfe939SBaowen Zheng fl_action = offload_action_alloc(num);
2648cbfe939SBaowen Zheng if (!fl_action)
2658cbfe939SBaowen Zheng return -ENOMEM;
2668cbfe939SBaowen Zheng
2678cbfe939SBaowen Zheng err = offload_action_init(fl_action, action, FLOW_ACT_REPLACE, extack);
2688cbfe939SBaowen Zheng if (err)
2698cbfe939SBaowen Zheng goto fl_err;
2708cbfe939SBaowen Zheng
27180cd22c3SPaul Blakey err = tc_setup_action(&fl_action->action, actions, 0, extack);
2728cbfe939SBaowen Zheng if (err) {
2738cbfe939SBaowen Zheng NL_SET_ERR_MSG_MOD(extack,
274ecf4a24cSWan Jiabing "Failed to setup tc actions for offload");
2758cbfe939SBaowen Zheng goto fl_err;
2768cbfe939SBaowen Zheng }
2778cbfe939SBaowen Zheng
27813926d19SBaowen Zheng err = tcf_action_offload_cmd(fl_action, &in_hw_count, cb, cb_priv);
2797adc5765SBaowen Zheng if (!err)
28013926d19SBaowen Zheng cb ? offload_action_hw_count_inc(action, in_hw_count) :
2817adc5765SBaowen Zheng offload_action_hw_count_set(action, in_hw_count);
2827adc5765SBaowen Zheng
2837adc5765SBaowen Zheng if (skip_sw && !tc_act_in_hw(action))
2847adc5765SBaowen Zheng err = -EINVAL;
2857adc5765SBaowen Zheng
2868cbfe939SBaowen Zheng tc_cleanup_offload_action(&fl_action->action);
2878cbfe939SBaowen Zheng
2888cbfe939SBaowen Zheng fl_err:
2898cbfe939SBaowen Zheng kfree(fl_action);
2908cbfe939SBaowen Zheng
2918cbfe939SBaowen Zheng return err;
2928cbfe939SBaowen Zheng }
2938cbfe939SBaowen Zheng
29413926d19SBaowen Zheng /* offload the tc action after it is inserted */
tcf_action_offload_add(struct tc_action * action,struct netlink_ext_ack * extack)29513926d19SBaowen Zheng static int tcf_action_offload_add(struct tc_action *action,
29613926d19SBaowen Zheng struct netlink_ext_ack *extack)
29713926d19SBaowen Zheng {
29813926d19SBaowen Zheng return tcf_action_offload_add_ex(action, extack, NULL, NULL);
29913926d19SBaowen Zheng }
30013926d19SBaowen Zheng
tcf_action_update_hw_stats(struct tc_action * action)301c7a66f8dSBaowen Zheng int tcf_action_update_hw_stats(struct tc_action *action)
302c7a66f8dSBaowen Zheng {
303c7a66f8dSBaowen Zheng struct flow_offload_action fl_act = {};
304c7a66f8dSBaowen Zheng int err;
305c7a66f8dSBaowen Zheng
306c7a66f8dSBaowen Zheng err = offload_action_init(&fl_act, action, FLOW_ACT_STATS, NULL);
307c7a66f8dSBaowen Zheng if (err)
308c7a66f8dSBaowen Zheng return err;
309c7a66f8dSBaowen Zheng
31013926d19SBaowen Zheng err = tcf_action_offload_cmd(&fl_act, NULL, NULL, NULL);
311c7a66f8dSBaowen Zheng if (!err) {
312c7a66f8dSBaowen Zheng preempt_disable();
313c7a66f8dSBaowen Zheng tcf_action_stats_update(action, fl_act.stats.bytes,
314c7a66f8dSBaowen Zheng fl_act.stats.pkts,
315c7a66f8dSBaowen Zheng fl_act.stats.drops,
316c7a66f8dSBaowen Zheng fl_act.stats.lastused,
317c7a66f8dSBaowen Zheng true);
318c7a66f8dSBaowen Zheng preempt_enable();
319c7a66f8dSBaowen Zheng action->used_hw_stats = fl_act.stats.used_hw_stats;
320c7a66f8dSBaowen Zheng action->used_hw_stats_valid = true;
321c7a66f8dSBaowen Zheng } else {
322c7a66f8dSBaowen Zheng return -EOPNOTSUPP;
323c7a66f8dSBaowen Zheng }
324c7a66f8dSBaowen Zheng
325c7a66f8dSBaowen Zheng return 0;
326c7a66f8dSBaowen Zheng }
327c7a66f8dSBaowen Zheng EXPORT_SYMBOL(tcf_action_update_hw_stats);
328c7a66f8dSBaowen Zheng
tcf_action_offload_del_ex(struct tc_action * action,flow_indr_block_bind_cb_t * cb,void * cb_priv)32913926d19SBaowen Zheng static int tcf_action_offload_del_ex(struct tc_action *action,
33013926d19SBaowen Zheng flow_indr_block_bind_cb_t *cb,
33113926d19SBaowen Zheng void *cb_priv)
3328cbfe939SBaowen Zheng {
3338cbfe939SBaowen Zheng struct flow_offload_action fl_act = {};
3347adc5765SBaowen Zheng u32 in_hw_count = 0;
3358cbfe939SBaowen Zheng int err = 0;
3368cbfe939SBaowen Zheng
3377adc5765SBaowen Zheng if (!tc_act_in_hw(action))
3387adc5765SBaowen Zheng return 0;
3397adc5765SBaowen Zheng
3408cbfe939SBaowen Zheng err = offload_action_init(&fl_act, action, FLOW_ACT_DESTROY, NULL);
3418cbfe939SBaowen Zheng if (err)
3428cbfe939SBaowen Zheng return err;
3438cbfe939SBaowen Zheng
34413926d19SBaowen Zheng err = tcf_action_offload_cmd(&fl_act, &in_hw_count, cb, cb_priv);
34513926d19SBaowen Zheng if (err < 0)
3467adc5765SBaowen Zheng return err;
3477adc5765SBaowen Zheng
34813926d19SBaowen Zheng if (!cb && action->in_hw_count != in_hw_count)
3497adc5765SBaowen Zheng return -EINVAL;
3507adc5765SBaowen Zheng
35113926d19SBaowen Zheng /* do not need to update hw state when deleting action */
35213926d19SBaowen Zheng if (cb && in_hw_count)
35313926d19SBaowen Zheng offload_action_hw_count_dec(action, in_hw_count);
35413926d19SBaowen Zheng
3557adc5765SBaowen Zheng return 0;
3568cbfe939SBaowen Zheng }
3578cbfe939SBaowen Zheng
tcf_action_offload_del(struct tc_action * action)35813926d19SBaowen Zheng static int tcf_action_offload_del(struct tc_action *action)
35913926d19SBaowen Zheng {
36013926d19SBaowen Zheng return tcf_action_offload_del_ex(action, NULL, NULL);
36113926d19SBaowen Zheng }
36213926d19SBaowen Zheng
tcf_action_cleanup(struct tc_action * p)36316af6067SVlad Buslov static void tcf_action_cleanup(struct tc_action *p)
364e9ce1cd3SDavid S. Miller {
3658cbfe939SBaowen Zheng tcf_action_offload_del(p);
36616af6067SVlad Buslov if (p->ops->cleanup)
36716af6067SVlad Buslov p->ops->cleanup(p);
36816af6067SVlad Buslov
3691c0d32fdSEric Dumazet gen_kill_estimator(&p->tcfa_rate_est);
370d7fb60b9SCong Wang free_tcf(p);
371e9ce1cd3SDavid S. Miller }
372e9ce1cd3SDavid S. Miller
__tcf_action_put(struct tc_action * p,bool bind)37316af6067SVlad Buslov static int __tcf_action_put(struct tc_action *p, bool bind)
37416af6067SVlad Buslov {
37516af6067SVlad Buslov struct tcf_idrinfo *idrinfo = p->idrinfo;
37616af6067SVlad Buslov
37795278ddaSCong Wang if (refcount_dec_and_mutex_lock(&p->tcfa_refcnt, &idrinfo->lock)) {
37816af6067SVlad Buslov if (bind)
37916af6067SVlad Buslov atomic_dec(&p->tcfa_bindcnt);
38016af6067SVlad Buslov idr_remove(&idrinfo->action_idr, p->tcfa_index);
38195278ddaSCong Wang mutex_unlock(&idrinfo->lock);
38216af6067SVlad Buslov
38316af6067SVlad Buslov tcf_action_cleanup(p);
38416af6067SVlad Buslov return 1;
38516af6067SVlad Buslov }
38616af6067SVlad Buslov
38716af6067SVlad Buslov if (bind)
38816af6067SVlad Buslov atomic_dec(&p->tcfa_bindcnt);
38916af6067SVlad Buslov
39016af6067SVlad Buslov return 0;
39116af6067SVlad Buslov }
39216af6067SVlad Buslov
__tcf_idr_release(struct tc_action * p,bool bind,bool strict)393b3650bf7SVlad Buslov static int __tcf_idr_release(struct tc_action *p, bool bind, bool strict)
394e9ce1cd3SDavid S. Miller {
395e9ce1cd3SDavid S. Miller int ret = 0;
396e9ce1cd3SDavid S. Miller
397036bb443SVlad Buslov /* Release with strict==1 and bind==0 is only called through act API
398036bb443SVlad Buslov * interface (classifiers always bind). Only case when action with
399036bb443SVlad Buslov * positive reference count and zero bind count can exist is when it was
400036bb443SVlad Buslov * also created with act API (unbinding last classifier will destroy the
401036bb443SVlad Buslov * action if it was created by classifier). So only case when bind count
402036bb443SVlad Buslov * can be changed after initial check is when unbound action is
403036bb443SVlad Buslov * destroyed by act API while classifier binds to action with same id
404036bb443SVlad Buslov * concurrently. This result either creation of new action(same behavior
405036bb443SVlad Buslov * as before), or reusing existing action if concurrent process
406036bb443SVlad Buslov * increments reference count before action is deleted. Both scenarios
407036bb443SVlad Buslov * are acceptable.
408036bb443SVlad Buslov */
409e9ce1cd3SDavid S. Miller if (p) {
41016af6067SVlad Buslov if (!bind && strict && atomic_read(&p->tcfa_bindcnt) > 0)
41155334a5dSWANG Cong return -EPERM;
412e9ce1cd3SDavid S. Miller
41316af6067SVlad Buslov if (__tcf_action_put(p, bind))
4141d4150c0SWANG Cong ret = ACT_P_DELETED;
415e9ce1cd3SDavid S. Miller }
41628e6b67fSDaniel Borkmann
417e9ce1cd3SDavid S. Miller return ret;
418e9ce1cd3SDavid S. Miller }
419b3650bf7SVlad Buslov
tcf_idr_release(struct tc_action * a,bool bind)420b3650bf7SVlad Buslov int tcf_idr_release(struct tc_action *a, bool bind)
421b3650bf7SVlad Buslov {
422b3650bf7SVlad Buslov const struct tc_action_ops *ops = a->ops;
423b3650bf7SVlad Buslov int ret;
424b3650bf7SVlad Buslov
425b3650bf7SVlad Buslov ret = __tcf_idr_release(a, bind, false);
426b3650bf7SVlad Buslov if (ret == ACT_P_DELETED)
427b3650bf7SVlad Buslov module_put(ops->owner);
428b3650bf7SVlad Buslov return ret;
429b3650bf7SVlad Buslov }
430b3650bf7SVlad Buslov EXPORT_SYMBOL(tcf_idr_release);
431e9ce1cd3SDavid S. Miller
tcf_action_shared_attrs_size(const struct tc_action * act)4324e76e75dSRoman Mashak static size_t tcf_action_shared_attrs_size(const struct tc_action *act)
4334e76e75dSRoman Mashak {
434db4b4902SPaul Blakey struct tc_cookie *user_cookie;
4354e76e75dSRoman Mashak u32 cookie_len = 0;
4364e76e75dSRoman Mashak
437e0479b67SVlad Buslov rcu_read_lock();
438db4b4902SPaul Blakey user_cookie = rcu_dereference(act->user_cookie);
439e0479b67SVlad Buslov
440db4b4902SPaul Blakey if (user_cookie)
441db4b4902SPaul Blakey cookie_len = nla_total_size(user_cookie->len);
442e0479b67SVlad Buslov rcu_read_unlock();
4434e76e75dSRoman Mashak
4444e76e75dSRoman Mashak return nla_total_size(0) /* action number nested */
4454e76e75dSRoman Mashak + nla_total_size(IFNAMSIZ) /* TCA_ACT_KIND */
4464e76e75dSRoman Mashak + cookie_len /* TCA_ACT_COOKIE */
4470dfb2d82SJakub Kicinski + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_HW_STATS */
4484e76e75dSRoman Mashak + nla_total_size(0) /* TCA_ACT_STATS nested */
4491521a67eSJiri Pirko + nla_total_size(sizeof(struct nla_bitfield32)) /* TCA_ACT_FLAGS */
4504e76e75dSRoman Mashak /* TCA_STATS_BASIC */
4514e76e75dSRoman Mashak + nla_total_size_64bit(sizeof(struct gnet_stats_basic))
452b33e699fSEric Dumazet /* TCA_STATS_PKT64 */
453b33e699fSEric Dumazet + nla_total_size_64bit(sizeof(u64))
4544e76e75dSRoman Mashak /* TCA_STATS_QUEUE */
4554e76e75dSRoman Mashak + nla_total_size_64bit(sizeof(struct gnet_stats_queue))
456fcb3a465SPedro Tammela + nla_total_size(0) /* TCA_ACT_OPTIONS nested */
4574e76e75dSRoman Mashak + nla_total_size(sizeof(struct tcf_t)); /* TCA_GACT_TM */
4584e76e75dSRoman Mashak }
4594e76e75dSRoman Mashak
tcf_action_full_attrs_size(size_t sz)4604e76e75dSRoman Mashak static size_t tcf_action_full_attrs_size(size_t sz)
4614e76e75dSRoman Mashak {
4624e76e75dSRoman Mashak return NLMSG_HDRLEN /* struct nlmsghdr */
4634e76e75dSRoman Mashak + sizeof(struct tcamsg)
4644e76e75dSRoman Mashak + nla_total_size(0) /* TCA_ACT_TAB nested */
4654e76e75dSRoman Mashak + sz;
4664e76e75dSRoman Mashak }
4674e76e75dSRoman Mashak
tcf_action_fill_size(const struct tc_action * act)4684e76e75dSRoman Mashak static size_t tcf_action_fill_size(const struct tc_action *act)
4694e76e75dSRoman Mashak {
4704e76e75dSRoman Mashak size_t sz = tcf_action_shared_attrs_size(act);
4714e76e75dSRoman Mashak
4724e76e75dSRoman Mashak if (act->ops->get_fill_size)
4734e76e75dSRoman Mashak return act->ops->get_fill_size(act) + sz;
4744e76e75dSRoman Mashak return sz;
4754e76e75dSRoman Mashak }
4764e76e75dSRoman Mashak
47794f44f28SVlad Buslov static int
tcf_action_dump_terse(struct sk_buff * skb,struct tc_action * a,bool from_act)47894f44f28SVlad Buslov tcf_action_dump_terse(struct sk_buff *skb, struct tc_action *a, bool from_act)
47994f44f28SVlad Buslov {
48094f44f28SVlad Buslov unsigned char *b = skb_tail_pointer(skb);
48194f44f28SVlad Buslov struct tc_cookie *cookie;
48294f44f28SVlad Buslov
483fcb3a465SPedro Tammela if (nla_put_string(skb, TCA_ACT_KIND, a->ops->kind))
48494f44f28SVlad Buslov goto nla_put_failure;
48594f44f28SVlad Buslov if (tcf_action_copy_stats(skb, a, 0))
48694f44f28SVlad Buslov goto nla_put_failure;
48794f44f28SVlad Buslov if (from_act && nla_put_u32(skb, TCA_ACT_INDEX, a->tcfa_index))
48894f44f28SVlad Buslov goto nla_put_failure;
48994f44f28SVlad Buslov
49094f44f28SVlad Buslov rcu_read_lock();
491db4b4902SPaul Blakey cookie = rcu_dereference(a->user_cookie);
49294f44f28SVlad Buslov if (cookie) {
49394f44f28SVlad Buslov if (nla_put(skb, TCA_ACT_COOKIE, cookie->len, cookie->data)) {
49494f44f28SVlad Buslov rcu_read_unlock();
49594f44f28SVlad Buslov goto nla_put_failure;
49694f44f28SVlad Buslov }
49794f44f28SVlad Buslov }
49894f44f28SVlad Buslov rcu_read_unlock();
49994f44f28SVlad Buslov
50094f44f28SVlad Buslov return 0;
50194f44f28SVlad Buslov
50294f44f28SVlad Buslov nla_put_failure:
50394f44f28SVlad Buslov nlmsg_trim(skb, b);
50494f44f28SVlad Buslov return -1;
50594f44f28SVlad Buslov }
50694f44f28SVlad Buslov
tcf_dump_walker(struct tcf_idrinfo * idrinfo,struct sk_buff * skb,struct netlink_callback * cb)50765a206c0SChris Mi static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
508a85a970aSWANG Cong struct netlink_callback *cb)
509e9ce1cd3SDavid S. Miller {
51065a206c0SChris Mi int err = 0, index = -1, s_i = 0, n_i = 0;
51190825b23SJamal Hadi Salim u32 act_flags = cb->args[2];
512e62e484dSJamal Hadi Salim unsigned long jiffy_since = cb->args[3];
5134b3550efSPatrick McHardy struct nlattr *nest;
51465a206c0SChris Mi struct idr *idr = &idrinfo->action_idr;
51565a206c0SChris Mi struct tc_action *p;
51665a206c0SChris Mi unsigned long id = 1;
517e33d2b74SCong Wang unsigned long tmp;
518e9ce1cd3SDavid S. Miller
51995278ddaSCong Wang mutex_lock(&idrinfo->lock);
520e9ce1cd3SDavid S. Miller
521e9ce1cd3SDavid S. Miller s_i = cb->args[0];
522e9ce1cd3SDavid S. Miller
523e33d2b74SCong Wang idr_for_each_entry_ul(idr, p, tmp, id) {
524e9ce1cd3SDavid S. Miller index++;
525e9ce1cd3SDavid S. Miller if (index < s_i)
526e9ce1cd3SDavid S. Miller continue;
527580e4273SCong Wang if (IS_ERR(p))
528580e4273SCong Wang continue;
5294b3550efSPatrick McHardy
530e62e484dSJamal Hadi Salim if (jiffy_since &&
531e62e484dSJamal Hadi Salim time_after(jiffy_since,
532e62e484dSJamal Hadi Salim (unsigned long)p->tcfa_tm.lastuse))
533e62e484dSJamal Hadi Salim continue;
534e62e484dSJamal Hadi Salim
5358f2ca70cSOz Shlomo tcf_action_update_hw_stats(p);
5368f2ca70cSOz Shlomo
537ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, n_i);
538734549ebSCraig Dillabaugh if (!nest) {
539734549ebSCraig Dillabaugh index--;
5404b3550efSPatrick McHardy goto nla_put_failure;
541734549ebSCraig Dillabaugh }
542f460019bSVlad Buslov err = (act_flags & TCA_ACT_FLAG_TERSE_DUMP) ?
54394f44f28SVlad Buslov tcf_action_dump_terse(skb, p, true) :
54494f44f28SVlad Buslov tcf_action_dump_1(skb, p, 0, 0);
545e9ce1cd3SDavid S. Miller if (err < 0) {
546e9ce1cd3SDavid S. Miller index--;
5474b3550efSPatrick McHardy nlmsg_trim(skb, nest);
548e9ce1cd3SDavid S. Miller goto done;
549e9ce1cd3SDavid S. Miller }
5504b3550efSPatrick McHardy nla_nest_end(skb, nest);
551e9ce1cd3SDavid S. Miller n_i++;
552f460019bSVlad Buslov if (!(act_flags & TCA_ACT_FLAG_LARGE_DUMP_ON) &&
55390825b23SJamal Hadi Salim n_i >= TCA_ACT_MAX_PRIO)
554e9ce1cd3SDavid S. Miller goto done;
555e9ce1cd3SDavid S. Miller }
556e9ce1cd3SDavid S. Miller done:
557e62e484dSJamal Hadi Salim if (index >= 0)
558e62e484dSJamal Hadi Salim cb->args[0] = index + 1;
559e62e484dSJamal Hadi Salim
56095278ddaSCong Wang mutex_unlock(&idrinfo->lock);
56190825b23SJamal Hadi Salim if (n_i) {
562f460019bSVlad Buslov if (act_flags & TCA_ACT_FLAG_LARGE_DUMP_ON)
56390825b23SJamal Hadi Salim cb->args[1] = n_i;
56490825b23SJamal Hadi Salim }
565e9ce1cd3SDavid S. Miller return n_i;
566e9ce1cd3SDavid S. Miller
5677ba699c6SPatrick McHardy nla_put_failure:
5684b3550efSPatrick McHardy nla_nest_cancel(skb, nest);
569e9ce1cd3SDavid S. Miller goto done;
570e9ce1cd3SDavid S. Miller }
571e9ce1cd3SDavid S. Miller
tcf_idr_release_unsafe(struct tc_action * p)572ec3ed293SVlad Buslov static int tcf_idr_release_unsafe(struct tc_action *p)
573ec3ed293SVlad Buslov {
574ec3ed293SVlad Buslov if (atomic_read(&p->tcfa_bindcnt) > 0)
575ec3ed293SVlad Buslov return -EPERM;
576ec3ed293SVlad Buslov
577ec3ed293SVlad Buslov if (refcount_dec_and_test(&p->tcfa_refcnt)) {
578ec3ed293SVlad Buslov idr_remove(&p->idrinfo->action_idr, p->tcfa_index);
579ec3ed293SVlad Buslov tcf_action_cleanup(p);
580ec3ed293SVlad Buslov return ACT_P_DELETED;
581ec3ed293SVlad Buslov }
582ec3ed293SVlad Buslov
583ec3ed293SVlad Buslov return 0;
584ec3ed293SVlad Buslov }
585ec3ed293SVlad Buslov
tcf_del_walker(struct tcf_idrinfo * idrinfo,struct sk_buff * skb,const struct tc_action_ops * ops,struct netlink_ext_ack * extack)58665a206c0SChris Mi static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb,
58776b39b94SVictor Nogueira const struct tc_action_ops *ops,
58876b39b94SVictor Nogueira struct netlink_ext_ack *extack)
589e9ce1cd3SDavid S. Miller {
5904b3550efSPatrick McHardy struct nlattr *nest;
59165a206c0SChris Mi int n_i = 0;
59255334a5dSWANG Cong int ret = -EINVAL;
59365a206c0SChris Mi struct idr *idr = &idrinfo->action_idr;
59465a206c0SChris Mi struct tc_action *p;
59565a206c0SChris Mi unsigned long id = 1;
596e33d2b74SCong Wang unsigned long tmp;
597e9ce1cd3SDavid S. Miller
598ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, 0);
5994b3550efSPatrick McHardy if (nest == NULL)
6004b3550efSPatrick McHardy goto nla_put_failure;
601fcb3a465SPedro Tammela if (nla_put_string(skb, TCA_ACT_KIND, ops->kind))
6021b34ec43SDavid S. Miller goto nla_put_failure;
603a85a970aSWANG Cong
60476b39b94SVictor Nogueira ret = 0;
60595278ddaSCong Wang mutex_lock(&idrinfo->lock);
606e33d2b74SCong Wang idr_for_each_entry_ul(idr, p, tmp, id) {
6070fedc63fSCong Wang if (IS_ERR(p))
6080fedc63fSCong Wang continue;
609ec3ed293SVlad Buslov ret = tcf_idr_release_unsafe(p);
61076b39b94SVictor Nogueira if (ret == ACT_P_DELETED)
611255cd50fSJiri Pirko module_put(ops->owner);
61276b39b94SVictor Nogueira else if (ret < 0)
61376b39b94SVictor Nogueira break;
614e9ce1cd3SDavid S. Miller n_i++;
61576b39b94SVictor Nogueira }
61695278ddaSCong Wang mutex_unlock(&idrinfo->lock);
61776b39b94SVictor Nogueira if (ret < 0) {
61876b39b94SVictor Nogueira if (n_i)
61976b39b94SVictor Nogueira NL_SET_ERR_MSG(extack, "Unable to flush all TC actions");
62076b39b94SVictor Nogueira else
62155334a5dSWANG Cong goto nla_put_failure;
622e9ce1cd3SDavid S. Miller }
623ec3ed293SVlad Buslov
62455d96f72SYang Yingliang ret = nla_put_u32(skb, TCA_FCNT, n_i);
62555d96f72SYang Yingliang if (ret)
6261b34ec43SDavid S. Miller goto nla_put_failure;
6274b3550efSPatrick McHardy nla_nest_end(skb, nest);
628e9ce1cd3SDavid S. Miller
629e9ce1cd3SDavid S. Miller return n_i;
6307ba699c6SPatrick McHardy nla_put_failure:
6314b3550efSPatrick McHardy nla_nest_cancel(skb, nest);
63255334a5dSWANG Cong return ret;
633e9ce1cd3SDavid S. Miller }
634e9ce1cd3SDavid S. Miller
tcf_generic_walker(struct tc_action_net * tn,struct sk_buff * skb,struct netlink_callback * cb,int type,const struct tc_action_ops * ops,struct netlink_ext_ack * extack)635ddf97ccdSWANG Cong int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb,
636ddf97ccdSWANG Cong struct netlink_callback *cb, int type,
637b3620145SAlexander Aring const struct tc_action_ops *ops,
638b3620145SAlexander Aring struct netlink_ext_ack *extack)
639e9ce1cd3SDavid S. Miller {
64065a206c0SChris Mi struct tcf_idrinfo *idrinfo = tn->idrinfo;
641ddf97ccdSWANG Cong
642e9ce1cd3SDavid S. Miller if (type == RTM_DELACTION) {
64376b39b94SVictor Nogueira return tcf_del_walker(idrinfo, skb, ops, extack);
644e9ce1cd3SDavid S. Miller } else if (type == RTM_GETACTION) {
64565a206c0SChris Mi return tcf_dump_walker(idrinfo, skb, cb);
646e9ce1cd3SDavid S. Miller } else {
647b3620145SAlexander Aring WARN(1, "tcf_generic_walker: unknown command %d\n", type);
648b3620145SAlexander Aring NL_SET_ERR_MSG(extack, "tcf_generic_walker: unknown command");
649e9ce1cd3SDavid S. Miller return -EINVAL;
650e9ce1cd3SDavid S. Miller }
651e9ce1cd3SDavid S. Miller }
652ddf97ccdSWANG Cong EXPORT_SYMBOL(tcf_generic_walker);
653e9ce1cd3SDavid S. Miller
tcf_idr_search(struct tc_action_net * tn,struct tc_action ** a,u32 index)6547d485c45SCong Wang int tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index)
655e9ce1cd3SDavid S. Miller {
6563f7c72bcSVlad Buslov struct tcf_idrinfo *idrinfo = tn->idrinfo;
6573f7c72bcSVlad Buslov struct tc_action *p;
658e9ce1cd3SDavid S. Miller
65995278ddaSCong Wang mutex_lock(&idrinfo->lock);
660322d884bSMatthew Wilcox p = idr_find(&idrinfo->action_idr, index);
6617d485c45SCong Wang if (IS_ERR(p))
6620190c1d4SVlad Buslov p = NULL;
6637d485c45SCong Wang else if (p)
6643f7c72bcSVlad Buslov refcount_inc(&p->tcfa_refcnt);
66595278ddaSCong Wang mutex_unlock(&idrinfo->lock);
666e9ce1cd3SDavid S. Miller
6673f7c72bcSVlad Buslov if (p) {
6683f7c72bcSVlad Buslov *a = p;
6693f7c72bcSVlad Buslov return true;
6703f7c72bcSVlad Buslov }
6713f7c72bcSVlad Buslov return false;
672e9ce1cd3SDavid S. Miller }
67365a206c0SChris Mi EXPORT_SYMBOL(tcf_idr_search);
674e9ce1cd3SDavid S. Miller
__tcf_generic_walker(struct net * net,struct sk_buff * skb,struct netlink_callback * cb,int type,const struct tc_action_ops * ops,struct netlink_ext_ack * extack)675fae52d93SZhengchao Shao static int __tcf_generic_walker(struct net *net, struct sk_buff *skb,
676fae52d93SZhengchao Shao struct netlink_callback *cb, int type,
677fae52d93SZhengchao Shao const struct tc_action_ops *ops,
678fae52d93SZhengchao Shao struct netlink_ext_ack *extack)
679fae52d93SZhengchao Shao {
680fae52d93SZhengchao Shao struct tc_action_net *tn = net_generic(net, ops->net_id);
681fae52d93SZhengchao Shao
682fae52d93SZhengchao Shao if (unlikely(ops->walk))
683fae52d93SZhengchao Shao return ops->walk(net, skb, cb, type, ops, extack);
684fae52d93SZhengchao Shao
685fae52d93SZhengchao Shao return tcf_generic_walker(tn, skb, cb, type, ops, extack);
686fae52d93SZhengchao Shao }
687fae52d93SZhengchao Shao
__tcf_idr_search(struct net * net,const struct tc_action_ops * ops,struct tc_action ** a,u32 index)688fae52d93SZhengchao Shao static int __tcf_idr_search(struct net *net,
689fae52d93SZhengchao Shao const struct tc_action_ops *ops,
690fae52d93SZhengchao Shao struct tc_action **a, u32 index)
691fae52d93SZhengchao Shao {
692fae52d93SZhengchao Shao struct tc_action_net *tn = net_generic(net, ops->net_id);
693fae52d93SZhengchao Shao
694fae52d93SZhengchao Shao if (unlikely(ops->lookup))
695fae52d93SZhengchao Shao return ops->lookup(net, a, index);
696fae52d93SZhengchao Shao
697fae52d93SZhengchao Shao return tcf_idr_search(tn, a, index);
698fae52d93SZhengchao Shao }
699fae52d93SZhengchao Shao
tcf_idr_delete_index(struct tcf_idrinfo * idrinfo,u32 index)70097a3f84fSCong Wang static int tcf_idr_delete_index(struct tcf_idrinfo *idrinfo, u32 index)
7012a2ea349SVlad Buslov {
7022a2ea349SVlad Buslov struct tc_action *p;
7032a2ea349SVlad Buslov int ret = 0;
7042a2ea349SVlad Buslov
70595278ddaSCong Wang mutex_lock(&idrinfo->lock);
7062a2ea349SVlad Buslov p = idr_find(&idrinfo->action_idr, index);
7072a2ea349SVlad Buslov if (!p) {
70895278ddaSCong Wang mutex_unlock(&idrinfo->lock);
7092a2ea349SVlad Buslov return -ENOENT;
7102a2ea349SVlad Buslov }
7112a2ea349SVlad Buslov
7122a2ea349SVlad Buslov if (!atomic_read(&p->tcfa_bindcnt)) {
7132a2ea349SVlad Buslov if (refcount_dec_and_test(&p->tcfa_refcnt)) {
7142a2ea349SVlad Buslov struct module *owner = p->ops->owner;
7152a2ea349SVlad Buslov
7162a2ea349SVlad Buslov WARN_ON(p != idr_remove(&idrinfo->action_idr,
7172a2ea349SVlad Buslov p->tcfa_index));
71895278ddaSCong Wang mutex_unlock(&idrinfo->lock);
7192a2ea349SVlad Buslov
72016af6067SVlad Buslov tcf_action_cleanup(p);
7212a2ea349SVlad Buslov module_put(owner);
7222a2ea349SVlad Buslov return 0;
7232a2ea349SVlad Buslov }
7242a2ea349SVlad Buslov ret = 0;
7252a2ea349SVlad Buslov } else {
7262a2ea349SVlad Buslov ret = -EPERM;
7272a2ea349SVlad Buslov }
7282a2ea349SVlad Buslov
72995278ddaSCong Wang mutex_unlock(&idrinfo->lock);
7302a2ea349SVlad Buslov return ret;
7312a2ea349SVlad Buslov }
7322a2ea349SVlad Buslov
tcf_idr_create(struct tc_action_net * tn,u32 index,struct nlattr * est,struct tc_action ** a,const struct tc_action_ops * ops,int bind,bool cpustats,u32 flags)73365a206c0SChris Mi int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est,
734a85a970aSWANG Cong struct tc_action **a, const struct tc_action_ops *ops,
735e3822678SVlad Buslov int bind, bool cpustats, u32 flags)
736e9ce1cd3SDavid S. Miller {
737ec0595ccSWANG Cong struct tc_action *p = kzalloc(ops->size, GFP_KERNEL);
73865a206c0SChris Mi struct tcf_idrinfo *idrinfo = tn->idrinfo;
739519c818eSEric Dumazet int err = -ENOMEM;
740e9ce1cd3SDavid S. Miller
741e9ce1cd3SDavid S. Miller if (unlikely(!p))
74286062033SWANG Cong return -ENOMEM;
743036bb443SVlad Buslov refcount_set(&p->tcfa_refcnt, 1);
744e9ce1cd3SDavid S. Miller if (bind)
745036bb443SVlad Buslov atomic_set(&p->tcfa_bindcnt, 1);
746e9ce1cd3SDavid S. Miller
747519c818eSEric Dumazet if (cpustats) {
74850dc9a85SAhmed S. Darwish p->cpu_bstats = netdev_alloc_pcpu_stats(struct gnet_stats_basic_sync);
749339913a8SMatthew Wilcox if (!p->cpu_bstats)
750519c818eSEric Dumazet goto err1;
75150dc9a85SAhmed S. Darwish p->cpu_bstats_hw = netdev_alloc_pcpu_stats(struct gnet_stats_basic_sync);
75228169abaSEelco Chaudron if (!p->cpu_bstats_hw)
75328169abaSEelco Chaudron goto err2;
754339913a8SMatthew Wilcox p->cpu_qstats = alloc_percpu(struct gnet_stats_queue);
755339913a8SMatthew Wilcox if (!p->cpu_qstats)
75628169abaSEelco Chaudron goto err3;
75765a206c0SChris Mi }
75850dc9a85SAhmed S. Darwish gnet_stats_basic_sync_init(&p->tcfa_bstats);
75950dc9a85SAhmed S. Darwish gnet_stats_basic_sync_init(&p->tcfa_bstats_hw);
760339913a8SMatthew Wilcox spin_lock_init(&p->tcfa_lock);
761339913a8SMatthew Wilcox p->tcfa_index = index;
762ec0595ccSWANG Cong p->tcfa_tm.install = jiffies;
763ec0595ccSWANG Cong p->tcfa_tm.lastuse = jiffies;
764ec0595ccSWANG Cong p->tcfa_tm.firstuse = 0;
765e8cb5bcfSBaowen Zheng p->tcfa_flags = flags;
7660e991ec6SStephen Hemminger if (est) {
767ec0595ccSWANG Cong err = gen_new_estimator(&p->tcfa_bstats, p->cpu_bstats,
768ec0595ccSWANG Cong &p->tcfa_rate_est,
76929cbcd85SAhmed S. Darwish &p->tcfa_lock, false, est);
770339913a8SMatthew Wilcox if (err)
77128169abaSEelco Chaudron goto err4;
7720e991ec6SStephen Hemminger }
7730e991ec6SStephen Hemminger
77465a206c0SChris Mi p->idrinfo = idrinfo;
775b3650bf7SVlad Buslov __module_get(ops->owner);
776ec0595ccSWANG Cong p->ops = ops;
777ec0595ccSWANG Cong *a = p;
77886062033SWANG Cong return 0;
77928169abaSEelco Chaudron err4:
780339913a8SMatthew Wilcox free_percpu(p->cpu_qstats);
78128169abaSEelco Chaudron err3:
78228169abaSEelco Chaudron free_percpu(p->cpu_bstats_hw);
783339913a8SMatthew Wilcox err2:
784339913a8SMatthew Wilcox free_percpu(p->cpu_bstats);
785339913a8SMatthew Wilcox err1:
786339913a8SMatthew Wilcox kfree(p);
787339913a8SMatthew Wilcox return err;
788e9ce1cd3SDavid S. Miller }
78965a206c0SChris Mi EXPORT_SYMBOL(tcf_idr_create);
790e9ce1cd3SDavid S. Miller
tcf_idr_create_from_flags(struct tc_action_net * tn,u32 index,struct nlattr * est,struct tc_action ** a,const struct tc_action_ops * ops,int bind,u32 flags)791e3822678SVlad Buslov int tcf_idr_create_from_flags(struct tc_action_net *tn, u32 index,
792e3822678SVlad Buslov struct nlattr *est, struct tc_action **a,
793e3822678SVlad Buslov const struct tc_action_ops *ops, int bind,
794e3822678SVlad Buslov u32 flags)
795e3822678SVlad Buslov {
796e3822678SVlad Buslov /* Set cpustats according to actions flags. */
797e3822678SVlad Buslov return tcf_idr_create(tn, index, est, a, ops, bind,
798e3822678SVlad Buslov !(flags & TCA_ACT_FLAGS_NO_PERCPU_STATS), flags);
799e3822678SVlad Buslov }
800e3822678SVlad Buslov EXPORT_SYMBOL(tcf_idr_create_from_flags);
801e3822678SVlad Buslov
8020190c1d4SVlad Buslov /* Cleanup idr index that was allocated but not initialized. */
8030190c1d4SVlad Buslov
tcf_idr_cleanup(struct tc_action_net * tn,u32 index)8040190c1d4SVlad Buslov void tcf_idr_cleanup(struct tc_action_net *tn, u32 index)
8050190c1d4SVlad Buslov {
8060190c1d4SVlad Buslov struct tcf_idrinfo *idrinfo = tn->idrinfo;
8070190c1d4SVlad Buslov
80895278ddaSCong Wang mutex_lock(&idrinfo->lock);
8090190c1d4SVlad Buslov /* Remove ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc */
8100190c1d4SVlad Buslov WARN_ON(!IS_ERR(idr_remove(&idrinfo->action_idr, index)));
81195278ddaSCong Wang mutex_unlock(&idrinfo->lock);
8120190c1d4SVlad Buslov }
8130190c1d4SVlad Buslov EXPORT_SYMBOL(tcf_idr_cleanup);
8140190c1d4SVlad Buslov
8150190c1d4SVlad Buslov /* Check if action with specified index exists. If actions is found, increments
8160190c1d4SVlad Buslov * its reference and bind counters, and return 1. Otherwise insert temporary
8170190c1d4SVlad Buslov * error pointer (to prevent concurrent users from inserting actions with same
8180190c1d4SVlad Buslov * index) and return 0.
819284fd7e9SPedro Tammela *
820284fd7e9SPedro Tammela * May return -EAGAIN for binding actions in case of a parallel add/delete on
821284fd7e9SPedro Tammela * the requested index.
8220190c1d4SVlad Buslov */
8230190c1d4SVlad Buslov
tcf_idr_check_alloc(struct tc_action_net * tn,u32 * index,struct tc_action ** a,int bind)8240190c1d4SVlad Buslov int tcf_idr_check_alloc(struct tc_action_net *tn, u32 *index,
8250190c1d4SVlad Buslov struct tc_action **a, int bind)
8260190c1d4SVlad Buslov {
8270190c1d4SVlad Buslov struct tcf_idrinfo *idrinfo = tn->idrinfo;
8280190c1d4SVlad Buslov struct tc_action *p;
8290190c1d4SVlad Buslov int ret;
830284fd7e9SPedro Tammela u32 max;
8310190c1d4SVlad Buslov
8320190c1d4SVlad Buslov if (*index) {
833284fd7e9SPedro Tammela rcu_read_lock();
8340190c1d4SVlad Buslov p = idr_find(&idrinfo->action_idr, *index);
835284fd7e9SPedro Tammela
8360190c1d4SVlad Buslov if (IS_ERR(p)) {
8370190c1d4SVlad Buslov /* This means that another process allocated
8380190c1d4SVlad Buslov * index but did not assign the pointer yet.
8390190c1d4SVlad Buslov */
840284fd7e9SPedro Tammela rcu_read_unlock();
8415f926aa9SDavid Ruth return -EAGAIN;
8420190c1d4SVlad Buslov }
8430190c1d4SVlad Buslov
844284fd7e9SPedro Tammela if (!p) {
845284fd7e9SPedro Tammela /* Empty slot, try to allocate it */
846284fd7e9SPedro Tammela max = *index;
847284fd7e9SPedro Tammela rcu_read_unlock();
848284fd7e9SPedro Tammela goto new;
849284fd7e9SPedro Tammela }
850284fd7e9SPedro Tammela
851284fd7e9SPedro Tammela if (!refcount_inc_not_zero(&p->tcfa_refcnt)) {
852284fd7e9SPedro Tammela /* Action was deleted in parallel */
853284fd7e9SPedro Tammela rcu_read_unlock();
854284fd7e9SPedro Tammela return -EAGAIN;
855284fd7e9SPedro Tammela }
856284fd7e9SPedro Tammela
8570190c1d4SVlad Buslov if (bind)
8580190c1d4SVlad Buslov atomic_inc(&p->tcfa_bindcnt);
8590190c1d4SVlad Buslov *a = p;
860284fd7e9SPedro Tammela
861284fd7e9SPedro Tammela rcu_read_unlock();
862284fd7e9SPedro Tammela
863284fd7e9SPedro Tammela return 1;
8640190c1d4SVlad Buslov } else {
865284fd7e9SPedro Tammela /* Find a slot */
8660190c1d4SVlad Buslov *index = 1;
867284fd7e9SPedro Tammela max = UINT_MAX;
8680190c1d4SVlad Buslov }
869284fd7e9SPedro Tammela
870284fd7e9SPedro Tammela new:
871284fd7e9SPedro Tammela *a = NULL;
872284fd7e9SPedro Tammela
873284fd7e9SPedro Tammela mutex_lock(&idrinfo->lock);
874284fd7e9SPedro Tammela ret = idr_alloc_u32(&idrinfo->action_idr, ERR_PTR(-EBUSY), index, max,
875284fd7e9SPedro Tammela GFP_KERNEL);
87695278ddaSCong Wang mutex_unlock(&idrinfo->lock);
877284fd7e9SPedro Tammela
878284fd7e9SPedro Tammela /* N binds raced for action allocation,
879284fd7e9SPedro Tammela * retry for all the ones that failed.
880284fd7e9SPedro Tammela */
881284fd7e9SPedro Tammela if (ret == -ENOSPC && *index == max)
882284fd7e9SPedro Tammela ret = -EAGAIN;
883284fd7e9SPedro Tammela
8840190c1d4SVlad Buslov return ret;
8850190c1d4SVlad Buslov }
8860190c1d4SVlad Buslov EXPORT_SYMBOL(tcf_idr_check_alloc);
8870190c1d4SVlad Buslov
tcf_idrinfo_destroy(const struct tc_action_ops * ops,struct tcf_idrinfo * idrinfo)88865a206c0SChris Mi void tcf_idrinfo_destroy(const struct tc_action_ops *ops,
88965a206c0SChris Mi struct tcf_idrinfo *idrinfo)
8901d4150c0SWANG Cong {
89165a206c0SChris Mi struct idr *idr = &idrinfo->action_idr;
892ec0595ccSWANG Cong struct tc_action *p;
8931d4150c0SWANG Cong int ret;
89465a206c0SChris Mi unsigned long id = 1;
895e33d2b74SCong Wang unsigned long tmp;
8961d4150c0SWANG Cong
897e33d2b74SCong Wang idr_for_each_entry_ul(idr, p, tmp, id) {
89865a206c0SChris Mi ret = __tcf_idr_release(p, false, true);
8991d4150c0SWANG Cong if (ret == ACT_P_DELETED)
9001d4150c0SWANG Cong module_put(ops->owner);
9011d4150c0SWANG Cong else if (ret < 0)
9021d4150c0SWANG Cong return;
9031d4150c0SWANG Cong }
90465a206c0SChris Mi idr_destroy(&idrinfo->action_idr);
9051d4150c0SWANG Cong }
90665a206c0SChris Mi EXPORT_SYMBOL(tcf_idrinfo_destroy);
9071d4150c0SWANG Cong
9081f747c26SWANG Cong static LIST_HEAD(act_base);
9091da177e4SLinus Torvalds static DEFINE_RWLOCK(act_mod_lock);
91013926d19SBaowen Zheng /* since act ops id is stored in pernet subsystem list,
91113926d19SBaowen Zheng * then there is no way to walk through only all the action
91213926d19SBaowen Zheng * subsystem, so we keep tc action pernet ops id for
91313926d19SBaowen Zheng * reoffload to walk through.
91413926d19SBaowen Zheng */
91513926d19SBaowen Zheng static LIST_HEAD(act_pernet_id_list);
91613926d19SBaowen Zheng static DEFINE_MUTEX(act_id_mutex);
91713926d19SBaowen Zheng struct tc_act_pernet_id {
91813926d19SBaowen Zheng struct list_head list;
91913926d19SBaowen Zheng unsigned int id;
92013926d19SBaowen Zheng };
92113926d19SBaowen Zheng
tcf_pernet_add_id_list(unsigned int id)92213926d19SBaowen Zheng static int tcf_pernet_add_id_list(unsigned int id)
92313926d19SBaowen Zheng {
92413926d19SBaowen Zheng struct tc_act_pernet_id *id_ptr;
92513926d19SBaowen Zheng int ret = 0;
92613926d19SBaowen Zheng
92713926d19SBaowen Zheng mutex_lock(&act_id_mutex);
92813926d19SBaowen Zheng list_for_each_entry(id_ptr, &act_pernet_id_list, list) {
92913926d19SBaowen Zheng if (id_ptr->id == id) {
93013926d19SBaowen Zheng ret = -EEXIST;
93113926d19SBaowen Zheng goto err_out;
93213926d19SBaowen Zheng }
93313926d19SBaowen Zheng }
93413926d19SBaowen Zheng
93513926d19SBaowen Zheng id_ptr = kzalloc(sizeof(*id_ptr), GFP_KERNEL);
93613926d19SBaowen Zheng if (!id_ptr) {
93713926d19SBaowen Zheng ret = -ENOMEM;
93813926d19SBaowen Zheng goto err_out;
93913926d19SBaowen Zheng }
94013926d19SBaowen Zheng id_ptr->id = id;
94113926d19SBaowen Zheng
94213926d19SBaowen Zheng list_add_tail(&id_ptr->list, &act_pernet_id_list);
94313926d19SBaowen Zheng
94413926d19SBaowen Zheng err_out:
94513926d19SBaowen Zheng mutex_unlock(&act_id_mutex);
94613926d19SBaowen Zheng return ret;
94713926d19SBaowen Zheng }
94813926d19SBaowen Zheng
tcf_pernet_del_id_list(unsigned int id)94913926d19SBaowen Zheng static void tcf_pernet_del_id_list(unsigned int id)
95013926d19SBaowen Zheng {
95113926d19SBaowen Zheng struct tc_act_pernet_id *id_ptr;
95213926d19SBaowen Zheng
95313926d19SBaowen Zheng mutex_lock(&act_id_mutex);
95413926d19SBaowen Zheng list_for_each_entry(id_ptr, &act_pernet_id_list, list) {
95513926d19SBaowen Zheng if (id_ptr->id == id) {
95613926d19SBaowen Zheng list_del(&id_ptr->list);
95713926d19SBaowen Zheng kfree(id_ptr);
95813926d19SBaowen Zheng break;
95913926d19SBaowen Zheng }
96013926d19SBaowen Zheng }
96113926d19SBaowen Zheng mutex_unlock(&act_id_mutex);
96213926d19SBaowen Zheng }
9631da177e4SLinus Torvalds
tcf_register_action(struct tc_action_ops * act,struct pernet_operations * ops)964ddf97ccdSWANG Cong int tcf_register_action(struct tc_action_ops *act,
965ddf97ccdSWANG Cong struct pernet_operations *ops)
9661da177e4SLinus Torvalds {
9671f747c26SWANG Cong struct tc_action_ops *a;
968ddf97ccdSWANG Cong int ret;
9691da177e4SLinus Torvalds
970fae52d93SZhengchao Shao if (!act->act || !act->dump || !act->init)
97176c82d7aSJamal Hadi Salim return -EINVAL;
97276c82d7aSJamal Hadi Salim
973ab102b80SWANG Cong /* We have to register pernet ops before making the action ops visible,
974ab102b80SWANG Cong * otherwise tcf_action_init_1() could get a partially initialized
975ab102b80SWANG Cong * netns.
976ab102b80SWANG Cong */
977ab102b80SWANG Cong ret = register_pernet_subsys(ops);
978ab102b80SWANG Cong if (ret)
979ab102b80SWANG Cong return ret;
980ab102b80SWANG Cong
98113926d19SBaowen Zheng if (ops->id) {
98213926d19SBaowen Zheng ret = tcf_pernet_add_id_list(*ops->id);
98313926d19SBaowen Zheng if (ret)
98413926d19SBaowen Zheng goto err_id;
98513926d19SBaowen Zheng }
98613926d19SBaowen Zheng
9871da177e4SLinus Torvalds write_lock(&act_mod_lock);
9881f747c26SWANG Cong list_for_each_entry(a, &act_base, head) {
989eddd2cf1SEli Cohen if (act->id == a->id || (strcmp(act->kind, a->kind) == 0)) {
99013926d19SBaowen Zheng ret = -EEXIST;
99113926d19SBaowen Zheng goto err_out;
9921da177e4SLinus Torvalds }
9931da177e4SLinus Torvalds }
9941f747c26SWANG Cong list_add_tail(&act->head, &act_base);
9951da177e4SLinus Torvalds write_unlock(&act_mod_lock);
996ddf97ccdSWANG Cong
9971da177e4SLinus Torvalds return 0;
99813926d19SBaowen Zheng
99913926d19SBaowen Zheng err_out:
100013926d19SBaowen Zheng write_unlock(&act_mod_lock);
100113926d19SBaowen Zheng if (ops->id)
100213926d19SBaowen Zheng tcf_pernet_del_id_list(*ops->id);
100313926d19SBaowen Zheng err_id:
100413926d19SBaowen Zheng unregister_pernet_subsys(ops);
100513926d19SBaowen Zheng return ret;
10061da177e4SLinus Torvalds }
100762e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_register_action);
10081da177e4SLinus Torvalds
tcf_unregister_action(struct tc_action_ops * act,struct pernet_operations * ops)1009ddf97ccdSWANG Cong int tcf_unregister_action(struct tc_action_ops *act,
1010ddf97ccdSWANG Cong struct pernet_operations *ops)
10111da177e4SLinus Torvalds {
10121f747c26SWANG Cong struct tc_action_ops *a;
10131da177e4SLinus Torvalds int err = -ENOENT;
10141da177e4SLinus Torvalds
10151da177e4SLinus Torvalds write_lock(&act_mod_lock);
1016a792866aSEric Dumazet list_for_each_entry(a, &act_base, head) {
1017a792866aSEric Dumazet if (a == act) {
10181f747c26SWANG Cong list_del(&act->head);
10191da177e4SLinus Torvalds err = 0;
1020a792866aSEric Dumazet break;
1021a792866aSEric Dumazet }
10221da177e4SLinus Torvalds }
10231da177e4SLinus Torvalds write_unlock(&act_mod_lock);
102413926d19SBaowen Zheng if (!err) {
1025ab102b80SWANG Cong unregister_pernet_subsys(ops);
102613926d19SBaowen Zheng if (ops->id)
102713926d19SBaowen Zheng tcf_pernet_del_id_list(*ops->id);
102813926d19SBaowen Zheng }
10291da177e4SLinus Torvalds return err;
10301da177e4SLinus Torvalds }
103162e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_unregister_action);
10321da177e4SLinus Torvalds
10331da177e4SLinus Torvalds /* lookup by name */
tc_lookup_action_n(char * kind)10341da177e4SLinus Torvalds static struct tc_action_ops *tc_lookup_action_n(char *kind)
10351da177e4SLinus Torvalds {
1036a792866aSEric Dumazet struct tc_action_ops *a, *res = NULL;
10371da177e4SLinus Torvalds
10381da177e4SLinus Torvalds if (kind) {
10391da177e4SLinus Torvalds read_lock(&act_mod_lock);
10401f747c26SWANG Cong list_for_each_entry(a, &act_base, head) {
10411da177e4SLinus Torvalds if (strcmp(kind, a->kind) == 0) {
1042a792866aSEric Dumazet if (try_module_get(a->owner))
1043a792866aSEric Dumazet res = a;
10441da177e4SLinus Torvalds break;
10451da177e4SLinus Torvalds }
10461da177e4SLinus Torvalds }
10471da177e4SLinus Torvalds read_unlock(&act_mod_lock);
10481da177e4SLinus Torvalds }
1049a792866aSEric Dumazet return res;
10501da177e4SLinus Torvalds }
10511da177e4SLinus Torvalds
10527ba699c6SPatrick McHardy /* lookup by nlattr */
tc_lookup_action(struct nlattr * kind)10537ba699c6SPatrick McHardy static struct tc_action_ops *tc_lookup_action(struct nlattr *kind)
10541da177e4SLinus Torvalds {
1055a792866aSEric Dumazet struct tc_action_ops *a, *res = NULL;
10561da177e4SLinus Torvalds
10571da177e4SLinus Torvalds if (kind) {
10581da177e4SLinus Torvalds read_lock(&act_mod_lock);
10591f747c26SWANG Cong list_for_each_entry(a, &act_base, head) {
10607ba699c6SPatrick McHardy if (nla_strcmp(kind, a->kind) == 0) {
1061a792866aSEric Dumazet if (try_module_get(a->owner))
1062a792866aSEric Dumazet res = a;
10631da177e4SLinus Torvalds break;
10641da177e4SLinus Torvalds }
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds read_unlock(&act_mod_lock);
10671da177e4SLinus Torvalds }
1068a792866aSEric Dumazet return res;
10691da177e4SLinus Torvalds }
10701da177e4SLinus Torvalds
1071e0ee84deSJamal Hadi Salim /*TCA_ACT_MAX_PRIO is 32, there count up to 32 */
1072e0ee84deSJamal Hadi Salim #define TCA_ACT_MAX_PRIO_MASK 0x1FF
tcf_action_exec(struct sk_buff * skb,struct tc_action ** actions,int nr_actions,struct tcf_result * res)107322dc13c8SWANG Cong int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions,
107422dc13c8SWANG Cong int nr_actions, struct tcf_result *res)
10751da177e4SLinus Torvalds {
1076e0ee84deSJamal Hadi Salim u32 jmp_prgcnt = 0;
1077e0ee84deSJamal Hadi Salim u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */
1078ec1a9ccaSJiri Pirko int i;
1079ec1a9ccaSJiri Pirko int ret = TC_ACT_OK;
10801da177e4SLinus Torvalds
1081e7246e12SWillem de Bruijn if (skb_skip_tc_classify(skb))
1082e7246e12SWillem de Bruijn return TC_ACT_OK;
1083e7246e12SWillem de Bruijn
1084e0ee84deSJamal Hadi Salim restart_act_graph:
108522dc13c8SWANG Cong for (i = 0; i < nr_actions; i++) {
108622dc13c8SWANG Cong const struct tc_action *a = actions[i];
10875740d068SEric Dumazet int repeat_ttl;
108822dc13c8SWANG Cong
1089e0ee84deSJamal Hadi Salim if (jmp_prgcnt > 0) {
1090e0ee84deSJamal Hadi Salim jmp_prgcnt -= 1;
1091e0ee84deSJamal Hadi Salim continue;
1092e0ee84deSJamal Hadi Salim }
10937adc5765SBaowen Zheng
10947adc5765SBaowen Zheng if (tc_act_skip_sw(a->tcfa_flags))
10957adc5765SBaowen Zheng continue;
10965740d068SEric Dumazet
10975740d068SEric Dumazet repeat_ttl = 32;
10981da177e4SLinus Torvalds repeat:
1099871cf386SPedro Tammela ret = tc_act(skb, a, res);
11005740d068SEric Dumazet if (unlikely(ret == TC_ACT_REPEAT)) {
11015740d068SEric Dumazet if (--repeat_ttl != 0)
11025740d068SEric Dumazet goto repeat;
11035740d068SEric Dumazet /* suspicious opcode, stop pipeline */
11045740d068SEric Dumazet net_warn_ratelimited("TC_ACT_REPEAT abuse ?\n");
11055740d068SEric Dumazet return TC_ACT_OK;
11065740d068SEric Dumazet }
11079da3242eSJiri Pirko if (TC_ACT_EXT_CMP(ret, TC_ACT_JUMP)) {
1108e0ee84deSJamal Hadi Salim jmp_prgcnt = ret & TCA_ACT_MAX_PRIO_MASK;
1109e0ee84deSJamal Hadi Salim if (!jmp_prgcnt || (jmp_prgcnt > nr_actions)) {
1110e0ee84deSJamal Hadi Salim /* faulty opcode, stop pipeline */
1111e0ee84deSJamal Hadi Salim return TC_ACT_OK;
1112e0ee84deSJamal Hadi Salim } else {
1113e0ee84deSJamal Hadi Salim jmp_ttl -= 1;
1114e0ee84deSJamal Hadi Salim if (jmp_ttl > 0)
1115e0ee84deSJamal Hadi Salim goto restart_act_graph;
1116e0ee84deSJamal Hadi Salim else /* faulty graph, stop pipeline */
1117e0ee84deSJamal Hadi Salim return TC_ACT_OK;
1118e0ee84deSJamal Hadi Salim }
1119db50514fSJiri Pirko } else if (TC_ACT_EXT_CMP(ret, TC_ACT_GOTO_CHAIN)) {
1120ee3bbfe8SDavide Caratti if (unlikely(!rcu_access_pointer(a->goto_chain))) {
1121ee3bbfe8SDavide Caratti net_warn_ratelimited("can't go to NULL chain!\n");
1122ee3bbfe8SDavide Caratti return TC_ACT_SHOT;
1123ee3bbfe8SDavide Caratti }
1124db50514fSJiri Pirko tcf_action_goto_chain_exec(a, res);
1125e0ee84deSJamal Hadi Salim }
1126e0ee84deSJamal Hadi Salim
112714d50e78SJ Hadi Salim if (ret != TC_ACT_PIPE)
1128e7246e12SWillem de Bruijn break;
11291da177e4SLinus Torvalds }
1130e0ee84deSJamal Hadi Salim
11311da177e4SLinus Torvalds return ret;
11321da177e4SLinus Torvalds }
113362e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_exec);
11341da177e4SLinus Torvalds
tcf_action_destroy(struct tc_action * actions[],int bind)113590b73b77SVlad Buslov int tcf_action_destroy(struct tc_action *actions[], int bind)
11361da177e4SLinus Torvalds {
1137255cd50fSJiri Pirko const struct tc_action_ops *ops;
113890b73b77SVlad Buslov struct tc_action *a;
113990b73b77SVlad Buslov int ret = 0, i;
11401da177e4SLinus Torvalds
114190b73b77SVlad Buslov for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) {
114290b73b77SVlad Buslov a = actions[i];
114390b73b77SVlad Buslov actions[i] = NULL;
1144255cd50fSJiri Pirko ops = a->ops;
114565a206c0SChris Mi ret = __tcf_idr_release(a, bind, true);
114655334a5dSWANG Cong if (ret == ACT_P_DELETED)
1147255cd50fSJiri Pirko module_put(ops->owner);
114855334a5dSWANG Cong else if (ret < 0)
114955334a5dSWANG Cong return ret;
11501da177e4SLinus Torvalds }
115155334a5dSWANG Cong return ret;
11521da177e4SLinus Torvalds }
11531da177e4SLinus Torvalds
tcf_action_put(struct tc_action * p)115416af6067SVlad Buslov static int tcf_action_put(struct tc_action *p)
115516af6067SVlad Buslov {
115616af6067SVlad Buslov return __tcf_action_put(p, false);
115716af6067SVlad Buslov }
115816af6067SVlad Buslov
1159edfaf94fSCong Wang /* Put all actions in this array, skip those NULL's. */
tcf_action_put_many(struct tc_action * actions[])116090b73b77SVlad Buslov static void tcf_action_put_many(struct tc_action *actions[])
1161cae422f3SVlad Buslov {
116290b73b77SVlad Buslov int i;
1163cae422f3SVlad Buslov
1164edfaf94fSCong Wang for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
116590b73b77SVlad Buslov struct tc_action *a = actions[i];
1166edfaf94fSCong Wang const struct tc_action_ops *ops;
1167cae422f3SVlad Buslov
1168edfaf94fSCong Wang if (!a)
1169edfaf94fSCong Wang continue;
1170edfaf94fSCong Wang ops = a->ops;
1171cae422f3SVlad Buslov if (tcf_action_put(a))
1172cae422f3SVlad Buslov module_put(ops->owner);
1173cae422f3SVlad Buslov }
1174cae422f3SVlad Buslov }
1175cae422f3SVlad Buslov
11761da177e4SLinus Torvalds int
tcf_action_dump_old(struct sk_buff * skb,struct tc_action * a,int bind,int ref)11771da177e4SLinus Torvalds tcf_action_dump_old(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
11781da177e4SLinus Torvalds {
11791da177e4SLinus Torvalds return a->ops->dump(skb, a, bind, ref);
11801da177e4SLinus Torvalds }
11811da177e4SLinus Torvalds
1182ca44b738SVlad Buslov int
tcf_action_dump_1(struct sk_buff * skb,struct tc_action * a,int bind,int ref)1183ca44b738SVlad Buslov tcf_action_dump_1(struct sk_buff *skb, struct tc_action *a, int bind, int ref)
1184ca44b738SVlad Buslov {
1185ca44b738SVlad Buslov int err = -EINVAL;
1186ca44b738SVlad Buslov unsigned char *b = skb_tail_pointer(skb);
1187ca44b738SVlad Buslov struct nlattr *nest;
1188e8cb5bcfSBaowen Zheng u32 flags;
1189ca44b738SVlad Buslov
119094f44f28SVlad Buslov if (tcf_action_dump_terse(skb, a, false))
1191ca44b738SVlad Buslov goto nla_put_failure;
1192ca44b738SVlad Buslov
11938953b077SJiri Pirko if (a->hw_stats != TCA_ACT_HW_STATS_ANY &&
11948953b077SJiri Pirko nla_put_bitfield32(skb, TCA_ACT_HW_STATS,
11958953b077SJiri Pirko a->hw_stats, TCA_ACT_HW_STATS_ANY))
119644f86580SJiri Pirko goto nla_put_failure;
119744f86580SJiri Pirko
119893a129ebSJiri Pirko if (a->used_hw_stats_valid &&
119993a129ebSJiri Pirko nla_put_bitfield32(skb, TCA_ACT_USED_HW_STATS,
120093a129ebSJiri Pirko a->used_hw_stats, TCA_ACT_HW_STATS_ANY))
120193a129ebSJiri Pirko goto nla_put_failure;
120293a129ebSJiri Pirko
1203e8cb5bcfSBaowen Zheng flags = a->tcfa_flags & TCA_ACT_FLAGS_USER_MASK;
1204e8cb5bcfSBaowen Zheng if (flags &&
12058953b077SJiri Pirko nla_put_bitfield32(skb, TCA_ACT_FLAGS,
1206e8cb5bcfSBaowen Zheng flags, flags))
1207e3822678SVlad Buslov goto nla_put_failure;
1208e3822678SVlad Buslov
12097adc5765SBaowen Zheng if (nla_put_u32(skb, TCA_ACT_IN_HW_COUNT, a->in_hw_count))
12107adc5765SBaowen Zheng goto nla_put_failure;
12117adc5765SBaowen Zheng
1212fcb3a465SPedro Tammela nest = nla_nest_start_noflag(skb, TCA_ACT_OPTIONS);
12134b3550efSPatrick McHardy if (nest == NULL)
12144b3550efSPatrick McHardy goto nla_put_failure;
1215cc7ec456SEric Dumazet err = tcf_action_dump_old(skb, a, bind, ref);
1216cc7ec456SEric Dumazet if (err > 0) {
12174b3550efSPatrick McHardy nla_nest_end(skb, nest);
12181da177e4SLinus Torvalds return err;
12191da177e4SLinus Torvalds }
12201da177e4SLinus Torvalds
12217ba699c6SPatrick McHardy nla_put_failure:
1222dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b);
12231da177e4SLinus Torvalds return -1;
12241da177e4SLinus Torvalds }
122562e3ba1bSPatrick McHardy EXPORT_SYMBOL(tcf_action_dump_1);
12261da177e4SLinus Torvalds
tcf_action_dump(struct sk_buff * skb,struct tc_action * actions[],int bind,int ref,bool terse)122790b73b77SVlad Buslov int tcf_action_dump(struct sk_buff *skb, struct tc_action *actions[],
1228ca44b738SVlad Buslov int bind, int ref, bool terse)
12291da177e4SLinus Torvalds {
12301da177e4SLinus Torvalds struct tc_action *a;
123190b73b77SVlad Buslov int err = -EINVAL, i;
12324b3550efSPatrick McHardy struct nlattr *nest;
12331da177e4SLinus Torvalds
123490b73b77SVlad Buslov for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) {
123590b73b77SVlad Buslov a = actions[i];
12364097e9d2SVlad Buslov nest = nla_nest_start_noflag(skb, i + 1);
12374b3550efSPatrick McHardy if (nest == NULL)
12384b3550efSPatrick McHardy goto nla_put_failure;
123994f44f28SVlad Buslov err = terse ? tcf_action_dump_terse(skb, a, false) :
1240ca44b738SVlad Buslov tcf_action_dump_1(skb, a, bind, ref);
12411da177e4SLinus Torvalds if (err < 0)
12424fe683f5SThomas Graf goto errout;
12434b3550efSPatrick McHardy nla_nest_end(skb, nest);
12441da177e4SLinus Torvalds }
12451da177e4SLinus Torvalds
12461da177e4SLinus Torvalds return 0;
12471da177e4SLinus Torvalds
12487ba699c6SPatrick McHardy nla_put_failure:
12494fe683f5SThomas Graf err = -EINVAL;
12504fe683f5SThomas Graf errout:
12514b3550efSPatrick McHardy nla_nest_cancel(skb, nest);
12524fe683f5SThomas Graf return err;
12531da177e4SLinus Torvalds }
12541da177e4SLinus Torvalds
nla_memdup_cookie(struct nlattr ** tb)1255e0535ce5SWolfgang Bumiller static struct tc_cookie *nla_memdup_cookie(struct nlattr **tb)
12561045ba77SJamal Hadi Salim {
1257e0535ce5SWolfgang Bumiller struct tc_cookie *c = kzalloc(sizeof(*c), GFP_KERNEL);
1258e0535ce5SWolfgang Bumiller if (!c)
1259e0535ce5SWolfgang Bumiller return NULL;
12601045ba77SJamal Hadi Salim
1261e0535ce5SWolfgang Bumiller c->data = nla_memdup(tb[TCA_ACT_COOKIE], GFP_KERNEL);
1262e0535ce5SWolfgang Bumiller if (!c->data) {
1263e0535ce5SWolfgang Bumiller kfree(c);
1264e0535ce5SWolfgang Bumiller return NULL;
12651045ba77SJamal Hadi Salim }
1266e0535ce5SWolfgang Bumiller c->len = nla_len(tb[TCA_ACT_COOKIE]);
12671045ba77SJamal Hadi Salim
1268e0535ce5SWolfgang Bumiller return c;
12691045ba77SJamal Hadi Salim }
12701045ba77SJamal Hadi Salim
tcf_action_hw_stats_get(struct nlattr * hw_stats_attr)12710dfb2d82SJakub Kicinski static u8 tcf_action_hw_stats_get(struct nlattr *hw_stats_attr)
127244f86580SJiri Pirko {
12730dfb2d82SJakub Kicinski struct nla_bitfield32 hw_stats_bf;
127444f86580SJiri Pirko
127544f86580SJiri Pirko /* If the user did not pass the attr, that means he does
127644f86580SJiri Pirko * not care about the type. Return "any" in that case
127744f86580SJiri Pirko * which is setting on all supported types.
127844f86580SJiri Pirko */
12790dfb2d82SJakub Kicinski if (!hw_stats_attr)
12800dfb2d82SJakub Kicinski return TCA_ACT_HW_STATS_ANY;
12810dfb2d82SJakub Kicinski hw_stats_bf = nla_get_bitfield32(hw_stats_attr);
12820dfb2d82SJakub Kicinski return hw_stats_bf.value;
128344f86580SJiri Pirko }
128444f86580SJiri Pirko
1285199ce850SCong Wang static const struct nla_policy tcf_action_policy[TCA_ACT_MAX + 1] = {
12864b793fecSCong Wang [TCA_ACT_KIND] = { .type = NLA_STRING },
1287199ce850SCong Wang [TCA_ACT_INDEX] = { .type = NLA_U32 },
1288199ce850SCong Wang [TCA_ACT_COOKIE] = { .type = NLA_BINARY,
1289199ce850SCong Wang .len = TC_COOKIE_MAX_SIZE },
1290199ce850SCong Wang [TCA_ACT_OPTIONS] = { .type = NLA_NESTED },
12917adc5765SBaowen Zheng [TCA_ACT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_ACT_FLAGS_NO_PERCPU_STATS |
12927adc5765SBaowen Zheng TCA_ACT_FLAGS_SKIP_HW |
12937adc5765SBaowen Zheng TCA_ACT_FLAGS_SKIP_SW),
129447a1494bSJohannes Berg [TCA_ACT_HW_STATS] = NLA_POLICY_BITFIELD32(TCA_ACT_HW_STATS_ANY),
1295199ce850SCong Wang };
1296199ce850SCong Wang
tcf_idr_insert_many(struct tc_action * actions[])1297396d7f23SVlad Buslov void tcf_idr_insert_many(struct tc_action *actions[])
1298e49d8c22SCong Wang {
12990fedc63fSCong Wang int i;
1300e49d8c22SCong Wang
13010fedc63fSCong Wang for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
13020fedc63fSCong Wang struct tc_action *a = actions[i];
13030fedc63fSCong Wang struct tcf_idrinfo *idrinfo;
13040fedc63fSCong Wang
13050fedc63fSCong Wang if (!a)
13060fedc63fSCong Wang continue;
13070fedc63fSCong Wang idrinfo = a->idrinfo;
1308e49d8c22SCong Wang mutex_lock(&idrinfo->lock);
13090fedc63fSCong Wang /* Replace ERR_PTR(-EBUSY) allocated by tcf_idr_check_alloc if
13100fedc63fSCong Wang * it is just created, otherwise this is just a nop.
13110fedc63fSCong Wang */
13120fedc63fSCong Wang idr_replace(&idrinfo->action_idr, a, a->tcfa_index);
1313e49d8c22SCong Wang mutex_unlock(&idrinfo->lock);
1314e49d8c22SCong Wang }
13150fedc63fSCong Wang }
1316e49d8c22SCong Wang
tc_action_load_ops(struct nlattr * nla,bool police,bool rtnl_held,struct netlink_ext_ack * extack)1317695176bfSCong Wang struct tc_action_ops *tc_action_load_ops(struct nlattr *nla, bool police,
1318789871bbSVlad Buslov bool rtnl_held,
1319aea0d727SAlexander Aring struct netlink_ext_ack *extack)
13201da177e4SLinus Torvalds {
13217ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1];
1322d349f997SCong Wang struct tc_action_ops *a_o;
1323d349f997SCong Wang char act_name[IFNAMSIZ];
13247ba699c6SPatrick McHardy struct nlattr *kind;
1325ab27cfb8SPatrick McHardy int err;
13261da177e4SLinus Torvalds
1327695176bfSCong Wang if (!police) {
1328199ce850SCong Wang err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
1329199ce850SCong Wang tcf_action_policy, extack);
1330cee63723SPatrick McHardy if (err < 0)
1331d349f997SCong Wang return ERR_PTR(err);
1332cee63723SPatrick McHardy err = -EINVAL;
13337ba699c6SPatrick McHardy kind = tb[TCA_ACT_KIND];
133484ae017aSAlexander Aring if (!kind) {
133584ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "TC action kind must be specified");
1336d349f997SCong Wang return ERR_PTR(err);
133784ae017aSAlexander Aring }
1338872f6903SFrancis Laniel if (nla_strscpy(act_name, kind, IFNAMSIZ) < 0) {
13394b793fecSCong Wang NL_SET_ERR_MSG(extack, "TC action name too long");
1340d349f997SCong Wang return ERR_PTR(err);
13414b793fecSCong Wang }
13421da177e4SLinus Torvalds } else {
1343989b52cdSAzeem Shaikh if (strscpy(act_name, "police", IFNAMSIZ) < 0) {
134484ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "TC action name too long");
1345d349f997SCong Wang return ERR_PTR(-EINVAL);
13461da177e4SLinus Torvalds }
134784ae017aSAlexander Aring }
13481da177e4SLinus Torvalds
13491da177e4SLinus Torvalds a_o = tc_lookup_action_n(act_name);
13501da177e4SLinus Torvalds if (a_o == NULL) {
135195a5afcaSJohannes Berg #ifdef CONFIG_MODULES
1352789871bbSVlad Buslov if (rtnl_held)
13531da177e4SLinus Torvalds rtnl_unlock();
13544bba3925SPatrick McHardy request_module("act_%s", act_name);
1355789871bbSVlad Buslov if (rtnl_held)
13561da177e4SLinus Torvalds rtnl_lock();
13571da177e4SLinus Torvalds
13581da177e4SLinus Torvalds a_o = tc_lookup_action_n(act_name);
13591da177e4SLinus Torvalds
13601da177e4SLinus Torvalds /* We dropped the RTNL semaphore in order to
13611da177e4SLinus Torvalds * perform the module load. So, even if we
13621da177e4SLinus Torvalds * succeeded in loading the module we have to
13631da177e4SLinus Torvalds * tell the caller to replay the request. We
13641da177e4SLinus Torvalds * indicate this using -EAGAIN.
13651da177e4SLinus Torvalds */
13661da177e4SLinus Torvalds if (a_o != NULL) {
1367d349f997SCong Wang module_put(a_o->owner);
1368d349f997SCong Wang return ERR_PTR(-EAGAIN);
13691da177e4SLinus Torvalds }
13701da177e4SLinus Torvalds #endif
137184ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to load TC action module");
1372d349f997SCong Wang return ERR_PTR(-ENOENT);
13731da177e4SLinus Torvalds }
13741da177e4SLinus Torvalds
1375d349f997SCong Wang return a_o;
1376d349f997SCong Wang }
1377d349f997SCong Wang
tcf_action_init_1(struct net * net,struct tcf_proto * tp,struct nlattr * nla,struct nlattr * est,struct tc_action_ops * a_o,int * init_res,u32 flags,struct netlink_ext_ack * extack)1378d349f997SCong Wang struct tc_action *tcf_action_init_1(struct net *net, struct tcf_proto *tp,
1379d349f997SCong Wang struct nlattr *nla, struct nlattr *est,
138087c750e8SVlad Buslov struct tc_action_ops *a_o, int *init_res,
1381695176bfSCong Wang u32 flags, struct netlink_ext_ack *extack)
1382d349f997SCong Wang {
1383695176bfSCong Wang bool police = flags & TCA_ACT_FLAGS_POLICE;
1384695176bfSCong Wang struct nla_bitfield32 userflags = { 0, 0 };
1385db4b4902SPaul Blakey struct tc_cookie *user_cookie = NULL;
1386d349f997SCong Wang u8 hw_stats = TCA_ACT_HW_STATS_ANY;
1387d349f997SCong Wang struct nlattr *tb[TCA_ACT_MAX + 1];
1388d349f997SCong Wang struct tc_action *a;
1389d349f997SCong Wang int err;
1390d349f997SCong Wang
13911da177e4SLinus Torvalds /* backward compatibility for policer */
1392695176bfSCong Wang if (!police) {
1393d349f997SCong Wang err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
1394d349f997SCong Wang tcf_action_policy, extack);
1395d349f997SCong Wang if (err < 0)
1396d349f997SCong Wang return ERR_PTR(err);
1397d349f997SCong Wang if (tb[TCA_ACT_COOKIE]) {
1398db4b4902SPaul Blakey user_cookie = nla_memdup_cookie(tb);
1399db4b4902SPaul Blakey if (!user_cookie) {
1400d349f997SCong Wang NL_SET_ERR_MSG(extack, "No memory to generate TC cookie");
1401d349f997SCong Wang err = -ENOMEM;
1402d349f997SCong Wang goto err_out;
1403d349f997SCong Wang }
1404d349f997SCong Wang }
1405d349f997SCong Wang hw_stats = tcf_action_hw_stats_get(tb[TCA_ACT_HW_STATS]);
14067adc5765SBaowen Zheng if (tb[TCA_ACT_FLAGS]) {
1407695176bfSCong Wang userflags = nla_get_bitfield32(tb[TCA_ACT_FLAGS]);
14087adc5765SBaowen Zheng if (!tc_act_flags_valid(userflags.value)) {
14097adc5765SBaowen Zheng err = -EINVAL;
14107adc5765SBaowen Zheng goto err_out;
14117adc5765SBaowen Zheng }
14127adc5765SBaowen Zheng }
1413d349f997SCong Wang
1414695176bfSCong Wang err = a_o->init(net, tb[TCA_ACT_OPTIONS], est, &a, tp,
1415695176bfSCong Wang userflags.value | flags, extack);
1416d349f997SCong Wang } else {
1417695176bfSCong Wang err = a_o->init(net, nla, est, &a, tp, userflags.value | flags,
1418695176bfSCong Wang extack);
1419d349f997SCong Wang }
1420ab27cfb8SPatrick McHardy if (err < 0)
1421d349f997SCong Wang goto err_out;
142287c750e8SVlad Buslov *init_res = err;
14231da177e4SLinus Torvalds
1424695176bfSCong Wang if (!police && tb[TCA_ACT_COOKIE])
1425db4b4902SPaul Blakey tcf_set_action_cookie(&a->user_cookie, user_cookie);
14261045ba77SJamal Hadi Salim
1427695176bfSCong Wang if (!police)
14280dfb2d82SJakub Kicinski a->hw_stats = hw_stats;
142944f86580SJiri Pirko
14301da177e4SLinus Torvalds return a;
14311da177e4SLinus Torvalds
1432d349f997SCong Wang err_out:
1433db4b4902SPaul Blakey if (user_cookie) {
1434db4b4902SPaul Blakey kfree(user_cookie->data);
1435db4b4902SPaul Blakey kfree(user_cookie);
1436e0535ce5SWolfgang Bumiller }
1437ab27cfb8SPatrick McHardy return ERR_PTR(err);
14381da177e4SLinus Torvalds }
14391da177e4SLinus Torvalds
tc_act_bind(u32 flags)14408cbfe939SBaowen Zheng static bool tc_act_bind(u32 flags)
14418cbfe939SBaowen Zheng {
14428cbfe939SBaowen Zheng return !!(flags & TCA_ACT_FLAGS_BIND);
14438cbfe939SBaowen Zheng }
14448cbfe939SBaowen Zheng
144590b73b77SVlad Buslov /* Returns numbers of initialized actions or negative error. */
144690b73b77SVlad Buslov
tcf_action_init(struct net * net,struct tcf_proto * tp,struct nlattr * nla,struct nlattr * est,struct tc_action * actions[],int init_res[],size_t * attr_size,u32 flags,u32 fl_flags,struct netlink_ext_ack * extack)14479fb9f251SJiri Pirko int tcf_action_init(struct net *net, struct tcf_proto *tp, struct nlattr *nla,
1448695176bfSCong Wang struct nlattr *est, struct tc_action *actions[],
1449c86e0209SBaowen Zheng int init_res[], size_t *attr_size,
1450c86e0209SBaowen Zheng u32 flags, u32 fl_flags,
1451695176bfSCong Wang struct netlink_ext_ack *extack)
14521da177e4SLinus Torvalds {
1453d349f997SCong Wang struct tc_action_ops *ops[TCA_ACT_MAX_PRIO] = {};
14547ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
145533be6271SWANG Cong struct tc_action *act;
14564e76e75dSRoman Mashak size_t sz = 0;
1457cee63723SPatrick McHardy int err;
14581da177e4SLinus Torvalds int i;
14591da177e4SLinus Torvalds
14608cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL,
14618cb08174SJohannes Berg extack);
1462cee63723SPatrick McHardy if (err < 0)
146333be6271SWANG Cong return err;
14641da177e4SLinus Torvalds
14657ba699c6SPatrick McHardy for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
1466d349f997SCong Wang struct tc_action_ops *a_o;
1467d349f997SCong Wang
1468695176bfSCong Wang a_o = tc_action_load_ops(tb[i], flags & TCA_ACT_FLAGS_POLICE,
1469695176bfSCong Wang !(flags & TCA_ACT_FLAGS_NO_RTNL),
1470695176bfSCong Wang extack);
1471d349f997SCong Wang if (IS_ERR(a_o)) {
1472d349f997SCong Wang err = PTR_ERR(a_o);
1473d349f997SCong Wang goto err_mod;
1474d349f997SCong Wang }
1475d349f997SCong Wang ops[i - 1] = a_o;
1476d349f997SCong Wang }
1477d349f997SCong Wang
1478d349f997SCong Wang for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
1479695176bfSCong Wang act = tcf_action_init_1(net, tp, tb[i], est, ops[i - 1],
1480695176bfSCong Wang &init_res[i - 1], flags, extack);
148133be6271SWANG Cong if (IS_ERR(act)) {
148233be6271SWANG Cong err = PTR_ERR(act);
14831da177e4SLinus Torvalds goto err;
14841da177e4SLinus Torvalds }
14854e76e75dSRoman Mashak sz += tcf_action_fill_size(act);
148690b73b77SVlad Buslov /* Start from index 0 */
148790b73b77SVlad Buslov actions[i - 1] = act;
1488c86e0209SBaowen Zheng if (tc_act_bind(flags)) {
1489c86e0209SBaowen Zheng bool skip_sw = tc_skip_sw(fl_flags);
1490c86e0209SBaowen Zheng bool skip_hw = tc_skip_hw(fl_flags);
1491c86e0209SBaowen Zheng
1492a5cf8670SVladimir Oltean if (tc_act_bind(act->tcfa_flags)) {
1493a5cf8670SVladimir Oltean /* Action is created by classifier and is not
1494a5cf8670SVladimir Oltean * standalone. Check that the user did not set
1495a5cf8670SVladimir Oltean * any action flags different than the
1496a5cf8670SVladimir Oltean * classifier flags, and inherit the flags from
1497a5cf8670SVladimir Oltean * the classifier for the compatibility case
1498a5cf8670SVladimir Oltean * where no flags were specified at all.
1499a5cf8670SVladimir Oltean */
1500a5cf8670SVladimir Oltean if ((tc_act_skip_sw(act->tcfa_flags) && !skip_sw) ||
1501a5cf8670SVladimir Oltean (tc_act_skip_hw(act->tcfa_flags) && !skip_hw)) {
1502a5cf8670SVladimir Oltean NL_SET_ERR_MSG(extack,
1503a5cf8670SVladimir Oltean "Mismatch between action and filter offload flags");
1504a5cf8670SVladimir Oltean err = -EINVAL;
1505a5cf8670SVladimir Oltean goto err;
1506a5cf8670SVladimir Oltean }
1507a5cf8670SVladimir Oltean if (skip_sw)
1508a5cf8670SVladimir Oltean act->tcfa_flags |= TCA_ACT_FLAGS_SKIP_SW;
1509a5cf8670SVladimir Oltean if (skip_hw)
1510a5cf8670SVladimir Oltean act->tcfa_flags |= TCA_ACT_FLAGS_SKIP_HW;
1511c86e0209SBaowen Zheng continue;
1512a5cf8670SVladimir Oltean }
1513a5cf8670SVladimir Oltean
1514a5cf8670SVladimir Oltean /* Action is standalone */
1515c86e0209SBaowen Zheng if (skip_sw != tc_act_skip_sw(act->tcfa_flags) ||
1516c86e0209SBaowen Zheng skip_hw != tc_act_skip_hw(act->tcfa_flags)) {
1517d922a99bSBaowen Zheng NL_SET_ERR_MSG(extack,
1518d922a99bSBaowen Zheng "Mismatch between action and filter offload flags");
1519c86e0209SBaowen Zheng err = -EINVAL;
1520c86e0209SBaowen Zheng goto err;
1521c86e0209SBaowen Zheng }
1522c86e0209SBaowen Zheng } else {
15237adc5765SBaowen Zheng err = tcf_action_offload_add(act, extack);
15247adc5765SBaowen Zheng if (tc_act_skip_sw(act->tcfa_flags) && err)
15257adc5765SBaowen Zheng goto err;
15267adc5765SBaowen Zheng }
152733be6271SWANG Cong }
1528aecc5cefSJamal Hadi Salim
15290fedc63fSCong Wang /* We have to commit them all together, because if any error happened in
15300fedc63fSCong Wang * between, we could not handle the failure gracefully.
15310fedc63fSCong Wang */
15320fedc63fSCong Wang tcf_idr_insert_many(actions);
15330fedc63fSCong Wang
15344e76e75dSRoman Mashak *attr_size = tcf_action_full_attrs_size(sz);
1535b3650bf7SVlad Buslov err = i - 1;
1536b3650bf7SVlad Buslov goto err_mod;
15371da177e4SLinus Torvalds
15381da177e4SLinus Torvalds err:
1539695176bfSCong Wang tcf_action_destroy(actions, flags & TCA_ACT_FLAGS_BIND);
1540d349f997SCong Wang err_mod:
1541d349f997SCong Wang for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
1542d349f997SCong Wang if (ops[i])
1543d349f997SCong Wang module_put(ops[i]->owner);
1544d349f997SCong Wang }
154533be6271SWANG Cong return err;
15461da177e4SLinus Torvalds }
15471da177e4SLinus Torvalds
tcf_action_update_stats(struct tc_action * a,u64 bytes,u64 packets,u64 drops,bool hw)15484b61d3e8SPo Liu void tcf_action_update_stats(struct tc_action *a, u64 bytes, u64 packets,
15494b61d3e8SPo Liu u64 drops, bool hw)
1550c8ecebd0SVlad Buslov {
15515e174d5eSVlad Buslov if (a->cpu_bstats) {
155250dc9a85SAhmed S. Darwish _bstats_update(this_cpu_ptr(a->cpu_bstats), bytes, packets);
1553c8ecebd0SVlad Buslov
15544b61d3e8SPo Liu this_cpu_ptr(a->cpu_qstats)->drops += drops;
1555c8ecebd0SVlad Buslov
1556c8ecebd0SVlad Buslov if (hw)
155750dc9a85SAhmed S. Darwish _bstats_update(this_cpu_ptr(a->cpu_bstats_hw),
1558c8ecebd0SVlad Buslov bytes, packets);
15595e174d5eSVlad Buslov return;
15605e174d5eSVlad Buslov }
15615e174d5eSVlad Buslov
15625e174d5eSVlad Buslov _bstats_update(&a->tcfa_bstats, bytes, packets);
15634b61d3e8SPo Liu a->tcfa_qstats.drops += drops;
15645e174d5eSVlad Buslov if (hw)
15655e174d5eSVlad Buslov _bstats_update(&a->tcfa_bstats_hw, bytes, packets);
1566c8ecebd0SVlad Buslov }
1567c8ecebd0SVlad Buslov EXPORT_SYMBOL(tcf_action_update_stats);
1568c8ecebd0SVlad Buslov
tcf_action_copy_stats(struct sk_buff * skb,struct tc_action * p,int compat_mode)1569ec0595ccSWANG Cong int tcf_action_copy_stats(struct sk_buff *skb, struct tc_action *p,
15701da177e4SLinus Torvalds int compat_mode)
15711da177e4SLinus Torvalds {
15721da177e4SLinus Torvalds int err = 0;
15731da177e4SLinus Torvalds struct gnet_dump d;
15741da177e4SLinus Torvalds
15757eb8896dSWANG Cong if (p == NULL)
15761da177e4SLinus Torvalds goto errout;
15771da177e4SLinus Torvalds
15781da177e4SLinus Torvalds /* compat_mode being true specifies a call that is supposed
157906fe9fb4SDirk Hohndel * to add additional backward compatibility statistic TLVs.
15801da177e4SLinus Torvalds */
15811da177e4SLinus Torvalds if (compat_mode) {
1582ec0595ccSWANG Cong if (p->type == TCA_OLD_COMPAT)
15831da177e4SLinus Torvalds err = gnet_stats_start_copy_compat(skb, 0,
15849854518eSNicolas Dichtel TCA_STATS,
15859854518eSNicolas Dichtel TCA_XSTATS,
1586ec0595ccSWANG Cong &p->tcfa_lock, &d,
15879854518eSNicolas Dichtel TCA_PAD);
15881da177e4SLinus Torvalds else
15891da177e4SLinus Torvalds return 0;
15901da177e4SLinus Torvalds } else
15911da177e4SLinus Torvalds err = gnet_stats_start_copy(skb, TCA_ACT_STATS,
1592ec0595ccSWANG Cong &p->tcfa_lock, &d, TCA_ACT_PAD);
15931da177e4SLinus Torvalds
15941da177e4SLinus Torvalds if (err < 0)
15951da177e4SLinus Torvalds goto errout;
15961da177e4SLinus Torvalds
159729cbcd85SAhmed S. Darwish if (gnet_stats_copy_basic(&d, p->cpu_bstats,
159829cbcd85SAhmed S. Darwish &p->tcfa_bstats, false) < 0 ||
159929cbcd85SAhmed S. Darwish gnet_stats_copy_basic_hw(&d, p->cpu_bstats_hw,
160029cbcd85SAhmed S. Darwish &p->tcfa_bstats_hw, false) < 0 ||
16011c0d32fdSEric Dumazet gnet_stats_copy_rate_est(&d, &p->tcfa_rate_est) < 0 ||
1602519c818eSEric Dumazet gnet_stats_copy_queue(&d, p->cpu_qstats,
1603ec0595ccSWANG Cong &p->tcfa_qstats,
1604ec0595ccSWANG Cong p->tcfa_qstats.qlen) < 0)
16051da177e4SLinus Torvalds goto errout;
16061da177e4SLinus Torvalds
16071da177e4SLinus Torvalds if (gnet_stats_finish_copy(&d) < 0)
16081da177e4SLinus Torvalds goto errout;
16091da177e4SLinus Torvalds
16101da177e4SLinus Torvalds return 0;
16111da177e4SLinus Torvalds
16121da177e4SLinus Torvalds errout:
16131da177e4SLinus Torvalds return -1;
16141da177e4SLinus Torvalds }
16151da177e4SLinus Torvalds
tca_get_fill(struct sk_buff * skb,struct tc_action * actions[],u32 portid,u32 seq,u16 flags,int event,int bind,int ref,struct netlink_ext_ack * extack)161690b73b77SVlad Buslov static int tca_get_fill(struct sk_buff *skb, struct tc_action *actions[],
16170b0f43feSJamal Hadi Salim u32 portid, u32 seq, u16 flags, int event, int bind,
16180349b877SHangbin Liu int ref, struct netlink_ext_ack *extack)
16191da177e4SLinus Torvalds {
16201da177e4SLinus Torvalds struct tcamsg *t;
16211da177e4SLinus Torvalds struct nlmsghdr *nlh;
162227a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb);
16234b3550efSPatrick McHardy struct nlattr *nest;
16241da177e4SLinus Torvalds
162515e47304SEric W. Biederman nlh = nlmsg_put(skb, portid, seq, event, sizeof(*t), flags);
16268b00a53cSDavid S. Miller if (!nlh)
16278b00a53cSDavid S. Miller goto out_nlmsg_trim;
16288b00a53cSDavid S. Miller t = nlmsg_data(nlh);
16291da177e4SLinus Torvalds t->tca_family = AF_UNSPEC;
16309ef1d4c7SPatrick McHardy t->tca__pad1 = 0;
16319ef1d4c7SPatrick McHardy t->tca__pad2 = 0;
16321da177e4SLinus Torvalds
16332f59823fSHangbin Liu if (extack && extack->_msg &&
16342f59823fSHangbin Liu nla_put_string(skb, TCA_ROOT_EXT_WARN_MSG, extack->_msg))
16352f59823fSHangbin Liu goto out_nlmsg_trim;
16362f59823fSHangbin Liu
1637ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_ACT_TAB);
16381af85155SAlexander Aring if (!nest)
16398b00a53cSDavid S. Miller goto out_nlmsg_trim;
16401da177e4SLinus Torvalds
1641ca44b738SVlad Buslov if (tcf_action_dump(skb, actions, bind, ref, false) < 0)
16428b00a53cSDavid S. Miller goto out_nlmsg_trim;
16431da177e4SLinus Torvalds
16448de2bd02SHangbin Liu nla_nest_end(skb, nest);
16458de2bd02SHangbin Liu
164627a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b;
16470349b877SHangbin Liu
16481da177e4SLinus Torvalds return skb->len;
16491da177e4SLinus Torvalds
16508b00a53cSDavid S. Miller out_nlmsg_trim:
1651dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b);
16521da177e4SLinus Torvalds return -1;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds
16551da177e4SLinus Torvalds static int
tcf_get_notify(struct net * net,u32 portid,struct nlmsghdr * n,struct tc_action * actions[],int event,struct netlink_ext_ack * extack)1656c4c4290cSRoman Mashak tcf_get_notify(struct net *net, u32 portid, struct nlmsghdr *n,
165790b73b77SVlad Buslov struct tc_action *actions[], int event,
165884ae017aSAlexander Aring struct netlink_ext_ack *extack)
16591da177e4SLinus Torvalds {
16601da177e4SLinus Torvalds struct sk_buff *skb;
16611da177e4SLinus Torvalds
16621da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
16631da177e4SLinus Torvalds if (!skb)
16641da177e4SLinus Torvalds return -ENOBUFS;
16650b0f43feSJamal Hadi Salim if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, event,
16660349b877SHangbin Liu 0, 1, NULL) <= 0) {
166784ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
16681da177e4SLinus Torvalds kfree_skb(skb);
16691da177e4SLinus Torvalds return -EINVAL;
16701da177e4SLinus Torvalds }
16712942e900SThomas Graf
167215e47304SEric W. Biederman return rtnl_unicast(skb, net, portid);
16731da177e4SLinus Torvalds }
16741da177e4SLinus Torvalds
tcf_action_get_1(struct net * net,struct nlattr * nla,struct nlmsghdr * n,u32 portid,struct netlink_ext_ack * extack)1675ddf97ccdSWANG Cong static struct tc_action *tcf_action_get_1(struct net *net, struct nlattr *nla,
167684ae017aSAlexander Aring struct nlmsghdr *n, u32 portid,
167784ae017aSAlexander Aring struct netlink_ext_ack *extack)
16781da177e4SLinus Torvalds {
16797ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1];
1680a85a970aSWANG Cong const struct tc_action_ops *ops;
16811da177e4SLinus Torvalds struct tc_action *a;
16821da177e4SLinus Torvalds int index;
1683ab27cfb8SPatrick McHardy int err;
16841da177e4SLinus Torvalds
1685199ce850SCong Wang err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
1686199ce850SCong Wang tcf_action_policy, extack);
1687cee63723SPatrick McHardy if (err < 0)
1688ab27cfb8SPatrick McHardy goto err_out;
16891da177e4SLinus Torvalds
1690cee63723SPatrick McHardy err = -EINVAL;
16917ba699c6SPatrick McHardy if (tb[TCA_ACT_INDEX] == NULL ||
169284ae017aSAlexander Aring nla_len(tb[TCA_ACT_INDEX]) < sizeof(index)) {
169384ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid TC action index value");
1694ab27cfb8SPatrick McHardy goto err_out;
169584ae017aSAlexander Aring }
16961587bac4SPatrick McHardy index = nla_get_u32(tb[TCA_ACT_INDEX]);
16971da177e4SLinus Torvalds
1698ab27cfb8SPatrick McHardy err = -EINVAL;
1699a85a970aSWANG Cong ops = tc_lookup_action(tb[TCA_ACT_KIND]);
170084ae017aSAlexander Aring if (!ops) { /* could happen in batch of actions */
1701f061b48cSCong Wang NL_SET_ERR_MSG(extack, "Specified TC action kind not found");
1702a85a970aSWANG Cong goto err_out;
170384ae017aSAlexander Aring }
1704ab27cfb8SPatrick McHardy err = -ENOENT;
1705fae52d93SZhengchao Shao if (__tcf_idr_search(net, ops, &a, index) == 0) {
1706f061b48cSCong Wang NL_SET_ERR_MSG(extack, "TC action with specified index not found");
17071da177e4SLinus Torvalds goto err_mod;
1708f061b48cSCong Wang }
17091da177e4SLinus Torvalds
1710a85a970aSWANG Cong module_put(ops->owner);
17111da177e4SLinus Torvalds return a;
1712ab27cfb8SPatrick McHardy
17131da177e4SLinus Torvalds err_mod:
1714a85a970aSWANG Cong module_put(ops->owner);
1715ab27cfb8SPatrick McHardy err_out:
1716ab27cfb8SPatrick McHardy return ERR_PTR(err);
17171da177e4SLinus Torvalds }
17181da177e4SLinus Torvalds
tca_action_flush(struct net * net,struct nlattr * nla,struct nlmsghdr * n,u32 portid,struct netlink_ext_ack * extack)17197316ae88STom Goff static int tca_action_flush(struct net *net, struct nlattr *nla,
172084ae017aSAlexander Aring struct nlmsghdr *n, u32 portid,
172184ae017aSAlexander Aring struct netlink_ext_ack *extack)
17221da177e4SLinus Torvalds {
17231da177e4SLinus Torvalds struct sk_buff *skb;
17241da177e4SLinus Torvalds unsigned char *b;
17251da177e4SLinus Torvalds struct nlmsghdr *nlh;
17261da177e4SLinus Torvalds struct tcamsg *t;
17271da177e4SLinus Torvalds struct netlink_callback dcb;
17284b3550efSPatrick McHardy struct nlattr *nest;
17297ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX + 1];
1730a85a970aSWANG Cong const struct tc_action_ops *ops;
17317ba699c6SPatrick McHardy struct nlattr *kind;
173236723873SJamal Hadi Salim int err = -ENOMEM;
17331da177e4SLinus Torvalds
17341da177e4SLinus Torvalds skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
173584ae017aSAlexander Aring if (!skb)
173636723873SJamal Hadi Salim return err;
17371da177e4SLinus Torvalds
173827a884dcSArnaldo Carvalho de Melo b = skb_tail_pointer(skb);
17391da177e4SLinus Torvalds
1740199ce850SCong Wang err = nla_parse_nested_deprecated(tb, TCA_ACT_MAX, nla,
1741199ce850SCong Wang tcf_action_policy, extack);
1742cee63723SPatrick McHardy if (err < 0)
17431da177e4SLinus Torvalds goto err_out;
17441da177e4SLinus Torvalds
1745cee63723SPatrick McHardy err = -EINVAL;
17467ba699c6SPatrick McHardy kind = tb[TCA_ACT_KIND];
1747a85a970aSWANG Cong ops = tc_lookup_action(kind);
174884ae017aSAlexander Aring if (!ops) { /*some idjot trying to flush unknown action */
174984ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Cannot flush unknown TC action");
17501da177e4SLinus Torvalds goto err_out;
175184ae017aSAlexander Aring }
17521da177e4SLinus Torvalds
17530b0f43feSJamal Hadi Salim nlh = nlmsg_put(skb, portid, n->nlmsg_seq, RTM_DELACTION,
17540b0f43feSJamal Hadi Salim sizeof(*t), 0);
175584ae017aSAlexander Aring if (!nlh) {
175684ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to create TC action flush notification");
17578b00a53cSDavid S. Miller goto out_module_put;
175884ae017aSAlexander Aring }
17598b00a53cSDavid S. Miller t = nlmsg_data(nlh);
17601da177e4SLinus Torvalds t->tca_family = AF_UNSPEC;
17619ef1d4c7SPatrick McHardy t->tca__pad1 = 0;
17629ef1d4c7SPatrick McHardy t->tca__pad2 = 0;
17631da177e4SLinus Torvalds
1764ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_ACT_TAB);
176584ae017aSAlexander Aring if (!nest) {
176684ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to add new netlink message");
17678b00a53cSDavid S. Miller goto out_module_put;
176884ae017aSAlexander Aring }
17691da177e4SLinus Torvalds
1770fae52d93SZhengchao Shao err = __tcf_generic_walker(net, skb, &dcb, RTM_DELACTION, ops, extack);
177166dede2dSDavide Caratti if (err <= 0) {
177266dede2dSDavide Caratti nla_nest_cancel(skb, nest);
17738b00a53cSDavid S. Miller goto out_module_put;
177466dede2dSDavide Caratti }
17751da177e4SLinus Torvalds
17764b3550efSPatrick McHardy nla_nest_end(skb, nest);
17771da177e4SLinus Torvalds
177827a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b;
17791da177e4SLinus Torvalds nlh->nlmsg_flags |= NLM_F_ROOT;
1780a85a970aSWANG Cong module_put(ops->owner);
178115e47304SEric W. Biederman err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1782cc7ec456SEric Dumazet n->nlmsg_flags & NLM_F_ECHO);
178384ae017aSAlexander Aring if (err < 0)
178484ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to send TC action flush notification");
17851da177e4SLinus Torvalds
17861da177e4SLinus Torvalds return err;
17871da177e4SLinus Torvalds
17888b00a53cSDavid S. Miller out_module_put:
1789a85a970aSWANG Cong module_put(ops->owner);
17901da177e4SLinus Torvalds err_out:
17911da177e4SLinus Torvalds kfree_skb(skb);
17921da177e4SLinus Torvalds return err;
17931da177e4SLinus Torvalds }
17941da177e4SLinus Torvalds
tcf_action_delete(struct net * net,struct tc_action * actions[])1795b144e7ecSCong Wang static int tcf_action_delete(struct net *net, struct tc_action *actions[])
179616af6067SVlad Buslov {
179797a3f84fSCong Wang int i;
179816af6067SVlad Buslov
179990b73b77SVlad Buslov for (i = 0; i < TCA_ACT_MAX_PRIO && actions[i]; i++) {
180090b73b77SVlad Buslov struct tc_action *a = actions[i];
180116af6067SVlad Buslov const struct tc_action_ops *ops = a->ops;
180216af6067SVlad Buslov /* Actions can be deleted concurrently so we must save their
180316af6067SVlad Buslov * type and id to search again after reference is released.
180416af6067SVlad Buslov */
180597a3f84fSCong Wang struct tcf_idrinfo *idrinfo = a->idrinfo;
180697a3f84fSCong Wang u32 act_index = a->tcfa_index;
180716af6067SVlad Buslov
1808c10bbfaeSVlad Buslov actions[i] = NULL;
180916af6067SVlad Buslov if (tcf_action_put(a)) {
181016af6067SVlad Buslov /* last reference, action was deleted concurrently */
181116af6067SVlad Buslov module_put(ops->owner);
181216af6067SVlad Buslov } else {
181397a3f84fSCong Wang int ret;
181497a3f84fSCong Wang
181516af6067SVlad Buslov /* now do the delete */
181697a3f84fSCong Wang ret = tcf_idr_delete_index(idrinfo, act_index);
1817edfaf94fSCong Wang if (ret < 0)
181816af6067SVlad Buslov return ret;
181916af6067SVlad Buslov }
182016af6067SVlad Buslov }
182116af6067SVlad Buslov return 0;
182216af6067SVlad Buslov }
182316af6067SVlad Buslov
18241da177e4SLinus Torvalds static int
tcf_reoffload_del_notify(struct net * net,struct tc_action * action)182513926d19SBaowen Zheng tcf_reoffload_del_notify(struct net *net, struct tc_action *action)
182613926d19SBaowen Zheng {
182713926d19SBaowen Zheng size_t attr_size = tcf_action_fill_size(action);
182813926d19SBaowen Zheng struct tc_action *actions[TCA_ACT_MAX_PRIO] = {
182913926d19SBaowen Zheng [0] = action,
183013926d19SBaowen Zheng };
183113926d19SBaowen Zheng const struct tc_action_ops *ops = action->ops;
183213926d19SBaowen Zheng struct sk_buff *skb;
183313926d19SBaowen Zheng int ret;
183413926d19SBaowen Zheng
183513926d19SBaowen Zheng skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
183613926d19SBaowen Zheng GFP_KERNEL);
183713926d19SBaowen Zheng if (!skb)
183813926d19SBaowen Zheng return -ENOBUFS;
183913926d19SBaowen Zheng
18400349b877SHangbin Liu if (tca_get_fill(skb, actions, 0, 0, 0, RTM_DELACTION, 0, 1, NULL) <= 0) {
184113926d19SBaowen Zheng kfree_skb(skb);
184213926d19SBaowen Zheng return -EINVAL;
184313926d19SBaowen Zheng }
184413926d19SBaowen Zheng
184513926d19SBaowen Zheng ret = tcf_idr_release_unsafe(action);
184613926d19SBaowen Zheng if (ret == ACT_P_DELETED) {
184713926d19SBaowen Zheng module_put(ops->owner);
184813926d19SBaowen Zheng ret = rtnetlink_send(skb, net, 0, RTNLGRP_TC, 0);
184913926d19SBaowen Zheng } else {
185013926d19SBaowen Zheng kfree_skb(skb);
185113926d19SBaowen Zheng }
185213926d19SBaowen Zheng
185313926d19SBaowen Zheng return ret;
185413926d19SBaowen Zheng }
185513926d19SBaowen Zheng
tcf_action_reoffload_cb(flow_indr_block_bind_cb_t * cb,void * cb_priv,bool add)185613926d19SBaowen Zheng int tcf_action_reoffload_cb(flow_indr_block_bind_cb_t *cb,
185713926d19SBaowen Zheng void *cb_priv, bool add)
185813926d19SBaowen Zheng {
185913926d19SBaowen Zheng struct tc_act_pernet_id *id_ptr;
186013926d19SBaowen Zheng struct tcf_idrinfo *idrinfo;
186113926d19SBaowen Zheng struct tc_action_net *tn;
186213926d19SBaowen Zheng struct tc_action *p;
186313926d19SBaowen Zheng unsigned int act_id;
186413926d19SBaowen Zheng unsigned long tmp;
186513926d19SBaowen Zheng unsigned long id;
186613926d19SBaowen Zheng struct idr *idr;
186713926d19SBaowen Zheng struct net *net;
186813926d19SBaowen Zheng int ret;
186913926d19SBaowen Zheng
187013926d19SBaowen Zheng if (!cb)
187113926d19SBaowen Zheng return -EINVAL;
187213926d19SBaowen Zheng
187313926d19SBaowen Zheng down_read(&net_rwsem);
187413926d19SBaowen Zheng mutex_lock(&act_id_mutex);
187513926d19SBaowen Zheng
187613926d19SBaowen Zheng for_each_net(net) {
187713926d19SBaowen Zheng list_for_each_entry(id_ptr, &act_pernet_id_list, list) {
187813926d19SBaowen Zheng act_id = id_ptr->id;
187913926d19SBaowen Zheng tn = net_generic(net, act_id);
188013926d19SBaowen Zheng if (!tn)
188113926d19SBaowen Zheng continue;
188213926d19SBaowen Zheng idrinfo = tn->idrinfo;
188313926d19SBaowen Zheng if (!idrinfo)
188413926d19SBaowen Zheng continue;
188513926d19SBaowen Zheng
188613926d19SBaowen Zheng mutex_lock(&idrinfo->lock);
188713926d19SBaowen Zheng idr = &idrinfo->action_idr;
188813926d19SBaowen Zheng idr_for_each_entry_ul(idr, p, tmp, id) {
188913926d19SBaowen Zheng if (IS_ERR(p) || tc_act_bind(p->tcfa_flags))
189013926d19SBaowen Zheng continue;
189113926d19SBaowen Zheng if (add) {
189213926d19SBaowen Zheng tcf_action_offload_add_ex(p, NULL, cb,
189313926d19SBaowen Zheng cb_priv);
189413926d19SBaowen Zheng continue;
189513926d19SBaowen Zheng }
189613926d19SBaowen Zheng
189713926d19SBaowen Zheng /* cb unregister to update hw count */
189813926d19SBaowen Zheng ret = tcf_action_offload_del_ex(p, cb, cb_priv);
189913926d19SBaowen Zheng if (ret < 0)
190013926d19SBaowen Zheng continue;
190113926d19SBaowen Zheng if (tc_act_skip_sw(p->tcfa_flags) &&
190213926d19SBaowen Zheng !tc_act_in_hw(p))
190313926d19SBaowen Zheng tcf_reoffload_del_notify(net, p);
190413926d19SBaowen Zheng }
190513926d19SBaowen Zheng mutex_unlock(&idrinfo->lock);
190613926d19SBaowen Zheng }
190713926d19SBaowen Zheng }
190813926d19SBaowen Zheng mutex_unlock(&act_id_mutex);
190913926d19SBaowen Zheng up_read(&net_rwsem);
191013926d19SBaowen Zheng
191113926d19SBaowen Zheng return 0;
191213926d19SBaowen Zheng }
191313926d19SBaowen Zheng
191413926d19SBaowen Zheng static int
tcf_del_notify(struct net * net,struct nlmsghdr * n,struct tc_action * actions[],u32 portid,size_t attr_size,struct netlink_ext_ack * extack)191590b73b77SVlad Buslov tcf_del_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
1916edfaf94fSCong Wang u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
1917a56e1953SWANG Cong {
1918a56e1953SWANG Cong int ret;
1919a56e1953SWANG Cong struct sk_buff *skb;
1920a56e1953SWANG Cong
1921d04e6990SRoman Mashak skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
1922d04e6990SRoman Mashak GFP_KERNEL);
1923a56e1953SWANG Cong if (!skb)
1924a56e1953SWANG Cong return -ENOBUFS;
1925a56e1953SWANG Cong
1926a56e1953SWANG Cong if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, 0, RTM_DELACTION,
19270349b877SHangbin Liu 0, 2, extack) <= 0) {
192884ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to fill netlink TC action attributes");
1929a56e1953SWANG Cong kfree_skb(skb);
1930a56e1953SWANG Cong return -EINVAL;
1931a56e1953SWANG Cong }
1932a56e1953SWANG Cong
1933a56e1953SWANG Cong /* now do the delete */
1934b144e7ecSCong Wang ret = tcf_action_delete(net, actions);
193555334a5dSWANG Cong if (ret < 0) {
193684ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Failed to delete TC action");
193755334a5dSWANG Cong kfree_skb(skb);
193855334a5dSWANG Cong return ret;
193955334a5dSWANG Cong }
1940a56e1953SWANG Cong
1941a56e1953SWANG Cong ret = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
1942a56e1953SWANG Cong n->nlmsg_flags & NLM_F_ECHO);
1943a56e1953SWANG Cong return ret;
1944a56e1953SWANG Cong }
1945a56e1953SWANG Cong
1946a56e1953SWANG Cong static int
tca_action_gd(struct net * net,struct nlattr * nla,struct nlmsghdr * n,u32 portid,int event,struct netlink_ext_ack * extack)19477316ae88STom Goff tca_action_gd(struct net *net, struct nlattr *nla, struct nlmsghdr *n,
194884ae017aSAlexander Aring u32 portid, int event, struct netlink_ext_ack *extack)
19491da177e4SLinus Torvalds {
1950cee63723SPatrick McHardy int i, ret;
19517ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
195233be6271SWANG Cong struct tc_action *act;
1953d04e6990SRoman Mashak size_t attr_size = 0;
1954edfaf94fSCong Wang struct tc_action *actions[TCA_ACT_MAX_PRIO] = {};
19551da177e4SLinus Torvalds
19568cb08174SJohannes Berg ret = nla_parse_nested_deprecated(tb, TCA_ACT_MAX_PRIO, nla, NULL,
19578cb08174SJohannes Berg extack);
1958cee63723SPatrick McHardy if (ret < 0)
1959cee63723SPatrick McHardy return ret;
19601da177e4SLinus Torvalds
19611da177e4SLinus Torvalds if (event == RTM_DELACTION && n->nlmsg_flags & NLM_F_ROOT) {
19621af85155SAlexander Aring if (tb[1])
196384ae017aSAlexander Aring return tca_action_flush(net, tb[1], n, portid, extack);
19641af85155SAlexander Aring
196584ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Invalid netlink attributes while flushing TC action");
1966f97017cdSJamal Hadi Salim return -EINVAL;
19671da177e4SLinus Torvalds }
19681da177e4SLinus Torvalds
19697ba699c6SPatrick McHardy for (i = 1; i <= TCA_ACT_MAX_PRIO && tb[i]; i++) {
197084ae017aSAlexander Aring act = tcf_action_get_1(net, tb[i], n, portid, extack);
1971ab27cfb8SPatrick McHardy if (IS_ERR(act)) {
1972ab27cfb8SPatrick McHardy ret = PTR_ERR(act);
19731da177e4SLinus Torvalds goto err;
1974ab27cfb8SPatrick McHardy }
19754e76e75dSRoman Mashak attr_size += tcf_action_fill_size(act);
197690b73b77SVlad Buslov actions[i - 1] = act;
19771da177e4SLinus Torvalds }
19781da177e4SLinus Torvalds
19794e76e75dSRoman Mashak attr_size = tcf_action_full_attrs_size(attr_size);
19804e76e75dSRoman Mashak
19811da177e4SLinus Torvalds if (event == RTM_GETACTION)
198290b73b77SVlad Buslov ret = tcf_get_notify(net, portid, n, actions, event, extack);
19831da177e4SLinus Torvalds else { /* delete */
1984edfaf94fSCong Wang ret = tcf_del_notify(net, n, actions, portid, attr_size, extack);
1985a56e1953SWANG Cong if (ret)
19861da177e4SLinus Torvalds goto err;
1987edfaf94fSCong Wang return 0;
19881da177e4SLinus Torvalds }
19891da177e4SLinus Torvalds err:
1990edfaf94fSCong Wang tcf_action_put_many(actions);
19911da177e4SLinus Torvalds return ret;
19921da177e4SLinus Torvalds }
19931da177e4SLinus Torvalds
1994a56e1953SWANG Cong static int
tcf_add_notify(struct net * net,struct nlmsghdr * n,struct tc_action * actions[],u32 portid,size_t attr_size,struct netlink_ext_ack * extack)199590b73b77SVlad Buslov tcf_add_notify(struct net *net, struct nlmsghdr *n, struct tc_action *actions[],
1996d04e6990SRoman Mashak u32 portid, size_t attr_size, struct netlink_ext_ack *extack)
19971da177e4SLinus Torvalds {
19981da177e4SLinus Torvalds struct sk_buff *skb;
19991da177e4SLinus Torvalds
2000d04e6990SRoman Mashak skb = alloc_skb(attr_size <= NLMSG_GOODSIZE ? NLMSG_GOODSIZE : attr_size,
2001d04e6990SRoman Mashak GFP_KERNEL);
20021da177e4SLinus Torvalds if (!skb)
20031da177e4SLinus Torvalds return -ENOBUFS;
20041da177e4SLinus Torvalds
2005a56e1953SWANG Cong if (tca_get_fill(skb, actions, portid, n->nlmsg_seq, n->nlmsg_flags,
20060349b877SHangbin Liu RTM_NEWACTION, 0, 0, extack) <= 0) {
2007d143b9e3SRoman Mashak NL_SET_ERR_MSG(extack, "Failed to fill netlink attributes while adding TC action");
2008a56e1953SWANG Cong kfree_skb(skb);
2009a56e1953SWANG Cong return -EINVAL;
2010a56e1953SWANG Cong }
20111da177e4SLinus Torvalds
2012f79a3bcbSYajun Deng return rtnetlink_send(skb, net, portid, RTNLGRP_TC,
2013a56e1953SWANG Cong n->nlmsg_flags & NLM_F_ECHO);
20141da177e4SLinus Torvalds }
20151da177e4SLinus Torvalds
tcf_action_add(struct net * net,struct nlattr * nla,struct nlmsghdr * n,u32 portid,u32 flags,struct netlink_ext_ack * extack)20165a7a5555SJamal Hadi Salim static int tcf_action_add(struct net *net, struct nlattr *nla,
2017695176bfSCong Wang struct nlmsghdr *n, u32 portid, u32 flags,
2018aea0d727SAlexander Aring struct netlink_ext_ack *extack)
20191da177e4SLinus Torvalds {
2020d04e6990SRoman Mashak size_t attr_size = 0;
202187c750e8SVlad Buslov int loop, ret, i;
202290b73b77SVlad Buslov struct tc_action *actions[TCA_ACT_MAX_PRIO] = {};
202387c750e8SVlad Buslov int init_res[TCA_ACT_MAX_PRIO] = {};
20241da177e4SLinus Torvalds
202539f13ea2SEric Dumazet for (loop = 0; loop < 10; loop++) {
2026695176bfSCong Wang ret = tcf_action_init(net, NULL, nla, NULL, actions, init_res,
2027c86e0209SBaowen Zheng &attr_size, flags, 0, extack);
202839f13ea2SEric Dumazet if (ret != -EAGAIN)
202939f13ea2SEric Dumazet break;
203039f13ea2SEric Dumazet }
203139f13ea2SEric Dumazet
203290b73b77SVlad Buslov if (ret < 0)
20331da177e4SLinus Torvalds return ret;
203490b73b77SVlad Buslov ret = tcf_add_notify(net, n, actions, portid, attr_size, extack);
203587c750e8SVlad Buslov
203687c750e8SVlad Buslov /* only put existing actions */
203787c750e8SVlad Buslov for (i = 0; i < TCA_ACT_MAX_PRIO; i++)
203887c750e8SVlad Buslov if (init_res[i] == ACT_P_CREATED)
203987c750e8SVlad Buslov actions[i] = NULL;
204090b73b77SVlad Buslov tcf_action_put_many(actions);
2041f07fed82SWANG Cong
2042cae422f3SVlad Buslov return ret;
20431da177e4SLinus Torvalds }
20441da177e4SLinus Torvalds
204590825b23SJamal Hadi Salim static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = {
2046f460019bSVlad Buslov [TCA_ROOT_FLAGS] = NLA_POLICY_BITFIELD32(TCA_ACT_FLAG_LARGE_DUMP_ON |
2047f460019bSVlad Buslov TCA_ACT_FLAG_TERSE_DUMP),
2048e62e484dSJamal Hadi Salim [TCA_ROOT_TIME_DELTA] = { .type = NLA_U32 },
204990825b23SJamal Hadi Salim };
205090825b23SJamal Hadi Salim
tc_ctl_action(struct sk_buff * skb,struct nlmsghdr * n,struct netlink_ext_ack * extack)2051c21ef3e3SDavid Ahern static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n,
2052c21ef3e3SDavid Ahern struct netlink_ext_ack *extack)
20531da177e4SLinus Torvalds {
20543b1e0a65SYOSHIFUJI Hideaki struct net *net = sock_net(skb->sk);
205590825b23SJamal Hadi Salim struct nlattr *tca[TCA_ROOT_MAX + 1];
20568bf15395SGaurav Singh u32 portid = NETLINK_CB(skb).portid;
2057695176bfSCong Wang u32 flags = 0;
2058695176bfSCong Wang int ret = 0;
20591da177e4SLinus Torvalds
20600b0f43feSJamal Hadi Salim if ((n->nlmsg_type != RTM_GETACTION) &&
20610b0f43feSJamal Hadi Salim !netlink_capable(skb, CAP_NET_ADMIN))
2062dfc47ef8SEric W. Biederman return -EPERM;
2063dfc47ef8SEric W. Biederman
20648cb08174SJohannes Berg ret = nlmsg_parse_deprecated(n, sizeof(struct tcamsg), tca,
20658cb08174SJohannes Berg TCA_ROOT_MAX, NULL, extack);
20667ba699c6SPatrick McHardy if (ret < 0)
20677ba699c6SPatrick McHardy return ret;
20687ba699c6SPatrick McHardy
20697ba699c6SPatrick McHardy if (tca[TCA_ACT_TAB] == NULL) {
207084ae017aSAlexander Aring NL_SET_ERR_MSG(extack, "Netlink action attributes missing");
20711da177e4SLinus Torvalds return -EINVAL;
20721da177e4SLinus Torvalds }
20731da177e4SLinus Torvalds
2074cc7ec456SEric Dumazet /* n->nlmsg_flags & NLM_F_CREATE */
20751da177e4SLinus Torvalds switch (n->nlmsg_type) {
20761da177e4SLinus Torvalds case RTM_NEWACTION:
20771da177e4SLinus Torvalds /* we are going to assume all other flags
207825985edcSLucas De Marchi * imply create only if it doesn't exist
20791da177e4SLinus Torvalds * Note that CREATE | EXCL implies that
20801da177e4SLinus Torvalds * but since we want avoid ambiguity (eg when flags
20811da177e4SLinus Torvalds * is zero) then just set this
20821da177e4SLinus Torvalds */
20831da177e4SLinus Torvalds if (n->nlmsg_flags & NLM_F_REPLACE)
2084695176bfSCong Wang flags = TCA_ACT_FLAGS_REPLACE;
2085695176bfSCong Wang ret = tcf_action_add(net, tca[TCA_ACT_TAB], n, portid, flags,
2086aea0d727SAlexander Aring extack);
20871da177e4SLinus Torvalds break;
20881da177e4SLinus Torvalds case RTM_DELACTION:
20897316ae88STom Goff ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
209084ae017aSAlexander Aring portid, RTM_DELACTION, extack);
20911da177e4SLinus Torvalds break;
20921da177e4SLinus Torvalds case RTM_GETACTION:
20937316ae88STom Goff ret = tca_action_gd(net, tca[TCA_ACT_TAB], n,
209484ae017aSAlexander Aring portid, RTM_GETACTION, extack);
20951da177e4SLinus Torvalds break;
20961da177e4SLinus Torvalds default:
20971da177e4SLinus Torvalds BUG();
20981da177e4SLinus Torvalds }
20991da177e4SLinus Torvalds
21001da177e4SLinus Torvalds return ret;
21011da177e4SLinus Torvalds }
21021da177e4SLinus Torvalds
find_dump_kind(struct nlattr ** nla)210390825b23SJamal Hadi Salim static struct nlattr *find_dump_kind(struct nlattr **nla)
21041da177e4SLinus Torvalds {
21057ba699c6SPatrick McHardy struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1];
21067ba699c6SPatrick McHardy struct nlattr *tb[TCA_ACT_MAX_PRIO + 1];
21077ba699c6SPatrick McHardy struct nlattr *kind;
21081da177e4SLinus Torvalds
21097ba699c6SPatrick McHardy tb1 = nla[TCA_ACT_TAB];
21101da177e4SLinus Torvalds if (tb1 == NULL)
21111da177e4SLinus Torvalds return NULL;
21121da177e4SLinus Torvalds
21138cb08174SJohannes Berg if (nla_parse_deprecated(tb, TCA_ACT_MAX_PRIO, nla_data(tb1), NLMSG_ALIGN(nla_len(tb1)), NULL, NULL) < 0)
21141da177e4SLinus Torvalds return NULL;
21151da177e4SLinus Torvalds
21166d834e04SPatrick McHardy if (tb[1] == NULL)
21176d834e04SPatrick McHardy return NULL;
2118199ce850SCong Wang if (nla_parse_nested_deprecated(tb2, TCA_ACT_MAX, tb[1], tcf_action_policy, NULL) < 0)
21191da177e4SLinus Torvalds return NULL;
21207ba699c6SPatrick McHardy kind = tb2[TCA_ACT_KIND];
21211da177e4SLinus Torvalds
212226dab893SThomas Graf return kind;
21231da177e4SLinus Torvalds }
21241da177e4SLinus Torvalds
tc_dump_action(struct sk_buff * skb,struct netlink_callback * cb)21255a7a5555SJamal Hadi Salim static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb)
21261da177e4SLinus Torvalds {
2127ddf97ccdSWANG Cong struct net *net = sock_net(skb->sk);
21281da177e4SLinus Torvalds struct nlmsghdr *nlh;
212927a884dcSArnaldo Carvalho de Melo unsigned char *b = skb_tail_pointer(skb);
21304b3550efSPatrick McHardy struct nlattr *nest;
21311da177e4SLinus Torvalds struct tc_action_ops *a_o;
21321da177e4SLinus Torvalds int ret = 0;
21338b00a53cSDavid S. Miller struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh);
213490825b23SJamal Hadi Salim struct nlattr *tb[TCA_ROOT_MAX + 1];
213590825b23SJamal Hadi Salim struct nlattr *count_attr = NULL;
2136e62e484dSJamal Hadi Salim unsigned long jiffy_since = 0;
213790825b23SJamal Hadi Salim struct nlattr *kind = NULL;
213890825b23SJamal Hadi Salim struct nla_bitfield32 bf;
2139e62e484dSJamal Hadi Salim u32 msecs_since = 0;
214090825b23SJamal Hadi Salim u32 act_count = 0;
21411da177e4SLinus Torvalds
21428cb08174SJohannes Berg ret = nlmsg_parse_deprecated(cb->nlh, sizeof(struct tcamsg), tb,
21438cb08174SJohannes Berg TCA_ROOT_MAX, tcaa_policy, cb->extack);
214490825b23SJamal Hadi Salim if (ret < 0)
214590825b23SJamal Hadi Salim return ret;
214690825b23SJamal Hadi Salim
214790825b23SJamal Hadi Salim kind = find_dump_kind(tb);
21481da177e4SLinus Torvalds if (kind == NULL) {
21496ff9c364Sstephen hemminger pr_info("tc_dump_action: action bad kind\n");
21501da177e4SLinus Torvalds return 0;
21511da177e4SLinus Torvalds }
21521da177e4SLinus Torvalds
215326dab893SThomas Graf a_o = tc_lookup_action(kind);
2154cc7ec456SEric Dumazet if (a_o == NULL)
21551da177e4SLinus Torvalds return 0;
21561da177e4SLinus Torvalds
215790825b23SJamal Hadi Salim cb->args[2] = 0;
215890825b23SJamal Hadi Salim if (tb[TCA_ROOT_FLAGS]) {
215990825b23SJamal Hadi Salim bf = nla_get_bitfield32(tb[TCA_ROOT_FLAGS]);
216090825b23SJamal Hadi Salim cb->args[2] = bf.value;
216190825b23SJamal Hadi Salim }
216290825b23SJamal Hadi Salim
2163e62e484dSJamal Hadi Salim if (tb[TCA_ROOT_TIME_DELTA]) {
2164e62e484dSJamal Hadi Salim msecs_since = nla_get_u32(tb[TCA_ROOT_TIME_DELTA]);
2165e62e484dSJamal Hadi Salim }
2166e62e484dSJamal Hadi Salim
216715e47304SEric W. Biederman nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
21688b00a53cSDavid S. Miller cb->nlh->nlmsg_type, sizeof(*t), 0);
21698b00a53cSDavid S. Miller if (!nlh)
21708b00a53cSDavid S. Miller goto out_module_put;
217190825b23SJamal Hadi Salim
2172e62e484dSJamal Hadi Salim if (msecs_since)
2173e62e484dSJamal Hadi Salim jiffy_since = jiffies - msecs_to_jiffies(msecs_since);
2174e62e484dSJamal Hadi Salim
21758b00a53cSDavid S. Miller t = nlmsg_data(nlh);
21761da177e4SLinus Torvalds t->tca_family = AF_UNSPEC;
21779ef1d4c7SPatrick McHardy t->tca__pad1 = 0;
21789ef1d4c7SPatrick McHardy t->tca__pad2 = 0;
2179e62e484dSJamal Hadi Salim cb->args[3] = jiffy_since;
218090825b23SJamal Hadi Salim count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32));
218190825b23SJamal Hadi Salim if (!count_attr)
218290825b23SJamal Hadi Salim goto out_module_put;
21831da177e4SLinus Torvalds
2184ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_ACT_TAB);
21854b3550efSPatrick McHardy if (nest == NULL)
21868b00a53cSDavid S. Miller goto out_module_put;
21871da177e4SLinus Torvalds
2188fae52d93SZhengchao Shao ret = __tcf_generic_walker(net, skb, cb, RTM_GETACTION, a_o, NULL);
21891da177e4SLinus Torvalds if (ret < 0)
21908b00a53cSDavid S. Miller goto out_module_put;
21911da177e4SLinus Torvalds
21921da177e4SLinus Torvalds if (ret > 0) {
21934b3550efSPatrick McHardy nla_nest_end(skb, nest);
21941da177e4SLinus Torvalds ret = skb->len;
219590825b23SJamal Hadi Salim act_count = cb->args[1];
219690825b23SJamal Hadi Salim memcpy(nla_data(count_attr), &act_count, sizeof(u32));
219790825b23SJamal Hadi Salim cb->args[1] = 0;
21981da177e4SLinus Torvalds } else
2199ebecaa66SJamal Hadi Salim nlmsg_trim(skb, b);
22001da177e4SLinus Torvalds
220127a884dcSArnaldo Carvalho de Melo nlh->nlmsg_len = skb_tail_pointer(skb) - b;
220215e47304SEric W. Biederman if (NETLINK_CB(cb->skb).portid && ret)
22031da177e4SLinus Torvalds nlh->nlmsg_flags |= NLM_F_MULTI;
22041da177e4SLinus Torvalds module_put(a_o->owner);
22051da177e4SLinus Torvalds return skb->len;
22061da177e4SLinus Torvalds
22078b00a53cSDavid S. Miller out_module_put:
22081da177e4SLinus Torvalds module_put(a_o->owner);
2209dc5fc579SArnaldo Carvalho de Melo nlmsg_trim(skb, b);
22101da177e4SLinus Torvalds return skb->len;
22111da177e4SLinus Torvalds }
22121da177e4SLinus Torvalds
tc_action_init(void)22131da177e4SLinus Torvalds static int __init tc_action_init(void)
22141da177e4SLinus Torvalds {
2215b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0);
2216b97bac64SFlorian Westphal rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0);
2217c7ac8679SGreg Rose rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action,
2218b97bac64SFlorian Westphal 0);
22191da177e4SLinus Torvalds
22201da177e4SLinus Torvalds return 0;
22211da177e4SLinus Torvalds }
22221da177e4SLinus Torvalds
22231da177e4SLinus Torvalds subsys_initcall(tc_action_init);
2224