1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/parman.h> |
6 | |
7 | #include "reg.h" |
8 | #include "spectrum.h" |
9 | #include "core_acl_flex_actions.h" |
10 | #include "spectrum_mr.h" |
11 | |
12 | struct mlxsw_sp1_mr_tcam_region { |
13 | struct mlxsw_sp *mlxsw_sp; |
14 | enum mlxsw_reg_rtar_key_type rtar_key_type; |
15 | struct parman *parman; |
16 | struct parman_prio *parman_prios; |
17 | }; |
18 | |
19 | struct mlxsw_sp1_mr_tcam { |
20 | struct mlxsw_sp1_mr_tcam_region tcam_regions[MLXSW_SP_L3_PROTO_MAX]; |
21 | }; |
22 | |
23 | struct mlxsw_sp1_mr_tcam_route { |
24 | struct parman_item parman_item; |
25 | struct parman_prio *parman_prio; |
26 | }; |
27 | |
28 | static int mlxsw_sp1_mr_tcam_route_replace(struct mlxsw_sp *mlxsw_sp, |
29 | struct parman_item *parman_item, |
30 | struct mlxsw_sp_mr_route_key *key, |
31 | struct mlxsw_afa_block *afa_block) |
32 | { |
33 | char rmft2_pl[MLXSW_REG_RMFT2_LEN]; |
34 | |
35 | switch (key->proto) { |
36 | case MLXSW_SP_L3_PROTO_IPV4: |
37 | mlxsw_reg_rmft2_ipv4_pack(payload: rmft2_pl, v: true, offset: parman_item->index, |
38 | virtual_router: key->vrid, |
39 | irif_mask: MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, irif: 0, |
40 | ntohl(key->group.addr4), |
41 | ntohl(key->group_mask.addr4), |
42 | ntohl(key->source.addr4), |
43 | ntohl(key->source_mask.addr4), |
44 | flexible_action_set: mlxsw_afa_block_first_set(block: afa_block)); |
45 | break; |
46 | case MLXSW_SP_L3_PROTO_IPV6: |
47 | mlxsw_reg_rmft2_ipv6_pack(payload: rmft2_pl, v: true, offset: parman_item->index, |
48 | virtual_router: key->vrid, |
49 | irif_mask: MLXSW_REG_RMFT2_IRIF_MASK_IGNORE, irif: 0, |
50 | dip6: key->group.addr6, |
51 | dip6_mask: key->group_mask.addr6, |
52 | sip6: key->source.addr6, |
53 | sip6_mask: key->source_mask.addr6, |
54 | flexible_action_set: mlxsw_afa_block_first_set(block: afa_block)); |
55 | } |
56 | |
57 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rmft2), payload: rmft2_pl); |
58 | } |
59 | |
60 | static int mlxsw_sp1_mr_tcam_route_remove(struct mlxsw_sp *mlxsw_sp, |
61 | struct parman_item *parman_item, |
62 | struct mlxsw_sp_mr_route_key *key) |
63 | { |
64 | struct in6_addr zero_addr = IN6ADDR_ANY_INIT; |
65 | char rmft2_pl[MLXSW_REG_RMFT2_LEN]; |
66 | |
67 | switch (key->proto) { |
68 | case MLXSW_SP_L3_PROTO_IPV4: |
69 | mlxsw_reg_rmft2_ipv4_pack(payload: rmft2_pl, v: false, offset: parman_item->index, |
70 | virtual_router: key->vrid, irif_mask: 0, irif: 0, dip4: 0, dip4_mask: 0, sip4: 0, sip4_mask: 0, NULL); |
71 | break; |
72 | case MLXSW_SP_L3_PROTO_IPV6: |
73 | mlxsw_reg_rmft2_ipv6_pack(payload: rmft2_pl, v: false, offset: parman_item->index, |
74 | virtual_router: key->vrid, irif_mask: 0, irif: 0, dip6: zero_addr, dip6_mask: zero_addr, |
75 | sip6: zero_addr, sip6_mask: zero_addr, NULL); |
76 | break; |
77 | } |
78 | |
79 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rmft2), payload: rmft2_pl); |
80 | } |
81 | |
82 | static struct mlxsw_sp1_mr_tcam_region * |
83 | mlxsw_sp1_mr_tcam_protocol_region(struct mlxsw_sp1_mr_tcam *mr_tcam, |
84 | enum mlxsw_sp_l3proto proto) |
85 | { |
86 | return &mr_tcam->tcam_regions[proto]; |
87 | } |
88 | |
89 | static int |
90 | mlxsw_sp1_mr_tcam_route_parman_item_add(struct mlxsw_sp1_mr_tcam *mr_tcam, |
91 | struct mlxsw_sp1_mr_tcam_route *route, |
92 | struct mlxsw_sp_mr_route_key *key, |
93 | enum mlxsw_sp_mr_route_prio prio) |
94 | { |
95 | struct mlxsw_sp1_mr_tcam_region *tcam_region; |
96 | int err; |
97 | |
98 | tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, proto: key->proto); |
99 | err = parman_item_add(parman: tcam_region->parman, |
100 | prio: &tcam_region->parman_prios[prio], |
101 | item: &route->parman_item); |
102 | if (err) |
103 | return err; |
104 | |
105 | route->parman_prio = &tcam_region->parman_prios[prio]; |
106 | return 0; |
107 | } |
108 | |
109 | static void |
110 | mlxsw_sp1_mr_tcam_route_parman_item_remove(struct mlxsw_sp1_mr_tcam *mr_tcam, |
111 | struct mlxsw_sp1_mr_tcam_route *route, |
112 | struct mlxsw_sp_mr_route_key *key) |
113 | { |
114 | struct mlxsw_sp1_mr_tcam_region *tcam_region; |
115 | |
116 | tcam_region = mlxsw_sp1_mr_tcam_protocol_region(mr_tcam, proto: key->proto); |
117 | parman_item_remove(parman: tcam_region->parman, |
118 | prio: route->parman_prio, item: &route->parman_item); |
119 | } |
120 | |
121 | static int |
122 | mlxsw_sp1_mr_tcam_route_create(struct mlxsw_sp *mlxsw_sp, void *priv, |
123 | void *route_priv, |
124 | struct mlxsw_sp_mr_route_key *key, |
125 | struct mlxsw_afa_block *afa_block, |
126 | enum mlxsw_sp_mr_route_prio prio) |
127 | { |
128 | struct mlxsw_sp1_mr_tcam_route *route = route_priv; |
129 | struct mlxsw_sp1_mr_tcam *mr_tcam = priv; |
130 | int err; |
131 | |
132 | err = mlxsw_sp1_mr_tcam_route_parman_item_add(mr_tcam, route, |
133 | key, prio); |
134 | if (err) |
135 | return err; |
136 | |
137 | err = mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, parman_item: &route->parman_item, |
138 | key, afa_block); |
139 | if (err) |
140 | goto err_route_replace; |
141 | return 0; |
142 | |
143 | err_route_replace: |
144 | mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key); |
145 | return err; |
146 | } |
147 | |
148 | static void |
149 | mlxsw_sp1_mr_tcam_route_destroy(struct mlxsw_sp *mlxsw_sp, void *priv, |
150 | void *route_priv, |
151 | struct mlxsw_sp_mr_route_key *key) |
152 | { |
153 | struct mlxsw_sp1_mr_tcam_route *route = route_priv; |
154 | struct mlxsw_sp1_mr_tcam *mr_tcam = priv; |
155 | |
156 | mlxsw_sp1_mr_tcam_route_remove(mlxsw_sp, parman_item: &route->parman_item, key); |
157 | mlxsw_sp1_mr_tcam_route_parman_item_remove(mr_tcam, route, key); |
158 | } |
159 | |
160 | static int |
161 | mlxsw_sp1_mr_tcam_route_update(struct mlxsw_sp *mlxsw_sp, |
162 | void *route_priv, |
163 | struct mlxsw_sp_mr_route_key *key, |
164 | struct mlxsw_afa_block *afa_block) |
165 | { |
166 | struct mlxsw_sp1_mr_tcam_route *route = route_priv; |
167 | |
168 | return mlxsw_sp1_mr_tcam_route_replace(mlxsw_sp, parman_item: &route->parman_item, |
169 | key, afa_block); |
170 | } |
171 | |
172 | #define MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT 16 |
173 | #define MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP 16 |
174 | |
175 | static int |
176 | mlxsw_sp1_mr_tcam_region_alloc(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) |
177 | { |
178 | struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; |
179 | char rtar_pl[MLXSW_REG_RTAR_LEN]; |
180 | |
181 | mlxsw_reg_rtar_pack(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_ALLOCATE, |
182 | key_type: mr_tcam_region->rtar_key_type, |
183 | MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT); |
184 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: rtar_pl); |
185 | } |
186 | |
187 | static void |
188 | mlxsw_sp1_mr_tcam_region_free(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) |
189 | { |
190 | struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; |
191 | char rtar_pl[MLXSW_REG_RTAR_LEN]; |
192 | |
193 | mlxsw_reg_rtar_pack(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_DEALLOCATE, |
194 | key_type: mr_tcam_region->rtar_key_type, region_size: 0); |
195 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: rtar_pl); |
196 | } |
197 | |
198 | static int mlxsw_sp1_mr_tcam_region_parman_resize(void *priv, |
199 | unsigned long new_count) |
200 | { |
201 | struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv; |
202 | struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; |
203 | char rtar_pl[MLXSW_REG_RTAR_LEN]; |
204 | u64 max_tcam_rules; |
205 | |
206 | max_tcam_rules = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_MAX_TCAM_RULES); |
207 | if (new_count > max_tcam_rules) |
208 | return -EINVAL; |
209 | mlxsw_reg_rtar_pack(payload: rtar_pl, op: MLXSW_REG_RTAR_OP_RESIZE, |
210 | key_type: mr_tcam_region->rtar_key_type, region_size: new_count); |
211 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rtar), payload: rtar_pl); |
212 | } |
213 | |
214 | static void mlxsw_sp1_mr_tcam_region_parman_move(void *priv, |
215 | unsigned long from_index, |
216 | unsigned long to_index, |
217 | unsigned long count) |
218 | { |
219 | struct mlxsw_sp1_mr_tcam_region *mr_tcam_region = priv; |
220 | struct mlxsw_sp *mlxsw_sp = mr_tcam_region->mlxsw_sp; |
221 | char rrcr_pl[MLXSW_REG_RRCR_LEN]; |
222 | |
223 | mlxsw_reg_rrcr_pack(payload: rrcr_pl, op: MLXSW_REG_RRCR_OP_MOVE, |
224 | offset: from_index, size: count, |
225 | table_id: mr_tcam_region->rtar_key_type, dest_offset: to_index); |
226 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(rrcr), payload: rrcr_pl); |
227 | } |
228 | |
229 | static const struct parman_ops mlxsw_sp1_mr_tcam_region_parman_ops = { |
230 | .base_count = MLXSW_SP1_MR_TCAM_REGION_BASE_COUNT, |
231 | .resize_step = MLXSW_SP1_MR_TCAM_REGION_RESIZE_STEP, |
232 | .resize = mlxsw_sp1_mr_tcam_region_parman_resize, |
233 | .move = mlxsw_sp1_mr_tcam_region_parman_move, |
234 | .algo = PARMAN_ALGO_TYPE_LSORT, |
235 | }; |
236 | |
237 | static int |
238 | mlxsw_sp1_mr_tcam_region_init(struct mlxsw_sp *mlxsw_sp, |
239 | struct mlxsw_sp1_mr_tcam_region *mr_tcam_region, |
240 | enum mlxsw_reg_rtar_key_type rtar_key_type) |
241 | { |
242 | struct parman_prio *parman_prios; |
243 | struct parman *parman; |
244 | int err; |
245 | int i; |
246 | |
247 | mr_tcam_region->rtar_key_type = rtar_key_type; |
248 | mr_tcam_region->mlxsw_sp = mlxsw_sp; |
249 | |
250 | err = mlxsw_sp1_mr_tcam_region_alloc(mr_tcam_region); |
251 | if (err) |
252 | return err; |
253 | |
254 | parman = parman_create(ops: &mlxsw_sp1_mr_tcam_region_parman_ops, |
255 | priv: mr_tcam_region); |
256 | if (!parman) { |
257 | err = -ENOMEM; |
258 | goto err_parman_create; |
259 | } |
260 | mr_tcam_region->parman = parman; |
261 | |
262 | parman_prios = kmalloc_array(MLXSW_SP_MR_ROUTE_PRIO_MAX + 1, |
263 | size: sizeof(*parman_prios), GFP_KERNEL); |
264 | if (!parman_prios) { |
265 | err = -ENOMEM; |
266 | goto err_parman_prios_alloc; |
267 | } |
268 | mr_tcam_region->parman_prios = parman_prios; |
269 | |
270 | for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++) |
271 | parman_prio_init(parman: mr_tcam_region->parman, |
272 | prio: &mr_tcam_region->parman_prios[i], priority: i); |
273 | return 0; |
274 | |
275 | err_parman_prios_alloc: |
276 | parman_destroy(parman); |
277 | err_parman_create: |
278 | mlxsw_sp1_mr_tcam_region_free(mr_tcam_region); |
279 | return err; |
280 | } |
281 | |
282 | static void |
283 | mlxsw_sp1_mr_tcam_region_fini(struct mlxsw_sp1_mr_tcam_region *mr_tcam_region) |
284 | { |
285 | int i; |
286 | |
287 | for (i = 0; i < MLXSW_SP_MR_ROUTE_PRIO_MAX + 1; i++) |
288 | parman_prio_fini(prio: &mr_tcam_region->parman_prios[i]); |
289 | kfree(objp: mr_tcam_region->parman_prios); |
290 | parman_destroy(parman: mr_tcam_region->parman); |
291 | mlxsw_sp1_mr_tcam_region_free(mr_tcam_region); |
292 | } |
293 | |
294 | static int mlxsw_sp1_mr_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv) |
295 | { |
296 | struct mlxsw_sp1_mr_tcam *mr_tcam = priv; |
297 | struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0]; |
298 | u32 rtar_key; |
299 | int err; |
300 | |
301 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_TCAM_RULES)) |
302 | return -EIO; |
303 | |
304 | rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV4_MULTICAST; |
305 | err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp, |
306 | mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV4], |
307 | rtar_key_type: rtar_key); |
308 | if (err) |
309 | return err; |
310 | |
311 | rtar_key = MLXSW_REG_RTAR_KEY_TYPE_IPV6_MULTICAST; |
312 | err = mlxsw_sp1_mr_tcam_region_init(mlxsw_sp, |
313 | mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV6], |
314 | rtar_key_type: rtar_key); |
315 | if (err) |
316 | goto err_ipv6_region_init; |
317 | |
318 | return 0; |
319 | |
320 | err_ipv6_region_init: |
321 | mlxsw_sp1_mr_tcam_region_fini(mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV4]); |
322 | return err; |
323 | } |
324 | |
325 | static void mlxsw_sp1_mr_tcam_fini(void *priv) |
326 | { |
327 | struct mlxsw_sp1_mr_tcam *mr_tcam = priv; |
328 | struct mlxsw_sp1_mr_tcam_region *region = &mr_tcam->tcam_regions[0]; |
329 | |
330 | mlxsw_sp1_mr_tcam_region_fini(mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV6]); |
331 | mlxsw_sp1_mr_tcam_region_fini(mr_tcam_region: ®ion[MLXSW_SP_L3_PROTO_IPV4]); |
332 | } |
333 | |
334 | const struct mlxsw_sp_mr_tcam_ops mlxsw_sp1_mr_tcam_ops = { |
335 | .priv_size = sizeof(struct mlxsw_sp1_mr_tcam), |
336 | .init = mlxsw_sp1_mr_tcam_init, |
337 | .fini = mlxsw_sp1_mr_tcam_fini, |
338 | .route_priv_size = sizeof(struct mlxsw_sp1_mr_tcam_route), |
339 | .route_create = mlxsw_sp1_mr_tcam_route_create, |
340 | .route_destroy = mlxsw_sp1_mr_tcam_route_destroy, |
341 | .route_update = mlxsw_sp1_mr_tcam_route_update, |
342 | }; |
343 | |