xref: /openbmc/linux/net/core/flow_offload.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
18f256622SPablo Neira Ayuso /* SPDX-License-Identifier: GPL-2.0 */
28f256622SPablo Neira Ayuso #include <linux/kernel.h>
38f256622SPablo Neira Ayuso #include <linux/slab.h>
413926d19SBaowen Zheng #include <net/act_api.h>
58f256622SPablo Neira Ayuso #include <net/flow_offload.h>
64e481908Swenxu #include <linux/rtnetlink.h>
71150ab0fSwenxu #include <linux/mutex.h>
8c2b69f24SHerbert Xu #include <linux/rhashtable.h>
98f256622SPablo Neira Ayuso 
flow_rule_alloc(unsigned int num_actions)10e3ab786bSPablo Neira Ayuso struct flow_rule *flow_rule_alloc(unsigned int num_actions)
118f256622SPablo Neira Ayuso {
12e3ab786bSPablo Neira Ayuso 	struct flow_rule *rule;
13060b6381SEdward Cree 	int i;
14e3ab786bSPablo Neira Ayuso 
156dca9360SGustavo A. R. Silva 	rule = kzalloc(struct_size(rule, action.entries, num_actions),
16e3ab786bSPablo Neira Ayuso 		       GFP_KERNEL);
17e3ab786bSPablo Neira Ayuso 	if (!rule)
18e3ab786bSPablo Neira Ayuso 		return NULL;
19e3ab786bSPablo Neira Ayuso 
20e3ab786bSPablo Neira Ayuso 	rule->action.num_entries = num_actions;
21060b6381SEdward Cree 	/* Pre-fill each action hw_stats with DONT_CARE.
22060b6381SEdward Cree 	 * Caller can override this if it wants stats for a given action.
23060b6381SEdward Cree 	 */
24060b6381SEdward Cree 	for (i = 0; i < num_actions; i++)
25060b6381SEdward Cree 		rule->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE;
26e3ab786bSPablo Neira Ayuso 
27e3ab786bSPablo Neira Ayuso 	return rule;
288f256622SPablo Neira Ayuso }
298f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_alloc);
308f256622SPablo Neira Ayuso 
offload_action_alloc(unsigned int num_actions)318cbfe939SBaowen Zheng struct flow_offload_action *offload_action_alloc(unsigned int num_actions)
328cbfe939SBaowen Zheng {
338cbfe939SBaowen Zheng 	struct flow_offload_action *fl_action;
348cbfe939SBaowen Zheng 	int i;
358cbfe939SBaowen Zheng 
368cbfe939SBaowen Zheng 	fl_action = kzalloc(struct_size(fl_action, action.entries, num_actions),
378cbfe939SBaowen Zheng 			    GFP_KERNEL);
388cbfe939SBaowen Zheng 	if (!fl_action)
398cbfe939SBaowen Zheng 		return NULL;
408cbfe939SBaowen Zheng 
418cbfe939SBaowen Zheng 	fl_action->action.num_entries = num_actions;
428cbfe939SBaowen Zheng 	/* Pre-fill each action hw_stats with DONT_CARE.
438cbfe939SBaowen Zheng 	 * Caller can override this if it wants stats for a given action.
448cbfe939SBaowen Zheng 	 */
458cbfe939SBaowen Zheng 	for (i = 0; i < num_actions; i++)
468cbfe939SBaowen Zheng 		fl_action->action.entries[i].hw_stats = FLOW_ACTION_HW_STATS_DONT_CARE;
478cbfe939SBaowen Zheng 
488cbfe939SBaowen Zheng 	return fl_action;
498cbfe939SBaowen Zheng }
508cbfe939SBaowen Zheng 
518f256622SPablo Neira Ayuso #define FLOW_DISSECTOR_MATCH(__rule, __type, __out)				\
528f256622SPablo Neira Ayuso 	const struct flow_match *__m = &(__rule)->match;			\
538f256622SPablo Neira Ayuso 	struct flow_dissector *__d = (__m)->dissector;				\
548f256622SPablo Neira Ayuso 										\
558f256622SPablo Neira Ayuso 	(__out)->key = skb_flow_dissector_target(__d, __type, (__m)->key);	\
568f256622SPablo Neira Ayuso 	(__out)->mask = skb_flow_dissector_target(__d, __type, (__m)->mask);	\
578f256622SPablo Neira Ayuso 
flow_rule_match_meta(const struct flow_rule * rule,struct flow_match_meta * out)589558a83aSJiri Pirko void flow_rule_match_meta(const struct flow_rule *rule,
599558a83aSJiri Pirko 			  struct flow_match_meta *out)
609558a83aSJiri Pirko {
619558a83aSJiri Pirko 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_META, out);
629558a83aSJiri Pirko }
639558a83aSJiri Pirko EXPORT_SYMBOL(flow_rule_match_meta);
649558a83aSJiri Pirko 
flow_rule_match_basic(const struct flow_rule * rule,struct flow_match_basic * out)658f256622SPablo Neira Ayuso void flow_rule_match_basic(const struct flow_rule *rule,
668f256622SPablo Neira Ayuso 			   struct flow_match_basic *out)
678f256622SPablo Neira Ayuso {
688f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_BASIC, out);
698f256622SPablo Neira Ayuso }
708f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_basic);
718f256622SPablo Neira Ayuso 
flow_rule_match_control(const struct flow_rule * rule,struct flow_match_control * out)728f256622SPablo Neira Ayuso void flow_rule_match_control(const struct flow_rule *rule,
738f256622SPablo Neira Ayuso 			     struct flow_match_control *out)
748f256622SPablo Neira Ayuso {
758f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CONTROL, out);
768f256622SPablo Neira Ayuso }
778f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_control);
788f256622SPablo Neira Ayuso 
flow_rule_match_eth_addrs(const struct flow_rule * rule,struct flow_match_eth_addrs * out)798f256622SPablo Neira Ayuso void flow_rule_match_eth_addrs(const struct flow_rule *rule,
808f256622SPablo Neira Ayuso 			       struct flow_match_eth_addrs *out)
818f256622SPablo Neira Ayuso {
828f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS, out);
838f256622SPablo Neira Ayuso }
848f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_eth_addrs);
858f256622SPablo Neira Ayuso 
flow_rule_match_vlan(const struct flow_rule * rule,struct flow_match_vlan * out)868f256622SPablo Neira Ayuso void flow_rule_match_vlan(const struct flow_rule *rule,
878f256622SPablo Neira Ayuso 			  struct flow_match_vlan *out)
888f256622SPablo Neira Ayuso {
898f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_VLAN, out);
908f256622SPablo Neira Ayuso }
918f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_vlan);
928f256622SPablo Neira Ayuso 
flow_rule_match_cvlan(const struct flow_rule * rule,struct flow_match_vlan * out)93bae9ed69SEdward Cree void flow_rule_match_cvlan(const struct flow_rule *rule,
94bae9ed69SEdward Cree 			   struct flow_match_vlan *out)
95bae9ed69SEdward Cree {
96bae9ed69SEdward Cree 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CVLAN, out);
97bae9ed69SEdward Cree }
98bae9ed69SEdward Cree EXPORT_SYMBOL(flow_rule_match_cvlan);
99bae9ed69SEdward Cree 
flow_rule_match_arp(const struct flow_rule * rule,struct flow_match_arp * out)10070ea86a0SSteen Hegelund void flow_rule_match_arp(const struct flow_rule *rule,
10170ea86a0SSteen Hegelund 			 struct flow_match_arp *out)
10270ea86a0SSteen Hegelund {
10370ea86a0SSteen Hegelund 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ARP, out);
10470ea86a0SSteen Hegelund }
10570ea86a0SSteen Hegelund EXPORT_SYMBOL(flow_rule_match_arp);
10670ea86a0SSteen Hegelund 
flow_rule_match_ipv4_addrs(const struct flow_rule * rule,struct flow_match_ipv4_addrs * out)1078f256622SPablo Neira Ayuso void flow_rule_match_ipv4_addrs(const struct flow_rule *rule,
1088f256622SPablo Neira Ayuso 				struct flow_match_ipv4_addrs *out)
1098f256622SPablo Neira Ayuso {
1108f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV4_ADDRS, out);
1118f256622SPablo Neira Ayuso }
1128f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_ipv4_addrs);
1138f256622SPablo Neira Ayuso 
flow_rule_match_ipv6_addrs(const struct flow_rule * rule,struct flow_match_ipv6_addrs * out)1148f256622SPablo Neira Ayuso void flow_rule_match_ipv6_addrs(const struct flow_rule *rule,
1158f256622SPablo Neira Ayuso 				struct flow_match_ipv6_addrs *out)
1168f256622SPablo Neira Ayuso {
1178f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPV6_ADDRS, out);
1188f256622SPablo Neira Ayuso }
1198f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_ipv6_addrs);
1208f256622SPablo Neira Ayuso 
flow_rule_match_ip(const struct flow_rule * rule,struct flow_match_ip * out)1218f256622SPablo Neira Ayuso void flow_rule_match_ip(const struct flow_rule *rule,
1228f256622SPablo Neira Ayuso 			struct flow_match_ip *out)
1238f256622SPablo Neira Ayuso {
1248f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IP, out);
1258f256622SPablo Neira Ayuso }
1268f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_ip);
1278f256622SPablo Neira Ayuso 
flow_rule_match_ports(const struct flow_rule * rule,struct flow_match_ports * out)1288f256622SPablo Neira Ayuso void flow_rule_match_ports(const struct flow_rule *rule,
1298f256622SPablo Neira Ayuso 			   struct flow_match_ports *out)
1308f256622SPablo Neira Ayuso {
1318f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS, out);
1328f256622SPablo Neira Ayuso }
1338f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_ports);
1348f256622SPablo Neira Ayuso 
flow_rule_match_ports_range(const struct flow_rule * rule,struct flow_match_ports_range * out)13583d85bb0SMaksym Glubokiy void flow_rule_match_ports_range(const struct flow_rule *rule,
13683d85bb0SMaksym Glubokiy 				 struct flow_match_ports_range *out)
13783d85bb0SMaksym Glubokiy {
13883d85bb0SMaksym Glubokiy 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PORTS_RANGE, out);
13983d85bb0SMaksym Glubokiy }
14083d85bb0SMaksym Glubokiy EXPORT_SYMBOL(flow_rule_match_ports_range);
14183d85bb0SMaksym Glubokiy 
flow_rule_match_tcp(const struct flow_rule * rule,struct flow_match_tcp * out)1428f256622SPablo Neira Ayuso void flow_rule_match_tcp(const struct flow_rule *rule,
1438f256622SPablo Neira Ayuso 			 struct flow_match_tcp *out)
1448f256622SPablo Neira Ayuso {
1458f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_TCP, out);
1468f256622SPablo Neira Ayuso }
1478f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_tcp);
1488f256622SPablo Neira Ayuso 
flow_rule_match_ipsec(const struct flow_rule * rule,struct flow_match_ipsec * out)149*c8915d73SRatheesh Kannoth void flow_rule_match_ipsec(const struct flow_rule *rule,
150*c8915d73SRatheesh Kannoth 			   struct flow_match_ipsec *out)
151*c8915d73SRatheesh Kannoth {
152*c8915d73SRatheesh Kannoth 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_IPSEC, out);
153*c8915d73SRatheesh Kannoth }
154*c8915d73SRatheesh Kannoth EXPORT_SYMBOL(flow_rule_match_ipsec);
155*c8915d73SRatheesh Kannoth 
flow_rule_match_icmp(const struct flow_rule * rule,struct flow_match_icmp * out)1568f256622SPablo Neira Ayuso void flow_rule_match_icmp(const struct flow_rule *rule,
1578f256622SPablo Neira Ayuso 			  struct flow_match_icmp *out)
1588f256622SPablo Neira Ayuso {
1598f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ICMP, out);
1608f256622SPablo Neira Ayuso }
1618f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_icmp);
1628f256622SPablo Neira Ayuso 
flow_rule_match_mpls(const struct flow_rule * rule,struct flow_match_mpls * out)1638f256622SPablo Neira Ayuso void flow_rule_match_mpls(const struct flow_rule *rule,
1648f256622SPablo Neira Ayuso 			  struct flow_match_mpls *out)
1658f256622SPablo Neira Ayuso {
1668f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_MPLS, out);
1678f256622SPablo Neira Ayuso }
1688f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_mpls);
1698f256622SPablo Neira Ayuso 
flow_rule_match_enc_control(const struct flow_rule * rule,struct flow_match_control * out)1708f256622SPablo Neira Ayuso void flow_rule_match_enc_control(const struct flow_rule *rule,
1718f256622SPablo Neira Ayuso 				 struct flow_match_control *out)
1728f256622SPablo Neira Ayuso {
1738f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_CONTROL, out);
1748f256622SPablo Neira Ayuso }
1758f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_control);
1768f256622SPablo Neira Ayuso 
flow_rule_match_enc_ipv4_addrs(const struct flow_rule * rule,struct flow_match_ipv4_addrs * out)1778f256622SPablo Neira Ayuso void flow_rule_match_enc_ipv4_addrs(const struct flow_rule *rule,
1788f256622SPablo Neira Ayuso 				    struct flow_match_ipv4_addrs *out)
1798f256622SPablo Neira Ayuso {
1808f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV4_ADDRS, out);
1818f256622SPablo Neira Ayuso }
1828f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_ipv4_addrs);
1838f256622SPablo Neira Ayuso 
flow_rule_match_enc_ipv6_addrs(const struct flow_rule * rule,struct flow_match_ipv6_addrs * out)1848f256622SPablo Neira Ayuso void flow_rule_match_enc_ipv6_addrs(const struct flow_rule *rule,
1858f256622SPablo Neira Ayuso 				    struct flow_match_ipv6_addrs *out)
1868f256622SPablo Neira Ayuso {
1878f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IPV6_ADDRS, out);
1888f256622SPablo Neira Ayuso }
1898f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_ipv6_addrs);
1908f256622SPablo Neira Ayuso 
flow_rule_match_enc_ip(const struct flow_rule * rule,struct flow_match_ip * out)1918f256622SPablo Neira Ayuso void flow_rule_match_enc_ip(const struct flow_rule *rule,
1928f256622SPablo Neira Ayuso 			    struct flow_match_ip *out)
1938f256622SPablo Neira Ayuso {
1948f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_IP, out);
1958f256622SPablo Neira Ayuso }
1968f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_ip);
1978f256622SPablo Neira Ayuso 
flow_rule_match_enc_ports(const struct flow_rule * rule,struct flow_match_ports * out)1988f256622SPablo Neira Ayuso void flow_rule_match_enc_ports(const struct flow_rule *rule,
1998f256622SPablo Neira Ayuso 			       struct flow_match_ports *out)
2008f256622SPablo Neira Ayuso {
2018f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_PORTS, out);
2028f256622SPablo Neira Ayuso }
2038f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_ports);
2048f256622SPablo Neira Ayuso 
flow_rule_match_enc_keyid(const struct flow_rule * rule,struct flow_match_enc_keyid * out)2058f256622SPablo Neira Ayuso void flow_rule_match_enc_keyid(const struct flow_rule *rule,
2068f256622SPablo Neira Ayuso 			       struct flow_match_enc_keyid *out)
2078f256622SPablo Neira Ayuso {
2088f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_KEYID, out);
2098f256622SPablo Neira Ayuso }
2108f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_keyid);
2118f256622SPablo Neira Ayuso 
flow_rule_match_enc_opts(const struct flow_rule * rule,struct flow_match_enc_opts * out)2128f256622SPablo Neira Ayuso void flow_rule_match_enc_opts(const struct flow_rule *rule,
2138f256622SPablo Neira Ayuso 			      struct flow_match_enc_opts *out)
2148f256622SPablo Neira Ayuso {
2158f256622SPablo Neira Ayuso 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_ENC_OPTS, out);
2168f256622SPablo Neira Ayuso }
2178f256622SPablo Neira Ayuso EXPORT_SYMBOL(flow_rule_match_enc_opts);
2184e95bc26SPablo Neira Ayuso 
flow_action_cookie_create(void * data,unsigned int len,gfp_t gfp)2192008495dSJiri Pirko struct flow_action_cookie *flow_action_cookie_create(void *data,
2202008495dSJiri Pirko 						     unsigned int len,
2212008495dSJiri Pirko 						     gfp_t gfp)
2222008495dSJiri Pirko {
2232008495dSJiri Pirko 	struct flow_action_cookie *cookie;
2242008495dSJiri Pirko 
2252008495dSJiri Pirko 	cookie = kmalloc(sizeof(*cookie) + len, gfp);
2262008495dSJiri Pirko 	if (!cookie)
2272008495dSJiri Pirko 		return NULL;
2282008495dSJiri Pirko 	cookie->cookie_len = len;
2292008495dSJiri Pirko 	memcpy(cookie->cookie, data, len);
2302008495dSJiri Pirko 	return cookie;
2312008495dSJiri Pirko }
2322008495dSJiri Pirko EXPORT_SYMBOL(flow_action_cookie_create);
2332008495dSJiri Pirko 
flow_action_cookie_destroy(struct flow_action_cookie * cookie)2342008495dSJiri Pirko void flow_action_cookie_destroy(struct flow_action_cookie *cookie)
2352008495dSJiri Pirko {
2362008495dSJiri Pirko 	kfree(cookie);
2372008495dSJiri Pirko }
2382008495dSJiri Pirko EXPORT_SYMBOL(flow_action_cookie_destroy);
2392008495dSJiri Pirko 
flow_rule_match_ct(const struct flow_rule * rule,struct flow_match_ct * out)240ee1c45e8SPaul Blakey void flow_rule_match_ct(const struct flow_rule *rule,
241ee1c45e8SPaul Blakey 			struct flow_match_ct *out)
242ee1c45e8SPaul Blakey {
243ee1c45e8SPaul Blakey 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_CT, out);
244ee1c45e8SPaul Blakey }
245ee1c45e8SPaul Blakey EXPORT_SYMBOL(flow_rule_match_ct);
246ee1c45e8SPaul Blakey 
flow_rule_match_pppoe(const struct flow_rule * rule,struct flow_match_pppoe * out)2476a21b085SWojciech Drewek void flow_rule_match_pppoe(const struct flow_rule *rule,
2486a21b085SWojciech Drewek 			   struct flow_match_pppoe *out)
2496a21b085SWojciech Drewek {
2506a21b085SWojciech Drewek 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_PPPOE, out);
2516a21b085SWojciech Drewek }
2526a21b085SWojciech Drewek EXPORT_SYMBOL(flow_rule_match_pppoe);
2536a21b085SWojciech Drewek 
flow_rule_match_l2tpv3(const struct flow_rule * rule,struct flow_match_l2tpv3 * out)2542c1befacSWojciech Drewek void flow_rule_match_l2tpv3(const struct flow_rule *rule,
2552c1befacSWojciech Drewek 			    struct flow_match_l2tpv3 *out)
2562c1befacSWojciech Drewek {
2572c1befacSWojciech Drewek 	FLOW_DISSECTOR_MATCH(rule, FLOW_DISSECTOR_KEY_L2TPV3, out);
2582c1befacSWojciech Drewek }
2592c1befacSWojciech Drewek EXPORT_SYMBOL(flow_rule_match_l2tpv3);
2602c1befacSWojciech Drewek 
flow_block_cb_alloc(flow_setup_cb_t * cb,void * cb_ident,void * cb_priv,void (* release)(void * cb_priv))261a7323311SPablo Neira Ayuso struct flow_block_cb *flow_block_cb_alloc(flow_setup_cb_t *cb,
262d63db30cSPablo Neira Ayuso 					  void *cb_ident, void *cb_priv,
263d63db30cSPablo Neira Ayuso 					  void (*release)(void *cb_priv))
264d63db30cSPablo Neira Ayuso {
265d63db30cSPablo Neira Ayuso 	struct flow_block_cb *block_cb;
266d63db30cSPablo Neira Ayuso 
267d63db30cSPablo Neira Ayuso 	block_cb = kzalloc(sizeof(*block_cb), GFP_KERNEL);
268d63db30cSPablo Neira Ayuso 	if (!block_cb)
269d63db30cSPablo Neira Ayuso 		return ERR_PTR(-ENOMEM);
270d63db30cSPablo Neira Ayuso 
271d63db30cSPablo Neira Ayuso 	block_cb->cb = cb;
272d63db30cSPablo Neira Ayuso 	block_cb->cb_ident = cb_ident;
273d63db30cSPablo Neira Ayuso 	block_cb->cb_priv = cb_priv;
274d63db30cSPablo Neira Ayuso 	block_cb->release = release;
275d63db30cSPablo Neira Ayuso 
276d63db30cSPablo Neira Ayuso 	return block_cb;
277d63db30cSPablo Neira Ayuso }
278d63db30cSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_alloc);
279d63db30cSPablo Neira Ayuso 
flow_block_cb_free(struct flow_block_cb * block_cb)280d63db30cSPablo Neira Ayuso void flow_block_cb_free(struct flow_block_cb *block_cb)
281d63db30cSPablo Neira Ayuso {
282d63db30cSPablo Neira Ayuso 	if (block_cb->release)
283d63db30cSPablo Neira Ayuso 		block_cb->release(block_cb->cb_priv);
284d63db30cSPablo Neira Ayuso 
285d63db30cSPablo Neira Ayuso 	kfree(block_cb);
286d63db30cSPablo Neira Ayuso }
287d63db30cSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_free);
288d63db30cSPablo Neira Ayuso 
flow_block_cb_lookup(struct flow_block * block,flow_setup_cb_t * cb,void * cb_ident)28914bfb13fSPablo Neira Ayuso struct flow_block_cb *flow_block_cb_lookup(struct flow_block *block,
290a7323311SPablo Neira Ayuso 					   flow_setup_cb_t *cb, void *cb_ident)
291da3eeb90SPablo Neira Ayuso {
292da3eeb90SPablo Neira Ayuso 	struct flow_block_cb *block_cb;
293da3eeb90SPablo Neira Ayuso 
29414bfb13fSPablo Neira Ayuso 	list_for_each_entry(block_cb, &block->cb_list, list) {
2950c7294ddSPablo Neira Ayuso 		if (block_cb->cb == cb &&
296da3eeb90SPablo Neira Ayuso 		    block_cb->cb_ident == cb_ident)
297da3eeb90SPablo Neira Ayuso 			return block_cb;
298da3eeb90SPablo Neira Ayuso 	}
299da3eeb90SPablo Neira Ayuso 
300da3eeb90SPablo Neira Ayuso 	return NULL;
301da3eeb90SPablo Neira Ayuso }
302da3eeb90SPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_lookup);
303da3eeb90SPablo Neira Ayuso 
flow_block_cb_priv(struct flow_block_cb * block_cb)30467bd0d5eSPablo Neira Ayuso void *flow_block_cb_priv(struct flow_block_cb *block_cb)
30567bd0d5eSPablo Neira Ayuso {
30667bd0d5eSPablo Neira Ayuso 	return block_cb->cb_priv;
30767bd0d5eSPablo Neira Ayuso }
30867bd0d5eSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_priv);
30967bd0d5eSPablo Neira Ayuso 
flow_block_cb_incref(struct flow_block_cb * block_cb)31067bd0d5eSPablo Neira Ayuso void flow_block_cb_incref(struct flow_block_cb *block_cb)
31167bd0d5eSPablo Neira Ayuso {
31267bd0d5eSPablo Neira Ayuso 	block_cb->refcnt++;
31367bd0d5eSPablo Neira Ayuso }
31467bd0d5eSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_incref);
31567bd0d5eSPablo Neira Ayuso 
flow_block_cb_decref(struct flow_block_cb * block_cb)31667bd0d5eSPablo Neira Ayuso unsigned int flow_block_cb_decref(struct flow_block_cb *block_cb)
31767bd0d5eSPablo Neira Ayuso {
31867bd0d5eSPablo Neira Ayuso 	return --block_cb->refcnt;
31967bd0d5eSPablo Neira Ayuso }
32067bd0d5eSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_decref);
32167bd0d5eSPablo Neira Ayuso 
flow_block_cb_is_busy(flow_setup_cb_t * cb,void * cb_ident,struct list_head * driver_block_list)322a7323311SPablo Neira Ayuso bool flow_block_cb_is_busy(flow_setup_cb_t *cb, void *cb_ident,
3230d4fd02eSPablo Neira Ayuso 			   struct list_head *driver_block_list)
3240d4fd02eSPablo Neira Ayuso {
3250d4fd02eSPablo Neira Ayuso 	struct flow_block_cb *block_cb;
3260d4fd02eSPablo Neira Ayuso 
3270d4fd02eSPablo Neira Ayuso 	list_for_each_entry(block_cb, driver_block_list, driver_list) {
3280d4fd02eSPablo Neira Ayuso 		if (block_cb->cb == cb &&
3290d4fd02eSPablo Neira Ayuso 		    block_cb->cb_ident == cb_ident)
3300d4fd02eSPablo Neira Ayuso 			return true;
3310d4fd02eSPablo Neira Ayuso 	}
3320d4fd02eSPablo Neira Ayuso 
3330d4fd02eSPablo Neira Ayuso 	return false;
3340d4fd02eSPablo Neira Ayuso }
3350d4fd02eSPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_is_busy);
3360d4fd02eSPablo Neira Ayuso 
flow_block_cb_setup_simple(struct flow_block_offload * f,struct list_head * driver_block_list,flow_setup_cb_t * cb,void * cb_ident,void * cb_priv,bool ingress_only)3374e95bc26SPablo Neira Ayuso int flow_block_cb_setup_simple(struct flow_block_offload *f,
3384e95bc26SPablo Neira Ayuso 			       struct list_head *driver_block_list,
339a7323311SPablo Neira Ayuso 			       flow_setup_cb_t *cb,
340a7323311SPablo Neira Ayuso 			       void *cb_ident, void *cb_priv,
3414e95bc26SPablo Neira Ayuso 			       bool ingress_only)
3424e95bc26SPablo Neira Ayuso {
343955bcb6eSPablo Neira Ayuso 	struct flow_block_cb *block_cb;
344955bcb6eSPablo Neira Ayuso 
3454e95bc26SPablo Neira Ayuso 	if (ingress_only &&
34632f8c409SPablo Neira Ayuso 	    f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
3474e95bc26SPablo Neira Ayuso 		return -EOPNOTSUPP;
3484e95bc26SPablo Neira Ayuso 
3494e95bc26SPablo Neira Ayuso 	f->driver_block_list = driver_block_list;
3504e95bc26SPablo Neira Ayuso 
3514e95bc26SPablo Neira Ayuso 	switch (f->command) {
3529c0e189eSPablo Neira Ayuso 	case FLOW_BLOCK_BIND:
3530d4fd02eSPablo Neira Ayuso 		if (flow_block_cb_is_busy(cb, cb_ident, driver_block_list))
3540d4fd02eSPablo Neira Ayuso 			return -EBUSY;
3550d4fd02eSPablo Neira Ayuso 
3560c7294ddSPablo Neira Ayuso 		block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, NULL);
357955bcb6eSPablo Neira Ayuso 		if (IS_ERR(block_cb))
358955bcb6eSPablo Neira Ayuso 			return PTR_ERR(block_cb);
359955bcb6eSPablo Neira Ayuso 
360955bcb6eSPablo Neira Ayuso 		flow_block_cb_add(block_cb, f);
361955bcb6eSPablo Neira Ayuso 		list_add_tail(&block_cb->driver_list, driver_block_list);
362955bcb6eSPablo Neira Ayuso 		return 0;
3639c0e189eSPablo Neira Ayuso 	case FLOW_BLOCK_UNBIND:
36414bfb13fSPablo Neira Ayuso 		block_cb = flow_block_cb_lookup(f->block, cb, cb_ident);
365955bcb6eSPablo Neira Ayuso 		if (!block_cb)
366955bcb6eSPablo Neira Ayuso 			return -ENOENT;
367955bcb6eSPablo Neira Ayuso 
368955bcb6eSPablo Neira Ayuso 		flow_block_cb_remove(block_cb, f);
369955bcb6eSPablo Neira Ayuso 		list_del(&block_cb->driver_list);
3704e95bc26SPablo Neira Ayuso 		return 0;
3714e95bc26SPablo Neira Ayuso 	default:
3724e95bc26SPablo Neira Ayuso 		return -EOPNOTSUPP;
3734e95bc26SPablo Neira Ayuso 	}
3744e95bc26SPablo Neira Ayuso }
3754e95bc26SPablo Neira Ayuso EXPORT_SYMBOL(flow_block_cb_setup_simple);
3764e481908Swenxu 
3771fac52daSPablo Neira Ayuso static DEFINE_MUTEX(flow_indr_block_lock);
3781fac52daSPablo Neira Ayuso static LIST_HEAD(flow_block_indr_list);
3791fac52daSPablo Neira Ayuso static LIST_HEAD(flow_block_indr_dev_list);
38074fc4f82SEli Cohen static LIST_HEAD(flow_indir_dev_list);
3811fac52daSPablo Neira Ayuso 
3821fac52daSPablo Neira Ayuso struct flow_indr_dev {
3831fac52daSPablo Neira Ayuso 	struct list_head		list;
3841fac52daSPablo Neira Ayuso 	flow_indr_block_bind_cb_t	*cb;
3851fac52daSPablo Neira Ayuso 	void				*cb_priv;
3861fac52daSPablo Neira Ayuso 	refcount_t			refcnt;
3871fac52daSPablo Neira Ayuso };
3881fac52daSPablo Neira Ayuso 
flow_indr_dev_alloc(flow_indr_block_bind_cb_t * cb,void * cb_priv)3891fac52daSPablo Neira Ayuso static struct flow_indr_dev *flow_indr_dev_alloc(flow_indr_block_bind_cb_t *cb,
3901fac52daSPablo Neira Ayuso 						 void *cb_priv)
3911fac52daSPablo Neira Ayuso {
3921fac52daSPablo Neira Ayuso 	struct flow_indr_dev *indr_dev;
3931fac52daSPablo Neira Ayuso 
3941fac52daSPablo Neira Ayuso 	indr_dev = kmalloc(sizeof(*indr_dev), GFP_KERNEL);
3951fac52daSPablo Neira Ayuso 	if (!indr_dev)
3961fac52daSPablo Neira Ayuso 		return NULL;
3971fac52daSPablo Neira Ayuso 
3981fac52daSPablo Neira Ayuso 	indr_dev->cb		= cb;
3991fac52daSPablo Neira Ayuso 	indr_dev->cb_priv	= cb_priv;
4001fac52daSPablo Neira Ayuso 	refcount_set(&indr_dev->refcnt, 1);
4011fac52daSPablo Neira Ayuso 
4021fac52daSPablo Neira Ayuso 	return indr_dev;
4031fac52daSPablo Neira Ayuso }
4041fac52daSPablo Neira Ayuso 
40574fc4f82SEli Cohen struct flow_indir_dev_info {
40674fc4f82SEli Cohen 	void *data;
40774fc4f82SEli Cohen 	struct net_device *dev;
40874fc4f82SEli Cohen 	struct Qdisc *sch;
40974fc4f82SEli Cohen 	enum tc_setup_type type;
41074fc4f82SEli Cohen 	void (*cleanup)(struct flow_block_cb *block_cb);
41174fc4f82SEli Cohen 	struct list_head list;
41274fc4f82SEli Cohen 	enum flow_block_command command;
41374fc4f82SEli Cohen 	enum flow_block_binder_type binder_type;
41474fc4f82SEli Cohen 	struct list_head *cb_list;
41574fc4f82SEli Cohen };
41674fc4f82SEli Cohen 
existing_qdiscs_register(flow_indr_block_bind_cb_t * cb,void * cb_priv)41774fc4f82SEli Cohen static void existing_qdiscs_register(flow_indr_block_bind_cb_t *cb, void *cb_priv)
41874fc4f82SEli Cohen {
41974fc4f82SEli Cohen 	struct flow_block_offload bo;
42074fc4f82SEli Cohen 	struct flow_indir_dev_info *cur;
42174fc4f82SEli Cohen 
42274fc4f82SEli Cohen 	list_for_each_entry(cur, &flow_indir_dev_list, list) {
42374fc4f82SEli Cohen 		memset(&bo, 0, sizeof(bo));
42474fc4f82SEli Cohen 		bo.command = cur->command;
42574fc4f82SEli Cohen 		bo.binder_type = cur->binder_type;
42674fc4f82SEli Cohen 		INIT_LIST_HEAD(&bo.cb_list);
42774fc4f82SEli Cohen 		cb(cur->dev, cur->sch, cb_priv, cur->type, &bo, cur->data, cur->cleanup);
42874fc4f82SEli Cohen 		list_splice(&bo.cb_list, cur->cb_list);
42974fc4f82SEli Cohen 	}
43074fc4f82SEli Cohen }
43174fc4f82SEli Cohen 
flow_indr_dev_register(flow_indr_block_bind_cb_t * cb,void * cb_priv)4321fac52daSPablo Neira Ayuso int flow_indr_dev_register(flow_indr_block_bind_cb_t *cb, void *cb_priv)
4331fac52daSPablo Neira Ayuso {
4341fac52daSPablo Neira Ayuso 	struct flow_indr_dev *indr_dev;
4351fac52daSPablo Neira Ayuso 
4361fac52daSPablo Neira Ayuso 	mutex_lock(&flow_indr_block_lock);
4371fac52daSPablo Neira Ayuso 	list_for_each_entry(indr_dev, &flow_block_indr_dev_list, list) {
4381fac52daSPablo Neira Ayuso 		if (indr_dev->cb == cb &&
4391fac52daSPablo Neira Ayuso 		    indr_dev->cb_priv == cb_priv) {
4401fac52daSPablo Neira Ayuso 			refcount_inc(&indr_dev->refcnt);
4411fac52daSPablo Neira Ayuso 			mutex_unlock(&flow_indr_block_lock);
4421fac52daSPablo Neira Ayuso 			return 0;
4431fac52daSPablo Neira Ayuso 		}
4441fac52daSPablo Neira Ayuso 	}
4451fac52daSPablo Neira Ayuso 
4461fac52daSPablo Neira Ayuso 	indr_dev = flow_indr_dev_alloc(cb, cb_priv);
4471fac52daSPablo Neira Ayuso 	if (!indr_dev) {
4481fac52daSPablo Neira Ayuso 		mutex_unlock(&flow_indr_block_lock);
4491fac52daSPablo Neira Ayuso 		return -ENOMEM;
4501fac52daSPablo Neira Ayuso 	}
4511fac52daSPablo Neira Ayuso 
4521fac52daSPablo Neira Ayuso 	list_add(&indr_dev->list, &flow_block_indr_dev_list);
45374fc4f82SEli Cohen 	existing_qdiscs_register(cb, cb_priv);
4541fac52daSPablo Neira Ayuso 	mutex_unlock(&flow_indr_block_lock);
4551fac52daSPablo Neira Ayuso 
45613926d19SBaowen Zheng 	tcf_action_reoffload_cb(cb, cb_priv, true);
45713926d19SBaowen Zheng 
4581fac52daSPablo Neira Ayuso 	return 0;
4591fac52daSPablo Neira Ayuso }
4601fac52daSPablo Neira Ayuso EXPORT_SYMBOL(flow_indr_dev_register);
4611fac52daSPablo Neira Ayuso 
__flow_block_indr_cleanup(void (* release)(void * cb_priv),void * cb_priv,struct list_head * cleanup_list)462a1db2178Swenxu static void __flow_block_indr_cleanup(void (*release)(void *cb_priv),
463a1db2178Swenxu 				      void *cb_priv,
4641fac52daSPablo Neira Ayuso 				      struct list_head *cleanup_list)
4651fac52daSPablo Neira Ayuso {
4661fac52daSPablo Neira Ayuso 	struct flow_block_cb *this, *next;
4671fac52daSPablo Neira Ayuso 
4681fac52daSPablo Neira Ayuso 	list_for_each_entry_safe(this, next, &flow_block_indr_list, indr.list) {
469a1db2178Swenxu 		if (this->release == release &&
4705137d303SChris Mi 		    this->indr.cb_priv == cb_priv)
4711fac52daSPablo Neira Ayuso 			list_move(&this->indr.list, cleanup_list);
4721fac52daSPablo Neira Ayuso 	}
4731fac52daSPablo Neira Ayuso }
4741fac52daSPablo Neira Ayuso 
flow_block_indr_notify(struct list_head * cleanup_list)4751fac52daSPablo Neira Ayuso static void flow_block_indr_notify(struct list_head *cleanup_list)
4761fac52daSPablo Neira Ayuso {
4771fac52daSPablo Neira Ayuso 	struct flow_block_cb *this, *next;
4781fac52daSPablo Neira Ayuso 
4791fac52daSPablo Neira Ayuso 	list_for_each_entry_safe(this, next, cleanup_list, indr.list) {
4801fac52daSPablo Neira Ayuso 		list_del(&this->indr.list);
4811fac52daSPablo Neira Ayuso 		this->indr.cleanup(this);
4821fac52daSPablo Neira Ayuso 	}
4831fac52daSPablo Neira Ayuso }
4841fac52daSPablo Neira Ayuso 
flow_indr_dev_unregister(flow_indr_block_bind_cb_t * cb,void * cb_priv,void (* release)(void * cb_priv))4851fac52daSPablo Neira Ayuso void flow_indr_dev_unregister(flow_indr_block_bind_cb_t *cb, void *cb_priv,
486a1db2178Swenxu 			      void (*release)(void *cb_priv))
4871fac52daSPablo Neira Ayuso {
4881fac52daSPablo Neira Ayuso 	struct flow_indr_dev *this, *next, *indr_dev = NULL;
4891fac52daSPablo Neira Ayuso 	LIST_HEAD(cleanup_list);
4901fac52daSPablo Neira Ayuso 
4911fac52daSPablo Neira Ayuso 	mutex_lock(&flow_indr_block_lock);
4921fac52daSPablo Neira Ayuso 	list_for_each_entry_safe(this, next, &flow_block_indr_dev_list, list) {
4931fac52daSPablo Neira Ayuso 		if (this->cb == cb &&
4941fac52daSPablo Neira Ayuso 		    this->cb_priv == cb_priv &&
4951fac52daSPablo Neira Ayuso 		    refcount_dec_and_test(&this->refcnt)) {
4961fac52daSPablo Neira Ayuso 			indr_dev = this;
4971fac52daSPablo Neira Ayuso 			list_del(&indr_dev->list);
4981fac52daSPablo Neira Ayuso 			break;
4991fac52daSPablo Neira Ayuso 		}
5001fac52daSPablo Neira Ayuso 	}
5011fac52daSPablo Neira Ayuso 
5021fac52daSPablo Neira Ayuso 	if (!indr_dev) {
5031fac52daSPablo Neira Ayuso 		mutex_unlock(&flow_indr_block_lock);
5041fac52daSPablo Neira Ayuso 		return;
5051fac52daSPablo Neira Ayuso 	}
5061fac52daSPablo Neira Ayuso 
507a1db2178Swenxu 	__flow_block_indr_cleanup(release, cb_priv, &cleanup_list);
5081fac52daSPablo Neira Ayuso 	mutex_unlock(&flow_indr_block_lock);
5091fac52daSPablo Neira Ayuso 
51013926d19SBaowen Zheng 	tcf_action_reoffload_cb(cb, cb_priv, false);
5111fac52daSPablo Neira Ayuso 	flow_block_indr_notify(&cleanup_list);
5121fac52daSPablo Neira Ayuso 	kfree(indr_dev);
5131fac52daSPablo Neira Ayuso }
5141fac52daSPablo Neira Ayuso EXPORT_SYMBOL(flow_indr_dev_unregister);
5151fac52daSPablo Neira Ayuso 
flow_block_indr_init(struct flow_block_cb * flow_block,struct flow_block_offload * bo,struct net_device * dev,struct Qdisc * sch,void * data,void * cb_priv,void (* cleanup)(struct flow_block_cb * block_cb))5161fac52daSPablo Neira Ayuso static void flow_block_indr_init(struct flow_block_cb *flow_block,
5171fac52daSPablo Neira Ayuso 				 struct flow_block_offload *bo,
518c40f4e50SPetr Machata 				 struct net_device *dev, struct Qdisc *sch, void *data,
519a1db2178Swenxu 				 void *cb_priv,
5201fac52daSPablo Neira Ayuso 				 void (*cleanup)(struct flow_block_cb *block_cb))
5211fac52daSPablo Neira Ayuso {
5221fac52daSPablo Neira Ayuso 	flow_block->indr.binder_type = bo->binder_type;
5231fac52daSPablo Neira Ayuso 	flow_block->indr.data = data;
524a1db2178Swenxu 	flow_block->indr.cb_priv = cb_priv;
5251fac52daSPablo Neira Ayuso 	flow_block->indr.dev = dev;
526c40f4e50SPetr Machata 	flow_block->indr.sch = sch;
5271fac52daSPablo Neira Ayuso 	flow_block->indr.cleanup = cleanup;
5281fac52daSPablo Neira Ayuso }
5291fac52daSPablo Neira Ayuso 
flow_indr_block_cb_alloc(flow_setup_cb_t * cb,void * cb_ident,void * cb_priv,void (* release)(void * cb_priv),struct flow_block_offload * bo,struct net_device * dev,struct Qdisc * sch,void * data,void * indr_cb_priv,void (* cleanup)(struct flow_block_cb * block_cb))53026f2eb27Swenxu struct flow_block_cb *flow_indr_block_cb_alloc(flow_setup_cb_t *cb,
53126f2eb27Swenxu 					       void *cb_ident, void *cb_priv,
53226f2eb27Swenxu 					       void (*release)(void *cb_priv),
53326f2eb27Swenxu 					       struct flow_block_offload *bo,
534c40f4e50SPetr Machata 					       struct net_device *dev,
535c40f4e50SPetr Machata 					       struct Qdisc *sch, void *data,
536a1db2178Swenxu 					       void *indr_cb_priv,
53726f2eb27Swenxu 					       void (*cleanup)(struct flow_block_cb *block_cb))
53826f2eb27Swenxu {
53926f2eb27Swenxu 	struct flow_block_cb *block_cb;
54026f2eb27Swenxu 
54126f2eb27Swenxu 	block_cb = flow_block_cb_alloc(cb, cb_ident, cb_priv, release);
54226f2eb27Swenxu 	if (IS_ERR(block_cb))
54326f2eb27Swenxu 		goto out;
54426f2eb27Swenxu 
545c40f4e50SPetr Machata 	flow_block_indr_init(block_cb, bo, dev, sch, data, indr_cb_priv, cleanup);
54626f2eb27Swenxu 	list_add(&block_cb->indr.list, &flow_block_indr_list);
54726f2eb27Swenxu 
54826f2eb27Swenxu out:
54926f2eb27Swenxu 	return block_cb;
55026f2eb27Swenxu }
55126f2eb27Swenxu EXPORT_SYMBOL(flow_indr_block_cb_alloc);
55226f2eb27Swenxu 
find_indir_dev(void * data)55374fc4f82SEli Cohen static struct flow_indir_dev_info *find_indir_dev(void *data)
55474fc4f82SEli Cohen {
55574fc4f82SEli Cohen 	struct flow_indir_dev_info *cur;
55674fc4f82SEli Cohen 
55774fc4f82SEli Cohen 	list_for_each_entry(cur, &flow_indir_dev_list, list) {
55874fc4f82SEli Cohen 		if (cur->data == data)
55974fc4f82SEli Cohen 			return cur;
56074fc4f82SEli Cohen 	}
56174fc4f82SEli Cohen 	return NULL;
56274fc4f82SEli Cohen }
56374fc4f82SEli Cohen 
indir_dev_add(void * data,struct net_device * dev,struct Qdisc * sch,enum tc_setup_type type,void (* cleanup)(struct flow_block_cb * block_cb),struct flow_block_offload * bo)56474fc4f82SEli Cohen static int indir_dev_add(void *data, struct net_device *dev, struct Qdisc *sch,
56574fc4f82SEli Cohen 			 enum tc_setup_type type, void (*cleanup)(struct flow_block_cb *block_cb),
56674fc4f82SEli Cohen 			 struct flow_block_offload *bo)
56774fc4f82SEli Cohen {
56874fc4f82SEli Cohen 	struct flow_indir_dev_info *info;
56974fc4f82SEli Cohen 
57074fc4f82SEli Cohen 	info = find_indir_dev(data);
57174fc4f82SEli Cohen 	if (info)
57274fc4f82SEli Cohen 		return -EEXIST;
57374fc4f82SEli Cohen 
57474fc4f82SEli Cohen 	info = kzalloc(sizeof(*info), GFP_KERNEL);
57574fc4f82SEli Cohen 	if (!info)
57674fc4f82SEli Cohen 		return -ENOMEM;
57774fc4f82SEli Cohen 
57874fc4f82SEli Cohen 	info->data = data;
57974fc4f82SEli Cohen 	info->dev = dev;
58074fc4f82SEli Cohen 	info->sch = sch;
58174fc4f82SEli Cohen 	info->type = type;
58274fc4f82SEli Cohen 	info->cleanup = cleanup;
58374fc4f82SEli Cohen 	info->command = bo->command;
58474fc4f82SEli Cohen 	info->binder_type = bo->binder_type;
58574fc4f82SEli Cohen 	info->cb_list = bo->cb_list_head;
58674fc4f82SEli Cohen 
58774fc4f82SEli Cohen 	list_add(&info->list, &flow_indir_dev_list);
58874fc4f82SEli Cohen 	return 0;
58974fc4f82SEli Cohen }
59074fc4f82SEli Cohen 
indir_dev_remove(void * data)59174fc4f82SEli Cohen static int indir_dev_remove(void *data)
59274fc4f82SEli Cohen {
59374fc4f82SEli Cohen 	struct flow_indir_dev_info *info;
59474fc4f82SEli Cohen 
59574fc4f82SEli Cohen 	info = find_indir_dev(data);
59674fc4f82SEli Cohen 	if (!info)
59774fc4f82SEli Cohen 		return -ENOENT;
59874fc4f82SEli Cohen 
59974fc4f82SEli Cohen 	list_del(&info->list);
60074fc4f82SEli Cohen 
60174fc4f82SEli Cohen 	kfree(info);
60274fc4f82SEli Cohen 	return 0;
60374fc4f82SEli Cohen }
60474fc4f82SEli Cohen 
flow_indr_dev_setup_offload(struct net_device * dev,struct Qdisc * sch,enum tc_setup_type type,void * data,struct flow_block_offload * bo,void (* cleanup)(struct flow_block_cb * block_cb))605c40f4e50SPetr Machata int flow_indr_dev_setup_offload(struct net_device *dev,	struct Qdisc *sch,
6061fac52daSPablo Neira Ayuso 				enum tc_setup_type type, void *data,
6071fac52daSPablo Neira Ayuso 				struct flow_block_offload *bo,
6081fac52daSPablo Neira Ayuso 				void (*cleanup)(struct flow_block_cb *block_cb))
6091fac52daSPablo Neira Ayuso {
6101fac52daSPablo Neira Ayuso 	struct flow_indr_dev *this;
6118cbfe939SBaowen Zheng 	u32 count = 0;
6128cbfe939SBaowen Zheng 	int err;
6131fac52daSPablo Neira Ayuso 
6141fac52daSPablo Neira Ayuso 	mutex_lock(&flow_indr_block_lock);
6158cbfe939SBaowen Zheng 	if (bo) {
61674fc4f82SEli Cohen 		if (bo->command == FLOW_BLOCK_BIND)
61774fc4f82SEli Cohen 			indir_dev_add(data, dev, sch, type, cleanup, bo);
61874fc4f82SEli Cohen 		else if (bo->command == FLOW_BLOCK_UNBIND)
61974fc4f82SEli Cohen 			indir_dev_remove(data);
6208cbfe939SBaowen Zheng 	}
62174fc4f82SEli Cohen 
6228cbfe939SBaowen Zheng 	list_for_each_entry(this, &flow_block_indr_dev_list, list) {
6238cbfe939SBaowen Zheng 		err = this->cb(dev, sch, this->cb_priv, type, bo, data, cleanup);
6248cbfe939SBaowen Zheng 		if (!err)
6258cbfe939SBaowen Zheng 			count++;
6268cbfe939SBaowen Zheng 	}
6271fac52daSPablo Neira Ayuso 
6281fac52daSPablo Neira Ayuso 	mutex_unlock(&flow_indr_block_lock);
6291fac52daSPablo Neira Ayuso 
6308cbfe939SBaowen Zheng 	return (bo && list_empty(&bo->cb_list)) ? -EOPNOTSUPP : count;
6311fac52daSPablo Neira Ayuso }
6321fac52daSPablo Neira Ayuso EXPORT_SYMBOL(flow_indr_dev_setup_offload);
6333a41c64dSPablo Neira Ayuso 
flow_indr_dev_exists(void)6343a41c64dSPablo Neira Ayuso bool flow_indr_dev_exists(void)
6353a41c64dSPablo Neira Ayuso {
6363a41c64dSPablo Neira Ayuso 	return !list_empty(&flow_block_indr_dev_list);
6373a41c64dSPablo Neira Ayuso }
6383a41c64dSPablo Neira Ayuso EXPORT_SYMBOL(flow_indr_dev_exists);
639