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 
9 static int
10 acl_ingress_ofld_setup(struct mlx5_eswitch *esw, struct mlx5_vport *vport);
11 
12 static bool
esw_acl_ingress_prio_tag_enabled(struct mlx5_eswitch * esw,const struct mlx5_vport * vport)13 esw_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 
esw_acl_ingress_prio_tag_create(struct mlx5_eswitch * esw,struct mlx5_vport * vport)20 static 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 
esw_acl_ingress_mod_metadata_create(struct mlx5_eswitch * esw,struct mlx5_vport * vport)65 static 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 
esw_acl_ingress_mod_metadata_destroy(struct mlx5_eswitch * esw,struct mlx5_vport * vport)113 static 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 
esw_acl_ingress_src_port_drop_create(struct mlx5_eswitch * esw,struct mlx5_vport * vport)124 static 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;
150 err_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 
esw_acl_ingress_src_port_drop_destroy(struct mlx5_eswitch * esw,struct mlx5_vport * vport)157 static 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 
esw_acl_ingress_ofld_rules_create(struct mlx5_eswitch * esw,struct mlx5_vport * vport)167 static 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 
194 prio_tag_err:
195 	esw_acl_ingress_mod_metadata_destroy(esw, vport);
196 	return err;
197 }
198 
esw_acl_ingress_ofld_rules_destroy(struct mlx5_eswitch * esw,struct mlx5_vport * vport)199 static 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 
esw_acl_ingress_ofld_groups_create(struct mlx5_eswitch * esw,struct mlx5_vport * vport)207 static 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 
285 metadata_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 	}
290 prio_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 	}
295 drop_err:
296 	kvfree(flow_group_in);
297 	return ret;
298 }
299 
esw_acl_ingress_ofld_groups_destroy(struct mlx5_vport * vport)300 static 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 
318 static int
acl_ingress_ofld_setup(struct mlx5_eswitch * esw,struct mlx5_vport * vport)319 acl_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 
355 rules_err:
356 	esw_acl_ingress_ofld_groups_destroy(vport);
357 group_err:
358 	esw_acl_ingress_table_destroy(vport);
359 	return err;
360 }
361 
esw_acl_ingress_ofld_setup(struct mlx5_eswitch * esw,struct mlx5_vport * vport)362 int 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 
esw_acl_ingress_ofld_cleanup(struct mlx5_eswitch * esw,struct mlx5_vport * vport)371 void 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 */
mlx5_esw_acl_ingress_vport_metadata_update(struct mlx5_eswitch * esw,u16 vport_num,u32 metadata)380 int 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 
402 out:
403 	vport->metadata = vport->default_metadata;
404 	return err;
405 }
406 
mlx5_esw_acl_ingress_vport_drop_rule_create(struct mlx5_eswitch * esw,u16 vport_num)407 int 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 
mlx5_esw_acl_ingress_vport_drop_rule_destroy(struct mlx5_eswitch * esw,u16 vport_num)419 void 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 }
430