1291939Shselasky/*-
2291939Shselasky * Copyright (c) 2013-2015, Mellanox Technologies, Ltd.  All rights reserved.
3291939Shselasky *
4291939Shselasky * Redistribution and use in source and binary forms, with or without
5291939Shselasky * modification, are permitted provided that the following conditions
6291939Shselasky * are met:
7291939Shselasky * 1. Redistributions of source code must retain the above copyright
8291939Shselasky *    notice, this list of conditions and the following disclaimer.
9291939Shselasky * 2. Redistributions in binary form must reproduce the above copyright
10291939Shselasky *    notice, this list of conditions and the following disclaimer in the
11291939Shselasky *    documentation and/or other materials provided with the distribution.
12291939Shselasky *
13291939Shselasky * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14291939Shselasky * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15291939Shselasky * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16291939Shselasky * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17291939Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18291939Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19291939Shselasky * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20291939Shselasky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21291939Shselasky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22291939Shselasky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23291939Shselasky * SUCH DAMAGE.
24291939Shselasky *
25291939Shselasky * $FreeBSD: releng/10.3/sys/dev/mlx5/mlx5_core/mlx5_eswitch_vacl.c 292196 2015-12-14 10:31:03Z hselasky $
26291939Shselasky */
27291939Shselasky
28292196Shselasky#include <linux/if_ether.h>
29291939Shselasky#include <linux/etherdevice.h>
30291939Shselasky#include <dev/mlx5/driver.h>
31291939Shselasky#include <dev/mlx5/flow_table.h>
32291939Shselasky#include <dev/mlx5/eswitch_vacl.h>
33291939Shselasky#include "mlx5_core.h"
34291939Shselasky
35291939Shselaskyenum {
36291939Shselasky	MLX5_ACL_LOOPBACK_GROUP_IDX	= 0,
37291939Shselasky	MLX5_ACL_UNTAGGED_GROUP_IDX	= 1,
38291939Shselasky	MLX5_ACL_VLAN_GROUP_IDX		= 2,
39291939Shselasky	MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX	= 3,
40291939Shselasky	MLX5_ACL_DEFAULT_GROUP_IDX	= 4,
41291939Shselasky	MLX5_ACL_GROUPS_NUM,
42291939Shselasky};
43291939Shselasky
44291939Shselaskystruct mlx_vacl_fr {
45291939Shselasky	bool			applied;
46291939Shselasky	u32			fi;
47291939Shselasky	u16			action;
48291939Shselasky};
49291939Shselasky
50291939Shselaskystruct mlx5_vacl_table {
51291939Shselasky	struct mlx5_core_dev	*dev;
52291939Shselasky	u16			vport;
53291939Shselasky	void			*ft;
54291939Shselasky	int			max_ft_size;
55291939Shselasky	int			acl_type;
56291939Shselasky
57291939Shselasky	struct mlx_vacl_fr	loopback_fr;
58291939Shselasky	struct mlx_vacl_fr	untagged_fr;
59291939Shselasky	struct mlx_vacl_fr	unknown_vlan_fr;
60291939Shselasky	struct mlx_vacl_fr	default_fr;
61291939Shselasky
62291939Shselasky	bool			vlan_filter_enabled;
63291939Shselasky	bool			vlan_filter_applied;
64291939Shselasky	unsigned long		*vlan_allowed_bitmap;
65291939Shselasky	u32			vlan_fi_table[4096];
66291939Shselasky
67291939Shselasky	bool			spoofchk_enabled;
68291939Shselasky	u8			smac[ETH_ALEN];
69291939Shselasky};
70291939Shselasky
71291939Shselaskystatic int mlx5_vacl_table_allow_vlan(void *acl_t, u16 vlan)
72291939Shselasky{
73291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
74291939Shselasky	u32 *flow_context = NULL;
75291939Shselasky	void *in_match_criteria = NULL;
76291939Shselasky	void *in_match_value = NULL;
77291939Shselasky	u8 *smac;
78291939Shselasky	int vlan_mc_enable = MLX5_MATCH_OUTER_HEADERS;
79291939Shselasky	int err = 0;
80291939Shselasky
81291939Shselasky	if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
82291939Shselasky		return -EINVAL;
83291939Shselasky
84291939Shselasky	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
85291939Shselasky	if (!flow_context) {
86291939Shselasky		err = -ENOMEM;
87291939Shselasky		goto out;
88291939Shselasky	}
89291939Shselasky
90291939Shselasky	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
91291939Shselasky	if (!in_match_criteria) {
92291939Shselasky		err = -ENOMEM;
93291939Shselasky		goto out;
94291939Shselasky	}
95291939Shselasky
96291939Shselasky	/* Apply vlan rule */
97291939Shselasky	MLX5_SET(flow_context, flow_context, action,
98291939Shselasky		 MLX5_FLOW_CONTEXT_ACTION_ALLOW);
99291939Shselasky	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
100291939Shselasky	MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 1);
101291939Shselasky	MLX5_SET(fte_match_param, in_match_value, outer_headers.first_vid,
102291939Shselasky		 vlan);
103291939Shselasky	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
104291939Shselasky	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.first_vid,
105291939Shselasky		 0xfff);
106291939Shselasky	if (acl_table->spoofchk_enabled) {
107291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
108291939Shselasky				    in_match_value,
109291939Shselasky				    outer_headers.smac_47_16);
110291939Shselasky		ether_addr_copy(smac, acl_table->smac);
111291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
112291939Shselasky				    in_match_criteria,
113291939Shselasky				    outer_headers.smac_47_16);
114291939Shselasky		memset(smac, 0xff, ETH_ALEN);
115291939Shselasky	}
116291939Shselasky	err = mlx5_add_flow_table_entry(acl_table->ft, vlan_mc_enable,
117291939Shselasky					in_match_criteria, flow_context,
118291939Shselasky					&acl_table->vlan_fi_table[vlan]);
119291939Shselaskyout:
120291939Shselasky	if (flow_context)
121291939Shselasky		vfree(flow_context);
122291939Shselasky	if (in_match_criteria)
123291939Shselasky		vfree(in_match_criteria);
124291939Shselasky	return err;
125291939Shselasky}
126291939Shselasky
127291939Shselaskystatic int mlx5_vacl_table_apply_loopback_filter(void *acl_t, u16 new_action)
128291939Shselasky{
129291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
130291939Shselasky	u8 loopback_mc_enable = MLX5_MATCH_MISC_PARAMETERS;
131291939Shselasky	u32 *flow_context = NULL;
132291939Shselasky	void *in_match_criteria = NULL;
133291939Shselasky	void *in_match_value = NULL;
134291939Shselasky	void *mv_misc = NULL;
135291939Shselasky	void *mc_misc = NULL;
136291939Shselasky	int err = 0;
137291939Shselasky
138291939Shselasky	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
139291939Shselasky	if (!flow_context) {
140291939Shselasky		err = -ENOMEM;
141291939Shselasky		goto out;
142291939Shselasky	}
143291939Shselasky
144291939Shselasky	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
145291939Shselasky	if (!in_match_criteria) {
146291939Shselasky		err = -ENOMEM;
147291939Shselasky		goto out;
148291939Shselasky	}
149291939Shselasky
150291939Shselasky	if (acl_table->loopback_fr.applied)
151291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
152291939Shselasky					  acl_table->loopback_fr.fi);
153291939Shselasky
154291939Shselasky	/* Apply new loopback rule */
155291939Shselasky	MLX5_SET(flow_context, flow_context, action, new_action);
156291939Shselasky	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
157291939Shselasky	mv_misc  = MLX5_ADDR_OF(fte_match_param, in_match_value,
158291939Shselasky				misc_parameters);
159291939Shselasky	mc_misc  = MLX5_ADDR_OF(fte_match_param, in_match_criteria,
160291939Shselasky				misc_parameters);
161291939Shselasky	MLX5_SET(fte_match_set_misc, mv_misc, source_port, acl_table->vport);
162291939Shselasky
163291939Shselasky	MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
164291939Shselasky
165291939Shselasky	err = mlx5_add_flow_table_entry(acl_table->ft, loopback_mc_enable,
166291939Shselasky					in_match_criteria, flow_context,
167291939Shselasky					&acl_table->loopback_fr.fi);
168291939Shselasky	if (err) {
169291939Shselasky		acl_table->loopback_fr.applied = false;
170291939Shselasky	} else {
171291939Shselasky		acl_table->loopback_fr.applied = true;
172291939Shselasky		acl_table->loopback_fr.action  = new_action;
173291939Shselasky	}
174291939Shselasky
175291939Shselaskyout:
176291939Shselasky	if (flow_context)
177291939Shselasky		vfree(flow_context);
178291939Shselasky	if (in_match_criteria)
179291939Shselasky		vfree(in_match_criteria);
180291939Shselasky	return err;
181291939Shselasky}
182291939Shselasky
183291939Shselaskystatic int mlx5_vacl_table_apply_default(void *acl_t, u16 new_action)
184291939Shselasky{
185291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
186291939Shselasky	u8 default_mc_enable = 0;
187291939Shselasky	u32 *flow_context = NULL;
188291939Shselasky	void *in_match_criteria = NULL;
189291939Shselasky	int err = 0;
190291939Shselasky
191291939Shselasky	if (!acl_table->spoofchk_enabled)
192291939Shselasky		return -EINVAL;
193291939Shselasky
194291939Shselasky	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
195291939Shselasky	if (!flow_context) {
196291939Shselasky		err = -ENOMEM;
197291939Shselasky		goto out;
198291939Shselasky	}
199291939Shselasky
200291939Shselasky	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
201291939Shselasky	if (!in_match_criteria) {
202291939Shselasky		err = -ENOMEM;
203291939Shselasky		goto out;
204291939Shselasky	}
205291939Shselasky
206291939Shselasky	if (acl_table->default_fr.applied)
207291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
208291939Shselasky					  acl_table->default_fr.fi);
209291939Shselasky
210291939Shselasky	/* Apply new default rule */
211291939Shselasky	MLX5_SET(flow_context, flow_context, action, new_action);
212291939Shselasky	err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
213291939Shselasky					in_match_criteria, flow_context,
214291939Shselasky					&acl_table->default_fr.fi);
215291939Shselasky	if (err) {
216291939Shselasky		acl_table->default_fr.applied = false;
217291939Shselasky	} else {
218291939Shselasky		acl_table->default_fr.applied = true;
219291939Shselasky		acl_table->default_fr.action  = new_action;
220291939Shselasky	}
221291939Shselasky
222291939Shselaskyout:
223291939Shselasky	if (flow_context)
224291939Shselasky		vfree(flow_context);
225291939Shselasky	if (in_match_criteria)
226291939Shselasky		vfree(in_match_criteria);
227291939Shselasky	return err;
228291939Shselasky}
229291939Shselasky
230291939Shselaskystatic int mlx5_vacl_table_apply_untagged(void *acl_t, u16 new_action)
231291939Shselasky{
232291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
233291939Shselasky	u8 untagged_mc_enable = MLX5_MATCH_OUTER_HEADERS;
234291939Shselasky	u8 *smac;
235291939Shselasky	u32 *flow_context = NULL;
236291939Shselasky	void *in_match_criteria = NULL;
237291939Shselasky	void *in_match_value = NULL;
238291939Shselasky	int err = 0;
239291939Shselasky
240291939Shselasky	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
241291939Shselasky	if (!flow_context) {
242291939Shselasky		err = -ENOMEM;
243291939Shselasky		goto out;
244291939Shselasky	}
245291939Shselasky
246291939Shselasky	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
247291939Shselasky	if (!in_match_criteria) {
248291939Shselasky		err = -ENOMEM;
249291939Shselasky		goto out;
250291939Shselasky	}
251291939Shselasky
252291939Shselasky	if (acl_table->untagged_fr.applied)
253291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
254291939Shselasky					  acl_table->untagged_fr.fi);
255291939Shselasky
256291939Shselasky	/* Apply new untagged rule */
257291939Shselasky	MLX5_SET(flow_context, flow_context, action, new_action);
258291939Shselasky	in_match_value = MLX5_ADDR_OF(flow_context, flow_context, match_value);
259291939Shselasky	MLX5_SET(fte_match_param, in_match_value, outer_headers.vlan_tag, 0);
260291939Shselasky	MLX5_SET(fte_match_param, in_match_criteria, outer_headers.vlan_tag, 1);
261291939Shselasky	if (acl_table->spoofchk_enabled) {
262291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
263291939Shselasky				    in_match_value,
264291939Shselasky				    outer_headers.smac_47_16);
265291939Shselasky		ether_addr_copy(smac, acl_table->smac);
266291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
267291939Shselasky				    in_match_criteria,
268291939Shselasky				    outer_headers.smac_47_16);
269291939Shselasky		memset(smac, 0xff, ETH_ALEN);
270291939Shselasky	}
271291939Shselasky	err = mlx5_add_flow_table_entry(acl_table->ft, untagged_mc_enable,
272291939Shselasky					in_match_criteria, flow_context,
273291939Shselasky					&acl_table->untagged_fr.fi);
274291939Shselasky	if (err) {
275291939Shselasky		acl_table->untagged_fr.applied = false;
276291939Shselasky	} else {
277291939Shselasky		acl_table->untagged_fr.applied = true;
278291939Shselasky		acl_table->untagged_fr.action  = new_action;
279291939Shselasky	}
280291939Shselasky
281291939Shselaskyout:
282291939Shselasky	if (flow_context)
283291939Shselasky		vfree(flow_context);
284291939Shselasky	if (in_match_criteria)
285291939Shselasky		vfree(in_match_criteria);
286291939Shselasky	return err;
287291939Shselasky}
288291939Shselasky
289291939Shselaskystatic int mlx5_vacl_table_apply_unknown_vlan(void *acl_t, u16 new_action)
290291939Shselasky{
291291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
292291939Shselasky	u8 default_mc_enable = (!acl_table->spoofchk_enabled) ? 0 :
293291939Shselasky				MLX5_MATCH_OUTER_HEADERS;
294291939Shselasky	u32 *flow_context = NULL;
295291939Shselasky	void *in_match_criteria = NULL;
296291939Shselasky	void *in_match_value = NULL;
297291939Shselasky	u8 *smac;
298291939Shselasky	int err = 0;
299291939Shselasky
300291939Shselasky	flow_context = mlx5_vzalloc(MLX5_ST_SZ_BYTES(flow_context));
301291939Shselasky	if (!flow_context) {
302291939Shselasky		err = -ENOMEM;
303291939Shselasky		goto out;
304291939Shselasky	}
305291939Shselasky
306291939Shselasky	in_match_criteria = mlx5_vzalloc(MLX5_ST_SZ_BYTES(fte_match_param));
307291939Shselasky	if (!in_match_criteria) {
308291939Shselasky		err = -ENOMEM;
309291939Shselasky		goto out;
310291939Shselasky	}
311291939Shselasky
312291939Shselasky	if (acl_table->unknown_vlan_fr.applied)
313291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
314291939Shselasky					  acl_table->unknown_vlan_fr.fi);
315291939Shselasky
316291939Shselasky	/* Apply new unknown vlan rule */
317291939Shselasky	MLX5_SET(flow_context, flow_context, action, new_action);
318291939Shselasky	if (acl_table->spoofchk_enabled) {
319291939Shselasky		in_match_value = MLX5_ADDR_OF(flow_context, flow_context,
320291939Shselasky					      match_value);
321291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
322291939Shselasky				    in_match_value,
323291939Shselasky				    outer_headers.smac_47_16);
324291939Shselasky		ether_addr_copy(smac, acl_table->smac);
325291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
326291939Shselasky				    in_match_criteria,
327291939Shselasky				    outer_headers.smac_47_16);
328291939Shselasky		memset(smac, 0xff, ETH_ALEN);
329291939Shselasky	}
330291939Shselasky	err = mlx5_add_flow_table_entry(acl_table->ft, default_mc_enable,
331291939Shselasky					in_match_criteria, flow_context,
332291939Shselasky					&acl_table->unknown_vlan_fr.fi);
333291939Shselasky	if (err) {
334291939Shselasky		acl_table->unknown_vlan_fr.applied = false;
335291939Shselasky	} else {
336291939Shselasky		acl_table->unknown_vlan_fr.applied = true;
337291939Shselasky		acl_table->unknown_vlan_fr.action  = new_action;
338291939Shselasky	}
339291939Shselasky
340291939Shselaskyout:
341291939Shselasky	if (flow_context)
342291939Shselasky		vfree(flow_context);
343291939Shselasky	if (in_match_criteria)
344291939Shselasky		vfree(in_match_criteria);
345291939Shselasky	return err;
346291939Shselasky}
347291939Shselasky
348291939Shselaskystatic int mlx5_vacl_table_apply_vlan_filter(void *acl_t)
349291939Shselasky{
350291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
351291939Shselasky	int index = 0;
352291939Shselasky	int err_index = 0;
353291939Shselasky	int err = 0;
354291939Shselasky
355291939Shselasky	if (acl_table->vlan_filter_applied)
356291939Shselasky		return 0;
357291939Shselasky
358291939Shselasky	for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
359291939Shselasky		index < 4096;
360291939Shselasky		index = find_next_bit(acl_table->vlan_allowed_bitmap,
361291939Shselasky				      4096, ++index)) {
362291939Shselasky		err = mlx5_vacl_table_allow_vlan(acl_t, index);
363291939Shselasky		if (err)
364291939Shselasky			goto err_disable_vlans;
365291939Shselasky	}
366291939Shselasky
367291939Shselasky	acl_table->vlan_filter_applied = true;
368291939Shselasky	return 0;
369291939Shselasky
370291939Shselaskyerr_disable_vlans:
371291939Shselasky	for (err_index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
372291939Shselasky		err_index < index;
373291939Shselasky		err_index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
374291939Shselasky					  ++err_index)) {
375291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
376291939Shselasky					  acl_table->vlan_fi_table[err_index]);
377291939Shselasky	}
378291939Shselasky	return err;
379291939Shselasky}
380291939Shselasky
381291939Shselaskystatic void mlx5_vacl_table_disapply_vlan_filter(void *acl_t)
382291939Shselasky{
383291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
384291939Shselasky	int index = 0;
385291939Shselasky
386291939Shselasky	if (!acl_table->vlan_filter_applied)
387291939Shselasky		return;
388291939Shselasky
389291939Shselasky	for (index = find_first_bit(acl_table->vlan_allowed_bitmap, 4096);
390291939Shselasky		index < 4096;
391291939Shselasky		index = find_next_bit(acl_table->vlan_allowed_bitmap, 4096,
392291939Shselasky				      ++index)) {
393291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
394291939Shselasky					  acl_table->vlan_fi_table[index]);
395291939Shselasky	}
396291939Shselasky
397291939Shselasky	acl_table->vlan_filter_applied = false;
398291939Shselasky}
399291939Shselasky
400291939Shselaskystatic void mlx5_vacl_table_disapply_all_filters(void *acl_t)
401291939Shselasky{
402291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
403291939Shselasky
404291939Shselasky	if (acl_table->default_fr.applied) {
405291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
406291939Shselasky					  acl_table->default_fr.fi);
407291939Shselasky		acl_table->default_fr.applied = false;
408291939Shselasky	}
409291939Shselasky	if (acl_table->unknown_vlan_fr.applied) {
410291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
411291939Shselasky					  acl_table->unknown_vlan_fr.fi);
412291939Shselasky		acl_table->unknown_vlan_fr.applied = false;
413291939Shselasky	}
414291939Shselasky	if (acl_table->loopback_fr.applied) {
415291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
416291939Shselasky					  acl_table->loopback_fr.fi);
417291939Shselasky		acl_table->loopback_fr.applied = false;
418291939Shselasky	}
419291939Shselasky	if (acl_table->untagged_fr.applied) {
420291939Shselasky		mlx5_del_flow_table_entry(acl_table->ft,
421291939Shselasky					  acl_table->untagged_fr.fi);
422291939Shselasky		acl_table->untagged_fr.applied = false;
423291939Shselasky	}
424291939Shselasky	if (acl_table->vlan_filter_applied) {
425291939Shselasky		mlx5_vacl_table_disapply_vlan_filter(acl_t);
426291939Shselasky		acl_table->vlan_filter_applied = false;
427291939Shselasky	}
428291939Shselasky}
429291939Shselasky
430291939Shselaskystatic int mlx5_vacl_table_apply_all_filters(void *acl_t)
431291939Shselasky{
432291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
433291939Shselasky	int err = 0;
434291939Shselasky
435291939Shselasky	if (!acl_table->default_fr.applied && acl_table->spoofchk_enabled) {
436291939Shselasky		err = mlx5_vacl_table_apply_default(acl_table,
437291939Shselasky						    acl_table->default_fr.action);
438291939Shselasky		if (err)
439291939Shselasky			goto err_disapply_all;
440291939Shselasky	}
441291939Shselasky
442291939Shselasky	if (!acl_table->unknown_vlan_fr.applied) {
443291939Shselasky		err = mlx5_vacl_table_apply_unknown_vlan(acl_table,
444291939Shselasky							 acl_table->unknown_vlan_fr.action);
445291939Shselasky		if (err)
446291939Shselasky			goto err_disapply_all;
447291939Shselasky	}
448291939Shselasky
449291939Shselasky	if (!acl_table->loopback_fr.applied &&
450291939Shselasky	    acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
451291939Shselasky		err = mlx5_vacl_table_apply_loopback_filter(
452291939Shselasky						acl_table,
453291939Shselasky						acl_table->loopback_fr.action);
454291939Shselasky		if (err)
455291939Shselasky			goto err_disapply_all;
456291939Shselasky	}
457291939Shselasky
458291939Shselasky	if (!acl_table->untagged_fr.applied) {
459291939Shselasky		err = mlx5_vacl_table_apply_untagged(acl_table,
460291939Shselasky						     acl_table->untagged_fr.action);
461291939Shselasky		if (err)
462291939Shselasky			goto err_disapply_all;
463291939Shselasky	}
464291939Shselasky
465291939Shselasky	if (!acl_table->vlan_filter_applied && acl_table->vlan_filter_enabled) {
466291939Shselasky		err = mlx5_vacl_table_apply_vlan_filter(acl_t);
467291939Shselasky		if (err)
468291939Shselasky			goto err_disapply_all;
469291939Shselasky	}
470291939Shselasky
471291939Shselasky	goto out;
472291939Shselasky
473291939Shselaskyerr_disapply_all:
474291939Shselasky	mlx5_vacl_table_disapply_all_filters(acl_t);
475291939Shselasky
476291939Shselaskyout:
477291939Shselasky	return err;
478291939Shselasky}
479291939Shselasky
480291939Shselaskystatic void mlx5_vacl_table_destroy_ft(void *acl_t)
481291939Shselasky{
482291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
483291939Shselasky
484291939Shselasky	mlx5_vacl_table_disapply_all_filters(acl_t);
485291939Shselasky	if (acl_table->ft)
486291939Shselasky		mlx5_destroy_flow_table(acl_table->ft);
487291939Shselasky	acl_table->ft = NULL;
488291939Shselasky}
489291939Shselasky
490291939Shselaskystatic int mlx5_vacl_table_create_ft(void *acl_t, bool spoofchk)
491291939Shselasky{
492291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
493291939Shselasky	int log_acl_ft_size;
494291939Shselasky	int err = 0;
495291939Shselasky	int groups_num = MLX5_ACL_GROUPS_NUM - 1;
496291939Shselasky	int shift_idx = MLX5_ACL_UNTAGGED_GROUP_IDX;
497291939Shselasky	u8 *smac;
498291939Shselasky	struct mlx5_flow_table_group *g;
499291939Shselasky
500291939Shselasky	if (acl_table->ft)
501291939Shselasky		return -EINVAL;
502291939Shselasky
503291939Shselasky	g = kcalloc(MLX5_ACL_GROUPS_NUM, sizeof(*g), GFP_KERNEL);
504291939Shselasky	if (!g)
505291939Shselasky		goto out;
506291939Shselasky
507291939Shselasky	acl_table->spoofchk_enabled = spoofchk;
508291939Shselasky
509291939Shselasky	/*
510291939Shselasky	 * for vlan group
511291939Shselasky	 */
512291939Shselasky	log_acl_ft_size = 4096;
513291939Shselasky	/*
514291939Shselasky	 * for loopback filter rule
515291939Shselasky	 */
516291939Shselasky	log_acl_ft_size += 1;
517291939Shselasky	/*
518291939Shselasky	 * for untagged rule
519291939Shselasky	 */
520291939Shselasky	log_acl_ft_size += 1;
521291939Shselasky	/*
522291939Shselasky	 * for unknown vlan rule
523291939Shselasky	 */
524291939Shselasky	log_acl_ft_size += 1;
525291939Shselasky	/*
526291939Shselasky	 * for default rule
527291939Shselasky	 */
528291939Shselasky	log_acl_ft_size += 1;
529291939Shselasky
530291939Shselasky	log_acl_ft_size = order_base_2(log_acl_ft_size);
531291939Shselasky	log_acl_ft_size = min_t(int, log_acl_ft_size, acl_table->max_ft_size);
532291939Shselasky
533291939Shselasky	if (log_acl_ft_size < 2)
534291939Shselasky		goto out;
535291939Shselasky
536291939Shselasky	if (acl_table->acl_type == MLX5_FLOW_TABLE_TYPE_EGRESS_ACL) {
537291939Shselasky		/* Loopback filter group */
538291939Shselasky		g[MLX5_ACL_LOOPBACK_GROUP_IDX].log_sz = 0;
539291939Shselasky		g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria_enable =
540291939Shselasky				MLX5_MATCH_MISC_PARAMETERS;
541291939Shselasky		MLX5_SET_TO_ONES(fte_match_param,
542291939Shselasky				 g[MLX5_ACL_LOOPBACK_GROUP_IDX].match_criteria,
543291939Shselasky				 misc_parameters.source_port);
544291939Shselasky		groups_num++;
545291939Shselasky		shift_idx = MLX5_ACL_LOOPBACK_GROUP_IDX;
546291939Shselasky	}
547291939Shselasky	/* Untagged traffic group */
548291939Shselasky	g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].log_sz = 0;
549291939Shselasky	g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria_enable =
550291939Shselasky			MLX5_MATCH_OUTER_HEADERS;
551291939Shselasky	MLX5_SET(fte_match_param,
552291939Shselasky		 g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx].match_criteria,
553291939Shselasky		 outer_headers.vlan_tag, 1);
554291939Shselasky	if (spoofchk) {
555291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
556291939Shselasky				    g[MLX5_ACL_UNTAGGED_GROUP_IDX - shift_idx]
557291939Shselasky				      .match_criteria,
558291939Shselasky				    outer_headers.smac_47_16);
559291939Shselasky		memset(smac, 0xff, ETH_ALEN);
560291939Shselasky	}
561291939Shselasky
562291939Shselasky	/* Allowed vlans group */
563291939Shselasky	g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].log_sz = log_acl_ft_size - 1;
564291939Shselasky	g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
565291939Shselasky			MLX5_MATCH_OUTER_HEADERS;
566291939Shselasky	MLX5_SET(fte_match_param,
567291939Shselasky		 g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
568291939Shselasky		 outer_headers.vlan_tag, 1);
569291939Shselasky	MLX5_SET(fte_match_param,
570291939Shselasky		 g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx].match_criteria,
571291939Shselasky		 outer_headers.first_vid, 0xfff);
572291939Shselasky	if (spoofchk) {
573291939Shselasky		smac = MLX5_ADDR_OF(fte_match_param,
574291939Shselasky				    g[MLX5_ACL_VLAN_GROUP_IDX - shift_idx]
575291939Shselasky				      .match_criteria,
576291939Shselasky				    outer_headers.smac_47_16);
577291939Shselasky		memset(smac, 0xff, ETH_ALEN);
578291939Shselasky	}
579291939Shselasky
580291939Shselasky	/* Unknown vlan traffic group */
581291939Shselasky	g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].log_sz = 0;
582291939Shselasky	g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx].match_criteria_enable =
583291939Shselasky			(spoofchk ? MLX5_MATCH_OUTER_HEADERS : 0);
584291939Shselasky	if (spoofchk) {
585291939Shselasky		smac = MLX5_ADDR_OF(
586291939Shselasky				fte_match_param,
587291939Shselasky				g[MLX5_ACL_UNKNOWN_VLAN_GROUP_IDX - shift_idx]
588291939Shselasky				  .match_criteria,
589291939Shselasky				outer_headers.smac_47_16);
590291939Shselasky		memset(smac, 0xff, ETH_ALEN);
591291939Shselasky	}
592291939Shselasky
593291939Shselasky	/*
594291939Shselasky	 * Default group - for spoofchk only.
595291939Shselasky	 */
596291939Shselasky	g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].log_sz = 0;
597291939Shselasky	g[MLX5_ACL_DEFAULT_GROUP_IDX - shift_idx].match_criteria_enable = 0;
598291939Shselasky
599291939Shselasky	acl_table->ft = mlx5_create_flow_table(acl_table->dev,
600291939Shselasky					       0,
601291939Shselasky					       acl_table->acl_type,
602291939Shselasky					       acl_table->vport,
603291939Shselasky					       groups_num,
604291939Shselasky					       g);
605291939Shselasky	if (!acl_table->ft) {
606291939Shselasky		err = -ENOMEM;
607291939Shselasky		goto out;
608291939Shselasky	}
609291939Shselasky
610291939Shselasky	err = mlx5_vacl_table_apply_all_filters(acl_t);
611291939Shselasky	if (err)
612291939Shselasky		goto err_destroy_ft;
613291939Shselasky
614291939Shselasky	goto out;
615291939Shselasky
616291939Shselaskyerr_destroy_ft:
617291939Shselasky	mlx5_vacl_table_destroy_ft(acl_table->ft);
618291939Shselasky	acl_table->ft = NULL;
619291939Shselasky
620291939Shselaskyout:
621291939Shselasky	kfree(g);
622291939Shselasky	return err;
623291939Shselasky}
624291939Shselasky
625291939Shselaskyvoid *mlx5_vacl_table_create(struct mlx5_core_dev *dev,
626291939Shselasky			     u16 vport, bool is_egress)
627291939Shselasky{
628291939Shselasky	struct mlx5_vacl_table *acl_table;
629291939Shselasky	int err = 0;
630291939Shselasky
631291939Shselasky	if (is_egress && !MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev, ft_support))
632291939Shselasky		return NULL;
633291939Shselasky
634291939Shselasky	if (!is_egress && !MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev, ft_support))
635291939Shselasky		return NULL;
636291939Shselasky
637291939Shselasky	acl_table = kzalloc(sizeof(*acl_table), GFP_KERNEL);
638291939Shselasky	if (!acl_table)
639291939Shselasky		return NULL;
640291939Shselasky
641291939Shselasky	acl_table->acl_type = is_egress ? MLX5_FLOW_TABLE_TYPE_EGRESS_ACL :
642291939Shselasky					  MLX5_FLOW_TABLE_TYPE_INGRESS_ACL;
643291939Shselasky	acl_table->max_ft_size = (is_egress ?
644291939Shselasky					MLX5_CAP_ESW_FLOWTABLE_EGRESS_ACL(dev,
645291939Shselasky									  log_max_ft_size) :
646291939Shselasky					MLX5_CAP_ESW_FLOWTABLE_INGRESS_ACL(dev,
647291939Shselasky									   log_max_ft_size));
648291939Shselasky	acl_table->dev = dev;
649291939Shselasky	acl_table->vport = vport;
650291939Shselasky
651291939Shselasky	/*
652291939Shselasky	 * default behavior : Allow and if spoofchk drop the default
653291939Shselasky	 */
654291939Shselasky	acl_table->default_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
655291939Shselasky	acl_table->loopback_fr.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
656291939Shselasky	acl_table->unknown_vlan_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
657291939Shselasky	acl_table->untagged_fr.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW;
658291939Shselasky	err = mlx5_vacl_table_create_ft(acl_table, false);
659291939Shselasky	if (err)
660291939Shselasky		goto err_free_acl_table;
661291939Shselasky
662291939Shselasky	acl_table->vlan_allowed_bitmap = kcalloc(BITS_TO_LONGS(4096),
663291939Shselasky						 sizeof(uintptr_t),
664291939Shselasky						 GFP_KERNEL);
665291939Shselasky	if (!acl_table->vlan_allowed_bitmap)
666291939Shselasky		goto err_destroy_ft;
667291939Shselasky
668291939Shselasky	goto out;
669291939Shselasky
670291939Shselaskyerr_destroy_ft:
671291939Shselasky	mlx5_vacl_table_destroy_ft(acl_table->ft);
672291939Shselasky	acl_table->ft = NULL;
673291939Shselasky
674291939Shselaskyerr_free_acl_table:
675291939Shselasky	kfree(acl_table);
676291939Shselasky	acl_table = NULL;
677291939Shselasky
678291939Shselaskyout:
679291939Shselasky	return (void *)acl_table;
680291939Shselasky}
681291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_create);
682291939Shselasky
683291939Shselaskyvoid mlx5_vacl_table_cleanup(void *acl_t)
684291939Shselasky{
685291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
686291939Shselasky
687291939Shselasky	mlx5_vacl_table_destroy_ft(acl_t);
688291939Shselasky	kfree(acl_table->vlan_allowed_bitmap);
689291939Shselasky	kfree(acl_table);
690291939Shselasky}
691291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_cleanup);
692291939Shselasky
693291939Shselaskyint mlx5_vacl_table_add_vlan(void *acl_t, u16 vlan)
694291939Shselasky{
695291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
696291939Shselasky	int err = 0;
697291939Shselasky
698291939Shselasky	if (test_bit(vlan, acl_table->vlan_allowed_bitmap))
699291939Shselasky		return 0;
700291939Shselasky	__set_bit(vlan, acl_table->vlan_allowed_bitmap);
701291939Shselasky	if (!acl_table->vlan_filter_applied)
702291939Shselasky		return 0;
703291939Shselasky
704291939Shselasky	err = mlx5_vacl_table_allow_vlan(acl_t, vlan);
705291939Shselasky	if (err)
706291939Shselasky		goto err_clear_vbit;
707291939Shselasky
708291939Shselasky	goto out;
709291939Shselasky
710291939Shselaskyerr_clear_vbit:
711291939Shselasky	__clear_bit(vlan, acl_table->vlan_allowed_bitmap);
712291939Shselasky
713291939Shselaskyout:
714291939Shselasky	return err;
715291939Shselasky}
716291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_add_vlan);
717291939Shselasky
718291939Shselaskyvoid mlx5_vacl_table_del_vlan(void *acl_t, u16 vlan)
719291939Shselasky{
720291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
721291939Shselasky
722291939Shselasky	if (!test_bit(vlan, acl_table->vlan_allowed_bitmap))
723291939Shselasky		return;
724291939Shselasky
725291939Shselasky	__clear_bit(vlan, acl_table->vlan_allowed_bitmap);
726291939Shselasky
727291939Shselasky	if (!acl_table->vlan_filter_applied)
728291939Shselasky		return;
729291939Shselasky
730291939Shselasky	mlx5_del_flow_table_entry(acl_table->ft,
731291939Shselasky				  acl_table->vlan_fi_table[vlan]);
732291939Shselasky}
733291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_del_vlan);
734291939Shselasky
735291939Shselaskyint mlx5_vacl_table_enable_vlan_filter(void *acl_t)
736291939Shselasky{
737291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
738291939Shselasky
739291939Shselasky	acl_table->vlan_filter_enabled = true;
740291939Shselasky	return mlx5_vacl_table_apply_vlan_filter(acl_t);
741291939Shselasky}
742291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_enable_vlan_filter);
743291939Shselasky
744291939Shselaskyvoid mlx5_vacl_table_disable_vlan_filter(void *acl_t)
745291939Shselasky{
746291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
747291939Shselasky
748291939Shselasky	acl_table->vlan_filter_enabled = false;
749291939Shselasky	mlx5_vacl_table_disapply_vlan_filter(acl_t);
750291939Shselasky}
751291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_disable_vlan_filter);
752291939Shselasky
753291939Shselaskyint mlx5_vacl_table_drop_untagged(void *acl_t)
754291939Shselasky{
755291939Shselasky	return mlx5_vacl_table_apply_untagged(acl_t,
756291939Shselasky			MLX5_FLOW_CONTEXT_ACTION_DROP);
757291939Shselasky}
758291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_drop_untagged);
759291939Shselasky
760291939Shselaskyint mlx5_vacl_table_allow_untagged(void *acl_t)
761291939Shselasky{
762291939Shselasky	return mlx5_vacl_table_apply_untagged(acl_t,
763291939Shselasky			MLX5_FLOW_CONTEXT_ACTION_ALLOW);
764291939Shselasky}
765291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_allow_untagged);
766291939Shselasky
767291939Shselaskyint mlx5_vacl_table_drop_unknown_vlan(void *acl_t)
768291939Shselasky{
769291939Shselasky	return mlx5_vacl_table_apply_unknown_vlan(acl_t,
770291939Shselasky			MLX5_FLOW_CONTEXT_ACTION_DROP);
771291939Shselasky}
772291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_drop_unknown_vlan);
773291939Shselasky
774291939Shselaskyint mlx5_vacl_table_allow_unknown_vlan(void *acl_t)
775291939Shselasky{
776291939Shselasky	return mlx5_vacl_table_apply_unknown_vlan(acl_t,
777291939Shselasky			MLX5_FLOW_CONTEXT_ACTION_ALLOW);
778291939Shselasky}
779291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_allow_unknown_vlan);
780291939Shselasky
781291939Shselaskyint mlx5_vacl_table_set_spoofchk(void *acl_t, bool spoofchk, u8 *vport_mac)
782291939Shselasky{
783291939Shselasky	struct mlx5_vacl_table *acl_table = (struct mlx5_vacl_table *)acl_t;
784291939Shselasky	int err = 0;
785291939Shselasky
786291939Shselasky	if (spoofchk == acl_table->spoofchk_enabled) {
787291939Shselasky		if (!spoofchk ||
788291939Shselasky		    (spoofchk && !memcmp(acl_table->smac, vport_mac, ETH_ALEN)))
789291939Shselasky			return 0;
790291939Shselasky	}
791291939Shselasky
792291939Shselasky	ether_addr_copy(acl_table->smac, vport_mac);
793291939Shselasky	if (spoofchk != acl_table->spoofchk_enabled) {
794291939Shselasky		mlx5_vacl_table_destroy_ft(acl_t);
795291939Shselasky		err = mlx5_vacl_table_create_ft(acl_t, spoofchk);
796291939Shselasky	} else {
797291939Shselasky		mlx5_vacl_table_disapply_all_filters(acl_t);
798291939Shselasky		err = mlx5_vacl_table_apply_all_filters(acl_t);
799291939Shselasky	}
800291939Shselasky
801291939Shselasky	return err;
802291939Shselasky}
803291939ShselaskyEXPORT_SYMBOL(mlx5_vacl_table_set_spoofchk);
804291939Shselasky
805