xref: /openbmc/linux/net/sched/act_api.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
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