File Index
Files and directories
Sign in
FILE

ingress_ofld.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/esw/acl/ingress_ofld.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2/* Copyright (c) 2020 Mellanox Technologies Inc. All rights reserved. */
  3
  4#include "mlx5_core.h"
  5#include "eswitch.h"
  6#include "helper.h"
  7#include "ofld.h"
  8
  9static int
 10acl_ingress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport);
 11
 12static bool
 13esw_acl_ingress_prio_tag_enabled(struct mlx5_eswitch *esw,
 14				 const struct mlx5_vport *vport)
 15{
 16	return (MLX5_CAP_GEN(esw->dev, prio_tag_required) &&
 17		mlx5_eswitch_is_vf_vport(esw, vport->vport));
 18}
 19
 20static int esw_acl_ingress_prio_tag_create(struct mlx5_eswitch *esw,
 21					   struct mlx5_vport *vport)
 22{
 23	struct mlx5_flow_act flow_act = {};
 24	struct mlx5_flow_spec *spec;
 25	int err = 0;
 26
 27	/* For prio tag mode, there is only 1 FTEs:
 28	 * 1) Untagged packets - push prio tag VLAN and modify metadata if
 29	 * required, allow
 30	 * Unmatched traffic is allowed by default
 31	 */
 32	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
 33	if (!spec)
 34		return -ENOMEM;
 35
 36	/* Untagged packets - push prio tag VLAN, allow */
 37	MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.cvlan_tag);
 38	MLX5_SET(fte_match_param, spec->match_value, outer_headers.cvlan_tag, 0);
 39	spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
 40	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_VLAN_PUSH |
 41			  MLX5_FLOW_CONTEXT_ACTION_ALLOW;
 42	flow_act.vlan[0].ethtype = ETH_P_8021Q;
 43	flow_act.vlan[0].vid = 0;
 44	flow_act.vlan[0].prio = 0;
 45
 46	if (vport->ingress.offloads.modify_metadata_rule) {
 47		flow_act.action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
 48		flow_act.modify_hdr = vport->ingress.offloads.modify_metadata;
 49	}
 50
 51	vport->ingress.allow_rule = mlx5_add_flow_rules(vport->ingress.acl, spec,
 52							&flow_act, NULL, 0);
 53	if (IS_ERR(vport->ingress.allow_rule)) {
 54		err = PTR_ERR(vport->ingress.allow_rule);
 55		esw_warn(esw->dev,
 56			 "vport[%d] configure ingress untagged allow rule, err(%d)\n",
 57			 vport->vport, err);
 58		vport->ingress.allow_rule = NULL;
 59	}
 60
 61	kvfree(spec);
 62	return err;
 63}
 64
 65static int esw_acl_ingress_mod_metadata_create(struct mlx5_eswitch *esw,
 66					       struct mlx5_vport *vport)
 67{
 68	u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
 69	struct mlx5_flow_act flow_act = {};
 70	int err = 0;
 71	u32 key;
 72
 73	key = mlx5_eswitch_get_vport_metadata_for_match(esw, vport->vport);
 74	key >>= ESW_SOURCE_PORT_METADATA_OFFSET;
 75
 76	MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET);
 77	MLX5_SET(set_action_in, action, field,
 78		 MLX5_ACTION_IN_FIELD_METADATA_REG_C_0);
 79	MLX5_SET(set_action_in, action, data, key);
 80	MLX5_SET(set_action_in, action, offset,
 81		 ESW_SOURCE_PORT_METADATA_OFFSET);
 82	MLX5_SET(set_action_in, action, length,
 83		 ESW_SOURCE_PORT_METADATA_BITS);
 84
 85	vport->ingress.offloads.modify_metadata =
 86		mlx5_modify_header_alloc(esw->dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS,
 87					 1, action);
 88	if (IS_ERR(vport->ingress.offloads.modify_metadata)) {
 89		err = PTR_ERR(vport->ingress.offloads.modify_metadata);
 90		esw_warn(esw->dev,
 91			 "failed to alloc modify header for vport %d ingress acl (%d)\n",
 92			 vport->vport, err);
 93		return err;
 94	}
 95
 96	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | MLX5_FLOW_CONTEXT_ACTION_ALLOW;
 97	flow_act.modify_hdr = vport->ingress.offloads.modify_metadata;
 98	flow_act.fg = vport->ingress.offloads.metadata_allmatch_grp;
 99	vport->ingress.offloads.modify_metadata_rule =
