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