1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */ |
3 | |
4 | #include <linux/bitmap.h> |
5 | #include <linux/errno.h> |
6 | #include <linux/genalloc.h> |
7 | #include <linux/gfp.h> |
8 | #include <linux/kernel.h> |
9 | #include <linux/list.h> |
10 | #include <linux/mutex.h> |
11 | #include <linux/objagg.h> |
12 | #include <linux/rtnetlink.h> |
13 | #include <linux/slab.h> |
14 | |
15 | #include "core.h" |
16 | #include "reg.h" |
17 | #include "spectrum.h" |
18 | #include "spectrum_acl_tcam.h" |
19 | |
20 | /* gen_pool_alloc() returns 0 when allocation fails, so use an offset */ |
21 | #define MLXSW_SP_ACL_ERP_GENALLOC_OFFSET 0x100 |
22 | #define MLXSW_SP_ACL_ERP_MAX_PER_REGION 16 |
23 | |
24 | struct mlxsw_sp_acl_erp_core { |
25 | unsigned int erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX + 1]; |
26 | struct gen_pool *erp_tables; |
27 | struct mlxsw_sp *mlxsw_sp; |
28 | struct mlxsw_sp_acl_bf *bf; |
29 | unsigned int num_erp_banks; |
30 | }; |
31 | |
32 | struct mlxsw_sp_acl_erp_key { |
33 | char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; |
34 | #define __MASK_LEN 0x38 |
35 | #define __MASK_IDX(i) (__MASK_LEN - (i) - 1) |
36 | bool ctcam; |
37 | }; |
38 | |
39 | struct mlxsw_sp_acl_erp { |
40 | struct mlxsw_sp_acl_erp_key key; |
41 | u8 id; |
42 | u8 index; |
43 | DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN); |
44 | struct list_head list; |
45 | struct mlxsw_sp_acl_erp_table *erp_table; |
46 | }; |
47 | |
48 | struct mlxsw_sp_acl_erp_master_mask { |
49 | DECLARE_BITMAP(bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN); |
50 | unsigned int count[MLXSW_SP_ACL_TCAM_MASK_LEN]; |
51 | }; |
52 | |
53 | struct mlxsw_sp_acl_erp_table { |
54 | struct mlxsw_sp_acl_erp_master_mask master_mask; |
55 | DECLARE_BITMAP(erp_id_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
56 | DECLARE_BITMAP(erp_index_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
57 | struct list_head atcam_erps_list; |
58 | struct mlxsw_sp_acl_erp_core *erp_core; |
59 | struct mlxsw_sp_acl_atcam_region *aregion; |
60 | const struct mlxsw_sp_acl_erp_table_ops *ops; |
61 | unsigned long base_index; |
62 | unsigned int num_atcam_erps; |
63 | unsigned int num_max_atcam_erps; |
64 | unsigned int num_ctcam_erps; |
65 | unsigned int num_deltas; |
66 | struct objagg *objagg; |
67 | struct mutex objagg_lock; /* guards objagg manipulation */ |
68 | }; |
69 | |
70 | struct mlxsw_sp_acl_erp_table_ops { |
71 | struct mlxsw_sp_acl_erp * |
72 | (*erp_create)(struct mlxsw_sp_acl_erp_table *erp_table, |
73 | struct mlxsw_sp_acl_erp_key *key); |
74 | void (*erp_destroy)(struct mlxsw_sp_acl_erp_table *erp_table, |
75 | struct mlxsw_sp_acl_erp *erp); |
76 | }; |
77 | |
78 | static struct mlxsw_sp_acl_erp * |
79 | mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
80 | struct mlxsw_sp_acl_erp_key *key); |
81 | static void |
82 | mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
83 | struct mlxsw_sp_acl_erp *erp); |
84 | static struct mlxsw_sp_acl_erp * |
85 | mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
86 | struct mlxsw_sp_acl_erp_key *key); |
87 | static void |
88 | mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
89 | struct mlxsw_sp_acl_erp *erp); |
90 | static struct mlxsw_sp_acl_erp * |
91 | mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
92 | struct mlxsw_sp_acl_erp_key *key); |
93 | static void |
94 | mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
95 | struct mlxsw_sp_acl_erp *erp); |
96 | static void |
97 | mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
98 | struct mlxsw_sp_acl_erp *erp); |
99 | |
100 | static const struct mlxsw_sp_acl_erp_table_ops erp_multiple_masks_ops = { |
101 | .erp_create = mlxsw_sp_acl_erp_mask_create, |
102 | .erp_destroy = mlxsw_sp_acl_erp_mask_destroy, |
103 | }; |
104 | |
105 | static const struct mlxsw_sp_acl_erp_table_ops erp_two_masks_ops = { |
106 | .erp_create = mlxsw_sp_acl_erp_mask_create, |
107 | .erp_destroy = mlxsw_sp_acl_erp_second_mask_destroy, |
108 | }; |
109 | |
110 | static const struct mlxsw_sp_acl_erp_table_ops erp_single_mask_ops = { |
111 | .erp_create = mlxsw_sp_acl_erp_second_mask_create, |
112 | .erp_destroy = mlxsw_sp_acl_erp_first_mask_destroy, |
113 | }; |
114 | |
115 | static const struct mlxsw_sp_acl_erp_table_ops erp_no_mask_ops = { |
116 | .erp_create = mlxsw_sp_acl_erp_first_mask_create, |
117 | .erp_destroy = mlxsw_sp_acl_erp_no_mask_destroy, |
118 | }; |
119 | |
120 | static bool |
121 | mlxsw_sp_acl_erp_table_is_used(const struct mlxsw_sp_acl_erp_table *erp_table) |
122 | { |
123 | return erp_table->ops != &erp_single_mask_ops && |
124 | erp_table->ops != &erp_no_mask_ops; |
125 | } |
126 | |
127 | static unsigned int |
128 | mlxsw_sp_acl_erp_bank_get(const struct mlxsw_sp_acl_erp *erp) |
129 | { |
130 | return erp->index % erp->erp_table->erp_core->num_erp_banks; |
131 | } |
132 | |
133 | static unsigned int |
134 | mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table *erp_table) |
135 | { |
136 | struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion; |
137 | struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core; |
138 | |
139 | return erp_core->erpt_entries_size[aregion->type]; |
140 | } |
141 | |
142 | static int mlxsw_sp_acl_erp_id_get(struct mlxsw_sp_acl_erp_table *erp_table, |
143 | u8 *p_id) |
144 | { |
145 | u8 id; |
146 | |
147 | id = find_first_zero_bit(addr: erp_table->erp_id_bitmap, |
148 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
149 | if (id < MLXSW_SP_ACL_ERP_MAX_PER_REGION) { |
150 | __set_bit(id, erp_table->erp_id_bitmap); |
151 | *p_id = id; |
152 | return 0; |
153 | } |
154 | |
155 | return -ENOBUFS; |
156 | } |
157 | |
158 | static void mlxsw_sp_acl_erp_id_put(struct mlxsw_sp_acl_erp_table *erp_table, |
159 | u8 id) |
160 | { |
161 | __clear_bit(id, erp_table->erp_id_bitmap); |
162 | } |
163 | |
164 | static void |
165 | mlxsw_sp_acl_erp_master_mask_bit_set(unsigned long bit, |
166 | struct mlxsw_sp_acl_erp_master_mask *mask) |
167 | { |
168 | if (mask->count[bit]++ == 0) |
169 | __set_bit(bit, mask->bitmap); |
170 | } |
171 | |
172 | static void |
173 | mlxsw_sp_acl_erp_master_mask_bit_clear(unsigned long bit, |
174 | struct mlxsw_sp_acl_erp_master_mask *mask) |
175 | { |
176 | if (--mask->count[bit] == 0) |
177 | __clear_bit(bit, mask->bitmap); |
178 | } |
179 | |
180 | static int |
181 | mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table *erp_table) |
182 | { |
183 | struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region; |
184 | struct mlxsw_sp *mlxsw_sp = region->mlxsw_sp; |
185 | char percr_pl[MLXSW_REG_PERCR_LEN]; |
186 | char *master_mask; |
187 | |
188 | mlxsw_reg_percr_pack(payload: percr_pl, region_id: region->id); |
189 | master_mask = mlxsw_reg_percr_master_mask_data(buf: percr_pl); |
190 | bitmap_to_arr32(buf: (u32 *) master_mask, bitmap: erp_table->master_mask.bitmap, |
191 | MLXSW_SP_ACL_TCAM_MASK_LEN); |
192 | |
193 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(percr), payload: percr_pl); |
194 | } |
195 | |
196 | static int |
197 | mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table, |
198 | struct mlxsw_sp_acl_erp_key *key) |
199 | { |
200 | DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN); |
201 | unsigned long bit; |
202 | int err; |
203 | |
204 | bitmap_from_arr32(bitmap: mask_bitmap, buf: (u32 *) key->mask, |
205 | MLXSW_SP_ACL_TCAM_MASK_LEN); |
206 | for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN) |
207 | mlxsw_sp_acl_erp_master_mask_bit_set(bit, |
208 | mask: &erp_table->master_mask); |
209 | |
210 | err = mlxsw_sp_acl_erp_master_mask_update(erp_table); |
211 | if (err) |
212 | goto err_master_mask_update; |
213 | |
214 | return 0; |
215 | |
216 | err_master_mask_update: |
217 | for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN) |
218 | mlxsw_sp_acl_erp_master_mask_bit_clear(bit, |
219 | mask: &erp_table->master_mask); |
220 | return err; |
221 | } |
222 | |
223 | static int |
224 | mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table, |
225 | struct mlxsw_sp_acl_erp_key *key) |
226 | { |
227 | DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN); |
228 | unsigned long bit; |
229 | int err; |
230 | |
231 | bitmap_from_arr32(bitmap: mask_bitmap, buf: (u32 *) key->mask, |
232 | MLXSW_SP_ACL_TCAM_MASK_LEN); |
233 | for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN) |
234 | mlxsw_sp_acl_erp_master_mask_bit_clear(bit, |
235 | mask: &erp_table->master_mask); |
236 | |
237 | err = mlxsw_sp_acl_erp_master_mask_update(erp_table); |
238 | if (err) |
239 | goto err_master_mask_update; |
240 | |
241 | return 0; |
242 | |
243 | err_master_mask_update: |
244 | for_each_set_bit(bit, mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN) |
245 | mlxsw_sp_acl_erp_master_mask_bit_set(bit, |
246 | mask: &erp_table->master_mask); |
247 | return err; |
248 | } |
249 | |
250 | static struct mlxsw_sp_acl_erp * |
251 | mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table, |
252 | struct mlxsw_sp_acl_erp_key *key) |
253 | { |
254 | struct mlxsw_sp_acl_erp *erp; |
255 | int err; |
256 | |
257 | erp = kzalloc(size: sizeof(*erp), GFP_KERNEL); |
258 | if (!erp) |
259 | return ERR_PTR(error: -ENOMEM); |
260 | |
261 | err = mlxsw_sp_acl_erp_id_get(erp_table, p_id: &erp->id); |
262 | if (err) |
263 | goto err_erp_id_get; |
264 | |
265 | memcpy(&erp->key, key, sizeof(*key)); |
266 | list_add(new: &erp->list, head: &erp_table->atcam_erps_list); |
267 | erp_table->num_atcam_erps++; |
268 | erp->erp_table = erp_table; |
269 | |
270 | err = mlxsw_sp_acl_erp_master_mask_set(erp_table, key: &erp->key); |
271 | if (err) |
272 | goto err_master_mask_set; |
273 | |
274 | return erp; |
275 | |
276 | err_master_mask_set: |
277 | erp_table->num_atcam_erps--; |
278 | list_del(entry: &erp->list); |
279 | mlxsw_sp_acl_erp_id_put(erp_table, id: erp->id); |
280 | err_erp_id_get: |
281 | kfree(objp: erp); |
282 | return ERR_PTR(error: err); |
283 | } |
284 | |
285 | static void |
286 | mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp) |
287 | { |
288 | struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table; |
289 | |
290 | mlxsw_sp_acl_erp_master_mask_clear(erp_table, key: &erp->key); |
291 | erp_table->num_atcam_erps--; |
292 | list_del(entry: &erp->list); |
293 | mlxsw_sp_acl_erp_id_put(erp_table, id: erp->id); |
294 | kfree(objp: erp); |
295 | } |
296 | |
297 | static int |
298 | mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core *erp_core, |
299 | unsigned int num_erps, |
300 | enum mlxsw_sp_acl_atcam_region_type region_type, |
301 | unsigned long *p_index) |
302 | { |
303 | unsigned int num_rows, entry_size; |
304 | unsigned long index; |
305 | |
306 | /* We only allow allocations of entire rows */ |
307 | if (num_erps % erp_core->num_erp_banks != 0) |
308 | return -EINVAL; |
309 | |
310 | entry_size = erp_core->erpt_entries_size[region_type]; |
311 | num_rows = num_erps / erp_core->num_erp_banks; |
312 | |
313 | index = gen_pool_alloc(pool: erp_core->erp_tables, size: num_rows * entry_size); |
314 | if (!index) |
315 | return -ENOBUFS; |
316 | |
317 | *p_index = index - MLXSW_SP_ACL_ERP_GENALLOC_OFFSET; |
318 | |
319 | return 0; |
320 | } |
321 | |
322 | static void |
323 | mlxsw_sp_acl_erp_table_free(struct mlxsw_sp_acl_erp_core *erp_core, |
324 | unsigned int num_erps, |
325 | enum mlxsw_sp_acl_atcam_region_type region_type, |
326 | unsigned long index) |
327 | { |
328 | unsigned long base_index; |
329 | unsigned int entry_size; |
330 | size_t size; |
331 | |
332 | entry_size = erp_core->erpt_entries_size[region_type]; |
333 | base_index = index + MLXSW_SP_ACL_ERP_GENALLOC_OFFSET; |
334 | size = num_erps / erp_core->num_erp_banks * entry_size; |
335 | gen_pool_free(pool: erp_core->erp_tables, addr: base_index, size); |
336 | } |
337 | |
338 | static struct mlxsw_sp_acl_erp * |
339 | mlxsw_sp_acl_erp_table_master_rp(struct mlxsw_sp_acl_erp_table *erp_table) |
340 | { |
341 | if (!list_is_singular(head: &erp_table->atcam_erps_list)) |
342 | return NULL; |
343 | |
344 | return list_first_entry(&erp_table->atcam_erps_list, |
345 | struct mlxsw_sp_acl_erp, list); |
346 | } |
347 | |
348 | static int mlxsw_sp_acl_erp_index_get(struct mlxsw_sp_acl_erp_table *erp_table, |
349 | u8 *p_index) |
350 | { |
351 | u8 index; |
352 | |
353 | index = find_first_zero_bit(addr: erp_table->erp_index_bitmap, |
354 | size: erp_table->num_max_atcam_erps); |
355 | if (index < erp_table->num_max_atcam_erps) { |
356 | __set_bit(index, erp_table->erp_index_bitmap); |
357 | *p_index = index; |
358 | return 0; |
359 | } |
360 | |
361 | return -ENOBUFS; |
362 | } |
363 | |
364 | static void mlxsw_sp_acl_erp_index_put(struct mlxsw_sp_acl_erp_table *erp_table, |
365 | u8 index) |
366 | { |
367 | __clear_bit(index, erp_table->erp_index_bitmap); |
368 | } |
369 | |
370 | static void |
371 | mlxsw_sp_acl_erp_table_locate(const struct mlxsw_sp_acl_erp_table *erp_table, |
372 | const struct mlxsw_sp_acl_erp *erp, |
373 | u8 *p_erpt_bank, u8 *p_erpt_index) |
374 | { |
375 | unsigned int entry_size = mlxsw_sp_acl_erp_table_entry_size(erp_table); |
376 | struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core; |
377 | unsigned int row; |
378 | |
379 | *p_erpt_bank = erp->index % erp_core->num_erp_banks; |
380 | row = erp->index / erp_core->num_erp_banks; |
381 | *p_erpt_index = erp_table->base_index + row * entry_size; |
382 | } |
383 | |
384 | static int |
385 | mlxsw_sp_acl_erp_table_erp_add(struct mlxsw_sp_acl_erp_table *erp_table, |
386 | struct mlxsw_sp_acl_erp *erp) |
387 | { |
388 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
389 | enum mlxsw_reg_perpt_key_size key_size; |
390 | char perpt_pl[MLXSW_REG_PERPT_LEN]; |
391 | u8 erpt_bank, erpt_index; |
392 | |
393 | mlxsw_sp_acl_erp_table_locate(erp_table, erp, p_erpt_bank: &erpt_bank, p_erpt_index: &erpt_index); |
394 | key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type; |
395 | mlxsw_reg_perpt_pack(payload: perpt_pl, erpt_bank, erpt_index, key_size, erp_id: erp->id, |
396 | erpt_base_bank: 0, erpt_base_index: erp_table->base_index, erp_index: erp->index, |
397 | mask: erp->key.mask); |
398 | mlxsw_reg_perpt_erp_vector_pack(payload: perpt_pl, erp_vector: erp_table->erp_index_bitmap, |
399 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
400 | mlxsw_reg_perpt_erp_vector_set(buf: perpt_pl, index: erp->index, val: true); |
401 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(perpt), payload: perpt_pl); |
402 | } |
403 | |
404 | static void mlxsw_sp_acl_erp_table_erp_del(struct mlxsw_sp_acl_erp *erp) |
405 | { |
406 | char empty_mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 }; |
407 | struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table; |
408 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
409 | enum mlxsw_reg_perpt_key_size key_size; |
410 | char perpt_pl[MLXSW_REG_PERPT_LEN]; |
411 | u8 erpt_bank, erpt_index; |
412 | |
413 | mlxsw_sp_acl_erp_table_locate(erp_table, erp, p_erpt_bank: &erpt_bank, p_erpt_index: &erpt_index); |
414 | key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type; |
415 | mlxsw_reg_perpt_pack(payload: perpt_pl, erpt_bank, erpt_index, key_size, erp_id: erp->id, |
416 | erpt_base_bank: 0, erpt_base_index: erp_table->base_index, erp_index: erp->index, mask: empty_mask); |
417 | mlxsw_reg_perpt_erp_vector_pack(payload: perpt_pl, erp_vector: erp_table->erp_index_bitmap, |
418 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
419 | mlxsw_reg_perpt_erp_vector_set(buf: perpt_pl, index: erp->index, val: false); |
420 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(perpt), payload: perpt_pl); |
421 | } |
422 | |
423 | static int |
424 | mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table *erp_table, |
425 | bool ctcam_le) |
426 | { |
427 | struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region; |
428 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
429 | char pererp_pl[MLXSW_REG_PERERP_LEN]; |
430 | |
431 | mlxsw_reg_pererp_pack(payload: pererp_pl, region_id: region->id, ctcam_le, erpt_pointer_valid: true, erpt_bank_pointer: 0, |
432 | erpt_pointer: erp_table->base_index, master_rp_id: 0); |
433 | mlxsw_reg_pererp_erp_vector_pack(payload: pererp_pl, erp_vector: erp_table->erp_index_bitmap, |
434 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
435 | |
436 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pererp), payload: pererp_pl); |
437 | } |
438 | |
439 | static void |
440 | mlxsw_sp_acl_erp_table_disable(struct mlxsw_sp_acl_erp_table *erp_table) |
441 | { |
442 | struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region; |
443 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
444 | char pererp_pl[MLXSW_REG_PERERP_LEN]; |
445 | struct mlxsw_sp_acl_erp *master_rp; |
446 | |
447 | master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table); |
448 | /* It is possible we do not have a master RP when we disable the |
449 | * table when there are no rules in the A-TCAM and the last C-TCAM |
450 | * rule is deleted |
451 | */ |
452 | mlxsw_reg_pererp_pack(payload: pererp_pl, region_id: region->id, ctcam_le: false, erpt_pointer_valid: false, erpt_bank_pointer: 0, erpt_pointer: 0, |
453 | master_rp_id: master_rp ? master_rp->id : 0); |
454 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pererp), payload: pererp_pl); |
455 | } |
456 | |
457 | static int |
458 | mlxsw_sp_acl_erp_table_relocate(struct mlxsw_sp_acl_erp_table *erp_table) |
459 | { |
460 | struct mlxsw_sp_acl_erp *erp; |
461 | int err; |
462 | |
463 | list_for_each_entry(erp, &erp_table->atcam_erps_list, list) { |
464 | err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp); |
465 | if (err) |
466 | goto err_table_erp_add; |
467 | } |
468 | |
469 | return 0; |
470 | |
471 | err_table_erp_add: |
472 | list_for_each_entry_continue_reverse(erp, &erp_table->atcam_erps_list, |
473 | list) |
474 | mlxsw_sp_acl_erp_table_erp_del(erp); |
475 | return err; |
476 | } |
477 | |
478 | static int |
479 | mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table *erp_table) |
480 | { |
481 | unsigned int num_erps, old_num_erps = erp_table->num_max_atcam_erps; |
482 | struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core; |
483 | unsigned long old_base_index = erp_table->base_index; |
484 | bool ctcam_le = erp_table->num_ctcam_erps > 0; |
485 | int err; |
486 | |
487 | if (erp_table->num_atcam_erps < erp_table->num_max_atcam_erps) |
488 | return 0; |
489 | |
490 | if (erp_table->num_max_atcam_erps == MLXSW_SP_ACL_ERP_MAX_PER_REGION) |
491 | return -ENOBUFS; |
492 | |
493 | num_erps = old_num_erps + erp_core->num_erp_banks; |
494 | err = mlxsw_sp_acl_erp_table_alloc(erp_core, num_erps, |
495 | region_type: erp_table->aregion->type, |
496 | p_index: &erp_table->base_index); |
497 | if (err) |
498 | return err; |
499 | erp_table->num_max_atcam_erps = num_erps; |
500 | |
501 | err = mlxsw_sp_acl_erp_table_relocate(erp_table); |
502 | if (err) |
503 | goto err_table_relocate; |
504 | |
505 | err = mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le); |
506 | if (err) |
507 | goto err_table_enable; |
508 | |
509 | mlxsw_sp_acl_erp_table_free(erp_core, num_erps: old_num_erps, |
510 | region_type: erp_table->aregion->type, index: old_base_index); |
511 | |
512 | return 0; |
513 | |
514 | err_table_enable: |
515 | err_table_relocate: |
516 | erp_table->num_max_atcam_erps = old_num_erps; |
517 | mlxsw_sp_acl_erp_table_free(erp_core, num_erps, |
518 | region_type: erp_table->aregion->type, |
519 | index: erp_table->base_index); |
520 | erp_table->base_index = old_base_index; |
521 | return err; |
522 | } |
523 | |
524 | static int |
525 | mlxsw_acl_erp_table_bf_add(struct mlxsw_sp_acl_erp_table *erp_table, |
526 | struct mlxsw_sp_acl_erp *erp) |
527 | { |
528 | struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion; |
529 | unsigned int erp_bank = mlxsw_sp_acl_erp_bank_get(erp); |
530 | struct mlxsw_sp_acl_atcam_entry *aentry; |
531 | int err; |
532 | |
533 | list_for_each_entry(aentry, &aregion->entries_list, list) { |
534 | err = mlxsw_sp_acl_bf_entry_add(mlxsw_sp: aregion->region->mlxsw_sp, |
535 | bf: erp_table->erp_core->bf, |
536 | aregion, erp_bank, aentry); |
537 | if (err) |
538 | goto bf_entry_add_err; |
539 | } |
540 | |
541 | return 0; |
542 | |
543 | bf_entry_add_err: |
544 | list_for_each_entry_continue_reverse(aentry, &aregion->entries_list, |
545 | list) |
546 | mlxsw_sp_acl_bf_entry_del(mlxsw_sp: aregion->region->mlxsw_sp, |
547 | bf: erp_table->erp_core->bf, |
548 | aregion, erp_bank, aentry); |
549 | return err; |
550 | } |
551 | |
552 | static void |
553 | mlxsw_acl_erp_table_bf_del(struct mlxsw_sp_acl_erp_table *erp_table, |
554 | struct mlxsw_sp_acl_erp *erp) |
555 | { |
556 | struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion; |
557 | unsigned int erp_bank = mlxsw_sp_acl_erp_bank_get(erp); |
558 | struct mlxsw_sp_acl_atcam_entry *aentry; |
559 | |
560 | list_for_each_entry_reverse(aentry, &aregion->entries_list, list) |
561 | mlxsw_sp_acl_bf_entry_del(mlxsw_sp: aregion->region->mlxsw_sp, |
562 | bf: erp_table->erp_core->bf, |
563 | aregion, erp_bank, aentry); |
564 | } |
565 | |
566 | static int |
567 | mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table) |
568 | { |
569 | struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core; |
570 | struct mlxsw_sp_acl_erp *master_rp; |
571 | int err; |
572 | |
573 | /* Initially, allocate a single eRP row. Expand later as needed */ |
574 | err = mlxsw_sp_acl_erp_table_alloc(erp_core, num_erps: erp_core->num_erp_banks, |
575 | region_type: erp_table->aregion->type, |
576 | p_index: &erp_table->base_index); |
577 | if (err) |
578 | return err; |
579 | erp_table->num_max_atcam_erps = erp_core->num_erp_banks; |
580 | |
581 | /* Transition the sole RP currently configured (the master RP) |
582 | * to the eRP table |
583 | */ |
584 | master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table); |
585 | if (!master_rp) { |
586 | err = -EINVAL; |
587 | goto err_table_master_rp; |
588 | } |
589 | |
590 | /* Make sure the master RP is using a valid index, as |
591 | * only a single eRP row is currently allocated. |
592 | */ |
593 | master_rp->index = 0; |
594 | __set_bit(master_rp->index, erp_table->erp_index_bitmap); |
595 | |
596 | err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp: master_rp); |
597 | if (err) |
598 | goto err_table_master_rp_add; |
599 | |
600 | /* Update Bloom filter before enabling eRP table, as rules |
601 | * on the master RP were not set to Bloom filter up to this |
602 | * point. |
603 | */ |
604 | err = mlxsw_acl_erp_table_bf_add(erp_table, erp: master_rp); |
605 | if (err) |
606 | goto err_table_bf_add; |
607 | |
608 | err = mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le: false); |
609 | if (err) |
610 | goto err_table_enable; |
611 | |
612 | return 0; |
613 | |
614 | err_table_enable: |
615 | mlxsw_acl_erp_table_bf_del(erp_table, erp: master_rp); |
616 | err_table_bf_add: |
617 | mlxsw_sp_acl_erp_table_erp_del(erp: master_rp); |
618 | err_table_master_rp_add: |
619 | __clear_bit(master_rp->index, erp_table->erp_index_bitmap); |
620 | err_table_master_rp: |
621 | mlxsw_sp_acl_erp_table_free(erp_core, num_erps: erp_table->num_max_atcam_erps, |
622 | region_type: erp_table->aregion->type, |
623 | index: erp_table->base_index); |
624 | return err; |
625 | } |
626 | |
627 | static void |
628 | mlxsw_sp_acl_erp_region_master_mask_trans(struct mlxsw_sp_acl_erp_table *erp_table) |
629 | { |
630 | struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core; |
631 | struct mlxsw_sp_acl_erp *master_rp; |
632 | |
633 | mlxsw_sp_acl_erp_table_disable(erp_table); |
634 | master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table); |
635 | if (!master_rp) |
636 | return; |
637 | mlxsw_acl_erp_table_bf_del(erp_table, erp: master_rp); |
638 | mlxsw_sp_acl_erp_table_erp_del(erp: master_rp); |
639 | __clear_bit(master_rp->index, erp_table->erp_index_bitmap); |
640 | mlxsw_sp_acl_erp_table_free(erp_core, num_erps: erp_table->num_max_atcam_erps, |
641 | region_type: erp_table->aregion->type, |
642 | index: erp_table->base_index); |
643 | } |
644 | |
645 | static int |
646 | mlxsw_sp_acl_erp_region_erp_add(struct mlxsw_sp_acl_erp_table *erp_table, |
647 | struct mlxsw_sp_acl_erp *erp) |
648 | { |
649 | struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region; |
650 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
651 | bool ctcam_le = erp_table->num_ctcam_erps > 0; |
652 | char pererp_pl[MLXSW_REG_PERERP_LEN]; |
653 | |
654 | mlxsw_reg_pererp_pack(payload: pererp_pl, region_id: region->id, ctcam_le, erpt_pointer_valid: true, erpt_bank_pointer: 0, |
655 | erpt_pointer: erp_table->base_index, master_rp_id: 0); |
656 | mlxsw_reg_pererp_erp_vector_pack(payload: pererp_pl, erp_vector: erp_table->erp_index_bitmap, |
657 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
658 | mlxsw_reg_pererp_erpt_vector_set(buf: pererp_pl, index: erp->index, val: true); |
659 | |
660 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pererp), payload: pererp_pl); |
661 | } |
662 | |
663 | static void mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp *erp) |
664 | { |
665 | struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table; |
666 | struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region; |
667 | struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp; |
668 | bool ctcam_le = erp_table->num_ctcam_erps > 0; |
669 | char pererp_pl[MLXSW_REG_PERERP_LEN]; |
670 | |
671 | mlxsw_reg_pererp_pack(payload: pererp_pl, region_id: region->id, ctcam_le, erpt_pointer_valid: true, erpt_bank_pointer: 0, |
672 | erpt_pointer: erp_table->base_index, master_rp_id: 0); |
673 | mlxsw_reg_pererp_erp_vector_pack(payload: pererp_pl, erp_vector: erp_table->erp_index_bitmap, |
674 | MLXSW_SP_ACL_ERP_MAX_PER_REGION); |
675 | mlxsw_reg_pererp_erpt_vector_set(buf: pererp_pl, index: erp->index, val: false); |
676 | |
677 | mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pererp), payload: pererp_pl); |
678 | } |
679 | |
680 | static int |
681 | mlxsw_sp_acl_erp_region_ctcam_enable(struct mlxsw_sp_acl_erp_table *erp_table) |
682 | { |
683 | /* No need to re-enable lookup in the C-TCAM */ |
684 | if (erp_table->num_ctcam_erps > 1) |
685 | return 0; |
686 | |
687 | return mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le: true); |
688 | } |
689 | |
690 | static void |
691 | mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table *erp_table) |
692 | { |
693 | /* Only disable C-TCAM lookup when last C-TCAM eRP is deleted */ |
694 | if (erp_table->num_ctcam_erps > 1) |
695 | return; |
696 | |
697 | mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le: false); |
698 | } |
699 | |
700 | static int |
701 | __mlxsw_sp_acl_erp_table_other_inc(struct mlxsw_sp_acl_erp_table *erp_table, |
702 | unsigned int *inc_num) |
703 | { |
704 | int err; |
705 | |
706 | /* If there are C-TCAM eRP or deltas in use we need to transition |
707 | * the region to use eRP table, if it is not already done |
708 | */ |
709 | if (!mlxsw_sp_acl_erp_table_is_used(erp_table)) { |
710 | err = mlxsw_sp_acl_erp_region_table_trans(erp_table); |
711 | if (err) |
712 | return err; |
713 | } |
714 | |
715 | /* When C-TCAM or deltas are used, the eRP table must be used */ |
716 | if (erp_table->ops != &erp_multiple_masks_ops) |
717 | erp_table->ops = &erp_multiple_masks_ops; |
718 | |
719 | (*inc_num)++; |
720 | |
721 | return 0; |
722 | } |
723 | |
724 | static int mlxsw_sp_acl_erp_ctcam_inc(struct mlxsw_sp_acl_erp_table *erp_table) |
725 | { |
726 | return __mlxsw_sp_acl_erp_table_other_inc(erp_table, |
727 | inc_num: &erp_table->num_ctcam_erps); |
728 | } |
729 | |
730 | static int mlxsw_sp_acl_erp_delta_inc(struct mlxsw_sp_acl_erp_table *erp_table) |
731 | { |
732 | return __mlxsw_sp_acl_erp_table_other_inc(erp_table, |
733 | inc_num: &erp_table->num_deltas); |
734 | } |
735 | |
736 | static void |
737 | __mlxsw_sp_acl_erp_table_other_dec(struct mlxsw_sp_acl_erp_table *erp_table, |
738 | unsigned int *dec_num) |
739 | { |
740 | (*dec_num)--; |
741 | |
742 | /* If there are no C-TCAM eRP or deltas in use, the state we |
743 | * transition to depends on the number of A-TCAM eRPs currently |
744 | * in use. |
745 | */ |
746 | if (erp_table->num_ctcam_erps > 0 || erp_table->num_deltas > 0) |
747 | return; |
748 | |
749 | switch (erp_table->num_atcam_erps) { |
750 | case 2: |
751 | /* Keep using the eRP table, but correctly set the |
752 | * operations pointer so that when an A-TCAM eRP is |
753 | * deleted we will transition to use the master mask |
754 | */ |
755 | erp_table->ops = &erp_two_masks_ops; |
756 | break; |
757 | case 1: |
758 | /* We only kept the eRP table because we had C-TCAM |
759 | * eRPs in use. Now that the last C-TCAM eRP is gone we |
760 | * can stop using the table and transition to use the |
761 | * master mask |
762 | */ |
763 | mlxsw_sp_acl_erp_region_master_mask_trans(erp_table); |
764 | erp_table->ops = &erp_single_mask_ops; |
765 | break; |
766 | case 0: |
767 | /* There are no more eRPs of any kind used by the region |
768 | * so free its eRP table and transition to initial state |
769 | */ |
770 | mlxsw_sp_acl_erp_table_disable(erp_table); |
771 | mlxsw_sp_acl_erp_table_free(erp_core: erp_table->erp_core, |
772 | num_erps: erp_table->num_max_atcam_erps, |
773 | region_type: erp_table->aregion->type, |
774 | index: erp_table->base_index); |
775 | erp_table->ops = &erp_no_mask_ops; |
776 | break; |
777 | default: |
778 | break; |
779 | } |
780 | } |
781 | |
782 | static void mlxsw_sp_acl_erp_ctcam_dec(struct mlxsw_sp_acl_erp_table *erp_table) |
783 | { |
784 | __mlxsw_sp_acl_erp_table_other_dec(erp_table, |
785 | dec_num: &erp_table->num_ctcam_erps); |
786 | } |
787 | |
788 | static void mlxsw_sp_acl_erp_delta_dec(struct mlxsw_sp_acl_erp_table *erp_table) |
789 | { |
790 | __mlxsw_sp_acl_erp_table_other_dec(erp_table, |
791 | dec_num: &erp_table->num_deltas); |
792 | } |
793 | |
794 | static struct mlxsw_sp_acl_erp * |
795 | mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
796 | struct mlxsw_sp_acl_erp_key *key) |
797 | { |
798 | struct mlxsw_sp_acl_erp *erp; |
799 | int err; |
800 | |
801 | erp = kzalloc(size: sizeof(*erp), GFP_KERNEL); |
802 | if (!erp) |
803 | return ERR_PTR(error: -ENOMEM); |
804 | |
805 | memcpy(&erp->key, key, sizeof(*key)); |
806 | bitmap_from_arr32(bitmap: erp->mask_bitmap, buf: (u32 *) key->mask, |
807 | MLXSW_SP_ACL_TCAM_MASK_LEN); |
808 | |
809 | err = mlxsw_sp_acl_erp_ctcam_inc(erp_table); |
810 | if (err) |
811 | goto err_erp_ctcam_inc; |
812 | |
813 | erp->erp_table = erp_table; |
814 | |
815 | err = mlxsw_sp_acl_erp_master_mask_set(erp_table, key: &erp->key); |
816 | if (err) |
817 | goto err_master_mask_set; |
818 | |
819 | err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table); |
820 | if (err) |
821 | goto err_erp_region_ctcam_enable; |
822 | |
823 | return erp; |
824 | |
825 | err_erp_region_ctcam_enable: |
826 | mlxsw_sp_acl_erp_master_mask_clear(erp_table, key: &erp->key); |
827 | err_master_mask_set: |
828 | mlxsw_sp_acl_erp_ctcam_dec(erp_table); |
829 | err_erp_ctcam_inc: |
830 | kfree(objp: erp); |
831 | return ERR_PTR(error: err); |
832 | } |
833 | |
834 | static void |
835 | mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp) |
836 | { |
837 | struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table; |
838 | |
839 | mlxsw_sp_acl_erp_region_ctcam_disable(erp_table); |
840 | mlxsw_sp_acl_erp_master_mask_clear(erp_table, key: &erp->key); |
841 | mlxsw_sp_acl_erp_ctcam_dec(erp_table); |
842 | kfree(objp: erp); |
843 | } |
844 | |
845 | static struct mlxsw_sp_acl_erp * |
846 | mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
847 | struct mlxsw_sp_acl_erp_key *key) |
848 | { |
849 | struct mlxsw_sp_acl_erp *erp; |
850 | int err; |
851 | |
852 | if (key->ctcam) |
853 | return mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key); |
854 | |
855 | /* Expand the eRP table for the new eRP, if needed */ |
856 | err = mlxsw_sp_acl_erp_table_expand(erp_table); |
857 | if (err) |
858 | return ERR_PTR(error: err); |
859 | |
860 | erp = mlxsw_sp_acl_erp_generic_create(erp_table, key); |
861 | if (IS_ERR(ptr: erp)) |
862 | return erp; |
863 | |
864 | err = mlxsw_sp_acl_erp_index_get(erp_table, p_index: &erp->index); |
865 | if (err) |
866 | goto err_erp_index_get; |
867 | |
868 | err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp); |
869 | if (err) |
870 | goto err_table_erp_add; |
871 | |
872 | err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp); |
873 | if (err) |
874 | goto err_region_erp_add; |
875 | |
876 | erp_table->ops = &erp_multiple_masks_ops; |
877 | |
878 | return erp; |
879 | |
880 | err_region_erp_add: |
881 | mlxsw_sp_acl_erp_table_erp_del(erp); |
882 | err_table_erp_add: |
883 | mlxsw_sp_acl_erp_index_put(erp_table, index: erp->index); |
884 | err_erp_index_get: |
885 | mlxsw_sp_acl_erp_generic_destroy(erp); |
886 | return ERR_PTR(error: err); |
887 | } |
888 | |
889 | static void |
890 | mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
891 | struct mlxsw_sp_acl_erp *erp) |
892 | { |
893 | if (erp->key.ctcam) |
894 | return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp); |
895 | |
896 | mlxsw_sp_acl_erp_region_erp_del(erp); |
897 | mlxsw_sp_acl_erp_table_erp_del(erp); |
898 | mlxsw_sp_acl_erp_index_put(erp_table, index: erp->index); |
899 | mlxsw_sp_acl_erp_generic_destroy(erp); |
900 | |
901 | if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0 && |
902 | erp_table->num_deltas == 0) |
903 | erp_table->ops = &erp_two_masks_ops; |
904 | } |
905 | |
906 | static struct mlxsw_sp_acl_erp * |
907 | mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
908 | struct mlxsw_sp_acl_erp_key *key) |
909 | { |
910 | struct mlxsw_sp_acl_erp *erp; |
911 | int err; |
912 | |
913 | if (key->ctcam) |
914 | return mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key); |
915 | |
916 | /* Transition to use eRP table instead of master mask */ |
917 | err = mlxsw_sp_acl_erp_region_table_trans(erp_table); |
918 | if (err) |
919 | return ERR_PTR(error: err); |
920 | |
921 | erp = mlxsw_sp_acl_erp_generic_create(erp_table, key); |
922 | if (IS_ERR(ptr: erp)) { |
923 | err = PTR_ERR(ptr: erp); |
924 | goto err_erp_create; |
925 | } |
926 | |
927 | err = mlxsw_sp_acl_erp_index_get(erp_table, p_index: &erp->index); |
928 | if (err) |
929 | goto err_erp_index_get; |
930 | |
931 | err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp); |
932 | if (err) |
933 | goto err_table_erp_add; |
934 | |
935 | err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp); |
936 | if (err) |
937 | goto err_region_erp_add; |
938 | |
939 | erp_table->ops = &erp_two_masks_ops; |
940 | |
941 | return erp; |
942 | |
943 | err_region_erp_add: |
944 | mlxsw_sp_acl_erp_table_erp_del(erp); |
945 | err_table_erp_add: |
946 | mlxsw_sp_acl_erp_index_put(erp_table, index: erp->index); |
947 | err_erp_index_get: |
948 | mlxsw_sp_acl_erp_generic_destroy(erp); |
949 | err_erp_create: |
950 | mlxsw_sp_acl_erp_region_master_mask_trans(erp_table); |
951 | return ERR_PTR(error: err); |
952 | } |
953 | |
954 | static void |
955 | mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
956 | struct mlxsw_sp_acl_erp *erp) |
957 | { |
958 | if (erp->key.ctcam) |
959 | return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp); |
960 | |
961 | mlxsw_sp_acl_erp_region_erp_del(erp); |
962 | mlxsw_sp_acl_erp_table_erp_del(erp); |
963 | mlxsw_sp_acl_erp_index_put(erp_table, index: erp->index); |
964 | mlxsw_sp_acl_erp_generic_destroy(erp); |
965 | /* Transition to use master mask instead of eRP table */ |
966 | mlxsw_sp_acl_erp_region_master_mask_trans(erp_table); |
967 | |
968 | erp_table->ops = &erp_single_mask_ops; |
969 | } |
970 | |
971 | static struct mlxsw_sp_acl_erp * |
972 | mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table, |
973 | struct mlxsw_sp_acl_erp_key *key) |
974 | { |
975 | struct mlxsw_sp_acl_erp *erp; |
976 | |
977 | if (key->ctcam) |
978 | return ERR_PTR(error: -EINVAL); |
979 | |
980 | erp = mlxsw_sp_acl_erp_generic_create(erp_table, key); |
981 | if (IS_ERR(ptr: erp)) |
982 | return erp; |
983 | |
984 | erp_table->ops = &erp_single_mask_ops; |
985 | |
986 | return erp; |
987 | } |
988 | |
989 | static void |
990 | mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
991 | struct mlxsw_sp_acl_erp *erp) |
992 | { |
993 | mlxsw_sp_acl_erp_generic_destroy(erp); |
994 | erp_table->ops = &erp_no_mask_ops; |
995 | } |
996 | |
997 | static void |
998 | mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table, |
999 | struct mlxsw_sp_acl_erp *erp) |
1000 | { |
1001 | WARN_ON(1); |
1002 | } |
1003 | |
1004 | struct mlxsw_sp_acl_erp_mask * |
1005 | mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion, |
1006 | const char *mask, bool ctcam) |
1007 | { |
1008 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1009 | struct mlxsw_sp_acl_erp_key key; |
1010 | struct objagg_obj *objagg_obj; |
1011 | |
1012 | memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN); |
1013 | key.ctcam = ctcam; |
1014 | mutex_lock(&erp_table->objagg_lock); |
1015 | objagg_obj = objagg_obj_get(objagg: erp_table->objagg, obj: &key); |
1016 | mutex_unlock(lock: &erp_table->objagg_lock); |
1017 | if (IS_ERR(ptr: objagg_obj)) |
1018 | return ERR_CAST(ptr: objagg_obj); |
1019 | return (struct mlxsw_sp_acl_erp_mask *) objagg_obj; |
1020 | } |
1021 | |
1022 | void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion, |
1023 | struct mlxsw_sp_acl_erp_mask *erp_mask) |
1024 | { |
1025 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1026 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1027 | |
1028 | mutex_lock(&erp_table->objagg_lock); |
1029 | objagg_obj_put(objagg: erp_table->objagg, objagg_obj); |
1030 | mutex_unlock(lock: &erp_table->objagg_lock); |
1031 | } |
1032 | |
1033 | int mlxsw_sp_acl_erp_bf_insert(struct mlxsw_sp *mlxsw_sp, |
1034 | struct mlxsw_sp_acl_atcam_region *aregion, |
1035 | struct mlxsw_sp_acl_erp_mask *erp_mask, |
1036 | struct mlxsw_sp_acl_atcam_entry *aentry) |
1037 | { |
1038 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1039 | const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj); |
1040 | unsigned int erp_bank; |
1041 | |
1042 | if (!mlxsw_sp_acl_erp_table_is_used(erp_table: erp->erp_table)) |
1043 | return 0; |
1044 | |
1045 | erp_bank = mlxsw_sp_acl_erp_bank_get(erp); |
1046 | return mlxsw_sp_acl_bf_entry_add(mlxsw_sp, |
1047 | bf: erp->erp_table->erp_core->bf, |
1048 | aregion, erp_bank, aentry); |
1049 | } |
1050 | |
1051 | void mlxsw_sp_acl_erp_bf_remove(struct mlxsw_sp *mlxsw_sp, |
1052 | struct mlxsw_sp_acl_atcam_region *aregion, |
1053 | struct mlxsw_sp_acl_erp_mask *erp_mask, |
1054 | struct mlxsw_sp_acl_atcam_entry *aentry) |
1055 | { |
1056 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1057 | const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj); |
1058 | unsigned int erp_bank; |
1059 | |
1060 | if (!mlxsw_sp_acl_erp_table_is_used(erp_table: erp->erp_table)) |
1061 | return; |
1062 | |
1063 | erp_bank = mlxsw_sp_acl_erp_bank_get(erp); |
1064 | mlxsw_sp_acl_bf_entry_del(mlxsw_sp, |
1065 | bf: erp->erp_table->erp_core->bf, |
1066 | aregion, erp_bank, aentry); |
1067 | } |
1068 | |
1069 | bool |
1070 | mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask) |
1071 | { |
1072 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1073 | const struct mlxsw_sp_acl_erp_key *key = objagg_obj_raw(objagg_obj); |
1074 | |
1075 | return key->ctcam; |
1076 | } |
1077 | |
1078 | u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask) |
1079 | { |
1080 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1081 | const struct mlxsw_sp_acl_erp *erp = objagg_obj_root_priv(objagg_obj); |
1082 | |
1083 | return erp->id; |
1084 | } |
1085 | |
1086 | struct mlxsw_sp_acl_erp_delta { |
1087 | struct mlxsw_sp_acl_erp_key key; |
1088 | u16 start; |
1089 | u8 mask; |
1090 | }; |
1091 | |
1092 | u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta) |
1093 | { |
1094 | return delta->start; |
1095 | } |
1096 | |
1097 | u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta) |
1098 | { |
1099 | return delta->mask; |
1100 | } |
1101 | |
1102 | u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta, |
1103 | const char *enc_key) |
1104 | { |
1105 | u16 start = delta->start; |
1106 | u8 mask = delta->mask; |
1107 | u16 tmp; |
1108 | |
1109 | if (!mask) |
1110 | return 0; |
1111 | |
1112 | tmp = (unsigned char) enc_key[__MASK_IDX(start / 8)]; |
1113 | if (start / 8 + 1 < __MASK_LEN) |
1114 | tmp |= (unsigned char) enc_key[__MASK_IDX(start / 8 + 1)] << 8; |
1115 | tmp >>= start % 8; |
1116 | tmp &= mask; |
1117 | return tmp; |
1118 | } |
1119 | |
1120 | void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta, |
1121 | const char *enc_key) |
1122 | { |
1123 | u16 start = delta->start; |
1124 | u8 mask = delta->mask; |
1125 | unsigned char *byte; |
1126 | u16 tmp; |
1127 | |
1128 | tmp = mask; |
1129 | tmp <<= start % 8; |
1130 | tmp = ~tmp; |
1131 | |
1132 | byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8)]; |
1133 | *byte &= tmp & 0xff; |
1134 | if (start / 8 + 1 < __MASK_LEN) { |
1135 | byte = (unsigned char *) &enc_key[__MASK_IDX(start / 8 + 1)]; |
1136 | *byte &= (tmp >> 8) & 0xff; |
1137 | } |
1138 | } |
1139 | |
1140 | static const struct mlxsw_sp_acl_erp_delta |
1141 | mlxsw_sp_acl_erp_delta_default = {}; |
1142 | |
1143 | const struct mlxsw_sp_acl_erp_delta * |
1144 | mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask) |
1145 | { |
1146 | struct objagg_obj *objagg_obj = (struct objagg_obj *) erp_mask; |
1147 | const struct mlxsw_sp_acl_erp_delta *delta; |
1148 | |
1149 | delta = objagg_obj_delta_priv(objagg_obj); |
1150 | if (!delta) |
1151 | delta = &mlxsw_sp_acl_erp_delta_default; |
1152 | return delta; |
1153 | } |
1154 | |
1155 | static int |
1156 | mlxsw_sp_acl_erp_delta_fill(const struct mlxsw_sp_acl_erp_key *parent_key, |
1157 | const struct mlxsw_sp_acl_erp_key *key, |
1158 | u16 *delta_start, u8 *delta_mask) |
1159 | { |
1160 | int offset = 0; |
1161 | int si = -1; |
1162 | u16 pmask; |
1163 | u16 mask; |
1164 | int i; |
1165 | |
1166 | /* The difference between 2 masks can be up to 8 consecutive bits. */ |
1167 | for (i = 0; i < __MASK_LEN; i++) { |
1168 | if (parent_key->mask[__MASK_IDX(i)] == key->mask[__MASK_IDX(i)]) |
1169 | continue; |
1170 | if (si == -1) |
1171 | si = i; |
1172 | else if (si != i - 1) |
1173 | return -EINVAL; |
1174 | } |
1175 | if (si == -1) { |
1176 | /* The masks are the same, this can happen in case eRPs with |
1177 | * the same mask were created in both A-TCAM and C-TCAM. |
1178 | * The only possible condition under which this can happen |
1179 | * is identical rule insertion. Delta is not possible here. |
1180 | */ |
1181 | return -EINVAL; |
1182 | } |
1183 | pmask = (unsigned char) parent_key->mask[__MASK_IDX(si)]; |
1184 | mask = (unsigned char) key->mask[__MASK_IDX(si)]; |
1185 | if (si + 1 < __MASK_LEN) { |
1186 | pmask |= (unsigned char) parent_key->mask[__MASK_IDX(si + 1)] << 8; |
1187 | mask |= (unsigned char) key->mask[__MASK_IDX(si + 1)] << 8; |
1188 | } |
1189 | |
1190 | if ((pmask ^ mask) & pmask) |
1191 | return -EINVAL; |
1192 | mask &= ~pmask; |
1193 | while (!(mask & (1 << offset))) |
1194 | offset++; |
1195 | while (!(mask & 1)) |
1196 | mask >>= 1; |
1197 | if (mask & 0xff00) |
1198 | return -EINVAL; |
1199 | |
1200 | *delta_start = si * 8 + offset; |
1201 | *delta_mask = mask; |
1202 | |
1203 | return 0; |
1204 | } |
1205 | |
1206 | static bool mlxsw_sp_acl_erp_delta_check(void *priv, const void *parent_obj, |
1207 | const void *obj) |
1208 | { |
1209 | const struct mlxsw_sp_acl_erp_key *parent_key = parent_obj; |
1210 | const struct mlxsw_sp_acl_erp_key *key = obj; |
1211 | u16 delta_start; |
1212 | u8 delta_mask; |
1213 | int err; |
1214 | |
1215 | err = mlxsw_sp_acl_erp_delta_fill(parent_key, key, |
1216 | delta_start: &delta_start, delta_mask: &delta_mask); |
1217 | return err ? false : true; |
1218 | } |
1219 | |
1220 | static int mlxsw_sp_acl_erp_hints_obj_cmp(const void *obj1, const void *obj2) |
1221 | { |
1222 | const struct mlxsw_sp_acl_erp_key *key1 = obj1; |
1223 | const struct mlxsw_sp_acl_erp_key *key2 = obj2; |
1224 | |
1225 | /* For hints purposes, two objects are considered equal |
1226 | * in case the masks are the same. Does not matter what |
1227 | * the "ctcam" value is. |
1228 | */ |
1229 | return memcmp(p: key1->mask, q: key2->mask, size: sizeof(key1->mask)); |
1230 | } |
1231 | |
1232 | static void *mlxsw_sp_acl_erp_delta_create(void *priv, void *parent_obj, |
1233 | void *obj) |
1234 | { |
1235 | struct mlxsw_sp_acl_erp_key *parent_key = parent_obj; |
1236 | struct mlxsw_sp_acl_atcam_region *aregion = priv; |
1237 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1238 | struct mlxsw_sp_acl_erp_key *key = obj; |
1239 | struct mlxsw_sp_acl_erp_delta *delta; |
1240 | u16 delta_start; |
1241 | u8 delta_mask; |
1242 | int err; |
1243 | |
1244 | if (parent_key->ctcam || key->ctcam) |
1245 | return ERR_PTR(error: -EINVAL); |
1246 | err = mlxsw_sp_acl_erp_delta_fill(parent_key, key, |
1247 | delta_start: &delta_start, delta_mask: &delta_mask); |
1248 | if (err) |
1249 | return ERR_PTR(error: -EINVAL); |
1250 | |
1251 | delta = kzalloc(size: sizeof(*delta), GFP_KERNEL); |
1252 | if (!delta) |
1253 | return ERR_PTR(error: -ENOMEM); |
1254 | delta->start = delta_start; |
1255 | delta->mask = delta_mask; |
1256 | |
1257 | err = mlxsw_sp_acl_erp_delta_inc(erp_table); |
1258 | if (err) |
1259 | goto err_erp_delta_inc; |
1260 | |
1261 | memcpy(&delta->key, key, sizeof(*key)); |
1262 | err = mlxsw_sp_acl_erp_master_mask_set(erp_table, key: &delta->key); |
1263 | if (err) |
1264 | goto err_master_mask_set; |
1265 | |
1266 | return delta; |
1267 | |
1268 | err_master_mask_set: |
1269 | mlxsw_sp_acl_erp_delta_dec(erp_table); |
1270 | err_erp_delta_inc: |
1271 | kfree(objp: delta); |
1272 | return ERR_PTR(error: err); |
1273 | } |
1274 | |
1275 | static void mlxsw_sp_acl_erp_delta_destroy(void *priv, void *delta_priv) |
1276 | { |
1277 | struct mlxsw_sp_acl_erp_delta *delta = delta_priv; |
1278 | struct mlxsw_sp_acl_atcam_region *aregion = priv; |
1279 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1280 | |
1281 | mlxsw_sp_acl_erp_master_mask_clear(erp_table, key: &delta->key); |
1282 | mlxsw_sp_acl_erp_delta_dec(erp_table); |
1283 | kfree(objp: delta); |
1284 | } |
1285 | |
1286 | static void *mlxsw_sp_acl_erp_root_create(void *priv, void *obj, |
1287 | unsigned int root_id) |
1288 | { |
1289 | struct mlxsw_sp_acl_atcam_region *aregion = priv; |
1290 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1291 | struct mlxsw_sp_acl_erp_key *key = obj; |
1292 | |
1293 | if (!key->ctcam && |
1294 | root_id != OBJAGG_OBJ_ROOT_ID_INVALID && |
1295 | root_id >= MLXSW_SP_ACL_ERP_MAX_PER_REGION) |
1296 | return ERR_PTR(error: -ENOBUFS); |
1297 | return erp_table->ops->erp_create(erp_table, key); |
1298 | } |
1299 | |
1300 | static void mlxsw_sp_acl_erp_root_destroy(void *priv, void *root_priv) |
1301 | { |
1302 | struct mlxsw_sp_acl_atcam_region *aregion = priv; |
1303 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1304 | |
1305 | erp_table->ops->erp_destroy(erp_table, root_priv); |
1306 | } |
1307 | |
1308 | static const struct objagg_ops mlxsw_sp_acl_erp_objagg_ops = { |
1309 | .obj_size = sizeof(struct mlxsw_sp_acl_erp_key), |
1310 | .delta_check = mlxsw_sp_acl_erp_delta_check, |
1311 | .hints_obj_cmp = mlxsw_sp_acl_erp_hints_obj_cmp, |
1312 | .delta_create = mlxsw_sp_acl_erp_delta_create, |
1313 | .delta_destroy = mlxsw_sp_acl_erp_delta_destroy, |
1314 | .root_create = mlxsw_sp_acl_erp_root_create, |
1315 | .root_destroy = mlxsw_sp_acl_erp_root_destroy, |
1316 | }; |
1317 | |
1318 | static struct mlxsw_sp_acl_erp_table * |
1319 | mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion, |
1320 | struct objagg_hints *hints) |
1321 | { |
1322 | struct mlxsw_sp_acl_erp_table *erp_table; |
1323 | int err; |
1324 | |
1325 | erp_table = kzalloc(size: sizeof(*erp_table), GFP_KERNEL); |
1326 | if (!erp_table) |
1327 | return ERR_PTR(error: -ENOMEM); |
1328 | |
1329 | erp_table->objagg = objagg_create(ops: &mlxsw_sp_acl_erp_objagg_ops, |
1330 | hints, priv: aregion); |
1331 | if (IS_ERR(ptr: erp_table->objagg)) { |
1332 | err = PTR_ERR(ptr: erp_table->objagg); |
1333 | goto err_objagg_create; |
1334 | } |
1335 | |
1336 | erp_table->erp_core = aregion->atcam->erp_core; |
1337 | erp_table->ops = &erp_no_mask_ops; |
1338 | INIT_LIST_HEAD(list: &erp_table->atcam_erps_list); |
1339 | erp_table->aregion = aregion; |
1340 | mutex_init(&erp_table->objagg_lock); |
1341 | |
1342 | return erp_table; |
1343 | |
1344 | err_objagg_create: |
1345 | kfree(objp: erp_table); |
1346 | return ERR_PTR(error: err); |
1347 | } |
1348 | |
1349 | static void |
1350 | mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table *erp_table) |
1351 | { |
1352 | WARN_ON(!list_empty(&erp_table->atcam_erps_list)); |
1353 | mutex_destroy(lock: &erp_table->objagg_lock); |
1354 | objagg_destroy(objagg: erp_table->objagg); |
1355 | kfree(objp: erp_table); |
1356 | } |
1357 | |
1358 | static int |
1359 | mlxsw_sp_acl_erp_master_mask_init(struct mlxsw_sp_acl_atcam_region *aregion) |
1360 | { |
1361 | struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp; |
1362 | char percr_pl[MLXSW_REG_PERCR_LEN]; |
1363 | |
1364 | mlxsw_reg_percr_pack(payload: percr_pl, region_id: aregion->region->id); |
1365 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(percr), payload: percr_pl); |
1366 | } |
1367 | |
1368 | static int |
1369 | mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region *aregion) |
1370 | { |
1371 | struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp; |
1372 | char pererp_pl[MLXSW_REG_PERERP_LEN]; |
1373 | |
1374 | mlxsw_reg_pererp_pack(payload: pererp_pl, region_id: aregion->region->id, ctcam_le: false, erpt_pointer_valid: false, erpt_bank_pointer: 0, |
1375 | erpt_pointer: 0, master_rp_id: 0); |
1376 | return mlxsw_reg_write(mlxsw_core: mlxsw_sp->core, MLXSW_REG(pererp), payload: pererp_pl); |
1377 | } |
1378 | |
1379 | static int |
1380 | mlxsw_sp_acl_erp_hints_check(struct mlxsw_sp *mlxsw_sp, |
1381 | struct mlxsw_sp_acl_atcam_region *aregion, |
1382 | struct objagg_hints *hints, bool *p_rehash_needed) |
1383 | { |
1384 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1385 | const struct objagg_stats *ostats; |
1386 | const struct objagg_stats *hstats; |
1387 | int err; |
1388 | |
1389 | *p_rehash_needed = false; |
1390 | |
1391 | mutex_lock(&erp_table->objagg_lock); |
1392 | ostats = objagg_stats_get(objagg: erp_table->objagg); |
1393 | mutex_unlock(lock: &erp_table->objagg_lock); |
1394 | if (IS_ERR(ptr: ostats)) { |
1395 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP stats\n" ); |
1396 | return PTR_ERR(ptr: ostats); |
1397 | } |
1398 | |
1399 | hstats = objagg_hints_stats_get(objagg_hints: hints); |
1400 | if (IS_ERR(ptr: hstats)) { |
1401 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get ERP hints stats\n" ); |
1402 | err = PTR_ERR(ptr: hstats); |
1403 | goto err_hints_stats_get; |
1404 | } |
1405 | |
1406 | /* Very basic criterion for now. */ |
1407 | if (hstats->root_count < ostats->root_count) |
1408 | *p_rehash_needed = true; |
1409 | |
1410 | err = 0; |
1411 | |
1412 | objagg_stats_put(objagg_stats: hstats); |
1413 | err_hints_stats_get: |
1414 | objagg_stats_put(objagg_stats: ostats); |
1415 | return err; |
1416 | } |
1417 | |
1418 | void * |
1419 | mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion) |
1420 | { |
1421 | struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table; |
1422 | struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp; |
1423 | struct objagg_hints *hints; |
1424 | bool rehash_needed; |
1425 | int err; |
1426 | |
1427 | mutex_lock(&erp_table->objagg_lock); |
1428 | hints = objagg_hints_get(objagg: erp_table->objagg, |
1429 | opt_algo_type: OBJAGG_OPT_ALGO_SIMPLE_GREEDY); |
1430 | mutex_unlock(lock: &erp_table->objagg_lock); |
1431 | if (IS_ERR(ptr: hints)) { |
1432 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to create ERP hints\n" ); |
1433 | return ERR_CAST(ptr: hints); |
1434 | } |
1435 | err = mlxsw_sp_acl_erp_hints_check(mlxsw_sp, aregion, hints, |
1436 | p_rehash_needed: &rehash_needed); |
1437 | if (err) |
1438 | goto errout; |
1439 | |
1440 | if (!rehash_needed) { |
1441 | err = -EAGAIN; |
1442 | goto errout; |
1443 | } |
1444 | return hints; |
1445 | |
1446 | errout: |
1447 | objagg_hints_put(objagg_hints: hints); |
1448 | return ERR_PTR(error: err); |
1449 | } |
1450 | |
1451 | void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv) |
1452 | { |
1453 | struct objagg_hints *hints = hints_priv; |
1454 | |
1455 | objagg_hints_put(objagg_hints: hints); |
1456 | } |
1457 | |
1458 | int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion, |
1459 | void *hints_priv) |
1460 | { |
1461 | struct mlxsw_sp_acl_erp_table *erp_table; |
1462 | struct objagg_hints *hints = hints_priv; |
1463 | int err; |
1464 | |
1465 | erp_table = mlxsw_sp_acl_erp_table_create(aregion, hints); |
1466 | if (IS_ERR(ptr: erp_table)) |
1467 | return PTR_ERR(ptr: erp_table); |
1468 | aregion->erp_table = erp_table; |
1469 | |
1470 | /* Initialize the region's master mask to all zeroes */ |
1471 | err = mlxsw_sp_acl_erp_master_mask_init(aregion); |
1472 | if (err) |
1473 | goto err_erp_master_mask_init; |
1474 | |
1475 | /* Initialize the region to not use the eRP table */ |
1476 | err = mlxsw_sp_acl_erp_region_param_init(aregion); |
1477 | if (err) |
1478 | goto err_erp_region_param_init; |
1479 | |
1480 | return 0; |
1481 | |
1482 | err_erp_region_param_init: |
1483 | err_erp_master_mask_init: |
1484 | mlxsw_sp_acl_erp_table_destroy(erp_table); |
1485 | return err; |
1486 | } |
1487 | |
1488 | void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion) |
1489 | { |
1490 | mlxsw_sp_acl_erp_table_destroy(erp_table: aregion->erp_table); |
1491 | } |
1492 | |
1493 | static int |
1494 | mlxsw_sp_acl_erp_tables_sizes_query(struct mlxsw_sp *mlxsw_sp, |
1495 | struct mlxsw_sp_acl_erp_core *erp_core) |
1496 | { |
1497 | unsigned int size; |
1498 | |
1499 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB) || |
1500 | !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB) || |
1501 | !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB) || |
1502 | !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB)) |
1503 | return -EIO; |
1504 | |
1505 | size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB); |
1506 | erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_2KB] = size; |
1507 | |
1508 | size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB); |
1509 | erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_4KB] = size; |
1510 | |
1511 | size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB); |
1512 | erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB] = size; |
1513 | |
1514 | size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB); |
1515 | erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_12KB] = size; |
1516 | |
1517 | return 0; |
1518 | } |
1519 | |
1520 | static int mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp *mlxsw_sp, |
1521 | struct mlxsw_sp_acl_erp_core *erp_core) |
1522 | { |
1523 | unsigned int erpt_bank_size; |
1524 | int err; |
1525 | |
1526 | if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANK_SIZE) || |
1527 | !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANKS)) |
1528 | return -EIO; |
1529 | erpt_bank_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, |
1530 | ACL_MAX_ERPT_BANK_SIZE); |
1531 | erp_core->num_erp_banks = MLXSW_CORE_RES_GET(mlxsw_sp->core, |
1532 | ACL_MAX_ERPT_BANKS); |
1533 | |
1534 | erp_core->erp_tables = gen_pool_create(0, -1); |
1535 | if (!erp_core->erp_tables) |
1536 | return -ENOMEM; |
1537 | gen_pool_set_algo(pool: erp_core->erp_tables, algo: gen_pool_best_fit, NULL); |
1538 | |
1539 | err = gen_pool_add(pool: erp_core->erp_tables, |
1540 | MLXSW_SP_ACL_ERP_GENALLOC_OFFSET, size: erpt_bank_size, |
1541 | nid: -1); |
1542 | if (err) |
1543 | goto err_gen_pool_add; |
1544 | |
1545 | erp_core->bf = mlxsw_sp_acl_bf_init(mlxsw_sp, num_erp_banks: erp_core->num_erp_banks); |
1546 | if (IS_ERR(ptr: erp_core->bf)) { |
1547 | err = PTR_ERR(ptr: erp_core->bf); |
1548 | goto err_bf_init; |
1549 | } |
1550 | |
1551 | /* Different regions require masks of different sizes */ |
1552 | err = mlxsw_sp_acl_erp_tables_sizes_query(mlxsw_sp, erp_core); |
1553 | if (err) |
1554 | goto err_erp_tables_sizes_query; |
1555 | |
1556 | return 0; |
1557 | |
1558 | err_erp_tables_sizes_query: |
1559 | mlxsw_sp_acl_bf_fini(bf: erp_core->bf); |
1560 | err_bf_init: |
1561 | err_gen_pool_add: |
1562 | gen_pool_destroy(erp_core->erp_tables); |
1563 | return err; |
1564 | } |
1565 | |
1566 | static void mlxsw_sp_acl_erp_tables_fini(struct mlxsw_sp *mlxsw_sp, |
1567 | struct mlxsw_sp_acl_erp_core *erp_core) |
1568 | { |
1569 | mlxsw_sp_acl_bf_fini(bf: erp_core->bf); |
1570 | gen_pool_destroy(erp_core->erp_tables); |
1571 | } |
1572 | |
1573 | int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp, |
1574 | struct mlxsw_sp_acl_atcam *atcam) |
1575 | { |
1576 | struct mlxsw_sp_acl_erp_core *erp_core; |
1577 | int err; |
1578 | |
1579 | erp_core = kzalloc(size: sizeof(*erp_core), GFP_KERNEL); |
1580 | if (!erp_core) |
1581 | return -ENOMEM; |
1582 | erp_core->mlxsw_sp = mlxsw_sp; |
1583 | atcam->erp_core = erp_core; |
1584 | |
1585 | err = mlxsw_sp_acl_erp_tables_init(mlxsw_sp, erp_core); |
1586 | if (err) |
1587 | goto err_erp_tables_init; |
1588 | |
1589 | return 0; |
1590 | |
1591 | err_erp_tables_init: |
1592 | kfree(objp: erp_core); |
1593 | return err; |
1594 | } |
1595 | |
1596 | void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp, |
1597 | struct mlxsw_sp_acl_atcam *atcam) |
1598 | { |
1599 | mlxsw_sp_acl_erp_tables_fini(mlxsw_sp, erp_core: atcam->erp_core); |
1600 | kfree(objp: atcam->erp_core); |
1601 | } |
1602 | |