100				mlx5_add_flow_rules(vport->ingress.acl,
101						    NULL, &flow_act, NULL, 0);
102	if (IS_ERR(vport->ingress.offloads.modify_metadata_rule)) {
103		err = PTR_ERR(vport->ingress.offloads.modify_metadata_rule);
104		esw_warn(esw->dev,
105			 "failed to add setting metadata rule for vport %d ingress acl, err(%d)\n",
106			 vport->vport, err);
107		mlx5_modify_header_dealloc(esw->dev, vport->ingress.offloads.modify_metadata);
108		vport->ingress.offloads.modify_metadata_rule = NULL;
109	}
110	return err;
111}
112
113static void esw_acl_ingress_mod_metadata_destroy(struct mlx5_eswitch *esw,
114						 struct mlx5_vport *vport)
115{
116	if (!vport->ingress.offloads.modify_metadata_rule)
117		return;
118
119	mlx5_del_flow_rules(vport->ingress.offloads.modify_metadata_rule);
120	mlx5_modify_header_dealloc(esw->dev, vport->ingress.offloads.modify_metadata);
121	vport->ingress.offloads.modify_metadata_rule = NULL;
122}
123
124static int esw_acl_ingress_src_port_drop_create(struct mlx5_eswitch *esw,
125						struct mlx5_vport *vport)
126{
127	struct mlx5_flow_act flow_act = {};
128	struct mlx5_flow_handle *flow_rule;
129	bool created = false;
130	int err = 0;
131
132	if (!vport->ingress.acl) {
133		err = acl_ingress_ofld_setup(esw, vport);
134		if (err)
135			return err;
136		created = true;
137	}
138
139	flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP;
140	flow_act.fg = vport->ingress.offloads.drop_grp;
141	flow_rule = mlx5_add_flow_rules(vport->ingress.acl, NULL, &flow_act, NULL, 0);
142	if (IS_ERR(flow_rule)) {
143		err = PTR_ERR(flow_rule);
144		goto err_out;
145	}
146
147	vport->ingress.offloads.drop_rule = flow_rule;
148
149	return 0;
150err_out:
151	/* Only destroy ingress acl created in this function. */
152	if (created)
153		esw_acl_ingress_ofld_cleanup(esw, vport);
154	return err;
155}
156
157static void esw_acl_ingress_src_port_drop_destroy(struct mlx5_eswitch *esw,
158						  struct mlx5_vport *vport)
159{
160	if (!vport->ingress.offloads.drop_rule)
161		return;
162
163	mlx5_del_flow_rules(vport->ingress.offloads.drop_rule);
164	vport->ingress.offloads.drop_rule = NULL;
165}
166
167static int esw_acl_ingress_ofld_rules_create(struct mlx5_eswitch *esw,
168					     struct mlx5_vport *vport)
169{
170	int err;
171
172	if (mlx5_eswitch_vport_match_metadata_enabled(esw)) {
173		err = esw_acl_ingress_mod_metadata_create(esw, vport);
174		if (err) {
175			esw_warn(esw->dev,
176				 "vport(%d) create ingress modify metadata, err(%d)\n",
177				 vport->vport, err);
178			return err;
179		}
180	}
181
182	if (esw_acl_ingress_prio_tag_enabled(esw, vport)) {
183		err = esw_acl_ingress_prio_tag_create(esw, vport);
184		if (err) {
185			esw_warn(esw->dev,
186				 "vport(%d) create ingress prio tag rule, err(%d)\n",
187				 vport->vport, err);
188			goto prio_tag_err;
189		}
190	}
191
192	return 0;
193
194prio_tag_err:
195	esw_acl_ingress_mod_metadata_destroy(esw, vport);
196	return err;
197}
198
199static void esw_acl_ingress_ofld_rules_destroy(struct mlx5_eswitch *esw,
200					       struct mlx5_vport *vport)
201{
202	esw_acl_ingress_allow_rule_destroy(vport);
203	esw_acl_ingress_mod_metadata_destroy(esw, vport);
204	esw_acl_ingress_src_port_drop_destroy(esw, vport);
205}
206
207static int esw_acl_ingress_ofld_groups_create(struct mlx5_eswitch *esw,
208					      struct mlx5_vport *vport)
209{
210	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
211	struct mlx5_flow_group *g;
212	void *match_criteria;
213	u32 *flow_group_in;
214	u32 flow_index = 0;
215	int ret = 0;
216
217	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
218	if (!flow_group_in)
219		return -ENOMEM;
220
221	if (vport->vport == MLX5_VPORT_UPLINK) {
222		/* This group can hold an FTE to drop all traffic.
223		 * Need in case LAG is enabled.
224		 */
225		MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index);
226		MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index);
227
228		g = mlx5_create_flow_group(vport->ingress.acl, flow_group_in);
229		if (IS_ERR(g)) {
230			ret = PTR_ERR(g);
231			esw_warn(esw->dev, "vport[%d] ingress create drop flow group, err(%d)\n",
232				 vport->vport, ret);
233			goto drop_err;
234		}
235		vport->ingress.offloads.drop_grp = g;
236		flow_index++;
237	}
238
239	if (esw_acl_ingress_prio_tag_enabled(esw, vport)) {
240		/* This group is to hold FTE to match untagged packets when prio_tag
241		 * is enabled.
242		 */
243		memset(flow_group_in, 0, inlen);
244		match_criteria = MLX5_ADDR_OF(create_flow_group_in,
245					      flow_group_in, match_criteria);
246		MLX5_SET(create_flow_group_in, flow_group_in,
247			 match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
248		MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
249		MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index);
250		MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index);
251
252		g = mlx5_create_flow_group(vport->ingress.acl, flow_group_in);
253		if (IS_ERR(g)) {
254			ret = PTR_ERR(g);
255			esw_warn(esw->dev, "vport[%d] ingress create untagged flow group, err(%d)\n",
256				 vport->vport, ret);
257			goto prio_tag_err;
258		}
259		vport->ingress.offloads.metadata_prio_tag_grp = g;
260		flow_index++;
261	}
262
263	if (mlx5_eswitch_vport_match_metadata_enabled(esw)) {
264		/* This group holds an FTE with no match to add metadata for
265		 * tagged packets if prio-tag is enabled, or for all untagged
266		 * traffic in case prio-tag is disabled.
267		 */
268		memset(flow_group_in, 0, inlen);
269		MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, flow_index);
270		MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, flow_index);
271
272		g = mlx5_create_flow_group(vport->ingress.acl, flow_group_in);
273		if (IS_ERR(g)) {
274			ret = PTR_ERR(g);
275			esw_warn(esw->dev, "vport[%d] ingress create drop flow group, err(%d)\n",
276				 vport->vport, ret);
277			goto metadata_err;
278		}
279		vport->ingress.offloads.metadata_allmatch_grp = g;
280	}
281
282	kvfree(flow_group_in);
283	return 0;
284
285metadata_err:
286	if (!IS_ERR_OR_NULL(vport->ingress.offloads.metadata_prio_tag_grp)) {
287		mlx5_destroy_flow_group(vport->ingress.offloads.metadata_prio_tag_grp);
288		vport->ingress.offloads.metadata_prio_tag_grp = NULL;
289	}
290prio_tag_err:
291	if (!IS_ERR_OR_NULL(vport->ingress.offloads.drop_grp)) {
292		mlx5_destroy_flow_group(vport->ingress.offloads.drop_grp);
293		vport->ingress.offloads.drop_grp = NULL;
294	}
295drop_err:
296	kvfree(flow_group_in);
297	return ret;
298}
299
300static void esw_acl_ingress_ofld_groups_destroy(struct mlx5_vport *vport)
301{
302	if (vport->ingress.offloads.metadata_allmatch_grp) {
303		mlx5_destroy_flow_group(vport->ingress.offloads.metadata_allmatch_grp);
304		vport->ingress.offloads.metadata_allmatch_grp = NULL;
305	}
306
307	if (vport->ingress.offloads.metadata_prio_tag_grp) {
308		mlx5_destroy_flow_group(vport->ingress.offloads.metadata_prio_tag_grp);
309		vport->ingress.offloads.metadata_prio_tag_grp = NULL;
310	}
311
312	if (vport->ingress.offloads.drop_grp) {
313		mlx5_destroy_flow_group(vport->ingress.offloads.drop_grp);
314		vport->ingress.offloads.drop_grp = NULL;
315	}
316}
317
318static int
319acl_ingress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
320{
321	int num_ftes = 0;
322	int err;
323
324	esw_acl_ingress_allow_rule_destroy(vport);
325
326	if (mlx5_eswitch_vport_match_metadata_enabled(esw))
327		num_ftes++;
328	if (vport->vport == MLX5_VPORT_UPLINK)
329		num_ftes++;
330	if (esw_acl_ingress_prio_tag_enabled(esw, vport))
331		num_ftes++;
332
333	vport->ingress.acl = esw_acl_table_create(esw, vport,
334						  MLX5_FLOW_NAMESPACE_ESW_INGRESS,
335						  num_ftes);
336	if (IS_ERR(vport->ingress.acl)) {
337		err = PTR_ERR(vport->ingress.acl);
338		vport->ingress.acl = NULL;
339		return err;
340	}
341
342	err = esw_acl_ingress_ofld_groups_create(esw, vport);
343	if (err)
344		goto group_err;
345
346	esw_debug(esw->dev,
347		  "vport[%d] configure ingress rules\n", vport->vport);
348
349	err = esw_acl_ingress_ofld_rules_create(esw, vport);
350	if (err)
351		goto rules_err;
352
353	return 0;
354
355rules_err:
356	esw_acl_ingress_ofld_groups_destroy(vport);
357group_err:
358	esw_acl_ingress_table_destroy(vport);
359	return err;
360}
361
362int esw_acl_ingress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
363{
364	if (!mlx5_eswitch_vport_match_metadata_enabled(esw) &&
365	    !esw_acl_ingress_prio_tag_enabled(esw, vport))
366		return 0;
367
368	return acl_ingress_ofld_setup(esw, vport);
369}
370
371void esw_acl_ingress_ofld_cleanup(struct mlx5_eswitch *esw,
372				  struct mlx5_vport *vport)
373{
374	esw_acl_ingress_ofld_rules_destroy(esw, vport);
375	esw_acl_ingress_ofld_groups_destroy(vport);
376	esw_acl_ingress_table_destroy(vport);
377}
378
379/* Caller must hold rtnl_lock */
380int mlx5_esw_acl_ingress_vport_metadata_update(struct mlx5_eswitch *esw, u16 vport_num,
381					       u32 metadata)
382{
383	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
384	int err;
385
386	if (WARN_ON_ONCE(IS_ERR(vport))) {
387		esw_warn(esw->dev, "vport(%d) invalid!\n", vport_num);
388		return PTR_ERR(vport);
389	}
390
391	esw_acl_ingress_ofld_rules_destroy(esw, vport);
392
393	vport->metadata = metadata ? metadata : vport->default_metadata;
394
395	/* Recreate ingress acl rules with vport->metadata */
396	err = esw_acl_ingress_ofld_rules_create(esw, vport);
397	if (err)
398		goto out;
399
400	return 0;
401
402out:
403	vport->metadata = vport->default_metadata;
404	return err;
405}
406
407int mlx5_esw_acl_ingress_vport_drop_rule_create(struct mlx5_eswitch *esw, u16 vport_num)
408{
409	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
410
411	if (IS_ERR(vport)) {
412		esw_warn(esw->dev, "vport(%d) invalid!\n", vport_num);
413		return PTR_ERR(vport);
414	}
415
416	return esw_acl_ingress_src_port_drop_create(esw, vport);
417}
418
419void mlx5_esw_acl_ingress_vport_drop_rule_destroy(struct mlx5_eswitch *esw, u16 vport_num)
420{
421	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
422
423	if (WARN_ON_ONCE(IS_ERR(vport))) {
424		esw_warn(esw->dev, "vport(%d) invalid!\n", vport_num);
425		return;
426	}
427
428	esw_acl_ingress_src_port_drop_destroy(esw, vport);
429}
~/ Read-only file preview.