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