162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * net/sched/cls_u32.c	Ugly (or Universal) 32bit key Packet Classifier.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *	The filters are packed to hash tables of key nodes
862306a36Sopenharmony_ci *	with a set of 32bit key/mask pairs at every node.
962306a36Sopenharmony_ci *	Nodes reference next level hash tables etc.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *	This scheme is the best universal classifier I managed to
1262306a36Sopenharmony_ci *	invent; it is not super-fast, but it is not slow (provided you
1362306a36Sopenharmony_ci *	program it correctly), and general enough.  And its relative
1462306a36Sopenharmony_ci *	speed grows as the number of rules becomes larger.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci *	It seems that it represents the best middle point between
1762306a36Sopenharmony_ci *	speed and manageability both by human and by machine.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci *	It is especially useful for link sharing combined with QoS;
2062306a36Sopenharmony_ci *	pure RSVP doesn't need such a general approach and can use
2162306a36Sopenharmony_ci *	much simpler (and faster) schemes, sort of cls_rsvp.c.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci *	nfmark match added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro>
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#include <linux/module.h>
2762306a36Sopenharmony_ci#include <linux/slab.h>
2862306a36Sopenharmony_ci#include <linux/types.h>
2962306a36Sopenharmony_ci#include <linux/kernel.h>
3062306a36Sopenharmony_ci#include <linux/string.h>
3162306a36Sopenharmony_ci#include <linux/errno.h>
3262306a36Sopenharmony_ci#include <linux/percpu.h>
3362306a36Sopenharmony_ci#include <linux/rtnetlink.h>
3462306a36Sopenharmony_ci#include <linux/skbuff.h>
3562306a36Sopenharmony_ci#include <linux/bitmap.h>
3662306a36Sopenharmony_ci#include <linux/netdevice.h>
3762306a36Sopenharmony_ci#include <linux/hash.h>
3862306a36Sopenharmony_ci#include <net/netlink.h>
3962306a36Sopenharmony_ci#include <net/act_api.h>
4062306a36Sopenharmony_ci#include <net/pkt_cls.h>
4162306a36Sopenharmony_ci#include <linux/idr.h>
4262306a36Sopenharmony_ci#include <net/tc_wrapper.h>
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistruct tc_u_knode {
4562306a36Sopenharmony_ci	struct tc_u_knode __rcu	*next;
4662306a36Sopenharmony_ci	u32			handle;
4762306a36Sopenharmony_ci	struct tc_u_hnode __rcu	*ht_up;
4862306a36Sopenharmony_ci	struct tcf_exts		exts;
4962306a36Sopenharmony_ci	int			ifindex;
5062306a36Sopenharmony_ci	u8			fshift;
5162306a36Sopenharmony_ci	struct tcf_result	res;
5262306a36Sopenharmony_ci	struct tc_u_hnode __rcu	*ht_down;
5362306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
5462306a36Sopenharmony_ci	struct tc_u32_pcnt __percpu *pf;
5562306a36Sopenharmony_ci#endif
5662306a36Sopenharmony_ci	u32			flags;
5762306a36Sopenharmony_ci	unsigned int		in_hw_count;
5862306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
5962306a36Sopenharmony_ci	u32			val;
6062306a36Sopenharmony_ci	u32			mask;
6162306a36Sopenharmony_ci	u32 __percpu		*pcpu_success;
6262306a36Sopenharmony_ci#endif
6362306a36Sopenharmony_ci	struct rcu_work		rwork;
6462306a36Sopenharmony_ci	/* The 'sel' field MUST be the last field in structure to allow for
6562306a36Sopenharmony_ci	 * tc_u32_keys allocated at end of structure.
6662306a36Sopenharmony_ci	 */
6762306a36Sopenharmony_ci	struct tc_u32_sel	sel;
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct tc_u_hnode {
7162306a36Sopenharmony_ci	struct tc_u_hnode __rcu	*next;
7262306a36Sopenharmony_ci	u32			handle;
7362306a36Sopenharmony_ci	u32			prio;
7462306a36Sopenharmony_ci	int			refcnt;
7562306a36Sopenharmony_ci	unsigned int		divisor;
7662306a36Sopenharmony_ci	struct idr		handle_idr;
7762306a36Sopenharmony_ci	bool			is_root;
7862306a36Sopenharmony_ci	struct rcu_head		rcu;
7962306a36Sopenharmony_ci	u32			flags;
8062306a36Sopenharmony_ci	/* The 'ht' field MUST be the last field in structure to allow for
8162306a36Sopenharmony_ci	 * more entries allocated at end of structure.
8262306a36Sopenharmony_ci	 */
8362306a36Sopenharmony_ci	struct tc_u_knode __rcu	*ht[];
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistruct tc_u_common {
8762306a36Sopenharmony_ci	struct tc_u_hnode __rcu	*hlist;
8862306a36Sopenharmony_ci	void			*ptr;
8962306a36Sopenharmony_ci	int			refcnt;
9062306a36Sopenharmony_ci	struct idr		handle_idr;
9162306a36Sopenharmony_ci	struct hlist_node	hnode;
9262306a36Sopenharmony_ci	long			knodes;
9362306a36Sopenharmony_ci};
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic inline unsigned int u32_hash_fold(__be32 key,
9662306a36Sopenharmony_ci					 const struct tc_u32_sel *sel,
9762306a36Sopenharmony_ci					 u8 fshift)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	unsigned int h = ntohl(key & sel->hmask) >> fshift;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	return h;
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ciTC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
10562306a36Sopenharmony_ci				   const struct tcf_proto *tp,
10662306a36Sopenharmony_ci				   struct tcf_result *res)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct {
10962306a36Sopenharmony_ci		struct tc_u_knode *knode;
11062306a36Sopenharmony_ci		unsigned int	  off;
11162306a36Sopenharmony_ci	} stack[TC_U32_MAXDEPTH];
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	struct tc_u_hnode *ht = rcu_dereference_bh(tp->root);
11462306a36Sopenharmony_ci	unsigned int off = skb_network_offset(skb);
11562306a36Sopenharmony_ci	struct tc_u_knode *n;
11662306a36Sopenharmony_ci	int sdepth = 0;
11762306a36Sopenharmony_ci	int off2 = 0;
11862306a36Sopenharmony_ci	int sel = 0;
11962306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
12062306a36Sopenharmony_ci	int j;
12162306a36Sopenharmony_ci#endif
12262306a36Sopenharmony_ci	int i, r;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cinext_ht:
12562306a36Sopenharmony_ci	n = rcu_dereference_bh(ht->ht[sel]);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cinext_knode:
12862306a36Sopenharmony_ci	if (n) {
12962306a36Sopenharmony_ci		struct tc_u32_key *key = n->sel.keys;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
13262306a36Sopenharmony_ci		__this_cpu_inc(n->pf->rcnt);
13362306a36Sopenharmony_ci		j = 0;
13462306a36Sopenharmony_ci#endif
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		if (tc_skip_sw(n->flags)) {
13762306a36Sopenharmony_ci			n = rcu_dereference_bh(n->next);
13862306a36Sopenharmony_ci			goto next_knode;
13962306a36Sopenharmony_ci		}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
14262306a36Sopenharmony_ci		if ((skb->mark & n->mask) != n->val) {
14362306a36Sopenharmony_ci			n = rcu_dereference_bh(n->next);
14462306a36Sopenharmony_ci			goto next_knode;
14562306a36Sopenharmony_ci		} else {
14662306a36Sopenharmony_ci			__this_cpu_inc(*n->pcpu_success);
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci#endif
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci		for (i = n->sel.nkeys; i > 0; i--, key++) {
15162306a36Sopenharmony_ci			int toff = off + key->off + (off2 & key->offmask);
15262306a36Sopenharmony_ci			__be32 *data, hdata;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci			if (skb_headroom(skb) + toff > INT_MAX)
15562306a36Sopenharmony_ci				goto out;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci			data = skb_header_pointer(skb, toff, 4, &hdata);
15862306a36Sopenharmony_ci			if (!data)
15962306a36Sopenharmony_ci				goto out;
16062306a36Sopenharmony_ci			if ((*data ^ key->val) & key->mask) {
16162306a36Sopenharmony_ci				n = rcu_dereference_bh(n->next);
16262306a36Sopenharmony_ci				goto next_knode;
16362306a36Sopenharmony_ci			}
16462306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
16562306a36Sopenharmony_ci			__this_cpu_inc(n->pf->kcnts[j]);
16662306a36Sopenharmony_ci			j++;
16762306a36Sopenharmony_ci#endif
16862306a36Sopenharmony_ci		}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_down);
17162306a36Sopenharmony_ci		if (!ht) {
17262306a36Sopenharmony_cicheck_terminal:
17362306a36Sopenharmony_ci			if (n->sel.flags & TC_U32_TERMINAL) {
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci				*res = n->res;
17662306a36Sopenharmony_ci				if (!tcf_match_indev(skb, n->ifindex)) {
17762306a36Sopenharmony_ci					n = rcu_dereference_bh(n->next);
17862306a36Sopenharmony_ci					goto next_knode;
17962306a36Sopenharmony_ci				}
18062306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
18162306a36Sopenharmony_ci				__this_cpu_inc(n->pf->rhit);
18262306a36Sopenharmony_ci#endif
18362306a36Sopenharmony_ci				r = tcf_exts_exec(skb, &n->exts, res);
18462306a36Sopenharmony_ci				if (r < 0) {
18562306a36Sopenharmony_ci					n = rcu_dereference_bh(n->next);
18662306a36Sopenharmony_ci					goto next_knode;
18762306a36Sopenharmony_ci				}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci				return r;
19062306a36Sopenharmony_ci			}
19162306a36Sopenharmony_ci			n = rcu_dereference_bh(n->next);
19262306a36Sopenharmony_ci			goto next_knode;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci		/* PUSH */
19662306a36Sopenharmony_ci		if (sdepth >= TC_U32_MAXDEPTH)
19762306a36Sopenharmony_ci			goto deadloop;
19862306a36Sopenharmony_ci		stack[sdepth].knode = n;
19962306a36Sopenharmony_ci		stack[sdepth].off = off;
20062306a36Sopenharmony_ci		sdepth++;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_down);
20362306a36Sopenharmony_ci		sel = 0;
20462306a36Sopenharmony_ci		if (ht->divisor) {
20562306a36Sopenharmony_ci			__be32 *data, hdata;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci			data = skb_header_pointer(skb, off + n->sel.hoff, 4,
20862306a36Sopenharmony_ci						  &hdata);
20962306a36Sopenharmony_ci			if (!data)
21062306a36Sopenharmony_ci				goto out;
21162306a36Sopenharmony_ci			sel = ht->divisor & u32_hash_fold(*data, &n->sel,
21262306a36Sopenharmony_ci							  n->fshift);
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci		if (!(n->sel.flags & (TC_U32_VAROFFSET | TC_U32_OFFSET | TC_U32_EAT)))
21562306a36Sopenharmony_ci			goto next_ht;
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci		if (n->sel.flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
21862306a36Sopenharmony_ci			off2 = n->sel.off + 3;
21962306a36Sopenharmony_ci			if (n->sel.flags & TC_U32_VAROFFSET) {
22062306a36Sopenharmony_ci				__be16 *data, hdata;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci				data = skb_header_pointer(skb,
22362306a36Sopenharmony_ci							  off + n->sel.offoff,
22462306a36Sopenharmony_ci							  2, &hdata);
22562306a36Sopenharmony_ci				if (!data)
22662306a36Sopenharmony_ci					goto out;
22762306a36Sopenharmony_ci				off2 += ntohs(n->sel.offmask & *data) >>
22862306a36Sopenharmony_ci					n->sel.offshift;
22962306a36Sopenharmony_ci			}
23062306a36Sopenharmony_ci			off2 &= ~3;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci		if (n->sel.flags & TC_U32_EAT) {
23362306a36Sopenharmony_ci			off += off2;
23462306a36Sopenharmony_ci			off2 = 0;
23562306a36Sopenharmony_ci		}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		if (off < skb->len)
23862306a36Sopenharmony_ci			goto next_ht;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	/* POP */
24262306a36Sopenharmony_ci	if (sdepth--) {
24362306a36Sopenharmony_ci		n = stack[sdepth].knode;
24462306a36Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_up);
24562306a36Sopenharmony_ci		off = stack[sdepth].off;
24662306a36Sopenharmony_ci		goto check_terminal;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ciout:
24962306a36Sopenharmony_ci	return -1;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cideadloop:
25262306a36Sopenharmony_ci	net_warn_ratelimited("cls_u32: dead loop\n");
25362306a36Sopenharmony_ci	return -1;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic struct tc_u_hnode *u32_lookup_ht(struct tc_u_common *tp_c, u32 handle)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct tc_u_hnode *ht;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
26162306a36Sopenharmony_ci	     ht;
26262306a36Sopenharmony_ci	     ht = rtnl_dereference(ht->next))
26362306a36Sopenharmony_ci		if (ht->handle == handle)
26462306a36Sopenharmony_ci			break;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	return ht;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_cistatic struct tc_u_knode *u32_lookup_key(struct tc_u_hnode *ht, u32 handle)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	unsigned int sel;
27262306a36Sopenharmony_ci	struct tc_u_knode *n = NULL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	sel = TC_U32_HASH(handle);
27562306a36Sopenharmony_ci	if (sel > ht->divisor)
27662306a36Sopenharmony_ci		goto out;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	for (n = rtnl_dereference(ht->ht[sel]);
27962306a36Sopenharmony_ci	     n;
28062306a36Sopenharmony_ci	     n = rtnl_dereference(n->next))
28162306a36Sopenharmony_ci		if (n->handle == handle)
28262306a36Sopenharmony_ci			break;
28362306a36Sopenharmony_ciout:
28462306a36Sopenharmony_ci	return n;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void *u32_get(struct tcf_proto *tp, u32 handle)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct tc_u_hnode *ht;
29162306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (TC_U32_HTID(handle) == TC_U32_ROOT)
29462306a36Sopenharmony_ci		ht = rtnl_dereference(tp->root);
29562306a36Sopenharmony_ci	else
29662306a36Sopenharmony_ci		ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle));
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (!ht)
29962306a36Sopenharmony_ci		return NULL;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	if (TC_U32_KEY(handle) == 0)
30262306a36Sopenharmony_ci		return ht;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	return u32_lookup_key(ht, handle);
30562306a36Sopenharmony_ci}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci/* Protected by rtnl lock */
30862306a36Sopenharmony_cistatic u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
31162306a36Sopenharmony_ci	if (id < 0)
31262306a36Sopenharmony_ci		return 0;
31362306a36Sopenharmony_ci	return (id | 0x800U) << 20;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_cistatic struct hlist_head *tc_u_common_hash;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci#define U32_HASH_SHIFT 10
31962306a36Sopenharmony_ci#define U32_HASH_SIZE (1 << U32_HASH_SHIFT)
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_cistatic void *tc_u_common_ptr(const struct tcf_proto *tp)
32262306a36Sopenharmony_ci{
32362306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	/* The block sharing is currently supported only
32662306a36Sopenharmony_ci	 * for classless qdiscs. In that case we use block
32762306a36Sopenharmony_ci	 * for tc_u_common identification. In case the
32862306a36Sopenharmony_ci	 * block is not shared, block->q is a valid pointer
32962306a36Sopenharmony_ci	 * and we can use that. That works for classful qdiscs.
33062306a36Sopenharmony_ci	 */
33162306a36Sopenharmony_ci	if (tcf_block_shared(block))
33262306a36Sopenharmony_ci		return block;
33362306a36Sopenharmony_ci	else
33462306a36Sopenharmony_ci		return block->q;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic struct hlist_head *tc_u_hash(void *key)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	return tc_u_common_hash + hash_ptr(key, U32_HASH_SHIFT);
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic struct tc_u_common *tc_u_common_find(void *key)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	struct tc_u_common *tc;
34562306a36Sopenharmony_ci	hlist_for_each_entry(tc, tc_u_hash(key), hnode) {
34662306a36Sopenharmony_ci		if (tc->ptr == key)
34762306a36Sopenharmony_ci			return tc;
34862306a36Sopenharmony_ci	}
34962306a36Sopenharmony_ci	return NULL;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int u32_init(struct tcf_proto *tp)
35362306a36Sopenharmony_ci{
35462306a36Sopenharmony_ci	struct tc_u_hnode *root_ht;
35562306a36Sopenharmony_ci	void *key = tc_u_common_ptr(tp);
35662306a36Sopenharmony_ci	struct tc_u_common *tp_c = tc_u_common_find(key);
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	root_ht = kzalloc(struct_size(root_ht, ht, 1), GFP_KERNEL);
35962306a36Sopenharmony_ci	if (root_ht == NULL)
36062306a36Sopenharmony_ci		return -ENOBUFS;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	root_ht->refcnt++;
36362306a36Sopenharmony_ci	root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000;
36462306a36Sopenharmony_ci	root_ht->prio = tp->prio;
36562306a36Sopenharmony_ci	root_ht->is_root = true;
36662306a36Sopenharmony_ci	idr_init(&root_ht->handle_idr);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	if (tp_c == NULL) {
36962306a36Sopenharmony_ci		tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL);
37062306a36Sopenharmony_ci		if (tp_c == NULL) {
37162306a36Sopenharmony_ci			kfree(root_ht);
37262306a36Sopenharmony_ci			return -ENOBUFS;
37362306a36Sopenharmony_ci		}
37462306a36Sopenharmony_ci		tp_c->ptr = key;
37562306a36Sopenharmony_ci		INIT_HLIST_NODE(&tp_c->hnode);
37662306a36Sopenharmony_ci		idr_init(&tp_c->handle_idr);
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci		hlist_add_head(&tp_c->hnode, tc_u_hash(key));
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	tp_c->refcnt++;
38262306a36Sopenharmony_ci	RCU_INIT_POINTER(root_ht->next, tp_c->hlist);
38362306a36Sopenharmony_ci	rcu_assign_pointer(tp_c->hlist, root_ht);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	root_ht->refcnt++;
38662306a36Sopenharmony_ci	rcu_assign_pointer(tp->root, root_ht);
38762306a36Sopenharmony_ci	tp->data = tp_c;
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic void __u32_destroy_key(struct tc_u_knode *n)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	tcf_exts_destroy(&n->exts);
39662306a36Sopenharmony_ci	if (ht && --ht->refcnt == 0)
39762306a36Sopenharmony_ci		kfree(ht);
39862306a36Sopenharmony_ci	kfree(n);
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_cistatic void u32_destroy_key(struct tc_u_knode *n, bool free_pf)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	tcf_exts_put_net(&n->exts);
40462306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
40562306a36Sopenharmony_ci	if (free_pf)
40662306a36Sopenharmony_ci		free_percpu(n->pf);
40762306a36Sopenharmony_ci#endif
40862306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
40962306a36Sopenharmony_ci	if (free_pf)
41062306a36Sopenharmony_ci		free_percpu(n->pcpu_success);
41162306a36Sopenharmony_ci#endif
41262306a36Sopenharmony_ci	__u32_destroy_key(n);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/* u32_delete_key_rcu should be called when free'ing a copied
41662306a36Sopenharmony_ci * version of a tc_u_knode obtained from u32_init_knode(). When
41762306a36Sopenharmony_ci * copies are obtained from u32_init_knode() the statistics are
41862306a36Sopenharmony_ci * shared between the old and new copies to allow readers to
41962306a36Sopenharmony_ci * continue to update the statistics during the copy. To support
42062306a36Sopenharmony_ci * this the u32_delete_key_rcu variant does not free the percpu
42162306a36Sopenharmony_ci * statistics.
42262306a36Sopenharmony_ci */
42362306a36Sopenharmony_cistatic void u32_delete_key_work(struct work_struct *work)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci	struct tc_u_knode *key = container_of(to_rcu_work(work),
42662306a36Sopenharmony_ci					      struct tc_u_knode,
42762306a36Sopenharmony_ci					      rwork);
42862306a36Sopenharmony_ci	rtnl_lock();
42962306a36Sopenharmony_ci	u32_destroy_key(key, false);
43062306a36Sopenharmony_ci	rtnl_unlock();
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci/* u32_delete_key_freepf_rcu is the rcu callback variant
43462306a36Sopenharmony_ci * that free's the entire structure including the statistics
43562306a36Sopenharmony_ci * percpu variables. Only use this if the key is not a copy
43662306a36Sopenharmony_ci * returned by u32_init_knode(). See u32_delete_key_rcu()
43762306a36Sopenharmony_ci * for the variant that should be used with keys return from
43862306a36Sopenharmony_ci * u32_init_knode()
43962306a36Sopenharmony_ci */
44062306a36Sopenharmony_cistatic void u32_delete_key_freepf_work(struct work_struct *work)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct tc_u_knode *key = container_of(to_rcu_work(work),
44362306a36Sopenharmony_ci					      struct tc_u_knode,
44462306a36Sopenharmony_ci					      rwork);
44562306a36Sopenharmony_ci	rtnl_lock();
44662306a36Sopenharmony_ci	u32_destroy_key(key, true);
44762306a36Sopenharmony_ci	rtnl_unlock();
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
45362306a36Sopenharmony_ci	struct tc_u_knode __rcu **kp;
45462306a36Sopenharmony_ci	struct tc_u_knode *pkp;
45562306a36Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(key->ht_up);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (ht) {
45862306a36Sopenharmony_ci		kp = &ht->ht[TC_U32_HASH(key->handle)];
45962306a36Sopenharmony_ci		for (pkp = rtnl_dereference(*kp); pkp;
46062306a36Sopenharmony_ci		     kp = &pkp->next, pkp = rtnl_dereference(*kp)) {
46162306a36Sopenharmony_ci			if (pkp == key) {
46262306a36Sopenharmony_ci				RCU_INIT_POINTER(*kp, key->next);
46362306a36Sopenharmony_ci				tp_c->knodes--;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci				tcf_unbind_filter(tp, &key->res);
46662306a36Sopenharmony_ci				idr_remove(&ht->handle_idr, key->handle);
46762306a36Sopenharmony_ci				tcf_exts_get_net(&key->exts);
46862306a36Sopenharmony_ci				tcf_queue_work(&key->rwork, u32_delete_key_freepf_work);
46962306a36Sopenharmony_ci				return 0;
47062306a36Sopenharmony_ci			}
47162306a36Sopenharmony_ci		}
47262306a36Sopenharmony_ci	}
47362306a36Sopenharmony_ci	WARN_ON(1);
47462306a36Sopenharmony_ci	return 0;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
47862306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
48162306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, h->flags, extack);
48462306a36Sopenharmony_ci	cls_u32.command = TC_CLSU32_DELETE_HNODE;
48562306a36Sopenharmony_ci	cls_u32.hnode.divisor = h->divisor;
48662306a36Sopenharmony_ci	cls_u32.hnode.handle = h->handle;
48762306a36Sopenharmony_ci	cls_u32.hnode.prio = h->prio;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, false, true);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
49362306a36Sopenharmony_ci				u32 flags, struct netlink_ext_ack *extack)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
49662306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
49762306a36Sopenharmony_ci	bool skip_sw = tc_skip_sw(flags);
49862306a36Sopenharmony_ci	bool offloaded = false;
49962306a36Sopenharmony_ci	int err;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
50262306a36Sopenharmony_ci	cls_u32.command = TC_CLSU32_NEW_HNODE;
50362306a36Sopenharmony_ci	cls_u32.hnode.divisor = h->divisor;
50462306a36Sopenharmony_ci	cls_u32.hnode.handle = h->handle;
50562306a36Sopenharmony_ci	cls_u32.hnode.prio = h->prio;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	err = tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, skip_sw, true);
50862306a36Sopenharmony_ci	if (err < 0) {
50962306a36Sopenharmony_ci		u32_clear_hw_hnode(tp, h, NULL);
51062306a36Sopenharmony_ci		return err;
51162306a36Sopenharmony_ci	} else if (err > 0) {
51262306a36Sopenharmony_ci		offloaded = true;
51362306a36Sopenharmony_ci	}
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (skip_sw && !offloaded)
51662306a36Sopenharmony_ci		return -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	return 0;
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
52262306a36Sopenharmony_ci				struct netlink_ext_ack *extack)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
52562306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
52862306a36Sopenharmony_ci	cls_u32.command = TC_CLSU32_DELETE_KNODE;
52962306a36Sopenharmony_ci	cls_u32.knode.handle = n->handle;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	tc_setup_cb_destroy(block, tp, TC_SETUP_CLSU32, &cls_u32, false,
53262306a36Sopenharmony_ci			    &n->flags, &n->in_hw_count, true);
53362306a36Sopenharmony_ci}
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_cistatic int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
53662306a36Sopenharmony_ci				u32 flags, struct netlink_ext_ack *extack)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
53962306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
54062306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
54162306a36Sopenharmony_ci	bool skip_sw = tc_skip_sw(flags);
54262306a36Sopenharmony_ci	int err;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
54562306a36Sopenharmony_ci	cls_u32.command = TC_CLSU32_REPLACE_KNODE;
54662306a36Sopenharmony_ci	cls_u32.knode.handle = n->handle;
54762306a36Sopenharmony_ci	cls_u32.knode.fshift = n->fshift;
54862306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
54962306a36Sopenharmony_ci	cls_u32.knode.val = n->val;
55062306a36Sopenharmony_ci	cls_u32.knode.mask = n->mask;
55162306a36Sopenharmony_ci#else
55262306a36Sopenharmony_ci	cls_u32.knode.val = 0;
55362306a36Sopenharmony_ci	cls_u32.knode.mask = 0;
55462306a36Sopenharmony_ci#endif
55562306a36Sopenharmony_ci	cls_u32.knode.sel = &n->sel;
55662306a36Sopenharmony_ci	cls_u32.knode.res = &n->res;
55762306a36Sopenharmony_ci	cls_u32.knode.exts = &n->exts;
55862306a36Sopenharmony_ci	if (n->ht_down)
55962306a36Sopenharmony_ci		cls_u32.knode.link_handle = ht->handle;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	err = tc_setup_cb_add(block, tp, TC_SETUP_CLSU32, &cls_u32, skip_sw,
56262306a36Sopenharmony_ci			      &n->flags, &n->in_hw_count, true);
56362306a36Sopenharmony_ci	if (err) {
56462306a36Sopenharmony_ci		u32_remove_hw_knode(tp, n, NULL);
56562306a36Sopenharmony_ci		return err;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (skip_sw && !(n->flags & TCA_CLS_FLAGS_IN_HW))
56962306a36Sopenharmony_ci		return -EINVAL;
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return 0;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
57562306a36Sopenharmony_ci			    struct netlink_ext_ack *extack)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
57862306a36Sopenharmony_ci	struct tc_u_knode *n;
57962306a36Sopenharmony_ci	unsigned int h;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	for (h = 0; h <= ht->divisor; h++) {
58262306a36Sopenharmony_ci		while ((n = rtnl_dereference(ht->ht[h])) != NULL) {
58362306a36Sopenharmony_ci			RCU_INIT_POINTER(ht->ht[h],
58462306a36Sopenharmony_ci					 rtnl_dereference(n->next));
58562306a36Sopenharmony_ci			tp_c->knodes--;
58662306a36Sopenharmony_ci			tcf_unbind_filter(tp, &n->res);
58762306a36Sopenharmony_ci			u32_remove_hw_knode(tp, n, extack);
58862306a36Sopenharmony_ci			idr_remove(&ht->handle_idr, n->handle);
58962306a36Sopenharmony_ci			if (tcf_exts_get_net(&n->exts))
59062306a36Sopenharmony_ci				tcf_queue_work(&n->rwork, u32_delete_key_freepf_work);
59162306a36Sopenharmony_ci			else
59262306a36Sopenharmony_ci				u32_destroy_key(n, true);
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci	}
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
59862306a36Sopenharmony_ci			     struct netlink_ext_ack *extack)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
60162306a36Sopenharmony_ci	struct tc_u_hnode __rcu **hn;
60262306a36Sopenharmony_ci	struct tc_u_hnode *phn;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	WARN_ON(--ht->refcnt);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	u32_clear_hnode(tp, ht, extack);
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci	hn = &tp_c->hlist;
60962306a36Sopenharmony_ci	for (phn = rtnl_dereference(*hn);
61062306a36Sopenharmony_ci	     phn;
61162306a36Sopenharmony_ci	     hn = &phn->next, phn = rtnl_dereference(*hn)) {
61262306a36Sopenharmony_ci		if (phn == ht) {
61362306a36Sopenharmony_ci			u32_clear_hw_hnode(tp, ht, extack);
61462306a36Sopenharmony_ci			idr_destroy(&ht->handle_idr);
61562306a36Sopenharmony_ci			idr_remove(&tp_c->handle_idr, ht->handle);
61662306a36Sopenharmony_ci			RCU_INIT_POINTER(*hn, ht->next);
61762306a36Sopenharmony_ci			kfree_rcu(ht, rcu);
61862306a36Sopenharmony_ci			return 0;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	return -ENOENT;
62362306a36Sopenharmony_ci}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_cistatic void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
62662306a36Sopenharmony_ci			struct netlink_ext_ack *extack)
62762306a36Sopenharmony_ci{
62862306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
62962306a36Sopenharmony_ci	struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	WARN_ON(root_ht == NULL);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (root_ht && --root_ht->refcnt == 1)
63462306a36Sopenharmony_ci		u32_destroy_hnode(tp, root_ht, extack);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	if (--tp_c->refcnt == 0) {
63762306a36Sopenharmony_ci		struct tc_u_hnode *ht;
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci		hlist_del(&tp_c->hnode);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci		while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) {
64262306a36Sopenharmony_ci			u32_clear_hnode(tp, ht, extack);
64362306a36Sopenharmony_ci			RCU_INIT_POINTER(tp_c->hlist, ht->next);
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci			/* u32_destroy_key() will later free ht for us, if it's
64662306a36Sopenharmony_ci			 * still referenced by some knode
64762306a36Sopenharmony_ci			 */
64862306a36Sopenharmony_ci			if (--ht->refcnt == 0)
64962306a36Sopenharmony_ci				kfree_rcu(ht, rcu);
65062306a36Sopenharmony_ci		}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci		idr_destroy(&tp_c->handle_idr);
65362306a36Sopenharmony_ci		kfree(tp_c);
65462306a36Sopenharmony_ci	}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	tp->data = NULL;
65762306a36Sopenharmony_ci}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_cistatic int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
66062306a36Sopenharmony_ci		      bool rtnl_held, struct netlink_ext_ack *extack)
66162306a36Sopenharmony_ci{
66262306a36Sopenharmony_ci	struct tc_u_hnode *ht = arg;
66362306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
66462306a36Sopenharmony_ci	int ret = 0;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	if (TC_U32_KEY(ht->handle)) {
66762306a36Sopenharmony_ci		u32_remove_hw_knode(tp, (struct tc_u_knode *)ht, extack);
66862306a36Sopenharmony_ci		ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
66962306a36Sopenharmony_ci		goto out;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if (ht->is_root) {
67362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Not allowed to delete root node");
67462306a36Sopenharmony_ci		return -EINVAL;
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (ht->refcnt == 1) {
67862306a36Sopenharmony_ci		u32_destroy_hnode(tp, ht, extack);
67962306a36Sopenharmony_ci	} else {
68062306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not delete in-use filter");
68162306a36Sopenharmony_ci		return -EBUSY;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ciout:
68562306a36Sopenharmony_ci	*last = tp_c->refcnt == 1 && tp_c->knodes == 0;
68662306a36Sopenharmony_ci	return ret;
68762306a36Sopenharmony_ci}
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
69062306a36Sopenharmony_ci{
69162306a36Sopenharmony_ci	u32 index = htid | 0x800;
69262306a36Sopenharmony_ci	u32 max = htid | 0xFFF;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
69562306a36Sopenharmony_ci		index = htid + 1;
69662306a36Sopenharmony_ci		if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
69762306a36Sopenharmony_ci				 GFP_KERNEL))
69862306a36Sopenharmony_ci			index = max;
69962306a36Sopenharmony_ci	}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci	return index;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
70562306a36Sopenharmony_ci	[TCA_U32_CLASSID]	= { .type = NLA_U32 },
70662306a36Sopenharmony_ci	[TCA_U32_HASH]		= { .type = NLA_U32 },
70762306a36Sopenharmony_ci	[TCA_U32_LINK]		= { .type = NLA_U32 },
70862306a36Sopenharmony_ci	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
70962306a36Sopenharmony_ci	[TCA_U32_SEL]		= { .len = sizeof(struct tc_u32_sel) },
71062306a36Sopenharmony_ci	[TCA_U32_INDEV]		= { .type = NLA_STRING, .len = IFNAMSIZ },
71162306a36Sopenharmony_ci	[TCA_U32_MARK]		= { .len = sizeof(struct tc_u32_mark) },
71262306a36Sopenharmony_ci	[TCA_U32_FLAGS]		= { .type = NLA_U32 },
71362306a36Sopenharmony_ci};
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void u32_unbind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
71662306a36Sopenharmony_ci			      struct nlattr **tb)
71762306a36Sopenharmony_ci{
71862306a36Sopenharmony_ci	if (tb[TCA_U32_CLASSID])
71962306a36Sopenharmony_ci		tcf_unbind_filter(tp, &n->res);
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic void u32_bind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
72362306a36Sopenharmony_ci			    unsigned long base, struct nlattr **tb)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	if (tb[TCA_U32_CLASSID]) {
72662306a36Sopenharmony_ci		n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
72762306a36Sopenharmony_ci		tcf_bind_filter(tp, &n->res, base);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci}
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_cistatic int u32_set_parms(struct net *net, struct tcf_proto *tp,
73262306a36Sopenharmony_ci			 struct tc_u_knode *n, struct nlattr **tb,
73362306a36Sopenharmony_ci			 struct nlattr *est, u32 flags, u32 fl_flags,
73462306a36Sopenharmony_ci			 struct netlink_ext_ack *extack)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	int err, ifindex = -1;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	err = tcf_exts_validate_ex(net, tp, tb, est, &n->exts, flags,
73962306a36Sopenharmony_ci				   fl_flags, extack);
74062306a36Sopenharmony_ci	if (err < 0)
74162306a36Sopenharmony_ci		return err;
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (tb[TCA_U32_INDEV]) {
74462306a36Sopenharmony_ci		ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
74562306a36Sopenharmony_ci		if (ifindex < 0)
74662306a36Sopenharmony_ci			return -EINVAL;
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	if (tb[TCA_U32_LINK]) {
75062306a36Sopenharmony_ci		u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
75162306a36Sopenharmony_ci		struct tc_u_hnode *ht_down = NULL, *ht_old;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci		if (TC_U32_KEY(handle)) {
75462306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "u32 Link handle must be a hash table");
75562306a36Sopenharmony_ci			return -EINVAL;
75662306a36Sopenharmony_ci		}
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci		if (handle) {
75962306a36Sopenharmony_ci			ht_down = u32_lookup_ht(tp->data, handle);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci			if (!ht_down) {
76262306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Link hash table not found");
76362306a36Sopenharmony_ci				return -EINVAL;
76462306a36Sopenharmony_ci			}
76562306a36Sopenharmony_ci			if (ht_down->is_root) {
76662306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Not linking to root node");
76762306a36Sopenharmony_ci				return -EINVAL;
76862306a36Sopenharmony_ci			}
76962306a36Sopenharmony_ci			ht_down->refcnt++;
77062306a36Sopenharmony_ci		}
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci		ht_old = rtnl_dereference(n->ht_down);
77362306a36Sopenharmony_ci		rcu_assign_pointer(n->ht_down, ht_down);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci		if (ht_old)
77662306a36Sopenharmony_ci			ht_old->refcnt--;
77762306a36Sopenharmony_ci	}
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci	if (ifindex >= 0)
78062306a36Sopenharmony_ci		n->ifindex = ifindex;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	return 0;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
78662306a36Sopenharmony_ci			      struct tc_u_knode *n)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	struct tc_u_knode __rcu **ins;
78962306a36Sopenharmony_ci	struct tc_u_knode *pins;
79062306a36Sopenharmony_ci	struct tc_u_hnode *ht;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	if (TC_U32_HTID(n->handle) == TC_U32_ROOT)
79362306a36Sopenharmony_ci		ht = rtnl_dereference(tp->root);
79462306a36Sopenharmony_ci	else
79562306a36Sopenharmony_ci		ht = u32_lookup_ht(tp_c, TC_U32_HTID(n->handle));
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	ins = &ht->ht[TC_U32_HASH(n->handle)];
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	/* The node must always exist for it to be replaced if this is not the
80062306a36Sopenharmony_ci	 * case then something went very wrong elsewhere.
80162306a36Sopenharmony_ci	 */
80262306a36Sopenharmony_ci	for (pins = rtnl_dereference(*ins); ;
80362306a36Sopenharmony_ci	     ins = &pins->next, pins = rtnl_dereference(*ins))
80462306a36Sopenharmony_ci		if (pins->handle == n->handle)
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	idr_replace(&ht->handle_idr, n, n->handle);
80862306a36Sopenharmony_ci	RCU_INIT_POINTER(n->next, pins->next);
80962306a36Sopenharmony_ci	rcu_assign_pointer(*ins, n);
81062306a36Sopenharmony_ci}
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_cistatic struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp,
81362306a36Sopenharmony_ci					 struct tc_u_knode *n)
81462306a36Sopenharmony_ci{
81562306a36Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
81662306a36Sopenharmony_ci	struct tc_u32_sel *s = &n->sel;
81762306a36Sopenharmony_ci	struct tc_u_knode *new;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	new = kzalloc(struct_size(new, sel.keys, s->nkeys), GFP_KERNEL);
82062306a36Sopenharmony_ci	if (!new)
82162306a36Sopenharmony_ci		return NULL;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	RCU_INIT_POINTER(new->next, n->next);
82462306a36Sopenharmony_ci	new->handle = n->handle;
82562306a36Sopenharmony_ci	RCU_INIT_POINTER(new->ht_up, n->ht_up);
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	new->ifindex = n->ifindex;
82862306a36Sopenharmony_ci	new->fshift = n->fshift;
82962306a36Sopenharmony_ci	new->flags = n->flags;
83062306a36Sopenharmony_ci	RCU_INIT_POINTER(new->ht_down, ht);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
83362306a36Sopenharmony_ci	/* Statistics may be incremented by readers during update
83462306a36Sopenharmony_ci	 * so we must keep them in tact. When the node is later destroyed
83562306a36Sopenharmony_ci	 * a special destroy call must be made to not free the pf memory.
83662306a36Sopenharmony_ci	 */
83762306a36Sopenharmony_ci	new->pf = n->pf;
83862306a36Sopenharmony_ci#endif
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
84162306a36Sopenharmony_ci	new->val = n->val;
84262306a36Sopenharmony_ci	new->mask = n->mask;
84362306a36Sopenharmony_ci	/* Similarly success statistics must be moved as pointers */
84462306a36Sopenharmony_ci	new->pcpu_success = n->pcpu_success;
84562306a36Sopenharmony_ci#endif
84662306a36Sopenharmony_ci	memcpy(&new->sel, s, struct_size(s, keys, s->nkeys));
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (tcf_exts_init(&new->exts, net, TCA_U32_ACT, TCA_U32_POLICE)) {
84962306a36Sopenharmony_ci		kfree(new);
85062306a36Sopenharmony_ci		return NULL;
85162306a36Sopenharmony_ci	}
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	/* bump reference count as long as we hold pointer to structure */
85462306a36Sopenharmony_ci	if (ht)
85562306a36Sopenharmony_ci		ht->refcnt++;
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	return new;
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic int u32_change(struct net *net, struct sk_buff *in_skb,
86162306a36Sopenharmony_ci		      struct tcf_proto *tp, unsigned long base, u32 handle,
86262306a36Sopenharmony_ci		      struct nlattr **tca, void **arg, u32 flags,
86362306a36Sopenharmony_ci		      struct netlink_ext_ack *extack)
86462306a36Sopenharmony_ci{
86562306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
86662306a36Sopenharmony_ci	struct tc_u_hnode *ht;
86762306a36Sopenharmony_ci	struct tc_u_knode *n;
86862306a36Sopenharmony_ci	struct tc_u32_sel *s;
86962306a36Sopenharmony_ci	struct nlattr *opt = tca[TCA_OPTIONS];
87062306a36Sopenharmony_ci	struct nlattr *tb[TCA_U32_MAX + 1];
87162306a36Sopenharmony_ci	u32 htid, userflags = 0;
87262306a36Sopenharmony_ci	size_t sel_size;
87362306a36Sopenharmony_ci	int err;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!opt) {
87662306a36Sopenharmony_ci		if (handle) {
87762306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Filter handle requires options");
87862306a36Sopenharmony_ci			return -EINVAL;
87962306a36Sopenharmony_ci		} else {
88062306a36Sopenharmony_ci			return 0;
88162306a36Sopenharmony_ci		}
88262306a36Sopenharmony_ci	}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_U32_MAX, opt, u32_policy,
88562306a36Sopenharmony_ci					  extack);
88662306a36Sopenharmony_ci	if (err < 0)
88762306a36Sopenharmony_ci		return err;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	if (tb[TCA_U32_FLAGS]) {
89062306a36Sopenharmony_ci		userflags = nla_get_u32(tb[TCA_U32_FLAGS]);
89162306a36Sopenharmony_ci		if (!tc_flags_valid(userflags)) {
89262306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid filter flags");
89362306a36Sopenharmony_ci			return -EINVAL;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	n = *arg;
89862306a36Sopenharmony_ci	if (n) {
89962306a36Sopenharmony_ci		struct tc_u_knode *new;
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci		if (TC_U32_KEY(n->handle) == 0) {
90262306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Key node id cannot be zero");
90362306a36Sopenharmony_ci			return -EINVAL;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci		if ((n->flags ^ userflags) &
90762306a36Sopenharmony_ci		    ~(TCA_CLS_FLAGS_IN_HW | TCA_CLS_FLAGS_NOT_IN_HW)) {
90862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Key node flags do not match passed flags");
90962306a36Sopenharmony_ci			return -EINVAL;
91062306a36Sopenharmony_ci		}
91162306a36Sopenharmony_ci
91262306a36Sopenharmony_ci		new = u32_init_knode(net, tp, n);
91362306a36Sopenharmony_ci		if (!new)
91462306a36Sopenharmony_ci			return -ENOMEM;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci		err = u32_set_parms(net, tp, new, tb, tca[TCA_RATE],
91762306a36Sopenharmony_ci				    flags, new->flags, extack);
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (err) {
92062306a36Sopenharmony_ci			__u32_destroy_key(new);
92162306a36Sopenharmony_ci			return err;
92262306a36Sopenharmony_ci		}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		u32_bind_filter(tp, new, base, tb);
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci		err = u32_replace_hw_knode(tp, new, flags, extack);
92762306a36Sopenharmony_ci		if (err) {
92862306a36Sopenharmony_ci			u32_unbind_filter(tp, new, tb);
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci			if (tb[TCA_U32_LINK]) {
93162306a36Sopenharmony_ci				struct tc_u_hnode *ht_old;
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci				ht_old = rtnl_dereference(n->ht_down);
93462306a36Sopenharmony_ci				if (ht_old)
93562306a36Sopenharmony_ci					ht_old->refcnt++;
93662306a36Sopenharmony_ci			}
93762306a36Sopenharmony_ci			__u32_destroy_key(new);
93862306a36Sopenharmony_ci			return err;
93962306a36Sopenharmony_ci		}
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci		if (!tc_in_hw(new->flags))
94262306a36Sopenharmony_ci			new->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci		u32_replace_knode(tp, tp_c, new);
94562306a36Sopenharmony_ci		tcf_unbind_filter(tp, &n->res);
94662306a36Sopenharmony_ci		tcf_exts_get_net(&n->exts);
94762306a36Sopenharmony_ci		tcf_queue_work(&n->rwork, u32_delete_key_work);
94862306a36Sopenharmony_ci		return 0;
94962306a36Sopenharmony_ci	}
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if (tb[TCA_U32_DIVISOR]) {
95262306a36Sopenharmony_ci		unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci		if (!is_power_of_2(divisor)) {
95562306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Divisor is not a power of 2");
95662306a36Sopenharmony_ci			return -EINVAL;
95762306a36Sopenharmony_ci		}
95862306a36Sopenharmony_ci		if (divisor-- > 0x100) {
95962306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Exceeded maximum 256 hash buckets");
96062306a36Sopenharmony_ci			return -EINVAL;
96162306a36Sopenharmony_ci		}
96262306a36Sopenharmony_ci		if (TC_U32_KEY(handle)) {
96362306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Divisor can only be used on a hash table");
96462306a36Sopenharmony_ci			return -EINVAL;
96562306a36Sopenharmony_ci		}
96662306a36Sopenharmony_ci		ht = kzalloc(struct_size(ht, ht, divisor + 1), GFP_KERNEL);
96762306a36Sopenharmony_ci		if (ht == NULL)
96862306a36Sopenharmony_ci			return -ENOBUFS;
96962306a36Sopenharmony_ci		if (handle == 0) {
97062306a36Sopenharmony_ci			handle = gen_new_htid(tp->data, ht);
97162306a36Sopenharmony_ci			if (handle == 0) {
97262306a36Sopenharmony_ci				kfree(ht);
97362306a36Sopenharmony_ci				return -ENOMEM;
97462306a36Sopenharmony_ci			}
97562306a36Sopenharmony_ci		} else {
97662306a36Sopenharmony_ci			err = idr_alloc_u32(&tp_c->handle_idr, ht, &handle,
97762306a36Sopenharmony_ci					    handle, GFP_KERNEL);
97862306a36Sopenharmony_ci			if (err) {
97962306a36Sopenharmony_ci				kfree(ht);
98062306a36Sopenharmony_ci				return err;
98162306a36Sopenharmony_ci			}
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci		ht->refcnt = 1;
98462306a36Sopenharmony_ci		ht->divisor = divisor;
98562306a36Sopenharmony_ci		ht->handle = handle;
98662306a36Sopenharmony_ci		ht->prio = tp->prio;
98762306a36Sopenharmony_ci		idr_init(&ht->handle_idr);
98862306a36Sopenharmony_ci		ht->flags = userflags;
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci		err = u32_replace_hw_hnode(tp, ht, userflags, extack);
99162306a36Sopenharmony_ci		if (err) {
99262306a36Sopenharmony_ci			idr_remove(&tp_c->handle_idr, handle);
99362306a36Sopenharmony_ci			kfree(ht);
99462306a36Sopenharmony_ci			return err;
99562306a36Sopenharmony_ci		}
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci		RCU_INIT_POINTER(ht->next, tp_c->hlist);
99862306a36Sopenharmony_ci		rcu_assign_pointer(tp_c->hlist, ht);
99962306a36Sopenharmony_ci		*arg = ht;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		return 0;
100262306a36Sopenharmony_ci	}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci	if (tb[TCA_U32_HASH]) {
100562306a36Sopenharmony_ci		htid = nla_get_u32(tb[TCA_U32_HASH]);
100662306a36Sopenharmony_ci		if (TC_U32_HTID(htid) == TC_U32_ROOT) {
100762306a36Sopenharmony_ci			ht = rtnl_dereference(tp->root);
100862306a36Sopenharmony_ci			htid = ht->handle;
100962306a36Sopenharmony_ci		} else {
101062306a36Sopenharmony_ci			ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid));
101162306a36Sopenharmony_ci			if (!ht) {
101262306a36Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Specified hash table not found");
101362306a36Sopenharmony_ci				return -EINVAL;
101462306a36Sopenharmony_ci			}
101562306a36Sopenharmony_ci		}
101662306a36Sopenharmony_ci	} else {
101762306a36Sopenharmony_ci		ht = rtnl_dereference(tp->root);
101862306a36Sopenharmony_ci		htid = ht->handle;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	if (ht->divisor < TC_U32_HASH(htid)) {
102262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Specified hash table buckets exceed configured value");
102362306a36Sopenharmony_ci		return -EINVAL;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	/* At this point, we need to derive the new handle that will be used to
102762306a36Sopenharmony_ci	 * uniquely map the identity of this table match entry. The
102862306a36Sopenharmony_ci	 * identity of the entry that we need to construct is 32 bits made of:
102962306a36Sopenharmony_ci	 *     htid(12b):bucketid(8b):node/entryid(12b)
103062306a36Sopenharmony_ci	 *
103162306a36Sopenharmony_ci	 * At this point _we have the table(ht)_ in which we will insert this
103262306a36Sopenharmony_ci	 * entry. We carry the table's id in variable "htid".
103362306a36Sopenharmony_ci	 * Note that earlier code picked the ht selection either by a) the user
103462306a36Sopenharmony_ci	 * providing the htid specified via TCA_U32_HASH attribute or b) when
103562306a36Sopenharmony_ci	 * no such attribute is passed then the root ht, is default to at ID
103662306a36Sopenharmony_ci	 * 0x[800][00][000]. Rule: the root table has a single bucket with ID 0.
103762306a36Sopenharmony_ci	 * If OTOH the user passed us the htid, they may also pass a bucketid of
103862306a36Sopenharmony_ci	 * choice. 0 is fine. For example a user htid is 0x[600][01][000] it is
103962306a36Sopenharmony_ci	 * indicating hash bucketid of 1. Rule: the entry/node ID _cannot_ be
104062306a36Sopenharmony_ci	 * passed via the htid, so even if it was non-zero it will be ignored.
104162306a36Sopenharmony_ci	 *
104262306a36Sopenharmony_ci	 * We may also have a handle, if the user passed one. The handle also
104362306a36Sopenharmony_ci	 * carries the same addressing of htid(12b):bucketid(8b):node/entryid(12b).
104462306a36Sopenharmony_ci	 * Rule: the bucketid on the handle is ignored even if one was passed;
104562306a36Sopenharmony_ci	 * rather the value on "htid" is always assumed to be the bucketid.
104662306a36Sopenharmony_ci	 */
104762306a36Sopenharmony_ci	if (handle) {
104862306a36Sopenharmony_ci		/* Rule: The htid from handle and tableid from htid must match */
104962306a36Sopenharmony_ci		if (TC_U32_HTID(handle) && TC_U32_HTID(handle ^ htid)) {
105062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Handle specified hash table address mismatch");
105162306a36Sopenharmony_ci			return -EINVAL;
105262306a36Sopenharmony_ci		}
105362306a36Sopenharmony_ci		/* Ok, so far we have a valid htid(12b):bucketid(8b) but we
105462306a36Sopenharmony_ci		 * need to finalize the table entry identification with the last
105562306a36Sopenharmony_ci		 * part - the node/entryid(12b)). Rule: Nodeid _cannot be 0_ for
105662306a36Sopenharmony_ci		 * entries. Rule: nodeid of 0 is reserved only for tables(see
105762306a36Sopenharmony_ci		 * earlier code which processes TC_U32_DIVISOR attribute).
105862306a36Sopenharmony_ci		 * Rule: The nodeid can only be derived from the handle (and not
105962306a36Sopenharmony_ci		 * htid).
106062306a36Sopenharmony_ci		 * Rule: if the handle specified zero for the node id example
106162306a36Sopenharmony_ci		 * 0x60000000, then pick a new nodeid from the pool of IDs
106262306a36Sopenharmony_ci		 * this hash table has been allocating from.
106362306a36Sopenharmony_ci		 * If OTOH it is specified (i.e for example the user passed a
106462306a36Sopenharmony_ci		 * handle such as 0x60000123), then we use it generate our final
106562306a36Sopenharmony_ci		 * handle which is used to uniquely identify the match entry.
106662306a36Sopenharmony_ci		 */
106762306a36Sopenharmony_ci		if (!TC_U32_NODE(handle)) {
106862306a36Sopenharmony_ci			handle = gen_new_kid(ht, htid);
106962306a36Sopenharmony_ci		} else {
107062306a36Sopenharmony_ci			handle = htid | TC_U32_NODE(handle);
107162306a36Sopenharmony_ci			err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
107262306a36Sopenharmony_ci					    handle, GFP_KERNEL);
107362306a36Sopenharmony_ci			if (err)
107462306a36Sopenharmony_ci				return err;
107562306a36Sopenharmony_ci		}
107662306a36Sopenharmony_ci	} else {
107762306a36Sopenharmony_ci		/* The user did not give us a handle; lets just generate one
107862306a36Sopenharmony_ci		 * from the table's pool of nodeids.
107962306a36Sopenharmony_ci		 */
108062306a36Sopenharmony_ci		handle = gen_new_kid(ht, htid);
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	if (tb[TCA_U32_SEL] == NULL) {
108462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Selector not specified");
108562306a36Sopenharmony_ci		err = -EINVAL;
108662306a36Sopenharmony_ci		goto erridr;
108762306a36Sopenharmony_ci	}
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	s = nla_data(tb[TCA_U32_SEL]);
109062306a36Sopenharmony_ci	sel_size = struct_size(s, keys, s->nkeys);
109162306a36Sopenharmony_ci	if (nla_len(tb[TCA_U32_SEL]) < sel_size) {
109262306a36Sopenharmony_ci		err = -EINVAL;
109362306a36Sopenharmony_ci		goto erridr;
109462306a36Sopenharmony_ci	}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	n = kzalloc(struct_size(n, sel.keys, s->nkeys), GFP_KERNEL);
109762306a36Sopenharmony_ci	if (n == NULL) {
109862306a36Sopenharmony_ci		err = -ENOBUFS;
109962306a36Sopenharmony_ci		goto erridr;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
110362306a36Sopenharmony_ci	n->pf = __alloc_percpu(struct_size(n->pf, kcnts, s->nkeys),
110462306a36Sopenharmony_ci			       __alignof__(struct tc_u32_pcnt));
110562306a36Sopenharmony_ci	if (!n->pf) {
110662306a36Sopenharmony_ci		err = -ENOBUFS;
110762306a36Sopenharmony_ci		goto errfree;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci#endif
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci	unsafe_memcpy(&n->sel, s, sel_size,
111262306a36Sopenharmony_ci		      /* A composite flex-array structure destination,
111362306a36Sopenharmony_ci		       * which was correctly sized with struct_size(),
111462306a36Sopenharmony_ci		       * bounds-checked against nla_len(), and allocated
111562306a36Sopenharmony_ci		       * above. */);
111662306a36Sopenharmony_ci	RCU_INIT_POINTER(n->ht_up, ht);
111762306a36Sopenharmony_ci	n->handle = handle;
111862306a36Sopenharmony_ci	n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
111962306a36Sopenharmony_ci	n->flags = userflags;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	err = tcf_exts_init(&n->exts, net, TCA_U32_ACT, TCA_U32_POLICE);
112262306a36Sopenharmony_ci	if (err < 0)
112362306a36Sopenharmony_ci		goto errout;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
112662306a36Sopenharmony_ci	n->pcpu_success = alloc_percpu(u32);
112762306a36Sopenharmony_ci	if (!n->pcpu_success) {
112862306a36Sopenharmony_ci		err = -ENOMEM;
112962306a36Sopenharmony_ci		goto errout;
113062306a36Sopenharmony_ci	}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (tb[TCA_U32_MARK]) {
113362306a36Sopenharmony_ci		struct tc_u32_mark *mark;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci		mark = nla_data(tb[TCA_U32_MARK]);
113662306a36Sopenharmony_ci		n->val = mark->val;
113762306a36Sopenharmony_ci		n->mask = mark->mask;
113862306a36Sopenharmony_ci	}
113962306a36Sopenharmony_ci#endif
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci	err = u32_set_parms(net, tp, n, tb, tca[TCA_RATE],
114262306a36Sopenharmony_ci			    flags, n->flags, extack);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	u32_bind_filter(tp, n, base, tb);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci	if (err == 0) {
114762306a36Sopenharmony_ci		struct tc_u_knode __rcu **ins;
114862306a36Sopenharmony_ci		struct tc_u_knode *pins;
114962306a36Sopenharmony_ci
115062306a36Sopenharmony_ci		err = u32_replace_hw_knode(tp, n, flags, extack);
115162306a36Sopenharmony_ci		if (err)
115262306a36Sopenharmony_ci			goto errunbind;
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci		if (!tc_in_hw(n->flags))
115562306a36Sopenharmony_ci			n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci		ins = &ht->ht[TC_U32_HASH(handle)];
115862306a36Sopenharmony_ci		for (pins = rtnl_dereference(*ins); pins;
115962306a36Sopenharmony_ci		     ins = &pins->next, pins = rtnl_dereference(*ins))
116062306a36Sopenharmony_ci			if (TC_U32_NODE(handle) < TC_U32_NODE(pins->handle))
116162306a36Sopenharmony_ci				break;
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci		RCU_INIT_POINTER(n->next, pins);
116462306a36Sopenharmony_ci		rcu_assign_pointer(*ins, n);
116562306a36Sopenharmony_ci		tp_c->knodes++;
116662306a36Sopenharmony_ci		*arg = n;
116762306a36Sopenharmony_ci		return 0;
116862306a36Sopenharmony_ci	}
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_cierrunbind:
117162306a36Sopenharmony_ci	u32_unbind_filter(tp, n, tb);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
117462306a36Sopenharmony_ci	free_percpu(n->pcpu_success);
117562306a36Sopenharmony_ci#endif
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_cierrout:
117862306a36Sopenharmony_ci	tcf_exts_destroy(&n->exts);
117962306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
118062306a36Sopenharmony_cierrfree:
118162306a36Sopenharmony_ci	free_percpu(n->pf);
118262306a36Sopenharmony_ci#endif
118362306a36Sopenharmony_ci	kfree(n);
118462306a36Sopenharmony_cierridr:
118562306a36Sopenharmony_ci	idr_remove(&ht->handle_idr, handle);
118662306a36Sopenharmony_ci	return err;
118762306a36Sopenharmony_ci}
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg,
119062306a36Sopenharmony_ci		     bool rtnl_held)
119162306a36Sopenharmony_ci{
119262306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
119362306a36Sopenharmony_ci	struct tc_u_hnode *ht;
119462306a36Sopenharmony_ci	struct tc_u_knode *n;
119562306a36Sopenharmony_ci	unsigned int h;
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	if (arg->stop)
119862306a36Sopenharmony_ci		return;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
120162306a36Sopenharmony_ci	     ht;
120262306a36Sopenharmony_ci	     ht = rtnl_dereference(ht->next)) {
120362306a36Sopenharmony_ci		if (ht->prio != tp->prio)
120462306a36Sopenharmony_ci			continue;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci		if (!tc_cls_stats_dump(tp, arg, ht))
120762306a36Sopenharmony_ci			return;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci		for (h = 0; h <= ht->divisor; h++) {
121062306a36Sopenharmony_ci			for (n = rtnl_dereference(ht->ht[h]);
121162306a36Sopenharmony_ci			     n;
121262306a36Sopenharmony_ci			     n = rtnl_dereference(n->next)) {
121362306a36Sopenharmony_ci				if (!tc_cls_stats_dump(tp, arg, n))
121462306a36Sopenharmony_ci					return;
121562306a36Sopenharmony_ci			}
121662306a36Sopenharmony_ci		}
121762306a36Sopenharmony_ci	}
121862306a36Sopenharmony_ci}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_cistatic int u32_reoffload_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
122162306a36Sopenharmony_ci			       bool add, flow_setup_cb_t *cb, void *cb_priv,
122262306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
122362306a36Sopenharmony_ci{
122462306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
122562306a36Sopenharmony_ci	int err;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, ht->flags, extack);
122862306a36Sopenharmony_ci	cls_u32.command = add ? TC_CLSU32_NEW_HNODE : TC_CLSU32_DELETE_HNODE;
122962306a36Sopenharmony_ci	cls_u32.hnode.divisor = ht->divisor;
123062306a36Sopenharmony_ci	cls_u32.hnode.handle = ht->handle;
123162306a36Sopenharmony_ci	cls_u32.hnode.prio = ht->prio;
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	err = cb(TC_SETUP_CLSU32, &cls_u32, cb_priv);
123462306a36Sopenharmony_ci	if (err && add && tc_skip_sw(ht->flags))
123562306a36Sopenharmony_ci		return err;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	return 0;
123862306a36Sopenharmony_ci}
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_cistatic int u32_reoffload_knode(struct tcf_proto *tp, struct tc_u_knode *n,
124162306a36Sopenharmony_ci			       bool add, flow_setup_cb_t *cb, void *cb_priv,
124262306a36Sopenharmony_ci			       struct netlink_ext_ack *extack)
124362306a36Sopenharmony_ci{
124462306a36Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
124562306a36Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
124662306a36Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
124962306a36Sopenharmony_ci	cls_u32.command = add ?
125062306a36Sopenharmony_ci		TC_CLSU32_REPLACE_KNODE : TC_CLSU32_DELETE_KNODE;
125162306a36Sopenharmony_ci	cls_u32.knode.handle = n->handle;
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	if (add) {
125462306a36Sopenharmony_ci		cls_u32.knode.fshift = n->fshift;
125562306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
125662306a36Sopenharmony_ci		cls_u32.knode.val = n->val;
125762306a36Sopenharmony_ci		cls_u32.knode.mask = n->mask;
125862306a36Sopenharmony_ci#else
125962306a36Sopenharmony_ci		cls_u32.knode.val = 0;
126062306a36Sopenharmony_ci		cls_u32.knode.mask = 0;
126162306a36Sopenharmony_ci#endif
126262306a36Sopenharmony_ci		cls_u32.knode.sel = &n->sel;
126362306a36Sopenharmony_ci		cls_u32.knode.res = &n->res;
126462306a36Sopenharmony_ci		cls_u32.knode.exts = &n->exts;
126562306a36Sopenharmony_ci		if (n->ht_down)
126662306a36Sopenharmony_ci			cls_u32.knode.link_handle = ht->handle;
126762306a36Sopenharmony_ci	}
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci	return tc_setup_cb_reoffload(block, tp, add, cb, TC_SETUP_CLSU32,
127062306a36Sopenharmony_ci				     &cls_u32, cb_priv, &n->flags,
127162306a36Sopenharmony_ci				     &n->in_hw_count);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int u32_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb,
127562306a36Sopenharmony_ci			 void *cb_priv, struct netlink_ext_ack *extack)
127662306a36Sopenharmony_ci{
127762306a36Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
127862306a36Sopenharmony_ci	struct tc_u_hnode *ht;
127962306a36Sopenharmony_ci	struct tc_u_knode *n;
128062306a36Sopenharmony_ci	unsigned int h;
128162306a36Sopenharmony_ci	int err;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
128462306a36Sopenharmony_ci	     ht;
128562306a36Sopenharmony_ci	     ht = rtnl_dereference(ht->next)) {
128662306a36Sopenharmony_ci		if (ht->prio != tp->prio)
128762306a36Sopenharmony_ci			continue;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		/* When adding filters to a new dev, try to offload the
129062306a36Sopenharmony_ci		 * hashtable first. When removing, do the filters before the
129162306a36Sopenharmony_ci		 * hashtable.
129262306a36Sopenharmony_ci		 */
129362306a36Sopenharmony_ci		if (add && !tc_skip_hw(ht->flags)) {
129462306a36Sopenharmony_ci			err = u32_reoffload_hnode(tp, ht, add, cb, cb_priv,
129562306a36Sopenharmony_ci						  extack);
129662306a36Sopenharmony_ci			if (err)
129762306a36Sopenharmony_ci				return err;
129862306a36Sopenharmony_ci		}
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci		for (h = 0; h <= ht->divisor; h++) {
130162306a36Sopenharmony_ci			for (n = rtnl_dereference(ht->ht[h]);
130262306a36Sopenharmony_ci			     n;
130362306a36Sopenharmony_ci			     n = rtnl_dereference(n->next)) {
130462306a36Sopenharmony_ci				if (tc_skip_hw(n->flags))
130562306a36Sopenharmony_ci					continue;
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci				err = u32_reoffload_knode(tp, n, add, cb,
130862306a36Sopenharmony_ci							  cb_priv, extack);
130962306a36Sopenharmony_ci				if (err)
131062306a36Sopenharmony_ci					return err;
131162306a36Sopenharmony_ci			}
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		if (!add && !tc_skip_hw(ht->flags))
131562306a36Sopenharmony_ci			u32_reoffload_hnode(tp, ht, add, cb, cb_priv, extack);
131662306a36Sopenharmony_ci	}
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	return 0;
131962306a36Sopenharmony_ci}
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_cistatic void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
132262306a36Sopenharmony_ci			   unsigned long base)
132362306a36Sopenharmony_ci{
132462306a36Sopenharmony_ci	struct tc_u_knode *n = fh;
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_ci	tc_cls_bind_class(classid, cl, q, &n->res, base);
132762306a36Sopenharmony_ci}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_cistatic int u32_dump(struct net *net, struct tcf_proto *tp, void *fh,
133062306a36Sopenharmony_ci		    struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
133162306a36Sopenharmony_ci{
133262306a36Sopenharmony_ci	struct tc_u_knode *n = fh;
133362306a36Sopenharmony_ci	struct tc_u_hnode *ht_up, *ht_down;
133462306a36Sopenharmony_ci	struct nlattr *nest;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci	if (n == NULL)
133762306a36Sopenharmony_ci		return skb->len;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	t->tcm_handle = n->handle;
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
134262306a36Sopenharmony_ci	if (nest == NULL)
134362306a36Sopenharmony_ci		goto nla_put_failure;
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_ci	if (TC_U32_KEY(n->handle) == 0) {
134662306a36Sopenharmony_ci		struct tc_u_hnode *ht = fh;
134762306a36Sopenharmony_ci		u32 divisor = ht->divisor + 1;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci		if (nla_put_u32(skb, TCA_U32_DIVISOR, divisor))
135062306a36Sopenharmony_ci			goto nla_put_failure;
135162306a36Sopenharmony_ci	} else {
135262306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
135362306a36Sopenharmony_ci		struct tc_u32_pcnt *gpf;
135462306a36Sopenharmony_ci		int cpu;
135562306a36Sopenharmony_ci#endif
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci		if (nla_put(skb, TCA_U32_SEL, struct_size(&n->sel, keys, n->sel.nkeys),
135862306a36Sopenharmony_ci			    &n->sel))
135962306a36Sopenharmony_ci			goto nla_put_failure;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci		ht_up = rtnl_dereference(n->ht_up);
136262306a36Sopenharmony_ci		if (ht_up) {
136362306a36Sopenharmony_ci			u32 htid = n->handle & 0xFFFFF000;
136462306a36Sopenharmony_ci			if (nla_put_u32(skb, TCA_U32_HASH, htid))
136562306a36Sopenharmony_ci				goto nla_put_failure;
136662306a36Sopenharmony_ci		}
136762306a36Sopenharmony_ci		if (n->res.classid &&
136862306a36Sopenharmony_ci		    nla_put_u32(skb, TCA_U32_CLASSID, n->res.classid))
136962306a36Sopenharmony_ci			goto nla_put_failure;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci		ht_down = rtnl_dereference(n->ht_down);
137262306a36Sopenharmony_ci		if (ht_down &&
137362306a36Sopenharmony_ci		    nla_put_u32(skb, TCA_U32_LINK, ht_down->handle))
137462306a36Sopenharmony_ci			goto nla_put_failure;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci		if (n->flags && nla_put_u32(skb, TCA_U32_FLAGS, n->flags))
137762306a36Sopenharmony_ci			goto nla_put_failure;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
138062306a36Sopenharmony_ci		if ((n->val || n->mask)) {
138162306a36Sopenharmony_ci			struct tc_u32_mark mark = {.val = n->val,
138262306a36Sopenharmony_ci						   .mask = n->mask,
138362306a36Sopenharmony_ci						   .success = 0};
138462306a36Sopenharmony_ci			int cpum;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci			for_each_possible_cpu(cpum) {
138762306a36Sopenharmony_ci				__u32 cnt = *per_cpu_ptr(n->pcpu_success, cpum);
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci				mark.success += cnt;
139062306a36Sopenharmony_ci			}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci			if (nla_put(skb, TCA_U32_MARK, sizeof(mark), &mark))
139362306a36Sopenharmony_ci				goto nla_put_failure;
139462306a36Sopenharmony_ci		}
139562306a36Sopenharmony_ci#endif
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci		if (tcf_exts_dump(skb, &n->exts) < 0)
139862306a36Sopenharmony_ci			goto nla_put_failure;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci		if (n->ifindex) {
140162306a36Sopenharmony_ci			struct net_device *dev;
140262306a36Sopenharmony_ci			dev = __dev_get_by_index(net, n->ifindex);
140362306a36Sopenharmony_ci			if (dev && nla_put_string(skb, TCA_U32_INDEV, dev->name))
140462306a36Sopenharmony_ci				goto nla_put_failure;
140562306a36Sopenharmony_ci		}
140662306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
140762306a36Sopenharmony_ci		gpf = kzalloc(struct_size(gpf, kcnts, n->sel.nkeys), GFP_KERNEL);
140862306a36Sopenharmony_ci		if (!gpf)
140962306a36Sopenharmony_ci			goto nla_put_failure;
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci		for_each_possible_cpu(cpu) {
141262306a36Sopenharmony_ci			int i;
141362306a36Sopenharmony_ci			struct tc_u32_pcnt *pf = per_cpu_ptr(n->pf, cpu);
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci			gpf->rcnt += pf->rcnt;
141662306a36Sopenharmony_ci			gpf->rhit += pf->rhit;
141762306a36Sopenharmony_ci			for (i = 0; i < n->sel.nkeys; i++)
141862306a36Sopenharmony_ci				gpf->kcnts[i] += pf->kcnts[i];
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		if (nla_put_64bit(skb, TCA_U32_PCNT, struct_size(gpf, kcnts, n->sel.nkeys),
142262306a36Sopenharmony_ci				  gpf, TCA_U32_PAD)) {
142362306a36Sopenharmony_ci			kfree(gpf);
142462306a36Sopenharmony_ci			goto nla_put_failure;
142562306a36Sopenharmony_ci		}
142662306a36Sopenharmony_ci		kfree(gpf);
142762306a36Sopenharmony_ci#endif
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	nla_nest_end(skb, nest);
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	if (TC_U32_KEY(n->handle))
143362306a36Sopenharmony_ci		if (tcf_exts_dump_stats(skb, &n->exts) < 0)
143462306a36Sopenharmony_ci			goto nla_put_failure;
143562306a36Sopenharmony_ci	return skb->len;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_cinla_put_failure:
143862306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
143962306a36Sopenharmony_ci	return -1;
144062306a36Sopenharmony_ci}
144162306a36Sopenharmony_ci
144262306a36Sopenharmony_cistatic struct tcf_proto_ops cls_u32_ops __read_mostly = {
144362306a36Sopenharmony_ci	.kind		=	"u32",
144462306a36Sopenharmony_ci	.classify	=	u32_classify,
144562306a36Sopenharmony_ci	.init		=	u32_init,
144662306a36Sopenharmony_ci	.destroy	=	u32_destroy,
144762306a36Sopenharmony_ci	.get		=	u32_get,
144862306a36Sopenharmony_ci	.change		=	u32_change,
144962306a36Sopenharmony_ci	.delete		=	u32_delete,
145062306a36Sopenharmony_ci	.walk		=	u32_walk,
145162306a36Sopenharmony_ci	.reoffload	=	u32_reoffload,
145262306a36Sopenharmony_ci	.dump		=	u32_dump,
145362306a36Sopenharmony_ci	.bind_class	=	u32_bind_class,
145462306a36Sopenharmony_ci	.owner		=	THIS_MODULE,
145562306a36Sopenharmony_ci};
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_cistatic int __init init_u32(void)
145862306a36Sopenharmony_ci{
145962306a36Sopenharmony_ci	int i, ret;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	pr_info("u32 classifier\n");
146262306a36Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
146362306a36Sopenharmony_ci	pr_info("    Performance counters on\n");
146462306a36Sopenharmony_ci#endif
146562306a36Sopenharmony_ci	pr_info("    input device check on\n");
146662306a36Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
146762306a36Sopenharmony_ci	pr_info("    Actions configured\n");
146862306a36Sopenharmony_ci#endif
146962306a36Sopenharmony_ci	tc_u_common_hash = kvmalloc_array(U32_HASH_SIZE,
147062306a36Sopenharmony_ci					  sizeof(struct hlist_head),
147162306a36Sopenharmony_ci					  GFP_KERNEL);
147262306a36Sopenharmony_ci	if (!tc_u_common_hash)
147362306a36Sopenharmony_ci		return -ENOMEM;
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	for (i = 0; i < U32_HASH_SIZE; i++)
147662306a36Sopenharmony_ci		INIT_HLIST_HEAD(&tc_u_common_hash[i]);
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	ret = register_tcf_proto_ops(&cls_u32_ops);
147962306a36Sopenharmony_ci	if (ret)
148062306a36Sopenharmony_ci		kvfree(tc_u_common_hash);
148162306a36Sopenharmony_ci	return ret;
148262306a36Sopenharmony_ci}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_cistatic void __exit exit_u32(void)
148562306a36Sopenharmony_ci{
148662306a36Sopenharmony_ci	unregister_tcf_proto_ops(&cls_u32_ops);
148762306a36Sopenharmony_ci	kvfree(tc_u_common_hash);
148862306a36Sopenharmony_ci}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_cimodule_init(init_u32)
149162306a36Sopenharmony_cimodule_exit(exit_u32)
149262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1493