xref: /openbmc/linux/net/sched/cls_api.c (revision 7a47281439ba00b11fc098f36695522184ce5a82)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * net/sched/cls_api.c	Packet classifier API.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Changes:
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * Eduardo J. Blanco <ejbs@netlabs.com.uy> :990222: kmod support
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/types.h>
141da177e4SLinus Torvalds #include <linux/kernel.h>
151da177e4SLinus Torvalds #include <linux/string.h>
161da177e4SLinus Torvalds #include <linux/errno.h>
1733a48927SJiri Pirko #include <linux/err.h>
181da177e4SLinus Torvalds #include <linux/skbuff.h>
191da177e4SLinus Torvalds #include <linux/init.h>
201da177e4SLinus Torvalds #include <linux/kmod.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
2248617387SJiri Pirko #include <linux/idr.h>
237f76fa36SJohn Hurley #include <linux/rhashtable.h>
2459eb87cbSJohn Hurley #include <linux/jhash.h>
25b854272bSDenis V. Lunev #include <net/net_namespace.h>
26b854272bSDenis V. Lunev #include <net/sock.h>
27dc5fc579SArnaldo Carvalho de Melo #include <net/netlink.h>
281da177e4SLinus Torvalds #include <net/pkt_sched.h>
291da177e4SLinus Torvalds #include <net/pkt_cls.h>
30e3ab786bSPablo Neira Ayuso #include <net/tc_act/tc_pedit.h>
313a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_mirred.h>
323a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_vlan.h>
333a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_tunnel_key.h>
343a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_csum.h>
353a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_gact.h>
368c8cfc6eSPieter Jansen van Vuuren #include <net/tc_act/tc_police.h>
37a7a7be60SPieter Jansen van Vuuren #include <net/tc_act/tc_sample.h>
383a7b6861SPablo Neira Ayuso #include <net/tc_act/tc_skbedit.h>
39b57dc7c1SPaul Blakey #include <net/tc_act/tc_ct.h>
406749d590SJohn Hurley #include <net/tc_act/tc_mpls.h>
414e481908Swenxu #include <net/flow_offload.h>
421da177e4SLinus Torvalds 
43e331473fSDavide Caratti extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1];
44e331473fSDavide Caratti 
451da177e4SLinus Torvalds /* The list of all installed classifier types */
4636272874SWANG Cong static LIST_HEAD(tcf_proto_base);
471da177e4SLinus Torvalds 
481da177e4SLinus Torvalds /* Protects list of registered TC modules. It is pure SMP lock. */
491da177e4SLinus Torvalds static DEFINE_RWLOCK(cls_mod_lock);
501da177e4SLinus Torvalds 
5159eb87cbSJohn Hurley static u32 destroy_obj_hashfn(const struct tcf_proto *tp)
5259eb87cbSJohn Hurley {
5359eb87cbSJohn Hurley 	return jhash_3words(tp->chain->index, tp->prio,
5459eb87cbSJohn Hurley 			    (__force __u32)tp->protocol, 0);
5559eb87cbSJohn Hurley }
5659eb87cbSJohn Hurley 
5759eb87cbSJohn Hurley static void tcf_proto_signal_destroying(struct tcf_chain *chain,
5859eb87cbSJohn Hurley 					struct tcf_proto *tp)
5959eb87cbSJohn Hurley {
6059eb87cbSJohn Hurley 	struct tcf_block *block = chain->block;
6159eb87cbSJohn Hurley 
6259eb87cbSJohn Hurley 	mutex_lock(&block->proto_destroy_lock);
6359eb87cbSJohn Hurley 	hash_add_rcu(block->proto_destroy_ht, &tp->destroy_ht_node,
6459eb87cbSJohn Hurley 		     destroy_obj_hashfn(tp));
6559eb87cbSJohn Hurley 	mutex_unlock(&block->proto_destroy_lock);
6659eb87cbSJohn Hurley }
6759eb87cbSJohn Hurley 
6859eb87cbSJohn Hurley static bool tcf_proto_cmp(const struct tcf_proto *tp1,
6959eb87cbSJohn Hurley 			  const struct tcf_proto *tp2)
7059eb87cbSJohn Hurley {
7159eb87cbSJohn Hurley 	return tp1->chain->index == tp2->chain->index &&
7259eb87cbSJohn Hurley 	       tp1->prio == tp2->prio &&
7359eb87cbSJohn Hurley 	       tp1->protocol == tp2->protocol;
7459eb87cbSJohn Hurley }
7559eb87cbSJohn Hurley 
7659eb87cbSJohn Hurley static bool tcf_proto_exists_destroying(struct tcf_chain *chain,
7759eb87cbSJohn Hurley 					struct tcf_proto *tp)
7859eb87cbSJohn Hurley {
7959eb87cbSJohn Hurley 	u32 hash = destroy_obj_hashfn(tp);
8059eb87cbSJohn Hurley 	struct tcf_proto *iter;
8159eb87cbSJohn Hurley 	bool found = false;
8259eb87cbSJohn Hurley 
8359eb87cbSJohn Hurley 	rcu_read_lock();
8459eb87cbSJohn Hurley 	hash_for_each_possible_rcu(chain->block->proto_destroy_ht, iter,
8559eb87cbSJohn Hurley 				   destroy_ht_node, hash) {
8659eb87cbSJohn Hurley 		if (tcf_proto_cmp(tp, iter)) {
8759eb87cbSJohn Hurley 			found = true;
8859eb87cbSJohn Hurley 			break;
8959eb87cbSJohn Hurley 		}
9059eb87cbSJohn Hurley 	}
9159eb87cbSJohn Hurley 	rcu_read_unlock();
9259eb87cbSJohn Hurley 
9359eb87cbSJohn Hurley 	return found;
9459eb87cbSJohn Hurley }
9559eb87cbSJohn Hurley 
9659eb87cbSJohn Hurley static void
9759eb87cbSJohn Hurley tcf_proto_signal_destroyed(struct tcf_chain *chain, struct tcf_proto *tp)
9859eb87cbSJohn Hurley {
9959eb87cbSJohn Hurley 	struct tcf_block *block = chain->block;
10059eb87cbSJohn Hurley 
10159eb87cbSJohn Hurley 	mutex_lock(&block->proto_destroy_lock);
10259eb87cbSJohn Hurley 	if (hash_hashed(&tp->destroy_ht_node))
10359eb87cbSJohn Hurley 		hash_del_rcu(&tp->destroy_ht_node);
10459eb87cbSJohn Hurley 	mutex_unlock(&block->proto_destroy_lock);
10559eb87cbSJohn Hurley }
10659eb87cbSJohn Hurley 
1071da177e4SLinus Torvalds /* Find classifier type by string name */
1081da177e4SLinus Torvalds 
109f34e8bffSJiri Pirko static const struct tcf_proto_ops *__tcf_proto_lookup_ops(const char *kind)
1101da177e4SLinus Torvalds {
111dcd76081SEric Dumazet 	const struct tcf_proto_ops *t, *res = NULL;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	if (kind) {
1141da177e4SLinus Torvalds 		read_lock(&cls_mod_lock);
11536272874SWANG Cong 		list_for_each_entry(t, &tcf_proto_base, head) {
11633a48927SJiri Pirko 			if (strcmp(kind, t->kind) == 0) {
117dcd76081SEric Dumazet 				if (try_module_get(t->owner))
118dcd76081SEric Dumazet 					res = t;
1191da177e4SLinus Torvalds 				break;
1201da177e4SLinus Torvalds 			}
1211da177e4SLinus Torvalds 		}
1221da177e4SLinus Torvalds 		read_unlock(&cls_mod_lock);
1231da177e4SLinus Torvalds 	}
124dcd76081SEric Dumazet 	return res;
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
127f34e8bffSJiri Pirko static const struct tcf_proto_ops *
12812db03b6SVlad Buslov tcf_proto_lookup_ops(const char *kind, bool rtnl_held,
12912db03b6SVlad Buslov 		     struct netlink_ext_ack *extack)
130f34e8bffSJiri Pirko {
131f34e8bffSJiri Pirko 	const struct tcf_proto_ops *ops;
132f34e8bffSJiri Pirko 
133f34e8bffSJiri Pirko 	ops = __tcf_proto_lookup_ops(kind);
134f34e8bffSJiri Pirko 	if (ops)
135f34e8bffSJiri Pirko 		return ops;
136f34e8bffSJiri Pirko #ifdef CONFIG_MODULES
13712db03b6SVlad Buslov 	if (rtnl_held)
138f34e8bffSJiri Pirko 		rtnl_unlock();
139f34e8bffSJiri Pirko 	request_module("cls_%s", kind);
14012db03b6SVlad Buslov 	if (rtnl_held)
141f34e8bffSJiri Pirko 		rtnl_lock();
142f34e8bffSJiri Pirko 	ops = __tcf_proto_lookup_ops(kind);
143f34e8bffSJiri Pirko 	/* We dropped the RTNL semaphore in order to perform
144f34e8bffSJiri Pirko 	 * the module load. So, even if we succeeded in loading
145f34e8bffSJiri Pirko 	 * the module we have to replay the request. We indicate
146f34e8bffSJiri Pirko 	 * this using -EAGAIN.
147f34e8bffSJiri Pirko 	 */
148f34e8bffSJiri Pirko 	if (ops) {
149f34e8bffSJiri Pirko 		module_put(ops->owner);
150f34e8bffSJiri Pirko 		return ERR_PTR(-EAGAIN);
151f34e8bffSJiri Pirko 	}
152f34e8bffSJiri Pirko #endif
153f34e8bffSJiri Pirko 	NL_SET_ERR_MSG(extack, "TC classifier not found");
154f34e8bffSJiri Pirko 	return ERR_PTR(-ENOENT);
155f34e8bffSJiri Pirko }
156f34e8bffSJiri Pirko 
1571da177e4SLinus Torvalds /* Register(unregister) new classifier type */
1581da177e4SLinus Torvalds 
1591da177e4SLinus Torvalds int register_tcf_proto_ops(struct tcf_proto_ops *ops)
1601da177e4SLinus Torvalds {
16136272874SWANG Cong 	struct tcf_proto_ops *t;
1621da177e4SLinus Torvalds 	int rc = -EEXIST;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	write_lock(&cls_mod_lock);
16536272874SWANG Cong 	list_for_each_entry(t, &tcf_proto_base, head)
1661da177e4SLinus Torvalds 		if (!strcmp(ops->kind, t->kind))
1671da177e4SLinus Torvalds 			goto out;
1681da177e4SLinus Torvalds 
16936272874SWANG Cong 	list_add_tail(&ops->head, &tcf_proto_base);
1701da177e4SLinus Torvalds 	rc = 0;
1711da177e4SLinus Torvalds out:
1721da177e4SLinus Torvalds 	write_unlock(&cls_mod_lock);
1731da177e4SLinus Torvalds 	return rc;
1741da177e4SLinus Torvalds }
175aa767bfeSStephen Hemminger EXPORT_SYMBOL(register_tcf_proto_ops);
1761da177e4SLinus Torvalds 
1777aa0045dSCong Wang static struct workqueue_struct *tc_filter_wq;
1787aa0045dSCong Wang 
1791da177e4SLinus Torvalds int unregister_tcf_proto_ops(struct tcf_proto_ops *ops)
1801da177e4SLinus Torvalds {
18136272874SWANG Cong 	struct tcf_proto_ops *t;
1821da177e4SLinus Torvalds 	int rc = -ENOENT;
1831da177e4SLinus Torvalds 
184c78e1746SDaniel Borkmann 	/* Wait for outstanding call_rcu()s, if any, from a
185c78e1746SDaniel Borkmann 	 * tcf_proto_ops's destroy() handler.
186c78e1746SDaniel Borkmann 	 */
187c78e1746SDaniel Borkmann 	rcu_barrier();
1887aa0045dSCong Wang 	flush_workqueue(tc_filter_wq);
189c78e1746SDaniel Borkmann 
1901da177e4SLinus Torvalds 	write_lock(&cls_mod_lock);
191dcd76081SEric Dumazet 	list_for_each_entry(t, &tcf_proto_base, head) {
192dcd76081SEric Dumazet 		if (t == ops) {
19336272874SWANG Cong 			list_del(&t->head);
1941da177e4SLinus Torvalds 			rc = 0;
195dcd76081SEric Dumazet 			break;
196dcd76081SEric Dumazet 		}
197dcd76081SEric Dumazet 	}
1981da177e4SLinus Torvalds 	write_unlock(&cls_mod_lock);
1991da177e4SLinus Torvalds 	return rc;
2001da177e4SLinus Torvalds }
201aa767bfeSStephen Hemminger EXPORT_SYMBOL(unregister_tcf_proto_ops);
2021da177e4SLinus Torvalds 
203aaa908ffSCong Wang bool tcf_queue_work(struct rcu_work *rwork, work_func_t func)
2047aa0045dSCong Wang {
205aaa908ffSCong Wang 	INIT_RCU_WORK(rwork, func);
206aaa908ffSCong Wang 	return queue_rcu_work(tc_filter_wq, rwork);
2077aa0045dSCong Wang }
2087aa0045dSCong Wang EXPORT_SYMBOL(tcf_queue_work);
2097aa0045dSCong Wang 
2101da177e4SLinus Torvalds /* Select new prio value from the range, managed by kernel. */
2111da177e4SLinus Torvalds 
212aa767bfeSStephen Hemminger static inline u32 tcf_auto_prio(struct tcf_proto *tp)
2131da177e4SLinus Torvalds {
2141da177e4SLinus Torvalds 	u32 first = TC_H_MAKE(0xC0000000U, 0U);
2151da177e4SLinus Torvalds 
2161da177e4SLinus Torvalds 	if (tp)
2171da177e4SLinus Torvalds 		first = tp->prio - 1;
2181da177e4SLinus Torvalds 
2197961973aSJiri Pirko 	return TC_H_MAJ(first);
2201da177e4SLinus Torvalds }
2211da177e4SLinus Torvalds 
2226f96c3c6SCong Wang static bool tcf_proto_check_kind(struct nlattr *kind, char *name)
2236f96c3c6SCong Wang {
2246f96c3c6SCong Wang 	if (kind)
2256f96c3c6SCong Wang 		return nla_strlcpy(name, kind, IFNAMSIZ) >= IFNAMSIZ;
2266f96c3c6SCong Wang 	memset(name, 0, IFNAMSIZ);
2276f96c3c6SCong Wang 	return false;
2286f96c3c6SCong Wang }
2296f96c3c6SCong Wang 
230470502deSVlad Buslov static bool tcf_proto_is_unlocked(const char *kind)
231470502deSVlad Buslov {
232470502deSVlad Buslov 	const struct tcf_proto_ops *ops;
233470502deSVlad Buslov 	bool ret;
234470502deSVlad Buslov 
2356f96c3c6SCong Wang 	if (strlen(kind) == 0)
2366f96c3c6SCong Wang 		return false;
2376f96c3c6SCong Wang 
238470502deSVlad Buslov 	ops = tcf_proto_lookup_ops(kind, false, NULL);
239470502deSVlad Buslov 	/* On error return false to take rtnl lock. Proto lookup/create
240470502deSVlad Buslov 	 * functions will perform lookup again and properly handle errors.
241470502deSVlad Buslov 	 */
242470502deSVlad Buslov 	if (IS_ERR(ops))
243470502deSVlad Buslov 		return false;
244470502deSVlad Buslov 
245470502deSVlad Buslov 	ret = !!(ops->flags & TCF_PROTO_OPS_DOIT_UNLOCKED);
246470502deSVlad Buslov 	module_put(ops->owner);
247470502deSVlad Buslov 	return ret;
248470502deSVlad Buslov }
249470502deSVlad Buslov 
25033a48927SJiri Pirko static struct tcf_proto *tcf_proto_create(const char *kind, u32 protocol,
251c35a4accSAlexander Aring 					  u32 prio, struct tcf_chain *chain,
25212db03b6SVlad Buslov 					  bool rtnl_held,
253c35a4accSAlexander Aring 					  struct netlink_ext_ack *extack)
25433a48927SJiri Pirko {
25533a48927SJiri Pirko 	struct tcf_proto *tp;
25633a48927SJiri Pirko 	int err;
25733a48927SJiri Pirko 
25833a48927SJiri Pirko 	tp = kzalloc(sizeof(*tp), GFP_KERNEL);
25933a48927SJiri Pirko 	if (!tp)
26033a48927SJiri Pirko 		return ERR_PTR(-ENOBUFS);
26133a48927SJiri Pirko 
26212db03b6SVlad Buslov 	tp->ops = tcf_proto_lookup_ops(kind, rtnl_held, extack);
263f34e8bffSJiri Pirko 	if (IS_ERR(tp->ops)) {
264f34e8bffSJiri Pirko 		err = PTR_ERR(tp->ops);
265d68d75fdSJiri Pirko 		goto errout;
26633a48927SJiri Pirko 	}
26733a48927SJiri Pirko 	tp->classify = tp->ops->classify;
26833a48927SJiri Pirko 	tp->protocol = protocol;
26933a48927SJiri Pirko 	tp->prio = prio;
2705bc17018SJiri Pirko 	tp->chain = chain;
2718b64678eSVlad Buslov 	spin_lock_init(&tp->lock);
2724dbfa766SVlad Buslov 	refcount_set(&tp->refcnt, 1);
27333a48927SJiri Pirko 
27433a48927SJiri Pirko 	err = tp->ops->init(tp);
27533a48927SJiri Pirko 	if (err) {
27633a48927SJiri Pirko 		module_put(tp->ops->owner);
27733a48927SJiri Pirko 		goto errout;
27833a48927SJiri Pirko 	}
27933a48927SJiri Pirko 	return tp;
28033a48927SJiri Pirko 
28133a48927SJiri Pirko errout:
28233a48927SJiri Pirko 	kfree(tp);
28333a48927SJiri Pirko 	return ERR_PTR(err);
28433a48927SJiri Pirko }
28533a48927SJiri Pirko 
2864dbfa766SVlad Buslov static void tcf_proto_get(struct tcf_proto *tp)
2874dbfa766SVlad Buslov {
2884dbfa766SVlad Buslov 	refcount_inc(&tp->refcnt);
2894dbfa766SVlad Buslov }
2904dbfa766SVlad Buslov 
2914dbfa766SVlad Buslov static void tcf_chain_put(struct tcf_chain *chain);
2924dbfa766SVlad Buslov 
29312db03b6SVlad Buslov static void tcf_proto_destroy(struct tcf_proto *tp, bool rtnl_held,
29459eb87cbSJohn Hurley 			      bool sig_destroy, struct netlink_ext_ack *extack)
295cf1facdaSJiri Pirko {
29612db03b6SVlad Buslov 	tp->ops->destroy(tp, rtnl_held, extack);
29759eb87cbSJohn Hurley 	if (sig_destroy)
29859eb87cbSJohn Hurley 		tcf_proto_signal_destroyed(tp->chain, tp);
2994dbfa766SVlad Buslov 	tcf_chain_put(tp->chain);
300cf1facdaSJiri Pirko 	module_put(tp->ops->owner);
301cf1facdaSJiri Pirko 	kfree_rcu(tp, rcu);
302cf1facdaSJiri Pirko }
303cf1facdaSJiri Pirko 
30412db03b6SVlad Buslov static void tcf_proto_put(struct tcf_proto *tp, bool rtnl_held,
3054dbfa766SVlad Buslov 			  struct netlink_ext_ack *extack)
3064dbfa766SVlad Buslov {
3074dbfa766SVlad Buslov 	if (refcount_dec_and_test(&tp->refcnt))
30859eb87cbSJohn Hurley 		tcf_proto_destroy(tp, rtnl_held, true, extack);
3094dbfa766SVlad Buslov }
3104dbfa766SVlad Buslov 
311a5b72a08SDavide Caratti static bool tcf_proto_check_delete(struct tcf_proto *tp)
3128b64678eSVlad Buslov {
313a5b72a08SDavide Caratti 	if (tp->ops->delete_empty)
314a5b72a08SDavide Caratti 		return tp->ops->delete_empty(tp);
3158b64678eSVlad Buslov 
3168b64678eSVlad Buslov 	tp->deleting = true;
3178b64678eSVlad Buslov 	return tp->deleting;
3188b64678eSVlad Buslov }
3198b64678eSVlad Buslov 
3208b64678eSVlad Buslov static void tcf_proto_mark_delete(struct tcf_proto *tp)
3218b64678eSVlad Buslov {
3228b64678eSVlad Buslov 	spin_lock(&tp->lock);
3238b64678eSVlad Buslov 	tp->deleting = true;
3248b64678eSVlad Buslov 	spin_unlock(&tp->lock);
3258b64678eSVlad Buslov }
3268b64678eSVlad Buslov 
3278b64678eSVlad Buslov static bool tcf_proto_is_deleting(struct tcf_proto *tp)
3288b64678eSVlad Buslov {
3298b64678eSVlad Buslov 	bool deleting;
3308b64678eSVlad Buslov 
3318b64678eSVlad Buslov 	spin_lock(&tp->lock);
3328b64678eSVlad Buslov 	deleting = tp->deleting;
3338b64678eSVlad Buslov 	spin_unlock(&tp->lock);
3348b64678eSVlad Buslov 
3358b64678eSVlad Buslov 	return deleting;
3368b64678eSVlad Buslov }
3378b64678eSVlad Buslov 
338c266f64dSVlad Buslov #define ASSERT_BLOCK_LOCKED(block)					\
339c266f64dSVlad Buslov 	lockdep_assert_held(&(block)->lock)
340c266f64dSVlad Buslov 
341a9b19443SJiri Pirko struct tcf_filter_chain_list_item {
342a9b19443SJiri Pirko 	struct list_head list;
343a9b19443SJiri Pirko 	tcf_chain_head_change_t *chain_head_change;
344a9b19443SJiri Pirko 	void *chain_head_change_priv;
345a9b19443SJiri Pirko };
346a9b19443SJiri Pirko 
3475bc17018SJiri Pirko static struct tcf_chain *tcf_chain_create(struct tcf_block *block,
3485bc17018SJiri Pirko 					  u32 chain_index)
3492190d1d0SJiri Pirko {
3505bc17018SJiri Pirko 	struct tcf_chain *chain;
3515bc17018SJiri Pirko 
352c266f64dSVlad Buslov 	ASSERT_BLOCK_LOCKED(block);
353c266f64dSVlad Buslov 
3545bc17018SJiri Pirko 	chain = kzalloc(sizeof(*chain), GFP_KERNEL);
3555bc17018SJiri Pirko 	if (!chain)
3565bc17018SJiri Pirko 		return NULL;
3575bc17018SJiri Pirko 	list_add_tail(&chain->list, &block->chain_list);
358ed76f5edSVlad Buslov 	mutex_init(&chain->filter_chain_lock);
3595bc17018SJiri Pirko 	chain->block = block;
3605bc17018SJiri Pirko 	chain->index = chain_index;
361e2ef7544SCong Wang 	chain->refcnt = 1;
362f71e0ca4SJiri Pirko 	if (!chain->index)
363f71e0ca4SJiri Pirko 		block->chain0.chain = chain;
3645bc17018SJiri Pirko 	return chain;
3652190d1d0SJiri Pirko }
3662190d1d0SJiri Pirko 
367a9b19443SJiri Pirko static void tcf_chain_head_change_item(struct tcf_filter_chain_list_item *item,
368a9b19443SJiri Pirko 				       struct tcf_proto *tp_head)
369a9b19443SJiri Pirko {
370a9b19443SJiri Pirko 	if (item->chain_head_change)
371a9b19443SJiri Pirko 		item->chain_head_change(tp_head, item->chain_head_change_priv);
372a9b19443SJiri Pirko }
373f71e0ca4SJiri Pirko 
374f71e0ca4SJiri Pirko static void tcf_chain0_head_change(struct tcf_chain *chain,
375c7eb7d72SJiri Pirko 				   struct tcf_proto *tp_head)
376c7eb7d72SJiri Pirko {
377a9b19443SJiri Pirko 	struct tcf_filter_chain_list_item *item;
378f71e0ca4SJiri Pirko 	struct tcf_block *block = chain->block;
379a9b19443SJiri Pirko 
380f71e0ca4SJiri Pirko 	if (chain->index)
381f71e0ca4SJiri Pirko 		return;
382165f0135SVlad Buslov 
383165f0135SVlad Buslov 	mutex_lock(&block->lock);
384f71e0ca4SJiri Pirko 	list_for_each_entry(item, &block->chain0.filter_chain_list, list)
385a9b19443SJiri Pirko 		tcf_chain_head_change_item(item, tp_head);
386165f0135SVlad Buslov 	mutex_unlock(&block->lock);
387c7eb7d72SJiri Pirko }
388c7eb7d72SJiri Pirko 
389c266f64dSVlad Buslov /* Returns true if block can be safely freed. */
390c266f64dSVlad Buslov 
391c266f64dSVlad Buslov static bool tcf_chain_detach(struct tcf_chain *chain)
392f93e1cdcSJiri Pirko {
393efbf7897SCong Wang 	struct tcf_block *block = chain->block;
394efbf7897SCong Wang 
395c266f64dSVlad Buslov 	ASSERT_BLOCK_LOCKED(block);
396c266f64dSVlad Buslov 
397e2ef7544SCong Wang 	list_del(&chain->list);
398f71e0ca4SJiri Pirko 	if (!chain->index)
399f71e0ca4SJiri Pirko 		block->chain0.chain = NULL;
400c266f64dSVlad Buslov 
401c266f64dSVlad Buslov 	if (list_empty(&block->chain_list) &&
402c266f64dSVlad Buslov 	    refcount_read(&block->refcnt) == 0)
403c266f64dSVlad Buslov 		return true;
404c266f64dSVlad Buslov 
405c266f64dSVlad Buslov 	return false;
406c266f64dSVlad Buslov }
407c266f64dSVlad Buslov 
408c266f64dSVlad Buslov static void tcf_block_destroy(struct tcf_block *block)
409c266f64dSVlad Buslov {
410c266f64dSVlad Buslov 	mutex_destroy(&block->lock);
41159eb87cbSJohn Hurley 	mutex_destroy(&block->proto_destroy_lock);
4120607e439SVlad Buslov 	kfree_rcu(block, rcu);
4132190d1d0SJiri Pirko }
4142190d1d0SJiri Pirko 
415c266f64dSVlad Buslov static void tcf_chain_destroy(struct tcf_chain *chain, bool free_block)
416c266f64dSVlad Buslov {
417c266f64dSVlad Buslov 	struct tcf_block *block = chain->block;
418c266f64dSVlad Buslov 
419ed76f5edSVlad Buslov 	mutex_destroy(&chain->filter_chain_lock);
420ee3bbfe8SDavide Caratti 	kfree_rcu(chain, rcu);
421c266f64dSVlad Buslov 	if (free_block)
422c266f64dSVlad Buslov 		tcf_block_destroy(block);
423c266f64dSVlad Buslov }
424c266f64dSVlad Buslov 
425e2ef7544SCong Wang static void tcf_chain_hold(struct tcf_chain *chain)
426e2ef7544SCong Wang {
427c266f64dSVlad Buslov 	ASSERT_BLOCK_LOCKED(chain->block);
428c266f64dSVlad Buslov 
429e2ef7544SCong Wang 	++chain->refcnt;
430e2ef7544SCong Wang }
431e2ef7544SCong Wang 
4323d32f4c5SJiri Pirko static bool tcf_chain_held_by_acts_only(struct tcf_chain *chain)
4331f3ed383SJiri Pirko {
434c266f64dSVlad Buslov 	ASSERT_BLOCK_LOCKED(chain->block);
435c266f64dSVlad Buslov 
4361f3ed383SJiri Pirko 	/* In case all the references are action references, this
4373d32f4c5SJiri Pirko 	 * chain should not be shown to the user.
4381f3ed383SJiri Pirko 	 */
4391f3ed383SJiri Pirko 	return chain->refcnt == chain->action_refcnt;
4401f3ed383SJiri Pirko }
4411f3ed383SJiri Pirko 
44232a4f5ecSJiri Pirko static struct tcf_chain *tcf_chain_lookup(struct tcf_block *block,
44332a4f5ecSJiri Pirko 					  u32 chain_index)
4445bc17018SJiri Pirko {
4455bc17018SJiri Pirko 	struct tcf_chain *chain;
4465bc17018SJiri Pirko 
447c266f64dSVlad Buslov 	ASSERT_BLOCK_LOCKED(block);
448c266f64dSVlad Buslov 
4495bc17018SJiri Pirko 	list_for_each_entry(chain, &block->chain_list, list) {
45032a4f5ecSJiri Pirko 		if (chain->index == chain_index)
45132a4f5ecSJiri Pirko 			return chain;
45232a4f5ecSJiri Pirko 	}
45332a4f5ecSJiri Pirko 	return NULL;
45432a4f5ecSJiri Pirko }
45532a4f5ecSJiri Pirko 
45632a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
45732a4f5ecSJiri Pirko 			   u32 seq, u16 flags, int event, bool unicast);
45832a4f5ecSJiri Pirko 
45953681407SJiri Pirko static struct tcf_chain *__tcf_chain_get(struct tcf_block *block,
46053681407SJiri Pirko 					 u32 chain_index, bool create,
46153681407SJiri Pirko 					 bool by_act)
46232a4f5ecSJiri Pirko {
463c266f64dSVlad Buslov 	struct tcf_chain *chain = NULL;
464c266f64dSVlad Buslov 	bool is_first_reference;
46532a4f5ecSJiri Pirko 
466c266f64dSVlad Buslov 	mutex_lock(&block->lock);
467c266f64dSVlad Buslov 	chain = tcf_chain_lookup(block, chain_index);
46832a4f5ecSJiri Pirko 	if (chain) {
469e2ef7544SCong Wang 		tcf_chain_hold(chain);
47053681407SJiri Pirko 	} else {
47132a4f5ecSJiri Pirko 		if (!create)
472c266f64dSVlad Buslov 			goto errout;
47332a4f5ecSJiri Pirko 		chain = tcf_chain_create(block, chain_index);
47432a4f5ecSJiri Pirko 		if (!chain)
475c266f64dSVlad Buslov 			goto errout;
47653681407SJiri Pirko 	}
47753681407SJiri Pirko 
47853681407SJiri Pirko 	if (by_act)
47953681407SJiri Pirko 		++chain->action_refcnt;
480c266f64dSVlad Buslov 	is_first_reference = chain->refcnt - chain->action_refcnt == 1;
481c266f64dSVlad Buslov 	mutex_unlock(&block->lock);
48253681407SJiri Pirko 
48353681407SJiri Pirko 	/* Send notification only in case we got the first
48453681407SJiri Pirko 	 * non-action reference. Until then, the chain acts only as
48553681407SJiri Pirko 	 * a placeholder for actions pointing to it and user ought
48653681407SJiri Pirko 	 * not know about them.
48753681407SJiri Pirko 	 */
488c266f64dSVlad Buslov 	if (is_first_reference && !by_act)
48932a4f5ecSJiri Pirko 		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
49032a4f5ecSJiri Pirko 				RTM_NEWCHAIN, false);
49153681407SJiri Pirko 
49232a4f5ecSJiri Pirko 	return chain;
493c266f64dSVlad Buslov 
494c266f64dSVlad Buslov errout:
495c266f64dSVlad Buslov 	mutex_unlock(&block->lock);
496c266f64dSVlad Buslov 	return chain;
497e2ef7544SCong Wang }
49853681407SJiri Pirko 
499290b1c8bSJiri Pirko static struct tcf_chain *tcf_chain_get(struct tcf_block *block, u32 chain_index,
50053681407SJiri Pirko 				       bool create)
50153681407SJiri Pirko {
50253681407SJiri Pirko 	return __tcf_chain_get(block, chain_index, create, false);
50353681407SJiri Pirko }
5045bc17018SJiri Pirko 
5051f3ed383SJiri Pirko struct tcf_chain *tcf_chain_get_by_act(struct tcf_block *block, u32 chain_index)
5061f3ed383SJiri Pirko {
50753681407SJiri Pirko 	return __tcf_chain_get(block, chain_index, true, true);
5081f3ed383SJiri Pirko }
5091f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_get_by_act);
5101f3ed383SJiri Pirko 
511a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
512a5654820SVlad Buslov 			       void *tmplt_priv);
513a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
514a5654820SVlad Buslov 				  void *tmplt_priv, u32 chain_index,
515a5654820SVlad Buslov 				  struct tcf_block *block, struct sk_buff *oskb,
516a5654820SVlad Buslov 				  u32 seq, u16 flags, bool unicast);
5179f407f17SJiri Pirko 
51891052fa1SVlad Buslov static void __tcf_chain_put(struct tcf_chain *chain, bool by_act,
51991052fa1SVlad Buslov 			    bool explicitly_created)
5205bc17018SJiri Pirko {
521c266f64dSVlad Buslov 	struct tcf_block *block = chain->block;
522a5654820SVlad Buslov 	const struct tcf_proto_ops *tmplt_ops;
523b62989fcSVlad Buslov 	bool free_block = false;
524c266f64dSVlad Buslov 	unsigned int refcnt;
525a5654820SVlad Buslov 	void *tmplt_priv;
526c266f64dSVlad Buslov 
527c266f64dSVlad Buslov 	mutex_lock(&block->lock);
52891052fa1SVlad Buslov 	if (explicitly_created) {
52991052fa1SVlad Buslov 		if (!chain->explicitly_created) {
53091052fa1SVlad Buslov 			mutex_unlock(&block->lock);
53191052fa1SVlad Buslov 			return;
53291052fa1SVlad Buslov 		}
53391052fa1SVlad Buslov 		chain->explicitly_created = false;
53491052fa1SVlad Buslov 	}
53591052fa1SVlad Buslov 
53653681407SJiri Pirko 	if (by_act)
53753681407SJiri Pirko 		chain->action_refcnt--;
538c266f64dSVlad Buslov 
539c266f64dSVlad Buslov 	/* tc_chain_notify_delete can't be called while holding block lock.
540c266f64dSVlad Buslov 	 * However, when block is unlocked chain can be changed concurrently, so
541c266f64dSVlad Buslov 	 * save these to temporary variables.
542c266f64dSVlad Buslov 	 */
543c266f64dSVlad Buslov 	refcnt = --chain->refcnt;
544a5654820SVlad Buslov 	tmplt_ops = chain->tmplt_ops;
545a5654820SVlad Buslov 	tmplt_priv = chain->tmplt_priv;
54653681407SJiri Pirko 
54753681407SJiri Pirko 	/* The last dropped non-action reference will trigger notification. */
548b62989fcSVlad Buslov 	if (refcnt - chain->action_refcnt == 0 && !by_act) {
549b62989fcSVlad Buslov 		tc_chain_notify_delete(tmplt_ops, tmplt_priv, chain->index,
550a5654820SVlad Buslov 				       block, NULL, 0, 0, false);
551726d0612SVlad Buslov 		/* Last reference to chain, no need to lock. */
552726d0612SVlad Buslov 		chain->flushing = false;
553726d0612SVlad Buslov 	}
55453681407SJiri Pirko 
555b62989fcSVlad Buslov 	if (refcnt == 0)
556b62989fcSVlad Buslov 		free_block = tcf_chain_detach(chain);
557b62989fcSVlad Buslov 	mutex_unlock(&block->lock);
558b62989fcSVlad Buslov 
559c266f64dSVlad Buslov 	if (refcnt == 0) {
560a5654820SVlad Buslov 		tc_chain_tmplt_del(tmplt_ops, tmplt_priv);
561c266f64dSVlad Buslov 		tcf_chain_destroy(chain, free_block);
5625bc17018SJiri Pirko 	}
56332a4f5ecSJiri Pirko }
56453681407SJiri Pirko 
565290b1c8bSJiri Pirko static void tcf_chain_put(struct tcf_chain *chain)
56653681407SJiri Pirko {
56791052fa1SVlad Buslov 	__tcf_chain_put(chain, false, false);
56853681407SJiri Pirko }
5695bc17018SJiri Pirko 
5701f3ed383SJiri Pirko void tcf_chain_put_by_act(struct tcf_chain *chain)
5711f3ed383SJiri Pirko {
57291052fa1SVlad Buslov 	__tcf_chain_put(chain, true, false);
5731f3ed383SJiri Pirko }
5741f3ed383SJiri Pirko EXPORT_SYMBOL(tcf_chain_put_by_act);
5751f3ed383SJiri Pirko 
57632a4f5ecSJiri Pirko static void tcf_chain_put_explicitly_created(struct tcf_chain *chain)
57732a4f5ecSJiri Pirko {
57891052fa1SVlad Buslov 	__tcf_chain_put(chain, false, true);
57932a4f5ecSJiri Pirko }
58032a4f5ecSJiri Pirko 
58112db03b6SVlad Buslov static void tcf_chain_flush(struct tcf_chain *chain, bool rtnl_held)
582290b1c8bSJiri Pirko {
5834dbfa766SVlad Buslov 	struct tcf_proto *tp, *tp_next;
584290b1c8bSJiri Pirko 
585ed76f5edSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
586ed76f5edSVlad Buslov 	tp = tcf_chain_dereference(chain->filter_chain, chain);
58759eb87cbSJohn Hurley 	while (tp) {
58859eb87cbSJohn Hurley 		tp_next = rcu_dereference_protected(tp->next, 1);
58959eb87cbSJohn Hurley 		tcf_proto_signal_destroying(chain, tp);
59059eb87cbSJohn Hurley 		tp = tp_next;
59159eb87cbSJohn Hurley 	}
59259eb87cbSJohn Hurley 	tp = tcf_chain_dereference(chain->filter_chain, chain);
5934dbfa766SVlad Buslov 	RCU_INIT_POINTER(chain->filter_chain, NULL);
594290b1c8bSJiri Pirko 	tcf_chain0_head_change(chain, NULL);
595726d0612SVlad Buslov 	chain->flushing = true;
596ed76f5edSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
597ed76f5edSVlad Buslov 
598290b1c8bSJiri Pirko 	while (tp) {
5994dbfa766SVlad Buslov 		tp_next = rcu_dereference_protected(tp->next, 1);
60012db03b6SVlad Buslov 		tcf_proto_put(tp, rtnl_held, NULL);
6014dbfa766SVlad Buslov 		tp = tp_next;
602290b1c8bSJiri Pirko 	}
603290b1c8bSJiri Pirko }
604290b1c8bSJiri Pirko 
6054e481908Swenxu static int tcf_block_setup(struct tcf_block *block,
6064e481908Swenxu 			   struct flow_block_offload *bo);
6074e481908Swenxu 
60825a443f7SJohn Hurley static void tc_indr_block_cmd(struct net_device *dev, struct tcf_block *block,
60925a443f7SJohn Hurley 			      flow_indr_block_bind_cb_t *cb, void *cb_priv,
61025a443f7SJohn Hurley 			      enum flow_block_command command, bool ingress)
6114e481908Swenxu {
6124e481908Swenxu 	struct flow_block_offload bo = {
6134e481908Swenxu 		.command	= command,
61425a443f7SJohn Hurley 		.binder_type	= ingress ?
61525a443f7SJohn Hurley 				  FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS :
61625a443f7SJohn Hurley 				  FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS,
6174e481908Swenxu 		.net		= dev_net(dev),
6184e481908Swenxu 		.block_shared	= tcf_block_non_null_shared(block),
6194e481908Swenxu 	};
6204e481908Swenxu 	INIT_LIST_HEAD(&bo.cb_list);
6214e481908Swenxu 
6224e481908Swenxu 	if (!block)
6234e481908Swenxu 		return;
6244e481908Swenxu 
6254e481908Swenxu 	bo.block = &block->flow_block;
6264e481908Swenxu 
6274f8116c8SVlad Buslov 	down_write(&block->cb_lock);
6284e481908Swenxu 	cb(dev, cb_priv, TC_SETUP_BLOCK, &bo);
6294e481908Swenxu 
6304e481908Swenxu 	tcf_block_setup(block, &bo);
6314f8116c8SVlad Buslov 	up_write(&block->cb_lock);
6324e481908Swenxu }
6334e481908Swenxu 
63425a443f7SJohn Hurley static struct tcf_block *tc_dev_block(struct net_device *dev, bool ingress)
6357f76fa36SJohn Hurley {
6367f76fa36SJohn Hurley 	const struct Qdisc_class_ops *cops;
63725a443f7SJohn Hurley 	const struct Qdisc_ops *ops;
6387f76fa36SJohn Hurley 	struct Qdisc *qdisc;
6397f76fa36SJohn Hurley 
6407f76fa36SJohn Hurley 	if (!dev_ingress_queue(dev))
6417f76fa36SJohn Hurley 		return NULL;
6427f76fa36SJohn Hurley 
6437f76fa36SJohn Hurley 	qdisc = dev_ingress_queue(dev)->qdisc_sleeping;
6447f76fa36SJohn Hurley 	if (!qdisc)
6457f76fa36SJohn Hurley 		return NULL;
6467f76fa36SJohn Hurley 
64725a443f7SJohn Hurley 	ops = qdisc->ops;
64825a443f7SJohn Hurley 	if (!ops)
64925a443f7SJohn Hurley 		return NULL;
65025a443f7SJohn Hurley 
65125a443f7SJohn Hurley 	if (!ingress && !strcmp("ingress", ops->id))
65225a443f7SJohn Hurley 		return NULL;
65325a443f7SJohn Hurley 
65425a443f7SJohn Hurley 	cops = ops->cl_ops;
6557f76fa36SJohn Hurley 	if (!cops)
6567f76fa36SJohn Hurley 		return NULL;
6577f76fa36SJohn Hurley 
6587f76fa36SJohn Hurley 	if (!cops->tcf_block)
6597f76fa36SJohn Hurley 		return NULL;
6607f76fa36SJohn Hurley 
66125a443f7SJohn Hurley 	return cops->tcf_block(qdisc,
66225a443f7SJohn Hurley 			       ingress ? TC_H_MIN_INGRESS : TC_H_MIN_EGRESS,
66325a443f7SJohn Hurley 			       NULL);
6647f76fa36SJohn Hurley }
6657f76fa36SJohn Hurley 
66625a443f7SJohn Hurley static void tc_indr_block_get_and_cmd(struct net_device *dev,
6674e481908Swenxu 				      flow_indr_block_bind_cb_t *cb,
668242453c2Swenxu 				      void *cb_priv,
6699c0e189eSPablo Neira Ayuso 				      enum flow_block_command command)
6707f76fa36SJohn Hurley {
67125a443f7SJohn Hurley 	struct tcf_block *block;
6727f76fa36SJohn Hurley 
67325a443f7SJohn Hurley 	block = tc_dev_block(dev, true);
67425a443f7SJohn Hurley 	tc_indr_block_cmd(dev, block, cb, cb_priv, command, true);
67525a443f7SJohn Hurley 
67625a443f7SJohn Hurley 	block = tc_dev_block(dev, false);
67725a443f7SJohn Hurley 	tc_indr_block_cmd(dev, block, cb, cb_priv, command, false);
6787f76fa36SJohn Hurley }
6797f76fa36SJohn Hurley 
6804e481908Swenxu static void tc_indr_block_call(struct tcf_block *block,
6814e481908Swenxu 			       struct net_device *dev,
6827f76fa36SJohn Hurley 			       struct tcf_block_ext_info *ei,
6839c0e189eSPablo Neira Ayuso 			       enum flow_block_command command,
6847f76fa36SJohn Hurley 			       struct netlink_ext_ack *extack)
6857f76fa36SJohn Hurley {
686955bcb6eSPablo Neira Ayuso 	struct flow_block_offload bo = {
6877f76fa36SJohn Hurley 		.command	= command,
6887f76fa36SJohn Hurley 		.binder_type	= ei->binder_type,
689da3eeb90SPablo Neira Ayuso 		.net		= dev_net(dev),
69014bfb13fSPablo Neira Ayuso 		.block		= &block->flow_block,
691955bcb6eSPablo Neira Ayuso 		.block_shared	= tcf_block_shared(block),
6927f76fa36SJohn Hurley 		.extack		= extack,
6937f76fa36SJohn Hurley 	};
69459094b1eSPablo Neira Ayuso 	INIT_LIST_HEAD(&bo.cb_list);
6957f76fa36SJohn Hurley 
6961150ab0fSwenxu 	flow_indr_block_call(dev, &bo, command);
69759094b1eSPablo Neira Ayuso 	tcf_block_setup(block, &bo);
6987f76fa36SJohn Hurley }
6997f76fa36SJohn Hurley 
700caa72601SJiri Pirko static bool tcf_block_offload_in_use(struct tcf_block *block)
701caa72601SJiri Pirko {
70297394befSVlad Buslov 	return atomic_read(&block->offloadcnt);
703caa72601SJiri Pirko }
704caa72601SJiri Pirko 
705caa72601SJiri Pirko static int tcf_block_offload_cmd(struct tcf_block *block,
706caa72601SJiri Pirko 				 struct net_device *dev,
7078c4083b3SJiri Pirko 				 struct tcf_block_ext_info *ei,
7089c0e189eSPablo Neira Ayuso 				 enum flow_block_command command,
70960513bd8SJohn Hurley 				 struct netlink_ext_ack *extack)
7108c4083b3SJiri Pirko {
711955bcb6eSPablo Neira Ayuso 	struct flow_block_offload bo = {};
71259094b1eSPablo Neira Ayuso 	int err;
7138c4083b3SJiri Pirko 
714da3eeb90SPablo Neira Ayuso 	bo.net = dev_net(dev);
7158c4083b3SJiri Pirko 	bo.command = command;
7168c4083b3SJiri Pirko 	bo.binder_type = ei->binder_type;
71714bfb13fSPablo Neira Ayuso 	bo.block = &block->flow_block;
718955bcb6eSPablo Neira Ayuso 	bo.block_shared = tcf_block_shared(block);
71960513bd8SJohn Hurley 	bo.extack = extack;
72059094b1eSPablo Neira Ayuso 	INIT_LIST_HEAD(&bo.cb_list);
72159094b1eSPablo Neira Ayuso 
72259094b1eSPablo Neira Ayuso 	err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_BLOCK, &bo);
72359094b1eSPablo Neira Ayuso 	if (err < 0)
72459094b1eSPablo Neira Ayuso 		return err;
72559094b1eSPablo Neira Ayuso 
72659094b1eSPablo Neira Ayuso 	return tcf_block_setup(block, &bo);
7278c4083b3SJiri Pirko }
7288c4083b3SJiri Pirko 
729caa72601SJiri Pirko static int tcf_block_offload_bind(struct tcf_block *block, struct Qdisc *q,
73060513bd8SJohn Hurley 				  struct tcf_block_ext_info *ei,
73160513bd8SJohn Hurley 				  struct netlink_ext_ack *extack)
7328c4083b3SJiri Pirko {
733caa72601SJiri Pirko 	struct net_device *dev = q->dev_queue->dev;
734caa72601SJiri Pirko 	int err;
735caa72601SJiri Pirko 
7364f8116c8SVlad Buslov 	down_write(&block->cb_lock);
737caa72601SJiri Pirko 	if (!dev->netdev_ops->ndo_setup_tc)
738caa72601SJiri Pirko 		goto no_offload_dev_inc;
739caa72601SJiri Pirko 
740caa72601SJiri Pirko 	/* If tc offload feature is disabled and the block we try to bind
741caa72601SJiri Pirko 	 * to already has some offloaded filters, forbid to bind.
742caa72601SJiri Pirko 	 */
74360513bd8SJohn Hurley 	if (!tc_can_offload(dev) && tcf_block_offload_in_use(block)) {
74460513bd8SJohn Hurley 		NL_SET_ERR_MSG(extack, "Bind to offloaded block failed as dev has offload disabled");
7454f8116c8SVlad Buslov 		err = -EOPNOTSUPP;
7464f8116c8SVlad Buslov 		goto err_unlock;
74760513bd8SJohn Hurley 	}
748caa72601SJiri Pirko 
7499c0e189eSPablo Neira Ayuso 	err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_BIND, extack);
750caa72601SJiri Pirko 	if (err == -EOPNOTSUPP)
751caa72601SJiri Pirko 		goto no_offload_dev_inc;
7527f76fa36SJohn Hurley 	if (err)
7534f8116c8SVlad Buslov 		goto err_unlock;
754caa72601SJiri Pirko 
7559c0e189eSPablo Neira Ayuso 	tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack);
7564f8116c8SVlad Buslov 	up_write(&block->cb_lock);
7577f76fa36SJohn Hurley 	return 0;
7587f76fa36SJohn Hurley 
759caa72601SJiri Pirko no_offload_dev_inc:
7604f8116c8SVlad Buslov 	if (tcf_block_offload_in_use(block)) {
7614f8116c8SVlad Buslov 		err = -EOPNOTSUPP;
7624f8116c8SVlad Buslov 		goto err_unlock;
7634f8116c8SVlad Buslov 	}
7644f8116c8SVlad Buslov 	err = 0;
765caa72601SJiri Pirko 	block->nooffloaddevcnt++;
7669c0e189eSPablo Neira Ayuso 	tc_indr_block_call(block, dev, ei, FLOW_BLOCK_BIND, extack);
7674f8116c8SVlad Buslov err_unlock:
7684f8116c8SVlad Buslov 	up_write(&block->cb_lock);
7694f8116c8SVlad Buslov 	return err;
7708c4083b3SJiri Pirko }
7718c4083b3SJiri Pirko 
7728c4083b3SJiri Pirko static void tcf_block_offload_unbind(struct tcf_block *block, struct Qdisc *q,
7738c4083b3SJiri Pirko 				     struct tcf_block_ext_info *ei)
7748c4083b3SJiri Pirko {
775caa72601SJiri Pirko 	struct net_device *dev = q->dev_queue->dev;
776caa72601SJiri Pirko 	int err;
777caa72601SJiri Pirko 
7784f8116c8SVlad Buslov 	down_write(&block->cb_lock);
7799c0e189eSPablo Neira Ayuso 	tc_indr_block_call(block, dev, ei, FLOW_BLOCK_UNBIND, NULL);
7807f76fa36SJohn Hurley 
781caa72601SJiri Pirko 	if (!dev->netdev_ops->ndo_setup_tc)
782caa72601SJiri Pirko 		goto no_offload_dev_dec;
7839c0e189eSPablo Neira Ayuso 	err = tcf_block_offload_cmd(block, dev, ei, FLOW_BLOCK_UNBIND, NULL);
784caa72601SJiri Pirko 	if (err == -EOPNOTSUPP)
785caa72601SJiri Pirko 		goto no_offload_dev_dec;
7864f8116c8SVlad Buslov 	up_write(&block->cb_lock);
787caa72601SJiri Pirko 	return;
788caa72601SJiri Pirko 
789caa72601SJiri Pirko no_offload_dev_dec:
790caa72601SJiri Pirko 	WARN_ON(block->nooffloaddevcnt-- == 0);
7914f8116c8SVlad Buslov 	up_write(&block->cb_lock);
7928c4083b3SJiri Pirko }
7938c4083b3SJiri Pirko 
794a9b19443SJiri Pirko static int
795f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_add(struct tcf_block *block,
796a9b19443SJiri Pirko 			      struct tcf_block_ext_info *ei,
797a9b19443SJiri Pirko 			      struct netlink_ext_ack *extack)
798a9b19443SJiri Pirko {
799a9b19443SJiri Pirko 	struct tcf_filter_chain_list_item *item;
800165f0135SVlad Buslov 	struct tcf_chain *chain0;
801a9b19443SJiri Pirko 
802a9b19443SJiri Pirko 	item = kmalloc(sizeof(*item), GFP_KERNEL);
803a9b19443SJiri Pirko 	if (!item) {
804a9b19443SJiri Pirko 		NL_SET_ERR_MSG(extack, "Memory allocation for head change callback item failed");
805a9b19443SJiri Pirko 		return -ENOMEM;
806a9b19443SJiri Pirko 	}
807a9b19443SJiri Pirko 	item->chain_head_change = ei->chain_head_change;
808a9b19443SJiri Pirko 	item->chain_head_change_priv = ei->chain_head_change_priv;
809165f0135SVlad Buslov 
810165f0135SVlad Buslov 	mutex_lock(&block->lock);
811165f0135SVlad Buslov 	chain0 = block->chain0.chain;
812ed76f5edSVlad Buslov 	if (chain0)
813ed76f5edSVlad Buslov 		tcf_chain_hold(chain0);
814ed76f5edSVlad Buslov 	else
815f71e0ca4SJiri Pirko 		list_add(&item->list, &block->chain0.filter_chain_list);
816165f0135SVlad Buslov 	mutex_unlock(&block->lock);
817165f0135SVlad Buslov 
818ed76f5edSVlad Buslov 	if (chain0) {
819ed76f5edSVlad Buslov 		struct tcf_proto *tp_head;
820ed76f5edSVlad Buslov 
821ed76f5edSVlad Buslov 		mutex_lock(&chain0->filter_chain_lock);
822ed76f5edSVlad Buslov 
823ed76f5edSVlad Buslov 		tp_head = tcf_chain_dereference(chain0->filter_chain, chain0);
824ed76f5edSVlad Buslov 		if (tp_head)
825ed76f5edSVlad Buslov 			tcf_chain_head_change_item(item, tp_head);
826ed76f5edSVlad Buslov 
827ed76f5edSVlad Buslov 		mutex_lock(&block->lock);
828ed76f5edSVlad Buslov 		list_add(&item->list, &block->chain0.filter_chain_list);
829ed76f5edSVlad Buslov 		mutex_unlock(&block->lock);
830ed76f5edSVlad Buslov 
831ed76f5edSVlad Buslov 		mutex_unlock(&chain0->filter_chain_lock);
832ed76f5edSVlad Buslov 		tcf_chain_put(chain0);
833ed76f5edSVlad Buslov 	}
834ed76f5edSVlad Buslov 
835a9b19443SJiri Pirko 	return 0;
836a9b19443SJiri Pirko }
837a9b19443SJiri Pirko 
838a9b19443SJiri Pirko static void
839f71e0ca4SJiri Pirko tcf_chain0_head_change_cb_del(struct tcf_block *block,
840a9b19443SJiri Pirko 			      struct tcf_block_ext_info *ei)
841a9b19443SJiri Pirko {
842a9b19443SJiri Pirko 	struct tcf_filter_chain_list_item *item;
843a9b19443SJiri Pirko 
844165f0135SVlad Buslov 	mutex_lock(&block->lock);
845f71e0ca4SJiri Pirko 	list_for_each_entry(item, &block->chain0.filter_chain_list, list) {
846a9b19443SJiri Pirko 		if ((!ei->chain_head_change && !ei->chain_head_change_priv) ||
847a9b19443SJiri Pirko 		    (item->chain_head_change == ei->chain_head_change &&
848a9b19443SJiri Pirko 		     item->chain_head_change_priv == ei->chain_head_change_priv)) {
849165f0135SVlad Buslov 			if (block->chain0.chain)
850a9b19443SJiri Pirko 				tcf_chain_head_change_item(item, NULL);
851a9b19443SJiri Pirko 			list_del(&item->list);
852165f0135SVlad Buslov 			mutex_unlock(&block->lock);
853165f0135SVlad Buslov 
854a9b19443SJiri Pirko 			kfree(item);
855a9b19443SJiri Pirko 			return;
856a9b19443SJiri Pirko 		}
857a9b19443SJiri Pirko 	}
858165f0135SVlad Buslov 	mutex_unlock(&block->lock);
859a9b19443SJiri Pirko 	WARN_ON(1);
860a9b19443SJiri Pirko }
861a9b19443SJiri Pirko 
86248617387SJiri Pirko struct tcf_net {
863ab281629SVlad Buslov 	spinlock_t idr_lock; /* Protects idr */
86448617387SJiri Pirko 	struct idr idr;
86548617387SJiri Pirko };
86648617387SJiri Pirko 
86748617387SJiri Pirko static unsigned int tcf_net_id;
86848617387SJiri Pirko 
86948617387SJiri Pirko static int tcf_block_insert(struct tcf_block *block, struct net *net,
870bb047dddSJiri Pirko 			    struct netlink_ext_ack *extack)
871a9b19443SJiri Pirko {
87248617387SJiri Pirko 	struct tcf_net *tn = net_generic(net, tcf_net_id);
873ab281629SVlad Buslov 	int err;
87448617387SJiri Pirko 
875ab281629SVlad Buslov 	idr_preload(GFP_KERNEL);
876ab281629SVlad Buslov 	spin_lock(&tn->idr_lock);
877ab281629SVlad Buslov 	err = idr_alloc_u32(&tn->idr, block, &block->index, block->index,
878ab281629SVlad Buslov 			    GFP_NOWAIT);
879ab281629SVlad Buslov 	spin_unlock(&tn->idr_lock);
880ab281629SVlad Buslov 	idr_preload_end();
881ab281629SVlad Buslov 
882ab281629SVlad Buslov 	return err;
883a9b19443SJiri Pirko }
884a9b19443SJiri Pirko 
88548617387SJiri Pirko static void tcf_block_remove(struct tcf_block *block, struct net *net)
88648617387SJiri Pirko {
88748617387SJiri Pirko 	struct tcf_net *tn = net_generic(net, tcf_net_id);
88848617387SJiri Pirko 
889ab281629SVlad Buslov 	spin_lock(&tn->idr_lock);
8909c160941SMatthew Wilcox 	idr_remove(&tn->idr, block->index);
891ab281629SVlad Buslov 	spin_unlock(&tn->idr_lock);
89248617387SJiri Pirko }
89348617387SJiri Pirko 
89448617387SJiri Pirko static struct tcf_block *tcf_block_create(struct net *net, struct Qdisc *q,
895bb047dddSJiri Pirko 					  u32 block_index,
8968d1a77f9SAlexander Aring 					  struct netlink_ext_ack *extack)
8976529eabaSJiri Pirko {
89848617387SJiri Pirko 	struct tcf_block *block;
8996529eabaSJiri Pirko 
90048617387SJiri Pirko 	block = kzalloc(sizeof(*block), GFP_KERNEL);
9018d1a77f9SAlexander Aring 	if (!block) {
9028d1a77f9SAlexander Aring 		NL_SET_ERR_MSG(extack, "Memory allocation for block failed");
90348617387SJiri Pirko 		return ERR_PTR(-ENOMEM);
9048d1a77f9SAlexander Aring 	}
905c266f64dSVlad Buslov 	mutex_init(&block->lock);
90659eb87cbSJohn Hurley 	mutex_init(&block->proto_destroy_lock);
9074f8116c8SVlad Buslov 	init_rwsem(&block->cb_lock);
90814bfb13fSPablo Neira Ayuso 	flow_block_init(&block->flow_block);
9095bc17018SJiri Pirko 	INIT_LIST_HEAD(&block->chain_list);
910f36fe1c4SJiri Pirko 	INIT_LIST_HEAD(&block->owner_list);
911f71e0ca4SJiri Pirko 	INIT_LIST_HEAD(&block->chain0.filter_chain_list);
912acb67442SJiri Pirko 
913cfebd7e2SVlad Buslov 	refcount_set(&block->refcnt, 1);
91448617387SJiri Pirko 	block->net = net;
915bb047dddSJiri Pirko 	block->index = block_index;
916bb047dddSJiri Pirko 
917bb047dddSJiri Pirko 	/* Don't store q pointer for blocks which are shared */
918bb047dddSJiri Pirko 	if (!tcf_block_shared(block))
91948617387SJiri Pirko 		block->q = q;
92048617387SJiri Pirko 	return block;
92148617387SJiri Pirko }
92248617387SJiri Pirko 
92348617387SJiri Pirko static struct tcf_block *tcf_block_lookup(struct net *net, u32 block_index)
92448617387SJiri Pirko {
92548617387SJiri Pirko 	struct tcf_net *tn = net_generic(net, tcf_net_id);
92648617387SJiri Pirko 
927322d884bSMatthew Wilcox 	return idr_find(&tn->idr, block_index);
92848617387SJiri Pirko }
92948617387SJiri Pirko 
9300607e439SVlad Buslov static struct tcf_block *tcf_block_refcnt_get(struct net *net, u32 block_index)
9310607e439SVlad Buslov {
9320607e439SVlad Buslov 	struct tcf_block *block;
9330607e439SVlad Buslov 
9340607e439SVlad Buslov 	rcu_read_lock();
9350607e439SVlad Buslov 	block = tcf_block_lookup(net, block_index);
9360607e439SVlad Buslov 	if (block && !refcount_inc_not_zero(&block->refcnt))
9370607e439SVlad Buslov 		block = NULL;
9380607e439SVlad Buslov 	rcu_read_unlock();
9390607e439SVlad Buslov 
9400607e439SVlad Buslov 	return block;
9410607e439SVlad Buslov }
9420607e439SVlad Buslov 
943bbf73830SVlad Buslov static struct tcf_chain *
944bbf73830SVlad Buslov __tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
945bbf73830SVlad Buslov {
946bbf73830SVlad Buslov 	mutex_lock(&block->lock);
947bbf73830SVlad Buslov 	if (chain)
948bbf73830SVlad Buslov 		chain = list_is_last(&chain->list, &block->chain_list) ?
949bbf73830SVlad Buslov 			NULL : list_next_entry(chain, list);
950bbf73830SVlad Buslov 	else
951bbf73830SVlad Buslov 		chain = list_first_entry_or_null(&block->chain_list,
952bbf73830SVlad Buslov 						 struct tcf_chain, list);
953bbf73830SVlad Buslov 
954bbf73830SVlad Buslov 	/* skip all action-only chains */
955bbf73830SVlad Buslov 	while (chain && tcf_chain_held_by_acts_only(chain))
956bbf73830SVlad Buslov 		chain = list_is_last(&chain->list, &block->chain_list) ?
957bbf73830SVlad Buslov 			NULL : list_next_entry(chain, list);
958bbf73830SVlad Buslov 
959bbf73830SVlad Buslov 	if (chain)
960bbf73830SVlad Buslov 		tcf_chain_hold(chain);
961bbf73830SVlad Buslov 	mutex_unlock(&block->lock);
962bbf73830SVlad Buslov 
963bbf73830SVlad Buslov 	return chain;
964bbf73830SVlad Buslov }
965bbf73830SVlad Buslov 
966bbf73830SVlad Buslov /* Function to be used by all clients that want to iterate over all chains on
967bbf73830SVlad Buslov  * block. It properly obtains block->lock and takes reference to chain before
968bbf73830SVlad Buslov  * returning it. Users of this function must be tolerant to concurrent chain
969bbf73830SVlad Buslov  * insertion/deletion or ensure that no concurrent chain modification is
970bbf73830SVlad Buslov  * possible. Note that all netlink dump callbacks cannot guarantee to provide
971bbf73830SVlad Buslov  * consistent dump because rtnl lock is released each time skb is filled with
972bbf73830SVlad Buslov  * data and sent to user-space.
973bbf73830SVlad Buslov  */
974bbf73830SVlad Buslov 
975bbf73830SVlad Buslov struct tcf_chain *
976bbf73830SVlad Buslov tcf_get_next_chain(struct tcf_block *block, struct tcf_chain *chain)
977bbf73830SVlad Buslov {
978bbf73830SVlad Buslov 	struct tcf_chain *chain_next = __tcf_get_next_chain(block, chain);
979bbf73830SVlad Buslov 
980bbf73830SVlad Buslov 	if (chain)
981bbf73830SVlad Buslov 		tcf_chain_put(chain);
982bbf73830SVlad Buslov 
983bbf73830SVlad Buslov 	return chain_next;
984bbf73830SVlad Buslov }
985bbf73830SVlad Buslov EXPORT_SYMBOL(tcf_get_next_chain);
986bbf73830SVlad Buslov 
987fe2923afSVlad Buslov static struct tcf_proto *
988fe2923afSVlad Buslov __tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp)
989fe2923afSVlad Buslov {
9908b64678eSVlad Buslov 	u32 prio = 0;
9918b64678eSVlad Buslov 
992fe2923afSVlad Buslov 	ASSERT_RTNL();
993fe2923afSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
994fe2923afSVlad Buslov 
9958b64678eSVlad Buslov 	if (!tp) {
996fe2923afSVlad Buslov 		tp = tcf_chain_dereference(chain->filter_chain, chain);
9978b64678eSVlad Buslov 	} else if (tcf_proto_is_deleting(tp)) {
9988b64678eSVlad Buslov 		/* 'deleting' flag is set and chain->filter_chain_lock was
9998b64678eSVlad Buslov 		 * unlocked, which means next pointer could be invalid. Restart
10008b64678eSVlad Buslov 		 * search.
10018b64678eSVlad Buslov 		 */
10028b64678eSVlad Buslov 		prio = tp->prio + 1;
10038b64678eSVlad Buslov 		tp = tcf_chain_dereference(chain->filter_chain, chain);
10048b64678eSVlad Buslov 
10058b64678eSVlad Buslov 		for (; tp; tp = tcf_chain_dereference(tp->next, chain))
10068b64678eSVlad Buslov 			if (!tp->deleting && tp->prio >= prio)
10078b64678eSVlad Buslov 				break;
10088b64678eSVlad Buslov 	} else {
1009fe2923afSVlad Buslov 		tp = tcf_chain_dereference(tp->next, chain);
10108b64678eSVlad Buslov 	}
1011fe2923afSVlad Buslov 
1012fe2923afSVlad Buslov 	if (tp)
1013fe2923afSVlad Buslov 		tcf_proto_get(tp);
1014fe2923afSVlad Buslov 
1015fe2923afSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
1016fe2923afSVlad Buslov 
1017fe2923afSVlad Buslov 	return tp;
1018fe2923afSVlad Buslov }
1019fe2923afSVlad Buslov 
1020fe2923afSVlad Buslov /* Function to be used by all clients that want to iterate over all tp's on
1021fe2923afSVlad Buslov  * chain. Users of this function must be tolerant to concurrent tp
1022fe2923afSVlad Buslov  * insertion/deletion or ensure that no concurrent chain modification is
1023fe2923afSVlad Buslov  * possible. Note that all netlink dump callbacks cannot guarantee to provide
1024fe2923afSVlad Buslov  * consistent dump because rtnl lock is released each time skb is filled with
1025fe2923afSVlad Buslov  * data and sent to user-space.
1026fe2923afSVlad Buslov  */
1027fe2923afSVlad Buslov 
1028fe2923afSVlad Buslov struct tcf_proto *
102912db03b6SVlad Buslov tcf_get_next_proto(struct tcf_chain *chain, struct tcf_proto *tp,
103012db03b6SVlad Buslov 		   bool rtnl_held)
1031fe2923afSVlad Buslov {
1032fe2923afSVlad Buslov 	struct tcf_proto *tp_next = __tcf_get_next_proto(chain, tp);
1033fe2923afSVlad Buslov 
1034fe2923afSVlad Buslov 	if (tp)
103512db03b6SVlad Buslov 		tcf_proto_put(tp, rtnl_held, NULL);
1036fe2923afSVlad Buslov 
1037fe2923afSVlad Buslov 	return tp_next;
1038fe2923afSVlad Buslov }
1039fe2923afSVlad Buslov EXPORT_SYMBOL(tcf_get_next_proto);
1040fe2923afSVlad Buslov 
104112db03b6SVlad Buslov static void tcf_block_flush_all_chains(struct tcf_block *block, bool rtnl_held)
1042f0023436SVlad Buslov {
1043f0023436SVlad Buslov 	struct tcf_chain *chain;
1044f0023436SVlad Buslov 
1045bbf73830SVlad Buslov 	/* Last reference to block. At this point chains cannot be added or
1046bbf73830SVlad Buslov 	 * removed concurrently.
1047f0023436SVlad Buslov 	 */
1048bbf73830SVlad Buslov 	for (chain = tcf_get_next_chain(block, NULL);
1049bbf73830SVlad Buslov 	     chain;
1050bbf73830SVlad Buslov 	     chain = tcf_get_next_chain(block, chain)) {
1051f0023436SVlad Buslov 		tcf_chain_put_explicitly_created(chain);
105212db03b6SVlad Buslov 		tcf_chain_flush(chain, rtnl_held);
1053f0023436SVlad Buslov 	}
1054f0023436SVlad Buslov }
1055f0023436SVlad Buslov 
105618d3eefbSVlad Buslov /* Lookup Qdisc and increments its reference counter.
105718d3eefbSVlad Buslov  * Set parent, if necessary.
105818d3eefbSVlad Buslov  */
105918d3eefbSVlad Buslov 
106018d3eefbSVlad Buslov static int __tcf_qdisc_find(struct net *net, struct Qdisc **q,
106118d3eefbSVlad Buslov 			    u32 *parent, int ifindex, bool rtnl_held,
106218d3eefbSVlad Buslov 			    struct netlink_ext_ack *extack)
106318d3eefbSVlad Buslov {
106418d3eefbSVlad Buslov 	const struct Qdisc_class_ops *cops;
106518d3eefbSVlad Buslov 	struct net_device *dev;
106618d3eefbSVlad Buslov 	int err = 0;
106718d3eefbSVlad Buslov 
106818d3eefbSVlad Buslov 	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
106918d3eefbSVlad Buslov 		return 0;
107018d3eefbSVlad Buslov 
107118d3eefbSVlad Buslov 	rcu_read_lock();
107218d3eefbSVlad Buslov 
107318d3eefbSVlad Buslov 	/* Find link */
107418d3eefbSVlad Buslov 	dev = dev_get_by_index_rcu(net, ifindex);
107518d3eefbSVlad Buslov 	if (!dev) {
107618d3eefbSVlad Buslov 		rcu_read_unlock();
107718d3eefbSVlad Buslov 		return -ENODEV;
107818d3eefbSVlad Buslov 	}
107918d3eefbSVlad Buslov 
108018d3eefbSVlad Buslov 	/* Find qdisc */
108118d3eefbSVlad Buslov 	if (!*parent) {
108218d3eefbSVlad Buslov 		*q = dev->qdisc;
108318d3eefbSVlad Buslov 		*parent = (*q)->handle;
108418d3eefbSVlad Buslov 	} else {
108518d3eefbSVlad Buslov 		*q = qdisc_lookup_rcu(dev, TC_H_MAJ(*parent));
108618d3eefbSVlad Buslov 		if (!*q) {
108718d3eefbSVlad Buslov 			NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
108818d3eefbSVlad Buslov 			err = -EINVAL;
108918d3eefbSVlad Buslov 			goto errout_rcu;
109018d3eefbSVlad Buslov 		}
109118d3eefbSVlad Buslov 	}
109218d3eefbSVlad Buslov 
109318d3eefbSVlad Buslov 	*q = qdisc_refcount_inc_nz(*q);
109418d3eefbSVlad Buslov 	if (!*q) {
109518d3eefbSVlad Buslov 		NL_SET_ERR_MSG(extack, "Parent Qdisc doesn't exists");
109618d3eefbSVlad Buslov 		err = -EINVAL;
109718d3eefbSVlad Buslov 		goto errout_rcu;
109818d3eefbSVlad Buslov 	}
109918d3eefbSVlad Buslov 
110018d3eefbSVlad Buslov 	/* Is it classful? */
110118d3eefbSVlad Buslov 	cops = (*q)->ops->cl_ops;
110218d3eefbSVlad Buslov 	if (!cops) {
110318d3eefbSVlad Buslov 		NL_SET_ERR_MSG(extack, "Qdisc not classful");
110418d3eefbSVlad Buslov 		err = -EINVAL;
110518d3eefbSVlad Buslov 		goto errout_qdisc;
110618d3eefbSVlad Buslov 	}
110718d3eefbSVlad Buslov 
110818d3eefbSVlad Buslov 	if (!cops->tcf_block) {
110918d3eefbSVlad Buslov 		NL_SET_ERR_MSG(extack, "Class doesn't support blocks");
111018d3eefbSVlad Buslov 		err = -EOPNOTSUPP;
111118d3eefbSVlad Buslov 		goto errout_qdisc;
111218d3eefbSVlad Buslov 	}
111318d3eefbSVlad Buslov 
111418d3eefbSVlad Buslov errout_rcu:
111518d3eefbSVlad Buslov 	/* At this point we know that qdisc is not noop_qdisc,
111618d3eefbSVlad Buslov 	 * which means that qdisc holds a reference to net_device
111718d3eefbSVlad Buslov 	 * and we hold a reference to qdisc, so it is safe to release
111818d3eefbSVlad Buslov 	 * rcu read lock.
111918d3eefbSVlad Buslov 	 */
112018d3eefbSVlad Buslov 	rcu_read_unlock();
112118d3eefbSVlad Buslov 	return err;
112218d3eefbSVlad Buslov 
112318d3eefbSVlad Buslov errout_qdisc:
112418d3eefbSVlad Buslov 	rcu_read_unlock();
112518d3eefbSVlad Buslov 
112618d3eefbSVlad Buslov 	if (rtnl_held)
112718d3eefbSVlad Buslov 		qdisc_put(*q);
112818d3eefbSVlad Buslov 	else
112918d3eefbSVlad Buslov 		qdisc_put_unlocked(*q);
113018d3eefbSVlad Buslov 	*q = NULL;
113118d3eefbSVlad Buslov 
113218d3eefbSVlad Buslov 	return err;
113318d3eefbSVlad Buslov }
113418d3eefbSVlad Buslov 
113518d3eefbSVlad Buslov static int __tcf_qdisc_cl_find(struct Qdisc *q, u32 parent, unsigned long *cl,
113618d3eefbSVlad Buslov 			       int ifindex, struct netlink_ext_ack *extack)
113718d3eefbSVlad Buslov {
113818d3eefbSVlad Buslov 	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK)
113918d3eefbSVlad Buslov 		return 0;
114018d3eefbSVlad Buslov 
114118d3eefbSVlad Buslov 	/* Do we search for filter, attached to class? */
114218d3eefbSVlad Buslov 	if (TC_H_MIN(parent)) {
114318d3eefbSVlad Buslov 		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
114418d3eefbSVlad Buslov 
114518d3eefbSVlad Buslov 		*cl = cops->find(q, parent);
114618d3eefbSVlad Buslov 		if (*cl == 0) {
114718d3eefbSVlad Buslov 			NL_SET_ERR_MSG(extack, "Specified class doesn't exist");
114818d3eefbSVlad Buslov 			return -ENOENT;
114918d3eefbSVlad Buslov 		}
115018d3eefbSVlad Buslov 	}
115118d3eefbSVlad Buslov 
115218d3eefbSVlad Buslov 	return 0;
115318d3eefbSVlad Buslov }
115418d3eefbSVlad Buslov 
115518d3eefbSVlad Buslov static struct tcf_block *__tcf_block_find(struct net *net, struct Qdisc *q,
115618d3eefbSVlad Buslov 					  unsigned long cl, int ifindex,
115718d3eefbSVlad Buslov 					  u32 block_index,
115818d3eefbSVlad Buslov 					  struct netlink_ext_ack *extack)
115918d3eefbSVlad Buslov {
116018d3eefbSVlad Buslov 	struct tcf_block *block;
116118d3eefbSVlad Buslov 
116218d3eefbSVlad Buslov 	if (ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
116318d3eefbSVlad Buslov 		block = tcf_block_refcnt_get(net, block_index);
116418d3eefbSVlad Buslov 		if (!block) {
116518d3eefbSVlad Buslov 			NL_SET_ERR_MSG(extack, "Block of given index was not found");
116618d3eefbSVlad Buslov 			return ERR_PTR(-EINVAL);
116718d3eefbSVlad Buslov 		}
116818d3eefbSVlad Buslov 	} else {
116918d3eefbSVlad Buslov 		const struct Qdisc_class_ops *cops = q->ops->cl_ops;
117018d3eefbSVlad Buslov 
117118d3eefbSVlad Buslov 		block = cops->tcf_block(q, cl, extack);
117218d3eefbSVlad Buslov 		if (!block)
117318d3eefbSVlad Buslov 			return ERR_PTR(-EINVAL);
117418d3eefbSVlad Buslov 
117518d3eefbSVlad Buslov 		if (tcf_block_shared(block)) {
117618d3eefbSVlad Buslov 			NL_SET_ERR_MSG(extack, "This filter block is shared. Please use the block index to manipulate the filters");
117718d3eefbSVlad Buslov 			return ERR_PTR(-EOPNOTSUPP);
117818d3eefbSVlad Buslov 		}
117918d3eefbSVlad Buslov 
118018d3eefbSVlad Buslov 		/* Always take reference to block in order to support execution
118118d3eefbSVlad Buslov 		 * of rules update path of cls API without rtnl lock. Caller
118218d3eefbSVlad Buslov 		 * must release block when it is finished using it. 'if' block
118318d3eefbSVlad Buslov 		 * of this conditional obtain reference to block by calling
118418d3eefbSVlad Buslov 		 * tcf_block_refcnt_get().
118518d3eefbSVlad Buslov 		 */
118618d3eefbSVlad Buslov 		refcount_inc(&block->refcnt);
118718d3eefbSVlad Buslov 	}
118818d3eefbSVlad Buslov 
118918d3eefbSVlad Buslov 	return block;
119018d3eefbSVlad Buslov }
119118d3eefbSVlad Buslov 
11920607e439SVlad Buslov static void __tcf_block_put(struct tcf_block *block, struct Qdisc *q,
119312db03b6SVlad Buslov 			    struct tcf_block_ext_info *ei, bool rtnl_held)
11940607e439SVlad Buslov {
1195c266f64dSVlad Buslov 	if (refcount_dec_and_mutex_lock(&block->refcnt, &block->lock)) {
11960607e439SVlad Buslov 		/* Flushing/putting all chains will cause the block to be
11970607e439SVlad Buslov 		 * deallocated when last chain is freed. However, if chain_list
11980607e439SVlad Buslov 		 * is empty, block has to be manually deallocated. After block
11990607e439SVlad Buslov 		 * reference counter reached 0, it is no longer possible to
12000607e439SVlad Buslov 		 * increment it or add new chains to block.
12010607e439SVlad Buslov 		 */
12020607e439SVlad Buslov 		bool free_block = list_empty(&block->chain_list);
12030607e439SVlad Buslov 
1204c266f64dSVlad Buslov 		mutex_unlock(&block->lock);
12050607e439SVlad Buslov 		if (tcf_block_shared(block))
12060607e439SVlad Buslov 			tcf_block_remove(block, block->net);
12070607e439SVlad Buslov 
12080607e439SVlad Buslov 		if (q)
12090607e439SVlad Buslov 			tcf_block_offload_unbind(block, q, ei);
12100607e439SVlad Buslov 
12110607e439SVlad Buslov 		if (free_block)
1212c266f64dSVlad Buslov 			tcf_block_destroy(block);
12130607e439SVlad Buslov 		else
121412db03b6SVlad Buslov 			tcf_block_flush_all_chains(block, rtnl_held);
12150607e439SVlad Buslov 	} else if (q) {
12160607e439SVlad Buslov 		tcf_block_offload_unbind(block, q, ei);
12170607e439SVlad Buslov 	}
12180607e439SVlad Buslov }
12190607e439SVlad Buslov 
122012db03b6SVlad Buslov static void tcf_block_refcnt_put(struct tcf_block *block, bool rtnl_held)
12210607e439SVlad Buslov {
122212db03b6SVlad Buslov 	__tcf_block_put(block, NULL, NULL, rtnl_held);
12230607e439SVlad Buslov }
12240607e439SVlad Buslov 
1225c431f89bSVlad Buslov /* Find tcf block.
1226c431f89bSVlad Buslov  * Set q, parent, cl when appropriate.
1227c431f89bSVlad Buslov  */
1228c431f89bSVlad Buslov 
1229c431f89bSVlad Buslov static struct tcf_block *tcf_block_find(struct net *net, struct Qdisc **q,
1230c431f89bSVlad Buslov 					u32 *parent, unsigned long *cl,
1231c431f89bSVlad Buslov 					int ifindex, u32 block_index,
1232c431f89bSVlad Buslov 					struct netlink_ext_ack *extack)
1233c431f89bSVlad Buslov {
1234c431f89bSVlad Buslov 	struct tcf_block *block;
1235e368fdb6SVlad Buslov 	int err = 0;
1236c431f89bSVlad Buslov 
123718d3eefbSVlad Buslov 	ASSERT_RTNL();
1238c431f89bSVlad Buslov 
123918d3eefbSVlad Buslov 	err = __tcf_qdisc_find(net, q, parent, ifindex, true, extack);
124018d3eefbSVlad Buslov 	if (err)
124118d3eefbSVlad Buslov 		goto errout;
1242e368fdb6SVlad Buslov 
124318d3eefbSVlad Buslov 	err = __tcf_qdisc_cl_find(*q, *parent, cl, ifindex, extack);
124418d3eefbSVlad Buslov 	if (err)
1245e368fdb6SVlad Buslov 		goto errout_qdisc;
1246c431f89bSVlad Buslov 
124718d3eefbSVlad Buslov 	block = __tcf_block_find(net, *q, *cl, ifindex, block_index, extack);
1248af736bf0SDan Carpenter 	if (IS_ERR(block)) {
1249af736bf0SDan Carpenter 		err = PTR_ERR(block);
1250e368fdb6SVlad Buslov 		goto errout_qdisc;
1251af736bf0SDan Carpenter 	}
1252c431f89bSVlad Buslov 
1253c431f89bSVlad Buslov 	return block;
1254e368fdb6SVlad Buslov 
1255e368fdb6SVlad Buslov errout_qdisc:
125618d3eefbSVlad Buslov 	if (*q)
1257e368fdb6SVlad Buslov 		qdisc_put(*q);
125818d3eefbSVlad Buslov errout:
1259460b3601SCong Wang 	*q = NULL;
1260e368fdb6SVlad Buslov 	return ERR_PTR(err);
1261e368fdb6SVlad Buslov }
1262e368fdb6SVlad Buslov 
126312db03b6SVlad Buslov static void tcf_block_release(struct Qdisc *q, struct tcf_block *block,
126412db03b6SVlad Buslov 			      bool rtnl_held)
1265e368fdb6SVlad Buslov {
1266787ce6d0SVlad Buslov 	if (!IS_ERR_OR_NULL(block))
126712db03b6SVlad Buslov 		tcf_block_refcnt_put(block, rtnl_held);
1268787ce6d0SVlad Buslov 
1269470502deSVlad Buslov 	if (q) {
1270470502deSVlad Buslov 		if (rtnl_held)
1271e368fdb6SVlad Buslov 			qdisc_put(q);
1272470502deSVlad Buslov 		else
1273470502deSVlad Buslov 			qdisc_put_unlocked(q);
1274470502deSVlad Buslov 	}
1275c431f89bSVlad Buslov }
1276c431f89bSVlad Buslov 
1277f36fe1c4SJiri Pirko struct tcf_block_owner_item {
1278f36fe1c4SJiri Pirko 	struct list_head list;
1279f36fe1c4SJiri Pirko 	struct Qdisc *q;
128032f8c409SPablo Neira Ayuso 	enum flow_block_binder_type binder_type;
1281f36fe1c4SJiri Pirko };
1282f36fe1c4SJiri Pirko 
1283f36fe1c4SJiri Pirko static void
1284f36fe1c4SJiri Pirko tcf_block_owner_netif_keep_dst(struct tcf_block *block,
1285f36fe1c4SJiri Pirko 			       struct Qdisc *q,
128632f8c409SPablo Neira Ayuso 			       enum flow_block_binder_type binder_type)
1287f36fe1c4SJiri Pirko {
1288f36fe1c4SJiri Pirko 	if (block->keep_dst &&
128932f8c409SPablo Neira Ayuso 	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS &&
129032f8c409SPablo Neira Ayuso 	    binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_EGRESS)
1291f36fe1c4SJiri Pirko 		netif_keep_dst(qdisc_dev(q));
1292f36fe1c4SJiri Pirko }
1293f36fe1c4SJiri Pirko 
1294f36fe1c4SJiri Pirko void tcf_block_netif_keep_dst(struct tcf_block *block)
1295f36fe1c4SJiri Pirko {
1296f36fe1c4SJiri Pirko 	struct tcf_block_owner_item *item;
1297f36fe1c4SJiri Pirko 
1298f36fe1c4SJiri Pirko 	block->keep_dst = true;
1299f36fe1c4SJiri Pirko 	list_for_each_entry(item, &block->owner_list, list)
1300f36fe1c4SJiri Pirko 		tcf_block_owner_netif_keep_dst(block, item->q,
1301f36fe1c4SJiri Pirko 					       item->binder_type);
1302f36fe1c4SJiri Pirko }
1303f36fe1c4SJiri Pirko EXPORT_SYMBOL(tcf_block_netif_keep_dst);
1304f36fe1c4SJiri Pirko 
1305f36fe1c4SJiri Pirko static int tcf_block_owner_add(struct tcf_block *block,
1306f36fe1c4SJiri Pirko 			       struct Qdisc *q,
130732f8c409SPablo Neira Ayuso 			       enum flow_block_binder_type binder_type)
1308f36fe1c4SJiri Pirko {
1309f36fe1c4SJiri Pirko 	struct tcf_block_owner_item *item;
1310f36fe1c4SJiri Pirko 
1311f36fe1c4SJiri Pirko 	item = kmalloc(sizeof(*item), GFP_KERNEL);
1312f36fe1c4SJiri Pirko 	if (!item)
1313f36fe1c4SJiri Pirko 		return -ENOMEM;
1314f36fe1c4SJiri Pirko 	item->q = q;
1315f36fe1c4SJiri Pirko 	item->binder_type = binder_type;
1316f36fe1c4SJiri Pirko 	list_add(&item->list, &block->owner_list);
1317f36fe1c4SJiri Pirko 	return 0;
1318f36fe1c4SJiri Pirko }
1319f36fe1c4SJiri Pirko 
1320f36fe1c4SJiri Pirko static void tcf_block_owner_del(struct tcf_block *block,
1321f36fe1c4SJiri Pirko 				struct Qdisc *q,
132232f8c409SPablo Neira Ayuso 				enum flow_block_binder_type binder_type)
1323f36fe1c4SJiri Pirko {
1324f36fe1c4SJiri Pirko 	struct tcf_block_owner_item *item;
1325f36fe1c4SJiri Pirko 
1326f36fe1c4SJiri Pirko 	list_for_each_entry(item, &block->owner_list, list) {
1327f36fe1c4SJiri Pirko 		if (item->q == q && item->binder_type == binder_type) {
1328f36fe1c4SJiri Pirko 			list_del(&item->list);
1329f36fe1c4SJiri Pirko 			kfree(item);
1330f36fe1c4SJiri Pirko 			return;
1331f36fe1c4SJiri Pirko 		}
1332f36fe1c4SJiri Pirko 	}
1333f36fe1c4SJiri Pirko 	WARN_ON(1);
1334f36fe1c4SJiri Pirko }
1335f36fe1c4SJiri Pirko 
133648617387SJiri Pirko int tcf_block_get_ext(struct tcf_block **p_block, struct Qdisc *q,
133748617387SJiri Pirko 		      struct tcf_block_ext_info *ei,
133848617387SJiri Pirko 		      struct netlink_ext_ack *extack)
133948617387SJiri Pirko {
134048617387SJiri Pirko 	struct net *net = qdisc_net(q);
134148617387SJiri Pirko 	struct tcf_block *block = NULL;
134248617387SJiri Pirko 	int err;
134348617387SJiri Pirko 
1344787ce6d0SVlad Buslov 	if (ei->block_index)
134548617387SJiri Pirko 		/* block_index not 0 means the shared block is requested */
1346787ce6d0SVlad Buslov 		block = tcf_block_refcnt_get(net, ei->block_index);
134748617387SJiri Pirko 
134848617387SJiri Pirko 	if (!block) {
1349bb047dddSJiri Pirko 		block = tcf_block_create(net, q, ei->block_index, extack);
135048617387SJiri Pirko 		if (IS_ERR(block))
135148617387SJiri Pirko 			return PTR_ERR(block);
1352bb047dddSJiri Pirko 		if (tcf_block_shared(block)) {
1353bb047dddSJiri Pirko 			err = tcf_block_insert(block, net, extack);
135448617387SJiri Pirko 			if (err)
135548617387SJiri Pirko 				goto err_block_insert;
135648617387SJiri Pirko 		}
135748617387SJiri Pirko 	}
135848617387SJiri Pirko 
1359f36fe1c4SJiri Pirko 	err = tcf_block_owner_add(block, q, ei->binder_type);
1360f36fe1c4SJiri Pirko 	if (err)
1361f36fe1c4SJiri Pirko 		goto err_block_owner_add;
1362f36fe1c4SJiri Pirko 
1363f36fe1c4SJiri Pirko 	tcf_block_owner_netif_keep_dst(block, q, ei->binder_type);
1364f36fe1c4SJiri Pirko 
1365f71e0ca4SJiri Pirko 	err = tcf_chain0_head_change_cb_add(block, ei, extack);
1366a9b19443SJiri Pirko 	if (err)
1367f71e0ca4SJiri Pirko 		goto err_chain0_head_change_cb_add;
1368caa72601SJiri Pirko 
136960513bd8SJohn Hurley 	err = tcf_block_offload_bind(block, q, ei, extack);
1370caa72601SJiri Pirko 	if (err)
1371caa72601SJiri Pirko 		goto err_block_offload_bind;
1372caa72601SJiri Pirko 
13736529eabaSJiri Pirko 	*p_block = block;
13746529eabaSJiri Pirko 	return 0;
13752190d1d0SJiri Pirko 
1376caa72601SJiri Pirko err_block_offload_bind:
1377f71e0ca4SJiri Pirko 	tcf_chain0_head_change_cb_del(block, ei);
1378f71e0ca4SJiri Pirko err_chain0_head_change_cb_add:
1379f36fe1c4SJiri Pirko 	tcf_block_owner_del(block, q, ei->binder_type);
1380f36fe1c4SJiri Pirko err_block_owner_add:
138148617387SJiri Pirko err_block_insert:
138212db03b6SVlad Buslov 	tcf_block_refcnt_put(block, true);
13832190d1d0SJiri Pirko 	return err;
13846529eabaSJiri Pirko }
13858c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_get_ext);
13868c4083b3SJiri Pirko 
1387c7eb7d72SJiri Pirko static void tcf_chain_head_change_dflt(struct tcf_proto *tp_head, void *priv)
1388c7eb7d72SJiri Pirko {
1389c7eb7d72SJiri Pirko 	struct tcf_proto __rcu **p_filter_chain = priv;
1390c7eb7d72SJiri Pirko 
1391c7eb7d72SJiri Pirko 	rcu_assign_pointer(*p_filter_chain, tp_head);
1392c7eb7d72SJiri Pirko }
1393c7eb7d72SJiri Pirko 
13948c4083b3SJiri Pirko int tcf_block_get(struct tcf_block **p_block,
13958d1a77f9SAlexander Aring 		  struct tcf_proto __rcu **p_filter_chain, struct Qdisc *q,
13968d1a77f9SAlexander Aring 		  struct netlink_ext_ack *extack)
13978c4083b3SJiri Pirko {
1398c7eb7d72SJiri Pirko 	struct tcf_block_ext_info ei = {
1399c7eb7d72SJiri Pirko 		.chain_head_change = tcf_chain_head_change_dflt,
1400c7eb7d72SJiri Pirko 		.chain_head_change_priv = p_filter_chain,
1401c7eb7d72SJiri Pirko 	};
14028c4083b3SJiri Pirko 
1403c7eb7d72SJiri Pirko 	WARN_ON(!p_filter_chain);
14048d1a77f9SAlexander Aring 	return tcf_block_get_ext(p_block, q, &ei, extack);
14058c4083b3SJiri Pirko }
14066529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_get);
14076529eabaSJiri Pirko 
14087aa0045dSCong Wang /* XXX: Standalone actions are not allowed to jump to any chain, and bound
1409a60b3f51SRoman Kapl  * actions should be all removed after flushing.
1410e2ef7544SCong Wang  */
1411c7eb7d72SJiri Pirko void tcf_block_put_ext(struct tcf_block *block, struct Qdisc *q,
1412e1ea2f98SDavid S. Miller 		       struct tcf_block_ext_info *ei)
14137aa0045dSCong Wang {
1414c30abd5eSDavid S. Miller 	if (!block)
1415c30abd5eSDavid S. Miller 		return;
1416f71e0ca4SJiri Pirko 	tcf_chain0_head_change_cb_del(block, ei);
1417f36fe1c4SJiri Pirko 	tcf_block_owner_del(block, q, ei->binder_type);
141848617387SJiri Pirko 
141912db03b6SVlad Buslov 	__tcf_block_put(block, q, ei, true);
142048617387SJiri Pirko }
14218c4083b3SJiri Pirko EXPORT_SYMBOL(tcf_block_put_ext);
14228c4083b3SJiri Pirko 
14238c4083b3SJiri Pirko void tcf_block_put(struct tcf_block *block)
14248c4083b3SJiri Pirko {
14258c4083b3SJiri Pirko 	struct tcf_block_ext_info ei = {0, };
14268c4083b3SJiri Pirko 
14274853f128SJiri Pirko 	if (!block)
14284853f128SJiri Pirko 		return;
1429c7eb7d72SJiri Pirko 	tcf_block_put_ext(block, block->q, &ei);
14308c4083b3SJiri Pirko }
1431e1ea2f98SDavid S. Miller 
14326529eabaSJiri Pirko EXPORT_SYMBOL(tcf_block_put);
1433cf1facdaSJiri Pirko 
143432636742SJohn Hurley static int
1435a7323311SPablo Neira Ayuso tcf_block_playback_offloads(struct tcf_block *block, flow_setup_cb_t *cb,
143632636742SJohn Hurley 			    void *cb_priv, bool add, bool offload_in_use,
143732636742SJohn Hurley 			    struct netlink_ext_ack *extack)
143832636742SJohn Hurley {
1439bbf73830SVlad Buslov 	struct tcf_chain *chain, *chain_prev;
1440fe2923afSVlad Buslov 	struct tcf_proto *tp, *tp_prev;
144132636742SJohn Hurley 	int err;
144232636742SJohn Hurley 
14434f8116c8SVlad Buslov 	lockdep_assert_held(&block->cb_lock);
14444f8116c8SVlad Buslov 
1445bbf73830SVlad Buslov 	for (chain = __tcf_get_next_chain(block, NULL);
1446bbf73830SVlad Buslov 	     chain;
1447bbf73830SVlad Buslov 	     chain_prev = chain,
1448bbf73830SVlad Buslov 		     chain = __tcf_get_next_chain(block, chain),
1449bbf73830SVlad Buslov 		     tcf_chain_put(chain_prev)) {
1450fe2923afSVlad Buslov 		for (tp = __tcf_get_next_proto(chain, NULL); tp;
1451fe2923afSVlad Buslov 		     tp_prev = tp,
1452fe2923afSVlad Buslov 			     tp = __tcf_get_next_proto(chain, tp),
145312db03b6SVlad Buslov 			     tcf_proto_put(tp_prev, true, NULL)) {
145432636742SJohn Hurley 			if (tp->ops->reoffload) {
145532636742SJohn Hurley 				err = tp->ops->reoffload(tp, add, cb, cb_priv,
145632636742SJohn Hurley 							 extack);
145732636742SJohn Hurley 				if (err && add)
145832636742SJohn Hurley 					goto err_playback_remove;
145932636742SJohn Hurley 			} else if (add && offload_in_use) {
146032636742SJohn Hurley 				err = -EOPNOTSUPP;
146132636742SJohn Hurley 				NL_SET_ERR_MSG(extack, "Filter HW offload failed - classifier without re-offloading support");
146232636742SJohn Hurley 				goto err_playback_remove;
146332636742SJohn Hurley 			}
146432636742SJohn Hurley 		}
146532636742SJohn Hurley 	}
146632636742SJohn Hurley 
146732636742SJohn Hurley 	return 0;
146832636742SJohn Hurley 
146932636742SJohn Hurley err_playback_remove:
147012db03b6SVlad Buslov 	tcf_proto_put(tp, true, NULL);
1471bbf73830SVlad Buslov 	tcf_chain_put(chain);
147232636742SJohn Hurley 	tcf_block_playback_offloads(block, cb, cb_priv, false, offload_in_use,
147332636742SJohn Hurley 				    extack);
147432636742SJohn Hurley 	return err;
147532636742SJohn Hurley }
147632636742SJohn Hurley 
147759094b1eSPablo Neira Ayuso static int tcf_block_bind(struct tcf_block *block,
147859094b1eSPablo Neira Ayuso 			  struct flow_block_offload *bo)
147959094b1eSPablo Neira Ayuso {
148059094b1eSPablo Neira Ayuso 	struct flow_block_cb *block_cb, *next;
148159094b1eSPablo Neira Ayuso 	int err, i = 0;
148259094b1eSPablo Neira Ayuso 
14834f8116c8SVlad Buslov 	lockdep_assert_held(&block->cb_lock);
14844f8116c8SVlad Buslov 
148559094b1eSPablo Neira Ayuso 	list_for_each_entry(block_cb, &bo->cb_list, list) {
148659094b1eSPablo Neira Ayuso 		err = tcf_block_playback_offloads(block, block_cb->cb,
148759094b1eSPablo Neira Ayuso 						  block_cb->cb_priv, true,
148859094b1eSPablo Neira Ayuso 						  tcf_block_offload_in_use(block),
148959094b1eSPablo Neira Ayuso 						  bo->extack);
149059094b1eSPablo Neira Ayuso 		if (err)
149159094b1eSPablo Neira Ayuso 			goto err_unroll;
1492c9f14470SVlad Buslov 		if (!bo->unlocked_driver_cb)
1493c9f14470SVlad Buslov 			block->lockeddevcnt++;
149459094b1eSPablo Neira Ayuso 
149559094b1eSPablo Neira Ayuso 		i++;
149659094b1eSPablo Neira Ayuso 	}
149714bfb13fSPablo Neira Ayuso 	list_splice(&bo->cb_list, &block->flow_block.cb_list);
149859094b1eSPablo Neira Ayuso 
149959094b1eSPablo Neira Ayuso 	return 0;
150059094b1eSPablo Neira Ayuso 
150159094b1eSPablo Neira Ayuso err_unroll:
150259094b1eSPablo Neira Ayuso 	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
150359094b1eSPablo Neira Ayuso 		if (i-- > 0) {
150459094b1eSPablo Neira Ayuso 			list_del(&block_cb->list);
150559094b1eSPablo Neira Ayuso 			tcf_block_playback_offloads(block, block_cb->cb,
150659094b1eSPablo Neira Ayuso 						    block_cb->cb_priv, false,
150759094b1eSPablo Neira Ayuso 						    tcf_block_offload_in_use(block),
150859094b1eSPablo Neira Ayuso 						    NULL);
1509c9f14470SVlad Buslov 			if (!bo->unlocked_driver_cb)
1510c9f14470SVlad Buslov 				block->lockeddevcnt--;
151159094b1eSPablo Neira Ayuso 		}
151259094b1eSPablo Neira Ayuso 		flow_block_cb_free(block_cb);
151359094b1eSPablo Neira Ayuso 	}
151459094b1eSPablo Neira Ayuso 
151559094b1eSPablo Neira Ayuso 	return err;
151659094b1eSPablo Neira Ayuso }
151759094b1eSPablo Neira Ayuso 
151859094b1eSPablo Neira Ayuso static void tcf_block_unbind(struct tcf_block *block,
151959094b1eSPablo Neira Ayuso 			     struct flow_block_offload *bo)
152059094b1eSPablo Neira Ayuso {
152159094b1eSPablo Neira Ayuso 	struct flow_block_cb *block_cb, *next;
152259094b1eSPablo Neira Ayuso 
15234f8116c8SVlad Buslov 	lockdep_assert_held(&block->cb_lock);
15244f8116c8SVlad Buslov 
152559094b1eSPablo Neira Ayuso 	list_for_each_entry_safe(block_cb, next, &bo->cb_list, list) {
152659094b1eSPablo Neira Ayuso 		tcf_block_playback_offloads(block, block_cb->cb,
152759094b1eSPablo Neira Ayuso 					    block_cb->cb_priv, false,
152859094b1eSPablo Neira Ayuso 					    tcf_block_offload_in_use(block),
152959094b1eSPablo Neira Ayuso 					    NULL);
153059094b1eSPablo Neira Ayuso 		list_del(&block_cb->list);
153159094b1eSPablo Neira Ayuso 		flow_block_cb_free(block_cb);
1532c9f14470SVlad Buslov 		if (!bo->unlocked_driver_cb)
1533c9f14470SVlad Buslov 			block->lockeddevcnt--;
153459094b1eSPablo Neira Ayuso 	}
153559094b1eSPablo Neira Ayuso }
153659094b1eSPablo Neira Ayuso 
153759094b1eSPablo Neira Ayuso static int tcf_block_setup(struct tcf_block *block,
153859094b1eSPablo Neira Ayuso 			   struct flow_block_offload *bo)
153959094b1eSPablo Neira Ayuso {
154059094b1eSPablo Neira Ayuso 	int err;
154159094b1eSPablo Neira Ayuso 
154259094b1eSPablo Neira Ayuso 	switch (bo->command) {
154359094b1eSPablo Neira Ayuso 	case FLOW_BLOCK_BIND:
154459094b1eSPablo Neira Ayuso 		err = tcf_block_bind(block, bo);
154559094b1eSPablo Neira Ayuso 		break;
154659094b1eSPablo Neira Ayuso 	case FLOW_BLOCK_UNBIND:
154759094b1eSPablo Neira Ayuso 		err = 0;
154859094b1eSPablo Neira Ayuso 		tcf_block_unbind(block, bo);
154959094b1eSPablo Neira Ayuso 		break;
155059094b1eSPablo Neira Ayuso 	default:
155159094b1eSPablo Neira Ayuso 		WARN_ON_ONCE(1);
155259094b1eSPablo Neira Ayuso 		err = -EOPNOTSUPP;
155359094b1eSPablo Neira Ayuso 	}
155459094b1eSPablo Neira Ayuso 
155559094b1eSPablo Neira Ayuso 	return err;
155659094b1eSPablo Neira Ayuso }
155759094b1eSPablo Neira Ayuso 
155887d83093SJiri Pirko /* Main classifier routine: scans classifier chain attached
155987d83093SJiri Pirko  * to this qdisc, (optionally) tests for protocol and asks
156087d83093SJiri Pirko  * specific classifiers.
156187d83093SJiri Pirko  */
156287d83093SJiri Pirko int tcf_classify(struct sk_buff *skb, const struct tcf_proto *tp,
156387d83093SJiri Pirko 		 struct tcf_result *res, bool compat_mode)
156487d83093SJiri Pirko {
156587d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT
156687d83093SJiri Pirko 	const int max_reclassify_loop = 4;
1567ee538dceSJiri Pirko 	const struct tcf_proto *orig_tp = tp;
1568ee538dceSJiri Pirko 	const struct tcf_proto *first_tp;
156987d83093SJiri Pirko 	int limit = 0;
157087d83093SJiri Pirko 
157187d83093SJiri Pirko reclassify:
157287d83093SJiri Pirko #endif
157387d83093SJiri Pirko 	for (; tp; tp = rcu_dereference_bh(tp->next)) {
1574cd0c4e70SCong Wang 		__be16 protocol = tc_skb_protocol(skb);
157587d83093SJiri Pirko 		int err;
157687d83093SJiri Pirko 
157787d83093SJiri Pirko 		if (tp->protocol != protocol &&
157887d83093SJiri Pirko 		    tp->protocol != htons(ETH_P_ALL))
157987d83093SJiri Pirko 			continue;
158087d83093SJiri Pirko 
158187d83093SJiri Pirko 		err = tp->classify(skb, tp, res);
158287d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT
1583db50514fSJiri Pirko 		if (unlikely(err == TC_ACT_RECLASSIFY && !compat_mode)) {
1584ee538dceSJiri Pirko 			first_tp = orig_tp;
158587d83093SJiri Pirko 			goto reset;
1586db50514fSJiri Pirko 		} else if (unlikely(TC_ACT_EXT_CMP(err, TC_ACT_GOTO_CHAIN))) {
1587ee538dceSJiri Pirko 			first_tp = res->goto_tp;
158895a7233cSPaul Blakey 
158995a7233cSPaul Blakey #if IS_ENABLED(CONFIG_NET_TC_SKB_EXT)
159095a7233cSPaul Blakey 			{
159195a7233cSPaul Blakey 				struct tc_skb_ext *ext;
159295a7233cSPaul Blakey 
159395a7233cSPaul Blakey 				ext = skb_ext_add(skb, TC_SKB_EXT);
159495a7233cSPaul Blakey 				if (WARN_ON_ONCE(!ext))
159595a7233cSPaul Blakey 					return TC_ACT_SHOT;
159695a7233cSPaul Blakey 
159795a7233cSPaul Blakey 				ext->chain = err & TC_ACT_EXT_VAL_MASK;
159895a7233cSPaul Blakey 			}
159995a7233cSPaul Blakey #endif
1600db50514fSJiri Pirko 			goto reset;
1601db50514fSJiri Pirko 		}
160287d83093SJiri Pirko #endif
160387d83093SJiri Pirko 		if (err >= 0)
160487d83093SJiri Pirko 			return err;
160587d83093SJiri Pirko 	}
160687d83093SJiri Pirko 
160787d83093SJiri Pirko 	return TC_ACT_UNSPEC; /* signal: continue lookup */
160887d83093SJiri Pirko #ifdef CONFIG_NET_CLS_ACT
160987d83093SJiri Pirko reset:
161087d83093SJiri Pirko 	if (unlikely(limit++ >= max_reclassify_loop)) {
16119d3aaff3SJiri Pirko 		net_notice_ratelimited("%u: reclassify loop, rule prio %u, protocol %02x\n",
16129d3aaff3SJiri Pirko 				       tp->chain->block->index,
16139d3aaff3SJiri Pirko 				       tp->prio & 0xffff,
161487d83093SJiri Pirko 				       ntohs(tp->protocol));
161587d83093SJiri Pirko 		return TC_ACT_SHOT;
161687d83093SJiri Pirko 	}
161787d83093SJiri Pirko 
1618ee538dceSJiri Pirko 	tp = first_tp;
161987d83093SJiri Pirko 	goto reclassify;
162087d83093SJiri Pirko #endif
162187d83093SJiri Pirko }
162287d83093SJiri Pirko EXPORT_SYMBOL(tcf_classify);
162387d83093SJiri Pirko 
16242190d1d0SJiri Pirko struct tcf_chain_info {
16252190d1d0SJiri Pirko 	struct tcf_proto __rcu **pprev;
16262190d1d0SJiri Pirko 	struct tcf_proto __rcu *next;
16272190d1d0SJiri Pirko };
16282190d1d0SJiri Pirko 
1629ed76f5edSVlad Buslov static struct tcf_proto *tcf_chain_tp_prev(struct tcf_chain *chain,
1630ed76f5edSVlad Buslov 					   struct tcf_chain_info *chain_info)
16312190d1d0SJiri Pirko {
1632ed76f5edSVlad Buslov 	return tcf_chain_dereference(*chain_info->pprev, chain);
16332190d1d0SJiri Pirko }
16342190d1d0SJiri Pirko 
1635726d0612SVlad Buslov static int tcf_chain_tp_insert(struct tcf_chain *chain,
16362190d1d0SJiri Pirko 			       struct tcf_chain_info *chain_info,
16372190d1d0SJiri Pirko 			       struct tcf_proto *tp)
16382190d1d0SJiri Pirko {
1639726d0612SVlad Buslov 	if (chain->flushing)
1640726d0612SVlad Buslov 		return -EAGAIN;
1641726d0612SVlad Buslov 
1642c7eb7d72SJiri Pirko 	if (*chain_info->pprev == chain->filter_chain)
1643f71e0ca4SJiri Pirko 		tcf_chain0_head_change(chain, tp);
16444dbfa766SVlad Buslov 	tcf_proto_get(tp);
1645ed76f5edSVlad Buslov 	RCU_INIT_POINTER(tp->next, tcf_chain_tp_prev(chain, chain_info));
16462190d1d0SJiri Pirko 	rcu_assign_pointer(*chain_info->pprev, tp);
1647726d0612SVlad Buslov 
1648726d0612SVlad Buslov 	return 0;
16492190d1d0SJiri Pirko }
16502190d1d0SJiri Pirko 
16512190d1d0SJiri Pirko static void tcf_chain_tp_remove(struct tcf_chain *chain,
16522190d1d0SJiri Pirko 				struct tcf_chain_info *chain_info,
16532190d1d0SJiri Pirko 				struct tcf_proto *tp)
16542190d1d0SJiri Pirko {
1655ed76f5edSVlad Buslov 	struct tcf_proto *next = tcf_chain_dereference(chain_info->next, chain);
16562190d1d0SJiri Pirko 
16578b64678eSVlad Buslov 	tcf_proto_mark_delete(tp);
1658c7eb7d72SJiri Pirko 	if (tp == chain->filter_chain)
1659f71e0ca4SJiri Pirko 		tcf_chain0_head_change(chain, next);
16602190d1d0SJiri Pirko 	RCU_INIT_POINTER(*chain_info->pprev, next);
16612190d1d0SJiri Pirko }
16622190d1d0SJiri Pirko 
16632190d1d0SJiri Pirko static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
16642190d1d0SJiri Pirko 					   struct tcf_chain_info *chain_info,
16652190d1d0SJiri Pirko 					   u32 protocol, u32 prio,
16668b64678eSVlad Buslov 					   bool prio_allocate);
16678b64678eSVlad Buslov 
16688b64678eSVlad Buslov /* Try to insert new proto.
16698b64678eSVlad Buslov  * If proto with specified priority already exists, free new proto
16708b64678eSVlad Buslov  * and return existing one.
16718b64678eSVlad Buslov  */
16728b64678eSVlad Buslov 
16738b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_insert_unique(struct tcf_chain *chain,
16748b64678eSVlad Buslov 						    struct tcf_proto *tp_new,
167512db03b6SVlad Buslov 						    u32 protocol, u32 prio,
167612db03b6SVlad Buslov 						    bool rtnl_held)
16778b64678eSVlad Buslov {
16788b64678eSVlad Buslov 	struct tcf_chain_info chain_info;
16798b64678eSVlad Buslov 	struct tcf_proto *tp;
1680726d0612SVlad Buslov 	int err = 0;
16818b64678eSVlad Buslov 
16828b64678eSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
16838b64678eSVlad Buslov 
168459eb87cbSJohn Hurley 	if (tcf_proto_exists_destroying(chain, tp_new)) {
168559eb87cbSJohn Hurley 		mutex_unlock(&chain->filter_chain_lock);
168659eb87cbSJohn Hurley 		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
168759eb87cbSJohn Hurley 		return ERR_PTR(-EAGAIN);
168859eb87cbSJohn Hurley 	}
168959eb87cbSJohn Hurley 
16908b64678eSVlad Buslov 	tp = tcf_chain_tp_find(chain, &chain_info,
16918b64678eSVlad Buslov 			       protocol, prio, false);
16928b64678eSVlad Buslov 	if (!tp)
1693726d0612SVlad Buslov 		err = tcf_chain_tp_insert(chain, &chain_info, tp_new);
16948b64678eSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
16958b64678eSVlad Buslov 
16968b64678eSVlad Buslov 	if (tp) {
169759eb87cbSJohn Hurley 		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
16988b64678eSVlad Buslov 		tp_new = tp;
1699726d0612SVlad Buslov 	} else if (err) {
170059eb87cbSJohn Hurley 		tcf_proto_destroy(tp_new, rtnl_held, false, NULL);
1701726d0612SVlad Buslov 		tp_new = ERR_PTR(err);
17028b64678eSVlad Buslov 	}
17038b64678eSVlad Buslov 
17048b64678eSVlad Buslov 	return tp_new;
17058b64678eSVlad Buslov }
17068b64678eSVlad Buslov 
17078b64678eSVlad Buslov static void tcf_chain_tp_delete_empty(struct tcf_chain *chain,
170812db03b6SVlad Buslov 				      struct tcf_proto *tp, bool rtnl_held,
17098b64678eSVlad Buslov 				      struct netlink_ext_ack *extack)
17108b64678eSVlad Buslov {
17118b64678eSVlad Buslov 	struct tcf_chain_info chain_info;
17128b64678eSVlad Buslov 	struct tcf_proto *tp_iter;
17138b64678eSVlad Buslov 	struct tcf_proto **pprev;
17148b64678eSVlad Buslov 	struct tcf_proto *next;
17158b64678eSVlad Buslov 
17168b64678eSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
17178b64678eSVlad Buslov 
17188b64678eSVlad Buslov 	/* Atomically find and remove tp from chain. */
17198b64678eSVlad Buslov 	for (pprev = &chain->filter_chain;
17208b64678eSVlad Buslov 	     (tp_iter = tcf_chain_dereference(*pprev, chain));
17218b64678eSVlad Buslov 	     pprev = &tp_iter->next) {
17228b64678eSVlad Buslov 		if (tp_iter == tp) {
17238b64678eSVlad Buslov 			chain_info.pprev = pprev;
17248b64678eSVlad Buslov 			chain_info.next = tp_iter->next;
17258b64678eSVlad Buslov 			WARN_ON(tp_iter->deleting);
17268b64678eSVlad Buslov 			break;
17278b64678eSVlad Buslov 		}
17288b64678eSVlad Buslov 	}
17298b64678eSVlad Buslov 	/* Verify that tp still exists and no new filters were inserted
17308b64678eSVlad Buslov 	 * concurrently.
17318b64678eSVlad Buslov 	 * Mark tp for deletion if it is empty.
17328b64678eSVlad Buslov 	 */
1733a5b72a08SDavide Caratti 	if (!tp_iter || !tcf_proto_check_delete(tp)) {
17348b64678eSVlad Buslov 		mutex_unlock(&chain->filter_chain_lock);
17358b64678eSVlad Buslov 		return;
17368b64678eSVlad Buslov 	}
17378b64678eSVlad Buslov 
173859eb87cbSJohn Hurley 	tcf_proto_signal_destroying(chain, tp);
17398b64678eSVlad Buslov 	next = tcf_chain_dereference(chain_info.next, chain);
17408b64678eSVlad Buslov 	if (tp == chain->filter_chain)
17418b64678eSVlad Buslov 		tcf_chain0_head_change(chain, next);
17428b64678eSVlad Buslov 	RCU_INIT_POINTER(*chain_info.pprev, next);
17438b64678eSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
17448b64678eSVlad Buslov 
174512db03b6SVlad Buslov 	tcf_proto_put(tp, rtnl_held, extack);
17468b64678eSVlad Buslov }
17478b64678eSVlad Buslov 
17488b64678eSVlad Buslov static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain,
17498b64678eSVlad Buslov 					   struct tcf_chain_info *chain_info,
17508b64678eSVlad Buslov 					   u32 protocol, u32 prio,
17512190d1d0SJiri Pirko 					   bool prio_allocate)
17522190d1d0SJiri Pirko {
17532190d1d0SJiri Pirko 	struct tcf_proto **pprev;
17542190d1d0SJiri Pirko 	struct tcf_proto *tp;
17552190d1d0SJiri Pirko 
17562190d1d0SJiri Pirko 	/* Check the chain for existence of proto-tcf with this priority */
17572190d1d0SJiri Pirko 	for (pprev = &chain->filter_chain;
1758ed76f5edSVlad Buslov 	     (tp = tcf_chain_dereference(*pprev, chain));
1759ed76f5edSVlad Buslov 	     pprev = &tp->next) {
17602190d1d0SJiri Pirko 		if (tp->prio >= prio) {
17612190d1d0SJiri Pirko 			if (tp->prio == prio) {
17622190d1d0SJiri Pirko 				if (prio_allocate ||
17632190d1d0SJiri Pirko 				    (tp->protocol != protocol && protocol))
17642190d1d0SJiri Pirko 					return ERR_PTR(-EINVAL);
17652190d1d0SJiri Pirko 			} else {
17662190d1d0SJiri Pirko 				tp = NULL;
17672190d1d0SJiri Pirko 			}
17682190d1d0SJiri Pirko 			break;
17692190d1d0SJiri Pirko 		}
17702190d1d0SJiri Pirko 	}
17712190d1d0SJiri Pirko 	chain_info->pprev = pprev;
17724dbfa766SVlad Buslov 	if (tp) {
17734dbfa766SVlad Buslov 		chain_info->next = tp->next;
17744dbfa766SVlad Buslov 		tcf_proto_get(tp);
17754dbfa766SVlad Buslov 	} else {
17764dbfa766SVlad Buslov 		chain_info->next = NULL;
17774dbfa766SVlad Buslov 	}
17782190d1d0SJiri Pirko 	return tp;
17792190d1d0SJiri Pirko }
17802190d1d0SJiri Pirko 
17817120371cSWANG Cong static int tcf_fill_node(struct net *net, struct sk_buff *skb,
17827960d1daSJiri Pirko 			 struct tcf_proto *tp, struct tcf_block *block,
17837960d1daSJiri Pirko 			 struct Qdisc *q, u32 parent, void *fh,
178412db03b6SVlad Buslov 			 u32 portid, u32 seq, u16 flags, int event,
178512db03b6SVlad Buslov 			 bool rtnl_held)
17867120371cSWANG Cong {
17877120371cSWANG Cong 	struct tcmsg *tcm;
17887120371cSWANG Cong 	struct nlmsghdr  *nlh;
17897120371cSWANG Cong 	unsigned char *b = skb_tail_pointer(skb);
17907120371cSWANG Cong 
17917120371cSWANG Cong 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
17927120371cSWANG Cong 	if (!nlh)
17937120371cSWANG Cong 		goto out_nlmsg_trim;
17947120371cSWANG Cong 	tcm = nlmsg_data(nlh);
17957120371cSWANG Cong 	tcm->tcm_family = AF_UNSPEC;
17967120371cSWANG Cong 	tcm->tcm__pad1 = 0;
17977120371cSWANG Cong 	tcm->tcm__pad2 = 0;
17987960d1daSJiri Pirko 	if (q) {
1799a10fa201SJiri Pirko 		tcm->tcm_ifindex = qdisc_dev(q)->ifindex;
1800a10fa201SJiri Pirko 		tcm->tcm_parent = parent;
18017960d1daSJiri Pirko 	} else {
18027960d1daSJiri Pirko 		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
18037960d1daSJiri Pirko 		tcm->tcm_block_index = block->index;
18047960d1daSJiri Pirko 	}
18057120371cSWANG Cong 	tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol);
18067120371cSWANG Cong 	if (nla_put_string(skb, TCA_KIND, tp->ops->kind))
18077120371cSWANG Cong 		goto nla_put_failure;
18087120371cSWANG Cong 	if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index))
18097120371cSWANG Cong 		goto nla_put_failure;
18107120371cSWANG Cong 	if (!fh) {
18117120371cSWANG Cong 		tcm->tcm_handle = 0;
18127120371cSWANG Cong 	} else {
181312db03b6SVlad Buslov 		if (tp->ops->dump &&
181412db03b6SVlad Buslov 		    tp->ops->dump(net, tp, fh, skb, tcm, rtnl_held) < 0)
18157120371cSWANG Cong 			goto nla_put_failure;
18167120371cSWANG Cong 	}
18177120371cSWANG Cong 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
18187120371cSWANG Cong 	return skb->len;
18197120371cSWANG Cong 
18207120371cSWANG Cong out_nlmsg_trim:
18217120371cSWANG Cong nla_put_failure:
18227120371cSWANG Cong 	nlmsg_trim(skb, b);
18237120371cSWANG Cong 	return -1;
18247120371cSWANG Cong }
18257120371cSWANG Cong 
18267120371cSWANG Cong static int tfilter_notify(struct net *net, struct sk_buff *oskb,
18277120371cSWANG Cong 			  struct nlmsghdr *n, struct tcf_proto *tp,
18287960d1daSJiri Pirko 			  struct tcf_block *block, struct Qdisc *q,
182912db03b6SVlad Buslov 			  u32 parent, void *fh, int event, bool unicast,
183012db03b6SVlad Buslov 			  bool rtnl_held)
18317120371cSWANG Cong {
18327120371cSWANG Cong 	struct sk_buff *skb;
18337120371cSWANG Cong 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
18345b5f99b1SZhike Wang 	int err = 0;
18357120371cSWANG Cong 
18367120371cSWANG Cong 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
18377120371cSWANG Cong 	if (!skb)
18387120371cSWANG Cong 		return -ENOBUFS;
18397120371cSWANG Cong 
18407960d1daSJiri Pirko 	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
184112db03b6SVlad Buslov 			  n->nlmsg_seq, n->nlmsg_flags, event,
184212db03b6SVlad Buslov 			  rtnl_held) <= 0) {
18437120371cSWANG Cong 		kfree_skb(skb);
18447120371cSWANG Cong 		return -EINVAL;
18457120371cSWANG Cong 	}
18467120371cSWANG Cong 
18477120371cSWANG Cong 	if (unicast)
18485b5f99b1SZhike Wang 		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
18495b5f99b1SZhike Wang 	else
18505b5f99b1SZhike Wang 		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
18517120371cSWANG Cong 				     n->nlmsg_flags & NLM_F_ECHO);
18525b5f99b1SZhike Wang 
18535b5f99b1SZhike Wang 	if (err > 0)
18545b5f99b1SZhike Wang 		err = 0;
18555b5f99b1SZhike Wang 	return err;
18567120371cSWANG Cong }
18577120371cSWANG Cong 
18587120371cSWANG Cong static int tfilter_del_notify(struct net *net, struct sk_buff *oskb,
18597120371cSWANG Cong 			      struct nlmsghdr *n, struct tcf_proto *tp,
18607960d1daSJiri Pirko 			      struct tcf_block *block, struct Qdisc *q,
1861c35a4accSAlexander Aring 			      u32 parent, void *fh, bool unicast, bool *last,
186212db03b6SVlad Buslov 			      bool rtnl_held, struct netlink_ext_ack *extack)
18637120371cSWANG Cong {
18647120371cSWANG Cong 	struct sk_buff *skb;
18657120371cSWANG Cong 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
18667120371cSWANG Cong 	int err;
18677120371cSWANG Cong 
18687120371cSWANG Cong 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
18697120371cSWANG Cong 	if (!skb)
18707120371cSWANG Cong 		return -ENOBUFS;
18717120371cSWANG Cong 
18727960d1daSJiri Pirko 	if (tcf_fill_node(net, skb, tp, block, q, parent, fh, portid,
187312db03b6SVlad Buslov 			  n->nlmsg_seq, n->nlmsg_flags, RTM_DELTFILTER,
187412db03b6SVlad Buslov 			  rtnl_held) <= 0) {
1875c35a4accSAlexander Aring 		NL_SET_ERR_MSG(extack, "Failed to build del event notification");
18767120371cSWANG Cong 		kfree_skb(skb);
18777120371cSWANG Cong 		return -EINVAL;
18787120371cSWANG Cong 	}
18797120371cSWANG Cong 
188012db03b6SVlad Buslov 	err = tp->ops->delete(tp, fh, last, rtnl_held, extack);
18817120371cSWANG Cong 	if (err) {
18827120371cSWANG Cong 		kfree_skb(skb);
18837120371cSWANG Cong 		return err;
18847120371cSWANG Cong 	}
18857120371cSWANG Cong 
18867120371cSWANG Cong 	if (unicast)
18875b5f99b1SZhike Wang 		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
18885b5f99b1SZhike Wang 	else
1889c35a4accSAlexander Aring 		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
18907120371cSWANG Cong 				     n->nlmsg_flags & NLM_F_ECHO);
1891c35a4accSAlexander Aring 	if (err < 0)
1892c35a4accSAlexander Aring 		NL_SET_ERR_MSG(extack, "Failed to send filter delete notification");
18935b5f99b1SZhike Wang 
18945b5f99b1SZhike Wang 	if (err > 0)
18955b5f99b1SZhike Wang 		err = 0;
1896c35a4accSAlexander Aring 	return err;
18977120371cSWANG Cong }
18987120371cSWANG Cong 
18997120371cSWANG Cong static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb,
19007960d1daSJiri Pirko 				 struct tcf_block *block, struct Qdisc *q,
19017960d1daSJiri Pirko 				 u32 parent, struct nlmsghdr *n,
190212db03b6SVlad Buslov 				 struct tcf_chain *chain, int event,
190312db03b6SVlad Buslov 				 bool rtnl_held)
19047120371cSWANG Cong {
19057120371cSWANG Cong 	struct tcf_proto *tp;
19067120371cSWANG Cong 
190712db03b6SVlad Buslov 	for (tp = tcf_get_next_proto(chain, NULL, rtnl_held);
190812db03b6SVlad Buslov 	     tp; tp = tcf_get_next_proto(chain, tp, rtnl_held))
19097960d1daSJiri Pirko 		tfilter_notify(net, oskb, n, tp, block,
191012db03b6SVlad Buslov 			       q, parent, NULL, event, false, rtnl_held);
19117120371cSWANG Cong }
19127120371cSWANG Cong 
19137d5509faSVlad Buslov static void tfilter_put(struct tcf_proto *tp, void *fh)
19147d5509faSVlad Buslov {
19157d5509faSVlad Buslov 	if (tp->ops->put && fh)
19167d5509faSVlad Buslov 		tp->ops->put(tp, fh);
19177d5509faSVlad Buslov }
19187d5509faSVlad Buslov 
1919c431f89bSVlad Buslov static int tc_new_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
1920c21ef3e3SDavid Ahern 			  struct netlink_ext_ack *extack)
19211da177e4SLinus Torvalds {
19223b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
1923add93b61SPatrick McHardy 	struct nlattr *tca[TCA_MAX + 1];
19246f96c3c6SCong Wang 	char name[IFNAMSIZ];
19251da177e4SLinus Torvalds 	struct tcmsg *t;
19261da177e4SLinus Torvalds 	u32 protocol;
19271da177e4SLinus Torvalds 	u32 prio;
19289d36d9e5SJiri Pirko 	bool prio_allocate;
19291da177e4SLinus Torvalds 	u32 parent;
19305bc17018SJiri Pirko 	u32 chain_index;
19317960d1daSJiri Pirko 	struct Qdisc *q = NULL;
19322190d1d0SJiri Pirko 	struct tcf_chain_info chain_info;
19335bc17018SJiri Pirko 	struct tcf_chain *chain = NULL;
19346529eabaSJiri Pirko 	struct tcf_block *block;
19351da177e4SLinus Torvalds 	struct tcf_proto *tp;
19361da177e4SLinus Torvalds 	unsigned long cl;
19378113c095SWANG Cong 	void *fh;
19381da177e4SLinus Torvalds 	int err;
1939628185cfSDaniel Borkmann 	int tp_created;
1940470502deSVlad Buslov 	bool rtnl_held = false;
19411da177e4SLinus Torvalds 
1942c431f89bSVlad Buslov 	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
1943dfc47ef8SEric W. Biederman 		return -EPERM;
1944de179c8cSHong zhi guo 
19451da177e4SLinus Torvalds replay:
1946628185cfSDaniel Borkmann 	tp_created = 0;
1947628185cfSDaniel Borkmann 
19488cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
19498cb08174SJohannes Berg 				     rtm_tca_policy, extack);
1950de179c8cSHong zhi guo 	if (err < 0)
1951de179c8cSHong zhi guo 		return err;
1952de179c8cSHong zhi guo 
1953942b8165SDavid S. Miller 	t = nlmsg_data(n);
19541da177e4SLinus Torvalds 	protocol = TC_H_MIN(t->tcm_info);
19551da177e4SLinus Torvalds 	prio = TC_H_MAJ(t->tcm_info);
19569d36d9e5SJiri Pirko 	prio_allocate = false;
19571da177e4SLinus Torvalds 	parent = t->tcm_parent;
19584dbfa766SVlad Buslov 	tp = NULL;
19591da177e4SLinus Torvalds 	cl = 0;
1960470502deSVlad Buslov 	block = NULL;
19611da177e4SLinus Torvalds 
19621da177e4SLinus Torvalds 	if (prio == 0) {
1963ea7f8277SDaniel Borkmann 		/* If no priority is provided by the user,
1964ea7f8277SDaniel Borkmann 		 * we allocate one.
1965ea7f8277SDaniel Borkmann 		 */
1966ea7f8277SDaniel Borkmann 		if (n->nlmsg_flags & NLM_F_CREATE) {
19671da177e4SLinus Torvalds 			prio = TC_H_MAKE(0x80000000U, 0U);
19689d36d9e5SJiri Pirko 			prio_allocate = true;
1969c431f89bSVlad Buslov 		} else {
1970c35a4accSAlexander Aring 			NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
1971ea7f8277SDaniel Borkmann 			return -ENOENT;
1972ea7f8277SDaniel Borkmann 		}
19731da177e4SLinus Torvalds 	}
19741da177e4SLinus Torvalds 
19751da177e4SLinus Torvalds 	/* Find head of filter chain. */
19761da177e4SLinus Torvalds 
1977470502deSVlad Buslov 	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
1978470502deSVlad Buslov 	if (err)
1979470502deSVlad Buslov 		return err;
1980470502deSVlad Buslov 
19816f96c3c6SCong Wang 	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
19826f96c3c6SCong Wang 		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
19836f96c3c6SCong Wang 		err = -EINVAL;
19846f96c3c6SCong Wang 		goto errout;
19856f96c3c6SCong Wang 	}
19866f96c3c6SCong Wang 
1987470502deSVlad Buslov 	/* Take rtnl mutex if rtnl_held was set to true on previous iteration,
1988470502deSVlad Buslov 	 * block is shared (no qdisc found), qdisc is not unlocked, classifier
1989470502deSVlad Buslov 	 * type is not specified, classifier is not unlocked.
1990470502deSVlad Buslov 	 */
1991470502deSVlad Buslov 	if (rtnl_held ||
1992470502deSVlad Buslov 	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
19936f96c3c6SCong Wang 	    !tcf_proto_is_unlocked(name)) {
1994470502deSVlad Buslov 		rtnl_held = true;
1995470502deSVlad Buslov 		rtnl_lock();
1996470502deSVlad Buslov 	}
1997470502deSVlad Buslov 
1998470502deSVlad Buslov 	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
1999470502deSVlad Buslov 	if (err)
2000470502deSVlad Buslov 		goto errout;
2001470502deSVlad Buslov 
2002470502deSVlad Buslov 	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
2003470502deSVlad Buslov 				 extack);
2004c431f89bSVlad Buslov 	if (IS_ERR(block)) {
2005c431f89bSVlad Buslov 		err = PTR_ERR(block);
20067960d1daSJiri Pirko 		goto errout;
20077960d1daSJiri Pirko 	}
20085bc17018SJiri Pirko 
20095bc17018SJiri Pirko 	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
20105bc17018SJiri Pirko 	if (chain_index > TC_ACT_EXT_VAL_MASK) {
2011c35a4accSAlexander Aring 		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
20125bc17018SJiri Pirko 		err = -EINVAL;
20135bc17018SJiri Pirko 		goto errout;
20145bc17018SJiri Pirko 	}
2015c431f89bSVlad Buslov 	chain = tcf_chain_get(block, chain_index, true);
20165bc17018SJiri Pirko 	if (!chain) {
2017d5ed72a5SJiri Pirko 		NL_SET_ERR_MSG(extack, "Cannot create specified filter chain");
2018c431f89bSVlad Buslov 		err = -ENOMEM;
2019ea7f8277SDaniel Borkmann 		goto errout;
2020ea7f8277SDaniel Borkmann 	}
20211da177e4SLinus Torvalds 
2022ed76f5edSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
20232190d1d0SJiri Pirko 	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
20242190d1d0SJiri Pirko 			       prio, prio_allocate);
20252190d1d0SJiri Pirko 	if (IS_ERR(tp)) {
2026c35a4accSAlexander Aring 		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
20272190d1d0SJiri Pirko 		err = PTR_ERR(tp);
2028ed76f5edSVlad Buslov 		goto errout_locked;
20296bb16e7aSJiri Pirko 	}
20301da177e4SLinus Torvalds 
20311da177e4SLinus Torvalds 	if (tp == NULL) {
20328b64678eSVlad Buslov 		struct tcf_proto *tp_new = NULL;
20338b64678eSVlad Buslov 
2034726d0612SVlad Buslov 		if (chain->flushing) {
2035726d0612SVlad Buslov 			err = -EAGAIN;
2036726d0612SVlad Buslov 			goto errout_locked;
2037726d0612SVlad Buslov 		}
2038726d0612SVlad Buslov 
20391da177e4SLinus Torvalds 		/* Proto-tcf does not exist, create new one */
20401da177e4SLinus Torvalds 
20416bb16e7aSJiri Pirko 		if (tca[TCA_KIND] == NULL || !protocol) {
2042c35a4accSAlexander Aring 			NL_SET_ERR_MSG(extack, "Filter kind and protocol must be specified");
20436bb16e7aSJiri Pirko 			err = -EINVAL;
2044ed76f5edSVlad Buslov 			goto errout_locked;
20456bb16e7aSJiri Pirko 		}
20461da177e4SLinus Torvalds 
2047c431f89bSVlad Buslov 		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
2048c35a4accSAlexander Aring 			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
20496bb16e7aSJiri Pirko 			err = -ENOENT;
2050ed76f5edSVlad Buslov 			goto errout_locked;
20516bb16e7aSJiri Pirko 		}
20521da177e4SLinus Torvalds 
20539d36d9e5SJiri Pirko 		if (prio_allocate)
2054ed76f5edSVlad Buslov 			prio = tcf_auto_prio(tcf_chain_tp_prev(chain,
2055ed76f5edSVlad Buslov 							       &chain_info));
20561da177e4SLinus Torvalds 
2057ed76f5edSVlad Buslov 		mutex_unlock(&chain->filter_chain_lock);
205836d79af7SEric Dumazet 		tp_new = tcf_proto_create(name, protocol, prio, chain,
205936d79af7SEric Dumazet 					  rtnl_held, extack);
20608b64678eSVlad Buslov 		if (IS_ERR(tp_new)) {
20618b64678eSVlad Buslov 			err = PTR_ERR(tp_new);
2062726d0612SVlad Buslov 			goto errout_tp;
20631da177e4SLinus Torvalds 		}
2064ed76f5edSVlad Buslov 
206512186be7SMinoru Usui 		tp_created = 1;
206612db03b6SVlad Buslov 		tp = tcf_chain_tp_insert_unique(chain, tp_new, protocol, prio,
206712db03b6SVlad Buslov 						rtnl_held);
2068726d0612SVlad Buslov 		if (IS_ERR(tp)) {
2069726d0612SVlad Buslov 			err = PTR_ERR(tp);
2070726d0612SVlad Buslov 			goto errout_tp;
2071726d0612SVlad Buslov 		}
2072ed76f5edSVlad Buslov 	} else {
2073ed76f5edSVlad Buslov 		mutex_unlock(&chain->filter_chain_lock);
20746bb16e7aSJiri Pirko 	}
20751da177e4SLinus Torvalds 
20768b64678eSVlad Buslov 	if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
20778b64678eSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
20788b64678eSVlad Buslov 		err = -EINVAL;
20798b64678eSVlad Buslov 		goto errout;
20808b64678eSVlad Buslov 	}
20818b64678eSVlad Buslov 
20821da177e4SLinus Torvalds 	fh = tp->ops->get(tp, t->tcm_handle);
20831da177e4SLinus Torvalds 
20848113c095SWANG Cong 	if (!fh) {
2085c431f89bSVlad Buslov 		if (!(n->nlmsg_flags & NLM_F_CREATE)) {
2086c35a4accSAlexander Aring 			NL_SET_ERR_MSG(extack, "Need both RTM_NEWTFILTER and NLM_F_CREATE to create a new filter");
20876bb16e7aSJiri Pirko 			err = -ENOENT;
20881da177e4SLinus Torvalds 			goto errout;
20896bb16e7aSJiri Pirko 		}
2090c431f89bSVlad Buslov 	} else if (n->nlmsg_flags & NLM_F_EXCL) {
20917d5509faSVlad Buslov 		tfilter_put(tp, fh);
2092c35a4accSAlexander Aring 		NL_SET_ERR_MSG(extack, "Filter already exists");
20936bb16e7aSJiri Pirko 		err = -EEXIST;
20941da177e4SLinus Torvalds 		goto errout;
209512186be7SMinoru Usui 	}
20961da177e4SLinus Torvalds 
20979f407f17SJiri Pirko 	if (chain->tmplt_ops && chain->tmplt_ops != tp->ops) {
20989f407f17SJiri Pirko 		NL_SET_ERR_MSG(extack, "Chain template is set to a different filter kind");
20999f407f17SJiri Pirko 		err = -EINVAL;
21009f407f17SJiri Pirko 		goto errout;
21019f407f17SJiri Pirko 	}
21029f407f17SJiri Pirko 
21032f7ef2f8SCong Wang 	err = tp->ops->change(net, skb, tp, cl, t->tcm_handle, tca, &fh,
21047306db38SAlexander Aring 			      n->nlmsg_flags & NLM_F_CREATE ? TCA_ACT_NOREPLACE : TCA_ACT_REPLACE,
210512db03b6SVlad Buslov 			      rtnl_held, extack);
21067d5509faSVlad Buslov 	if (err == 0) {
21077960d1daSJiri Pirko 		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
210812db03b6SVlad Buslov 			       RTM_NEWTFILTER, false, rtnl_held);
21097d5509faSVlad Buslov 		tfilter_put(tp, fh);
2110503d81d4SVlad Buslov 		/* q pointer is NULL for shared blocks */
2111503d81d4SVlad Buslov 		if (q)
21123f05e688SCong Wang 			q->flags &= ~TCQ_F_CAN_BYPASS;
21137d5509faSVlad Buslov 	}
21141da177e4SLinus Torvalds 
21151da177e4SLinus Torvalds errout:
21168b64678eSVlad Buslov 	if (err && tp_created)
211712db03b6SVlad Buslov 		tcf_chain_tp_delete_empty(chain, tp, rtnl_held, NULL);
2118726d0612SVlad Buslov errout_tp:
21194dbfa766SVlad Buslov 	if (chain) {
21204dbfa766SVlad Buslov 		if (tp && !IS_ERR(tp))
212112db03b6SVlad Buslov 			tcf_proto_put(tp, rtnl_held, NULL);
21224dbfa766SVlad Buslov 		if (!tp_created)
21234dbfa766SVlad Buslov 			tcf_chain_put(chain);
21244dbfa766SVlad Buslov 	}
212512db03b6SVlad Buslov 	tcf_block_release(q, block, rtnl_held);
2126470502deSVlad Buslov 
2127470502deSVlad Buslov 	if (rtnl_held)
2128470502deSVlad Buslov 		rtnl_unlock();
2129470502deSVlad Buslov 
2130470502deSVlad Buslov 	if (err == -EAGAIN) {
2131470502deSVlad Buslov 		/* Take rtnl lock in case EAGAIN is caused by concurrent flush
2132470502deSVlad Buslov 		 * of target chain.
2133470502deSVlad Buslov 		 */
2134470502deSVlad Buslov 		rtnl_held = true;
21351da177e4SLinus Torvalds 		/* Replay the request. */
21361da177e4SLinus Torvalds 		goto replay;
2137470502deSVlad Buslov 	}
21381da177e4SLinus Torvalds 	return err;
2139ed76f5edSVlad Buslov 
2140ed76f5edSVlad Buslov errout_locked:
2141ed76f5edSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
2142ed76f5edSVlad Buslov 	goto errout;
21431da177e4SLinus Torvalds }
21441da177e4SLinus Torvalds 
2145c431f89bSVlad Buslov static int tc_del_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2146c431f89bSVlad Buslov 			  struct netlink_ext_ack *extack)
2147c431f89bSVlad Buslov {
2148c431f89bSVlad Buslov 	struct net *net = sock_net(skb->sk);
2149c431f89bSVlad Buslov 	struct nlattr *tca[TCA_MAX + 1];
21506f96c3c6SCong Wang 	char name[IFNAMSIZ];
2151c431f89bSVlad Buslov 	struct tcmsg *t;
2152c431f89bSVlad Buslov 	u32 protocol;
2153c431f89bSVlad Buslov 	u32 prio;
2154c431f89bSVlad Buslov 	u32 parent;
2155c431f89bSVlad Buslov 	u32 chain_index;
2156c431f89bSVlad Buslov 	struct Qdisc *q = NULL;
2157c431f89bSVlad Buslov 	struct tcf_chain_info chain_info;
2158c431f89bSVlad Buslov 	struct tcf_chain *chain = NULL;
2159470502deSVlad Buslov 	struct tcf_block *block = NULL;
2160c431f89bSVlad Buslov 	struct tcf_proto *tp = NULL;
2161c431f89bSVlad Buslov 	unsigned long cl = 0;
2162c431f89bSVlad Buslov 	void *fh = NULL;
2163c431f89bSVlad Buslov 	int err;
2164470502deSVlad Buslov 	bool rtnl_held = false;
2165c431f89bSVlad Buslov 
2166c431f89bSVlad Buslov 	if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
2167c431f89bSVlad Buslov 		return -EPERM;
2168c431f89bSVlad Buslov 
21698cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
21708cb08174SJohannes Berg 				     rtm_tca_policy, extack);
2171c431f89bSVlad Buslov 	if (err < 0)
2172c431f89bSVlad Buslov 		return err;
2173c431f89bSVlad Buslov 
2174c431f89bSVlad Buslov 	t = nlmsg_data(n);
2175c431f89bSVlad Buslov 	protocol = TC_H_MIN(t->tcm_info);
2176c431f89bSVlad Buslov 	prio = TC_H_MAJ(t->tcm_info);
2177c431f89bSVlad Buslov 	parent = t->tcm_parent;
2178c431f89bSVlad Buslov 
2179c431f89bSVlad Buslov 	if (prio == 0 && (protocol || t->tcm_handle || tca[TCA_KIND])) {
2180c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Cannot flush filters with protocol, handle or kind set");
2181c431f89bSVlad Buslov 		return -ENOENT;
2182c431f89bSVlad Buslov 	}
2183c431f89bSVlad Buslov 
2184c431f89bSVlad Buslov 	/* Find head of filter chain. */
2185c431f89bSVlad Buslov 
2186470502deSVlad Buslov 	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
2187470502deSVlad Buslov 	if (err)
2188470502deSVlad Buslov 		return err;
2189470502deSVlad Buslov 
21906f96c3c6SCong Wang 	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
21916f96c3c6SCong Wang 		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
21926f96c3c6SCong Wang 		err = -EINVAL;
21936f96c3c6SCong Wang 		goto errout;
21946f96c3c6SCong Wang 	}
2195470502deSVlad Buslov 	/* Take rtnl mutex if flushing whole chain, block is shared (no qdisc
2196470502deSVlad Buslov 	 * found), qdisc is not unlocked, classifier type is not specified,
2197470502deSVlad Buslov 	 * classifier is not unlocked.
2198470502deSVlad Buslov 	 */
2199470502deSVlad Buslov 	if (!prio ||
2200470502deSVlad Buslov 	    (q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
22016f96c3c6SCong Wang 	    !tcf_proto_is_unlocked(name)) {
2202470502deSVlad Buslov 		rtnl_held = true;
2203470502deSVlad Buslov 		rtnl_lock();
2204470502deSVlad Buslov 	}
2205470502deSVlad Buslov 
2206470502deSVlad Buslov 	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
2207470502deSVlad Buslov 	if (err)
2208470502deSVlad Buslov 		goto errout;
2209470502deSVlad Buslov 
2210470502deSVlad Buslov 	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
2211470502deSVlad Buslov 				 extack);
2212c431f89bSVlad Buslov 	if (IS_ERR(block)) {
2213c431f89bSVlad Buslov 		err = PTR_ERR(block);
2214c431f89bSVlad Buslov 		goto errout;
2215c431f89bSVlad Buslov 	}
2216c431f89bSVlad Buslov 
2217c431f89bSVlad Buslov 	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
2218c431f89bSVlad Buslov 	if (chain_index > TC_ACT_EXT_VAL_MASK) {
2219c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
2220c431f89bSVlad Buslov 		err = -EINVAL;
2221c431f89bSVlad Buslov 		goto errout;
2222c431f89bSVlad Buslov 	}
2223c431f89bSVlad Buslov 	chain = tcf_chain_get(block, chain_index, false);
2224c431f89bSVlad Buslov 	if (!chain) {
22255ca8a25cSJiri Pirko 		/* User requested flush on non-existent chain. Nothing to do,
22265ca8a25cSJiri Pirko 		 * so just return success.
22275ca8a25cSJiri Pirko 		 */
22285ca8a25cSJiri Pirko 		if (prio == 0) {
22295ca8a25cSJiri Pirko 			err = 0;
22305ca8a25cSJiri Pirko 			goto errout;
22315ca8a25cSJiri Pirko 		}
2232c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
2233b7b4247dSJiri Pirko 		err = -ENOENT;
2234c431f89bSVlad Buslov 		goto errout;
2235c431f89bSVlad Buslov 	}
2236c431f89bSVlad Buslov 
2237c431f89bSVlad Buslov 	if (prio == 0) {
2238c431f89bSVlad Buslov 		tfilter_notify_chain(net, skb, block, q, parent, n,
223912db03b6SVlad Buslov 				     chain, RTM_DELTFILTER, rtnl_held);
224012db03b6SVlad Buslov 		tcf_chain_flush(chain, rtnl_held);
2241c431f89bSVlad Buslov 		err = 0;
2242c431f89bSVlad Buslov 		goto errout;
2243c431f89bSVlad Buslov 	}
2244c431f89bSVlad Buslov 
2245ed76f5edSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
2246c431f89bSVlad Buslov 	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
2247c431f89bSVlad Buslov 			       prio, false);
2248c431f89bSVlad Buslov 	if (!tp || IS_ERR(tp)) {
2249c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
22500e399035SVlad Buslov 		err = tp ? PTR_ERR(tp) : -ENOENT;
2251ed76f5edSVlad Buslov 		goto errout_locked;
2252c431f89bSVlad Buslov 	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
2253c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
2254c431f89bSVlad Buslov 		err = -EINVAL;
2255ed76f5edSVlad Buslov 		goto errout_locked;
2256ed76f5edSVlad Buslov 	} else if (t->tcm_handle == 0) {
225759eb87cbSJohn Hurley 		tcf_proto_signal_destroying(chain, tp);
2258c431f89bSVlad Buslov 		tcf_chain_tp_remove(chain, &chain_info, tp);
2259ed76f5edSVlad Buslov 		mutex_unlock(&chain->filter_chain_lock);
2260ed76f5edSVlad Buslov 
226112db03b6SVlad Buslov 		tcf_proto_put(tp, rtnl_held, NULL);
2262c431f89bSVlad Buslov 		tfilter_notify(net, skb, n, tp, block, q, parent, fh,
226312db03b6SVlad Buslov 			       RTM_DELTFILTER, false, rtnl_held);
2264c431f89bSVlad Buslov 		err = 0;
2265ed76f5edSVlad Buslov 		goto errout;
2266ed76f5edSVlad Buslov 	}
2267ed76f5edSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
2268ed76f5edSVlad Buslov 
2269ed76f5edSVlad Buslov 	fh = tp->ops->get(tp, t->tcm_handle);
2270ed76f5edSVlad Buslov 
2271ed76f5edSVlad Buslov 	if (!fh) {
2272c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
2273c431f89bSVlad Buslov 		err = -ENOENT;
2274c431f89bSVlad Buslov 	} else {
2275c431f89bSVlad Buslov 		bool last;
2276c431f89bSVlad Buslov 
2277c431f89bSVlad Buslov 		err = tfilter_del_notify(net, skb, n, tp, block,
2278c431f89bSVlad Buslov 					 q, parent, fh, false, &last,
227912db03b6SVlad Buslov 					 rtnl_held, extack);
228012db03b6SVlad Buslov 
2281c431f89bSVlad Buslov 		if (err)
2282c431f89bSVlad Buslov 			goto errout;
22838b64678eSVlad Buslov 		if (last)
228412db03b6SVlad Buslov 			tcf_chain_tp_delete_empty(chain, tp, rtnl_held, extack);
2285c431f89bSVlad Buslov 	}
2286c431f89bSVlad Buslov 
2287c431f89bSVlad Buslov errout:
22884dbfa766SVlad Buslov 	if (chain) {
22894dbfa766SVlad Buslov 		if (tp && !IS_ERR(tp))
229012db03b6SVlad Buslov 			tcf_proto_put(tp, rtnl_held, NULL);
2291c431f89bSVlad Buslov 		tcf_chain_put(chain);
22924dbfa766SVlad Buslov 	}
229312db03b6SVlad Buslov 	tcf_block_release(q, block, rtnl_held);
2294470502deSVlad Buslov 
2295470502deSVlad Buslov 	if (rtnl_held)
2296470502deSVlad Buslov 		rtnl_unlock();
2297470502deSVlad Buslov 
2298c431f89bSVlad Buslov 	return err;
2299ed76f5edSVlad Buslov 
2300ed76f5edSVlad Buslov errout_locked:
2301ed76f5edSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
2302ed76f5edSVlad Buslov 	goto errout;
2303c431f89bSVlad Buslov }
2304c431f89bSVlad Buslov 
2305c431f89bSVlad Buslov static int tc_get_tfilter(struct sk_buff *skb, struct nlmsghdr *n,
2306c431f89bSVlad Buslov 			  struct netlink_ext_ack *extack)
2307c431f89bSVlad Buslov {
2308c431f89bSVlad Buslov 	struct net *net = sock_net(skb->sk);
2309c431f89bSVlad Buslov 	struct nlattr *tca[TCA_MAX + 1];
23106f96c3c6SCong Wang 	char name[IFNAMSIZ];
2311c431f89bSVlad Buslov 	struct tcmsg *t;
2312c431f89bSVlad Buslov 	u32 protocol;
2313c431f89bSVlad Buslov 	u32 prio;
2314c431f89bSVlad Buslov 	u32 parent;
2315c431f89bSVlad Buslov 	u32 chain_index;
2316c431f89bSVlad Buslov 	struct Qdisc *q = NULL;
2317c431f89bSVlad Buslov 	struct tcf_chain_info chain_info;
2318c431f89bSVlad Buslov 	struct tcf_chain *chain = NULL;
2319470502deSVlad Buslov 	struct tcf_block *block = NULL;
2320c431f89bSVlad Buslov 	struct tcf_proto *tp = NULL;
2321c431f89bSVlad Buslov 	unsigned long cl = 0;
2322c431f89bSVlad Buslov 	void *fh = NULL;
2323c431f89bSVlad Buslov 	int err;
2324470502deSVlad Buslov 	bool rtnl_held = false;
2325c431f89bSVlad Buslov 
23268cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
23278cb08174SJohannes Berg 				     rtm_tca_policy, extack);
2328c431f89bSVlad Buslov 	if (err < 0)
2329c431f89bSVlad Buslov 		return err;
2330c431f89bSVlad Buslov 
2331c431f89bSVlad Buslov 	t = nlmsg_data(n);
2332c431f89bSVlad Buslov 	protocol = TC_H_MIN(t->tcm_info);
2333c431f89bSVlad Buslov 	prio = TC_H_MAJ(t->tcm_info);
2334c431f89bSVlad Buslov 	parent = t->tcm_parent;
2335c431f89bSVlad Buslov 
2336c431f89bSVlad Buslov 	if (prio == 0) {
2337c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Invalid filter command with priority of zero");
2338c431f89bSVlad Buslov 		return -ENOENT;
2339c431f89bSVlad Buslov 	}
2340c431f89bSVlad Buslov 
2341c431f89bSVlad Buslov 	/* Find head of filter chain. */
2342c431f89bSVlad Buslov 
2343470502deSVlad Buslov 	err = __tcf_qdisc_find(net, &q, &parent, t->tcm_ifindex, false, extack);
2344470502deSVlad Buslov 	if (err)
2345470502deSVlad Buslov 		return err;
2346470502deSVlad Buslov 
23476f96c3c6SCong Wang 	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
23486f96c3c6SCong Wang 		NL_SET_ERR_MSG(extack, "Specified TC filter name too long");
23496f96c3c6SCong Wang 		err = -EINVAL;
23506f96c3c6SCong Wang 		goto errout;
23516f96c3c6SCong Wang 	}
2352470502deSVlad Buslov 	/* Take rtnl mutex if block is shared (no qdisc found), qdisc is not
2353470502deSVlad Buslov 	 * unlocked, classifier type is not specified, classifier is not
2354470502deSVlad Buslov 	 * unlocked.
2355470502deSVlad Buslov 	 */
2356470502deSVlad Buslov 	if ((q && !(q->ops->cl_ops->flags & QDISC_CLASS_OPS_DOIT_UNLOCKED)) ||
23576f96c3c6SCong Wang 	    !tcf_proto_is_unlocked(name)) {
2358470502deSVlad Buslov 		rtnl_held = true;
2359470502deSVlad Buslov 		rtnl_lock();
2360470502deSVlad Buslov 	}
2361470502deSVlad Buslov 
2362470502deSVlad Buslov 	err = __tcf_qdisc_cl_find(q, parent, &cl, t->tcm_ifindex, extack);
2363470502deSVlad Buslov 	if (err)
2364470502deSVlad Buslov 		goto errout;
2365470502deSVlad Buslov 
2366470502deSVlad Buslov 	block = __tcf_block_find(net, q, cl, t->tcm_ifindex, t->tcm_block_index,
2367470502deSVlad Buslov 				 extack);
2368c431f89bSVlad Buslov 	if (IS_ERR(block)) {
2369c431f89bSVlad Buslov 		err = PTR_ERR(block);
2370c431f89bSVlad Buslov 		goto errout;
2371c431f89bSVlad Buslov 	}
2372c431f89bSVlad Buslov 
2373c431f89bSVlad Buslov 	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
2374c431f89bSVlad Buslov 	if (chain_index > TC_ACT_EXT_VAL_MASK) {
2375c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
2376c431f89bSVlad Buslov 		err = -EINVAL;
2377c431f89bSVlad Buslov 		goto errout;
2378c431f89bSVlad Buslov 	}
2379c431f89bSVlad Buslov 	chain = tcf_chain_get(block, chain_index, false);
2380c431f89bSVlad Buslov 	if (!chain) {
2381c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
2382c431f89bSVlad Buslov 		err = -EINVAL;
2383c431f89bSVlad Buslov 		goto errout;
2384c431f89bSVlad Buslov 	}
2385c431f89bSVlad Buslov 
2386ed76f5edSVlad Buslov 	mutex_lock(&chain->filter_chain_lock);
2387c431f89bSVlad Buslov 	tp = tcf_chain_tp_find(chain, &chain_info, protocol,
2388c431f89bSVlad Buslov 			       prio, false);
2389ed76f5edSVlad Buslov 	mutex_unlock(&chain->filter_chain_lock);
2390c431f89bSVlad Buslov 	if (!tp || IS_ERR(tp)) {
2391c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Filter with specified priority/protocol not found");
23920e399035SVlad Buslov 		err = tp ? PTR_ERR(tp) : -ENOENT;
2393c431f89bSVlad Buslov 		goto errout;
2394c431f89bSVlad Buslov 	} else if (tca[TCA_KIND] && nla_strcmp(tca[TCA_KIND], tp->ops->kind)) {
2395c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified filter kind does not match existing one");
2396c431f89bSVlad Buslov 		err = -EINVAL;
2397c431f89bSVlad Buslov 		goto errout;
2398c431f89bSVlad Buslov 	}
2399c431f89bSVlad Buslov 
2400c431f89bSVlad Buslov 	fh = tp->ops->get(tp, t->tcm_handle);
2401c431f89bSVlad Buslov 
2402c431f89bSVlad Buslov 	if (!fh) {
2403c431f89bSVlad Buslov 		NL_SET_ERR_MSG(extack, "Specified filter handle not found");
2404c431f89bSVlad Buslov 		err = -ENOENT;
2405c431f89bSVlad Buslov 	} else {
2406c431f89bSVlad Buslov 		err = tfilter_notify(net, skb, n, tp, block, q, parent,
240712db03b6SVlad Buslov 				     fh, RTM_NEWTFILTER, true, rtnl_held);
2408c431f89bSVlad Buslov 		if (err < 0)
2409c431f89bSVlad Buslov 			NL_SET_ERR_MSG(extack, "Failed to send filter notify message");
2410c431f89bSVlad Buslov 	}
2411c431f89bSVlad Buslov 
24127d5509faSVlad Buslov 	tfilter_put(tp, fh);
2413c431f89bSVlad Buslov errout:
24144dbfa766SVlad Buslov 	if (chain) {
24154dbfa766SVlad Buslov 		if (tp && !IS_ERR(tp))
241612db03b6SVlad Buslov 			tcf_proto_put(tp, rtnl_held, NULL);
2417c431f89bSVlad Buslov 		tcf_chain_put(chain);
24184dbfa766SVlad Buslov 	}
241912db03b6SVlad Buslov 	tcf_block_release(q, block, rtnl_held);
2420470502deSVlad Buslov 
2421470502deSVlad Buslov 	if (rtnl_held)
2422470502deSVlad Buslov 		rtnl_unlock();
2423470502deSVlad Buslov 
2424c431f89bSVlad Buslov 	return err;
2425c431f89bSVlad Buslov }
2426c431f89bSVlad Buslov 
2427aa767bfeSStephen Hemminger struct tcf_dump_args {
24281da177e4SLinus Torvalds 	struct tcf_walker w;
24291da177e4SLinus Torvalds 	struct sk_buff *skb;
24301da177e4SLinus Torvalds 	struct netlink_callback *cb;
24317960d1daSJiri Pirko 	struct tcf_block *block;
2432a10fa201SJiri Pirko 	struct Qdisc *q;
2433a10fa201SJiri Pirko 	u32 parent;
24341da177e4SLinus Torvalds };
24351da177e4SLinus Torvalds 
24368113c095SWANG Cong static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg)
24371da177e4SLinus Torvalds {
24381da177e4SLinus Torvalds 	struct tcf_dump_args *a = (void *)arg;
2439832d1d5bSWANG Cong 	struct net *net = sock_net(a->skb->sk);
24401da177e4SLinus Torvalds 
24417960d1daSJiri Pirko 	return tcf_fill_node(net, a->skb, tp, a->block, a->q, a->parent,
2442a10fa201SJiri Pirko 			     n, NETLINK_CB(a->cb->skb).portid,
24435a7a5555SJamal Hadi Salim 			     a->cb->nlh->nlmsg_seq, NLM_F_MULTI,
244412db03b6SVlad Buslov 			     RTM_NEWTFILTER, true);
24451da177e4SLinus Torvalds }
24461da177e4SLinus Torvalds 
2447a10fa201SJiri Pirko static bool tcf_chain_dump(struct tcf_chain *chain, struct Qdisc *q, u32 parent,
2448a10fa201SJiri Pirko 			   struct sk_buff *skb, struct netlink_callback *cb,
2449acb31faeSJiri Pirko 			   long index_start, long *p_index)
2450acb31faeSJiri Pirko {
2451acb31faeSJiri Pirko 	struct net *net = sock_net(skb->sk);
24527960d1daSJiri Pirko 	struct tcf_block *block = chain->block;
2453acb31faeSJiri Pirko 	struct tcmsg *tcm = nlmsg_data(cb->nlh);
2454fe2923afSVlad Buslov 	struct tcf_proto *tp, *tp_prev;
2455acb31faeSJiri Pirko 	struct tcf_dump_args arg;
2456acb31faeSJiri Pirko 
2457fe2923afSVlad Buslov 	for (tp = __tcf_get_next_proto(chain, NULL);
2458fe2923afSVlad Buslov 	     tp;
2459fe2923afSVlad Buslov 	     tp_prev = tp,
2460fe2923afSVlad Buslov 		     tp = __tcf_get_next_proto(chain, tp),
246112db03b6SVlad Buslov 		     tcf_proto_put(tp_prev, true, NULL),
2462fe2923afSVlad Buslov 		     (*p_index)++) {
2463acb31faeSJiri Pirko 		if (*p_index < index_start)
2464acb31faeSJiri Pirko 			continue;
2465acb31faeSJiri Pirko 		if (TC_H_MAJ(tcm->tcm_info) &&
2466acb31faeSJiri Pirko 		    TC_H_MAJ(tcm->tcm_info) != tp->prio)
2467acb31faeSJiri Pirko 			continue;
2468acb31faeSJiri Pirko 		if (TC_H_MIN(tcm->tcm_info) &&
2469acb31faeSJiri Pirko 		    TC_H_MIN(tcm->tcm_info) != tp->protocol)
2470acb31faeSJiri Pirko 			continue;
2471acb31faeSJiri Pirko 		if (*p_index > index_start)
2472acb31faeSJiri Pirko 			memset(&cb->args[1], 0,
2473acb31faeSJiri Pirko 			       sizeof(cb->args) - sizeof(cb->args[0]));
2474acb31faeSJiri Pirko 		if (cb->args[1] == 0) {
247553189183SYueHaibing 			if (tcf_fill_node(net, skb, tp, block, q, parent, NULL,
2476acb31faeSJiri Pirko 					  NETLINK_CB(cb->skb).portid,
2477acb31faeSJiri Pirko 					  cb->nlh->nlmsg_seq, NLM_F_MULTI,
247812db03b6SVlad Buslov 					  RTM_NEWTFILTER, true) <= 0)
2479fe2923afSVlad Buslov 				goto errout;
2480acb31faeSJiri Pirko 			cb->args[1] = 1;
2481acb31faeSJiri Pirko 		}
2482acb31faeSJiri Pirko 		if (!tp->ops->walk)
2483acb31faeSJiri Pirko 			continue;
2484acb31faeSJiri Pirko 		arg.w.fn = tcf_node_dump;
2485acb31faeSJiri Pirko 		arg.skb = skb;
2486acb31faeSJiri Pirko 		arg.cb = cb;
24877960d1daSJiri Pirko 		arg.block = block;
2488a10fa201SJiri Pirko 		arg.q = q;
2489a10fa201SJiri Pirko 		arg.parent = parent;
2490acb31faeSJiri Pirko 		arg.w.stop = 0;
2491acb31faeSJiri Pirko 		arg.w.skip = cb->args[1] - 1;
2492acb31faeSJiri Pirko 		arg.w.count = 0;
249301683a14SVlad Buslov 		arg.w.cookie = cb->args[2];
249412db03b6SVlad Buslov 		tp->ops->walk(tp, &arg.w, true);
249501683a14SVlad Buslov 		cb->args[2] = arg.w.cookie;
2496acb31faeSJiri Pirko 		cb->args[1] = arg.w.count + 1;
2497acb31faeSJiri Pirko 		if (arg.w.stop)
2498fe2923afSVlad Buslov 			goto errout;
2499acb31faeSJiri Pirko 	}
25005bc17018SJiri Pirko 	return true;
2501fe2923afSVlad Buslov 
2502fe2923afSVlad Buslov errout:
250312db03b6SVlad Buslov 	tcf_proto_put(tp, true, NULL);
2504fe2923afSVlad Buslov 	return false;
2505acb31faeSJiri Pirko }
2506acb31faeSJiri Pirko 
2507bd27a875SEric Dumazet /* called with RTNL */
25081da177e4SLinus Torvalds static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb)
25091da177e4SLinus Torvalds {
2510bbf73830SVlad Buslov 	struct tcf_chain *chain, *chain_prev;
25113b1e0a65SYOSHIFUJI Hideaki 	struct net *net = sock_net(skb->sk);
25125bc17018SJiri Pirko 	struct nlattr *tca[TCA_MAX + 1];
25137960d1daSJiri Pirko 	struct Qdisc *q = NULL;
25146529eabaSJiri Pirko 	struct tcf_block *block;
2515942b8165SDavid S. Miller 	struct tcmsg *tcm = nlmsg_data(cb->nlh);
2516acb31faeSJiri Pirko 	long index_start;
2517acb31faeSJiri Pirko 	long index;
2518a10fa201SJiri Pirko 	u32 parent;
25195bc17018SJiri Pirko 	int err;
25201da177e4SLinus Torvalds 
2521573ce260SHong zhi guo 	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
25221da177e4SLinus Torvalds 		return skb->len;
25235bc17018SJiri Pirko 
25248cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
25258cb08174SJohannes Berg 				     NULL, cb->extack);
25265bc17018SJiri Pirko 	if (err)
25275bc17018SJiri Pirko 		return err;
25285bc17018SJiri Pirko 
25297960d1daSJiri Pirko 	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
2530787ce6d0SVlad Buslov 		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
25317960d1daSJiri Pirko 		if (!block)
25327960d1daSJiri Pirko 			goto out;
2533d680b352SJiri Pirko 		/* If we work with block index, q is NULL and parent value
2534d680b352SJiri Pirko 		 * will never be used in the following code. The check
2535d680b352SJiri Pirko 		 * in tcf_fill_node prevents it. However, compiler does not
2536d680b352SJiri Pirko 		 * see that far, so set parent to zero to silence the warning
2537d680b352SJiri Pirko 		 * about parent being uninitialized.
2538d680b352SJiri Pirko 		 */
2539d680b352SJiri Pirko 		parent = 0;
25407960d1daSJiri Pirko 	} else {
25417960d1daSJiri Pirko 		const struct Qdisc_class_ops *cops;
25427960d1daSJiri Pirko 		struct net_device *dev;
25437960d1daSJiri Pirko 		unsigned long cl = 0;
25447960d1daSJiri Pirko 
2545cc7ec456SEric Dumazet 		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
2546cc7ec456SEric Dumazet 		if (!dev)
25471da177e4SLinus Torvalds 			return skb->len;
25481da177e4SLinus Torvalds 
2549a10fa201SJiri Pirko 		parent = tcm->tcm_parent;
2550a10fa201SJiri Pirko 		if (!parent) {
2551af356afaSPatrick McHardy 			q = dev->qdisc;
2552a10fa201SJiri Pirko 			parent = q->handle;
2553a10fa201SJiri Pirko 		} else {
25541da177e4SLinus Torvalds 			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
2555a10fa201SJiri Pirko 		}
25561da177e4SLinus Torvalds 		if (!q)
25571da177e4SLinus Torvalds 			goto out;
2558cc7ec456SEric Dumazet 		cops = q->ops->cl_ops;
2559cc7ec456SEric Dumazet 		if (!cops)
2560143976ceSWANG Cong 			goto out;
25616529eabaSJiri Pirko 		if (!cops->tcf_block)
2562143976ceSWANG Cong 			goto out;
25631da177e4SLinus Torvalds 		if (TC_H_MIN(tcm->tcm_parent)) {
2564143976ceSWANG Cong 			cl = cops->find(q, tcm->tcm_parent);
25651da177e4SLinus Torvalds 			if (cl == 0)
2566143976ceSWANG Cong 				goto out;
25671da177e4SLinus Torvalds 		}
2568cbaacc4eSAlexander Aring 		block = cops->tcf_block(q, cl, NULL);
25696529eabaSJiri Pirko 		if (!block)
2570143976ceSWANG Cong 			goto out;
25717960d1daSJiri Pirko 		if (tcf_block_shared(block))
25727960d1daSJiri Pirko 			q = NULL;
25737960d1daSJiri Pirko 	}
25741da177e4SLinus Torvalds 
2575acb31faeSJiri Pirko 	index_start = cb->args[0];
2576acb31faeSJiri Pirko 	index = 0;
25775bc17018SJiri Pirko 
2578bbf73830SVlad Buslov 	for (chain = __tcf_get_next_chain(block, NULL);
2579bbf73830SVlad Buslov 	     chain;
2580bbf73830SVlad Buslov 	     chain_prev = chain,
2581bbf73830SVlad Buslov 		     chain = __tcf_get_next_chain(block, chain),
2582bbf73830SVlad Buslov 		     tcf_chain_put(chain_prev)) {
25835bc17018SJiri Pirko 		if (tca[TCA_CHAIN] &&
25845bc17018SJiri Pirko 		    nla_get_u32(tca[TCA_CHAIN]) != chain->index)
25855bc17018SJiri Pirko 			continue;
2586a10fa201SJiri Pirko 		if (!tcf_chain_dump(chain, q, parent, skb, cb,
25875ae437adSRoman Kapl 				    index_start, &index)) {
2588bbf73830SVlad Buslov 			tcf_chain_put(chain);
25895ae437adSRoman Kapl 			err = -EMSGSIZE;
25905bc17018SJiri Pirko 			break;
25915bc17018SJiri Pirko 		}
25925ae437adSRoman Kapl 	}
25935bc17018SJiri Pirko 
2594787ce6d0SVlad Buslov 	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
259512db03b6SVlad Buslov 		tcf_block_refcnt_put(block, true);
2596acb31faeSJiri Pirko 	cb->args[0] = index;
25971da177e4SLinus Torvalds 
25981da177e4SLinus Torvalds out:
25995ae437adSRoman Kapl 	/* If we did no progress, the error (EMSGSIZE) is real */
26005ae437adSRoman Kapl 	if (skb->len == 0 && err)
26015ae437adSRoman Kapl 		return err;
26021da177e4SLinus Torvalds 	return skb->len;
26031da177e4SLinus Torvalds }
26041da177e4SLinus Torvalds 
2605a5654820SVlad Buslov static int tc_chain_fill_node(const struct tcf_proto_ops *tmplt_ops,
2606a5654820SVlad Buslov 			      void *tmplt_priv, u32 chain_index,
2607a5654820SVlad Buslov 			      struct net *net, struct sk_buff *skb,
2608a5654820SVlad Buslov 			      struct tcf_block *block,
260932a4f5ecSJiri Pirko 			      u32 portid, u32 seq, u16 flags, int event)
261032a4f5ecSJiri Pirko {
261132a4f5ecSJiri Pirko 	unsigned char *b = skb_tail_pointer(skb);
26129f407f17SJiri Pirko 	const struct tcf_proto_ops *ops;
261332a4f5ecSJiri Pirko 	struct nlmsghdr *nlh;
261432a4f5ecSJiri Pirko 	struct tcmsg *tcm;
26159f407f17SJiri Pirko 	void *priv;
26169f407f17SJiri Pirko 
2617a5654820SVlad Buslov 	ops = tmplt_ops;
2618a5654820SVlad Buslov 	priv = tmplt_priv;
261932a4f5ecSJiri Pirko 
262032a4f5ecSJiri Pirko 	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags);
262132a4f5ecSJiri Pirko 	if (!nlh)
262232a4f5ecSJiri Pirko 		goto out_nlmsg_trim;
262332a4f5ecSJiri Pirko 	tcm = nlmsg_data(nlh);
262432a4f5ecSJiri Pirko 	tcm->tcm_family = AF_UNSPEC;
262532a4f5ecSJiri Pirko 	tcm->tcm__pad1 = 0;
262632a4f5ecSJiri Pirko 	tcm->tcm__pad2 = 0;
262732a4f5ecSJiri Pirko 	tcm->tcm_handle = 0;
262832a4f5ecSJiri Pirko 	if (block->q) {
262932a4f5ecSJiri Pirko 		tcm->tcm_ifindex = qdisc_dev(block->q)->ifindex;
263032a4f5ecSJiri Pirko 		tcm->tcm_parent = block->q->handle;
263132a4f5ecSJiri Pirko 	} else {
263232a4f5ecSJiri Pirko 		tcm->tcm_ifindex = TCM_IFINDEX_MAGIC_BLOCK;
263332a4f5ecSJiri Pirko 		tcm->tcm_block_index = block->index;
263432a4f5ecSJiri Pirko 	}
263532a4f5ecSJiri Pirko 
2636a5654820SVlad Buslov 	if (nla_put_u32(skb, TCA_CHAIN, chain_index))
263732a4f5ecSJiri Pirko 		goto nla_put_failure;
263832a4f5ecSJiri Pirko 
26399f407f17SJiri Pirko 	if (ops) {
26409f407f17SJiri Pirko 		if (nla_put_string(skb, TCA_KIND, ops->kind))
26419f407f17SJiri Pirko 			goto nla_put_failure;
26429f407f17SJiri Pirko 		if (ops->tmplt_dump(skb, net, priv) < 0)
26439f407f17SJiri Pirko 			goto nla_put_failure;
26449f407f17SJiri Pirko 	}
26459f407f17SJiri Pirko 
264632a4f5ecSJiri Pirko 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
264732a4f5ecSJiri Pirko 	return skb->len;
264832a4f5ecSJiri Pirko 
264932a4f5ecSJiri Pirko out_nlmsg_trim:
265032a4f5ecSJiri Pirko nla_put_failure:
265132a4f5ecSJiri Pirko 	nlmsg_trim(skb, b);
265232a4f5ecSJiri Pirko 	return -EMSGSIZE;
265332a4f5ecSJiri Pirko }
265432a4f5ecSJiri Pirko 
265532a4f5ecSJiri Pirko static int tc_chain_notify(struct tcf_chain *chain, struct sk_buff *oskb,
265632a4f5ecSJiri Pirko 			   u32 seq, u16 flags, int event, bool unicast)
265732a4f5ecSJiri Pirko {
265832a4f5ecSJiri Pirko 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
265932a4f5ecSJiri Pirko 	struct tcf_block *block = chain->block;
266032a4f5ecSJiri Pirko 	struct net *net = block->net;
266132a4f5ecSJiri Pirko 	struct sk_buff *skb;
26625b5f99b1SZhike Wang 	int err = 0;
266332a4f5ecSJiri Pirko 
266432a4f5ecSJiri Pirko 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
266532a4f5ecSJiri Pirko 	if (!skb)
266632a4f5ecSJiri Pirko 		return -ENOBUFS;
266732a4f5ecSJiri Pirko 
2668a5654820SVlad Buslov 	if (tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
2669a5654820SVlad Buslov 			       chain->index, net, skb, block, portid,
267032a4f5ecSJiri Pirko 			       seq, flags, event) <= 0) {
267132a4f5ecSJiri Pirko 		kfree_skb(skb);
267232a4f5ecSJiri Pirko 		return -EINVAL;
267332a4f5ecSJiri Pirko 	}
267432a4f5ecSJiri Pirko 
267532a4f5ecSJiri Pirko 	if (unicast)
26765b5f99b1SZhike Wang 		err = netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
26775b5f99b1SZhike Wang 	else
26785b5f99b1SZhike Wang 		err = rtnetlink_send(skb, net, portid, RTNLGRP_TC,
26795b5f99b1SZhike Wang 				     flags & NLM_F_ECHO);
268032a4f5ecSJiri Pirko 
26815b5f99b1SZhike Wang 	if (err > 0)
26825b5f99b1SZhike Wang 		err = 0;
26835b5f99b1SZhike Wang 	return err;
268432a4f5ecSJiri Pirko }
268532a4f5ecSJiri Pirko 
2686a5654820SVlad Buslov static int tc_chain_notify_delete(const struct tcf_proto_ops *tmplt_ops,
2687a5654820SVlad Buslov 				  void *tmplt_priv, u32 chain_index,
2688a5654820SVlad Buslov 				  struct tcf_block *block, struct sk_buff *oskb,
2689a5654820SVlad Buslov 				  u32 seq, u16 flags, bool unicast)
2690a5654820SVlad Buslov {
2691a5654820SVlad Buslov 	u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
2692a5654820SVlad Buslov 	struct net *net = block->net;
2693a5654820SVlad Buslov 	struct sk_buff *skb;
2694a5654820SVlad Buslov 
2695a5654820SVlad Buslov 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
2696a5654820SVlad Buslov 	if (!skb)
2697a5654820SVlad Buslov 		return -ENOBUFS;
2698a5654820SVlad Buslov 
2699a5654820SVlad Buslov 	if (tc_chain_fill_node(tmplt_ops, tmplt_priv, chain_index, net, skb,
2700a5654820SVlad Buslov 			       block, portid, seq, flags, RTM_DELCHAIN) <= 0) {
2701a5654820SVlad Buslov 		kfree_skb(skb);
2702a5654820SVlad Buslov 		return -EINVAL;
2703a5654820SVlad Buslov 	}
2704a5654820SVlad Buslov 
2705a5654820SVlad Buslov 	if (unicast)
2706a5654820SVlad Buslov 		return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT);
2707a5654820SVlad Buslov 
2708a5654820SVlad Buslov 	return rtnetlink_send(skb, net, portid, RTNLGRP_TC, flags & NLM_F_ECHO);
2709a5654820SVlad Buslov }
2710a5654820SVlad Buslov 
27119f407f17SJiri Pirko static int tc_chain_tmplt_add(struct tcf_chain *chain, struct net *net,
27129f407f17SJiri Pirko 			      struct nlattr **tca,
27139f407f17SJiri Pirko 			      struct netlink_ext_ack *extack)
27149f407f17SJiri Pirko {
27159f407f17SJiri Pirko 	const struct tcf_proto_ops *ops;
27162dd5616eSEric Dumazet 	char name[IFNAMSIZ];
27179f407f17SJiri Pirko 	void *tmplt_priv;
27189f407f17SJiri Pirko 
27199f407f17SJiri Pirko 	/* If kind is not set, user did not specify template. */
27209f407f17SJiri Pirko 	if (!tca[TCA_KIND])
27219f407f17SJiri Pirko 		return 0;
27229f407f17SJiri Pirko 
27232dd5616eSEric Dumazet 	if (tcf_proto_check_kind(tca[TCA_KIND], name)) {
27242dd5616eSEric Dumazet 		NL_SET_ERR_MSG(extack, "Specified TC chain template name too long");
27252dd5616eSEric Dumazet 		return -EINVAL;
27262dd5616eSEric Dumazet 	}
27272dd5616eSEric Dumazet 
27282dd5616eSEric Dumazet 	ops = tcf_proto_lookup_ops(name, true, extack);
27299f407f17SJiri Pirko 	if (IS_ERR(ops))
27309f407f17SJiri Pirko 		return PTR_ERR(ops);
27319f407f17SJiri Pirko 	if (!ops->tmplt_create || !ops->tmplt_destroy || !ops->tmplt_dump) {
27329f407f17SJiri Pirko 		NL_SET_ERR_MSG(extack, "Chain templates are not supported with specified classifier");
27339f407f17SJiri Pirko 		return -EOPNOTSUPP;
27349f407f17SJiri Pirko 	}
27359f407f17SJiri Pirko 
27369f407f17SJiri Pirko 	tmplt_priv = ops->tmplt_create(net, chain, tca, extack);
27379f407f17SJiri Pirko 	if (IS_ERR(tmplt_priv)) {
27389f407f17SJiri Pirko 		module_put(ops->owner);
27399f407f17SJiri Pirko 		return PTR_ERR(tmplt_priv);
27409f407f17SJiri Pirko 	}
27419f407f17SJiri Pirko 	chain->tmplt_ops = ops;
27429f407f17SJiri Pirko 	chain->tmplt_priv = tmplt_priv;
27439f407f17SJiri Pirko 	return 0;
27449f407f17SJiri Pirko }
27459f407f17SJiri Pirko 
2746a5654820SVlad Buslov static void tc_chain_tmplt_del(const struct tcf_proto_ops *tmplt_ops,
2747a5654820SVlad Buslov 			       void *tmplt_priv)
27489f407f17SJiri Pirko {
27499f407f17SJiri Pirko 	/* If template ops are set, no work to do for us. */
2750a5654820SVlad Buslov 	if (!tmplt_ops)
27519f407f17SJiri Pirko 		return;
27529f407f17SJiri Pirko 
2753a5654820SVlad Buslov 	tmplt_ops->tmplt_destroy(tmplt_priv);
2754a5654820SVlad Buslov 	module_put(tmplt_ops->owner);
27559f407f17SJiri Pirko }
27569f407f17SJiri Pirko 
275732a4f5ecSJiri Pirko /* Add/delete/get a chain */
275832a4f5ecSJiri Pirko 
275932a4f5ecSJiri Pirko static int tc_ctl_chain(struct sk_buff *skb, struct nlmsghdr *n,
276032a4f5ecSJiri Pirko 			struct netlink_ext_ack *extack)
276132a4f5ecSJiri Pirko {
276232a4f5ecSJiri Pirko 	struct net *net = sock_net(skb->sk);
276332a4f5ecSJiri Pirko 	struct nlattr *tca[TCA_MAX + 1];
276432a4f5ecSJiri Pirko 	struct tcmsg *t;
276532a4f5ecSJiri Pirko 	u32 parent;
276632a4f5ecSJiri Pirko 	u32 chain_index;
276732a4f5ecSJiri Pirko 	struct Qdisc *q = NULL;
276832a4f5ecSJiri Pirko 	struct tcf_chain *chain = NULL;
276932a4f5ecSJiri Pirko 	struct tcf_block *block;
277032a4f5ecSJiri Pirko 	unsigned long cl;
277132a4f5ecSJiri Pirko 	int err;
277232a4f5ecSJiri Pirko 
277332a4f5ecSJiri Pirko 	if (n->nlmsg_type != RTM_GETCHAIN &&
277432a4f5ecSJiri Pirko 	    !netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN))
277532a4f5ecSJiri Pirko 		return -EPERM;
277632a4f5ecSJiri Pirko 
277732a4f5ecSJiri Pirko replay:
27788cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(n, sizeof(*t), tca, TCA_MAX,
27798cb08174SJohannes Berg 				     rtm_tca_policy, extack);
278032a4f5ecSJiri Pirko 	if (err < 0)
278132a4f5ecSJiri Pirko 		return err;
278232a4f5ecSJiri Pirko 
278332a4f5ecSJiri Pirko 	t = nlmsg_data(n);
278432a4f5ecSJiri Pirko 	parent = t->tcm_parent;
278532a4f5ecSJiri Pirko 	cl = 0;
278632a4f5ecSJiri Pirko 
278732a4f5ecSJiri Pirko 	block = tcf_block_find(net, &q, &parent, &cl,
278832a4f5ecSJiri Pirko 			       t->tcm_ifindex, t->tcm_block_index, extack);
278932a4f5ecSJiri Pirko 	if (IS_ERR(block))
279032a4f5ecSJiri Pirko 		return PTR_ERR(block);
279132a4f5ecSJiri Pirko 
279232a4f5ecSJiri Pirko 	chain_index = tca[TCA_CHAIN] ? nla_get_u32(tca[TCA_CHAIN]) : 0;
279332a4f5ecSJiri Pirko 	if (chain_index > TC_ACT_EXT_VAL_MASK) {
279432a4f5ecSJiri Pirko 		NL_SET_ERR_MSG(extack, "Specified chain index exceeds upper limit");
2795e368fdb6SVlad Buslov 		err = -EINVAL;
2796e368fdb6SVlad Buslov 		goto errout_block;
279732a4f5ecSJiri Pirko 	}
27982cbfab07SVlad Buslov 
27992cbfab07SVlad Buslov 	mutex_lock(&block->lock);
280032a4f5ecSJiri Pirko 	chain = tcf_chain_lookup(block, chain_index);
280132a4f5ecSJiri Pirko 	if (n->nlmsg_type == RTM_NEWCHAIN) {
280232a4f5ecSJiri Pirko 		if (chain) {
28033d32f4c5SJiri Pirko 			if (tcf_chain_held_by_acts_only(chain)) {
28041f3ed383SJiri Pirko 				/* The chain exists only because there is
28053d32f4c5SJiri Pirko 				 * some action referencing it.
28061f3ed383SJiri Pirko 				 */
28071f3ed383SJiri Pirko 				tcf_chain_hold(chain);
28081f3ed383SJiri Pirko 			} else {
280932a4f5ecSJiri Pirko 				NL_SET_ERR_MSG(extack, "Filter chain already exists");
2810e368fdb6SVlad Buslov 				err = -EEXIST;
28112cbfab07SVlad Buslov 				goto errout_block_locked;
281232a4f5ecSJiri Pirko 			}
28131f3ed383SJiri Pirko 		} else {
281432a4f5ecSJiri Pirko 			if (!(n->nlmsg_flags & NLM_F_CREATE)) {
281532a4f5ecSJiri Pirko 				NL_SET_ERR_MSG(extack, "Need both RTM_NEWCHAIN and NLM_F_CREATE to create a new chain");
2816e368fdb6SVlad Buslov 				err = -ENOENT;
28172cbfab07SVlad Buslov 				goto errout_block_locked;
281832a4f5ecSJiri Pirko 			}
281932a4f5ecSJiri Pirko 			chain = tcf_chain_create(block, chain_index);
282032a4f5ecSJiri Pirko 			if (!chain) {
282132a4f5ecSJiri Pirko 				NL_SET_ERR_MSG(extack, "Failed to create filter chain");
2822e368fdb6SVlad Buslov 				err = -ENOMEM;
28232cbfab07SVlad Buslov 				goto errout_block_locked;
282432a4f5ecSJiri Pirko 			}
28251f3ed383SJiri Pirko 		}
282632a4f5ecSJiri Pirko 	} else {
28273d32f4c5SJiri Pirko 		if (!chain || tcf_chain_held_by_acts_only(chain)) {
282832a4f5ecSJiri Pirko 			NL_SET_ERR_MSG(extack, "Cannot find specified filter chain");
2829e368fdb6SVlad Buslov 			err = -EINVAL;
28302cbfab07SVlad Buslov 			goto errout_block_locked;
283132a4f5ecSJiri Pirko 		}
283232a4f5ecSJiri Pirko 		tcf_chain_hold(chain);
283332a4f5ecSJiri Pirko 	}
283432a4f5ecSJiri Pirko 
28352cbfab07SVlad Buslov 	if (n->nlmsg_type == RTM_NEWCHAIN) {
28362cbfab07SVlad Buslov 		/* Modifying chain requires holding parent block lock. In case
28372cbfab07SVlad Buslov 		 * the chain was successfully added, take a reference to the
28382cbfab07SVlad Buslov 		 * chain. This ensures that an empty chain does not disappear at
28392cbfab07SVlad Buslov 		 * the end of this function.
284032a4f5ecSJiri Pirko 		 */
284132a4f5ecSJiri Pirko 		tcf_chain_hold(chain);
284232a4f5ecSJiri Pirko 		chain->explicitly_created = true;
28432cbfab07SVlad Buslov 	}
28442cbfab07SVlad Buslov 	mutex_unlock(&block->lock);
28452cbfab07SVlad Buslov 
28462cbfab07SVlad Buslov 	switch (n->nlmsg_type) {
28472cbfab07SVlad Buslov 	case RTM_NEWCHAIN:
28482cbfab07SVlad Buslov 		err = tc_chain_tmplt_add(chain, net, tca, extack);
28492cbfab07SVlad Buslov 		if (err) {
28502cbfab07SVlad Buslov 			tcf_chain_put_explicitly_created(chain);
28512cbfab07SVlad Buslov 			goto errout;
28522cbfab07SVlad Buslov 		}
28532cbfab07SVlad Buslov 
285432a4f5ecSJiri Pirko 		tc_chain_notify(chain, NULL, 0, NLM_F_CREATE | NLM_F_EXCL,
285532a4f5ecSJiri Pirko 				RTM_NEWCHAIN, false);
285632a4f5ecSJiri Pirko 		break;
285732a4f5ecSJiri Pirko 	case RTM_DELCHAIN:
2858f5b9bac7SCong Wang 		tfilter_notify_chain(net, skb, block, q, parent, n,
285912db03b6SVlad Buslov 				     chain, RTM_DELTFILTER, true);
286032a4f5ecSJiri Pirko 		/* Flush the chain first as the user requested chain removal. */
286112db03b6SVlad Buslov 		tcf_chain_flush(chain, true);
286232a4f5ecSJiri Pirko 		/* In case the chain was successfully deleted, put a reference
286332a4f5ecSJiri Pirko 		 * to the chain previously taken during addition.
286432a4f5ecSJiri Pirko 		 */
286532a4f5ecSJiri Pirko 		tcf_chain_put_explicitly_created(chain);
286632a4f5ecSJiri Pirko 		break;
286732a4f5ecSJiri Pirko 	case RTM_GETCHAIN:
286832a4f5ecSJiri Pirko 		err = tc_chain_notify(chain, skb, n->nlmsg_seq,
286932a4f5ecSJiri Pirko 				      n->nlmsg_seq, n->nlmsg_type, true);
287032a4f5ecSJiri Pirko 		if (err < 0)
287132a4f5ecSJiri Pirko 			NL_SET_ERR_MSG(extack, "Failed to send chain notify message");
287232a4f5ecSJiri Pirko 		break;
287332a4f5ecSJiri Pirko 	default:
287432a4f5ecSJiri Pirko 		err = -EOPNOTSUPP;
287532a4f5ecSJiri Pirko 		NL_SET_ERR_MSG(extack, "Unsupported message type");
287632a4f5ecSJiri Pirko 		goto errout;
287732a4f5ecSJiri Pirko 	}
287832a4f5ecSJiri Pirko 
287932a4f5ecSJiri Pirko errout:
288032a4f5ecSJiri Pirko 	tcf_chain_put(chain);
2881e368fdb6SVlad Buslov errout_block:
288212db03b6SVlad Buslov 	tcf_block_release(q, block, true);
288332a4f5ecSJiri Pirko 	if (err == -EAGAIN)
288432a4f5ecSJiri Pirko 		/* Replay the request. */
288532a4f5ecSJiri Pirko 		goto replay;
288632a4f5ecSJiri Pirko 	return err;
28872cbfab07SVlad Buslov 
28882cbfab07SVlad Buslov errout_block_locked:
28892cbfab07SVlad Buslov 	mutex_unlock(&block->lock);
28902cbfab07SVlad Buslov 	goto errout_block;
289132a4f5ecSJiri Pirko }
289232a4f5ecSJiri Pirko 
289332a4f5ecSJiri Pirko /* called with RTNL */
289432a4f5ecSJiri Pirko static int tc_dump_chain(struct sk_buff *skb, struct netlink_callback *cb)
289532a4f5ecSJiri Pirko {
289632a4f5ecSJiri Pirko 	struct net *net = sock_net(skb->sk);
289732a4f5ecSJiri Pirko 	struct nlattr *tca[TCA_MAX + 1];
289832a4f5ecSJiri Pirko 	struct Qdisc *q = NULL;
289932a4f5ecSJiri Pirko 	struct tcf_block *block;
290032a4f5ecSJiri Pirko 	struct tcmsg *tcm = nlmsg_data(cb->nlh);
2901ace4a267SVlad Buslov 	struct tcf_chain *chain;
290232a4f5ecSJiri Pirko 	long index_start;
290332a4f5ecSJiri Pirko 	long index;
290432a4f5ecSJiri Pirko 	u32 parent;
290532a4f5ecSJiri Pirko 	int err;
290632a4f5ecSJiri Pirko 
290732a4f5ecSJiri Pirko 	if (nlmsg_len(cb->nlh) < sizeof(*tcm))
290832a4f5ecSJiri Pirko 		return skb->len;
290932a4f5ecSJiri Pirko 
29108cb08174SJohannes Berg 	err = nlmsg_parse_deprecated(cb->nlh, sizeof(*tcm), tca, TCA_MAX,
29118cb08174SJohannes Berg 				     rtm_tca_policy, cb->extack);
291232a4f5ecSJiri Pirko 	if (err)
291332a4f5ecSJiri Pirko 		return err;
291432a4f5ecSJiri Pirko 
291532a4f5ecSJiri Pirko 	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK) {
2916787ce6d0SVlad Buslov 		block = tcf_block_refcnt_get(net, tcm->tcm_block_index);
291732a4f5ecSJiri Pirko 		if (!block)
291832a4f5ecSJiri Pirko 			goto out;
291932a4f5ecSJiri Pirko 		/* If we work with block index, q is NULL and parent value
292032a4f5ecSJiri Pirko 		 * will never be used in the following code. The check
292132a4f5ecSJiri Pirko 		 * in tcf_fill_node prevents it. However, compiler does not
292232a4f5ecSJiri Pirko 		 * see that far, so set parent to zero to silence the warning
292332a4f5ecSJiri Pirko 		 * about parent being uninitialized.
292432a4f5ecSJiri Pirko 		 */
292532a4f5ecSJiri Pirko 		parent = 0;
292632a4f5ecSJiri Pirko 	} else {
292732a4f5ecSJiri Pirko 		const struct Qdisc_class_ops *cops;
292832a4f5ecSJiri Pirko 		struct net_device *dev;
292932a4f5ecSJiri Pirko 		unsigned long cl = 0;
293032a4f5ecSJiri Pirko 
293132a4f5ecSJiri Pirko 		dev = __dev_get_by_index(net, tcm->tcm_ifindex);
293232a4f5ecSJiri Pirko 		if (!dev)
293332a4f5ecSJiri Pirko 			return skb->len;
293432a4f5ecSJiri Pirko 
293532a4f5ecSJiri Pirko 		parent = tcm->tcm_parent;
293632a4f5ecSJiri Pirko 		if (!parent) {
293732a4f5ecSJiri Pirko 			q = dev->qdisc;
293832a4f5ecSJiri Pirko 			parent = q->handle;
293932a4f5ecSJiri Pirko 		} else {
294032a4f5ecSJiri Pirko 			q = qdisc_lookup(dev, TC_H_MAJ(tcm->tcm_parent));
294132a4f5ecSJiri Pirko 		}
294232a4f5ecSJiri Pirko 		if (!q)
294332a4f5ecSJiri Pirko 			goto out;
294432a4f5ecSJiri Pirko 		cops = q->ops->cl_ops;
294532a4f5ecSJiri Pirko 		if (!cops)
294632a4f5ecSJiri Pirko 			goto out;
294732a4f5ecSJiri Pirko 		if (!cops->tcf_block)
294832a4f5ecSJiri Pirko 			goto out;
294932a4f5ecSJiri Pirko 		if (TC_H_MIN(tcm->tcm_parent)) {
295032a4f5ecSJiri Pirko 			cl = cops->find(q, tcm->tcm_parent);
295132a4f5ecSJiri Pirko 			if (cl == 0)
295232a4f5ecSJiri Pirko 				goto out;
295332a4f5ecSJiri Pirko 		}
295432a4f5ecSJiri Pirko 		block = cops->tcf_block(q, cl, NULL);
295532a4f5ecSJiri Pirko 		if (!block)
295632a4f5ecSJiri Pirko 			goto out;
295732a4f5ecSJiri Pirko 		if (tcf_block_shared(block))
295832a4f5ecSJiri Pirko 			q = NULL;
295932a4f5ecSJiri Pirko 	}
296032a4f5ecSJiri Pirko 
296132a4f5ecSJiri Pirko 	index_start = cb->args[0];
296232a4f5ecSJiri Pirko 	index = 0;
296332a4f5ecSJiri Pirko 
2964ace4a267SVlad Buslov 	mutex_lock(&block->lock);
2965ace4a267SVlad Buslov 	list_for_each_entry(chain, &block->chain_list, list) {
296632a4f5ecSJiri Pirko 		if ((tca[TCA_CHAIN] &&
296732a4f5ecSJiri Pirko 		     nla_get_u32(tca[TCA_CHAIN]) != chain->index))
296832a4f5ecSJiri Pirko 			continue;
296932a4f5ecSJiri Pirko 		if (index < index_start) {
297032a4f5ecSJiri Pirko 			index++;
297132a4f5ecSJiri Pirko 			continue;
297232a4f5ecSJiri Pirko 		}
2973ace4a267SVlad Buslov 		if (tcf_chain_held_by_acts_only(chain))
2974ace4a267SVlad Buslov 			continue;
2975a5654820SVlad Buslov 		err = tc_chain_fill_node(chain->tmplt_ops, chain->tmplt_priv,
2976a5654820SVlad Buslov 					 chain->index, net, skb, block,
297732a4f5ecSJiri Pirko 					 NETLINK_CB(cb->skb).portid,
297832a4f5ecSJiri Pirko 					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
297932a4f5ecSJiri Pirko 					 RTM_NEWCHAIN);
2980ace4a267SVlad Buslov 		if (err <= 0)
298132a4f5ecSJiri Pirko 			break;
298232a4f5ecSJiri Pirko 		index++;
298332a4f5ecSJiri Pirko 	}
2984ace4a267SVlad Buslov 	mutex_unlock(&block->lock);
298532a4f5ecSJiri Pirko 
2986787ce6d0SVlad Buslov 	if (tcm->tcm_ifindex == TCM_IFINDEX_MAGIC_BLOCK)
298712db03b6SVlad Buslov 		tcf_block_refcnt_put(block, true);
298832a4f5ecSJiri Pirko 	cb->args[0] = index;
298932a4f5ecSJiri Pirko 
299032a4f5ecSJiri Pirko out:
299132a4f5ecSJiri Pirko 	/* If we did no progress, the error (EMSGSIZE) is real */
299232a4f5ecSJiri Pirko 	if (skb->len == 0 && err)
299332a4f5ecSJiri Pirko 		return err;
299432a4f5ecSJiri Pirko 	return skb->len;
299532a4f5ecSJiri Pirko }
299632a4f5ecSJiri Pirko 
299718d0264fSWANG Cong void tcf_exts_destroy(struct tcf_exts *exts)
29981da177e4SLinus Torvalds {
29991da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
30003d66b89cSEric Dumazet 	if (exts->actions) {
300190b73b77SVlad Buslov 		tcf_action_destroy(exts->actions, TCA_ACT_UNBIND);
300222dc13c8SWANG Cong 		kfree(exts->actions);
30033d66b89cSEric Dumazet 	}
300422dc13c8SWANG Cong 	exts->nr_actions = 0;
30051da177e4SLinus Torvalds #endif
30061da177e4SLinus Torvalds }
3007aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_destroy);
30081da177e4SLinus Torvalds 
3009c1b52739SBenjamin LaHaise int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb,
301050a56190SAlexander Aring 		      struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr,
3011ec6743a1SVlad Buslov 		      bool rtnl_held, struct netlink_ext_ack *extack)
30121da177e4SLinus Torvalds {
30131da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
30141da177e4SLinus Torvalds 	{
30151da177e4SLinus Torvalds 		struct tc_action *act;
3016d04e6990SRoman Mashak 		size_t attr_size = 0;
30171da177e4SLinus Torvalds 
30185da57f42SWANG Cong 		if (exts->police && tb[exts->police]) {
30199fb9f251SJiri Pirko 			act = tcf_action_init_1(net, tp, tb[exts->police],
30209fb9f251SJiri Pirko 						rate_tlv, "police", ovr,
3021ec6743a1SVlad Buslov 						TCA_ACT_BIND, rtnl_held,
3022ec6743a1SVlad Buslov 						extack);
3023ab27cfb8SPatrick McHardy 			if (IS_ERR(act))
3024ab27cfb8SPatrick McHardy 				return PTR_ERR(act);
30251da177e4SLinus Torvalds 
302633be6271SWANG Cong 			act->type = exts->type = TCA_OLD_COMPAT;
302722dc13c8SWANG Cong 			exts->actions[0] = act;
302822dc13c8SWANG Cong 			exts->nr_actions = 1;
30295da57f42SWANG Cong 		} else if (exts->action && tb[exts->action]) {
303090b73b77SVlad Buslov 			int err;
303122dc13c8SWANG Cong 
30329fb9f251SJiri Pirko 			err = tcf_action_init(net, tp, tb[exts->action],
30339fb9f251SJiri Pirko 					      rate_tlv, NULL, ovr, TCA_ACT_BIND,
3034ec6743a1SVlad Buslov 					      exts->actions, &attr_size,
3035ec6743a1SVlad Buslov 					      rtnl_held, extack);
303690b73b77SVlad Buslov 			if (err < 0)
303733be6271SWANG Cong 				return err;
303890b73b77SVlad Buslov 			exts->nr_actions = err;
30391da177e4SLinus Torvalds 		}
30401da177e4SLinus Torvalds 	}
30411da177e4SLinus Torvalds #else
30425da57f42SWANG Cong 	if ((exts->action && tb[exts->action]) ||
304350a56190SAlexander Aring 	    (exts->police && tb[exts->police])) {
304450a56190SAlexander Aring 		NL_SET_ERR_MSG(extack, "Classifier actions are not supported per compile options (CONFIG_NET_CLS_ACT)");
30451da177e4SLinus Torvalds 		return -EOPNOTSUPP;
304650a56190SAlexander Aring 	}
30471da177e4SLinus Torvalds #endif
30481da177e4SLinus Torvalds 
30491da177e4SLinus Torvalds 	return 0;
30501da177e4SLinus Torvalds }
3051aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_validate);
30521da177e4SLinus Torvalds 
30539b0d4446SJiri Pirko void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src)
30541da177e4SLinus Torvalds {
30551da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
305622dc13c8SWANG Cong 	struct tcf_exts old = *dst;
305722dc13c8SWANG Cong 
30589b0d4446SJiri Pirko 	*dst = *src;
305922dc13c8SWANG Cong 	tcf_exts_destroy(&old);
30601da177e4SLinus Torvalds #endif
30611da177e4SLinus Torvalds }
3062aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_change);
30631da177e4SLinus Torvalds 
306422dc13c8SWANG Cong #ifdef CONFIG_NET_CLS_ACT
306522dc13c8SWANG Cong static struct tc_action *tcf_exts_first_act(struct tcf_exts *exts)
306622dc13c8SWANG Cong {
306722dc13c8SWANG Cong 	if (exts->nr_actions == 0)
306822dc13c8SWANG Cong 		return NULL;
306922dc13c8SWANG Cong 	else
307022dc13c8SWANG Cong 		return exts->actions[0];
307122dc13c8SWANG Cong }
307222dc13c8SWANG Cong #endif
307333be6271SWANG Cong 
30745da57f42SWANG Cong int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts)
30751da177e4SLinus Torvalds {
30761da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
30779cc63db5SCong Wang 	struct nlattr *nest;
30789cc63db5SCong Wang 
3079978dfd8dSJiri Pirko 	if (exts->action && tcf_exts_has_actions(exts)) {
30801da177e4SLinus Torvalds 		/*
30811da177e4SLinus Torvalds 		 * again for backward compatible mode - we want
30821da177e4SLinus Torvalds 		 * to work with both old and new modes of entering
30831da177e4SLinus Torvalds 		 * tc data even if iproute2  was newer - jhs
30841da177e4SLinus Torvalds 		 */
308533be6271SWANG Cong 		if (exts->type != TCA_OLD_COMPAT) {
3086ae0be8deSMichal Kubecek 			nest = nla_nest_start_noflag(skb, exts->action);
30874b3550efSPatrick McHardy 			if (nest == NULL)
30884b3550efSPatrick McHardy 				goto nla_put_failure;
308922dc13c8SWANG Cong 
309090b73b77SVlad Buslov 			if (tcf_action_dump(skb, exts->actions, 0, 0) < 0)
3091add93b61SPatrick McHardy 				goto nla_put_failure;
30924b3550efSPatrick McHardy 			nla_nest_end(skb, nest);
30935da57f42SWANG Cong 		} else if (exts->police) {
309433be6271SWANG Cong 			struct tc_action *act = tcf_exts_first_act(exts);
3095ae0be8deSMichal Kubecek 			nest = nla_nest_start_noflag(skb, exts->police);
309663acd680SJamal Hadi Salim 			if (nest == NULL || !act)
30974b3550efSPatrick McHardy 				goto nla_put_failure;
309833be6271SWANG Cong 			if (tcf_action_dump_old(skb, act, 0, 0) < 0)
3099add93b61SPatrick McHardy 				goto nla_put_failure;
31004b3550efSPatrick McHardy 			nla_nest_end(skb, nest);
31011da177e4SLinus Torvalds 		}
31021da177e4SLinus Torvalds 	}
31031da177e4SLinus Torvalds 	return 0;
31049cc63db5SCong Wang 
31059cc63db5SCong Wang nla_put_failure:
31069cc63db5SCong Wang 	nla_nest_cancel(skb, nest);
31071da177e4SLinus Torvalds 	return -1;
31089cc63db5SCong Wang #else
31099cc63db5SCong Wang 	return 0;
31109cc63db5SCong Wang #endif
31111da177e4SLinus Torvalds }
3112aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump);
31131da177e4SLinus Torvalds 
3114aa767bfeSStephen Hemminger 
31155da57f42SWANG Cong int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts)
31161da177e4SLinus Torvalds {
31171da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
311833be6271SWANG Cong 	struct tc_action *a = tcf_exts_first_act(exts);
3119b057df24SIgnacy Gawędzki 	if (a != NULL && tcf_action_copy_stats(skb, a, 1) < 0)
312033be6271SWANG Cong 		return -1;
31211da177e4SLinus Torvalds #endif
31221da177e4SLinus Torvalds 	return 0;
31231da177e4SLinus Torvalds }
3124aa767bfeSStephen Hemminger EXPORT_SYMBOL(tcf_exts_dump_stats);
31251da177e4SLinus Torvalds 
312640119211SVlad Buslov static void tcf_block_offload_inc(struct tcf_block *block, u32 *flags)
312740119211SVlad Buslov {
312840119211SVlad Buslov 	if (*flags & TCA_CLS_FLAGS_IN_HW)
312940119211SVlad Buslov 		return;
313040119211SVlad Buslov 	*flags |= TCA_CLS_FLAGS_IN_HW;
313140119211SVlad Buslov 	atomic_inc(&block->offloadcnt);
313240119211SVlad Buslov }
313340119211SVlad Buslov 
313440119211SVlad Buslov static void tcf_block_offload_dec(struct tcf_block *block, u32 *flags)
313540119211SVlad Buslov {
313640119211SVlad Buslov 	if (!(*flags & TCA_CLS_FLAGS_IN_HW))
313740119211SVlad Buslov 		return;
313840119211SVlad Buslov 	*flags &= ~TCA_CLS_FLAGS_IN_HW;
313940119211SVlad Buslov 	atomic_dec(&block->offloadcnt);
314040119211SVlad Buslov }
314140119211SVlad Buslov 
314240119211SVlad Buslov static void tc_cls_offload_cnt_update(struct tcf_block *block,
314340119211SVlad Buslov 				      struct tcf_proto *tp, u32 *cnt,
314440119211SVlad Buslov 				      u32 *flags, u32 diff, bool add)
314540119211SVlad Buslov {
314640119211SVlad Buslov 	lockdep_assert_held(&block->cb_lock);
314740119211SVlad Buslov 
314840119211SVlad Buslov 	spin_lock(&tp->lock);
314940119211SVlad Buslov 	if (add) {
315040119211SVlad Buslov 		if (!*cnt)
315140119211SVlad Buslov 			tcf_block_offload_inc(block, flags);
315240119211SVlad Buslov 		*cnt += diff;
315340119211SVlad Buslov 	} else {
315440119211SVlad Buslov 		*cnt -= diff;
315540119211SVlad Buslov 		if (!*cnt)
315640119211SVlad Buslov 			tcf_block_offload_dec(block, flags);
315740119211SVlad Buslov 	}
315840119211SVlad Buslov 	spin_unlock(&tp->lock);
315940119211SVlad Buslov }
316040119211SVlad Buslov 
316140119211SVlad Buslov static void
316240119211SVlad Buslov tc_cls_offload_cnt_reset(struct tcf_block *block, struct tcf_proto *tp,
316340119211SVlad Buslov 			 u32 *cnt, u32 *flags)
316440119211SVlad Buslov {
316540119211SVlad Buslov 	lockdep_assert_held(&block->cb_lock);
316640119211SVlad Buslov 
316740119211SVlad Buslov 	spin_lock(&tp->lock);
316840119211SVlad Buslov 	tcf_block_offload_dec(block, flags);
316940119211SVlad Buslov 	*cnt = 0;
317040119211SVlad Buslov 	spin_unlock(&tp->lock);
317140119211SVlad Buslov }
317240119211SVlad Buslov 
317340119211SVlad Buslov static int
317440119211SVlad Buslov __tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
3175aeb3fecdSCong Wang 		   void *type_data, bool err_stop)
3176717503b9SJiri Pirko {
3177955bcb6eSPablo Neira Ayuso 	struct flow_block_cb *block_cb;
3178aeb3fecdSCong Wang 	int ok_count = 0;
3179aeb3fecdSCong Wang 	int err;
3180aeb3fecdSCong Wang 
318140119211SVlad Buslov 	list_for_each_entry(block_cb, &block->flow_block.cb_list, list) {
318240119211SVlad Buslov 		err = block_cb->cb(type, type_data, block_cb->cb_priv);
318340119211SVlad Buslov 		if (err) {
318440119211SVlad Buslov 			if (err_stop)
318540119211SVlad Buslov 				return err;
318640119211SVlad Buslov 		} else {
318740119211SVlad Buslov 			ok_count++;
318840119211SVlad Buslov 		}
318940119211SVlad Buslov 	}
319040119211SVlad Buslov 	return ok_count;
319140119211SVlad Buslov }
319240119211SVlad Buslov 
319340119211SVlad Buslov int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
319440119211SVlad Buslov 		     void *type_data, bool err_stop, bool rtnl_held)
319540119211SVlad Buslov {
319611bd634dSVlad Buslov 	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
319740119211SVlad Buslov 	int ok_count;
319840119211SVlad Buslov 
319911bd634dSVlad Buslov retry:
320011bd634dSVlad Buslov 	if (take_rtnl)
320111bd634dSVlad Buslov 		rtnl_lock();
320240119211SVlad Buslov 	down_read(&block->cb_lock);
320311bd634dSVlad Buslov 	/* Need to obtain rtnl lock if block is bound to devs that require it.
320411bd634dSVlad Buslov 	 * In block bind code cb_lock is obtained while holding rtnl, so we must
320511bd634dSVlad Buslov 	 * obtain the locks in same order here.
320611bd634dSVlad Buslov 	 */
320711bd634dSVlad Buslov 	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
320840119211SVlad Buslov 		up_read(&block->cb_lock);
320911bd634dSVlad Buslov 		take_rtnl = true;
321011bd634dSVlad Buslov 		goto retry;
321111bd634dSVlad Buslov 	}
321211bd634dSVlad Buslov 
321311bd634dSVlad Buslov 	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
321411bd634dSVlad Buslov 
321511bd634dSVlad Buslov 	up_read(&block->cb_lock);
321611bd634dSVlad Buslov 	if (take_rtnl)
321711bd634dSVlad Buslov 		rtnl_unlock();
321840119211SVlad Buslov 	return ok_count;
321940119211SVlad Buslov }
322040119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_call);
322140119211SVlad Buslov 
322240119211SVlad Buslov /* Non-destructive filter add. If filter that wasn't already in hardware is
322340119211SVlad Buslov  * successfully offloaded, increment block offloads counter. On failure,
322440119211SVlad Buslov  * previously offloaded filter is considered to be intact and offloads counter
322540119211SVlad Buslov  * is not decremented.
322640119211SVlad Buslov  */
322740119211SVlad Buslov 
322840119211SVlad Buslov int tc_setup_cb_add(struct tcf_block *block, struct tcf_proto *tp,
322940119211SVlad Buslov 		    enum tc_setup_type type, void *type_data, bool err_stop,
323040119211SVlad Buslov 		    u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
323140119211SVlad Buslov {
323211bd634dSVlad Buslov 	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
323340119211SVlad Buslov 	int ok_count;
323440119211SVlad Buslov 
323511bd634dSVlad Buslov retry:
323611bd634dSVlad Buslov 	if (take_rtnl)
323711bd634dSVlad Buslov 		rtnl_lock();
32384f8116c8SVlad Buslov 	down_read(&block->cb_lock);
323911bd634dSVlad Buslov 	/* Need to obtain rtnl lock if block is bound to devs that require it.
324011bd634dSVlad Buslov 	 * In block bind code cb_lock is obtained while holding rtnl, so we must
324111bd634dSVlad Buslov 	 * obtain the locks in same order here.
324211bd634dSVlad Buslov 	 */
324311bd634dSVlad Buslov 	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
324411bd634dSVlad Buslov 		up_read(&block->cb_lock);
324511bd634dSVlad Buslov 		take_rtnl = true;
324611bd634dSVlad Buslov 		goto retry;
324711bd634dSVlad Buslov 	}
324811bd634dSVlad Buslov 
3249aeb3fecdSCong Wang 	/* Make sure all netdevs sharing this block are offload-capable. */
32504f8116c8SVlad Buslov 	if (block->nooffloaddevcnt && err_stop) {
32514f8116c8SVlad Buslov 		ok_count = -EOPNOTSUPP;
32524f8116c8SVlad Buslov 		goto err_unlock;
32534f8116c8SVlad Buslov 	}
3254aeb3fecdSCong Wang 
325540119211SVlad Buslov 	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
3256a449a3e7SVlad Buslov 	if (ok_count < 0)
3257a449a3e7SVlad Buslov 		goto err_unlock;
3258a449a3e7SVlad Buslov 
3259a449a3e7SVlad Buslov 	if (tp->ops->hw_add)
3260a449a3e7SVlad Buslov 		tp->ops->hw_add(tp, type_data);
326140119211SVlad Buslov 	if (ok_count > 0)
326240119211SVlad Buslov 		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags,
326340119211SVlad Buslov 					  ok_count, true);
32644f8116c8SVlad Buslov err_unlock:
32654f8116c8SVlad Buslov 	up_read(&block->cb_lock);
326611bd634dSVlad Buslov 	if (take_rtnl)
326711bd634dSVlad Buslov 		rtnl_unlock();
326840119211SVlad Buslov 	return ok_count < 0 ? ok_count : 0;
3269717503b9SJiri Pirko }
327040119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_add);
327140119211SVlad Buslov 
327240119211SVlad Buslov /* Destructive filter replace. If filter that wasn't already in hardware is
327340119211SVlad Buslov  * successfully offloaded, increment block offload counter. On failure,
327440119211SVlad Buslov  * previously offloaded filter is considered to be destroyed and offload counter
327540119211SVlad Buslov  * is decremented.
327640119211SVlad Buslov  */
327740119211SVlad Buslov 
327840119211SVlad Buslov int tc_setup_cb_replace(struct tcf_block *block, struct tcf_proto *tp,
327940119211SVlad Buslov 			enum tc_setup_type type, void *type_data, bool err_stop,
328040119211SVlad Buslov 			u32 *old_flags, unsigned int *old_in_hw_count,
328140119211SVlad Buslov 			u32 *new_flags, unsigned int *new_in_hw_count,
328240119211SVlad Buslov 			bool rtnl_held)
328340119211SVlad Buslov {
328411bd634dSVlad Buslov 	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
328540119211SVlad Buslov 	int ok_count;
328640119211SVlad Buslov 
328711bd634dSVlad Buslov retry:
328811bd634dSVlad Buslov 	if (take_rtnl)
328911bd634dSVlad Buslov 		rtnl_lock();
329040119211SVlad Buslov 	down_read(&block->cb_lock);
329111bd634dSVlad Buslov 	/* Need to obtain rtnl lock if block is bound to devs that require it.
329211bd634dSVlad Buslov 	 * In block bind code cb_lock is obtained while holding rtnl, so we must
329311bd634dSVlad Buslov 	 * obtain the locks in same order here.
329411bd634dSVlad Buslov 	 */
329511bd634dSVlad Buslov 	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
329611bd634dSVlad Buslov 		up_read(&block->cb_lock);
329711bd634dSVlad Buslov 		take_rtnl = true;
329811bd634dSVlad Buslov 		goto retry;
329911bd634dSVlad Buslov 	}
330011bd634dSVlad Buslov 
330140119211SVlad Buslov 	/* Make sure all netdevs sharing this block are offload-capable. */
330240119211SVlad Buslov 	if (block->nooffloaddevcnt && err_stop) {
330340119211SVlad Buslov 		ok_count = -EOPNOTSUPP;
330440119211SVlad Buslov 		goto err_unlock;
330540119211SVlad Buslov 	}
330640119211SVlad Buslov 
330740119211SVlad Buslov 	tc_cls_offload_cnt_reset(block, tp, old_in_hw_count, old_flags);
3308a449a3e7SVlad Buslov 	if (tp->ops->hw_del)
3309a449a3e7SVlad Buslov 		tp->ops->hw_del(tp, type_data);
331040119211SVlad Buslov 
331140119211SVlad Buslov 	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
3312a449a3e7SVlad Buslov 	if (ok_count < 0)
3313a449a3e7SVlad Buslov 		goto err_unlock;
3314a449a3e7SVlad Buslov 
3315a449a3e7SVlad Buslov 	if (tp->ops->hw_add)
3316a449a3e7SVlad Buslov 		tp->ops->hw_add(tp, type_data);
331740119211SVlad Buslov 	if (ok_count > 0)
3318a449a3e7SVlad Buslov 		tc_cls_offload_cnt_update(block, tp, new_in_hw_count,
3319a449a3e7SVlad Buslov 					  new_flags, ok_count, true);
332040119211SVlad Buslov err_unlock:
332140119211SVlad Buslov 	up_read(&block->cb_lock);
332211bd634dSVlad Buslov 	if (take_rtnl)
332311bd634dSVlad Buslov 		rtnl_unlock();
332440119211SVlad Buslov 	return ok_count < 0 ? ok_count : 0;
332540119211SVlad Buslov }
332640119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_replace);
332740119211SVlad Buslov 
332840119211SVlad Buslov /* Destroy filter and decrement block offload counter, if filter was previously
332940119211SVlad Buslov  * offloaded.
333040119211SVlad Buslov  */
333140119211SVlad Buslov 
333240119211SVlad Buslov int tc_setup_cb_destroy(struct tcf_block *block, struct tcf_proto *tp,
333340119211SVlad Buslov 			enum tc_setup_type type, void *type_data, bool err_stop,
333440119211SVlad Buslov 			u32 *flags, unsigned int *in_hw_count, bool rtnl_held)
333540119211SVlad Buslov {
333611bd634dSVlad Buslov 	bool take_rtnl = READ_ONCE(block->lockeddevcnt) && !rtnl_held;
333740119211SVlad Buslov 	int ok_count;
333840119211SVlad Buslov 
333911bd634dSVlad Buslov retry:
334011bd634dSVlad Buslov 	if (take_rtnl)
334111bd634dSVlad Buslov 		rtnl_lock();
334240119211SVlad Buslov 	down_read(&block->cb_lock);
334311bd634dSVlad Buslov 	/* Need to obtain rtnl lock if block is bound to devs that require it.
334411bd634dSVlad Buslov 	 * In block bind code cb_lock is obtained while holding rtnl, so we must
334511bd634dSVlad Buslov 	 * obtain the locks in same order here.
334611bd634dSVlad Buslov 	 */
334711bd634dSVlad Buslov 	if (!rtnl_held && !take_rtnl && block->lockeddevcnt) {
334811bd634dSVlad Buslov 		up_read(&block->cb_lock);
334911bd634dSVlad Buslov 		take_rtnl = true;
335011bd634dSVlad Buslov 		goto retry;
335111bd634dSVlad Buslov 	}
335211bd634dSVlad Buslov 
335340119211SVlad Buslov 	ok_count = __tc_setup_cb_call(block, type, type_data, err_stop);
335440119211SVlad Buslov 
335540119211SVlad Buslov 	tc_cls_offload_cnt_reset(block, tp, in_hw_count, flags);
3356a449a3e7SVlad Buslov 	if (tp->ops->hw_del)
3357a449a3e7SVlad Buslov 		tp->ops->hw_del(tp, type_data);
3358a449a3e7SVlad Buslov 
335940119211SVlad Buslov 	up_read(&block->cb_lock);
336011bd634dSVlad Buslov 	if (take_rtnl)
336111bd634dSVlad Buslov 		rtnl_unlock();
336240119211SVlad Buslov 	return ok_count < 0 ? ok_count : 0;
336340119211SVlad Buslov }
336440119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_destroy);
336540119211SVlad Buslov 
336640119211SVlad Buslov int tc_setup_cb_reoffload(struct tcf_block *block, struct tcf_proto *tp,
336740119211SVlad Buslov 			  bool add, flow_setup_cb_t *cb,
336840119211SVlad Buslov 			  enum tc_setup_type type, void *type_data,
336940119211SVlad Buslov 			  void *cb_priv, u32 *flags, unsigned int *in_hw_count)
337040119211SVlad Buslov {
337140119211SVlad Buslov 	int err = cb(type, type_data, cb_priv);
337240119211SVlad Buslov 
337340119211SVlad Buslov 	if (err) {
337440119211SVlad Buslov 		if (add && tc_skip_sw(*flags))
337540119211SVlad Buslov 			return err;
337640119211SVlad Buslov 	} else {
337740119211SVlad Buslov 		tc_cls_offload_cnt_update(block, tp, in_hw_count, flags, 1,
337840119211SVlad Buslov 					  add);
337940119211SVlad Buslov 	}
338040119211SVlad Buslov 
338140119211SVlad Buslov 	return 0;
338240119211SVlad Buslov }
338340119211SVlad Buslov EXPORT_SYMBOL(tc_setup_cb_reoffload);
3384b3f55bddSJiri Pirko 
33855a6ff4b1SVlad Buslov void tc_cleanup_flow_action(struct flow_action *flow_action)
33865a6ff4b1SVlad Buslov {
33875a6ff4b1SVlad Buslov 	struct flow_action_entry *entry;
33885a6ff4b1SVlad Buslov 	int i;
33895a6ff4b1SVlad Buslov 
33901158958aSVlad Buslov 	flow_action_for_each(i, entry, flow_action)
33911158958aSVlad Buslov 		if (entry->destructor)
33921158958aSVlad Buslov 			entry->destructor(entry->destructor_priv);
33935a6ff4b1SVlad Buslov }
33945a6ff4b1SVlad Buslov EXPORT_SYMBOL(tc_cleanup_flow_action);
33955a6ff4b1SVlad Buslov 
33961158958aSVlad Buslov static void tcf_mirred_get_dev(struct flow_action_entry *entry,
33971158958aSVlad Buslov 			       const struct tc_action *act)
33981158958aSVlad Buslov {
3399470d5060SVlad Buslov #ifdef CONFIG_NET_CLS_ACT
3400470d5060SVlad Buslov 	entry->dev = act->ops->get_dev(act, &entry->destructor);
34011158958aSVlad Buslov 	if (!entry->dev)
34021158958aSVlad Buslov 		return;
34031158958aSVlad Buslov 	entry->destructor_priv = entry->dev;
3404470d5060SVlad Buslov #endif
34051158958aSVlad Buslov }
34061158958aSVlad Buslov 
34071158958aSVlad Buslov static void tcf_tunnel_encap_put_tunnel(void *priv)
34081158958aSVlad Buslov {
34091158958aSVlad Buslov 	struct ip_tunnel_info *tunnel = priv;
34101158958aSVlad Buslov 
34111158958aSVlad Buslov 	kfree(tunnel);
34121158958aSVlad Buslov }
34131158958aSVlad Buslov 
34141158958aSVlad Buslov static int tcf_tunnel_encap_get_tunnel(struct flow_action_entry *entry,
34151158958aSVlad Buslov 				       const struct tc_action *act)
34161158958aSVlad Buslov {
34171158958aSVlad Buslov 	entry->tunnel = tcf_tunnel_info_copy(act);
34181158958aSVlad Buslov 	if (!entry->tunnel)
34191158958aSVlad Buslov 		return -ENOMEM;
34201158958aSVlad Buslov 	entry->destructor = tcf_tunnel_encap_put_tunnel;
34211158958aSVlad Buslov 	entry->destructor_priv = entry->tunnel;
34221158958aSVlad Buslov 	return 0;
34231158958aSVlad Buslov }
34241158958aSVlad Buslov 
34254a5da47dSVlad Buslov static void tcf_sample_get_group(struct flow_action_entry *entry,
34264a5da47dSVlad Buslov 				 const struct tc_action *act)
34274a5da47dSVlad Buslov {
34284a5da47dSVlad Buslov #ifdef CONFIG_NET_CLS_ACT
34294a5da47dSVlad Buslov 	entry->sample.psample_group =
34304a5da47dSVlad Buslov 		act->ops->get_psample_group(act, &entry->destructor);
34314a5da47dSVlad Buslov 	entry->destructor_priv = entry->sample.psample_group;
34324a5da47dSVlad Buslov #endif
34334a5da47dSVlad Buslov }
34344a5da47dSVlad Buslov 
34353a7b6861SPablo Neira Ayuso int tc_setup_flow_action(struct flow_action *flow_action,
34369838b20aSVlad Buslov 			 const struct tcf_exts *exts, bool rtnl_held)
34373a7b6861SPablo Neira Ayuso {
3438*7a472814SVlad Buslov 	struct tc_action *act;
34399838b20aSVlad Buslov 	int i, j, k, err = 0;
34403a7b6861SPablo Neira Ayuso 
34413a7b6861SPablo Neira Ayuso 	if (!exts)
34423a7b6861SPablo Neira Ayuso 		return 0;
34433a7b6861SPablo Neira Ayuso 
34449838b20aSVlad Buslov 	if (!rtnl_held)
34459838b20aSVlad Buslov 		rtnl_lock();
34469838b20aSVlad Buslov 
34473a7b6861SPablo Neira Ayuso 	j = 0;
34483a7b6861SPablo Neira Ayuso 	tcf_exts_for_each_action(i, act, exts) {
34493a7b6861SPablo Neira Ayuso 		struct flow_action_entry *entry;
34503a7b6861SPablo Neira Ayuso 
34513a7b6861SPablo Neira Ayuso 		entry = &flow_action->entries[j];
3452*7a472814SVlad Buslov 		spin_lock_bh(&act->tcfa_lock);
34533a7b6861SPablo Neira Ayuso 		if (is_tcf_gact_ok(act)) {
34543a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_ACCEPT;
34553a7b6861SPablo Neira Ayuso 		} else if (is_tcf_gact_shot(act)) {
34563a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_DROP;
34573a7b6861SPablo Neira Ayuso 		} else if (is_tcf_gact_trap(act)) {
34583a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_TRAP;
34593a7b6861SPablo Neira Ayuso 		} else if (is_tcf_gact_goto_chain(act)) {
34603a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_GOTO;
34613a7b6861SPablo Neira Ayuso 			entry->chain_index = tcf_gact_goto_chain_index(act);
34623a7b6861SPablo Neira Ayuso 		} else if (is_tcf_mirred_egress_redirect(act)) {
34633a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_REDIRECT;
34641158958aSVlad Buslov 			tcf_mirred_get_dev(entry, act);
34653a7b6861SPablo Neira Ayuso 		} else if (is_tcf_mirred_egress_mirror(act)) {
34663a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_MIRRED;
34671158958aSVlad Buslov 			tcf_mirred_get_dev(entry, act);
346848e584acSJohn Hurley 		} else if (is_tcf_mirred_ingress_redirect(act)) {
346948e584acSJohn Hurley 			entry->id = FLOW_ACTION_REDIRECT_INGRESS;
34701158958aSVlad Buslov 			tcf_mirred_get_dev(entry, act);
347148e584acSJohn Hurley 		} else if (is_tcf_mirred_ingress_mirror(act)) {
347248e584acSJohn Hurley 			entry->id = FLOW_ACTION_MIRRED_INGRESS;
34731158958aSVlad Buslov 			tcf_mirred_get_dev(entry, act);
34743a7b6861SPablo Neira Ayuso 		} else if (is_tcf_vlan(act)) {
34753a7b6861SPablo Neira Ayuso 			switch (tcf_vlan_action(act)) {
34763a7b6861SPablo Neira Ayuso 			case TCA_VLAN_ACT_PUSH:
34773a7b6861SPablo Neira Ayuso 				entry->id = FLOW_ACTION_VLAN_PUSH;
34783a7b6861SPablo Neira Ayuso 				entry->vlan.vid = tcf_vlan_push_vid(act);
34793a7b6861SPablo Neira Ayuso 				entry->vlan.proto = tcf_vlan_push_proto(act);
34803a7b6861SPablo Neira Ayuso 				entry->vlan.prio = tcf_vlan_push_prio(act);
34813a7b6861SPablo Neira Ayuso 				break;
34823a7b6861SPablo Neira Ayuso 			case TCA_VLAN_ACT_POP:
34833a7b6861SPablo Neira Ayuso 				entry->id = FLOW_ACTION_VLAN_POP;
34843a7b6861SPablo Neira Ayuso 				break;
34853a7b6861SPablo Neira Ayuso 			case TCA_VLAN_ACT_MODIFY:
34863a7b6861SPablo Neira Ayuso 				entry->id = FLOW_ACTION_VLAN_MANGLE;
34873a7b6861SPablo Neira Ayuso 				entry->vlan.vid = tcf_vlan_push_vid(act);
34883a7b6861SPablo Neira Ayuso 				entry->vlan.proto = tcf_vlan_push_proto(act);
34893a7b6861SPablo Neira Ayuso 				entry->vlan.prio = tcf_vlan_push_prio(act);
34903a7b6861SPablo Neira Ayuso 				break;
34913a7b6861SPablo Neira Ayuso 			default:
34929838b20aSVlad Buslov 				err = -EOPNOTSUPP;
3493*7a472814SVlad Buslov 				goto err_out_locked;
34943a7b6861SPablo Neira Ayuso 			}
34953a7b6861SPablo Neira Ayuso 		} else if (is_tcf_tunnel_set(act)) {
34963a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_TUNNEL_ENCAP;
34971158958aSVlad Buslov 			err = tcf_tunnel_encap_get_tunnel(entry, act);
34981158958aSVlad Buslov 			if (err)
3499*7a472814SVlad Buslov 				goto err_out_locked;
35003a7b6861SPablo Neira Ayuso 		} else if (is_tcf_tunnel_release(act)) {
35013a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_TUNNEL_DECAP;
35023a7b6861SPablo Neira Ayuso 		} else if (is_tcf_pedit(act)) {
35033a7b6861SPablo Neira Ayuso 			for (k = 0; k < tcf_pedit_nkeys(act); k++) {
35043a7b6861SPablo Neira Ayuso 				switch (tcf_pedit_cmd(act, k)) {
35053a7b6861SPablo Neira Ayuso 				case TCA_PEDIT_KEY_EX_CMD_SET:
35063a7b6861SPablo Neira Ayuso 					entry->id = FLOW_ACTION_MANGLE;
35073a7b6861SPablo Neira Ayuso 					break;
35083a7b6861SPablo Neira Ayuso 				case TCA_PEDIT_KEY_EX_CMD_ADD:
35093a7b6861SPablo Neira Ayuso 					entry->id = FLOW_ACTION_ADD;
35103a7b6861SPablo Neira Ayuso 					break;
35113a7b6861SPablo Neira Ayuso 				default:
35129838b20aSVlad Buslov 					err = -EOPNOTSUPP;
3513*7a472814SVlad Buslov 					goto err_out_locked;
35143a7b6861SPablo Neira Ayuso 				}
35153a7b6861SPablo Neira Ayuso 				entry->mangle.htype = tcf_pedit_htype(act, k);
35163a7b6861SPablo Neira Ayuso 				entry->mangle.mask = tcf_pedit_mask(act, k);
35173a7b6861SPablo Neira Ayuso 				entry->mangle.val = tcf_pedit_val(act, k);
35183a7b6861SPablo Neira Ayuso 				entry->mangle.offset = tcf_pedit_offset(act, k);
35193a7b6861SPablo Neira Ayuso 				entry = &flow_action->entries[++j];
35203a7b6861SPablo Neira Ayuso 			}
35213a7b6861SPablo Neira Ayuso 		} else if (is_tcf_csum(act)) {
35223a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_CSUM;
35233a7b6861SPablo Neira Ayuso 			entry->csum_flags = tcf_csum_update_flags(act);
35243a7b6861SPablo Neira Ayuso 		} else if (is_tcf_skbedit_mark(act)) {
35253a7b6861SPablo Neira Ayuso 			entry->id = FLOW_ACTION_MARK;
35263a7b6861SPablo Neira Ayuso 			entry->mark = tcf_skbedit_mark(act);
3527a7a7be60SPieter Jansen van Vuuren 		} else if (is_tcf_sample(act)) {
3528a7a7be60SPieter Jansen van Vuuren 			entry->id = FLOW_ACTION_SAMPLE;
3529a7a7be60SPieter Jansen van Vuuren 			entry->sample.trunc_size = tcf_sample_trunc_size(act);
3530a7a7be60SPieter Jansen van Vuuren 			entry->sample.truncate = tcf_sample_truncate(act);
3531a7a7be60SPieter Jansen van Vuuren 			entry->sample.rate = tcf_sample_rate(act);
35324a5da47dSVlad Buslov 			tcf_sample_get_group(entry, act);
35338c8cfc6eSPieter Jansen van Vuuren 		} else if (is_tcf_police(act)) {
35348c8cfc6eSPieter Jansen van Vuuren 			entry->id = FLOW_ACTION_POLICE;
35358c8cfc6eSPieter Jansen van Vuuren 			entry->police.burst = tcf_police_tcfp_burst(act);
35368c8cfc6eSPieter Jansen van Vuuren 			entry->police.rate_bytes_ps =
35378c8cfc6eSPieter Jansen van Vuuren 				tcf_police_rate_bytes_ps(act);
3538b57dc7c1SPaul Blakey 		} else if (is_tcf_ct(act)) {
3539b57dc7c1SPaul Blakey 			entry->id = FLOW_ACTION_CT;
3540b57dc7c1SPaul Blakey 			entry->ct.action = tcf_ct_action(act);
3541b57dc7c1SPaul Blakey 			entry->ct.zone = tcf_ct_zone(act);
35426749d590SJohn Hurley 		} else if (is_tcf_mpls(act)) {
35436749d590SJohn Hurley 			switch (tcf_mpls_action(act)) {
35446749d590SJohn Hurley 			case TCA_MPLS_ACT_PUSH:
35456749d590SJohn Hurley 				entry->id = FLOW_ACTION_MPLS_PUSH;
35466749d590SJohn Hurley 				entry->mpls_push.proto = tcf_mpls_proto(act);
35476749d590SJohn Hurley 				entry->mpls_push.label = tcf_mpls_label(act);
35486749d590SJohn Hurley 				entry->mpls_push.tc = tcf_mpls_tc(act);
35496749d590SJohn Hurley 				entry->mpls_push.bos = tcf_mpls_bos(act);
35506749d590SJohn Hurley 				entry->mpls_push.ttl = tcf_mpls_ttl(act);
35516749d590SJohn Hurley 				break;
35526749d590SJohn Hurley 			case TCA_MPLS_ACT_POP:
35536749d590SJohn Hurley 				entry->id = FLOW_ACTION_MPLS_POP;
35546749d590SJohn Hurley 				entry->mpls_pop.proto = tcf_mpls_proto(act);
35556749d590SJohn Hurley 				break;
35566749d590SJohn Hurley 			case TCA_MPLS_ACT_MODIFY:
35576749d590SJohn Hurley 				entry->id = FLOW_ACTION_MPLS_MANGLE;
35586749d590SJohn Hurley 				entry->mpls_mangle.label = tcf_mpls_label(act);
35596749d590SJohn Hurley 				entry->mpls_mangle.tc = tcf_mpls_tc(act);
35606749d590SJohn Hurley 				entry->mpls_mangle.bos = tcf_mpls_bos(act);
35616749d590SJohn Hurley 				entry->mpls_mangle.ttl = tcf_mpls_ttl(act);
35626749d590SJohn Hurley 				break;
35636749d590SJohn Hurley 			default:
3564*7a472814SVlad Buslov 				goto err_out_locked;
35656749d590SJohn Hurley 			}
3566fb1b775aSJohn Hurley 		} else if (is_tcf_skbedit_ptype(act)) {
3567fb1b775aSJohn Hurley 			entry->id = FLOW_ACTION_PTYPE;
3568fb1b775aSJohn Hurley 			entry->ptype = tcf_skbedit_ptype(act);
35693a7b6861SPablo Neira Ayuso 		} else {
35709838b20aSVlad Buslov 			err = -EOPNOTSUPP;
3571*7a472814SVlad Buslov 			goto err_out_locked;
35723a7b6861SPablo Neira Ayuso 		}
3573*7a472814SVlad Buslov 		spin_unlock_bh(&act->tcfa_lock);
35743a7b6861SPablo Neira Ayuso 
35753a7b6861SPablo Neira Ayuso 		if (!is_tcf_pedit(act))
35763a7b6861SPablo Neira Ayuso 			j++;
35773a7b6861SPablo Neira Ayuso 	}
35789838b20aSVlad Buslov 
35793a7b6861SPablo Neira Ayuso err_out:
35809838b20aSVlad Buslov 	if (!rtnl_held)
35819838b20aSVlad Buslov 		rtnl_unlock();
35829838b20aSVlad Buslov 
35835a6ff4b1SVlad Buslov 	if (err)
35845a6ff4b1SVlad Buslov 		tc_cleanup_flow_action(flow_action);
35855a6ff4b1SVlad Buslov 
35869838b20aSVlad Buslov 	return err;
3587*7a472814SVlad Buslov err_out_locked:
3588*7a472814SVlad Buslov 	spin_unlock_bh(&act->tcfa_lock);
3589*7a472814SVlad Buslov 	goto err_out;
35903a7b6861SPablo Neira Ayuso }
35913a7b6861SPablo Neira Ayuso EXPORT_SYMBOL(tc_setup_flow_action);
35923a7b6861SPablo Neira Ayuso 
3593e3ab786bSPablo Neira Ayuso unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
3594e3ab786bSPablo Neira Ayuso {
3595e3ab786bSPablo Neira Ayuso 	unsigned int num_acts = 0;
3596e3ab786bSPablo Neira Ayuso 	struct tc_action *act;
3597e3ab786bSPablo Neira Ayuso 	int i;
3598e3ab786bSPablo Neira Ayuso 
3599e3ab786bSPablo Neira Ayuso 	tcf_exts_for_each_action(i, act, exts) {
3600e3ab786bSPablo Neira Ayuso 		if (is_tcf_pedit(act))
3601e3ab786bSPablo Neira Ayuso 			num_acts += tcf_pedit_nkeys(act);
3602e3ab786bSPablo Neira Ayuso 		else
3603e3ab786bSPablo Neira Ayuso 			num_acts++;
3604e3ab786bSPablo Neira Ayuso 	}
3605e3ab786bSPablo Neira Ayuso 	return num_acts;
3606e3ab786bSPablo Neira Ayuso }
3607e3ab786bSPablo Neira Ayuso EXPORT_SYMBOL(tcf_exts_num_actions);
3608e3ab786bSPablo Neira Ayuso 
360948617387SJiri Pirko static __net_init int tcf_net_init(struct net *net)
361048617387SJiri Pirko {
361148617387SJiri Pirko 	struct tcf_net *tn = net_generic(net, tcf_net_id);
361248617387SJiri Pirko 
3613ab281629SVlad Buslov 	spin_lock_init(&tn->idr_lock);
361448617387SJiri Pirko 	idr_init(&tn->idr);
361548617387SJiri Pirko 	return 0;
361648617387SJiri Pirko }
361748617387SJiri Pirko 
361848617387SJiri Pirko static void __net_exit tcf_net_exit(struct net *net)
361948617387SJiri Pirko {
362048617387SJiri Pirko 	struct tcf_net *tn = net_generic(net, tcf_net_id);
362148617387SJiri Pirko 
362248617387SJiri Pirko 	idr_destroy(&tn->idr);
362348617387SJiri Pirko }
362448617387SJiri Pirko 
362548617387SJiri Pirko static struct pernet_operations tcf_net_ops = {
362648617387SJiri Pirko 	.init = tcf_net_init,
362748617387SJiri Pirko 	.exit = tcf_net_exit,
362848617387SJiri Pirko 	.id   = &tcf_net_id,
362948617387SJiri Pirko 	.size = sizeof(struct tcf_net),
363048617387SJiri Pirko };
363148617387SJiri Pirko 
363225a443f7SJohn Hurley static struct flow_indr_block_entry block_entry = {
363325a443f7SJohn Hurley 	.cb = tc_indr_block_get_and_cmd,
363425a443f7SJohn Hurley 	.list = LIST_HEAD_INIT(block_entry.list),
36351150ab0fSwenxu };
36361150ab0fSwenxu 
36371da177e4SLinus Torvalds static int __init tc_filter_init(void)
36381da177e4SLinus Torvalds {
363948617387SJiri Pirko 	int err;
364048617387SJiri Pirko 
36417aa0045dSCong Wang 	tc_filter_wq = alloc_ordered_workqueue("tc_filter_workqueue", 0);
36427aa0045dSCong Wang 	if (!tc_filter_wq)
36437aa0045dSCong Wang 		return -ENOMEM;
36447aa0045dSCong Wang 
364548617387SJiri Pirko 	err = register_pernet_subsys(&tcf_net_ops);
364648617387SJiri Pirko 	if (err)
364748617387SJiri Pirko 		goto err_register_pernet_subsys;
364848617387SJiri Pirko 
364925a443f7SJohn Hurley 	flow_indr_add_block_cb(&block_entry);
36501150ab0fSwenxu 
3651470502deSVlad Buslov 	rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_new_tfilter, NULL,
3652470502deSVlad Buslov 		      RTNL_FLAG_DOIT_UNLOCKED);
3653470502deSVlad Buslov 	rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_del_tfilter, NULL,
3654470502deSVlad Buslov 		      RTNL_FLAG_DOIT_UNLOCKED);
3655c431f89bSVlad Buslov 	rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_get_tfilter,
3656470502deSVlad Buslov 		      tc_dump_tfilter, RTNL_FLAG_DOIT_UNLOCKED);
365732a4f5ecSJiri Pirko 	rtnl_register(PF_UNSPEC, RTM_NEWCHAIN, tc_ctl_chain, NULL, 0);
365832a4f5ecSJiri Pirko 	rtnl_register(PF_UNSPEC, RTM_DELCHAIN, tc_ctl_chain, NULL, 0);
365932a4f5ecSJiri Pirko 	rtnl_register(PF_UNSPEC, RTM_GETCHAIN, tc_ctl_chain,
366032a4f5ecSJiri Pirko 		      tc_dump_chain, 0);
36611da177e4SLinus Torvalds 
36621da177e4SLinus Torvalds 	return 0;
366348617387SJiri Pirko 
366448617387SJiri Pirko err_register_pernet_subsys:
366548617387SJiri Pirko 	destroy_workqueue(tc_filter_wq);
366648617387SJiri Pirko 	return err;
36671da177e4SLinus Torvalds }
36681da177e4SLinus Torvalds 
36691da177e4SLinus Torvalds subsys_initcall(tc_filter_init);
3670