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