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 
8 struct mlx5_flow_table *
9 esw_acl_table_create(struct mlx5_eswitch *esw, u16 vport_num, int ns, int size)
10 {
11 	struct mlx5_flow_table_attr ft_attr = {};
12 	struct mlx5_core_dev *dev = esw->dev;
13 	struct mlx5_flow_namespace *root_ns;
14 	struct mlx5_flow_table *acl;
15 	int acl_supported;
16 	int vport_index;
17 	int err;
18 
19 	acl_supported = (ns == MLX5_FLOW_NAMESPACE_ESW_INGRESS) ?
20 			MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support) :
21 			MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support);
22 
23 	if (!acl_supported)
24 		return ERR_PTR(-EOPNOTSUPP);
25 
26 	esw_debug(dev, "Create vport[%d] %s ACL table\n", vport_num,
27 		  ns == MLX5_FLOW_NAMESPACE_ESW_INGRESS ? "ingress" : "egress");
28 
29 	vport_index = mlx5_eswitch_vport_num_to_index(esw, vport_num);
30 	root_ns = mlx5_get_flow_vport_acl_namespace(dev, ns, vport_index);
31 	if (!root_ns) {
32 		esw_warn(dev, "Failed to get E-Switch root namespace for vport (%d)\n",
33 			 vport_num);
34 		return ERR_PTR(-EOPNOTSUPP);
35 	}
36 
37 	ft_attr.max_fte = size;
38 	ft_attr.flags = MLX5_FLOW_TABLE_OTHER_VPORT;
39 	acl = mlx5_create_vport_flow_table(root_ns, &ft_attr, vport_num);
40 	if (IS_ERR(acl)) {
41 		err = PTR_ERR(acl);
42 		esw_warn(dev, "vport[%d] create %s ACL table, err(%d)\n", vport_num,
43 			 ns == MLX5_FLOW_NAMESPACE_ESW_INGRESS ? "ingress" : "egress", err);
44 	}
45 	return acl;
46 }
47 
48 int esw_egress_acl_vlan_create(struct mlx5_eswitch *esw,
49 			       struct mlx5_vport *vport,
50 			       struct mlx5_flow_destination *fwd_dest,
51 			       u16 vlan_id, u32 flow_action)
52 {
53 	struct mlx5_flow_act flow_act = {};
54 	struct mlx5_flow_spec *spec;
55 	int err = 0;
56 
57 	if (vport->egress.allowed_vlan)
58 		return -EEXIST;
59 
60 	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
61 	if (!spec)
62 		return -ENOMEM;
63 
64 	MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.cvlan_tag);
65 	MLX5_SET_TO_ONES(fte_match_param, spec->match_value, outer_headers.cvlan_tag);
66 	MLX5_SET_TO_ONES(fte_match_param, spec->match_criteria, outer_headers.first_vid);
67 	MLX5_SET(fte_match_param, spec->match_value, outer_headers.first_vid, vlan_id);
68 
69 	spec->match_criteria_enable = MLX5_MATCH_OUTER_HEADERS;
70 	flow_act.action = flow_action;
71 	vport->egress.allowed_vlan =
72 		mlx5_add_flow_rules(vport->egress.acl, spec,
73 				    &flow_act, fwd_dest, 0);
74 	if (IS_ERR(vport->egress.allowed_vlan)) {
75 		err = PTR_ERR(vport->egress.allowed_vlan);
76 		esw_warn(esw->dev,
77 			 "vport[%d] configure egress vlan rule failed, err(%d)\n",
78 			 vport->vport, err);
79 		vport->egress.allowed_vlan = NULL;
80 	}
81 
82 	kvfree(spec);
83 	return err;
84 }
85 
86 void esw_acl_egress_vlan_destroy(struct mlx5_vport *vport)
87 {
88 	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan)) {
89 		mlx5_del_flow_rules(vport->egress.allowed_vlan);
90 		vport->egress.allowed_vlan = NULL;
91 	}
92 }
93 
94 int esw_acl_egress_vlan_grp_create(struct mlx5_eswitch *esw, struct mlx5_vport *vport)
95 {
96 	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
97 	struct mlx5_flow_group *vlan_grp;
98 	void *match_criteria;
99 	u32 *flow_group_in;
100 	int ret = 0;
101 
102 	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
103 	if (!flow_group_in)
104 		return -ENOMEM;
105 
106 	MLX5_SET(create_flow_group_in, flow_group_in,
107 		 match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
108 	match_criteria = MLX5_ADDR_OF(create_flow_group_in,
109 				      flow_group_in, match_criteria);
110 	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
111 	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.first_vid);
112 	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
113 	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
114 
115 	vlan_grp = mlx5_create_flow_group(vport->egress.acl, flow_group_in);
116 	if (IS_ERR(vlan_grp)) {
117 		ret = PTR_ERR(vlan_grp);
118 		esw_warn(esw->dev,
119 			 "Failed to create E-Switch vport[%d] egress pop vlans flow group, err(%d)\n",
120 			 vport->vport, ret);
121 		goto out;
122 	}
123 	vport->egress.vlan_grp = vlan_grp;
124 
125 out:
126 	kvfree(flow_group_in);
127 	return ret;
128 }
129 
130 void esw_acl_egress_vlan_grp_destroy(struct mlx5_vport *vport)
131 {
132 	if (!IS_ERR_OR_NULL(vport->egress.vlan_grp)) {
133 		mlx5_destroy_flow_group(vport->egress.vlan_grp);
134 		vport->egress.vlan_grp = NULL;
135 	}
136 }
137 
138 void esw_acl_egress_table_destroy(struct mlx5_vport *vport)
139 {
140 	if (IS_ERR_OR_NULL(vport->egress.acl))
141 		return;
142 
143 	mlx5_destroy_flow_table(vport->egress.acl);
144 	vport->egress.acl = NULL;
145 }
146 
147 void esw_acl_ingress_table_destroy(struct mlx5_vport *vport)
148 {
149 	if (!vport->ingress.acl)
150 		return;
151 
152 	mlx5_destroy_flow_table(vport->ingress.acl);
153 	vport->ingress.acl = NULL;
154 }
155 
156 void esw_acl_ingress_allow_rule_destroy(struct mlx5_vport *vport)
157 {
158 	if (!vport->ingress.allow_rule)
159 		return;
160 
161 	mlx5_del_flow_rules(vport->ingress.allow_rule);
162 	vport->ingress.allow_rule = NULL;
163 }
164