1e8f887acSAmir Vadai /*
2e8f887acSAmir Vadai  * Copyright (c) 2016, Mellanox Technologies. All rights reserved.
3e8f887acSAmir Vadai  *
4e8f887acSAmir Vadai  * This software is available to you under a choice of one of two
5e8f887acSAmir Vadai  * licenses.  You may choose to be licensed under the terms of the GNU
6e8f887acSAmir Vadai  * General Public License (GPL) Version 2, available from the file
7e8f887acSAmir Vadai  * COPYING in the main directory of this source tree, or the
8e8f887acSAmir Vadai  * OpenIB.org BSD license below:
9e8f887acSAmir Vadai  *
10e8f887acSAmir Vadai  *     Redistribution and use in source and binary forms, with or
11e8f887acSAmir Vadai  *     without modification, are permitted provided that the following
12e8f887acSAmir Vadai  *     conditions are met:
13e8f887acSAmir Vadai  *
14e8f887acSAmir Vadai  *      - Redistributions of source code must retain the above
15e8f887acSAmir Vadai  *        copyright notice, this list of conditions and the following
16e8f887acSAmir Vadai  *        disclaimer.
17e8f887acSAmir Vadai  *
18e8f887acSAmir Vadai  *      - Redistributions in binary form must reproduce the above
19e8f887acSAmir Vadai  *        copyright notice, this list of conditions and the following
20e8f887acSAmir Vadai  *        disclaimer in the documentation and/or other materials
21e8f887acSAmir Vadai  *        provided with the distribution.
22e8f887acSAmir Vadai  *
23e8f887acSAmir Vadai  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24e8f887acSAmir Vadai  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25e8f887acSAmir Vadai  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26e8f887acSAmir Vadai  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27e8f887acSAmir Vadai  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28e8f887acSAmir Vadai  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29e8f887acSAmir Vadai  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30e8f887acSAmir Vadai  * SOFTWARE.
31e8f887acSAmir Vadai  */
32e8f887acSAmir Vadai 
33e3a2b7edSAmir Vadai #include <net/flow_dissector.h>
34e3a2b7edSAmir Vadai #include <net/pkt_cls.h>
35e3a2b7edSAmir Vadai #include <net/tc_act/tc_gact.h>
3612185a9fSAmir Vadai #include <net/tc_act/tc_skbedit.h>
37e8f887acSAmir Vadai #include <linux/mlx5/fs.h>
38e8f887acSAmir Vadai #include <linux/mlx5/device.h>
39e8f887acSAmir Vadai #include <linux/rhashtable.h>
40e8f887acSAmir Vadai #include "en.h"
41e8f887acSAmir Vadai #include "en_tc.h"
42e8f887acSAmir Vadai 
43e8f887acSAmir Vadai struct mlx5e_tc_flow {
44e8f887acSAmir Vadai 	struct rhash_head	node;
45e8f887acSAmir Vadai 	u64			cookie;
46e8f887acSAmir Vadai 	struct mlx5_flow_rule	*rule;
47e8f887acSAmir Vadai };
48e8f887acSAmir Vadai 
49acff797cSMaor Gottlieb #define MLX5E_TC_TABLE_NUM_ENTRIES 1024
50acff797cSMaor Gottlieb #define MLX5E_TC_TABLE_NUM_GROUPS 4
51e8f887acSAmir Vadai 
52e8f887acSAmir Vadai static struct mlx5_flow_rule *mlx5e_tc_add_flow(struct mlx5e_priv *priv,
53e8f887acSAmir Vadai 						u32 *match_c, u32 *match_v,
54e8f887acSAmir Vadai 						u32 action, u32 flow_tag)
55e8f887acSAmir Vadai {
56e8f887acSAmir Vadai 	struct mlx5_flow_destination dest = {
57e8f887acSAmir Vadai 		.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE,
58acff797cSMaor Gottlieb 		{.ft = priv->fs.vlan.ft.t},
59e8f887acSAmir Vadai 	};
60e8f887acSAmir Vadai 	struct mlx5_flow_rule *rule;
61e8f887acSAmir Vadai 	bool table_created = false;
62e8f887acSAmir Vadai 
63acff797cSMaor Gottlieb 	if (IS_ERR_OR_NULL(priv->fs.tc.t)) {
64acff797cSMaor Gottlieb 		priv->fs.tc.t =
65acff797cSMaor Gottlieb 			mlx5_create_auto_grouped_flow_table(priv->fs.ns,
66acff797cSMaor Gottlieb 							    MLX5E_TC_PRIO,
67acff797cSMaor Gottlieb 							    MLX5E_TC_TABLE_NUM_ENTRIES,
68acff797cSMaor Gottlieb 							    MLX5E_TC_TABLE_NUM_GROUPS,
69d63cd286SMaor Gottlieb 							    0);
70acff797cSMaor Gottlieb 		if (IS_ERR(priv->fs.tc.t)) {
71e8f887acSAmir Vadai 			netdev_err(priv->netdev,
72e8f887acSAmir Vadai 				   "Failed to create tc offload table\n");
73acff797cSMaor Gottlieb 			return ERR_CAST(priv->fs.tc.t);
74e8f887acSAmir Vadai 		}
75e8f887acSAmir Vadai 
76e8f887acSAmir Vadai 		table_created = true;
77e8f887acSAmir Vadai 	}
78e8f887acSAmir Vadai 
79acff797cSMaor Gottlieb 	rule = mlx5_add_flow_rule(priv->fs.tc.t, MLX5_MATCH_OUTER_HEADERS,
80e8f887acSAmir Vadai 				  match_c, match_v,
81e8f887acSAmir Vadai 				  action, flow_tag,
82e8f887acSAmir Vadai 				  action & MLX5_FLOW_CONTEXT_ACTION_FWD_DEST ? &dest : NULL);
83e8f887acSAmir Vadai 
84e8f887acSAmir Vadai 	if (IS_ERR(rule) && table_created) {
85acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(priv->fs.tc.t);
86acff797cSMaor Gottlieb 		priv->fs.tc.t = NULL;
87e8f887acSAmir Vadai 	}
88e8f887acSAmir Vadai 
89e8f887acSAmir Vadai 	return rule;
90e8f887acSAmir Vadai }
91e8f887acSAmir Vadai 
92e8f887acSAmir Vadai static void mlx5e_tc_del_flow(struct mlx5e_priv *priv,
93e8f887acSAmir Vadai 			      struct mlx5_flow_rule *rule)
94e8f887acSAmir Vadai {
95e8f887acSAmir Vadai 	mlx5_del_flow_rule(rule);
96e8f887acSAmir Vadai 
97e8f887acSAmir Vadai 	if (!mlx5e_tc_num_filters(priv)) {
98acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(priv->fs.tc.t);
99acff797cSMaor Gottlieb 		priv->fs.tc.t = NULL;
100e8f887acSAmir Vadai 	}
101e8f887acSAmir Vadai }
102e8f887acSAmir Vadai 
103e3a2b7edSAmir Vadai static int parse_cls_flower(struct mlx5e_priv *priv,
104e3a2b7edSAmir Vadai 			    u32 *match_c, u32 *match_v,
105e3a2b7edSAmir Vadai 			    struct tc_cls_flower_offload *f)
106e3a2b7edSAmir Vadai {
107e3a2b7edSAmir Vadai 	void *headers_c = MLX5_ADDR_OF(fte_match_param, match_c, outer_headers);
108e3a2b7edSAmir Vadai 	void *headers_v = MLX5_ADDR_OF(fte_match_param, match_v, outer_headers);
109e3a2b7edSAmir Vadai 	u16 addr_type = 0;
110e3a2b7edSAmir Vadai 	u8 ip_proto = 0;
111e3a2b7edSAmir Vadai 
112e3a2b7edSAmir Vadai 	if (f->dissector->used_keys &
113e3a2b7edSAmir Vadai 	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
114e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
115e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
116e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
117e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
118e3a2b7edSAmir Vadai 	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
119e3a2b7edSAmir Vadai 		netdev_warn(priv->netdev, "Unsupported key used: 0x%x\n",
120e3a2b7edSAmir Vadai 			    f->dissector->used_keys);
121e3a2b7edSAmir Vadai 		return -EOPNOTSUPP;
122e3a2b7edSAmir Vadai 	}
123e3a2b7edSAmir Vadai 
124e3a2b7edSAmir Vadai 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) {
125e3a2b7edSAmir Vadai 		struct flow_dissector_key_control *key =
126e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
127e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_BASIC,
128e3a2b7edSAmir Vadai 						  f->key);
129e3a2b7edSAmir Vadai 		addr_type = key->addr_type;
130e3a2b7edSAmir Vadai 	}
131e3a2b7edSAmir Vadai 
132e3a2b7edSAmir Vadai 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) {
133e3a2b7edSAmir Vadai 		struct flow_dissector_key_basic *key =
134e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
135e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_BASIC,
136e3a2b7edSAmir Vadai 						  f->key);
137e3a2b7edSAmir Vadai 		struct flow_dissector_key_basic *mask =
138e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
139e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_BASIC,
140e3a2b7edSAmir Vadai 						  f->mask);
141e3a2b7edSAmir Vadai 		ip_proto = key->ip_proto;
142e3a2b7edSAmir Vadai 
143e3a2b7edSAmir Vadai 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ethertype,
144e3a2b7edSAmir Vadai 			 ntohs(mask->n_proto));
145e3a2b7edSAmir Vadai 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ethertype,
146e3a2b7edSAmir Vadai 			 ntohs(key->n_proto));
147e3a2b7edSAmir Vadai 
148e3a2b7edSAmir Vadai 		MLX5_SET(fte_match_set_lyr_2_4, headers_c, ip_protocol,
149e3a2b7edSAmir Vadai 			 mask->ip_proto);
150e3a2b7edSAmir Vadai 		MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
151e3a2b7edSAmir Vadai 			 key->ip_proto);
152e3a2b7edSAmir Vadai 	}
153e3a2b7edSAmir Vadai 
154e3a2b7edSAmir Vadai 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
155e3a2b7edSAmir Vadai 		struct flow_dissector_key_eth_addrs *key =
156e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
157e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
158e3a2b7edSAmir Vadai 						  f->key);
159e3a2b7edSAmir Vadai 		struct flow_dissector_key_eth_addrs *mask =
160e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
161e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_ETH_ADDRS,
162e3a2b7edSAmir Vadai 						  f->mask);
163e3a2b7edSAmir Vadai 
164e3a2b7edSAmir Vadai 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
165e3a2b7edSAmir Vadai 					     dmac_47_16),
166e3a2b7edSAmir Vadai 				mask->dst);
167e3a2b7edSAmir Vadai 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
168e3a2b7edSAmir Vadai 					     dmac_47_16),
169e3a2b7edSAmir Vadai 				key->dst);
170e3a2b7edSAmir Vadai 
171e3a2b7edSAmir Vadai 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
172e3a2b7edSAmir Vadai 					     smac_47_16),
173e3a2b7edSAmir Vadai 				mask->src);
174e3a2b7edSAmir Vadai 		ether_addr_copy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
175e3a2b7edSAmir Vadai 					     smac_47_16),
176e3a2b7edSAmir Vadai 				key->src);
177e3a2b7edSAmir Vadai 	}
178e3a2b7edSAmir Vadai 
179e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
180e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv4_addrs *key =
181e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
182e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
183e3a2b7edSAmir Vadai 						  f->key);
184e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv4_addrs *mask =
185e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
186e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV4_ADDRS,
187e3a2b7edSAmir Vadai 						  f->mask);
188e3a2b7edSAmir Vadai 
189e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
190e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
191e3a2b7edSAmir Vadai 		       &mask->src, sizeof(mask->src));
192e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
193e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv4_layout.ipv4),
194e3a2b7edSAmir Vadai 		       &key->src, sizeof(key->src));
195e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
196e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
197e3a2b7edSAmir Vadai 		       &mask->dst, sizeof(mask->dst));
198e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
199e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv4_layout.ipv4),
200e3a2b7edSAmir Vadai 		       &key->dst, sizeof(key->dst));
201e3a2b7edSAmir Vadai 	}
202e3a2b7edSAmir Vadai 
203e3a2b7edSAmir Vadai 	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
204e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv6_addrs *key =
205e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
206e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
207e3a2b7edSAmir Vadai 						  f->key);
208e3a2b7edSAmir Vadai 		struct flow_dissector_key_ipv6_addrs *mask =
209e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
210e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_IPV6_ADDRS,
211e3a2b7edSAmir Vadai 						  f->mask);
212e3a2b7edSAmir Vadai 
213e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
214e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
215e3a2b7edSAmir Vadai 		       &mask->src, sizeof(mask->src));
216e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
217e3a2b7edSAmir Vadai 				    src_ipv4_src_ipv6.ipv6_layout.ipv6),
218e3a2b7edSAmir Vadai 		       &key->src, sizeof(key->src));
219e3a2b7edSAmir Vadai 
220e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_c,
221e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
222e3a2b7edSAmir Vadai 		       &mask->dst, sizeof(mask->dst));
223e3a2b7edSAmir Vadai 		memcpy(MLX5_ADDR_OF(fte_match_set_lyr_2_4, headers_v,
224e3a2b7edSAmir Vadai 				    dst_ipv4_dst_ipv6.ipv6_layout.ipv6),
225e3a2b7edSAmir Vadai 		       &key->dst, sizeof(key->dst));
226e3a2b7edSAmir Vadai 	}
227e3a2b7edSAmir Vadai 
228e3a2b7edSAmir Vadai 	if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) {
229e3a2b7edSAmir Vadai 		struct flow_dissector_key_ports *key =
230e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
231e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_PORTS,
232e3a2b7edSAmir Vadai 						  f->key);
233e3a2b7edSAmir Vadai 		struct flow_dissector_key_ports *mask =
234e3a2b7edSAmir Vadai 			skb_flow_dissector_target(f->dissector,
235e3a2b7edSAmir Vadai 						  FLOW_DISSECTOR_KEY_PORTS,
236e3a2b7edSAmir Vadai 						  f->mask);
237e3a2b7edSAmir Vadai 		switch (ip_proto) {
238e3a2b7edSAmir Vadai 		case IPPROTO_TCP:
239e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
240e3a2b7edSAmir Vadai 				 tcp_sport, ntohs(mask->src));
241e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
242e3a2b7edSAmir Vadai 				 tcp_sport, ntohs(key->src));
243e3a2b7edSAmir Vadai 
244e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
245e3a2b7edSAmir Vadai 				 tcp_dport, ntohs(mask->dst));
246e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
247e3a2b7edSAmir Vadai 				 tcp_dport, ntohs(key->dst));
248e3a2b7edSAmir Vadai 			break;
249e3a2b7edSAmir Vadai 
250e3a2b7edSAmir Vadai 		case IPPROTO_UDP:
251e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
252e3a2b7edSAmir Vadai 				 udp_sport, ntohs(mask->src));
253e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
254e3a2b7edSAmir Vadai 				 udp_sport, ntohs(key->src));
255e3a2b7edSAmir Vadai 
256e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_c,
257e3a2b7edSAmir Vadai 				 udp_dport, ntohs(mask->dst));
258e3a2b7edSAmir Vadai 			MLX5_SET(fte_match_set_lyr_2_4, headers_v,
259e3a2b7edSAmir Vadai 				 udp_dport, ntohs(key->dst));
260e3a2b7edSAmir Vadai 			break;
261e3a2b7edSAmir Vadai 		default:
262e3a2b7edSAmir Vadai 			netdev_err(priv->netdev,
263e3a2b7edSAmir Vadai 				   "Only UDP and TCP transport are supported\n");
264e3a2b7edSAmir Vadai 			return -EINVAL;
265e3a2b7edSAmir Vadai 		}
266e3a2b7edSAmir Vadai 	}
267e3a2b7edSAmir Vadai 
268e3a2b7edSAmir Vadai 	return 0;
269e3a2b7edSAmir Vadai }
270e3a2b7edSAmir Vadai 
271e3a2b7edSAmir Vadai static int parse_tc_actions(struct mlx5e_priv *priv, struct tcf_exts *exts,
272e3a2b7edSAmir Vadai 			    u32 *action, u32 *flow_tag)
273e3a2b7edSAmir Vadai {
274e3a2b7edSAmir Vadai 	const struct tc_action *a;
275e3a2b7edSAmir Vadai 
276e3a2b7edSAmir Vadai 	if (tc_no_actions(exts))
277e3a2b7edSAmir Vadai 		return -EINVAL;
278e3a2b7edSAmir Vadai 
279e3a2b7edSAmir Vadai 	*flow_tag = MLX5_FS_DEFAULT_FLOW_TAG;
280e3a2b7edSAmir Vadai 	*action = 0;
281e3a2b7edSAmir Vadai 
282e3a2b7edSAmir Vadai 	tc_for_each_action(a, exts) {
283e3a2b7edSAmir Vadai 		/* Only support a single action per rule */
284e3a2b7edSAmir Vadai 		if (*action)
285e3a2b7edSAmir Vadai 			return -EINVAL;
286e3a2b7edSAmir Vadai 
287e3a2b7edSAmir Vadai 		if (is_tcf_gact_shot(a)) {
288e3a2b7edSAmir Vadai 			*action |= MLX5_FLOW_CONTEXT_ACTION_DROP;
289e3a2b7edSAmir Vadai 			continue;
290e3a2b7edSAmir Vadai 		}
291e3a2b7edSAmir Vadai 
292e3a2b7edSAmir Vadai 		if (is_tcf_skbedit_mark(a)) {
293e3a2b7edSAmir Vadai 			u32 mark = tcf_skbedit_mark(a);
294e3a2b7edSAmir Vadai 
295e3a2b7edSAmir Vadai 			if (mark & ~MLX5E_TC_FLOW_ID_MASK) {
296e3a2b7edSAmir Vadai 				netdev_warn(priv->netdev, "Bad flow mark - only 16 bit is supported: 0x%x\n",
297e3a2b7edSAmir Vadai 					    mark);
298e3a2b7edSAmir Vadai 				return -EINVAL;
299e3a2b7edSAmir Vadai 			}
300e3a2b7edSAmir Vadai 
301e3a2b7edSAmir Vadai 			*flow_tag = mark;
302e3a2b7edSAmir Vadai 			*action |= MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
303e3a2b7edSAmir Vadai 			continue;
304e3a2b7edSAmir Vadai 		}
305e3a2b7edSAmir Vadai 
306e3a2b7edSAmir Vadai 		return -EINVAL;
307e3a2b7edSAmir Vadai 	}
308e3a2b7edSAmir Vadai 
309e3a2b7edSAmir Vadai 	return 0;
310e3a2b7edSAmir Vadai }
311e3a2b7edSAmir Vadai 
312e3a2b7edSAmir Vadai int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol,
313e3a2b7edSAmir Vadai 			   struct tc_cls_flower_offload *f)
314e3a2b7edSAmir Vadai {
315acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
316e3a2b7edSAmir Vadai 	u32 *match_c;
317e3a2b7edSAmir Vadai 	u32 *match_v;
318e3a2b7edSAmir Vadai 	int err = 0;
319e3a2b7edSAmir Vadai 	u32 flow_tag;
320e3a2b7edSAmir Vadai 	u32 action;
321e3a2b7edSAmir Vadai 	struct mlx5e_tc_flow *flow;
322e3a2b7edSAmir Vadai 	struct mlx5_flow_rule *old = NULL;
323e3a2b7edSAmir Vadai 
324e3a2b7edSAmir Vadai 	flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
325e3a2b7edSAmir Vadai 				      tc->ht_params);
326e3a2b7edSAmir Vadai 	if (flow)
327e3a2b7edSAmir Vadai 		old = flow->rule;
328e3a2b7edSAmir Vadai 	else
329e3a2b7edSAmir Vadai 		flow = kzalloc(sizeof(*flow), GFP_KERNEL);
330e3a2b7edSAmir Vadai 
331e3a2b7edSAmir Vadai 	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
332e3a2b7edSAmir Vadai 	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
333e3a2b7edSAmir Vadai 	if (!match_c || !match_v || !flow) {
334e3a2b7edSAmir Vadai 		err = -ENOMEM;
335e3a2b7edSAmir Vadai 		goto err_free;
336e3a2b7edSAmir Vadai 	}
337e3a2b7edSAmir Vadai 
338e3a2b7edSAmir Vadai 	flow->cookie = f->cookie;
339e3a2b7edSAmir Vadai 
340e3a2b7edSAmir Vadai 	err = parse_cls_flower(priv, match_c, match_v, f);
341e3a2b7edSAmir Vadai 	if (err < 0)
342e3a2b7edSAmir Vadai 		goto err_free;
343e3a2b7edSAmir Vadai 
344e3a2b7edSAmir Vadai 	err = parse_tc_actions(priv, f->exts, &action, &flow_tag);
345e3a2b7edSAmir Vadai 	if (err < 0)
346e3a2b7edSAmir Vadai 		goto err_free;
347e3a2b7edSAmir Vadai 
348e3a2b7edSAmir Vadai 	err = rhashtable_insert_fast(&tc->ht, &flow->node,
349e3a2b7edSAmir Vadai 				     tc->ht_params);
350e3a2b7edSAmir Vadai 	if (err)
351e3a2b7edSAmir Vadai 		goto err_free;
352e3a2b7edSAmir Vadai 
353e3a2b7edSAmir Vadai 	flow->rule = mlx5e_tc_add_flow(priv, match_c, match_v, action,
354e3a2b7edSAmir Vadai 				       flow_tag);
355e3a2b7edSAmir Vadai 	if (IS_ERR(flow->rule)) {
356e3a2b7edSAmir Vadai 		err = PTR_ERR(flow->rule);
357e3a2b7edSAmir Vadai 		goto err_hash_del;
358e3a2b7edSAmir Vadai 	}
359e3a2b7edSAmir Vadai 
360e3a2b7edSAmir Vadai 	if (old)
361e3a2b7edSAmir Vadai 		mlx5e_tc_del_flow(priv, old);
362e3a2b7edSAmir Vadai 
363e3a2b7edSAmir Vadai 	goto out;
364e3a2b7edSAmir Vadai 
365e3a2b7edSAmir Vadai err_hash_del:
366e3a2b7edSAmir Vadai 	rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
367e3a2b7edSAmir Vadai 
368e3a2b7edSAmir Vadai err_free:
369e3a2b7edSAmir Vadai 	if (!old)
370e3a2b7edSAmir Vadai 		kfree(flow);
371e3a2b7edSAmir Vadai out:
372e3a2b7edSAmir Vadai 	kfree(match_c);
373e3a2b7edSAmir Vadai 	kfree(match_v);
374e3a2b7edSAmir Vadai 	return err;
375e3a2b7edSAmir Vadai }
376e3a2b7edSAmir Vadai 
377e3a2b7edSAmir Vadai int mlx5e_delete_flower(struct mlx5e_priv *priv,
378e3a2b7edSAmir Vadai 			struct tc_cls_flower_offload *f)
379e3a2b7edSAmir Vadai {
380e3a2b7edSAmir Vadai 	struct mlx5e_tc_flow *flow;
381acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
382e3a2b7edSAmir Vadai 
383e3a2b7edSAmir Vadai 	flow = rhashtable_lookup_fast(&tc->ht, &f->cookie,
384e3a2b7edSAmir Vadai 				      tc->ht_params);
385e3a2b7edSAmir Vadai 	if (!flow)
386e3a2b7edSAmir Vadai 		return -EINVAL;
387e3a2b7edSAmir Vadai 
388e3a2b7edSAmir Vadai 	rhashtable_remove_fast(&tc->ht, &flow->node, tc->ht_params);
389e3a2b7edSAmir Vadai 
390e3a2b7edSAmir Vadai 	mlx5e_tc_del_flow(priv, flow->rule);
391e3a2b7edSAmir Vadai 
392e3a2b7edSAmir Vadai 	kfree(flow);
393e3a2b7edSAmir Vadai 
394e3a2b7edSAmir Vadai 	return 0;
395e3a2b7edSAmir Vadai }
396e3a2b7edSAmir Vadai 
397e8f887acSAmir Vadai static const struct rhashtable_params mlx5e_tc_flow_ht_params = {
398e8f887acSAmir Vadai 	.head_offset = offsetof(struct mlx5e_tc_flow, node),
399e8f887acSAmir Vadai 	.key_offset = offsetof(struct mlx5e_tc_flow, cookie),
400e8f887acSAmir Vadai 	.key_len = sizeof(((struct mlx5e_tc_flow *)0)->cookie),
401e8f887acSAmir Vadai 	.automatic_shrinking = true,
402e8f887acSAmir Vadai };
403e8f887acSAmir Vadai 
404e8f887acSAmir Vadai int mlx5e_tc_init(struct mlx5e_priv *priv)
405e8f887acSAmir Vadai {
406acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
407e8f887acSAmir Vadai 
408e8f887acSAmir Vadai 	tc->ht_params = mlx5e_tc_flow_ht_params;
409e8f887acSAmir Vadai 	return rhashtable_init(&tc->ht, &tc->ht_params);
410e8f887acSAmir Vadai }
411e8f887acSAmir Vadai 
412e8f887acSAmir Vadai static void _mlx5e_tc_del_flow(void *ptr, void *arg)
413e8f887acSAmir Vadai {
414e8f887acSAmir Vadai 	struct mlx5e_tc_flow *flow = ptr;
415e8f887acSAmir Vadai 	struct mlx5e_priv *priv = arg;
416e8f887acSAmir Vadai 
417e8f887acSAmir Vadai 	mlx5e_tc_del_flow(priv, flow->rule);
418e8f887acSAmir Vadai 	kfree(flow);
419e8f887acSAmir Vadai }
420e8f887acSAmir Vadai 
421e8f887acSAmir Vadai void mlx5e_tc_cleanup(struct mlx5e_priv *priv)
422e8f887acSAmir Vadai {
423acff797cSMaor Gottlieb 	struct mlx5e_tc_table *tc = &priv->fs.tc;
424e8f887acSAmir Vadai 
425e8f887acSAmir Vadai 	rhashtable_free_and_destroy(&tc->ht, _mlx5e_tc_del_flow, priv);
426e8f887acSAmir Vadai 
427acff797cSMaor Gottlieb 	if (!IS_ERR_OR_NULL(tc->t)) {
428acff797cSMaor Gottlieb 		mlx5_destroy_flow_table(tc->t);
429acff797cSMaor Gottlieb 		tc->t = NULL;
430e8f887acSAmir Vadai 	}
431e8f887acSAmir Vadai }
432