12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * net/sched/cls_u32.c Ugly (or Universal) 32bit key Packet Classifier.
41da177e4SLinus Torvalds *
51da177e4SLinus Torvalds * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
61da177e4SLinus Torvalds *
71da177e4SLinus Torvalds * The filters are packed to hash tables of key nodes
81da177e4SLinus Torvalds * with a set of 32bit key/mask pairs at every node.
91da177e4SLinus Torvalds * Nodes reference next level hash tables etc.
101da177e4SLinus Torvalds *
111da177e4SLinus Torvalds * This scheme is the best universal classifier I managed to
121da177e4SLinus Torvalds * invent; it is not super-fast, but it is not slow (provided you
131da177e4SLinus Torvalds * program it correctly), and general enough. And its relative
141da177e4SLinus Torvalds * speed grows as the number of rules becomes larger.
151da177e4SLinus Torvalds *
161da177e4SLinus Torvalds * It seems that it represents the best middle point between
171da177e4SLinus Torvalds * speed and manageability both by human and by machine.
181da177e4SLinus Torvalds *
191da177e4SLinus Torvalds * It is especially useful for link sharing combined with QoS;
201da177e4SLinus Torvalds * pure RSVP doesn't need such a general approach and can use
211da177e4SLinus Torvalds * much simpler (and faster) schemes, sort of cls_rsvp.c.
221da177e4SLinus Torvalds *
231da177e4SLinus Torvalds * nfmark match added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro>
241da177e4SLinus Torvalds */
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds #include <linux/module.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
281da177e4SLinus Torvalds #include <linux/types.h>
291da177e4SLinus Torvalds #include <linux/kernel.h>
301da177e4SLinus Torvalds #include <linux/string.h>
311da177e4SLinus Torvalds #include <linux/errno.h>
321ce87720SJohn Fastabend #include <linux/percpu.h>
331da177e4SLinus Torvalds #include <linux/rtnetlink.h>
341da177e4SLinus Torvalds #include <linux/skbuff.h>
357801db8aSCong Wang #include <linux/bitmap.h>
363cd904ecSWANG Cong #include <linux/netdevice.h>
373cd904ecSWANG Cong #include <linux/hash.h>
380ba48053SPatrick McHardy #include <net/netlink.h>
391da177e4SLinus Torvalds #include <net/act_api.h>
401da177e4SLinus Torvalds #include <net/pkt_cls.h>
41e7614370SCong Wang #include <linux/idr.h>
429f3101dcSPedro Tammela #include <net/tc_wrapper.h>
431da177e4SLinus Torvalds
44cc7ec456SEric Dumazet struct tc_u_knode {
451ce87720SJohn Fastabend struct tc_u_knode __rcu *next;
461da177e4SLinus Torvalds u32 handle;
471ce87720SJohn Fastabend struct tc_u_hnode __rcu *ht_up;
481da177e4SLinus Torvalds struct tcf_exts exts;
492519a602SWANG Cong int ifindex;
501da177e4SLinus Torvalds u8 fshift;
511da177e4SLinus Torvalds struct tcf_result res;
521ce87720SJohn Fastabend struct tc_u_hnode __rcu *ht_down;
531da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
54459d5f62SJohn Fastabend struct tc_u32_pcnt __percpu *pf;
551da177e4SLinus Torvalds #endif
569e8ce79cSJohn Fastabend u32 flags;
57530d9951SJohn Hurley unsigned int in_hw_count;
581da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_MARK
59459d5f62SJohn Fastabend u32 val;
60459d5f62SJohn Fastabend u32 mask;
61459d5f62SJohn Fastabend u32 __percpu *pcpu_success;
621da177e4SLinus Torvalds #endif
63aaa908ffSCong Wang struct rcu_work rwork;
644e2840eeSJohn Fastabend /* The 'sel' field MUST be the last field in structure to allow for
654e2840eeSJohn Fastabend * tc_u32_keys allocated at end of structure.
664e2840eeSJohn Fastabend */
674e2840eeSJohn Fastabend struct tc_u32_sel sel;
681da177e4SLinus Torvalds };
691da177e4SLinus Torvalds
70cc7ec456SEric Dumazet struct tc_u_hnode {
711ce87720SJohn Fastabend struct tc_u_hnode __rcu *next;
721da177e4SLinus Torvalds u32 handle;
731da177e4SLinus Torvalds u32 prio;
749fd5661aSPedro Tammela refcount_t refcnt;
75cc7ec456SEric Dumazet unsigned int divisor;
76e7614370SCong Wang struct idr handle_idr;
77b44ef845SAl Viro bool is_root;
781ce87720SJohn Fastabend struct rcu_head rcu;
79f40fe58dSJakub Kicinski u32 flags;
805778d39dSWANG Cong /* The 'ht' field MUST be the last field in structure to allow for
815778d39dSWANG Cong * more entries allocated at end of structure.
825778d39dSWANG Cong */
83d61491a5SGustavo A. R. Silva struct tc_u_knode __rcu *ht[];
841da177e4SLinus Torvalds };
851da177e4SLinus Torvalds
86cc7ec456SEric Dumazet struct tc_u_common {
871ce87720SJohn Fastabend struct tc_u_hnode __rcu *hlist;
88339c21d7SJiri Pirko void *ptr;
899fd5661aSPedro Tammela refcount_t refcnt;
90e7614370SCong Wang struct idr handle_idr;
913cd904ecSWANG Cong struct hlist_node hnode;
92b245d32cSAl Viro long knodes;
931da177e4SLinus Torvalds };
941da177e4SLinus Torvalds
handle2id(u32 h)95*789f9963SAlexandre Ferrieux static u32 handle2id(u32 h)
96*789f9963SAlexandre Ferrieux {
97*789f9963SAlexandre Ferrieux return ((h & 0x80000000) ? ((h >> 20) & 0x7FF) : h);
98*789f9963SAlexandre Ferrieux }
99*789f9963SAlexandre Ferrieux
id2handle(u32 id)100*789f9963SAlexandre Ferrieux static u32 id2handle(u32 id)
101*789f9963SAlexandre Ferrieux {
102*789f9963SAlexandre Ferrieux return (id | 0x800U) << 20;
103*789f9963SAlexandre Ferrieux }
104*789f9963SAlexandre Ferrieux
u32_hash_fold(__be32 key,const struct tc_u32_sel * sel,u8 fshift)105cc7ec456SEric Dumazet static inline unsigned int u32_hash_fold(__be32 key,
106cc7ec456SEric Dumazet const struct tc_u32_sel *sel,
107cc7ec456SEric Dumazet u8 fshift)
1081da177e4SLinus Torvalds {
109cc7ec456SEric Dumazet unsigned int h = ntohl(key & sel->hmask) >> fshift;
1101da177e4SLinus Torvalds
1111da177e4SLinus Torvalds return h;
1121da177e4SLinus Torvalds }
1131da177e4SLinus Torvalds
u32_classify(struct sk_buff * skb,const struct tcf_proto * tp,struct tcf_result * res)1149f3101dcSPedro Tammela TC_INDIRECT_SCOPE int u32_classify(struct sk_buff *skb,
1159f3101dcSPedro Tammela const struct tcf_proto *tp,
1165a7a5555SJamal Hadi Salim struct tcf_result *res)
1171da177e4SLinus Torvalds {
1181da177e4SLinus Torvalds struct {
1191da177e4SLinus Torvalds struct tc_u_knode *knode;
120fbc2e7d9SChangli Gao unsigned int off;
1211da177e4SLinus Torvalds } stack[TC_U32_MAXDEPTH];
1221da177e4SLinus Torvalds
1231ce87720SJohn Fastabend struct tc_u_hnode *ht = rcu_dereference_bh(tp->root);
124fbc2e7d9SChangli Gao unsigned int off = skb_network_offset(skb);
1251da177e4SLinus Torvalds struct tc_u_knode *n;
1261da177e4SLinus Torvalds int sdepth = 0;
1271da177e4SLinus Torvalds int off2 = 0;
1281da177e4SLinus Torvalds int sel = 0;
1291da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
1301da177e4SLinus Torvalds int j;
1311da177e4SLinus Torvalds #endif
1321da177e4SLinus Torvalds int i, r;
1331da177e4SLinus Torvalds
1341da177e4SLinus Torvalds next_ht:
1351ce87720SJohn Fastabend n = rcu_dereference_bh(ht->ht[sel]);
1361da177e4SLinus Torvalds
1371da177e4SLinus Torvalds next_knode:
1381da177e4SLinus Torvalds if (n) {
1391da177e4SLinus Torvalds struct tc_u32_key *key = n->sel.keys;
1401da177e4SLinus Torvalds
1411da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
142459d5f62SJohn Fastabend __this_cpu_inc(n->pf->rcnt);
1431da177e4SLinus Torvalds j = 0;
1441da177e4SLinus Torvalds #endif
1451da177e4SLinus Torvalds
146d34e3e18SSamudrala, Sridhar if (tc_skip_sw(n->flags)) {
147d34e3e18SSamudrala, Sridhar n = rcu_dereference_bh(n->next);
148d34e3e18SSamudrala, Sridhar goto next_knode;
149d34e3e18SSamudrala, Sridhar }
150d34e3e18SSamudrala, Sridhar
1511da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_MARK
152459d5f62SJohn Fastabend if ((skb->mark & n->mask) != n->val) {
1531ce87720SJohn Fastabend n = rcu_dereference_bh(n->next);
1541da177e4SLinus Torvalds goto next_knode;
1551da177e4SLinus Torvalds } else {
156459d5f62SJohn Fastabend __this_cpu_inc(*n->pcpu_success);
1571da177e4SLinus Torvalds }
1581da177e4SLinus Torvalds #endif
1591da177e4SLinus Torvalds
1601da177e4SLinus Torvalds for (i = n->sel.nkeys; i > 0; i--, key++) {
16166d50d25Sstephen hemminger int toff = off + key->off + (off2 & key->offmask);
16286fce3baSstephen hemminger __be32 *data, hdata;
1631da177e4SLinus Torvalds
1644e18b3edSDan Carpenter if (skb_headroom(skb) + toff > INT_MAX)
16566d50d25Sstephen hemminger goto out;
16666d50d25Sstephen hemminger
16786fce3baSstephen hemminger data = skb_header_pointer(skb, toff, 4, &hdata);
168fbc2e7d9SChangli Gao if (!data)
169fbc2e7d9SChangli Gao goto out;
170fbc2e7d9SChangli Gao if ((*data ^ key->val) & key->mask) {
1711ce87720SJohn Fastabend n = rcu_dereference_bh(n->next);
1721da177e4SLinus Torvalds goto next_knode;
1731da177e4SLinus Torvalds }
1741da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
175459d5f62SJohn Fastabend __this_cpu_inc(n->pf->kcnts[j]);
1761da177e4SLinus Torvalds j++;
1771da177e4SLinus Torvalds #endif
1781da177e4SLinus Torvalds }
1791ce87720SJohn Fastabend
1801ce87720SJohn Fastabend ht = rcu_dereference_bh(n->ht_down);
1811ce87720SJohn Fastabend if (!ht) {
1821da177e4SLinus Torvalds check_terminal:
1831da177e4SLinus Torvalds if (n->sel.flags & TC_U32_TERMINAL) {
1841da177e4SLinus Torvalds
1851da177e4SLinus Torvalds *res = n->res;
1862519a602SWANG Cong if (!tcf_match_indev(skb, n->ifindex)) {
1871ce87720SJohn Fastabend n = rcu_dereference_bh(n->next);
1881da177e4SLinus Torvalds goto next_knode;
1891da177e4SLinus Torvalds }
1901da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
191459d5f62SJohn Fastabend __this_cpu_inc(n->pf->rhit);
1921da177e4SLinus Torvalds #endif
1931da177e4SLinus Torvalds r = tcf_exts_exec(skb, &n->exts, res);
1941da177e4SLinus Torvalds if (r < 0) {
1951ce87720SJohn Fastabend n = rcu_dereference_bh(n->next);
1961da177e4SLinus Torvalds goto next_knode;
1971da177e4SLinus Torvalds }
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds return r;
2001da177e4SLinus Torvalds }
2011ce87720SJohn Fastabend n = rcu_dereference_bh(n->next);
2021da177e4SLinus Torvalds goto next_knode;
2031da177e4SLinus Torvalds }
2041da177e4SLinus Torvalds
2051da177e4SLinus Torvalds /* PUSH */
2061da177e4SLinus Torvalds if (sdepth >= TC_U32_MAXDEPTH)
2071da177e4SLinus Torvalds goto deadloop;
2081da177e4SLinus Torvalds stack[sdepth].knode = n;
209fbc2e7d9SChangli Gao stack[sdepth].off = off;
2101da177e4SLinus Torvalds sdepth++;
2111da177e4SLinus Torvalds
2121ce87720SJohn Fastabend ht = rcu_dereference_bh(n->ht_down);
2131da177e4SLinus Torvalds sel = 0;
214fbc2e7d9SChangli Gao if (ht->divisor) {
21586fce3baSstephen hemminger __be32 *data, hdata;
2161da177e4SLinus Torvalds
217fbc2e7d9SChangli Gao data = skb_header_pointer(skb, off + n->sel.hoff, 4,
21886fce3baSstephen hemminger &hdata);
219fbc2e7d9SChangli Gao if (!data)
220fbc2e7d9SChangli Gao goto out;
221fbc2e7d9SChangli Gao sel = ht->divisor & u32_hash_fold(*data, &n->sel,
222fbc2e7d9SChangli Gao n->fshift);
223fbc2e7d9SChangli Gao }
2241da177e4SLinus Torvalds if (!(n->sel.flags & (TC_U32_VAROFFSET | TC_U32_OFFSET | TC_U32_EAT)))
2251da177e4SLinus Torvalds goto next_ht;
2261da177e4SLinus Torvalds
2271da177e4SLinus Torvalds if (n->sel.flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) {
2281da177e4SLinus Torvalds off2 = n->sel.off + 3;
229fbc2e7d9SChangli Gao if (n->sel.flags & TC_U32_VAROFFSET) {
23086fce3baSstephen hemminger __be16 *data, hdata;
231fbc2e7d9SChangli Gao
232fbc2e7d9SChangli Gao data = skb_header_pointer(skb,
233fbc2e7d9SChangli Gao off + n->sel.offoff,
23486fce3baSstephen hemminger 2, &hdata);
235fbc2e7d9SChangli Gao if (!data)
236fbc2e7d9SChangli Gao goto out;
237fbc2e7d9SChangli Gao off2 += ntohs(n->sel.offmask & *data) >>
238fbc2e7d9SChangli Gao n->sel.offshift;
239fbc2e7d9SChangli Gao }
2401da177e4SLinus Torvalds off2 &= ~3;
2411da177e4SLinus Torvalds }
2421da177e4SLinus Torvalds if (n->sel.flags & TC_U32_EAT) {
243fbc2e7d9SChangli Gao off += off2;
2441da177e4SLinus Torvalds off2 = 0;
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds
247fbc2e7d9SChangli Gao if (off < skb->len)
2481da177e4SLinus Torvalds goto next_ht;
2491da177e4SLinus Torvalds }
2501da177e4SLinus Torvalds
2511da177e4SLinus Torvalds /* POP */
2521da177e4SLinus Torvalds if (sdepth--) {
2531da177e4SLinus Torvalds n = stack[sdepth].knode;
2541ce87720SJohn Fastabend ht = rcu_dereference_bh(n->ht_up);
255fbc2e7d9SChangli Gao off = stack[sdepth].off;
2561da177e4SLinus Torvalds goto check_terminal;
2571da177e4SLinus Torvalds }
258fbc2e7d9SChangli Gao out:
2591da177e4SLinus Torvalds return -1;
2601da177e4SLinus Torvalds
2611da177e4SLinus Torvalds deadloop:
262e87cc472SJoe Perches net_warn_ratelimited("cls_u32: dead loop\n");
2631da177e4SLinus Torvalds return -1;
2641da177e4SLinus Torvalds }
2651da177e4SLinus Torvalds
u32_lookup_ht(struct tc_u_common * tp_c,u32 handle)2665a7a5555SJamal Hadi Salim static struct tc_u_hnode *u32_lookup_ht(struct tc_u_common *tp_c, u32 handle)
2671da177e4SLinus Torvalds {
2681da177e4SLinus Torvalds struct tc_u_hnode *ht;
2691da177e4SLinus Torvalds
2701ce87720SJohn Fastabend for (ht = rtnl_dereference(tp_c->hlist);
2711ce87720SJohn Fastabend ht;
2721ce87720SJohn Fastabend ht = rtnl_dereference(ht->next))
2731da177e4SLinus Torvalds if (ht->handle == handle)
2741da177e4SLinus Torvalds break;
2751da177e4SLinus Torvalds
2761da177e4SLinus Torvalds return ht;
2771da177e4SLinus Torvalds }
2781da177e4SLinus Torvalds
u32_lookup_key(struct tc_u_hnode * ht,u32 handle)2795a7a5555SJamal Hadi Salim static struct tc_u_knode *u32_lookup_key(struct tc_u_hnode *ht, u32 handle)
2801da177e4SLinus Torvalds {
281cc7ec456SEric Dumazet unsigned int sel;
2821da177e4SLinus Torvalds struct tc_u_knode *n = NULL;
2831da177e4SLinus Torvalds
2841da177e4SLinus Torvalds sel = TC_U32_HASH(handle);
2851da177e4SLinus Torvalds if (sel > ht->divisor)
2861da177e4SLinus Torvalds goto out;
2871da177e4SLinus Torvalds
2881ce87720SJohn Fastabend for (n = rtnl_dereference(ht->ht[sel]);
2891ce87720SJohn Fastabend n;
2901ce87720SJohn Fastabend n = rtnl_dereference(n->next))
2911da177e4SLinus Torvalds if (n->handle == handle)
2921da177e4SLinus Torvalds break;
2931da177e4SLinus Torvalds out:
2941da177e4SLinus Torvalds return n;
2951da177e4SLinus Torvalds }
2961da177e4SLinus Torvalds
2971da177e4SLinus Torvalds
u32_get(struct tcf_proto * tp,u32 handle)2988113c095SWANG Cong static void *u32_get(struct tcf_proto *tp, u32 handle)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds struct tc_u_hnode *ht;
3011da177e4SLinus Torvalds struct tc_u_common *tp_c = tp->data;
3021da177e4SLinus Torvalds
3031da177e4SLinus Torvalds if (TC_U32_HTID(handle) == TC_U32_ROOT)
3041ce87720SJohn Fastabend ht = rtnl_dereference(tp->root);
3051da177e4SLinus Torvalds else
3061da177e4SLinus Torvalds ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle));
3071da177e4SLinus Torvalds
3081da177e4SLinus Torvalds if (!ht)
3098113c095SWANG Cong return NULL;
3101da177e4SLinus Torvalds
3111da177e4SLinus Torvalds if (TC_U32_KEY(handle) == 0)
3128113c095SWANG Cong return ht;
3131da177e4SLinus Torvalds
3148113c095SWANG Cong return u32_lookup_key(ht, handle);
3151da177e4SLinus Torvalds }
3161da177e4SLinus Torvalds
317ffdc2d9eSMatthew Wilcox /* Protected by rtnl lock */
gen_new_htid(struct tc_u_common * tp_c,struct tc_u_hnode * ptr)318e7614370SCong Wang static u32 gen_new_htid(struct tc_u_common *tp_c, struct tc_u_hnode *ptr)
3191da177e4SLinus Torvalds {
320ffdc2d9eSMatthew Wilcox int id = idr_alloc_cyclic(&tp_c->handle_idr, ptr, 1, 0x7FF, GFP_KERNEL);
321ffdc2d9eSMatthew Wilcox if (id < 0)
322e7614370SCong Wang return 0;
323*789f9963SAlexandre Ferrieux return id2handle(id);
3241da177e4SLinus Torvalds }
3251da177e4SLinus Torvalds
3263cd904ecSWANG Cong static struct hlist_head *tc_u_common_hash;
3273cd904ecSWANG Cong
3283cd904ecSWANG Cong #define U32_HASH_SHIFT 10
3293cd904ecSWANG Cong #define U32_HASH_SIZE (1 << U32_HASH_SHIFT)
3303cd904ecSWANG Cong
tc_u_common_ptr(const struct tcf_proto * tp)331339c21d7SJiri Pirko static void *tc_u_common_ptr(const struct tcf_proto *tp)
332339c21d7SJiri Pirko {
333339c21d7SJiri Pirko struct tcf_block *block = tp->chain->block;
334339c21d7SJiri Pirko
335339c21d7SJiri Pirko /* The block sharing is currently supported only
336339c21d7SJiri Pirko * for classless qdiscs. In that case we use block
337339c21d7SJiri Pirko * for tc_u_common identification. In case the
338339c21d7SJiri Pirko * block is not shared, block->q is a valid pointer
339339c21d7SJiri Pirko * and we can use that. That works for classful qdiscs.
340339c21d7SJiri Pirko */
341339c21d7SJiri Pirko if (tcf_block_shared(block))
342339c21d7SJiri Pirko return block;
343339c21d7SJiri Pirko else
344339c21d7SJiri Pirko return block->q;
345339c21d7SJiri Pirko }
346339c21d7SJiri Pirko
tc_u_hash(void * key)3474895c42fSAl Viro static struct hlist_head *tc_u_hash(void *key)
3483cd904ecSWANG Cong {
3494895c42fSAl Viro return tc_u_common_hash + hash_ptr(key, U32_HASH_SHIFT);
3503cd904ecSWANG Cong }
3513cd904ecSWANG Cong
tc_u_common_find(void * key)3524895c42fSAl Viro static struct tc_u_common *tc_u_common_find(void *key)
3533cd904ecSWANG Cong {
3543cd904ecSWANG Cong struct tc_u_common *tc;
3554895c42fSAl Viro hlist_for_each_entry(tc, tc_u_hash(key), hnode) {
3564895c42fSAl Viro if (tc->ptr == key)
3573cd904ecSWANG Cong return tc;
3583cd904ecSWANG Cong }
3593cd904ecSWANG Cong return NULL;
3603cd904ecSWANG Cong }
3613cd904ecSWANG Cong
u32_init(struct tcf_proto * tp)3621da177e4SLinus Torvalds static int u32_init(struct tcf_proto *tp)
3631da177e4SLinus Torvalds {
3641da177e4SLinus Torvalds struct tc_u_hnode *root_ht;
3654895c42fSAl Viro void *key = tc_u_common_ptr(tp);
3664895c42fSAl Viro struct tc_u_common *tp_c = tc_u_common_find(key);
3671da177e4SLinus Torvalds
368d61491a5SGustavo A. R. Silva root_ht = kzalloc(struct_size(root_ht, ht, 1), GFP_KERNEL);
3691da177e4SLinus Torvalds if (root_ht == NULL)
3701da177e4SLinus Torvalds return -ENOBUFS;
3711da177e4SLinus Torvalds
3729fd5661aSPedro Tammela refcount_set(&root_ht->refcnt, 1);
373*789f9963SAlexandre Ferrieux root_ht->handle = tp_c ? gen_new_htid(tp_c, root_ht) : id2handle(0);
3741da177e4SLinus Torvalds root_ht->prio = tp->prio;
375b44ef845SAl Viro root_ht->is_root = true;
376e7614370SCong Wang idr_init(&root_ht->handle_idr);
3771da177e4SLinus Torvalds
3781da177e4SLinus Torvalds if (tp_c == NULL) {
379c4d49196SGustavo A. R. Silva tp_c = kzalloc(sizeof(*tp_c), GFP_KERNEL);
3801da177e4SLinus Torvalds if (tp_c == NULL) {
3811da177e4SLinus Torvalds kfree(root_ht);
3821da177e4SLinus Torvalds return -ENOBUFS;
3831da177e4SLinus Torvalds }
3849fd5661aSPedro Tammela refcount_set(&tp_c->refcnt, 1);
3854895c42fSAl Viro tp_c->ptr = key;
3863cd904ecSWANG Cong INIT_HLIST_NODE(&tp_c->hnode);
387e7614370SCong Wang idr_init(&tp_c->handle_idr);
3883cd904ecSWANG Cong
3894895c42fSAl Viro hlist_add_head(&tp_c->hnode, tc_u_hash(key));
3909fd5661aSPedro Tammela } else {
3919fd5661aSPedro Tammela refcount_inc(&tp_c->refcnt);
3921da177e4SLinus Torvalds }
3931da177e4SLinus Torvalds
3941ce87720SJohn Fastabend RCU_INIT_POINTER(root_ht->next, tp_c->hlist);
3951ce87720SJohn Fastabend rcu_assign_pointer(tp_c->hlist, root_ht);
3961da177e4SLinus Torvalds
3979fd5661aSPedro Tammela /* root_ht must be destroyed when tcf_proto is destroyed */
3981ce87720SJohn Fastabend rcu_assign_pointer(tp->root, root_ht);
3991da177e4SLinus Torvalds tp->data = tp_c;
4001da177e4SLinus Torvalds return 0;
4011da177e4SLinus Torvalds }
4021da177e4SLinus Torvalds
__u32_destroy_key(struct tc_u_knode * n)4033db09e76SEric Dumazet static void __u32_destroy_key(struct tc_u_knode *n)
4041da177e4SLinus Torvalds {
405d7cdee5eSPaolo Abeni struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
406d7cdee5eSPaolo Abeni
40718d0264fSWANG Cong tcf_exts_destroy(&n->exts);
4089fd5661aSPedro Tammela if (ht && refcount_dec_and_test(&ht->refcnt))
409d7cdee5eSPaolo Abeni kfree(ht);
4103db09e76SEric Dumazet kfree(n);
4113db09e76SEric Dumazet }
4123db09e76SEric Dumazet
u32_destroy_key(struct tc_u_knode * n,bool free_pf)4133db09e76SEric Dumazet static void u32_destroy_key(struct tc_u_knode *n, bool free_pf)
4143db09e76SEric Dumazet {
4153db09e76SEric Dumazet tcf_exts_put_net(&n->exts);
4161da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
417de5df632SJohn Fastabend if (free_pf)
418459d5f62SJohn Fastabend free_percpu(n->pf);
4191da177e4SLinus Torvalds #endif
420a1ddcfeeSJohn Fastabend #ifdef CONFIG_CLS_U32_MARK
421de5df632SJohn Fastabend if (free_pf)
422a1ddcfeeSJohn Fastabend free_percpu(n->pcpu_success);
423a1ddcfeeSJohn Fastabend #endif
4243db09e76SEric Dumazet __u32_destroy_key(n);
4251da177e4SLinus Torvalds }
4261da177e4SLinus Torvalds
427de5df632SJohn Fastabend /* u32_delete_key_rcu should be called when free'ing a copied
428de5df632SJohn Fastabend * version of a tc_u_knode obtained from u32_init_knode(). When
429de5df632SJohn Fastabend * copies are obtained from u32_init_knode() the statistics are
430de5df632SJohn Fastabend * shared between the old and new copies to allow readers to
431de5df632SJohn Fastabend * continue to update the statistics during the copy. To support
432de5df632SJohn Fastabend * this the u32_delete_key_rcu variant does not free the percpu
433de5df632SJohn Fastabend * statistics.
434de5df632SJohn Fastabend */
u32_delete_key_work(struct work_struct * work)435c0d378efSCong Wang static void u32_delete_key_work(struct work_struct *work)
436c0d378efSCong Wang {
437aaa908ffSCong Wang struct tc_u_knode *key = container_of(to_rcu_work(work),
438aaa908ffSCong Wang struct tc_u_knode,
439aaa908ffSCong Wang rwork);
440c0d378efSCong Wang rtnl_lock();
441dc07c573SAl Viro u32_destroy_key(key, false);
442c0d378efSCong Wang rtnl_unlock();
443c0d378efSCong Wang }
444c0d378efSCong Wang
445de5df632SJohn Fastabend /* u32_delete_key_freepf_rcu is the rcu callback variant
446de5df632SJohn Fastabend * that free's the entire structure including the statistics
447de5df632SJohn Fastabend * percpu variables. Only use this if the key is not a copy
448de5df632SJohn Fastabend * returned by u32_init_knode(). See u32_delete_key_rcu()
449de5df632SJohn Fastabend * for the variant that should be used with keys return from
450de5df632SJohn Fastabend * u32_init_knode()
451de5df632SJohn Fastabend */
u32_delete_key_freepf_work(struct work_struct * work)452c0d378efSCong Wang static void u32_delete_key_freepf_work(struct work_struct *work)
453c0d378efSCong Wang {
454aaa908ffSCong Wang struct tc_u_knode *key = container_of(to_rcu_work(work),
455aaa908ffSCong Wang struct tc_u_knode,
456aaa908ffSCong Wang rwork);
457c0d378efSCong Wang rtnl_lock();
458dc07c573SAl Viro u32_destroy_key(key, true);
459c0d378efSCong Wang rtnl_unlock();
460c0d378efSCong Wang }
461c0d378efSCong Wang
u32_delete_key(struct tcf_proto * tp,struct tc_u_knode * key)4621da177e4SLinus Torvalds static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key)
4631da177e4SLinus Torvalds {
464b245d32cSAl Viro struct tc_u_common *tp_c = tp->data;
4651ce87720SJohn Fastabend struct tc_u_knode __rcu **kp;
4661ce87720SJohn Fastabend struct tc_u_knode *pkp;
467a96366bfSJohn Fastabend struct tc_u_hnode *ht = rtnl_dereference(key->ht_up);
4681da177e4SLinus Torvalds
4691da177e4SLinus Torvalds if (ht) {
4701ce87720SJohn Fastabend kp = &ht->ht[TC_U32_HASH(key->handle)];
4711ce87720SJohn Fastabend for (pkp = rtnl_dereference(*kp); pkp;
4721ce87720SJohn Fastabend kp = &pkp->next, pkp = rtnl_dereference(*kp)) {
4731ce87720SJohn Fastabend if (pkp == key) {
4741ce87720SJohn Fastabend RCU_INIT_POINTER(*kp, key->next);
475b245d32cSAl Viro tp_c->knodes--;
4761da177e4SLinus Torvalds
477a0efb80cSWANG Cong tcf_unbind_filter(tp, &key->res);
478f12c6432SCong Wang idr_remove(&ht->handle_idr, key->handle);
47935c55fc1SCong Wang tcf_exts_get_net(&key->exts);
480aaa908ffSCong Wang tcf_queue_work(&key->rwork, u32_delete_key_freepf_work);
4811da177e4SLinus Torvalds return 0;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds }
4841da177e4SLinus Torvalds }
485547b792cSIlpo Järvinen WARN_ON(1);
4861da177e4SLinus Torvalds return 0;
4871da177e4SLinus Torvalds }
4881da177e4SLinus Torvalds
u32_clear_hw_hnode(struct tcf_proto * tp,struct tc_u_hnode * h,struct netlink_ext_ack * extack)489458e704dSJakub Kicinski static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
490458e704dSJakub Kicinski struct netlink_ext_ack *extack)
491a1b7c5fdSJohn Fastabend {
492245dc512SJiri Pirko struct tcf_block *block = tp->chain->block;
493de4784caSJiri Pirko struct tc_cls_u32_offload cls_u32 = {};
494a1b7c5fdSJohn Fastabend
495d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, h->flags, extack);
49677460411SJiri Pirko cls_u32.command = TC_CLSU32_DELETE_HNODE;
49777460411SJiri Pirko cls_u32.hnode.divisor = h->divisor;
49877460411SJiri Pirko cls_u32.hnode.handle = h->handle;
49977460411SJiri Pirko cls_u32.hnode.prio = h->prio;
500de4784caSJiri Pirko
50140119211SVlad Buslov tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, false, true);
502a1b7c5fdSJohn Fastabend }
503a1b7c5fdSJohn Fastabend
u32_replace_hw_hnode(struct tcf_proto * tp,struct tc_u_hnode * h,u32 flags,struct netlink_ext_ack * extack)5045a7a5555SJamal Hadi Salim static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h,
50510a47e0fSQuentin Monnet u32 flags, struct netlink_ext_ack *extack)
506a1b7c5fdSJohn Fastabend {
507245dc512SJiri Pirko struct tcf_block *block = tp->chain->block;
508de4784caSJiri Pirko struct tc_cls_u32_offload cls_u32 = {};
509245dc512SJiri Pirko bool skip_sw = tc_skip_sw(flags);
510245dc512SJiri Pirko bool offloaded = false;
511d34e3e18SSamudrala, Sridhar int err;
512a1b7c5fdSJohn Fastabend
513d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
514de4784caSJiri Pirko cls_u32.command = TC_CLSU32_NEW_HNODE;
515de4784caSJiri Pirko cls_u32.hnode.divisor = h->divisor;
516de4784caSJiri Pirko cls_u32.hnode.handle = h->handle;
517de4784caSJiri Pirko cls_u32.hnode.prio = h->prio;
518a1b7c5fdSJohn Fastabend
51940119211SVlad Buslov err = tc_setup_cb_call(block, TC_SETUP_CLSU32, &cls_u32, skip_sw, true);
520245dc512SJiri Pirko if (err < 0) {
521458e704dSJakub Kicinski u32_clear_hw_hnode(tp, h, NULL);
522245dc512SJiri Pirko return err;
523245dc512SJiri Pirko } else if (err > 0) {
524245dc512SJiri Pirko offloaded = true;
525245dc512SJiri Pirko }
526245dc512SJiri Pirko
527245dc512SJiri Pirko if (skip_sw && !offloaded)
528245dc512SJiri Pirko return -EINVAL;
529d34e3e18SSamudrala, Sridhar
530d34e3e18SSamudrala, Sridhar return 0;
531a1b7c5fdSJohn Fastabend }
532a1b7c5fdSJohn Fastabend
u32_remove_hw_knode(struct tcf_proto * tp,struct tc_u_knode * n,struct netlink_ext_ack * extack)533458e704dSJakub Kicinski static void u32_remove_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
534458e704dSJakub Kicinski struct netlink_ext_ack *extack)
535a1b7c5fdSJohn Fastabend {
536245dc512SJiri Pirko struct tcf_block *block = tp->chain->block;
537de4784caSJiri Pirko struct tc_cls_u32_offload cls_u32 = {};
538a1b7c5fdSJohn Fastabend
539d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
54077460411SJiri Pirko cls_u32.command = TC_CLSU32_DELETE_KNODE;
541caa72601SJiri Pirko cls_u32.knode.handle = n->handle;
542a1b7c5fdSJohn Fastabend
54340119211SVlad Buslov tc_setup_cb_destroy(block, tp, TC_SETUP_CLSU32, &cls_u32, false,
54440119211SVlad Buslov &n->flags, &n->in_hw_count, true);
545a1b7c5fdSJohn Fastabend }
546a1b7c5fdSJohn Fastabend
u32_replace_hw_knode(struct tcf_proto * tp,struct tc_u_knode * n,u32 flags,struct netlink_ext_ack * extack)5475a7a5555SJamal Hadi Salim static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n,
54810a47e0fSQuentin Monnet u32 flags, struct netlink_ext_ack *extack)
549a1b7c5fdSJohn Fastabend {
550058a6c03SPaolo Abeni struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
551245dc512SJiri Pirko struct tcf_block *block = tp->chain->block;
552de4784caSJiri Pirko struct tc_cls_u32_offload cls_u32 = {};
553245dc512SJiri Pirko bool skip_sw = tc_skip_sw(flags);
554d34e3e18SSamudrala, Sridhar int err;
555a1b7c5fdSJohn Fastabend
556d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, flags, extack);
557de4784caSJiri Pirko cls_u32.command = TC_CLSU32_REPLACE_KNODE;
558de4784caSJiri Pirko cls_u32.knode.handle = n->handle;
559de4784caSJiri Pirko cls_u32.knode.fshift = n->fshift;
560a1b7c5fdSJohn Fastabend #ifdef CONFIG_CLS_U32_MARK
561de4784caSJiri Pirko cls_u32.knode.val = n->val;
562de4784caSJiri Pirko cls_u32.knode.mask = n->mask;
563a1b7c5fdSJohn Fastabend #else
564de4784caSJiri Pirko cls_u32.knode.val = 0;
565de4784caSJiri Pirko cls_u32.knode.mask = 0;
566a1b7c5fdSJohn Fastabend #endif
567de4784caSJiri Pirko cls_u32.knode.sel = &n->sel;
568068ceb35SJakub Kicinski cls_u32.knode.res = &n->res;
569de4784caSJiri Pirko cls_u32.knode.exts = &n->exts;
570a1b7c5fdSJohn Fastabend if (n->ht_down)
571058a6c03SPaolo Abeni cls_u32.knode.link_handle = ht->handle;
572a1b7c5fdSJohn Fastabend
57340119211SVlad Buslov err = tc_setup_cb_add(block, tp, TC_SETUP_CLSU32, &cls_u32, skip_sw,
57440119211SVlad Buslov &n->flags, &n->in_hw_count, true);
57540119211SVlad Buslov if (err) {
576458e704dSJakub Kicinski u32_remove_hw_knode(tp, n, NULL);
577245dc512SJiri Pirko return err;
578245dc512SJiri Pirko }
579245dc512SJiri Pirko
5800f04d057SColin Ian King if (skip_sw && !(n->flags & TCA_CLS_FLAGS_IN_HW))
581245dc512SJiri Pirko return -EINVAL;
582d34e3e18SSamudrala, Sridhar
583d34e3e18SSamudrala, Sridhar return 0;
584a1b7c5fdSJohn Fastabend }
585a1b7c5fdSJohn Fastabend
u32_clear_hnode(struct tcf_proto * tp,struct tc_u_hnode * ht,struct netlink_ext_ack * extack)586458e704dSJakub Kicinski static void u32_clear_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
587458e704dSJakub Kicinski struct netlink_ext_ack *extack)
5881da177e4SLinus Torvalds {
589b245d32cSAl Viro struct tc_u_common *tp_c = tp->data;
5901da177e4SLinus Torvalds struct tc_u_knode *n;
591cc7ec456SEric Dumazet unsigned int h;
5921da177e4SLinus Torvalds
5931da177e4SLinus Torvalds for (h = 0; h <= ht->divisor; h++) {
5941ce87720SJohn Fastabend while ((n = rtnl_dereference(ht->ht[h])) != NULL) {
5951ce87720SJohn Fastabend RCU_INIT_POINTER(ht->ht[h],
5961ce87720SJohn Fastabend rtnl_dereference(n->next));
597b245d32cSAl Viro tp_c->knodes--;
598a0efb80cSWANG Cong tcf_unbind_filter(tp, &n->res);
599458e704dSJakub Kicinski u32_remove_hw_knode(tp, n, extack);
6009c160941SMatthew Wilcox idr_remove(&ht->handle_idr, n->handle);
60135c55fc1SCong Wang if (tcf_exts_get_net(&n->exts))
602aaa908ffSCong Wang tcf_queue_work(&n->rwork, u32_delete_key_freepf_work);
60335c55fc1SCong Wang else
604dc07c573SAl Viro u32_destroy_key(n, true);
6051da177e4SLinus Torvalds }
6061da177e4SLinus Torvalds }
6071da177e4SLinus Torvalds }
6081da177e4SLinus Torvalds
u32_destroy_hnode(struct tcf_proto * tp,struct tc_u_hnode * ht,struct netlink_ext_ack * extack)609458e704dSJakub Kicinski static int u32_destroy_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
610458e704dSJakub Kicinski struct netlink_ext_ack *extack)
6111da177e4SLinus Torvalds {
6121da177e4SLinus Torvalds struct tc_u_common *tp_c = tp->data;
6131ce87720SJohn Fastabend struct tc_u_hnode __rcu **hn;
6141ce87720SJohn Fastabend struct tc_u_hnode *phn;
6151da177e4SLinus Torvalds
616458e704dSJakub Kicinski u32_clear_hnode(tp, ht, extack);
6171da177e4SLinus Torvalds
6181ce87720SJohn Fastabend hn = &tp_c->hlist;
6191ce87720SJohn Fastabend for (phn = rtnl_dereference(*hn);
6201ce87720SJohn Fastabend phn;
6211ce87720SJohn Fastabend hn = &phn->next, phn = rtnl_dereference(*hn)) {
6221ce87720SJohn Fastabend if (phn == ht) {
623458e704dSJakub Kicinski u32_clear_hw_hnode(tp, ht, extack);
624e7614370SCong Wang idr_destroy(&ht->handle_idr);
625*789f9963SAlexandre Ferrieux idr_remove(&tp_c->handle_idr, handle2id(ht->handle));
6261ce87720SJohn Fastabend RCU_INIT_POINTER(*hn, ht->next);
6271ce87720SJohn Fastabend kfree_rcu(ht, rcu);
6281da177e4SLinus Torvalds return 0;
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds }
6311da177e4SLinus Torvalds
6321da177e4SLinus Torvalds return -ENOENT;
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds
u32_destroy(struct tcf_proto * tp,bool rtnl_held,struct netlink_ext_ack * extack)63512db03b6SVlad Buslov static void u32_destroy(struct tcf_proto *tp, bool rtnl_held,
63612db03b6SVlad Buslov struct netlink_ext_ack *extack)
6371da177e4SLinus Torvalds {
6381da177e4SLinus Torvalds struct tc_u_common *tp_c = tp->data;
6391ce87720SJohn Fastabend struct tc_u_hnode *root_ht = rtnl_dereference(tp->root);
6401da177e4SLinus Torvalds
641547b792cSIlpo Järvinen WARN_ON(root_ht == NULL);
6421da177e4SLinus Torvalds
6439fd5661aSPedro Tammela if (root_ht && refcount_dec_and_test(&root_ht->refcnt))
644458e704dSJakub Kicinski u32_destroy_hnode(tp, root_ht, extack);
6451da177e4SLinus Torvalds
6469fd5661aSPedro Tammela if (refcount_dec_and_test(&tp_c->refcnt)) {
6471da177e4SLinus Torvalds struct tc_u_hnode *ht;
6481da177e4SLinus Torvalds
6493cd904ecSWANG Cong hlist_del(&tp_c->hnode);
6501da177e4SLinus Torvalds
6511ce87720SJohn Fastabend while ((ht = rtnl_dereference(tp_c->hlist)) != NULL) {
652d7cdee5eSPaolo Abeni u32_clear_hnode(tp, ht, extack);
6531ce87720SJohn Fastabend RCU_INIT_POINTER(tp_c->hlist, ht->next);
654d7cdee5eSPaolo Abeni
655d7cdee5eSPaolo Abeni /* u32_destroy_key() will later free ht for us, if it's
656d7cdee5eSPaolo Abeni * still referenced by some knode
657d7cdee5eSPaolo Abeni */
6589fd5661aSPedro Tammela if (refcount_dec_and_test(&ht->refcnt))
6591ce87720SJohn Fastabend kfree_rcu(ht, rcu);
6603ff50b79SStephen Hemminger }
6611da177e4SLinus Torvalds
662e7614370SCong Wang idr_destroy(&tp_c->handle_idr);
6631da177e4SLinus Torvalds kfree(tp_c);
6641da177e4SLinus Torvalds }
6651da177e4SLinus Torvalds
6661da177e4SLinus Torvalds tp->data = NULL;
6671da177e4SLinus Torvalds }
6681da177e4SLinus Torvalds
u32_delete(struct tcf_proto * tp,void * arg,bool * last,bool rtnl_held,struct netlink_ext_ack * extack)669571acf21SAlexander Aring static int u32_delete(struct tcf_proto *tp, void *arg, bool *last,
67012db03b6SVlad Buslov bool rtnl_held, struct netlink_ext_ack *extack)
6711da177e4SLinus Torvalds {
6728113c095SWANG Cong struct tc_u_hnode *ht = arg;
673763dbf63SWANG Cong struct tc_u_common *tp_c = tp->data;
674763dbf63SWANG Cong int ret = 0;
6751da177e4SLinus Torvalds
676a1b7c5fdSJohn Fastabend if (TC_U32_KEY(ht->handle)) {
677458e704dSJakub Kicinski u32_remove_hw_knode(tp, (struct tc_u_knode *)ht, extack);
678763dbf63SWANG Cong ret = u32_delete_key(tp, (struct tc_u_knode *)ht);
679763dbf63SWANG Cong goto out;
680a1b7c5fdSJohn Fastabend }
6811da177e4SLinus Torvalds
682b44ef845SAl Viro if (ht->is_root) {
6834b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Not allowed to delete root node");
6841da177e4SLinus Torvalds return -EINVAL;
6854b981dbcSAlexander Aring }
6861da177e4SLinus Torvalds
6879fd5661aSPedro Tammela if (refcount_dec_if_one(&ht->refcnt)) {
688458e704dSJakub Kicinski u32_destroy_hnode(tp, ht, extack);
689e56cfad1SJarek Poplawski } else {
6904b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Can not delete in-use filter");
691e56cfad1SJarek Poplawski return -EBUSY;
692e56cfad1SJarek Poplawski }
6931da177e4SLinus Torvalds
694763dbf63SWANG Cong out:
6959fd5661aSPedro Tammela *last = refcount_read(&tp_c->refcnt) == 1 && tp_c->knodes == 0;
696763dbf63SWANG Cong return ret;
6971da177e4SLinus Torvalds }
6981da177e4SLinus Torvalds
gen_new_kid(struct tc_u_hnode * ht,u32 htid)699e7614370SCong Wang static u32 gen_new_kid(struct tc_u_hnode *ht, u32 htid)
7001da177e4SLinus Torvalds {
701f730cb93SMatthew Wilcox u32 index = htid | 0x800;
702e7614370SCong Wang u32 max = htid | 0xFFF;
7031da177e4SLinus Torvalds
704f730cb93SMatthew Wilcox if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max, GFP_KERNEL)) {
705f730cb93SMatthew Wilcox index = htid + 1;
706f730cb93SMatthew Wilcox if (idr_alloc_u32(&ht->handle_idr, NULL, &index, max,
707f730cb93SMatthew Wilcox GFP_KERNEL))
708f730cb93SMatthew Wilcox index = max;
709e7614370SCong Wang }
7101da177e4SLinus Torvalds
711f730cb93SMatthew Wilcox return index;
7121da177e4SLinus Torvalds }
7131da177e4SLinus Torvalds
7146fa8c014SPatrick McHardy static const struct nla_policy u32_policy[TCA_U32_MAX + 1] = {
7156fa8c014SPatrick McHardy [TCA_U32_CLASSID] = { .type = NLA_U32 },
7166fa8c014SPatrick McHardy [TCA_U32_HASH] = { .type = NLA_U32 },
7176fa8c014SPatrick McHardy [TCA_U32_LINK] = { .type = NLA_U32 },
7186fa8c014SPatrick McHardy [TCA_U32_DIVISOR] = { .type = NLA_U32 },
7196fa8c014SPatrick McHardy [TCA_U32_SEL] = { .len = sizeof(struct tc_u32_sel) },
7206fa8c014SPatrick McHardy [TCA_U32_INDEV] = { .type = NLA_STRING, .len = IFNAMSIZ },
7216fa8c014SPatrick McHardy [TCA_U32_MARK] = { .len = sizeof(struct tc_u32_mark) },
7229e8ce79cSJohn Fastabend [TCA_U32_FLAGS] = { .type = NLA_U32 },
7236fa8c014SPatrick McHardy };
7246fa8c014SPatrick McHardy
u32_unbind_filter(struct tcf_proto * tp,struct tc_u_knode * n,struct nlattr ** tb)7259cb36faeSVictor Nogueira static void u32_unbind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
7269cb36faeSVictor Nogueira struct nlattr **tb)
7279cb36faeSVictor Nogueira {
7289cb36faeSVictor Nogueira if (tb[TCA_U32_CLASSID])
7299cb36faeSVictor Nogueira tcf_unbind_filter(tp, &n->res);
7309cb36faeSVictor Nogueira }
7319cb36faeSVictor Nogueira
u32_bind_filter(struct tcf_proto * tp,struct tc_u_knode * n,unsigned long base,struct nlattr ** tb)7329cb36faeSVictor Nogueira static void u32_bind_filter(struct tcf_proto *tp, struct tc_u_knode *n,
7339cb36faeSVictor Nogueira unsigned long base, struct nlattr **tb)
7349cb36faeSVictor Nogueira {
7359cb36faeSVictor Nogueira if (tb[TCA_U32_CLASSID]) {
7369cb36faeSVictor Nogueira n->res.classid = nla_get_u32(tb[TCA_U32_CLASSID]);
7379cb36faeSVictor Nogueira tcf_bind_filter(tp, &n->res, base);
7389cb36faeSVictor Nogueira }
7399cb36faeSVictor Nogueira }
7409cb36faeSVictor Nogueira
u32_set_parms(struct net * net,struct tcf_proto * tp,struct tc_u_knode * n,struct nlattr ** tb,struct nlattr * est,u32 flags,u32 fl_flags,struct netlink_ext_ack * extack)741c1b52739SBenjamin LaHaise static int u32_set_parms(struct net *net, struct tcf_proto *tp,
742add93b61SPatrick McHardy struct tc_u_knode *n, struct nlattr **tb,
743c86e0209SBaowen Zheng struct nlattr *est, u32 flags, u32 fl_flags,
74450a56190SAlexander Aring struct netlink_ext_ack *extack)
7451da177e4SLinus Torvalds {
74604c55383SLee Jones int err, ifindex = -1;
7471da177e4SLinus Torvalds
748c86e0209SBaowen Zheng err = tcf_exts_validate_ex(net, tp, tb, est, &n->exts, flags,
749c86e0209SBaowen Zheng fl_flags, extack);
7501da177e4SLinus Torvalds if (err < 0)
7511da177e4SLinus Torvalds return err;
7521da177e4SLinus Torvalds
75304c55383SLee Jones if (tb[TCA_U32_INDEV]) {
75404c55383SLee Jones ifindex = tcf_change_indev(net, tb[TCA_U32_INDEV], extack);
75504c55383SLee Jones if (ifindex < 0)
75604c55383SLee Jones return -EINVAL;
75704c55383SLee Jones }
75804c55383SLee Jones
759add93b61SPatrick McHardy if (tb[TCA_U32_LINK]) {
7601587bac4SPatrick McHardy u32 handle = nla_get_u32(tb[TCA_U32_LINK]);
76147a1a1d4SPatrick McHardy struct tc_u_hnode *ht_down = NULL, *ht_old;
7621da177e4SLinus Torvalds
7634b981dbcSAlexander Aring if (TC_U32_KEY(handle)) {
7644b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "u32 Link handle must be a hash table");
765705c7091SJiri Pirko return -EINVAL;
7664b981dbcSAlexander Aring }
7671da177e4SLinus Torvalds
7681da177e4SLinus Torvalds if (handle) {
7698a8065f6SAl Viro ht_down = u32_lookup_ht(tp->data, handle);
7701da177e4SLinus Torvalds
7714b981dbcSAlexander Aring if (!ht_down) {
7724b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Link hash table not found");
773705c7091SJiri Pirko return -EINVAL;
7744b981dbcSAlexander Aring }
77527594ec4SAl Viro if (ht_down->is_root) {
77627594ec4SAl Viro NL_SET_ERR_MSG_MOD(extack, "Not linking to root node");
77727594ec4SAl Viro return -EINVAL;
77827594ec4SAl Viro }
7799fd5661aSPedro Tammela refcount_inc(&ht_down->refcnt);
7801da177e4SLinus Torvalds }
7811da177e4SLinus Torvalds
7821ce87720SJohn Fastabend ht_old = rtnl_dereference(n->ht_down);
7831ce87720SJohn Fastabend rcu_assign_pointer(n->ht_down, ht_down);
7841da177e4SLinus Torvalds
78547a1a1d4SPatrick McHardy if (ht_old)
7869fd5661aSPedro Tammela refcount_dec(&ht_old->refcnt);
7871da177e4SLinus Torvalds }
7881da177e4SLinus Torvalds
78904c55383SLee Jones if (ifindex >= 0)
79004c55383SLee Jones n->ifindex = ifindex;
79104c55383SLee Jones
7921da177e4SLinus Torvalds return 0;
7931da177e4SLinus Torvalds }
7941da177e4SLinus Torvalds
u32_replace_knode(struct tcf_proto * tp,struct tc_u_common * tp_c,struct tc_u_knode * n)7955a7a5555SJamal Hadi Salim static void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c,
796de5df632SJohn Fastabend struct tc_u_knode *n)
797de5df632SJohn Fastabend {
798de5df632SJohn Fastabend struct tc_u_knode __rcu **ins;
799de5df632SJohn Fastabend struct tc_u_knode *pins;
800de5df632SJohn Fastabend struct tc_u_hnode *ht;
801de5df632SJohn Fastabend
802de5df632SJohn Fastabend if (TC_U32_HTID(n->handle) == TC_U32_ROOT)
803de5df632SJohn Fastabend ht = rtnl_dereference(tp->root);
804de5df632SJohn Fastabend else
805de5df632SJohn Fastabend ht = u32_lookup_ht(tp_c, TC_U32_HTID(n->handle));
806de5df632SJohn Fastabend
807de5df632SJohn Fastabend ins = &ht->ht[TC_U32_HASH(n->handle)];
808de5df632SJohn Fastabend
809de5df632SJohn Fastabend /* The node must always exist for it to be replaced if this is not the
810de5df632SJohn Fastabend * case then something went very wrong elsewhere.
811de5df632SJohn Fastabend */
812de5df632SJohn Fastabend for (pins = rtnl_dereference(*ins); ;
813de5df632SJohn Fastabend ins = &pins->next, pins = rtnl_dereference(*ins))
814de5df632SJohn Fastabend if (pins->handle == n->handle)
815de5df632SJohn Fastabend break;
816de5df632SJohn Fastabend
817234a4624SMatthew Wilcox idr_replace(&ht->handle_idr, n, n->handle);
818de5df632SJohn Fastabend RCU_INIT_POINTER(n->next, pins->next);
819de5df632SJohn Fastabend rcu_assign_pointer(*ins, n);
820de5df632SJohn Fastabend }
821de5df632SJohn Fastabend
u32_init_knode(struct net * net,struct tcf_proto * tp,struct tc_u_knode * n)82214215108SCong Wang static struct tc_u_knode *u32_init_knode(struct net *net, struct tcf_proto *tp,
823de5df632SJohn Fastabend struct tc_u_knode *n)
824de5df632SJohn Fastabend {
825058a6c03SPaolo Abeni struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
826de5df632SJohn Fastabend struct tc_u32_sel *s = &n->sel;
827058a6c03SPaolo Abeni struct tc_u_knode *new;
828de5df632SJohn Fastabend
829c5eb179eSGustavo A. R. Silva new = kzalloc(struct_size(new, sel.keys, s->nkeys), GFP_KERNEL);
830de5df632SJohn Fastabend if (!new)
831de5df632SJohn Fastabend return NULL;
832de5df632SJohn Fastabend
833de5df632SJohn Fastabend RCU_INIT_POINTER(new->next, n->next);
834de5df632SJohn Fastabend new->handle = n->handle;
835de5df632SJohn Fastabend RCU_INIT_POINTER(new->ht_up, n->ht_up);
836de5df632SJohn Fastabend
837de5df632SJohn Fastabend new->ifindex = n->ifindex;
838de5df632SJohn Fastabend new->fshift = n->fshift;
8399e8ce79cSJohn Fastabend new->flags = n->flags;
840058a6c03SPaolo Abeni RCU_INIT_POINTER(new->ht_down, ht);
841de5df632SJohn Fastabend
842de5df632SJohn Fastabend #ifdef CONFIG_CLS_U32_PERF
843de5df632SJohn Fastabend /* Statistics may be incremented by readers during update
844de5df632SJohn Fastabend * so we must keep them in tact. When the node is later destroyed
845de5df632SJohn Fastabend * a special destroy call must be made to not free the pf memory.
846de5df632SJohn Fastabend */
847de5df632SJohn Fastabend new->pf = n->pf;
848de5df632SJohn Fastabend #endif
849de5df632SJohn Fastabend
850de5df632SJohn Fastabend #ifdef CONFIG_CLS_U32_MARK
851de5df632SJohn Fastabend new->val = n->val;
852de5df632SJohn Fastabend new->mask = n->mask;
853de5df632SJohn Fastabend /* Similarly success statistics must be moved as pointers */
854de5df632SJohn Fastabend new->pcpu_success = n->pcpu_success;
855de5df632SJohn Fastabend #endif
856e512fcf0SGustavo A. R. Silva memcpy(&new->sel, s, struct_size(s, keys, s->nkeys));
857de5df632SJohn Fastabend
85814215108SCong Wang if (tcf_exts_init(&new->exts, net, TCA_U32_ACT, TCA_U32_POLICE)) {
859b9a24bb7SWANG Cong kfree(new);
860b9a24bb7SWANG Cong return NULL;
861b9a24bb7SWANG Cong }
862de5df632SJohn Fastabend
863ec5b0f60SEric Dumazet /* bump reference count as long as we hold pointer to structure */
864ec5b0f60SEric Dumazet if (ht)
8659fd5661aSPedro Tammela refcount_inc(&ht->refcnt);
866ec5b0f60SEric Dumazet
867de5df632SJohn Fastabend return new;
868de5df632SJohn Fastabend }
869de5df632SJohn Fastabend
u32_change(struct net * net,struct sk_buff * in_skb,struct tcf_proto * tp,unsigned long base,u32 handle,struct nlattr ** tca,void ** arg,u32 flags,struct netlink_ext_ack * extack)870c1b52739SBenjamin LaHaise static int u32_change(struct net *net, struct sk_buff *in_skb,
871af4c6641SEric W. Biederman struct tcf_proto *tp, unsigned long base, u32 handle,
872695176bfSCong Wang struct nlattr **tca, void **arg, u32 flags,
8737306db38SAlexander Aring struct netlink_ext_ack *extack)
8741da177e4SLinus Torvalds {
8751da177e4SLinus Torvalds struct tc_u_common *tp_c = tp->data;
8761da177e4SLinus Torvalds struct tc_u_hnode *ht;
8771da177e4SLinus Torvalds struct tc_u_knode *n;
8781da177e4SLinus Torvalds struct tc_u32_sel *s;
879add93b61SPatrick McHardy struct nlattr *opt = tca[TCA_OPTIONS];
880add93b61SPatrick McHardy struct nlattr *tb[TCA_U32_MAX + 1];
881695176bfSCong Wang u32 htid, userflags = 0;
88298c8f125SKees Cook size_t sel_size;
8831da177e4SLinus Torvalds int err;
8841da177e4SLinus Torvalds
8854b981dbcSAlexander Aring if (!opt) {
8864b981dbcSAlexander Aring if (handle) {
8874b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Filter handle requires options");
8884b981dbcSAlexander Aring return -EINVAL;
8894b981dbcSAlexander Aring } else {
8904b981dbcSAlexander Aring return 0;
8914b981dbcSAlexander Aring }
8924b981dbcSAlexander Aring }
8931da177e4SLinus Torvalds
8948cb08174SJohannes Berg err = nla_parse_nested_deprecated(tb, TCA_U32_MAX, opt, u32_policy,
8958cb08174SJohannes Berg extack);
896cee63723SPatrick McHardy if (err < 0)
897cee63723SPatrick McHardy return err;
8981da177e4SLinus Torvalds
899d34e3e18SSamudrala, Sridhar if (tb[TCA_U32_FLAGS]) {
900695176bfSCong Wang userflags = nla_get_u32(tb[TCA_U32_FLAGS]);
901695176bfSCong Wang if (!tc_flags_valid(userflags)) {
9024b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Invalid filter flags");
9031a0f7d29SJakub Kicinski return -EINVAL;
904d34e3e18SSamudrala, Sridhar }
9054b981dbcSAlexander Aring }
9069e8ce79cSJohn Fastabend
9078113c095SWANG Cong n = *arg;
908cc7ec456SEric Dumazet if (n) {
909de5df632SJohn Fastabend struct tc_u_knode *new;
910de5df632SJohn Fastabend
9114b981dbcSAlexander Aring if (TC_U32_KEY(n->handle) == 0) {
9124b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Key node id cannot be zero");
9131da177e4SLinus Torvalds return -EINVAL;
9144b981dbcSAlexander Aring }
9151da177e4SLinus Torvalds
916695176bfSCong Wang if ((n->flags ^ userflags) &
917eb53f7afSIvan Vecera ~(TCA_CLS_FLAGS_IN_HW | TCA_CLS_FLAGS_NOT_IN_HW)) {
9184b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Key node flags do not match passed flags");
9199e8ce79cSJohn Fastabend return -EINVAL;
9204b981dbcSAlexander Aring }
9219e8ce79cSJohn Fastabend
92214215108SCong Wang new = u32_init_knode(net, tp, n);
923de5df632SJohn Fastabend if (!new)
924de5df632SJohn Fastabend return -ENOMEM;
925de5df632SJohn Fastabend
9269cb36faeSVictor Nogueira err = u32_set_parms(net, tp, new, tb, tca[TCA_RATE],
9279cb36faeSVictor Nogueira flags, new->flags, extack);
928de5df632SJohn Fastabend
929de5df632SJohn Fastabend if (err) {
9303db09e76SEric Dumazet __u32_destroy_key(new);
931de5df632SJohn Fastabend return err;
932de5df632SJohn Fastabend }
933de5df632SJohn Fastabend
9349cb36faeSVictor Nogueira u32_bind_filter(tp, new, base, tb);
9359cb36faeSVictor Nogueira
93610a47e0fSQuentin Monnet err = u32_replace_hw_knode(tp, new, flags, extack);
937d34e3e18SSamudrala, Sridhar if (err) {
9389cb36faeSVictor Nogueira u32_unbind_filter(tp, new, tb);
9399cb36faeSVictor Nogueira
940e8d3d78cSVictor Nogueira if (tb[TCA_U32_LINK]) {
941e8d3d78cSVictor Nogueira struct tc_u_hnode *ht_old;
942e8d3d78cSVictor Nogueira
943e8d3d78cSVictor Nogueira ht_old = rtnl_dereference(n->ht_down);
944e8d3d78cSVictor Nogueira if (ht_old)
9459fd5661aSPedro Tammela refcount_inc(&ht_old->refcnt);
946e8d3d78cSVictor Nogueira }
9473db09e76SEric Dumazet __u32_destroy_key(new);
948d34e3e18SSamudrala, Sridhar return err;
949d34e3e18SSamudrala, Sridhar }
950d34e3e18SSamudrala, Sridhar
95124d3dc6dSOr Gerlitz if (!tc_in_hw(new->flags))
95224d3dc6dSOr Gerlitz new->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
95324d3dc6dSOr Gerlitz
954de5df632SJohn Fastabend u32_replace_knode(tp, tp_c, new);
955a0efb80cSWANG Cong tcf_unbind_filter(tp, &n->res);
95635c55fc1SCong Wang tcf_exts_get_net(&n->exts);
957aaa908ffSCong Wang tcf_queue_work(&n->rwork, u32_delete_key_work);
958de5df632SJohn Fastabend return 0;
9591da177e4SLinus Torvalds }
9601da177e4SLinus Torvalds
961add93b61SPatrick McHardy if (tb[TCA_U32_DIVISOR]) {
962cc7ec456SEric Dumazet unsigned int divisor = nla_get_u32(tb[TCA_U32_DIVISOR]);
9631da177e4SLinus Torvalds
9642f0c982dSAl Viro if (!is_power_of_2(divisor)) {
9652f0c982dSAl Viro NL_SET_ERR_MSG_MOD(extack, "Divisor is not a power of 2");
9662f0c982dSAl Viro return -EINVAL;
9672f0c982dSAl Viro }
9682f0c982dSAl Viro if (divisor-- > 0x100) {
9694b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Exceeded maximum 256 hash buckets");
9701da177e4SLinus Torvalds return -EINVAL;
9714b981dbcSAlexander Aring }
9724b981dbcSAlexander Aring if (TC_U32_KEY(handle)) {
9734b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Divisor can only be used on a hash table");
9741da177e4SLinus Torvalds return -EINVAL;
9754b981dbcSAlexander Aring }
976d61491a5SGustavo A. R. Silva ht = kzalloc(struct_size(ht, ht, divisor + 1), GFP_KERNEL);
9771da177e4SLinus Torvalds if (ht == NULL)
9781da177e4SLinus Torvalds return -ENOBUFS;
979e7614370SCong Wang if (handle == 0) {
980e7614370SCong Wang handle = gen_new_htid(tp->data, ht);
981e7614370SCong Wang if (handle == 0) {
982e7614370SCong Wang kfree(ht);
983e7614370SCong Wang return -ENOMEM;
984e7614370SCong Wang }
985e7614370SCong Wang } else {
986f730cb93SMatthew Wilcox err = idr_alloc_u32(&tp_c->handle_idr, ht, &handle,
987f730cb93SMatthew Wilcox handle, GFP_KERNEL);
988e7614370SCong Wang if (err) {
989e7614370SCong Wang kfree(ht);
990e7614370SCong Wang return err;
991e7614370SCong Wang }
992e7614370SCong Wang }
9939fd5661aSPedro Tammela refcount_set(&ht->refcnt, 1);
9941da177e4SLinus Torvalds ht->divisor = divisor;
9951da177e4SLinus Torvalds ht->handle = handle;
9961da177e4SLinus Torvalds ht->prio = tp->prio;
997e7614370SCong Wang idr_init(&ht->handle_idr);
998695176bfSCong Wang ht->flags = userflags;
9996eef3801SJakub Kicinski
1000695176bfSCong Wang err = u32_replace_hw_hnode(tp, ht, userflags, extack);
10016eef3801SJakub Kicinski if (err) {
1002*789f9963SAlexandre Ferrieux idr_remove(&tp_c->handle_idr, handle2id(handle));
10036eef3801SJakub Kicinski kfree(ht);
10046eef3801SJakub Kicinski return err;
10056eef3801SJakub Kicinski }
10066eef3801SJakub Kicinski
10071ce87720SJohn Fastabend RCU_INIT_POINTER(ht->next, tp_c->hlist);
10081ce87720SJohn Fastabend rcu_assign_pointer(tp_c->hlist, ht);
10098113c095SWANG Cong *arg = ht;
1010a1b7c5fdSJohn Fastabend
10111da177e4SLinus Torvalds return 0;
10121da177e4SLinus Torvalds }
10131da177e4SLinus Torvalds
1014add93b61SPatrick McHardy if (tb[TCA_U32_HASH]) {
10151587bac4SPatrick McHardy htid = nla_get_u32(tb[TCA_U32_HASH]);
10161da177e4SLinus Torvalds if (TC_U32_HTID(htid) == TC_U32_ROOT) {
10171ce87720SJohn Fastabend ht = rtnl_dereference(tp->root);
10181da177e4SLinus Torvalds htid = ht->handle;
10191da177e4SLinus Torvalds } else {
10201da177e4SLinus Torvalds ht = u32_lookup_ht(tp->data, TC_U32_HTID(htid));
10214b981dbcSAlexander Aring if (!ht) {
10224b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Specified hash table not found");
10231da177e4SLinus Torvalds return -EINVAL;
10241da177e4SLinus Torvalds }
10254b981dbcSAlexander Aring }
10261da177e4SLinus Torvalds } else {
10271ce87720SJohn Fastabend ht = rtnl_dereference(tp->root);
10281da177e4SLinus Torvalds htid = ht->handle;
10291da177e4SLinus Torvalds }
10301da177e4SLinus Torvalds
10314b981dbcSAlexander Aring if (ht->divisor < TC_U32_HASH(htid)) {
10324b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Specified hash table buckets exceed configured value");
10331da177e4SLinus Torvalds return -EINVAL;
10344b981dbcSAlexander Aring }
10351da177e4SLinus Torvalds
1036e68409dbSJamal Hadi Salim /* At this point, we need to derive the new handle that will be used to
1037e68409dbSJamal Hadi Salim * uniquely map the identity of this table match entry. The
1038e68409dbSJamal Hadi Salim * identity of the entry that we need to construct is 32 bits made of:
1039e68409dbSJamal Hadi Salim * htid(12b):bucketid(8b):node/entryid(12b)
1040e68409dbSJamal Hadi Salim *
1041e68409dbSJamal Hadi Salim * At this point _we have the table(ht)_ in which we will insert this
1042e68409dbSJamal Hadi Salim * entry. We carry the table's id in variable "htid".
1043e68409dbSJamal Hadi Salim * Note that earlier code picked the ht selection either by a) the user
1044e68409dbSJamal Hadi Salim * providing the htid specified via TCA_U32_HASH attribute or b) when
1045e68409dbSJamal Hadi Salim * no such attribute is passed then the root ht, is default to at ID
1046e68409dbSJamal Hadi Salim * 0x[800][00][000]. Rule: the root table has a single bucket with ID 0.
1047e68409dbSJamal Hadi Salim * If OTOH the user passed us the htid, they may also pass a bucketid of
1048e68409dbSJamal Hadi Salim * choice. 0 is fine. For example a user htid is 0x[600][01][000] it is
1049e68409dbSJamal Hadi Salim * indicating hash bucketid of 1. Rule: the entry/node ID _cannot_ be
1050e68409dbSJamal Hadi Salim * passed via the htid, so even if it was non-zero it will be ignored.
1051e68409dbSJamal Hadi Salim *
1052e68409dbSJamal Hadi Salim * We may also have a handle, if the user passed one. The handle also
1053e68409dbSJamal Hadi Salim * carries the same addressing of htid(12b):bucketid(8b):node/entryid(12b).
1054e68409dbSJamal Hadi Salim * Rule: the bucketid on the handle is ignored even if one was passed;
1055e68409dbSJamal Hadi Salim * rather the value on "htid" is always assumed to be the bucketid.
1056e68409dbSJamal Hadi Salim */
10571da177e4SLinus Torvalds if (handle) {
1058e68409dbSJamal Hadi Salim /* Rule: The htid from handle and tableid from htid must match */
10594b981dbcSAlexander Aring if (TC_U32_HTID(handle) && TC_U32_HTID(handle ^ htid)) {
10604b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Handle specified hash table address mismatch");
10611da177e4SLinus Torvalds return -EINVAL;
10624b981dbcSAlexander Aring }
1063e68409dbSJamal Hadi Salim /* Ok, so far we have a valid htid(12b):bucketid(8b) but we
1064e68409dbSJamal Hadi Salim * need to finalize the table entry identification with the last
1065e68409dbSJamal Hadi Salim * part - the node/entryid(12b)). Rule: Nodeid _cannot be 0_ for
1066e68409dbSJamal Hadi Salim * entries. Rule: nodeid of 0 is reserved only for tables(see
1067e68409dbSJamal Hadi Salim * earlier code which processes TC_U32_DIVISOR attribute).
1068e68409dbSJamal Hadi Salim * Rule: The nodeid can only be derived from the handle (and not
1069e68409dbSJamal Hadi Salim * htid).
1070e68409dbSJamal Hadi Salim * Rule: if the handle specified zero for the node id example
1071e68409dbSJamal Hadi Salim * 0x60000000, then pick a new nodeid from the pool of IDs
1072e68409dbSJamal Hadi Salim * this hash table has been allocating from.
1073e68409dbSJamal Hadi Salim * If OTOH it is specified (i.e for example the user passed a
1074e68409dbSJamal Hadi Salim * handle such as 0x60000123), then we use it generate our final
1075e68409dbSJamal Hadi Salim * handle which is used to uniquely identify the match entry.
1076e68409dbSJamal Hadi Salim */
1077e68409dbSJamal Hadi Salim if (!TC_U32_NODE(handle)) {
1078e68409dbSJamal Hadi Salim handle = gen_new_kid(ht, htid);
1079e68409dbSJamal Hadi Salim } else {
10801da177e4SLinus Torvalds handle = htid | TC_U32_NODE(handle);
1081e68409dbSJamal Hadi Salim err = idr_alloc_u32(&ht->handle_idr, NULL, &handle,
1082e68409dbSJamal Hadi Salim handle, GFP_KERNEL);
1083e7614370SCong Wang if (err)
1084e7614370SCong Wang return err;
1085e68409dbSJamal Hadi Salim }
1086e68409dbSJamal Hadi Salim } else {
1087e68409dbSJamal Hadi Salim /* The user did not give us a handle; lets just generate one
1088e68409dbSJamal Hadi Salim * from the table's pool of nodeids.
1089e68409dbSJamal Hadi Salim */
10901da177e4SLinus Torvalds handle = gen_new_kid(ht, htid);
1091e68409dbSJamal Hadi Salim }
10921da177e4SLinus Torvalds
1093e7614370SCong Wang if (tb[TCA_U32_SEL] == NULL) {
10944b981dbcSAlexander Aring NL_SET_ERR_MSG_MOD(extack, "Selector not specified");
1095e7614370SCong Wang err = -EINVAL;
1096e7614370SCong Wang goto erridr;
1097e7614370SCong Wang }
10981da177e4SLinus Torvalds
1099add93b61SPatrick McHardy s = nla_data(tb[TCA_U32_SEL]);
110098c8f125SKees Cook sel_size = struct_size(s, keys, s->nkeys);
110198c8f125SKees Cook if (nla_len(tb[TCA_U32_SEL]) < sel_size) {
110298c8f125SKees Cook err = -EINVAL;
110398c8f125SKees Cook goto erridr;
110498c8f125SKees Cook }
11051da177e4SLinus Torvalds
110677aec5e1SGustavo A. R. Silva n = kzalloc(struct_size(n, sel.keys, s->nkeys), GFP_KERNEL);
1107e7614370SCong Wang if (n == NULL) {
1108e7614370SCong Wang err = -ENOBUFS;
1109e7614370SCong Wang goto erridr;
1110e7614370SCong Wang }
11111da177e4SLinus Torvalds
11121da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
111377aec5e1SGustavo A. R. Silva n->pf = __alloc_percpu(struct_size(n->pf, kcnts, s->nkeys),
111477aec5e1SGustavo A. R. Silva __alignof__(struct tc_u32_pcnt));
1115459d5f62SJohn Fastabend if (!n->pf) {
1116e7614370SCong Wang err = -ENOBUFS;
1117e7614370SCong Wang goto errfree;
11181da177e4SLinus Torvalds }
11191da177e4SLinus Torvalds #endif
11201da177e4SLinus Torvalds
11217cba1833SKees Cook unsafe_memcpy(&n->sel, s, sel_size,
11227cba1833SKees Cook /* A composite flex-array structure destination,
11237cba1833SKees Cook * which was correctly sized with struct_size(),
11247cba1833SKees Cook * bounds-checked against nla_len(), and allocated
11257cba1833SKees Cook * above. */);
1126a96366bfSJohn Fastabend RCU_INIT_POINTER(n->ht_up, ht);
11271da177e4SLinus Torvalds n->handle = handle;
1128b2268016SRadu Rendec n->fshift = s->hmask ? ffs(ntohl(s->hmask)) - 1 : 0;
1129695176bfSCong Wang n->flags = userflags;
11301da177e4SLinus Torvalds
113114215108SCong Wang err = tcf_exts_init(&n->exts, net, TCA_U32_ACT, TCA_U32_POLICE);
1132b9a24bb7SWANG Cong if (err < 0)
1133b9a24bb7SWANG Cong goto errout;
1134b9a24bb7SWANG Cong
11351da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_MARK
1136459d5f62SJohn Fastabend n->pcpu_success = alloc_percpu(u32);
1137a1ddcfeeSJohn Fastabend if (!n->pcpu_success) {
1138a1ddcfeeSJohn Fastabend err = -ENOMEM;
1139a1ddcfeeSJohn Fastabend goto errout;
1140a1ddcfeeSJohn Fastabend }
1141459d5f62SJohn Fastabend
1142add93b61SPatrick McHardy if (tb[TCA_U32_MARK]) {
11431da177e4SLinus Torvalds struct tc_u32_mark *mark;
11441da177e4SLinus Torvalds
1145add93b61SPatrick McHardy mark = nla_data(tb[TCA_U32_MARK]);
1146459d5f62SJohn Fastabend n->val = mark->val;
1147459d5f62SJohn Fastabend n->mask = mark->mask;
11481da177e4SLinus Torvalds }
11491da177e4SLinus Torvalds #endif
11501da177e4SLinus Torvalds
11519cb36faeSVictor Nogueira err = u32_set_parms(net, tp, n, tb, tca[TCA_RATE],
1152c86e0209SBaowen Zheng flags, n->flags, extack);
11539cb36faeSVictor Nogueira
11549cb36faeSVictor Nogueira u32_bind_filter(tp, n, base, tb);
11559cb36faeSVictor Nogueira
11561da177e4SLinus Torvalds if (err == 0) {
11571ce87720SJohn Fastabend struct tc_u_knode __rcu **ins;
11581ce87720SJohn Fastabend struct tc_u_knode *pins;
11591ce87720SJohn Fastabend
116010a47e0fSQuentin Monnet err = u32_replace_hw_knode(tp, n, flags, extack);
1161d34e3e18SSamudrala, Sridhar if (err)
11629cb36faeSVictor Nogueira goto errunbind;
1163d34e3e18SSamudrala, Sridhar
116424d3dc6dSOr Gerlitz if (!tc_in_hw(n->flags))
116524d3dc6dSOr Gerlitz n->flags |= TCA_CLS_FLAGS_NOT_IN_HW;
116624d3dc6dSOr Gerlitz
11671ce87720SJohn Fastabend ins = &ht->ht[TC_U32_HASH(handle)];
11681ce87720SJohn Fastabend for (pins = rtnl_dereference(*ins); pins;
11691ce87720SJohn Fastabend ins = &pins->next, pins = rtnl_dereference(*ins))
11701ce87720SJohn Fastabend if (TC_U32_NODE(handle) < TC_U32_NODE(pins->handle))
11711da177e4SLinus Torvalds break;
11721da177e4SLinus Torvalds
11731ce87720SJohn Fastabend RCU_INIT_POINTER(n->next, pins);
11741ce87720SJohn Fastabend rcu_assign_pointer(*ins, n);
1175b245d32cSAl Viro tp_c->knodes++;
11768113c095SWANG Cong *arg = n;
11771da177e4SLinus Torvalds return 0;
11781da177e4SLinus Torvalds }
1179a1ddcfeeSJohn Fastabend
11809cb36faeSVictor Nogueira errunbind:
11819cb36faeSVictor Nogueira u32_unbind_filter(tp, n, tb);
11829cb36faeSVictor Nogueira
1183a1ddcfeeSJohn Fastabend #ifdef CONFIG_CLS_U32_MARK
1184a1ddcfeeSJohn Fastabend free_percpu(n->pcpu_success);
1185a1ddcfeeSJohn Fastabend #endif
1186a1ddcfeeSJohn Fastabend
1187b9a24bb7SWANG Cong errout:
1188b9a24bb7SWANG Cong tcf_exts_destroy(&n->exts);
11891da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
1190e7614370SCong Wang errfree:
11911ce87720SJohn Fastabend free_percpu(n->pf);
11921da177e4SLinus Torvalds #endif
11931da177e4SLinus Torvalds kfree(n);
1194e7614370SCong Wang erridr:
11959c160941SMatthew Wilcox idr_remove(&ht->handle_idr, handle);
11961da177e4SLinus Torvalds return err;
11971da177e4SLinus Torvalds }
11981da177e4SLinus Torvalds
u32_walk(struct tcf_proto * tp,struct tcf_walker * arg,bool rtnl_held)119912db03b6SVlad Buslov static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg,
120012db03b6SVlad Buslov bool rtnl_held)
12011da177e4SLinus Torvalds {
12021da177e4SLinus Torvalds struct tc_u_common *tp_c = tp->data;
12031da177e4SLinus Torvalds struct tc_u_hnode *ht;
12041da177e4SLinus Torvalds struct tc_u_knode *n;
1205cc7ec456SEric Dumazet unsigned int h;
12061da177e4SLinus Torvalds
12071da177e4SLinus Torvalds if (arg->stop)
12081da177e4SLinus Torvalds return;
12091da177e4SLinus Torvalds
12101ce87720SJohn Fastabend for (ht = rtnl_dereference(tp_c->hlist);
12111ce87720SJohn Fastabend ht;
12121ce87720SJohn Fastabend ht = rtnl_dereference(ht->next)) {
12131da177e4SLinus Torvalds if (ht->prio != tp->prio)
12141da177e4SLinus Torvalds continue;
12155508ff7cSZhengchao Shao
12165508ff7cSZhengchao Shao if (!tc_cls_stats_dump(tp, arg, ht))
12171da177e4SLinus Torvalds return;
12185508ff7cSZhengchao Shao
12191da177e4SLinus Torvalds for (h = 0; h <= ht->divisor; h++) {
12201ce87720SJohn Fastabend for (n = rtnl_dereference(ht->ht[h]);
12211ce87720SJohn Fastabend n;
12221ce87720SJohn Fastabend n = rtnl_dereference(n->next)) {
12235508ff7cSZhengchao Shao if (!tc_cls_stats_dump(tp, arg, n))
12241da177e4SLinus Torvalds return;
12251da177e4SLinus Torvalds }
12261da177e4SLinus Torvalds }
12271da177e4SLinus Torvalds }
12281da177e4SLinus Torvalds }
12291da177e4SLinus Torvalds
u32_reoffload_hnode(struct tcf_proto * tp,struct tc_u_hnode * ht,bool add,flow_setup_cb_t * cb,void * cb_priv,struct netlink_ext_ack * extack)1230530d9951SJohn Hurley static int u32_reoffload_hnode(struct tcf_proto *tp, struct tc_u_hnode *ht,
1231a7323311SPablo Neira Ayuso bool add, flow_setup_cb_t *cb, void *cb_priv,
1232530d9951SJohn Hurley struct netlink_ext_ack *extack)
1233530d9951SJohn Hurley {
1234530d9951SJohn Hurley struct tc_cls_u32_offload cls_u32 = {};
1235530d9951SJohn Hurley int err;
1236530d9951SJohn Hurley
1237d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, ht->flags, extack);
1238530d9951SJohn Hurley cls_u32.command = add ? TC_CLSU32_NEW_HNODE : TC_CLSU32_DELETE_HNODE;
1239530d9951SJohn Hurley cls_u32.hnode.divisor = ht->divisor;
1240530d9951SJohn Hurley cls_u32.hnode.handle = ht->handle;
1241530d9951SJohn Hurley cls_u32.hnode.prio = ht->prio;
1242530d9951SJohn Hurley
1243530d9951SJohn Hurley err = cb(TC_SETUP_CLSU32, &cls_u32, cb_priv);
1244530d9951SJohn Hurley if (err && add && tc_skip_sw(ht->flags))
1245530d9951SJohn Hurley return err;
1246530d9951SJohn Hurley
1247530d9951SJohn Hurley return 0;
1248530d9951SJohn Hurley }
1249530d9951SJohn Hurley
u32_reoffload_knode(struct tcf_proto * tp,struct tc_u_knode * n,bool add,flow_setup_cb_t * cb,void * cb_priv,struct netlink_ext_ack * extack)1250530d9951SJohn Hurley static int u32_reoffload_knode(struct tcf_proto *tp, struct tc_u_knode *n,
1251a7323311SPablo Neira Ayuso bool add, flow_setup_cb_t *cb, void *cb_priv,
1252530d9951SJohn Hurley struct netlink_ext_ack *extack)
1253530d9951SJohn Hurley {
1254530d9951SJohn Hurley struct tc_u_hnode *ht = rtnl_dereference(n->ht_down);
1255530d9951SJohn Hurley struct tcf_block *block = tp->chain->block;
1256530d9951SJohn Hurley struct tc_cls_u32_offload cls_u32 = {};
1257530d9951SJohn Hurley
1258d6787147SPieter Jansen van Vuuren tc_cls_common_offload_init(&cls_u32.common, tp, n->flags, extack);
1259530d9951SJohn Hurley cls_u32.command = add ?
1260530d9951SJohn Hurley TC_CLSU32_REPLACE_KNODE : TC_CLSU32_DELETE_KNODE;
1261530d9951SJohn Hurley cls_u32.knode.handle = n->handle;
1262530d9951SJohn Hurley
1263530d9951SJohn Hurley if (add) {
1264530d9951SJohn Hurley cls_u32.knode.fshift = n->fshift;
1265530d9951SJohn Hurley #ifdef CONFIG_CLS_U32_MARK
1266530d9951SJohn Hurley cls_u32.knode.val = n->val;
1267530d9951SJohn Hurley cls_u32.knode.mask = n->mask;
1268530d9951SJohn Hurley #else
1269530d9951SJohn Hurley cls_u32.knode.val = 0;
1270530d9951SJohn Hurley cls_u32.knode.mask = 0;
1271530d9951SJohn Hurley #endif
1272530d9951SJohn Hurley cls_u32.knode.sel = &n->sel;
1273068ceb35SJakub Kicinski cls_u32.knode.res = &n->res;
1274530d9951SJohn Hurley cls_u32.knode.exts = &n->exts;
1275530d9951SJohn Hurley if (n->ht_down)
1276530d9951SJohn Hurley cls_u32.knode.link_handle = ht->handle;
1277530d9951SJohn Hurley }
1278530d9951SJohn Hurley
127957b0637dSZheng Yongjun return tc_setup_cb_reoffload(block, tp, add, cb, TC_SETUP_CLSU32,
128040119211SVlad Buslov &cls_u32, cb_priv, &n->flags,
128140119211SVlad Buslov &n->in_hw_count);
1282530d9951SJohn Hurley }
1283530d9951SJohn Hurley
u32_reoffload(struct tcf_proto * tp,bool add,flow_setup_cb_t * cb,void * cb_priv,struct netlink_ext_ack * extack)1284a7323311SPablo Neira Ayuso static int u32_reoffload(struct tcf_proto *tp, bool add, flow_setup_cb_t *cb,
1285530d9951SJohn Hurley void *cb_priv, struct netlink_ext_ack *extack)
1286530d9951SJohn Hurley {
1287530d9951SJohn Hurley struct tc_u_common *tp_c = tp->data;
1288530d9951SJohn Hurley struct tc_u_hnode *ht;
1289530d9951SJohn Hurley struct tc_u_knode *n;
1290530d9951SJohn Hurley unsigned int h;
1291530d9951SJohn Hurley int err;
1292530d9951SJohn Hurley
1293530d9951SJohn Hurley for (ht = rtnl_dereference(tp_c->hlist);
1294530d9951SJohn Hurley ht;
1295530d9951SJohn Hurley ht = rtnl_dereference(ht->next)) {
1296530d9951SJohn Hurley if (ht->prio != tp->prio)
1297530d9951SJohn Hurley continue;
1298530d9951SJohn Hurley
1299530d9951SJohn Hurley /* When adding filters to a new dev, try to offload the
1300530d9951SJohn Hurley * hashtable first. When removing, do the filters before the
1301530d9951SJohn Hurley * hashtable.
1302530d9951SJohn Hurley */
1303530d9951SJohn Hurley if (add && !tc_skip_hw(ht->flags)) {
1304530d9951SJohn Hurley err = u32_reoffload_hnode(tp, ht, add, cb, cb_priv,
1305530d9951SJohn Hurley extack);
1306530d9951SJohn Hurley if (err)
1307530d9951SJohn Hurley return err;
1308530d9951SJohn Hurley }
1309530d9951SJohn Hurley
1310530d9951SJohn Hurley for (h = 0; h <= ht->divisor; h++) {
1311530d9951SJohn Hurley for (n = rtnl_dereference(ht->ht[h]);
1312530d9951SJohn Hurley n;
1313530d9951SJohn Hurley n = rtnl_dereference(n->next)) {
1314530d9951SJohn Hurley if (tc_skip_hw(n->flags))
1315530d9951SJohn Hurley continue;
1316530d9951SJohn Hurley
1317530d9951SJohn Hurley err = u32_reoffload_knode(tp, n, add, cb,
1318530d9951SJohn Hurley cb_priv, extack);
1319530d9951SJohn Hurley if (err)
1320530d9951SJohn Hurley return err;
1321530d9951SJohn Hurley }
1322530d9951SJohn Hurley }
1323530d9951SJohn Hurley
1324530d9951SJohn Hurley if (!add && !tc_skip_hw(ht->flags))
1325530d9951SJohn Hurley u32_reoffload_hnode(tp, ht, add, cb, cb_priv, extack);
1326530d9951SJohn Hurley }
1327530d9951SJohn Hurley
1328530d9951SJohn Hurley return 0;
1329530d9951SJohn Hurley }
1330530d9951SJohn Hurley
u32_bind_class(void * fh,u32 classid,unsigned long cl,void * q,unsigned long base)13312e24cd75SCong Wang static void u32_bind_class(void *fh, u32 classid, unsigned long cl, void *q,
13322e24cd75SCong Wang unsigned long base)
133307d79fc7SCong Wang {
133407d79fc7SCong Wang struct tc_u_knode *n = fh;
133507d79fc7SCong Wang
1336cc9039a1SZhengchao Shao tc_cls_bind_class(classid, cl, q, &n->res, base);
133707d79fc7SCong Wang }
133807d79fc7SCong Wang
u32_dump(struct net * net,struct tcf_proto * tp,void * fh,struct sk_buff * skb,struct tcmsg * t,bool rtnl_held)13398113c095SWANG Cong static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh,
134012db03b6SVlad Buslov struct sk_buff *skb, struct tcmsg *t, bool rtnl_held)
13411da177e4SLinus Torvalds {
13428113c095SWANG Cong struct tc_u_knode *n = fh;
13431ce87720SJohn Fastabend struct tc_u_hnode *ht_up, *ht_down;
13444b3550efSPatrick McHardy struct nlattr *nest;
13451da177e4SLinus Torvalds
13461da177e4SLinus Torvalds if (n == NULL)
13471da177e4SLinus Torvalds return skb->len;
13481da177e4SLinus Torvalds
13491da177e4SLinus Torvalds t->tcm_handle = n->handle;
13501da177e4SLinus Torvalds
1351ae0be8deSMichal Kubecek nest = nla_nest_start_noflag(skb, TCA_OPTIONS);
13524b3550efSPatrick McHardy if (nest == NULL)
13534b3550efSPatrick McHardy goto nla_put_failure;
13541da177e4SLinus Torvalds
13551da177e4SLinus Torvalds if (TC_U32_KEY(n->handle) == 0) {
13568113c095SWANG Cong struct tc_u_hnode *ht = fh;
13571da177e4SLinus Torvalds u32 divisor = ht->divisor + 1;
1358cc7ec456SEric Dumazet
13591b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_U32_DIVISOR, divisor))
13601b34ec43SDavid S. Miller goto nla_put_failure;
13611da177e4SLinus Torvalds } else {
1362459d5f62SJohn Fastabend #ifdef CONFIG_CLS_U32_PERF
1363459d5f62SJohn Fastabend struct tc_u32_pcnt *gpf;
1364459d5f62SJohn Fastabend int cpu;
136580aab73dSJohn Fastabend #endif
1366459d5f62SJohn Fastabend
136777aec5e1SGustavo A. R. Silva if (nla_put(skb, TCA_U32_SEL, struct_size(&n->sel, keys, n->sel.nkeys),
13681b34ec43SDavid S. Miller &n->sel))
13691b34ec43SDavid S. Miller goto nla_put_failure;
13701ce87720SJohn Fastabend
13711ce87720SJohn Fastabend ht_up = rtnl_dereference(n->ht_up);
13721ce87720SJohn Fastabend if (ht_up) {
13731da177e4SLinus Torvalds u32 htid = n->handle & 0xFFFFF000;
13741b34ec43SDavid S. Miller if (nla_put_u32(skb, TCA_U32_HASH, htid))
13751b34ec43SDavid S. Miller goto nla_put_failure;
13761da177e4SLinus Torvalds }
13771b34ec43SDavid S. Miller if (n->res.classid &&
13781b34ec43SDavid S. Miller nla_put_u32(skb, TCA_U32_CLASSID, n->res.classid))
13791b34ec43SDavid S. Miller goto nla_put_failure;
13801ce87720SJohn Fastabend
13811ce87720SJohn Fastabend ht_down = rtnl_dereference(n->ht_down);
13821ce87720SJohn Fastabend if (ht_down &&
13831ce87720SJohn Fastabend nla_put_u32(skb, TCA_U32_LINK, ht_down->handle))
13841b34ec43SDavid S. Miller goto nla_put_failure;
13851da177e4SLinus Torvalds
13869e8ce79cSJohn Fastabend if (n->flags && nla_put_u32(skb, TCA_U32_FLAGS, n->flags))
13879e8ce79cSJohn Fastabend goto nla_put_failure;
13889e8ce79cSJohn Fastabend
13891da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_MARK
1390459d5f62SJohn Fastabend if ((n->val || n->mask)) {
1391459d5f62SJohn Fastabend struct tc_u32_mark mark = {.val = n->val,
1392459d5f62SJohn Fastabend .mask = n->mask,
1393459d5f62SJohn Fastabend .success = 0};
139480aab73dSJohn Fastabend int cpum;
1395459d5f62SJohn Fastabend
139680aab73dSJohn Fastabend for_each_possible_cpu(cpum) {
139780aab73dSJohn Fastabend __u32 cnt = *per_cpu_ptr(n->pcpu_success, cpum);
1398459d5f62SJohn Fastabend
1399459d5f62SJohn Fastabend mark.success += cnt;
1400459d5f62SJohn Fastabend }
1401459d5f62SJohn Fastabend
1402459d5f62SJohn Fastabend if (nla_put(skb, TCA_U32_MARK, sizeof(mark), &mark))
14031b34ec43SDavid S. Miller goto nla_put_failure;
1404459d5f62SJohn Fastabend }
14051da177e4SLinus Torvalds #endif
14061da177e4SLinus Torvalds
14075da57f42SWANG Cong if (tcf_exts_dump(skb, &n->exts) < 0)
1408add93b61SPatrick McHardy goto nla_put_failure;
14091da177e4SLinus Torvalds
14102519a602SWANG Cong if (n->ifindex) {
14112519a602SWANG Cong struct net_device *dev;
14122519a602SWANG Cong dev = __dev_get_by_index(net, n->ifindex);
14132519a602SWANG Cong if (dev && nla_put_string(skb, TCA_U32_INDEV, dev->name))
14141b34ec43SDavid S. Miller goto nla_put_failure;
14152519a602SWANG Cong }
14161da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
141777aec5e1SGustavo A. R. Silva gpf = kzalloc(struct_size(gpf, kcnts, n->sel.nkeys), GFP_KERNEL);
1418459d5f62SJohn Fastabend if (!gpf)
1419459d5f62SJohn Fastabend goto nla_put_failure;
1420459d5f62SJohn Fastabend
1421459d5f62SJohn Fastabend for_each_possible_cpu(cpu) {
1422459d5f62SJohn Fastabend int i;
1423459d5f62SJohn Fastabend struct tc_u32_pcnt *pf = per_cpu_ptr(n->pf, cpu);
1424459d5f62SJohn Fastabend
1425459d5f62SJohn Fastabend gpf->rcnt += pf->rcnt;
1426459d5f62SJohn Fastabend gpf->rhit += pf->rhit;
1427459d5f62SJohn Fastabend for (i = 0; i < n->sel.nkeys; i++)
1428459d5f62SJohn Fastabend gpf->kcnts[i] += pf->kcnts[i];
1429459d5f62SJohn Fastabend }
1430459d5f62SJohn Fastabend
143177aec5e1SGustavo A. R. Silva if (nla_put_64bit(skb, TCA_U32_PCNT, struct_size(gpf, kcnts, n->sel.nkeys),
14329854518eSNicolas Dichtel gpf, TCA_U32_PAD)) {
1433459d5f62SJohn Fastabend kfree(gpf);
14341b34ec43SDavid S. Miller goto nla_put_failure;
1435459d5f62SJohn Fastabend }
1436459d5f62SJohn Fastabend kfree(gpf);
14371da177e4SLinus Torvalds #endif
14381da177e4SLinus Torvalds }
14391da177e4SLinus Torvalds
14404b3550efSPatrick McHardy nla_nest_end(skb, nest);
14414b3550efSPatrick McHardy
14421da177e4SLinus Torvalds if (TC_U32_KEY(n->handle))
14435da57f42SWANG Cong if (tcf_exts_dump_stats(skb, &n->exts) < 0)
1444add93b61SPatrick McHardy goto nla_put_failure;
14451da177e4SLinus Torvalds return skb->len;
14461da177e4SLinus Torvalds
1447add93b61SPatrick McHardy nla_put_failure:
14484b3550efSPatrick McHardy nla_nest_cancel(skb, nest);
14491da177e4SLinus Torvalds return -1;
14501da177e4SLinus Torvalds }
14511da177e4SLinus Torvalds
14522eb9d75cSPatrick McHardy static struct tcf_proto_ops cls_u32_ops __read_mostly = {
14531da177e4SLinus Torvalds .kind = "u32",
14541da177e4SLinus Torvalds .classify = u32_classify,
14551da177e4SLinus Torvalds .init = u32_init,
14561da177e4SLinus Torvalds .destroy = u32_destroy,
14571da177e4SLinus Torvalds .get = u32_get,
14581da177e4SLinus Torvalds .change = u32_change,
14591da177e4SLinus Torvalds .delete = u32_delete,
14601da177e4SLinus Torvalds .walk = u32_walk,
1461530d9951SJohn Hurley .reoffload = u32_reoffload,
14621da177e4SLinus Torvalds .dump = u32_dump,
146307d79fc7SCong Wang .bind_class = u32_bind_class,
14641da177e4SLinus Torvalds .owner = THIS_MODULE,
14651da177e4SLinus Torvalds };
14661da177e4SLinus Torvalds
init_u32(void)14671da177e4SLinus Torvalds static int __init init_u32(void)
14681da177e4SLinus Torvalds {
14693cd904ecSWANG Cong int i, ret;
14703cd904ecSWANG Cong
14716ff9c364Sstephen hemminger pr_info("u32 classifier\n");
14721da177e4SLinus Torvalds #ifdef CONFIG_CLS_U32_PERF
14736ff9c364Sstephen hemminger pr_info(" Performance counters on\n");
14741da177e4SLinus Torvalds #endif
14756ff9c364Sstephen hemminger pr_info(" input device check on\n");
14761da177e4SLinus Torvalds #ifdef CONFIG_NET_CLS_ACT
14776ff9c364Sstephen hemminger pr_info(" Actions configured\n");
14781da177e4SLinus Torvalds #endif
14793cd904ecSWANG Cong tc_u_common_hash = kvmalloc_array(U32_HASH_SIZE,
14803cd904ecSWANG Cong sizeof(struct hlist_head),
14813cd904ecSWANG Cong GFP_KERNEL);
14823cd904ecSWANG Cong if (!tc_u_common_hash)
14833cd904ecSWANG Cong return -ENOMEM;
14843cd904ecSWANG Cong
14853cd904ecSWANG Cong for (i = 0; i < U32_HASH_SIZE; i++)
14863cd904ecSWANG Cong INIT_HLIST_HEAD(&tc_u_common_hash[i]);
14873cd904ecSWANG Cong
14883cd904ecSWANG Cong ret = register_tcf_proto_ops(&cls_u32_ops);
14893cd904ecSWANG Cong if (ret)
14903cd904ecSWANG Cong kvfree(tc_u_common_hash);
14913cd904ecSWANG Cong return ret;
14921da177e4SLinus Torvalds }
14931da177e4SLinus Torvalds
exit_u32(void)14941da177e4SLinus Torvalds static void __exit exit_u32(void)
14951da177e4SLinus Torvalds {
14961da177e4SLinus Torvalds unregister_tcf_proto_ops(&cls_u32_ops);
14973cd904ecSWANG Cong kvfree(tc_u_common_hash);
14981da177e4SLinus Torvalds }
14991da177e4SLinus Torvalds
15001da177e4SLinus Torvalds module_init(init_u32)
15011da177e4SLinus Torvalds module_exit(exit_u32)
15021da177e4SLinus Torvalds MODULE_LICENSE("GPL");
1503