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

source code of linux/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_erp.c