1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/kernel.h> |
5 | |
6 | #include "core_acl_flex_actions.h" |
7 | #include "spectrum.h" |
8 | #include "spectrum_mr.h" |
9 | |
10 | struct mlxsw_sp2_mr_tcam { |
11 | struct mlxsw_sp *mlxsw_sp; |
12 | struct mlxsw_sp_flow_block *flow_block; |
13 | struct mlxsw_sp_acl_ruleset *ruleset4; |
14 | struct mlxsw_sp_acl_ruleset *ruleset6; |
15 | }; |
16 | |
17 | struct mlxsw_sp2_mr_route { |
18 | struct mlxsw_sp2_mr_tcam *mr_tcam; |
19 | }; |
20 | |
21 | static struct mlxsw_sp_acl_ruleset * |
22 | mlxsw_sp2_mr_tcam_proto_ruleset(struct mlxsw_sp2_mr_tcam *mr_tcam, |
23 | enum mlxsw_sp_l3proto proto) |
24 | { |
25 | switch (proto) { |
26 | case MLXSW_SP_L3_PROTO_IPV4: |
27 | return mr_tcam->ruleset4; |
28 | case MLXSW_SP_L3_PROTO_IPV6: |
29 | return mr_tcam->ruleset6; |
30 | } |
31 | return NULL; |
32 | } |
33 | |
34 | static int mlxsw_sp2_mr_tcam_bind_group(struct mlxsw_sp *mlxsw_sp, |
35 | enum mlxsw_reg_pemrbt_protocol protocol, |
36 | struct mlxsw_sp_acl_ruleset *ruleset) |
37 | { |
38 | char pemrbt_pl[MLXSW_REG_PEMRBT_LEN]; |
39 | u16 group_id; |
40 | |
41 | group_id = mlxsw_sp_acl_ruleset_group_id(ruleset); |
42 | |
43 | mlxsw_reg_pemrbt_pack(payload: pemrbt_pl, protocol, group_id); |
44 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pemrbt), payload: pemrbt_pl); |
45 | } |
46 | |
47 | static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv4[] = { |
48 | MLXSW_AFK_ELEMENT_VIRT_ROUTER, |
49 | MLXSW_AFK_ELEMENT_SRC_IP_0_31, |
50 | MLXSW_AFK_ELEMENT_DST_IP_0_31, |
51 | }; |
52 | |
53 | static int mlxsw_sp2_mr_tcam_ipv4_init(struct mlxsw_sp2_mr_tcam *mr_tcam) |
54 | { |
55 | struct mlxsw_afk_element_usage elusage; |
56 | int err; |
57 | |
58 | /* Initialize IPv4 ACL group. */ |
59 | mlxsw_afk_element_usage_fill(elusage: &elusage, |
60 | elements: mlxsw_sp2_mr_tcam_usage_ipv4, |
61 | ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv4)); |
62 | mr_tcam->ruleset4 = mlxsw_sp_acl_ruleset_get(mlxsw_sp: mr_tcam->mlxsw_sp, |
63 | block: mr_tcam->flow_block, |
64 | chain_index: MLXSW_SP_L3_PROTO_IPV4, |
65 | profile: MLXSW_SP_ACL_PROFILE_MR, |
66 | tmplt_elusage: &elusage); |
67 | |
68 | if (IS_ERR(ptr: mr_tcam->ruleset4)) |
69 | return PTR_ERR(ptr: mr_tcam->ruleset4); |
70 | |
71 | /* MC Router groups should be bound before routes are inserted. */ |
72 | err = mlxsw_sp2_mr_tcam_bind_group(mlxsw_sp: mr_tcam->mlxsw_sp, |
73 | protocol: MLXSW_REG_PEMRBT_PROTO_IPV4, |
74 | ruleset: mr_tcam->ruleset4); |
75 | if (err) |
76 | goto err_bind_group; |
77 | |
78 | return 0; |
79 | |
80 | err_bind_group: |
81 | mlxsw_sp_acl_ruleset_put(mlxsw_sp: mr_tcam->mlxsw_sp, ruleset: mr_tcam->ruleset4); |
82 | return err; |
83 | } |
84 | |
85 | static void mlxsw_sp2_mr_tcam_ipv4_fini(struct mlxsw_sp2_mr_tcam *mr_tcam) |
86 | { |
87 | mlxsw_sp_acl_ruleset_put(mlxsw_sp: mr_tcam->mlxsw_sp, ruleset: mr_tcam->ruleset4); |
88 | } |
89 | |
90 | static const enum mlxsw_afk_element mlxsw_sp2_mr_tcam_usage_ipv6[] = { |
91 | MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_3, |
92 | MLXSW_AFK_ELEMENT_VIRT_ROUTER_4_7, |
93 | MLXSW_AFK_ELEMENT_VIRT_ROUTER_MSB, |
94 | MLXSW_AFK_ELEMENT_SRC_IP_96_127, |
95 | MLXSW_AFK_ELEMENT_SRC_IP_64_95, |
96 | MLXSW_AFK_ELEMENT_SRC_IP_32_63, |
97 | MLXSW_AFK_ELEMENT_SRC_IP_0_31, |
98 | MLXSW_AFK_ELEMENT_DST_IP_96_127, |
99 | MLXSW_AFK_ELEMENT_DST_IP_64_95, |
100 | MLXSW_AFK_ELEMENT_DST_IP_32_63, |
101 | MLXSW_AFK_ELEMENT_DST_IP_0_31, |
102 | }; |
103 | |
104 | static int mlxsw_sp2_mr_tcam_ipv6_init(struct mlxsw_sp2_mr_tcam *mr_tcam) |
105 | { |
106 | struct mlxsw_afk_element_usage elusage; |
107 | int err; |
108 | |
109 | /* Initialize IPv6 ACL group */ |
110 | mlxsw_afk_element_usage_fill(elusage: &elusage, |
111 | elements: mlxsw_sp2_mr_tcam_usage_ipv6, |
112 | ARRAY_SIZE(mlxsw_sp2_mr_tcam_usage_ipv6)); |
113 | mr_tcam->ruleset6 = mlxsw_sp_acl_ruleset_get(mlxsw_sp: mr_tcam->mlxsw_sp, |
114 | block: mr_tcam->flow_block, |
115 | chain_index: MLXSW_SP_L3_PROTO_IPV6, |
116 | profile: MLXSW_SP_ACL_PROFILE_MR, |
117 | tmplt_elusage: &elusage); |
118 | |
119 | if (IS_ERR(ptr: mr_tcam->ruleset6)) |
120 | return PTR_ERR(ptr: mr_tcam->ruleset6); |
121 | |
122 | /* MC Router groups should be bound before routes are inserted. */ |
123 | err = mlxsw_sp2_mr_tcam_bind_group(mlxsw_sp: mr_tcam->mlxsw_sp, |
124 | protocol: MLXSW_REG_PEMRBT_PROTO_IPV6, |
125 | ruleset: mr_tcam->ruleset6); |
126 | if (err) |
127 | goto err_bind_group; |
128 | |
129 | return 0; |
130 | |
131 | err_bind_group: |
132 | mlxsw_sp_acl_ruleset_put(mlxsw_sp: mr_tcam->mlxsw_sp, ruleset: mr_tcam->ruleset6); |
133 | return err; |
134 | } |
135 | |
136 | static void mlxsw_sp2_mr_tcam_ipv6_fini(struct mlxsw_sp2_mr_tcam *mr_tcam) |
137 | { |
138 | mlxsw_sp_acl_ruleset_put(mlxsw_sp: mr_tcam->mlxsw_sp, ruleset: mr_tcam->ruleset6); |
139 | } |
140 | |
141 | static void |
142 | mlxsw_sp2_mr_tcam_rule_parse4(struct mlxsw_sp_acl_rule_info *rulei, |
143 | struct mlxsw_sp_mr_route_key *key) |
144 | { |
145 | mlxsw_sp_acl_rulei_keymask_u32(rulei, element: MLXSW_AFK_ELEMENT_VIRT_ROUTER, |
146 | key_value: key->vrid, GENMASK(11, 0)); |
147 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_SRC_IP_0_31, |
148 | key_value: (char *) &key->source.addr4, |
149 | mask_value: (char *) &key->source_mask.addr4, len: 4); |
150 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_DST_IP_0_31, |
151 | key_value: (char *) &key->group.addr4, |
152 | mask_value: (char *) &key->group_mask.addr4, len: 4); |
153 | } |
154 | |
155 | static void |
156 | mlxsw_sp2_mr_tcam_rule_parse6(struct mlxsw_sp_acl_rule_info *rulei, |
157 | struct mlxsw_sp_mr_route_key *key) |
158 | { |
159 | mlxsw_sp_acl_rulei_keymask_u32(rulei, element: MLXSW_AFK_ELEMENT_VIRT_ROUTER_0_3, |
160 | key_value: key->vrid, GENMASK(3, 0)); |
161 | mlxsw_sp_acl_rulei_keymask_u32(rulei, element: MLXSW_AFK_ELEMENT_VIRT_ROUTER_4_7, |
162 | key_value: key->vrid >> 4, GENMASK(3, 0)); |
163 | mlxsw_sp_acl_rulei_keymask_u32(rulei, |
164 | element: MLXSW_AFK_ELEMENT_VIRT_ROUTER_MSB, |
165 | key_value: key->vrid >> 8, GENMASK(3, 0)); |
166 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_SRC_IP_96_127, |
167 | key_value: &key->source.addr6.s6_addr[0x0], |
168 | mask_value: &key->source_mask.addr6.s6_addr[0x0], len: 4); |
169 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_SRC_IP_64_95, |
170 | key_value: &key->source.addr6.s6_addr[0x4], |
171 | mask_value: &key->source_mask.addr6.s6_addr[0x4], len: 4); |
172 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_SRC_IP_32_63, |
173 | key_value: &key->source.addr6.s6_addr[0x8], |
174 | mask_value: &key->source_mask.addr6.s6_addr[0x8], len: 4); |
175 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_SRC_IP_0_31, |
176 | key_value: &key->source.addr6.s6_addr[0xc], |
177 | mask_value: &key->source_mask.addr6.s6_addr[0xc], len: 4); |
178 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_DST_IP_96_127, |
179 | key_value: &key->group.addr6.s6_addr[0x0], |
180 | mask_value: &key->group_mask.addr6.s6_addr[0x0], len: 4); |
181 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_DST_IP_64_95, |
182 | key_value: &key->group.addr6.s6_addr[0x4], |
183 | mask_value: &key->group_mask.addr6.s6_addr[0x4], len: 4); |
184 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_DST_IP_32_63, |
185 | key_value: &key->group.addr6.s6_addr[0x8], |
186 | mask_value: &key->group_mask.addr6.s6_addr[0x8], len: 4); |
187 | mlxsw_sp_acl_rulei_keymask_buf(rulei, element: MLXSW_AFK_ELEMENT_DST_IP_0_31, |
188 | key_value: &key->group.addr6.s6_addr[0xc], |
189 | mask_value: &key->group_mask.addr6.s6_addr[0xc], len: 4); |
190 | } |
191 | |
192 | static void |
193 | mlxsw_sp2_mr_tcam_rule_parse(struct mlxsw_sp_acl_rule *rule, |
194 | struct mlxsw_sp_mr_route_key *key, |
195 | unsigned int priority) |
196 | { |
197 | struct mlxsw_sp_acl_rule_info *rulei; |
198 | |
199 | rulei = mlxsw_sp_acl_rule_rulei(rule); |
200 | rulei->priority = priority; |
201 | switch (key->proto) { |
202 | case MLXSW_SP_L3_PROTO_IPV4: |
203 | return mlxsw_sp2_mr_tcam_rule_parse4(rulei, key); |
204 | case MLXSW_SP_L3_PROTO_IPV6: |
205 | return mlxsw_sp2_mr_tcam_rule_parse6(rulei, key); |
206 | } |
207 | } |
208 | |
209 | static int |
210 | mlxsw_sp2_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv, |
211 | void *route_priv, |
212 | struct mlxsw_sp_mr_route_key *key, |
213 | struct mlxsw_afa_block *afa_block, |
214 | enum mlxsw_sp_mr_route_prio prio) |
215 | { |
216 | struct mlxsw_sp2_mr_route *mr_route = route_priv; |
217 | struct mlxsw_sp2_mr_tcam *mr_tcam = priv; |
218 | struct mlxsw_sp_acl_ruleset *ruleset; |
219 | struct mlxsw_sp_acl_rule *rule; |
220 | int err; |
221 | |
222 | mr_route->mr_tcam = mr_tcam; |
223 | ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, proto: key->proto); |
224 | if (WARN_ON(!ruleset)) |
225 | return -EINVAL; |
226 | |
227 | rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, |
228 | cookie: (unsigned long) route_priv, afa_block, |
229 | NULL); |
230 | if (IS_ERR(ptr: rule)) |
231 | return PTR_ERR(ptr: rule); |
232 | |
233 | mlxsw_sp2_mr_tcam_rule_parse(rule, key, priority: prio); |
234 | err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule); |
235 | if (err) |
236 | goto err_rule_add; |
237 | |
238 | return 0; |
239 | |
240 | err_rule_add: |
241 | mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); |
242 | return err; |
243 | } |
244 | |
245 | static void |
246 | mlxsw_sp2_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv, |
247 | void *route_priv, |
248 | struct mlxsw_sp_mr_route_key *key) |
249 | { |
250 | struct mlxsw_sp2_mr_tcam *mr_tcam = priv; |
251 | struct mlxsw_sp_acl_ruleset *ruleset; |
252 | struct mlxsw_sp_acl_rule *rule; |
253 | |
254 | ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, proto: key->proto); |
255 | if (WARN_ON(!ruleset)) |
256 | return; |
257 | |
258 | rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, |
259 | cookie: (unsigned long) route_priv); |
260 | if (WARN_ON(!rule)) |
261 | return; |
262 | |
263 | mlxsw_sp_acl_rule_del(mlxsw_sp, rule); |
264 | mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); |
265 | } |
266 | |
267 | static int |
268 | mlxsw_sp2_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, |
269 | void *route_priv, |
270 | struct mlxsw_sp_mr_route_key *key, |
271 | struct mlxsw_afa_block *afa_block) |
272 | { |
273 | struct mlxsw_sp2_mr_route *mr_route = route_priv; |
274 | struct mlxsw_sp2_mr_tcam *mr_tcam = mr_route->mr_tcam; |
275 | struct mlxsw_sp_acl_ruleset *ruleset; |
276 | struct mlxsw_sp_acl_rule *rule; |
277 | |
278 | ruleset = mlxsw_sp2_mr_tcam_proto_ruleset(mr_tcam, proto: key->proto); |
279 | if (WARN_ON(!ruleset)) |
280 | return -EINVAL; |
281 | |
282 | rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, |
283 | cookie: (unsigned long) route_priv); |
284 | if (WARN_ON(!rule)) |
285 | return -EINVAL; |
286 | |
287 | return mlxsw_sp_acl_rule_action_replace(mlxsw_sp, rule, afa_block); |
288 | } |
289 | |
290 | static int mlxsw_sp2_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv) |
291 | { |
292 | struct mlxsw_sp2_mr_tcam *mr_tcam = priv; |
293 | int err; |
294 | |
295 | mr_tcam->mlxsw_sp = mlxsw_sp; |
296 | mr_tcam->flow_block = mlxsw_sp_flow_block_create(mlxsw_sp, NULL); |
297 | if (!mr_tcam->flow_block) |
298 | return -ENOMEM; |
299 | |
300 | err = mlxsw_sp2_mr_tcam_ipv4_init(mr_tcam); |
301 | if (err) |
302 | goto err_ipv4_init; |
303 | |
304 | err = mlxsw_sp2_mr_tcam_ipv6_init(mr_tcam); |
305 | if (err) |
306 | goto err_ipv6_init; |
307 | |
308 | return 0; |
309 | |
310 | err_ipv6_init: |
311 | mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam); |
312 | err_ipv4_init: |
313 | mlxsw_sp_flow_block_destroy(block: mr_tcam->flow_block); |
314 | return err; |
315 | } |
316 | |
317 | static void mlxsw_sp2_mr_tcam_fini(void *priv) |
318 | { |
319 | struct mlxsw_sp2_mr_tcam *mr_tcam = priv; |
320 | |
321 | mlxsw_sp2_mr_tcam_ipv6_fini(mr_tcam); |
322 | mlxsw_sp2_mr_tcam_ipv4_fini(mr_tcam); |
323 | mlxsw_sp_flow_block_destroy(block: mr_tcam->flow_block); |
324 | } |
325 | |
326 | const struct mlxsw_sp_mr_tcam_ops mlxsw_sp2_mr_tcam_ops = { |
327 | .priv_size = sizeof(struct mlxsw_sp2_mr_tcam), |
328 | .init = mlxsw_sp2_mr_tcam_init, |
329 | .fini = mlxsw_sp2_mr_tcam_fini, |
330 | .route_priv_size = sizeof(struct mlxsw_sp2_mr_route), |
331 | .route_create = mlxsw_sp2_mr_tcam_route_create, |
332 | .route_destroy = mlxsw_sp2_mr_tcam_route_destroy, |
333 | .route_update = mlxsw_sp2_mr_tcam_route_update, |
334 | }; |
335 | |