xref: /kernel/linux/linux-5.10/net/sched/cls_u32.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * net/sched/cls_u32.c	Ugly (or Universal) 32bit key Packet Classifier.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *	The filters are packed to hash tables of key nodes
88c2ecf20Sopenharmony_ci *	with a set of 32bit key/mask pairs at every node.
98c2ecf20Sopenharmony_ci *	Nodes reference next level hash tables etc.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *	This scheme is the best universal classifier I managed to
128c2ecf20Sopenharmony_ci *	invent; it is not super-fast, but it is not slow (provided you
138c2ecf20Sopenharmony_ci *	program it correctly), and general enough.  And its relative
148c2ecf20Sopenharmony_ci *	speed grows as the number of rules becomes larger.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci *	It seems that it represents the best middle point between
178c2ecf20Sopenharmony_ci *	speed and manageability both by human and by machine.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *	It is especially useful for link sharing combined with QoS;
208c2ecf20Sopenharmony_ci *	pure RSVP doesn't need such a general approach and can use
218c2ecf20Sopenharmony_ci *	much simpler (and faster) schemes, sort of cls_rsvp.c.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci *	nfmark match added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro>
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/slab.h>
288c2ecf20Sopenharmony_ci#include <linux/types.h>
298c2ecf20Sopenharmony_ci#include <linux/kernel.h>
308c2ecf20Sopenharmony_ci#include <linux/string.h>
318c2ecf20Sopenharmony_ci#include <linux/errno.h>
328c2ecf20Sopenharmony_ci#include <linux/percpu.h>
338c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h>
348c2ecf20Sopenharmony_ci#include <linux/skbuff.h>
358c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
368c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
378c2ecf20Sopenharmony_ci#include <linux/hash.h>
388c2ecf20Sopenharmony_ci#include <net/netlink.h>
398c2ecf20Sopenharmony_ci#include <net/act_api.h>
408c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
418c2ecf20Sopenharmony_ci#include <linux/idr.h>
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistruct tc_u_knode {
448c2ecf20Sopenharmony_ci	struct tc_u_knode __rcu	*next;
458c2ecf20Sopenharmony_ci	u32			handle;
468c2ecf20Sopenharmony_ci	struct tc_u_hnode __rcu	*ht_up;
478c2ecf20Sopenharmony_ci	struct tcf_exts		exts;
488c2ecf20Sopenharmony_ci	int			ifindex;
498c2ecf20Sopenharmony_ci	u8			fshift;
508c2ecf20Sopenharmony_ci	struct tcf_result	res;
518c2ecf20Sopenharmony_ci	struct tc_u_hnode __rcu	*ht_down;
528c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
538c2ecf20Sopenharmony_ci	struct tc_u32_pcnt __percpu *pf;
548c2ecf20Sopenharmony_ci#endif
558c2ecf20Sopenharmony_ci	u32			flags;
568c2ecf20Sopenharmony_ci	unsigned int		in_hw_count;
578c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
588c2ecf20Sopenharmony_ci	u32			val;
598c2ecf20Sopenharmony_ci	u32			mask;
608c2ecf20Sopenharmony_ci	u32 __percpu		*pcpu_success;
618c2ecf20Sopenharmony_ci#endif
628c2ecf20Sopenharmony_ci	struct rcu_work		rwork;
638c2ecf20Sopenharmony_ci	/* The 'sel' field MUST be the last field in structure to allow for
648c2ecf20Sopenharmony_ci	 * tc_u32_keys allocated at end of structure.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	struct tc_u32_sel	sel;
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct tc_u_hnode {
708c2ecf20Sopenharmony_ci	struct tc_u_hnode __rcu	*next;
718c2ecf20Sopenharmony_ci	u32			handle;
728c2ecf20Sopenharmony_ci	u32			prio;
738c2ecf20Sopenharmony_ci	int			refcnt;
748c2ecf20Sopenharmony_ci	unsigned int		divisor;
758c2ecf20Sopenharmony_ci	struct idr		handle_idr;
768c2ecf20Sopenharmony_ci	bool			is_root;
778c2ecf20Sopenharmony_ci	struct rcu_head		rcu;
788c2ecf20Sopenharmony_ci	u32			flags;
798c2ecf20Sopenharmony_ci	/* The 'ht' field MUST be the last field in structure to allow for
808c2ecf20Sopenharmony_ci	 * more entries allocated at end of structure.
818c2ecf20Sopenharmony_ci	 */
828c2ecf20Sopenharmony_ci	struct tc_u_knode __rcu	*ht[];
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistruct tc_u_common {
868c2ecf20Sopenharmony_ci	struct tc_u_hnode __rcu	*hlist;
878c2ecf20Sopenharmony_ci	void			*ptr;
888c2ecf20Sopenharmony_ci	int			refcnt;
898c2ecf20Sopenharmony_ci	struct idr		handle_idr;
908c2ecf20Sopenharmony_ci	struct hlist_node	hnode;
918c2ecf20Sopenharmony_ci	long			knodes;
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic inline unsigned int u32_hash_fold(__be32 key,
958c2ecf20Sopenharmony_ci					 const struct tc_u32_sel *sel,
968c2ecf20Sopenharmony_ci					 u8 fshift)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	unsigned int h = ntohl(key & sel->hmask) >> fshift;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return h;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic int u32_classify(struct sk_buff *skb, const struct tcf_proto *tp,
1048c2ecf20Sopenharmony_ci			struct tcf_result *res)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct {
1078c2ecf20Sopenharmony_ci		struct tc_u_knode *knode;
1088c2ecf20Sopenharmony_ci		unsigned int	  off;
1098c2ecf20Sopenharmony_ci	} stack[TC_U32_MAXDEPTH];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rcu_dereference_bh(tp->root);
1128c2ecf20Sopenharmony_ci	unsigned int off = skb_network_offset(skb);
1138c2ecf20Sopenharmony_ci	struct tc_u_knode *n;
1148c2ecf20Sopenharmony_ci	int sdepth = 0;
1158c2ecf20Sopenharmony_ci	int off2 = 0;
1168c2ecf20Sopenharmony_ci	int sel = 0;
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
1188c2ecf20Sopenharmony_ci	int j;
1198c2ecf20Sopenharmony_ci#endif
1208c2ecf20Sopenharmony_ci	int i, r;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cinext_ht:
1238c2ecf20Sopenharmony_ci	n = rcu_dereference_bh(ht->ht[sel]);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cinext_knode:
1268c2ecf20Sopenharmony_ci	if (n) {
1278c2ecf20Sopenharmony_ci		struct tc_u32_key *key = n->sel.keys;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
1308c2ecf20Sopenharmony_ci		__this_cpu_inc(n->pf->rcnt);
1318c2ecf20Sopenharmony_ci		j = 0;
1328c2ecf20Sopenharmony_ci#endif
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		if (tc_skip_sw(n->flags)) {
1358c2ecf20Sopenharmony_ci			n = rcu_dereference_bh(n->next);
1368c2ecf20Sopenharmony_ci			goto next_knode;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
1408c2ecf20Sopenharmony_ci		if ((skb->mark & n->mask) != n->val) {
1418c2ecf20Sopenharmony_ci			n = rcu_dereference_bh(n->next);
1428c2ecf20Sopenharmony_ci			goto next_knode;
1438c2ecf20Sopenharmony_ci		} else {
1448c2ecf20Sopenharmony_ci			__this_cpu_inc(*n->pcpu_success);
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci#endif
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		for (i = n->sel.nkeys; i > 0; i--, key++) {
1498c2ecf20Sopenharmony_ci			int toff = off + key->off + (off2 & key->offmask);
1508c2ecf20Sopenharmony_ci			__be32 *data, hdata;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci			if (skb_headroom(skb) + toff > INT_MAX)
1538c2ecf20Sopenharmony_ci				goto out;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci			data = skb_header_pointer(skb, toff, 4, &hdata);
1568c2ecf20Sopenharmony_ci			if (!data)
1578c2ecf20Sopenharmony_ci				goto out;
1588c2ecf20Sopenharmony_ci			if ((*data ^ key->val) & key->mask) {
1598c2ecf20Sopenharmony_ci				n = rcu_dereference_bh(n->next);
1608c2ecf20Sopenharmony_ci				goto next_knode;
1618c2ecf20Sopenharmony_ci			}
1628c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
1638c2ecf20Sopenharmony_ci			__this_cpu_inc(n->pf->kcnts[j]);
1648c2ecf20Sopenharmony_ci			j++;
1658c2ecf20Sopenharmony_ci#endif
1668c2ecf20Sopenharmony_ci		}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_down);
1698c2ecf20Sopenharmony_ci		if (!ht) {
1708c2ecf20Sopenharmony_cicheck_terminal:
1718c2ecf20Sopenharmony_ci			if (n->sel.flags & TC_U32_TERMINAL) {
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci				*res = n->res;
1748c2ecf20Sopenharmony_ci				if (!tcf_match_indev(skb, n->ifindex)) {
1758c2ecf20Sopenharmony_ci					n = rcu_dereference_bh(n->next);
1768c2ecf20Sopenharmony_ci					goto next_knode;
1778c2ecf20Sopenharmony_ci				}
1788c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
1798c2ecf20Sopenharmony_ci				__this_cpu_inc(n->pf->rhit);
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci				r = tcf_exts_exec(skb, &n->exts, res);
1828c2ecf20Sopenharmony_ci				if (r < 0) {
1838c2ecf20Sopenharmony_ci					n = rcu_dereference_bh(n->next);
1848c2ecf20Sopenharmony_ci					goto next_knode;
1858c2ecf20Sopenharmony_ci				}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci				return r;
1888c2ecf20Sopenharmony_ci			}
1898c2ecf20Sopenharmony_ci			n = rcu_dereference_bh(n->next);
1908c2ecf20Sopenharmony_ci			goto next_knode;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci		/* PUSH */
1948c2ecf20Sopenharmony_ci		if (sdepth >= TC_U32_MAXDEPTH)
1958c2ecf20Sopenharmony_ci			goto deadloop;
1968c2ecf20Sopenharmony_ci		stack[sdepth].knode = n;
1978c2ecf20Sopenharmony_ci		stack[sdepth].off = off;
1988c2ecf20Sopenharmony_ci		sdepth++;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_down);
2018c2ecf20Sopenharmony_ci		sel = 0;
2028c2ecf20Sopenharmony_ci		if (ht->divisor) {
2038c2ecf20Sopenharmony_ci			__be32 *data, hdata;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			data = skb_header_pointer(skb, off + n->sel.hoff, 4,
2068c2ecf20Sopenharmony_ci						  &hdata);
2078c2ecf20Sopenharmony_ci			if (!data)
2088c2ecf20Sopenharmony_ci				goto out;
2098c2ecf20Sopenharmony_ci			sel = ht->divisor & u32_hash_fold(*data, &n->sel,
2108c2ecf20Sopenharmony_ci							  n->fshift);
2118c2ecf20Sopenharmony_ci		}
2128c2ecf20Sopenharmony_ci		if (!(n->sel.flags & (TC_U32_VAROFFSET | TC_U32_OFFSET | TC_U32_EAT)))
2138c2ecf20Sopenharmony_ci			goto next_ht;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		if (n->sel.flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
2168c2ecf20Sopenharmony_ci			off2 = n->sel.off + 3;
2178c2ecf20Sopenharmony_ci			if (n->sel.flags & TC_U32_VAROFFSET) {
2188c2ecf20Sopenharmony_ci				__be16 *data, hdata;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci				data = skb_header_pointer(skb,
2218c2ecf20Sopenharmony_ci							  off + n->sel.offoff,
2228c2ecf20Sopenharmony_ci							  2, &hdata);
2238c2ecf20Sopenharmony_ci				if (!data)
2248c2ecf20Sopenharmony_ci					goto out;
2258c2ecf20Sopenharmony_ci				off2 += ntohs(n->sel.offmask & *data) >>
2268c2ecf20Sopenharmony_ci					n->sel.offshift;
2278c2ecf20Sopenharmony_ci			}
2288c2ecf20Sopenharmony_ci			off2 &= ~3;
2298c2ecf20Sopenharmony_ci		}
2308c2ecf20Sopenharmony_ci		if (n->sel.flags & TC_U32_EAT) {
2318c2ecf20Sopenharmony_ci			off += off2;
2328c2ecf20Sopenharmony_ci			off2 = 0;
2338c2ecf20Sopenharmony_ci		}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci		if (off < skb->len)
2368c2ecf20Sopenharmony_ci			goto next_ht;
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* POP */
2408c2ecf20Sopenharmony_ci	if (sdepth--) {
2418c2ecf20Sopenharmony_ci		n = stack[sdepth].knode;
2428c2ecf20Sopenharmony_ci		ht = rcu_dereference_bh(n->ht_up);
2438c2ecf20Sopenharmony_ci		off = stack[sdepth].off;
2448c2ecf20Sopenharmony_ci		goto check_terminal;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ciout:
2478c2ecf20Sopenharmony_ci	return -1;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cideadloop:
2508c2ecf20Sopenharmony_ci	net_warn_ratelimited("cls_u32: dead loop\n");
2518c2ecf20Sopenharmony_ci	return -1;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic struct tc_u_hnode *u32_lookup_ht(struct tc_u_common *tp_c, u32 handle)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
2598c2ecf20Sopenharmony_ci	     ht;
2608c2ecf20Sopenharmony_ci	     ht = rtnl_dereference(ht->next))
2618c2ecf20Sopenharmony_ci		if (ht->handle == handle)
2628c2ecf20Sopenharmony_ci			break;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return ht;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic struct tc_u_knode *u32_lookup_key(struct tc_u_hnode *ht, u32 handle)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	unsigned int sel;
2708c2ecf20Sopenharmony_ci	struct tc_u_knode *n = NULL;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	sel = TC_U32_HASH(handle);
2738c2ecf20Sopenharmony_ci	if (sel > ht->divisor)
2748c2ecf20Sopenharmony_ci		goto out;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	for (n = rtnl_dereference(ht->ht[sel]);
2778c2ecf20Sopenharmony_ci	     n;
2788c2ecf20Sopenharmony_ci	     n = rtnl_dereference(n->next))
2798c2ecf20Sopenharmony_ci		if (n->handle == handle)
2808c2ecf20Sopenharmony_ci			break;
2818c2ecf20Sopenharmony_ciout:
2828c2ecf20Sopenharmony_ci	return n;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic void *u32_get(struct tcf_proto *tp, u32 handle)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
2898c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (TC_U32_HTID(handle) == TC_U32_ROOT)
2928c2ecf20Sopenharmony_ci		ht = rtnl_dereference(tp->root);
2938c2ecf20Sopenharmony_ci	else
2948c2ecf20Sopenharmony_ci		ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle));
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (!ht)
2978c2ecf20Sopenharmony_ci		return NULL;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	if (TC_U32_KEY(handle) == 0)
3008c2ecf20Sopenharmony_ci		return ht;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	return u32_lookup_key(ht, handle);
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci/* Protected by rtnl lock */
3068c2ecf20Sopenharmony_cistatic u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
3098c2ecf20Sopenharmony_ci	if (id < 0)
3108c2ecf20Sopenharmony_ci		return 0;
3118c2ecf20Sopenharmony_ci	return (id | 0x800U) << 20;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic struct hlist_head *tc_u_common_hash;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci#define U32_HASH_SHIFT 10
3178c2ecf20Sopenharmony_ci#define U32_HASH_SIZE (1 << U32_HASH_SHIFT)
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic void *tc_u_common_ptr(const struct tcf_proto *tp)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	/* The block sharing is currently supported only
3248c2ecf20Sopenharmony_ci	 * for classless qdiscs. In that case we use block
3258c2ecf20Sopenharmony_ci	 * for tc_u_common identification. In case the
3268c2ecf20Sopenharmony_ci	 * block is not shared, block->q is a valid pointer
3278c2ecf20Sopenharmony_ci	 * and we can use that. That works for classful qdiscs.
3288c2ecf20Sopenharmony_ci	 */
3298c2ecf20Sopenharmony_ci	if (tcf_block_shared(block))
3308c2ecf20Sopenharmony_ci		return block;
3318c2ecf20Sopenharmony_ci	else
3328c2ecf20Sopenharmony_ci		return block->q;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic struct hlist_head *tc_u_hash(void *key)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	return tc_u_common_hash + hash_ptr(key, U32_HASH_SHIFT);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic struct tc_u_common *tc_u_common_find(void *key)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct tc_u_common *tc;
3438c2ecf20Sopenharmony_ci	hlist_for_each_entry(tc, tc_u_hash(key), hnode) {
3448c2ecf20Sopenharmony_ci		if (tc->ptr == key)
3458c2ecf20Sopenharmony_ci			return tc;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	return NULL;
3488c2ecf20Sopenharmony_ci}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_cistatic int u32_init(struct tcf_proto *tp)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	struct tc_u_hnode *root_ht;
3538c2ecf20Sopenharmony_ci	void *key = tc_u_common_ptr(tp);
3548c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tc_u_common_find(key);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	root_ht = kzalloc(struct_size(root_ht, ht, 1), GFP_KERNEL);
3578c2ecf20Sopenharmony_ci	if (root_ht == NULL)
3588c2ecf20Sopenharmony_ci		return -ENOBUFS;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	root_ht->refcnt++;
3618c2ecf20Sopenharmony_ci	root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : 0x80000000;
3628c2ecf20Sopenharmony_ci	root_ht->prio = tp->prio;
3638c2ecf20Sopenharmony_ci	root_ht->is_root = true;
3648c2ecf20Sopenharmony_ci	idr_init(&root_ht->handle_idr);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (tp_c == NULL) {
3678c2ecf20Sopenharmony_ci		tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL);
3688c2ecf20Sopenharmony_ci		if (tp_c == NULL) {
3698c2ecf20Sopenharmony_ci			kfree(root_ht);
3708c2ecf20Sopenharmony_ci			return -ENOBUFS;
3718c2ecf20Sopenharmony_ci		}
3728c2ecf20Sopenharmony_ci		tp_c->ptr = key;
3738c2ecf20Sopenharmony_ci		INIT_HLIST_NODE(&tp_c->hnode);
3748c2ecf20Sopenharmony_ci		idr_init(&tp_c->handle_idr);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		hlist_add_head(&tp_c->hnode, tc_u_hash(key));
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	tp_c->refcnt++;
3808c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(root_ht->next, tp_c->hlist);
3818c2ecf20Sopenharmony_ci	rcu_assign_pointer(tp_c->hlist, root_ht);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	root_ht->refcnt++;
3848c2ecf20Sopenharmony_ci	rcu_assign_pointer(tp->root, root_ht);
3858c2ecf20Sopenharmony_ci	tp->data = tp_c;
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void __u32_destroy_key(struct tc_u_knode *n)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	tcf_exts_destroy(&n->exts);
3948c2ecf20Sopenharmony_ci	if (ht && --ht->refcnt == 0)
3958c2ecf20Sopenharmony_ci		kfree(ht);
3968c2ecf20Sopenharmony_ci	kfree(n);
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic void u32_destroy_key(struct tc_u_knode *n, bool free_pf)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	tcf_exts_put_net(&n->exts);
4028c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
4038c2ecf20Sopenharmony_ci	if (free_pf)
4048c2ecf20Sopenharmony_ci		free_percpu(n->pf);
4058c2ecf20Sopenharmony_ci#endif
4068c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
4078c2ecf20Sopenharmony_ci	if (free_pf)
4088c2ecf20Sopenharmony_ci		free_percpu(n->pcpu_success);
4098c2ecf20Sopenharmony_ci#endif
4108c2ecf20Sopenharmony_ci	__u32_destroy_key(n);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci/* u32_delete_key_rcu should be called when free'ing a copied
4148c2ecf20Sopenharmony_ci * version of a tc_u_knode obtained from u32_init_knode(). When
4158c2ecf20Sopenharmony_ci * copies are obtained from u32_init_knode() the statistics are
4168c2ecf20Sopenharmony_ci * shared between the old and new copies to allow readers to
4178c2ecf20Sopenharmony_ci * continue to update the statistics during the copy. To support
4188c2ecf20Sopenharmony_ci * this the u32_delete_key_rcu variant does not free the percpu
4198c2ecf20Sopenharmony_ci * statistics.
4208c2ecf20Sopenharmony_ci */
4218c2ecf20Sopenharmony_cistatic void u32_delete_key_work(struct work_struct *work)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct tc_u_knode *key = container_of(to_rcu_work(work),
4248c2ecf20Sopenharmony_ci					      struct tc_u_knode,
4258c2ecf20Sopenharmony_ci					      rwork);
4268c2ecf20Sopenharmony_ci	rtnl_lock();
4278c2ecf20Sopenharmony_ci	u32_destroy_key(key, false);
4288c2ecf20Sopenharmony_ci	rtnl_unlock();
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/* u32_delete_key_freepf_rcu is the rcu callback variant
4328c2ecf20Sopenharmony_ci * that free's the entire structure including the statistics
4338c2ecf20Sopenharmony_ci * percpu variables. Only use this if the key is not a copy
4348c2ecf20Sopenharmony_ci * returned by u32_init_knode(). See u32_delete_key_rcu()
4358c2ecf20Sopenharmony_ci * for the variant that should be used with keys return from
4368c2ecf20Sopenharmony_ci * u32_init_knode()
4378c2ecf20Sopenharmony_ci */
4388c2ecf20Sopenharmony_cistatic void u32_delete_key_freepf_work(struct work_struct *work)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct tc_u_knode *key = container_of(to_rcu_work(work),
4418c2ecf20Sopenharmony_ci					      struct tc_u_knode,
4428c2ecf20Sopenharmony_ci					      rwork);
4438c2ecf20Sopenharmony_ci	rtnl_lock();
4448c2ecf20Sopenharmony_ci	u32_destroy_key(key, true);
4458c2ecf20Sopenharmony_ci	rtnl_unlock();
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
4518c2ecf20Sopenharmony_ci	struct tc_u_knode __rcu **kp;
4528c2ecf20Sopenharmony_ci	struct tc_u_knode *pkp;
4538c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(key->ht_up);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	if (ht) {
4568c2ecf20Sopenharmony_ci		kp = &ht->ht[TC_U32_HASH(key->handle)];
4578c2ecf20Sopenharmony_ci		for (pkp = rtnl_dereference(*kp); pkp;
4588c2ecf20Sopenharmony_ci		     kp = &pkp->next, pkp = rtnl_dereference(*kp)) {
4598c2ecf20Sopenharmony_ci			if (pkp == key) {
4608c2ecf20Sopenharmony_ci				RCU_INIT_POINTER(*kp, key->next);
4618c2ecf20Sopenharmony_ci				tp_c->knodes--;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci				tcf_unbind_filter(tp, &key->res);
4648c2ecf20Sopenharmony_ci				idr_remove(&ht->handle_idr, key->handle);
4658c2ecf20Sopenharmony_ci				tcf_exts_get_net(&key->exts);
4668c2ecf20Sopenharmony_ci				tcf_queue_work(&key->rwork, u32_delete_key_freepf_work);
4678c2ecf20Sopenharmony_ci				return 0;
4688c2ecf20Sopenharmony_ci			}
4698c2ecf20Sopenharmony_ci		}
4708c2ecf20Sopenharmony_ci	}
4718c2ecf20Sopenharmony_ci	WARN_ON(1);
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
4768c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
4778c2ecf20Sopenharmony_ci{
4788c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
4798c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, h->flags, extack);
4828c2ecf20Sopenharmony_ci	cls_u32.command = TC_CLSU32_DELETE_HNODE;
4838c2ecf20Sopenharmony_ci	cls_u32.hnode.divisor = h->divisor;
4848c2ecf20Sopenharmony_ci	cls_u32.hnode.handle = h->handle;
4858c2ecf20Sopenharmony_ci	cls_u32.hnode.prio = h->prio;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, false, true);
4888c2ecf20Sopenharmony_ci}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
4918c2ecf20Sopenharmony_ci				u32 flags, struct netlink_ext_ack *extack)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
4948c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
4958c2ecf20Sopenharmony_ci	bool skip_sw = tc_skip_sw(flags);
4968c2ecf20Sopenharmony_ci	bool offloaded = false;
4978c2ecf20Sopenharmony_ci	int err;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
5008c2ecf20Sopenharmony_ci	cls_u32.command = TC_CLSU32_NEW_HNODE;
5018c2ecf20Sopenharmony_ci	cls_u32.hnode.divisor = h->divisor;
5028c2ecf20Sopenharmony_ci	cls_u32.hnode.handle = h->handle;
5038c2ecf20Sopenharmony_ci	cls_u32.hnode.prio = h->prio;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	err = tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, skip_sw, true);
5068c2ecf20Sopenharmony_ci	if (err < 0) {
5078c2ecf20Sopenharmony_ci		u32_clear_hw_hnode(tp, h, NULL);
5088c2ecf20Sopenharmony_ci		return err;
5098c2ecf20Sopenharmony_ci	} else if (err > 0) {
5108c2ecf20Sopenharmony_ci		offloaded = true;
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	if (skip_sw && !offloaded)
5148c2ecf20Sopenharmony_ci		return -EINVAL;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return 0;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
5208c2ecf20Sopenharmony_ci				struct netlink_ext_ack *extack)
5218c2ecf20Sopenharmony_ci{
5228c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
5238c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
5268c2ecf20Sopenharmony_ci	cls_u32.command = TC_CLSU32_DELETE_KNODE;
5278c2ecf20Sopenharmony_ci	cls_u32.knode.handle = n->handle;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	tc_setup_cb_destroy(block, tp, TC_SETUP_CLSU32, &cls_u32, false,
5308c2ecf20Sopenharmony_ci			    &n->flags, &n->in_hw_count, true);
5318c2ecf20Sopenharmony_ci}
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_cistatic int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
5348c2ecf20Sopenharmony_ci				u32 flags, struct netlink_ext_ack *extack)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
5378c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
5388c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
5398c2ecf20Sopenharmony_ci	bool skip_sw = tc_skip_sw(flags);
5408c2ecf20Sopenharmony_ci	int err;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
5438c2ecf20Sopenharmony_ci	cls_u32.command = TC_CLSU32_REPLACE_KNODE;
5448c2ecf20Sopenharmony_ci	cls_u32.knode.handle = n->handle;
5458c2ecf20Sopenharmony_ci	cls_u32.knode.fshift = n->fshift;
5468c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
5478c2ecf20Sopenharmony_ci	cls_u32.knode.val = n->val;
5488c2ecf20Sopenharmony_ci	cls_u32.knode.mask = n->mask;
5498c2ecf20Sopenharmony_ci#else
5508c2ecf20Sopenharmony_ci	cls_u32.knode.val = 0;
5518c2ecf20Sopenharmony_ci	cls_u32.knode.mask = 0;
5528c2ecf20Sopenharmony_ci#endif
5538c2ecf20Sopenharmony_ci	cls_u32.knode.sel = &n->sel;
5548c2ecf20Sopenharmony_ci	cls_u32.knode.res = &n->res;
5558c2ecf20Sopenharmony_ci	cls_u32.knode.exts = &n->exts;
5568c2ecf20Sopenharmony_ci	if (n->ht_down)
5578c2ecf20Sopenharmony_ci		cls_u32.knode.link_handle = ht->handle;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	err = tc_setup_cb_add(block, tp, TC_SETUP_CLSU32, &cls_u32, skip_sw,
5608c2ecf20Sopenharmony_ci			      &n->flags, &n->in_hw_count, true);
5618c2ecf20Sopenharmony_ci	if (err) {
5628c2ecf20Sopenharmony_ci		u32_remove_hw_knode(tp, n, NULL);
5638c2ecf20Sopenharmony_ci		return err;
5648c2ecf20Sopenharmony_ci	}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (skip_sw && !(n->flags & TCA_CLS_FLAGS_IN_HW))
5678c2ecf20Sopenharmony_ci		return -EINVAL;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	return 0;
5708c2ecf20Sopenharmony_ci}
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_cistatic void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
5738c2ecf20Sopenharmony_ci			    struct netlink_ext_ack *extack)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
5768c2ecf20Sopenharmony_ci	struct tc_u_knode *n;
5778c2ecf20Sopenharmony_ci	unsigned int h;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	for (h = 0; h <= ht->divisor; h++) {
5808c2ecf20Sopenharmony_ci		while ((n = rtnl_dereference(ht->ht[h])) != NULL) {
5818c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(ht->ht[h],
5828c2ecf20Sopenharmony_ci					 rtnl_dereference(n->next));
5838c2ecf20Sopenharmony_ci			tp_c->knodes--;
5848c2ecf20Sopenharmony_ci			tcf_unbind_filter(tp, &n->res);
5858c2ecf20Sopenharmony_ci			u32_remove_hw_knode(tp, n, extack);
5868c2ecf20Sopenharmony_ci			idr_remove(&ht->handle_idr, n->handle);
5878c2ecf20Sopenharmony_ci			if (tcf_exts_get_net(&n->exts))
5888c2ecf20Sopenharmony_ci				tcf_queue_work(&n->rwork, u32_delete_key_freepf_work);
5898c2ecf20Sopenharmony_ci			else
5908c2ecf20Sopenharmony_ci				u32_destroy_key(n, true);
5918c2ecf20Sopenharmony_ci		}
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
5968c2ecf20Sopenharmony_ci			     struct netlink_ext_ack *extack)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
5998c2ecf20Sopenharmony_ci	struct tc_u_hnode __rcu **hn;
6008c2ecf20Sopenharmony_ci	struct tc_u_hnode *phn;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	WARN_ON(--ht->refcnt);
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	u32_clear_hnode(tp, ht, extack);
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	hn = &tp_c->hlist;
6078c2ecf20Sopenharmony_ci	for (phn = rtnl_dereference(*hn);
6088c2ecf20Sopenharmony_ci	     phn;
6098c2ecf20Sopenharmony_ci	     hn = &phn->next, phn = rtnl_dereference(*hn)) {
6108c2ecf20Sopenharmony_ci		if (phn == ht) {
6118c2ecf20Sopenharmony_ci			u32_clear_hw_hnode(tp, ht, extack);
6128c2ecf20Sopenharmony_ci			idr_destroy(&ht->handle_idr);
6138c2ecf20Sopenharmony_ci			idr_remove(&tp_c->handle_idr, ht->handle);
6148c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(*hn, ht->next);
6158c2ecf20Sopenharmony_ci			kfree_rcu(ht, rcu);
6168c2ecf20Sopenharmony_ci			return 0;
6178c2ecf20Sopenharmony_ci		}
6188c2ecf20Sopenharmony_ci	}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	return -ENOENT;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
6248c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
6278c2ecf20Sopenharmony_ci	struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ci	WARN_ON(root_ht == NULL);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	if (root_ht && --root_ht->refcnt == 1)
6328c2ecf20Sopenharmony_ci		u32_destroy_hnode(tp, root_ht, extack);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	if (--tp_c->refcnt == 0) {
6358c2ecf20Sopenharmony_ci		struct tc_u_hnode *ht;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci		hlist_del(&tp_c->hnode);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) {
6408c2ecf20Sopenharmony_ci			u32_clear_hnode(tp, ht, extack);
6418c2ecf20Sopenharmony_ci			RCU_INIT_POINTER(tp_c->hlist, ht->next);
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci			/* u32_destroy_key() will later free ht for us, if it's
6448c2ecf20Sopenharmony_ci			 * still referenced by some knode
6458c2ecf20Sopenharmony_ci			 */
6468c2ecf20Sopenharmony_ci			if (--ht->refcnt == 0)
6478c2ecf20Sopenharmony_ci				kfree_rcu(ht, rcu);
6488c2ecf20Sopenharmony_ci		}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci		idr_destroy(&tp_c->handle_idr);
6518c2ecf20Sopenharmony_ci		kfree(tp_c);
6528c2ecf20Sopenharmony_ci	}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	tp->data = NULL;
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
6588c2ecf20Sopenharmony_ci		      bool rtnl_held, struct netlink_ext_ack *extack)
6598c2ecf20Sopenharmony_ci{
6608c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = arg;
6618c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
6628c2ecf20Sopenharmony_ci	int ret = 0;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	if (TC_U32_KEY(ht->handle)) {
6658c2ecf20Sopenharmony_ci		u32_remove_hw_knode(tp, (struct tc_u_knode *)ht, extack);
6668c2ecf20Sopenharmony_ci		ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
6678c2ecf20Sopenharmony_ci		goto out;
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	if (ht->is_root) {
6718c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Not allowed to delete root node");
6728c2ecf20Sopenharmony_ci		return -EINVAL;
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	if (ht->refcnt == 1) {
6768c2ecf20Sopenharmony_ci		u32_destroy_hnode(tp, ht, extack);
6778c2ecf20Sopenharmony_ci	} else {
6788c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Can not delete in-use filter");
6798c2ecf20Sopenharmony_ci		return -EBUSY;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ciout:
6838c2ecf20Sopenharmony_ci	*last = tp_c->refcnt == 1 && tp_c->knodes == 0;
6848c2ecf20Sopenharmony_ci	return ret;
6858c2ecf20Sopenharmony_ci}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_cistatic u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
6888c2ecf20Sopenharmony_ci{
6898c2ecf20Sopenharmony_ci	u32 index = htid | 0x800;
6908c2ecf20Sopenharmony_ci	u32 max = htid | 0xFFF;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
6938c2ecf20Sopenharmony_ci		index = htid + 1;
6948c2ecf20Sopenharmony_ci		if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
6958c2ecf20Sopenharmony_ci				 GFP_KERNEL))
6968c2ecf20Sopenharmony_ci			index = max;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	return index;
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
7038c2ecf20Sopenharmony_ci	[TCA_U32_CLASSID]	= { .type = NLA_U32 },
7048c2ecf20Sopenharmony_ci	[TCA_U32_HASH]		= { .type = NLA_U32 },
7058c2ecf20Sopenharmony_ci	[TCA_U32_LINK]		= { .type = NLA_U32 },
7068c2ecf20Sopenharmony_ci	[TCA_U32_DIVISOR]	= { .type = NLA_U32 },
7078c2ecf20Sopenharmony_ci	[TCA_U32_SEL]		= { .len = sizeof(struct tc_u32_sel) },
7088c2ecf20Sopenharmony_ci	[TCA_U32_INDEV]		= { .type = NLA_STRING, .len = IFNAMSIZ },
7098c2ecf20Sopenharmony_ci	[TCA_U32_MARK]		= { .len = sizeof(struct tc_u32_mark) },
7108c2ecf20Sopenharmony_ci	[TCA_U32_FLAGS]		= { .type = NLA_U32 },
7118c2ecf20Sopenharmony_ci};
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_cistatic int u32_set_parms(struct net *net, struct tcf_proto *tp,
7148c2ecf20Sopenharmony_ci			 unsigned long base,
7158c2ecf20Sopenharmony_ci			 struct tc_u_knode *n, struct nlattr **tb,
7168c2ecf20Sopenharmony_ci			 struct nlattr *est, bool ovr,
7178c2ecf20Sopenharmony_ci			 struct netlink_ext_ack *extack)
7188c2ecf20Sopenharmony_ci{
7198c2ecf20Sopenharmony_ci	int err, ifindex = -1;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	err = tcf_exts_validate(net, tp, tb, est, &n->exts, ovr, true, extack);
7228c2ecf20Sopenharmony_ci	if (err < 0)
7238c2ecf20Sopenharmony_ci		return err;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (tb[TCA_U32_INDEV]) {
7268c2ecf20Sopenharmony_ci		ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
7278c2ecf20Sopenharmony_ci		if (ifindex < 0)
7288c2ecf20Sopenharmony_ci			return -EINVAL;
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (tb[TCA_U32_LINK]) {
7328c2ecf20Sopenharmony_ci		u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
7338c2ecf20Sopenharmony_ci		struct tc_u_hnode *ht_down = NULL, *ht_old;
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		if (TC_U32_KEY(handle)) {
7368c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "u32 Link handle must be a hash table");
7378c2ecf20Sopenharmony_ci			return -EINVAL;
7388c2ecf20Sopenharmony_ci		}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci		if (handle) {
7418c2ecf20Sopenharmony_ci			ht_down = u32_lookup_ht(tp->data, handle);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci			if (!ht_down) {
7448c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Link hash table not found");
7458c2ecf20Sopenharmony_ci				return -EINVAL;
7468c2ecf20Sopenharmony_ci			}
7478c2ecf20Sopenharmony_ci			if (ht_down->is_root) {
7488c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Not linking to root node");
7498c2ecf20Sopenharmony_ci				return -EINVAL;
7508c2ecf20Sopenharmony_ci			}
7518c2ecf20Sopenharmony_ci			ht_down->refcnt++;
7528c2ecf20Sopenharmony_ci		}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci		ht_old = rtnl_dereference(n->ht_down);
7558c2ecf20Sopenharmony_ci		rcu_assign_pointer(n->ht_down, ht_down);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci		if (ht_old)
7588c2ecf20Sopenharmony_ci			ht_old->refcnt--;
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	if (tb[TCA_U32_CLASSID]) {
7618c2ecf20Sopenharmony_ci		n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
7628c2ecf20Sopenharmony_ci		tcf_bind_filter(tp, &n->res, base);
7638c2ecf20Sopenharmony_ci	}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (ifindex >= 0)
7668c2ecf20Sopenharmony_ci		n->ifindex = ifindex;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return 0;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_cistatic void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
7728c2ecf20Sopenharmony_ci			      struct tc_u_knode *n)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct tc_u_knode __rcu **ins;
7758c2ecf20Sopenharmony_ci	struct tc_u_knode *pins;
7768c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (TC_U32_HTID(n->handle) == TC_U32_ROOT)
7798c2ecf20Sopenharmony_ci		ht = rtnl_dereference(tp->root);
7808c2ecf20Sopenharmony_ci	else
7818c2ecf20Sopenharmony_ci		ht = u32_lookup_ht(tp_c, TC_U32_HTID(n->handle));
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	ins = &ht->ht[TC_U32_HASH(n->handle)];
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	/* The node must always exist for it to be replaced if this is not the
7868c2ecf20Sopenharmony_ci	 * case then something went very wrong elsewhere.
7878c2ecf20Sopenharmony_ci	 */
7888c2ecf20Sopenharmony_ci	for (pins = rtnl_dereference(*ins); ;
7898c2ecf20Sopenharmony_ci	     ins = &pins->next, pins = rtnl_dereference(*ins))
7908c2ecf20Sopenharmony_ci		if (pins->handle == n->handle)
7918c2ecf20Sopenharmony_ci			break;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	idr_replace(&ht->handle_idr, n, n->handle);
7948c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(n->next, pins->next);
7958c2ecf20Sopenharmony_ci	rcu_assign_pointer(*ins, n);
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp,
7998c2ecf20Sopenharmony_ci					 struct tc_u_knode *n)
8008c2ecf20Sopenharmony_ci{
8018c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
8028c2ecf20Sopenharmony_ci	struct tc_u32_sel *s = &n->sel;
8038c2ecf20Sopenharmony_ci	struct tc_u_knode *new;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	new = kzalloc(struct_size(new, sel.keys, s->nkeys), GFP_KERNEL);
8068c2ecf20Sopenharmony_ci	if (!new)
8078c2ecf20Sopenharmony_ci		return NULL;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(new->next, n->next);
8108c2ecf20Sopenharmony_ci	new->handle = n->handle;
8118c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(new->ht_up, n->ht_up);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	new->ifindex = n->ifindex;
8148c2ecf20Sopenharmony_ci	new->fshift = n->fshift;
8158c2ecf20Sopenharmony_ci	new->flags = n->flags;
8168c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(new->ht_down, ht);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
8198c2ecf20Sopenharmony_ci	/* Statistics may be incremented by readers during update
8208c2ecf20Sopenharmony_ci	 * so we must keep them in tact. When the node is later destroyed
8218c2ecf20Sopenharmony_ci	 * a special destroy call must be made to not free the pf memory.
8228c2ecf20Sopenharmony_ci	 */
8238c2ecf20Sopenharmony_ci	new->pf = n->pf;
8248c2ecf20Sopenharmony_ci#endif
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
8278c2ecf20Sopenharmony_ci	new->val = n->val;
8288c2ecf20Sopenharmony_ci	new->mask = n->mask;
8298c2ecf20Sopenharmony_ci	/* Similarly success statistics must be moved as pointers */
8308c2ecf20Sopenharmony_ci	new->pcpu_success = n->pcpu_success;
8318c2ecf20Sopenharmony_ci#endif
8328c2ecf20Sopenharmony_ci	memcpy(&new->sel, s, struct_size(s, keys, s->nkeys));
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	if (tcf_exts_init(&new->exts, net, TCA_U32_ACT, TCA_U32_POLICE)) {
8358c2ecf20Sopenharmony_ci		kfree(new);
8368c2ecf20Sopenharmony_ci		return NULL;
8378c2ecf20Sopenharmony_ci	}
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	/* bump reference count as long as we hold pointer to structure */
8408c2ecf20Sopenharmony_ci	if (ht)
8418c2ecf20Sopenharmony_ci		ht->refcnt++;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	return new;
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_cistatic int u32_change(struct net *net, struct sk_buff *in_skb,
8478c2ecf20Sopenharmony_ci		      struct tcf_proto *tp, unsigned long base, u32 handle,
8488c2ecf20Sopenharmony_ci		      struct nlattr **tca, void **arg, bool ovr, bool rtnl_held,
8498c2ecf20Sopenharmony_ci		      struct netlink_ext_ack *extack)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
8528c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
8538c2ecf20Sopenharmony_ci	struct tc_u_knode *n;
8548c2ecf20Sopenharmony_ci	struct tc_u32_sel *s;
8558c2ecf20Sopenharmony_ci	struct nlattr *opt = tca[TCA_OPTIONS];
8568c2ecf20Sopenharmony_ci	struct nlattr *tb[TCA_U32_MAX + 1];
8578c2ecf20Sopenharmony_ci	u32 htid, flags = 0;
8588c2ecf20Sopenharmony_ci	size_t sel_size;
8598c2ecf20Sopenharmony_ci	int err;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci	if (!opt) {
8628c2ecf20Sopenharmony_ci		if (handle) {
8638c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Filter handle requires options");
8648c2ecf20Sopenharmony_ci			return -EINVAL;
8658c2ecf20Sopenharmony_ci		} else {
8668c2ecf20Sopenharmony_ci			return 0;
8678c2ecf20Sopenharmony_ci		}
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	err = nla_parse_nested_deprecated(tb, TCA_U32_MAX, opt, u32_policy,
8718c2ecf20Sopenharmony_ci					  extack);
8728c2ecf20Sopenharmony_ci	if (err < 0)
8738c2ecf20Sopenharmony_ci		return err;
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	if (tb[TCA_U32_FLAGS]) {
8768c2ecf20Sopenharmony_ci		flags = nla_get_u32(tb[TCA_U32_FLAGS]);
8778c2ecf20Sopenharmony_ci		if (!tc_flags_valid(flags)) {
8788c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Invalid filter flags");
8798c2ecf20Sopenharmony_ci			return -EINVAL;
8808c2ecf20Sopenharmony_ci		}
8818c2ecf20Sopenharmony_ci	}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	n = *arg;
8848c2ecf20Sopenharmony_ci	if (n) {
8858c2ecf20Sopenharmony_ci		struct tc_u_knode *new;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci		if (TC_U32_KEY(n->handle) == 0) {
8888c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Key node id cannot be zero");
8898c2ecf20Sopenharmony_ci			return -EINVAL;
8908c2ecf20Sopenharmony_ci		}
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci		if ((n->flags ^ flags) &
8938c2ecf20Sopenharmony_ci		    ~(TCA_CLS_FLAGS_IN_HW | TCA_CLS_FLAGS_NOT_IN_HW)) {
8948c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Key node flags do not match passed flags");
8958c2ecf20Sopenharmony_ci			return -EINVAL;
8968c2ecf20Sopenharmony_ci		}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci		new = u32_init_knode(net, tp, n);
8998c2ecf20Sopenharmony_ci		if (!new)
9008c2ecf20Sopenharmony_ci			return -ENOMEM;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci		err = u32_set_parms(net, tp, base, new, tb,
9038c2ecf20Sopenharmony_ci				    tca[TCA_RATE], ovr, extack);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci		if (err) {
9068c2ecf20Sopenharmony_ci			__u32_destroy_key(new);
9078c2ecf20Sopenharmony_ci			return err;
9088c2ecf20Sopenharmony_ci		}
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci		err = u32_replace_hw_knode(tp, new, flags, extack);
9118c2ecf20Sopenharmony_ci		if (err) {
9128c2ecf20Sopenharmony_ci			__u32_destroy_key(new);
9138c2ecf20Sopenharmony_ci			return err;
9148c2ecf20Sopenharmony_ci		}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		if (!tc_in_hw(new->flags))
9178c2ecf20Sopenharmony_ci			new->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci		u32_replace_knode(tp, tp_c, new);
9208c2ecf20Sopenharmony_ci		tcf_unbind_filter(tp, &n->res);
9218c2ecf20Sopenharmony_ci		tcf_exts_get_net(&n->exts);
9228c2ecf20Sopenharmony_ci		tcf_queue_work(&n->rwork, u32_delete_key_work);
9238c2ecf20Sopenharmony_ci		return 0;
9248c2ecf20Sopenharmony_ci	}
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	if (tb[TCA_U32_DIVISOR]) {
9278c2ecf20Sopenharmony_ci		unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci		if (!is_power_of_2(divisor)) {
9308c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Divisor is not a power of 2");
9318c2ecf20Sopenharmony_ci			return -EINVAL;
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		if (divisor-- > 0x100) {
9348c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Exceeded maximum 256 hash buckets");
9358c2ecf20Sopenharmony_ci			return -EINVAL;
9368c2ecf20Sopenharmony_ci		}
9378c2ecf20Sopenharmony_ci		if (TC_U32_KEY(handle)) {
9388c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Divisor can only be used on a hash table");
9398c2ecf20Sopenharmony_ci			return -EINVAL;
9408c2ecf20Sopenharmony_ci		}
9418c2ecf20Sopenharmony_ci		ht = kzalloc(struct_size(ht, ht, divisor + 1), GFP_KERNEL);
9428c2ecf20Sopenharmony_ci		if (ht == NULL)
9438c2ecf20Sopenharmony_ci			return -ENOBUFS;
9448c2ecf20Sopenharmony_ci		if (handle == 0) {
9458c2ecf20Sopenharmony_ci			handle = gen_new_htid(tp->data, ht);
9468c2ecf20Sopenharmony_ci			if (handle == 0) {
9478c2ecf20Sopenharmony_ci				kfree(ht);
9488c2ecf20Sopenharmony_ci				return -ENOMEM;
9498c2ecf20Sopenharmony_ci			}
9508c2ecf20Sopenharmony_ci		} else {
9518c2ecf20Sopenharmony_ci			err = idr_alloc_u32(&tp_c->handle_idr, ht, &handle,
9528c2ecf20Sopenharmony_ci					    handle, GFP_KERNEL);
9538c2ecf20Sopenharmony_ci			if (err) {
9548c2ecf20Sopenharmony_ci				kfree(ht);
9558c2ecf20Sopenharmony_ci				return err;
9568c2ecf20Sopenharmony_ci			}
9578c2ecf20Sopenharmony_ci		}
9588c2ecf20Sopenharmony_ci		ht->refcnt = 1;
9598c2ecf20Sopenharmony_ci		ht->divisor = divisor;
9608c2ecf20Sopenharmony_ci		ht->handle = handle;
9618c2ecf20Sopenharmony_ci		ht->prio = tp->prio;
9628c2ecf20Sopenharmony_ci		idr_init(&ht->handle_idr);
9638c2ecf20Sopenharmony_ci		ht->flags = flags;
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		err = u32_replace_hw_hnode(tp, ht, flags, extack);
9668c2ecf20Sopenharmony_ci		if (err) {
9678c2ecf20Sopenharmony_ci			idr_remove(&tp_c->handle_idr, handle);
9688c2ecf20Sopenharmony_ci			kfree(ht);
9698c2ecf20Sopenharmony_ci			return err;
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(ht->next, tp_c->hlist);
9738c2ecf20Sopenharmony_ci		rcu_assign_pointer(tp_c->hlist, ht);
9748c2ecf20Sopenharmony_ci		*arg = ht;
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		return 0;
9778c2ecf20Sopenharmony_ci	}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci	if (tb[TCA_U32_HASH]) {
9808c2ecf20Sopenharmony_ci		htid = nla_get_u32(tb[TCA_U32_HASH]);
9818c2ecf20Sopenharmony_ci		if (TC_U32_HTID(htid) == TC_U32_ROOT) {
9828c2ecf20Sopenharmony_ci			ht = rtnl_dereference(tp->root);
9838c2ecf20Sopenharmony_ci			htid = ht->handle;
9848c2ecf20Sopenharmony_ci		} else {
9858c2ecf20Sopenharmony_ci			ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid));
9868c2ecf20Sopenharmony_ci			if (!ht) {
9878c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack, "Specified hash table not found");
9888c2ecf20Sopenharmony_ci				return -EINVAL;
9898c2ecf20Sopenharmony_ci			}
9908c2ecf20Sopenharmony_ci		}
9918c2ecf20Sopenharmony_ci	} else {
9928c2ecf20Sopenharmony_ci		ht = rtnl_dereference(tp->root);
9938c2ecf20Sopenharmony_ci		htid = ht->handle;
9948c2ecf20Sopenharmony_ci	}
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci	if (ht->divisor < TC_U32_HASH(htid)) {
9978c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Specified hash table buckets exceed configured value");
9988c2ecf20Sopenharmony_ci		return -EINVAL;
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* At this point, we need to derive the new handle that will be used to
10028c2ecf20Sopenharmony_ci	 * uniquely map the identity of this table match entry. The
10038c2ecf20Sopenharmony_ci	 * identity of the entry that we need to construct is 32 bits made of:
10048c2ecf20Sopenharmony_ci	 *     htid(12b):bucketid(8b):node/entryid(12b)
10058c2ecf20Sopenharmony_ci	 *
10068c2ecf20Sopenharmony_ci	 * At this point _we have the table(ht)_ in which we will insert this
10078c2ecf20Sopenharmony_ci	 * entry. We carry the table's id in variable "htid".
10088c2ecf20Sopenharmony_ci	 * Note that earlier code picked the ht selection either by a) the user
10098c2ecf20Sopenharmony_ci	 * providing the htid specified via TCA_U32_HASH attribute or b) when
10108c2ecf20Sopenharmony_ci	 * no such attribute is passed then the root ht, is default to at ID
10118c2ecf20Sopenharmony_ci	 * 0x[800][00][000]. Rule: the root table has a single bucket with ID 0.
10128c2ecf20Sopenharmony_ci	 * If OTOH the user passed us the htid, they may also pass a bucketid of
10138c2ecf20Sopenharmony_ci	 * choice. 0 is fine. For example a user htid is 0x[600][01][000] it is
10148c2ecf20Sopenharmony_ci	 * indicating hash bucketid of 1. Rule: the entry/node ID _cannot_ be
10158c2ecf20Sopenharmony_ci	 * passed via the htid, so even if it was non-zero it will be ignored.
10168c2ecf20Sopenharmony_ci	 *
10178c2ecf20Sopenharmony_ci	 * We may also have a handle, if the user passed one. The handle also
10188c2ecf20Sopenharmony_ci	 * carries the same addressing of htid(12b):bucketid(8b):node/entryid(12b).
10198c2ecf20Sopenharmony_ci	 * Rule: the bucketid on the handle is ignored even if one was passed;
10208c2ecf20Sopenharmony_ci	 * rather the value on "htid" is always assumed to be the bucketid.
10218c2ecf20Sopenharmony_ci	 */
10228c2ecf20Sopenharmony_ci	if (handle) {
10238c2ecf20Sopenharmony_ci		/* Rule: The htid from handle and tableid from htid must match */
10248c2ecf20Sopenharmony_ci		if (TC_U32_HTID(handle) && TC_U32_HTID(handle ^ htid)) {
10258c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack, "Handle specified hash table address mismatch");
10268c2ecf20Sopenharmony_ci			return -EINVAL;
10278c2ecf20Sopenharmony_ci		}
10288c2ecf20Sopenharmony_ci		/* Ok, so far we have a valid htid(12b):bucketid(8b) but we
10298c2ecf20Sopenharmony_ci		 * need to finalize the table entry identification with the last
10308c2ecf20Sopenharmony_ci		 * part - the node/entryid(12b)). Rule: Nodeid _cannot be 0_ for
10318c2ecf20Sopenharmony_ci		 * entries. Rule: nodeid of 0 is reserved only for tables(see
10328c2ecf20Sopenharmony_ci		 * earlier code which processes TC_U32_DIVISOR attribute).
10338c2ecf20Sopenharmony_ci		 * Rule: The nodeid can only be derived from the handle (and not
10348c2ecf20Sopenharmony_ci		 * htid).
10358c2ecf20Sopenharmony_ci		 * Rule: if the handle specified zero for the node id example
10368c2ecf20Sopenharmony_ci		 * 0x60000000, then pick a new nodeid from the pool of IDs
10378c2ecf20Sopenharmony_ci		 * this hash table has been allocating from.
10388c2ecf20Sopenharmony_ci		 * If OTOH it is specified (i.e for example the user passed a
10398c2ecf20Sopenharmony_ci		 * handle such as 0x60000123), then we use it generate our final
10408c2ecf20Sopenharmony_ci		 * handle which is used to uniquely identify the match entry.
10418c2ecf20Sopenharmony_ci		 */
10428c2ecf20Sopenharmony_ci		if (!TC_U32_NODE(handle)) {
10438c2ecf20Sopenharmony_ci			handle = gen_new_kid(ht, htid);
10448c2ecf20Sopenharmony_ci		} else {
10458c2ecf20Sopenharmony_ci			handle = htid | TC_U32_NODE(handle);
10468c2ecf20Sopenharmony_ci			err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
10478c2ecf20Sopenharmony_ci					    handle, GFP_KERNEL);
10488c2ecf20Sopenharmony_ci			if (err)
10498c2ecf20Sopenharmony_ci				return err;
10508c2ecf20Sopenharmony_ci		}
10518c2ecf20Sopenharmony_ci	} else {
10528c2ecf20Sopenharmony_ci		/* The user did not give us a handle; lets just generate one
10538c2ecf20Sopenharmony_ci		 * from the table's pool of nodeids.
10548c2ecf20Sopenharmony_ci		 */
10558c2ecf20Sopenharmony_ci		handle = gen_new_kid(ht, htid);
10568c2ecf20Sopenharmony_ci	}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	if (tb[TCA_U32_SEL] == NULL) {
10598c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Selector not specified");
10608c2ecf20Sopenharmony_ci		err = -EINVAL;
10618c2ecf20Sopenharmony_ci		goto erridr;
10628c2ecf20Sopenharmony_ci	}
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	s = nla_data(tb[TCA_U32_SEL]);
10658c2ecf20Sopenharmony_ci	sel_size = struct_size(s, keys, s->nkeys);
10668c2ecf20Sopenharmony_ci	if (nla_len(tb[TCA_U32_SEL]) < sel_size) {
10678c2ecf20Sopenharmony_ci		err = -EINVAL;
10688c2ecf20Sopenharmony_ci		goto erridr;
10698c2ecf20Sopenharmony_ci	}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci	n = kzalloc(struct_size(n, sel.keys, s->nkeys), GFP_KERNEL);
10728c2ecf20Sopenharmony_ci	if (n == NULL) {
10738c2ecf20Sopenharmony_ci		err = -ENOBUFS;
10748c2ecf20Sopenharmony_ci		goto erridr;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
10788c2ecf20Sopenharmony_ci	n->pf = __alloc_percpu(struct_size(n->pf, kcnts, s->nkeys),
10798c2ecf20Sopenharmony_ci			       __alignof__(struct tc_u32_pcnt));
10808c2ecf20Sopenharmony_ci	if (!n->pf) {
10818c2ecf20Sopenharmony_ci		err = -ENOBUFS;
10828c2ecf20Sopenharmony_ci		goto errfree;
10838c2ecf20Sopenharmony_ci	}
10848c2ecf20Sopenharmony_ci#endif
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_ci	memcpy(&n->sel, s, sel_size);
10878c2ecf20Sopenharmony_ci	RCU_INIT_POINTER(n->ht_up, ht);
10888c2ecf20Sopenharmony_ci	n->handle = handle;
10898c2ecf20Sopenharmony_ci	n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
10908c2ecf20Sopenharmony_ci	n->flags = flags;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	err = tcf_exts_init(&n->exts, net, TCA_U32_ACT, TCA_U32_POLICE);
10938c2ecf20Sopenharmony_ci	if (err < 0)
10948c2ecf20Sopenharmony_ci		goto errout;
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
10978c2ecf20Sopenharmony_ci	n->pcpu_success = alloc_percpu(u32);
10988c2ecf20Sopenharmony_ci	if (!n->pcpu_success) {
10998c2ecf20Sopenharmony_ci		err = -ENOMEM;
11008c2ecf20Sopenharmony_ci		goto errout;
11018c2ecf20Sopenharmony_ci	}
11028c2ecf20Sopenharmony_ci
11038c2ecf20Sopenharmony_ci	if (tb[TCA_U32_MARK]) {
11048c2ecf20Sopenharmony_ci		struct tc_u32_mark *mark;
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci		mark = nla_data(tb[TCA_U32_MARK]);
11078c2ecf20Sopenharmony_ci		n->val = mark->val;
11088c2ecf20Sopenharmony_ci		n->mask = mark->mask;
11098c2ecf20Sopenharmony_ci	}
11108c2ecf20Sopenharmony_ci#endif
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	err = u32_set_parms(net, tp, base, n, tb, tca[TCA_RATE], ovr,
11138c2ecf20Sopenharmony_ci			    extack);
11148c2ecf20Sopenharmony_ci	if (err == 0) {
11158c2ecf20Sopenharmony_ci		struct tc_u_knode __rcu **ins;
11168c2ecf20Sopenharmony_ci		struct tc_u_knode *pins;
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		err = u32_replace_hw_knode(tp, n, flags, extack);
11198c2ecf20Sopenharmony_ci		if (err)
11208c2ecf20Sopenharmony_ci			goto errhw;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		if (!tc_in_hw(n->flags))
11238c2ecf20Sopenharmony_ci			n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci		ins = &ht->ht[TC_U32_HASH(handle)];
11268c2ecf20Sopenharmony_ci		for (pins = rtnl_dereference(*ins); pins;
11278c2ecf20Sopenharmony_ci		     ins = &pins->next, pins = rtnl_dereference(*ins))
11288c2ecf20Sopenharmony_ci			if (TC_U32_NODE(handle) < TC_U32_NODE(pins->handle))
11298c2ecf20Sopenharmony_ci				break;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(n->next, pins);
11328c2ecf20Sopenharmony_ci		rcu_assign_pointer(*ins, n);
11338c2ecf20Sopenharmony_ci		tp_c->knodes++;
11348c2ecf20Sopenharmony_ci		*arg = n;
11358c2ecf20Sopenharmony_ci		return 0;
11368c2ecf20Sopenharmony_ci	}
11378c2ecf20Sopenharmony_ci
11388c2ecf20Sopenharmony_cierrhw:
11398c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
11408c2ecf20Sopenharmony_ci	free_percpu(n->pcpu_success);
11418c2ecf20Sopenharmony_ci#endif
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_cierrout:
11448c2ecf20Sopenharmony_ci	tcf_exts_destroy(&n->exts);
11458c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
11468c2ecf20Sopenharmony_cierrfree:
11478c2ecf20Sopenharmony_ci	free_percpu(n->pf);
11488c2ecf20Sopenharmony_ci#endif
11498c2ecf20Sopenharmony_ci	kfree(n);
11508c2ecf20Sopenharmony_cierridr:
11518c2ecf20Sopenharmony_ci	idr_remove(&ht->handle_idr, handle);
11528c2ecf20Sopenharmony_ci	return err;
11538c2ecf20Sopenharmony_ci}
11548c2ecf20Sopenharmony_ci
11558c2ecf20Sopenharmony_cistatic void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg,
11568c2ecf20Sopenharmony_ci		     bool rtnl_held)
11578c2ecf20Sopenharmony_ci{
11588c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
11598c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
11608c2ecf20Sopenharmony_ci	struct tc_u_knode *n;
11618c2ecf20Sopenharmony_ci	unsigned int h;
11628c2ecf20Sopenharmony_ci
11638c2ecf20Sopenharmony_ci	if (arg->stop)
11648c2ecf20Sopenharmony_ci		return;
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
11678c2ecf20Sopenharmony_ci	     ht;
11688c2ecf20Sopenharmony_ci	     ht = rtnl_dereference(ht->next)) {
11698c2ecf20Sopenharmony_ci		if (ht->prio != tp->prio)
11708c2ecf20Sopenharmony_ci			continue;
11718c2ecf20Sopenharmony_ci		if (arg->count >= arg->skip) {
11728c2ecf20Sopenharmony_ci			if (arg->fn(tp, ht, arg) < 0) {
11738c2ecf20Sopenharmony_ci				arg->stop = 1;
11748c2ecf20Sopenharmony_ci				return;
11758c2ecf20Sopenharmony_ci			}
11768c2ecf20Sopenharmony_ci		}
11778c2ecf20Sopenharmony_ci		arg->count++;
11788c2ecf20Sopenharmony_ci		for (h = 0; h <= ht->divisor; h++) {
11798c2ecf20Sopenharmony_ci			for (n = rtnl_dereference(ht->ht[h]);
11808c2ecf20Sopenharmony_ci			     n;
11818c2ecf20Sopenharmony_ci			     n = rtnl_dereference(n->next)) {
11828c2ecf20Sopenharmony_ci				if (arg->count < arg->skip) {
11838c2ecf20Sopenharmony_ci					arg->count++;
11848c2ecf20Sopenharmony_ci					continue;
11858c2ecf20Sopenharmony_ci				}
11868c2ecf20Sopenharmony_ci				if (arg->fn(tp, n, arg) < 0) {
11878c2ecf20Sopenharmony_ci					arg->stop = 1;
11888c2ecf20Sopenharmony_ci					return;
11898c2ecf20Sopenharmony_ci				}
11908c2ecf20Sopenharmony_ci				arg->count++;
11918c2ecf20Sopenharmony_ci			}
11928c2ecf20Sopenharmony_ci		}
11938c2ecf20Sopenharmony_ci	}
11948c2ecf20Sopenharmony_ci}
11958c2ecf20Sopenharmony_ci
11968c2ecf20Sopenharmony_cistatic int u32_reoffload_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
11978c2ecf20Sopenharmony_ci			       bool add, flow_setup_cb_t *cb, void *cb_priv,
11988c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
11998c2ecf20Sopenharmony_ci{
12008c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
12018c2ecf20Sopenharmony_ci	int err;
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, ht->flags, extack);
12048c2ecf20Sopenharmony_ci	cls_u32.command = add ? TC_CLSU32_NEW_HNODE : TC_CLSU32_DELETE_HNODE;
12058c2ecf20Sopenharmony_ci	cls_u32.hnode.divisor = ht->divisor;
12068c2ecf20Sopenharmony_ci	cls_u32.hnode.handle = ht->handle;
12078c2ecf20Sopenharmony_ci	cls_u32.hnode.prio = ht->prio;
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci	err = cb(TC_SETUP_CLSU32, &cls_u32, cb_priv);
12108c2ecf20Sopenharmony_ci	if (err && add && tc_skip_sw(ht->flags))
12118c2ecf20Sopenharmony_ci		return err;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci	return 0;
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_cistatic int u32_reoffload_knode(struct tcf_proto *tp, struct tc_u_knode *n,
12178c2ecf20Sopenharmony_ci			       bool add, flow_setup_cb_t *cb, void *cb_priv,
12188c2ecf20Sopenharmony_ci			       struct netlink_ext_ack *extack)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
12218c2ecf20Sopenharmony_ci	struct tcf_block *block = tp->chain->block;
12228c2ecf20Sopenharmony_ci	struct tc_cls_u32_offload cls_u32 = {};
12238c2ecf20Sopenharmony_ci	int err;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
12268c2ecf20Sopenharmony_ci	cls_u32.command = add ?
12278c2ecf20Sopenharmony_ci		TC_CLSU32_REPLACE_KNODE : TC_CLSU32_DELETE_KNODE;
12288c2ecf20Sopenharmony_ci	cls_u32.knode.handle = n->handle;
12298c2ecf20Sopenharmony_ci
12308c2ecf20Sopenharmony_ci	if (add) {
12318c2ecf20Sopenharmony_ci		cls_u32.knode.fshift = n->fshift;
12328c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
12338c2ecf20Sopenharmony_ci		cls_u32.knode.val = n->val;
12348c2ecf20Sopenharmony_ci		cls_u32.knode.mask = n->mask;
12358c2ecf20Sopenharmony_ci#else
12368c2ecf20Sopenharmony_ci		cls_u32.knode.val = 0;
12378c2ecf20Sopenharmony_ci		cls_u32.knode.mask = 0;
12388c2ecf20Sopenharmony_ci#endif
12398c2ecf20Sopenharmony_ci		cls_u32.knode.sel = &n->sel;
12408c2ecf20Sopenharmony_ci		cls_u32.knode.res = &n->res;
12418c2ecf20Sopenharmony_ci		cls_u32.knode.exts = &n->exts;
12428c2ecf20Sopenharmony_ci		if (n->ht_down)
12438c2ecf20Sopenharmony_ci			cls_u32.knode.link_handle = ht->handle;
12448c2ecf20Sopenharmony_ci	}
12458c2ecf20Sopenharmony_ci
12468c2ecf20Sopenharmony_ci	err = tc_setup_cb_reoffload(block, tp, add, cb, TC_SETUP_CLSU32,
12478c2ecf20Sopenharmony_ci				    &cls_u32, cb_priv, &n->flags,
12488c2ecf20Sopenharmony_ci				    &n->in_hw_count);
12498c2ecf20Sopenharmony_ci	if (err)
12508c2ecf20Sopenharmony_ci		return err;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci	return 0;
12538c2ecf20Sopenharmony_ci}
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_cistatic int u32_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb,
12568c2ecf20Sopenharmony_ci			 void *cb_priv, struct netlink_ext_ack *extack)
12578c2ecf20Sopenharmony_ci{
12588c2ecf20Sopenharmony_ci	struct tc_u_common *tp_c = tp->data;
12598c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht;
12608c2ecf20Sopenharmony_ci	struct tc_u_knode *n;
12618c2ecf20Sopenharmony_ci	unsigned int h;
12628c2ecf20Sopenharmony_ci	int err;
12638c2ecf20Sopenharmony_ci
12648c2ecf20Sopenharmony_ci	for (ht = rtnl_dereference(tp_c->hlist);
12658c2ecf20Sopenharmony_ci	     ht;
12668c2ecf20Sopenharmony_ci	     ht = rtnl_dereference(ht->next)) {
12678c2ecf20Sopenharmony_ci		if (ht->prio != tp->prio)
12688c2ecf20Sopenharmony_ci			continue;
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci		/* When adding filters to a new dev, try to offload the
12718c2ecf20Sopenharmony_ci		 * hashtable first. When removing, do the filters before the
12728c2ecf20Sopenharmony_ci		 * hashtable.
12738c2ecf20Sopenharmony_ci		 */
12748c2ecf20Sopenharmony_ci		if (add && !tc_skip_hw(ht->flags)) {
12758c2ecf20Sopenharmony_ci			err = u32_reoffload_hnode(tp, ht, add, cb, cb_priv,
12768c2ecf20Sopenharmony_ci						  extack);
12778c2ecf20Sopenharmony_ci			if (err)
12788c2ecf20Sopenharmony_ci				return err;
12798c2ecf20Sopenharmony_ci		}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_ci		for (h = 0; h <= ht->divisor; h++) {
12828c2ecf20Sopenharmony_ci			for (n = rtnl_dereference(ht->ht[h]);
12838c2ecf20Sopenharmony_ci			     n;
12848c2ecf20Sopenharmony_ci			     n = rtnl_dereference(n->next)) {
12858c2ecf20Sopenharmony_ci				if (tc_skip_hw(n->flags))
12868c2ecf20Sopenharmony_ci					continue;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci				err = u32_reoffload_knode(tp, n, add, cb,
12898c2ecf20Sopenharmony_ci							  cb_priv, extack);
12908c2ecf20Sopenharmony_ci				if (err)
12918c2ecf20Sopenharmony_ci					return err;
12928c2ecf20Sopenharmony_ci			}
12938c2ecf20Sopenharmony_ci		}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_ci		if (!add && !tc_skip_hw(ht->flags))
12968c2ecf20Sopenharmony_ci			u32_reoffload_hnode(tp, ht, add, cb, cb_priv, extack);
12978c2ecf20Sopenharmony_ci	}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci	return 0;
13008c2ecf20Sopenharmony_ci}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_cistatic void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
13038c2ecf20Sopenharmony_ci			   unsigned long base)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	struct tc_u_knode *n = fh;
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	if (n && n->res.classid == classid) {
13088c2ecf20Sopenharmony_ci		if (cl)
13098c2ecf20Sopenharmony_ci			__tcf_bind_filter(q, &n->res, base);
13108c2ecf20Sopenharmony_ci		else
13118c2ecf20Sopenharmony_ci			__tcf_unbind_filter(q, &n->res);
13128c2ecf20Sopenharmony_ci	}
13138c2ecf20Sopenharmony_ci}
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_cistatic int u32_dump(struct net *net, struct tcf_proto *tp, void *fh,
13168c2ecf20Sopenharmony_ci		    struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
13178c2ecf20Sopenharmony_ci{
13188c2ecf20Sopenharmony_ci	struct tc_u_knode *n = fh;
13198c2ecf20Sopenharmony_ci	struct tc_u_hnode *ht_up, *ht_down;
13208c2ecf20Sopenharmony_ci	struct nlattr *nest;
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	if (n == NULL)
13238c2ecf20Sopenharmony_ci		return skb->len;
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	t->tcm_handle = n->handle;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
13288c2ecf20Sopenharmony_ci	if (nest == NULL)
13298c2ecf20Sopenharmony_ci		goto nla_put_failure;
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	if (TC_U32_KEY(n->handle) == 0) {
13328c2ecf20Sopenharmony_ci		struct tc_u_hnode *ht = fh;
13338c2ecf20Sopenharmony_ci		u32 divisor = ht->divisor + 1;
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci		if (nla_put_u32(skb, TCA_U32_DIVISOR, divisor))
13368c2ecf20Sopenharmony_ci			goto nla_put_failure;
13378c2ecf20Sopenharmony_ci	} else {
13388c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
13398c2ecf20Sopenharmony_ci		struct tc_u32_pcnt *gpf;
13408c2ecf20Sopenharmony_ci		int cpu;
13418c2ecf20Sopenharmony_ci#endif
13428c2ecf20Sopenharmony_ci
13438c2ecf20Sopenharmony_ci		if (nla_put(skb, TCA_U32_SEL, struct_size(&n->sel, keys, n->sel.nkeys),
13448c2ecf20Sopenharmony_ci			    &n->sel))
13458c2ecf20Sopenharmony_ci			goto nla_put_failure;
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci		ht_up = rtnl_dereference(n->ht_up);
13488c2ecf20Sopenharmony_ci		if (ht_up) {
13498c2ecf20Sopenharmony_ci			u32 htid = n->handle & 0xFFFFF000;
13508c2ecf20Sopenharmony_ci			if (nla_put_u32(skb, TCA_U32_HASH, htid))
13518c2ecf20Sopenharmony_ci				goto nla_put_failure;
13528c2ecf20Sopenharmony_ci		}
13538c2ecf20Sopenharmony_ci		if (n->res.classid &&
13548c2ecf20Sopenharmony_ci		    nla_put_u32(skb, TCA_U32_CLASSID, n->res.classid))
13558c2ecf20Sopenharmony_ci			goto nla_put_failure;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci		ht_down = rtnl_dereference(n->ht_down);
13588c2ecf20Sopenharmony_ci		if (ht_down &&
13598c2ecf20Sopenharmony_ci		    nla_put_u32(skb, TCA_U32_LINK, ht_down->handle))
13608c2ecf20Sopenharmony_ci			goto nla_put_failure;
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci		if (n->flags && nla_put_u32(skb, TCA_U32_FLAGS, n->flags))
13638c2ecf20Sopenharmony_ci			goto nla_put_failure;
13648c2ecf20Sopenharmony_ci
13658c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_MARK
13668c2ecf20Sopenharmony_ci		if ((n->val || n->mask)) {
13678c2ecf20Sopenharmony_ci			struct tc_u32_mark mark = {.val = n->val,
13688c2ecf20Sopenharmony_ci						   .mask = n->mask,
13698c2ecf20Sopenharmony_ci						   .success = 0};
13708c2ecf20Sopenharmony_ci			int cpum;
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci			for_each_possible_cpu(cpum) {
13738c2ecf20Sopenharmony_ci				__u32 cnt = *per_cpu_ptr(n->pcpu_success, cpum);
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci				mark.success += cnt;
13768c2ecf20Sopenharmony_ci			}
13778c2ecf20Sopenharmony_ci
13788c2ecf20Sopenharmony_ci			if (nla_put(skb, TCA_U32_MARK, sizeof(mark), &mark))
13798c2ecf20Sopenharmony_ci				goto nla_put_failure;
13808c2ecf20Sopenharmony_ci		}
13818c2ecf20Sopenharmony_ci#endif
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci		if (tcf_exts_dump(skb, &n->exts) < 0)
13848c2ecf20Sopenharmony_ci			goto nla_put_failure;
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_ci		if (n->ifindex) {
13878c2ecf20Sopenharmony_ci			struct net_device *dev;
13888c2ecf20Sopenharmony_ci			dev = __dev_get_by_index(net, n->ifindex);
13898c2ecf20Sopenharmony_ci			if (dev && nla_put_string(skb, TCA_U32_INDEV, dev->name))
13908c2ecf20Sopenharmony_ci				goto nla_put_failure;
13918c2ecf20Sopenharmony_ci		}
13928c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
13938c2ecf20Sopenharmony_ci		gpf = kzalloc(struct_size(gpf, kcnts, n->sel.nkeys), GFP_KERNEL);
13948c2ecf20Sopenharmony_ci		if (!gpf)
13958c2ecf20Sopenharmony_ci			goto nla_put_failure;
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		for_each_possible_cpu(cpu) {
13988c2ecf20Sopenharmony_ci			int i;
13998c2ecf20Sopenharmony_ci			struct tc_u32_pcnt *pf = per_cpu_ptr(n->pf, cpu);
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci			gpf->rcnt += pf->rcnt;
14028c2ecf20Sopenharmony_ci			gpf->rhit += pf->rhit;
14038c2ecf20Sopenharmony_ci			for (i = 0; i < n->sel.nkeys; i++)
14048c2ecf20Sopenharmony_ci				gpf->kcnts[i] += pf->kcnts[i];
14058c2ecf20Sopenharmony_ci		}
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci		if (nla_put_64bit(skb, TCA_U32_PCNT, struct_size(gpf, kcnts, n->sel.nkeys),
14088c2ecf20Sopenharmony_ci				  gpf, TCA_U32_PAD)) {
14098c2ecf20Sopenharmony_ci			kfree(gpf);
14108c2ecf20Sopenharmony_ci			goto nla_put_failure;
14118c2ecf20Sopenharmony_ci		}
14128c2ecf20Sopenharmony_ci		kfree(gpf);
14138c2ecf20Sopenharmony_ci#endif
14148c2ecf20Sopenharmony_ci	}
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci	if (TC_U32_KEY(n->handle))
14198c2ecf20Sopenharmony_ci		if (tcf_exts_dump_stats(skb, &n->exts) < 0)
14208c2ecf20Sopenharmony_ci			goto nla_put_failure;
14218c2ecf20Sopenharmony_ci	return skb->len;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_cinla_put_failure:
14248c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
14258c2ecf20Sopenharmony_ci	return -1;
14268c2ecf20Sopenharmony_ci}
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_cistatic struct tcf_proto_ops cls_u32_ops __read_mostly = {
14298c2ecf20Sopenharmony_ci	.kind		=	"u32",
14308c2ecf20Sopenharmony_ci	.classify	=	u32_classify,
14318c2ecf20Sopenharmony_ci	.init		=	u32_init,
14328c2ecf20Sopenharmony_ci	.destroy	=	u32_destroy,
14338c2ecf20Sopenharmony_ci	.get		=	u32_get,
14348c2ecf20Sopenharmony_ci	.change		=	u32_change,
14358c2ecf20Sopenharmony_ci	.delete		=	u32_delete,
14368c2ecf20Sopenharmony_ci	.walk		=	u32_walk,
14378c2ecf20Sopenharmony_ci	.reoffload	=	u32_reoffload,
14388c2ecf20Sopenharmony_ci	.dump		=	u32_dump,
14398c2ecf20Sopenharmony_ci	.bind_class	=	u32_bind_class,
14408c2ecf20Sopenharmony_ci	.owner		=	THIS_MODULE,
14418c2ecf20Sopenharmony_ci};
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_cistatic int __init init_u32(void)
14448c2ecf20Sopenharmony_ci{
14458c2ecf20Sopenharmony_ci	int i, ret;
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	pr_info("u32 classifier\n");
14488c2ecf20Sopenharmony_ci#ifdef CONFIG_CLS_U32_PERF
14498c2ecf20Sopenharmony_ci	pr_info("    Performance counters on\n");
14508c2ecf20Sopenharmony_ci#endif
14518c2ecf20Sopenharmony_ci	pr_info("    input device check on\n");
14528c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_CLS_ACT
14538c2ecf20Sopenharmony_ci	pr_info("    Actions configured\n");
14548c2ecf20Sopenharmony_ci#endif
14558c2ecf20Sopenharmony_ci	tc_u_common_hash = kvmalloc_array(U32_HASH_SIZE,
14568c2ecf20Sopenharmony_ci					  sizeof(struct hlist_head),
14578c2ecf20Sopenharmony_ci					  GFP_KERNEL);
14588c2ecf20Sopenharmony_ci	if (!tc_u_common_hash)
14598c2ecf20Sopenharmony_ci		return -ENOMEM;
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci	for (i = 0; i < U32_HASH_SIZE; i++)
14628c2ecf20Sopenharmony_ci		INIT_HLIST_HEAD(&tc_u_common_hash[i]);
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	ret = register_tcf_proto_ops(&cls_u32_ops);
14658c2ecf20Sopenharmony_ci	if (ret)
14668c2ecf20Sopenharmony_ci		kvfree(tc_u_common_hash);
14678c2ecf20Sopenharmony_ci	return ret;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_cistatic void __exit exit_u32(void)
14718c2ecf20Sopenharmony_ci{
14728c2ecf20Sopenharmony_ci	unregister_tcf_proto_ops(&cls_u32_ops);
14738c2ecf20Sopenharmony_ci	kvfree(tc_u_common_hash);
14748c2ecf20Sopenharmony_ci}
14758c2ecf20Sopenharmony_ci
14768c2ecf20Sopenharmony_cimodule_init(init_u32)
14778c2ecf20Sopenharmony_cimodule_exit(exit_u32)
14788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1479