162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci#include <linux/kernel.h> 362306a36Sopenharmony_ci#include <linux/slab.h> 462306a36Sopenharmony_ci#include <net/act_api.h> 562306a36Sopenharmony_ci#include <net/flow_offload.h> 662306a36Sopenharmony_ci#include <linux/rtnetlink.h> 762306a36Sopenharmony_ci#include <linux/mutex.h> 862306a36Sopenharmony_ci#include <linux/rhashtable.h> 962306a36Sopenharmony_ci 1062306a36Sopenharmony_cistruct flow_rule *flow_rule_alloc(unsigned int num_actions) 1162306a36Sopenharmony_ci{ 1262306a36Sopenharmony_ci struct flow_rule *rule; 1362306a36Sopenharmony_ci int i; 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci rule = kzalloc(struct_size(rule, action.entries, num_actions), 1662306a36Sopenharmony_ci GFP_KERNEL); 1762306a36Sopenharmony_ci if (!rule) 1862306a36Sopenharmony_ci return NULL; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci rule->action.num_entries = num_actions; 2162306a36Sopenharmony_ci /* Pre-fill each action hw_stats with DONT_CARE. 2262306a36Sopenharmony_ci * Caller can override this if it wants stats for a given action. 2362306a36Sopenharmony_ci */ 2462306a36Sopenharmony_ci for (i = 0; i < num_actions; i++) 2562306a36Sopenharmony_ci rule->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci return rule; 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_alloc); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct flow_offload_action *offload_action_alloc(unsigned int num_actions) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct flow_offload_action *fl_action; 3462306a36Sopenharmony_ci int i; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci fl_action = kzalloc(struct_size(fl_action, action.entries, num_actions), 3762306a36Sopenharmony_ci GFP_KERNEL); 3862306a36Sopenharmony_ci if (!fl_action) 3962306a36Sopenharmony_ci return NULL; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci fl_action->action.num_entries = num_actions; 4262306a36Sopenharmony_ci /* Pre-fill each action hw_stats with DONT_CARE. 4362306a36Sopenharmony_ci * Caller can override this if it wants stats for a given action. 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci for (i = 0; i < num_actions; i++) 4662306a36Sopenharmony_ci fl_action->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return fl_action; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define FLOW_DISSECTOR_MATCH(__rule, __type, __out) \ 5262306a36Sopenharmony_ci const struct flow_match *__m = &(__rule)->match; \ 5362306a36Sopenharmony_ci struct flow_dissector *__d = (__m)->dissector; \ 5462306a36Sopenharmony_ci \ 5562306a36Sopenharmony_ci (__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key); \ 5662306a36Sopenharmony_ci (__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask); \ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid flow_rule_match_meta(const struct flow_rule *rule, 5962306a36Sopenharmony_ci struct flow_match_meta *out) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_META, out); 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_meta); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid flow_rule_match_basic(const struct flow_rule *rule, 6662306a36Sopenharmony_ci struct flow_match_basic *out) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_basic); 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_civoid flow_rule_match_control(const struct flow_rule *rule, 7362306a36Sopenharmony_ci struct flow_match_control *out) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_control); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid flow_rule_match_eth_addrs(const struct flow_rule *rule, 8062306a36Sopenharmony_ci struct flow_match_eth_addrs *out) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_eth_addrs); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_civoid flow_rule_match_vlan(const struct flow_rule *rule, 8762306a36Sopenharmony_ci struct flow_match_vlan *out) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out); 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_vlan); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_civoid flow_rule_match_cvlan(const struct flow_rule *rule, 9462306a36Sopenharmony_ci struct flow_match_vlan *out) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CVLAN, out); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_cvlan); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_civoid flow_rule_match_arp(const struct flow_rule *rule, 10162306a36Sopenharmony_ci struct flow_match_arp *out) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ARP, out); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_arp); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_civoid flow_rule_match_ipv4_addrs(const struct flow_rule *rule, 10862306a36Sopenharmony_ci struct flow_match_ipv4_addrs *out) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ipv4_addrs); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_civoid flow_rule_match_ipv6_addrs(const struct flow_rule *rule, 11562306a36Sopenharmony_ci struct flow_match_ipv6_addrs *out) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ipv6_addrs); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_civoid flow_rule_match_ip(const struct flow_rule *rule, 12262306a36Sopenharmony_ci struct flow_match_ip *out) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IP, out); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ip); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid flow_rule_match_ports(const struct flow_rule *rule, 12962306a36Sopenharmony_ci struct flow_match_ports *out) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ports); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_civoid flow_rule_match_ports_range(const struct flow_rule *rule, 13662306a36Sopenharmony_ci struct flow_match_ports_range *out) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE, out); 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ports_range); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_civoid flow_rule_match_tcp(const struct flow_rule *rule, 14362306a36Sopenharmony_ci struct flow_match_tcp *out) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_TCP, out); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_tcp); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_civoid flow_rule_match_ipsec(const struct flow_rule *rule, 15062306a36Sopenharmony_ci struct flow_match_ipsec *out) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPSEC, out); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ipsec); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_civoid flow_rule_match_icmp(const struct flow_rule *rule, 15762306a36Sopenharmony_ci struct flow_match_icmp *out) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ICMP, out); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_icmp); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_civoid flow_rule_match_mpls(const struct flow_rule *rule, 16462306a36Sopenharmony_ci struct flow_match_mpls *out) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_MPLS, out); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_mpls); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_civoid flow_rule_match_enc_control(const struct flow_rule *rule, 17162306a36Sopenharmony_ci struct flow_match_control *out) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_control); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_civoid flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule, 17862306a36Sopenharmony_ci struct flow_match_ipv4_addrs *out) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_ipv4_addrs); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_civoid flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule, 18562306a36Sopenharmony_ci struct flow_match_ipv6_addrs *out) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_ipv6_addrs); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_civoid flow_rule_match_enc_ip(const struct flow_rule *rule, 19262306a36Sopenharmony_ci struct flow_match_ip *out) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IP, out); 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_ip); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid flow_rule_match_enc_ports(const struct flow_rule *rule, 19962306a36Sopenharmony_ci struct flow_match_ports *out) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_ports); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_civoid flow_rule_match_enc_keyid(const struct flow_rule *rule, 20662306a36Sopenharmony_ci struct flow_match_enc_keyid *out) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_keyid); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_civoid flow_rule_match_enc_opts(const struct flow_rule *rule, 21362306a36Sopenharmony_ci struct flow_match_enc_opts *out) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_OPTS, out); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_enc_opts); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistruct flow_action_cookie *flow_action_cookie_create(void *data, 22062306a36Sopenharmony_ci unsigned int len, 22162306a36Sopenharmony_ci gfp_t gfp) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct flow_action_cookie *cookie; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci cookie = kmalloc(sizeof(*cookie) + len, gfp); 22662306a36Sopenharmony_ci if (!cookie) 22762306a36Sopenharmony_ci return NULL; 22862306a36Sopenharmony_ci cookie->cookie_len = len; 22962306a36Sopenharmony_ci memcpy(cookie->cookie, data, len); 23062306a36Sopenharmony_ci return cookie; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_action_cookie_create); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid flow_action_cookie_destroy(struct flow_action_cookie *cookie) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci kfree(cookie); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_action_cookie_destroy); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid flow_rule_match_ct(const struct flow_rule *rule, 24162306a36Sopenharmony_ci struct flow_match_ct *out) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CT, out); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_ct); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_civoid flow_rule_match_pppoe(const struct flow_rule *rule, 24862306a36Sopenharmony_ci struct flow_match_pppoe *out) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PPPOE, out); 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_pppoe); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_civoid flow_rule_match_l2tpv3(const struct flow_rule *rule, 25562306a36Sopenharmony_ci struct flow_match_l2tpv3 *out) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_L2TPV3, out); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ciEXPORT_SYMBOL(flow_rule_match_l2tpv3); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistruct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb, 26262306a36Sopenharmony_ci void *cb_ident, void *cb_priv, 26362306a36Sopenharmony_ci void (*release)(void *cb_priv)) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct flow_block_cb *block_cb; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL); 26862306a36Sopenharmony_ci if (!block_cb) 26962306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci block_cb->cb = cb; 27262306a36Sopenharmony_ci block_cb->cb_ident = cb_ident; 27362306a36Sopenharmony_ci block_cb->cb_priv = cb_priv; 27462306a36Sopenharmony_ci block_cb->release = release; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci return block_cb; 27762306a36Sopenharmony_ci} 27862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_alloc); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_civoid flow_block_cb_free(struct flow_block_cb *block_cb) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci if (block_cb->release) 28362306a36Sopenharmony_ci block_cb->release(block_cb->cb_priv); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci kfree(block_cb); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_free); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistruct flow_block_cb *flow_block_cb_lookup(struct flow_block *block, 29062306a36Sopenharmony_ci flow_setup_cb_t *cb, void *cb_ident) 29162306a36Sopenharmony_ci{ 29262306a36Sopenharmony_ci struct flow_block_cb *block_cb; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci list_for_each_entry(block_cb, &block->cb_list, list) { 29562306a36Sopenharmony_ci if (block_cb->cb == cb && 29662306a36Sopenharmony_ci block_cb->cb_ident == cb_ident) 29762306a36Sopenharmony_ci return block_cb; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return NULL; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_lookup); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_civoid *flow_block_cb_priv(struct flow_block_cb *block_cb) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci return block_cb->cb_priv; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_priv); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_civoid flow_block_cb_incref(struct flow_block_cb *block_cb) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci block_cb->refcnt++; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_incref); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ciunsigned int flow_block_cb_decref(struct flow_block_cb *block_cb) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci return --block_cb->refcnt; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_decref); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cibool flow_block_cb_is_busy(flow_setup_cb_t *cb, void *cb_ident, 32362306a36Sopenharmony_ci struct list_head *driver_block_list) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct flow_block_cb *block_cb; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci list_for_each_entry(block_cb, driver_block_list, driver_list) { 32862306a36Sopenharmony_ci if (block_cb->cb == cb && 32962306a36Sopenharmony_ci block_cb->cb_ident == cb_ident) 33062306a36Sopenharmony_ci return true; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return false; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_is_busy); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint flow_block_cb_setup_simple(struct flow_block_offload *f, 33862306a36Sopenharmony_ci struct list_head *driver_block_list, 33962306a36Sopenharmony_ci flow_setup_cb_t *cb, 34062306a36Sopenharmony_ci void *cb_ident, void *cb_priv, 34162306a36Sopenharmony_ci bool ingress_only) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct flow_block_cb *block_cb; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (ingress_only && 34662306a36Sopenharmony_ci f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS) 34762306a36Sopenharmony_ci return -EOPNOTSUPP; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci f->driver_block_list = driver_block_list; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (f->command) { 35262306a36Sopenharmony_ci case FLOW_BLOCK_BIND: 35362306a36Sopenharmony_ci if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list)) 35462306a36Sopenharmony_ci return -EBUSY; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, NULL); 35762306a36Sopenharmony_ci if (IS_ERR(block_cb)) 35862306a36Sopenharmony_ci return PTR_ERR(block_cb); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci flow_block_cb_add(block_cb, f); 36162306a36Sopenharmony_ci list_add_tail(&block_cb->driver_list, driver_block_list); 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci case FLOW_BLOCK_UNBIND: 36462306a36Sopenharmony_ci block_cb = flow_block_cb_lookup(f->block, cb, cb_ident); 36562306a36Sopenharmony_ci if (!block_cb) 36662306a36Sopenharmony_ci return -ENOENT; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci flow_block_cb_remove(block_cb, f); 36962306a36Sopenharmony_ci list_del(&block_cb->driver_list); 37062306a36Sopenharmony_ci return 0; 37162306a36Sopenharmony_ci default: 37262306a36Sopenharmony_ci return -EOPNOTSUPP; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ciEXPORT_SYMBOL(flow_block_cb_setup_simple); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic DEFINE_MUTEX(flow_indr_block_lock); 37862306a36Sopenharmony_cistatic LIST_HEAD(flow_block_indr_list); 37962306a36Sopenharmony_cistatic LIST_HEAD(flow_block_indr_dev_list); 38062306a36Sopenharmony_cistatic LIST_HEAD(flow_indir_dev_list); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_cistruct flow_indr_dev { 38362306a36Sopenharmony_ci struct list_head list; 38462306a36Sopenharmony_ci flow_indr_block_bind_cb_t *cb; 38562306a36Sopenharmony_ci void *cb_priv; 38662306a36Sopenharmony_ci refcount_t refcnt; 38762306a36Sopenharmony_ci}; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb, 39062306a36Sopenharmony_ci void *cb_priv) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci struct flow_indr_dev *indr_dev; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci indr_dev = kmalloc(sizeof(*indr_dev), GFP_KERNEL); 39562306a36Sopenharmony_ci if (!indr_dev) 39662306a36Sopenharmony_ci return NULL; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci indr_dev->cb = cb; 39962306a36Sopenharmony_ci indr_dev->cb_priv = cb_priv; 40062306a36Sopenharmony_ci refcount_set(&indr_dev->refcnt, 1); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return indr_dev; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistruct flow_indir_dev_info { 40662306a36Sopenharmony_ci void *data; 40762306a36Sopenharmony_ci struct net_device *dev; 40862306a36Sopenharmony_ci struct Qdisc *sch; 40962306a36Sopenharmony_ci enum tc_setup_type type; 41062306a36Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb); 41162306a36Sopenharmony_ci struct list_head list; 41262306a36Sopenharmony_ci enum flow_block_command command; 41362306a36Sopenharmony_ci enum flow_block_binder_type binder_type; 41462306a36Sopenharmony_ci struct list_head *cb_list; 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic void existing_qdiscs_register(flow_indr_block_bind_cb_t *cb, void *cb_priv) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci struct flow_block_offload bo; 42062306a36Sopenharmony_ci struct flow_indir_dev_info *cur; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci list_for_each_entry(cur, &flow_indir_dev_list, list) { 42362306a36Sopenharmony_ci memset(&bo, 0, sizeof(bo)); 42462306a36Sopenharmony_ci bo.command = cur->command; 42562306a36Sopenharmony_ci bo.binder_type = cur->binder_type; 42662306a36Sopenharmony_ci INIT_LIST_HEAD(&bo.cb_list); 42762306a36Sopenharmony_ci cb(cur->dev, cur->sch, cb_priv, cur->type, &bo, cur->data, cur->cleanup); 42862306a36Sopenharmony_ci list_splice(&bo.cb_list, cur->cb_list); 42962306a36Sopenharmony_ci } 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ciint flow_indr_dev_register(flow_indr_block_bind_cb_t *cb, void *cb_priv) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci struct flow_indr_dev *indr_dev; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci mutex_lock(&flow_indr_block_lock); 43762306a36Sopenharmony_ci list_for_each_entry(indr_dev, &flow_block_indr_dev_list, list) { 43862306a36Sopenharmony_ci if (indr_dev->cb == cb && 43962306a36Sopenharmony_ci indr_dev->cb_priv == cb_priv) { 44062306a36Sopenharmony_ci refcount_inc(&indr_dev->refcnt); 44162306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci indr_dev = flow_indr_dev_alloc(cb, cb_priv); 44762306a36Sopenharmony_ci if (!indr_dev) { 44862306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 44962306a36Sopenharmony_ci return -ENOMEM; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci list_add(&indr_dev->list, &flow_block_indr_dev_list); 45362306a36Sopenharmony_ci existing_qdiscs_register(cb, cb_priv); 45462306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci tcf_action_reoffload_cb(cb, cb_priv, true); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci return 0; 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ciEXPORT_SYMBOL(flow_indr_dev_register); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic void __flow_block_indr_cleanup(void (*release)(void *cb_priv), 46362306a36Sopenharmony_ci void *cb_priv, 46462306a36Sopenharmony_ci struct list_head *cleanup_list) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct flow_block_cb *this, *next; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci list_for_each_entry_safe(this, next, &flow_block_indr_list, indr.list) { 46962306a36Sopenharmony_ci if (this->release == release && 47062306a36Sopenharmony_ci this->indr.cb_priv == cb_priv) 47162306a36Sopenharmony_ci list_move(&this->indr.list, cleanup_list); 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic void flow_block_indr_notify(struct list_head *cleanup_list) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci struct flow_block_cb *this, *next; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci list_for_each_entry_safe(this, next, cleanup_list, indr.list) { 48062306a36Sopenharmony_ci list_del(&this->indr.list); 48162306a36Sopenharmony_ci this->indr.cleanup(this); 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_civoid flow_indr_dev_unregister(flow_indr_block_bind_cb_t *cb, void *cb_priv, 48662306a36Sopenharmony_ci void (*release)(void *cb_priv)) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci struct flow_indr_dev *this, *next, *indr_dev = NULL; 48962306a36Sopenharmony_ci LIST_HEAD(cleanup_list); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci mutex_lock(&flow_indr_block_lock); 49262306a36Sopenharmony_ci list_for_each_entry_safe(this, next, &flow_block_indr_dev_list, list) { 49362306a36Sopenharmony_ci if (this->cb == cb && 49462306a36Sopenharmony_ci this->cb_priv == cb_priv && 49562306a36Sopenharmony_ci refcount_dec_and_test(&this->refcnt)) { 49662306a36Sopenharmony_ci indr_dev = this; 49762306a36Sopenharmony_ci list_del(&indr_dev->list); 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (!indr_dev) { 50362306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 50462306a36Sopenharmony_ci return; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci __flow_block_indr_cleanup(release, cb_priv, &cleanup_list); 50862306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci tcf_action_reoffload_cb(cb, cb_priv, false); 51162306a36Sopenharmony_ci flow_block_indr_notify(&cleanup_list); 51262306a36Sopenharmony_ci kfree(indr_dev); 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ciEXPORT_SYMBOL(flow_indr_dev_unregister); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic void flow_block_indr_init(struct flow_block_cb *flow_block, 51762306a36Sopenharmony_ci struct flow_block_offload *bo, 51862306a36Sopenharmony_ci struct net_device *dev, struct Qdisc *sch, void *data, 51962306a36Sopenharmony_ci void *cb_priv, 52062306a36Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb)) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci flow_block->indr.binder_type = bo->binder_type; 52362306a36Sopenharmony_ci flow_block->indr.data = data; 52462306a36Sopenharmony_ci flow_block->indr.cb_priv = cb_priv; 52562306a36Sopenharmony_ci flow_block->indr.dev = dev; 52662306a36Sopenharmony_ci flow_block->indr.sch = sch; 52762306a36Sopenharmony_ci flow_block->indr.cleanup = cleanup; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistruct flow_block_cb *flow_indr_block_cb_alloc(flow_setup_cb_t *cb, 53162306a36Sopenharmony_ci void *cb_ident, void *cb_priv, 53262306a36Sopenharmony_ci void (*release)(void *cb_priv), 53362306a36Sopenharmony_ci struct flow_block_offload *bo, 53462306a36Sopenharmony_ci struct net_device *dev, 53562306a36Sopenharmony_ci struct Qdisc *sch, void *data, 53662306a36Sopenharmony_ci void *indr_cb_priv, 53762306a36Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb)) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci struct flow_block_cb *block_cb; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, release); 54262306a36Sopenharmony_ci if (IS_ERR(block_cb)) 54362306a36Sopenharmony_ci goto out; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci flow_block_indr_init(block_cb, bo, dev, sch, data, indr_cb_priv, cleanup); 54662306a36Sopenharmony_ci list_add(&block_cb->indr.list, &flow_block_indr_list); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ciout: 54962306a36Sopenharmony_ci return block_cb; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ciEXPORT_SYMBOL(flow_indr_block_cb_alloc); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic struct flow_indir_dev_info *find_indir_dev(void *data) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct flow_indir_dev_info *cur; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci list_for_each_entry(cur, &flow_indir_dev_list, list) { 55862306a36Sopenharmony_ci if (cur->data == data) 55962306a36Sopenharmony_ci return cur; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci return NULL; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int indir_dev_add(void *data, struct net_device *dev, struct Qdisc *sch, 56562306a36Sopenharmony_ci enum tc_setup_type type, void (*cleanup)(struct flow_block_cb *block_cb), 56662306a36Sopenharmony_ci struct flow_block_offload *bo) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct flow_indir_dev_info *info; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci info = find_indir_dev(data); 57162306a36Sopenharmony_ci if (info) 57262306a36Sopenharmony_ci return -EEXIST; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci info = kzalloc(sizeof(*info), GFP_KERNEL); 57562306a36Sopenharmony_ci if (!info) 57662306a36Sopenharmony_ci return -ENOMEM; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci info->data = data; 57962306a36Sopenharmony_ci info->dev = dev; 58062306a36Sopenharmony_ci info->sch = sch; 58162306a36Sopenharmony_ci info->type = type; 58262306a36Sopenharmony_ci info->cleanup = cleanup; 58362306a36Sopenharmony_ci info->command = bo->command; 58462306a36Sopenharmony_ci info->binder_type = bo->binder_type; 58562306a36Sopenharmony_ci info->cb_list = bo->cb_list_head; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci list_add(&info->list, &flow_indir_dev_list); 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int indir_dev_remove(void *data) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct flow_indir_dev_info *info; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci info = find_indir_dev(data); 59662306a36Sopenharmony_ci if (!info) 59762306a36Sopenharmony_ci return -ENOENT; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci list_del(&info->list); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci kfree(info); 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ciint flow_indr_dev_setup_offload(struct net_device *dev, struct Qdisc *sch, 60662306a36Sopenharmony_ci enum tc_setup_type type, void *data, 60762306a36Sopenharmony_ci struct flow_block_offload *bo, 60862306a36Sopenharmony_ci void (*cleanup)(struct flow_block_cb *block_cb)) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct flow_indr_dev *this; 61162306a36Sopenharmony_ci u32 count = 0; 61262306a36Sopenharmony_ci int err; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci mutex_lock(&flow_indr_block_lock); 61562306a36Sopenharmony_ci if (bo) { 61662306a36Sopenharmony_ci if (bo->command == FLOW_BLOCK_BIND) 61762306a36Sopenharmony_ci indir_dev_add(data, dev, sch, type, cleanup, bo); 61862306a36Sopenharmony_ci else if (bo->command == FLOW_BLOCK_UNBIND) 61962306a36Sopenharmony_ci indir_dev_remove(data); 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci list_for_each_entry(this, &flow_block_indr_dev_list, list) { 62362306a36Sopenharmony_ci err = this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup); 62462306a36Sopenharmony_ci if (!err) 62562306a36Sopenharmony_ci count++; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci mutex_unlock(&flow_indr_block_lock); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ciEXPORT_SYMBOL(flow_indr_dev_setup_offload); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_cibool flow_indr_dev_exists(void) 63562306a36Sopenharmony_ci{ 63662306a36Sopenharmony_ci return !list_empty(&flow_block_indr_dev_list); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ciEXPORT_SYMBOL(flow_indr_dev_exists); 639