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/rhashtable.h>
11 #include <linux/rtnetlink.h>
12 #include <linux/slab.h>
13
14 #include "core.h"
15 #include "reg.h"
16 #include "spectrum.h"
17 #include "spectrum_acl_tcam.h"
18
19 /* gen_pool_alloc() returns 0 when allocation fails, so use an offset */
20 #define MLXSW_SP_ACL_ERP_GENALLOC_OFFSET 0x100
21 #define MLXSW_SP_ACL_ERP_MAX_PER_REGION 16
22
23 struct mlxsw_sp_acl_erp_core {
24 unsigned int erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX + 1];
25 struct gen_pool *erp_tables;
26 struct mlxsw_sp *mlxsw_sp;
27 unsigned int num_erp_banks;
28 };
29
30 struct mlxsw_sp_acl_erp_key {
31 char mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN];
32 bool ctcam;
33 };
34
35 struct mlxsw_sp_acl_erp {
36 struct mlxsw_sp_acl_erp_key key;
37 u8 id;
38 u8 index;
39 refcount_t refcnt;
40 DECLARE_BITMAP(mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
41 struct list_head list;
42 struct rhash_head ht_node;
43 struct mlxsw_sp_acl_erp_table *erp_table;
44 };
45
46 struct mlxsw_sp_acl_erp_master_mask {
47 DECLARE_BITMAP(bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN);
48 unsigned int count[MLXSW_SP_ACL_TCAM_MASK_LEN];
49 };
50
51 struct mlxsw_sp_acl_erp_table {
52 struct mlxsw_sp_acl_erp_master_mask master_mask;
53 DECLARE_BITMAP(erp_id_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
54 DECLARE_BITMAP(erp_index_bitmap, MLXSW_SP_ACL_ERP_MAX_PER_REGION);
55 struct list_head atcam_erps_list;
56 struct rhashtable erp_ht;
57 struct mlxsw_sp_acl_erp_core *erp_core;
58 struct mlxsw_sp_acl_atcam_region *aregion;
59 const struct mlxsw_sp_acl_erp_table_ops *ops;
60 unsigned long base_index;
61 unsigned int num_atcam_erps;
62 unsigned int num_max_atcam_erps;
63 unsigned int num_ctcam_erps;
64 };
65
66 static const struct rhashtable_params mlxsw_sp_acl_erp_ht_params = {
67 .key_len = sizeof(struct mlxsw_sp_acl_erp_key),
68 .key_offset = offsetof(struct mlxsw_sp_acl_erp, key),
69 .head_offset = offsetof(struct mlxsw_sp_acl_erp, ht_node),
70 };
71
72 struct mlxsw_sp_acl_erp_table_ops {
73 struct mlxsw_sp_acl_erp *
74 (*erp_create)(struct mlxsw_sp_acl_erp_table *erp_table,
75 struct mlxsw_sp_acl_erp_key *key);
76 void (*erp_destroy)(struct mlxsw_sp_acl_erp_table *erp_table,
77 struct mlxsw_sp_acl_erp *erp);
78 };
79
80 static struct mlxsw_sp_acl_erp *
81 mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
82 struct mlxsw_sp_acl_erp_key *key);
83 static void
84 mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
85 struct mlxsw_sp_acl_erp *erp);
86 static struct mlxsw_sp_acl_erp *
87 mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
88 struct mlxsw_sp_acl_erp_key *key);
89 static void
90 mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
91 struct mlxsw_sp_acl_erp *erp);
92 static struct mlxsw_sp_acl_erp *
93 mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
94 struct mlxsw_sp_acl_erp_key *key);
95 static void
96 mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
97 struct mlxsw_sp_acl_erp *erp);
98 static void
99 mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
100 struct mlxsw_sp_acl_erp *erp);
101
102 static const struct mlxsw_sp_acl_erp_table_ops erp_multiple_masks_ops = {
103 .erp_create = mlxsw_sp_acl_erp_mask_create,
104 .erp_destroy = mlxsw_sp_acl_erp_mask_destroy,
105 };
106
107 static const struct mlxsw_sp_acl_erp_table_ops erp_two_masks_ops = {
108 .erp_create = mlxsw_sp_acl_erp_mask_create,
109 .erp_destroy = mlxsw_sp_acl_erp_second_mask_destroy,
110 };
111
112 static const struct mlxsw_sp_acl_erp_table_ops erp_single_mask_ops = {
113 .erp_create = mlxsw_sp_acl_erp_second_mask_create,
114 .erp_destroy = mlxsw_sp_acl_erp_first_mask_destroy,
115 };
116
117 static const struct mlxsw_sp_acl_erp_table_ops erp_no_mask_ops = {
118 .erp_create = mlxsw_sp_acl_erp_first_mask_create,
119 .erp_destroy = mlxsw_sp_acl_erp_no_mask_destroy,
120 };
121
mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp * erp)122 bool mlxsw_sp_acl_erp_is_ctcam_erp(const struct mlxsw_sp_acl_erp *erp)
123 {
124 return erp->key.ctcam;
125 }
126
mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp * erp)127 u8 mlxsw_sp_acl_erp_id(const struct mlxsw_sp_acl_erp *erp)
128 {
129 return erp->id;
130 }
131
132 static unsigned int
mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table * erp_table)133 mlxsw_sp_acl_erp_table_entry_size(const struct mlxsw_sp_acl_erp_table *erp_table)
134 {
135 struct mlxsw_sp_acl_atcam_region *aregion = erp_table->aregion;
136 struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
137
138 return erp_core->erpt_entries_size[aregion->type];
139 }
140
mlxsw_sp_acl_erp_id_get(struct mlxsw_sp_acl_erp_table * erp_table,u8 * p_id)141 static int mlxsw_sp_acl_erp_id_get(struct mlxsw_sp_acl_erp_table *erp_table,
142 u8 *p_id)
143 {
144 u8 id;
145
146 id = find_first_zero_bit(erp_table->erp_id_bitmap,
147 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
148 if (id < MLXSW_SP_ACL_ERP_MAX_PER_REGION) {
149 __set_bit(id, erp_table->erp_id_bitmap);
150 *p_id = id;
151 return 0;
152 }
153
154 return -ENOBUFS;
155 }
156
mlxsw_sp_acl_erp_id_put(struct mlxsw_sp_acl_erp_table * erp_table,u8 id)157 static void mlxsw_sp_acl_erp_id_put(struct mlxsw_sp_acl_erp_table *erp_table,
158 u8 id)
159 {
160 __clear_bit(id, erp_table->erp_id_bitmap);
161 }
162
163 static void
mlxsw_sp_acl_erp_master_mask_bit_set(unsigned long bit,struct mlxsw_sp_acl_erp_master_mask * mask)164 mlxsw_sp_acl_erp_master_mask_bit_set(unsigned long bit,
165 struct mlxsw_sp_acl_erp_master_mask *mask)
166 {
167 if (mask->count[bit]++ == 0)
168 __set_bit(bit, mask->bitmap);
169 }
170
171 static void
mlxsw_sp_acl_erp_master_mask_bit_clear(unsigned long bit,struct mlxsw_sp_acl_erp_master_mask * mask)172 mlxsw_sp_acl_erp_master_mask_bit_clear(unsigned long bit,
173 struct mlxsw_sp_acl_erp_master_mask *mask)
174 {
175 if (--mask->count[bit] == 0)
176 __clear_bit(bit, mask->bitmap);
177 }
178
179 static int
mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table * erp_table)180 mlxsw_sp_acl_erp_master_mask_update(struct mlxsw_sp_acl_erp_table *erp_table)
181 {
182 struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
183 struct mlxsw_sp *mlxsw_sp = region->mlxsw_sp;
184 char percr_pl[MLXSW_REG_PERCR_LEN];
185 char *master_mask;
186
187 mlxsw_reg_percr_pack(percr_pl, region->id);
188 master_mask = mlxsw_reg_percr_master_mask_data(percr_pl);
189 bitmap_to_arr32((u32 *) master_mask, erp_table->master_mask.bitmap,
190 MLXSW_SP_ACL_TCAM_MASK_LEN);
191
192 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(percr), percr_pl);
193 }
194
195 static int
mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table * erp_table,const struct mlxsw_sp_acl_erp * erp)196 mlxsw_sp_acl_erp_master_mask_set(struct mlxsw_sp_acl_erp_table *erp_table,
197 const struct mlxsw_sp_acl_erp *erp)
198 {
199 unsigned long bit;
200 int err;
201
202 for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
203 mlxsw_sp_acl_erp_master_mask_bit_set(bit,
204 &erp_table->master_mask);
205
206 err = mlxsw_sp_acl_erp_master_mask_update(erp_table);
207 if (err)
208 goto err_master_mask_update;
209
210 return 0;
211
212 err_master_mask_update:
213 for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
214 mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
215 &erp_table->master_mask);
216 return err;
217 }
218
219 static int
mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table * erp_table,const struct mlxsw_sp_acl_erp * erp)220 mlxsw_sp_acl_erp_master_mask_clear(struct mlxsw_sp_acl_erp_table *erp_table,
221 const struct mlxsw_sp_acl_erp *erp)
222 {
223 unsigned long bit;
224 int err;
225
226 for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
227 mlxsw_sp_acl_erp_master_mask_bit_clear(bit,
228 &erp_table->master_mask);
229
230 err = mlxsw_sp_acl_erp_master_mask_update(erp_table);
231 if (err)
232 goto err_master_mask_update;
233
234 return 0;
235
236 err_master_mask_update:
237 for_each_set_bit(bit, erp->mask_bitmap, MLXSW_SP_ACL_TCAM_MASK_LEN)
238 mlxsw_sp_acl_erp_master_mask_bit_set(bit,
239 &erp_table->master_mask);
240 return err;
241 }
242
243 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)244 mlxsw_sp_acl_erp_generic_create(struct mlxsw_sp_acl_erp_table *erp_table,
245 struct mlxsw_sp_acl_erp_key *key)
246 {
247 struct mlxsw_sp_acl_erp *erp;
248 int err;
249
250 erp = kzalloc(sizeof(*erp), GFP_KERNEL);
251 if (!erp)
252 return ERR_PTR(-ENOMEM);
253
254 err = mlxsw_sp_acl_erp_id_get(erp_table, &erp->id);
255 if (err)
256 goto err_erp_id_get;
257
258 memcpy(&erp->key, key, sizeof(*key));
259 bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
260 MLXSW_SP_ACL_TCAM_MASK_LEN);
261 list_add(&erp->list, &erp_table->atcam_erps_list);
262 refcount_set(&erp->refcnt, 1);
263 erp_table->num_atcam_erps++;
264 erp->erp_table = erp_table;
265
266 err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
267 if (err)
268 goto err_master_mask_set;
269
270 err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
271 mlxsw_sp_acl_erp_ht_params);
272 if (err)
273 goto err_rhashtable_insert;
274
275 return erp;
276
277 err_rhashtable_insert:
278 mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
279 err_master_mask_set:
280 erp_table->num_atcam_erps--;
281 list_del(&erp->list);
282 mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
283 err_erp_id_get:
284 kfree(erp);
285 return ERR_PTR(err);
286 }
287
288 static void
mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp * erp)289 mlxsw_sp_acl_erp_generic_destroy(struct mlxsw_sp_acl_erp *erp)
290 {
291 struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
292
293 rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
294 mlxsw_sp_acl_erp_ht_params);
295 mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
296 erp_table->num_atcam_erps--;
297 list_del(&erp->list);
298 mlxsw_sp_acl_erp_id_put(erp_table, erp->id);
299 kfree(erp);
300 }
301
302 static int
mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core * erp_core,unsigned int num_erps,enum mlxsw_sp_acl_atcam_region_type region_type,unsigned long * p_index)303 mlxsw_sp_acl_erp_table_alloc(struct mlxsw_sp_acl_erp_core *erp_core,
304 unsigned int num_erps,
305 enum mlxsw_sp_acl_atcam_region_type region_type,
306 unsigned long *p_index)
307 {
308 unsigned int num_rows, entry_size;
309
310 /* We only allow allocations of entire rows */
311 if (num_erps % erp_core->num_erp_banks != 0)
312 return -EINVAL;
313
314 entry_size = erp_core->erpt_entries_size[region_type];
315 num_rows = num_erps / erp_core->num_erp_banks;
316
317 *p_index = gen_pool_alloc(erp_core->erp_tables, num_rows * entry_size);
318 if (*p_index == 0)
319 return -ENOBUFS;
320 *p_index -= MLXSW_SP_ACL_ERP_GENALLOC_OFFSET;
321
322 return 0;
323 }
324
325 static void
mlxsw_sp_acl_erp_table_free(struct mlxsw_sp_acl_erp_core * erp_core,unsigned int num_erps,enum mlxsw_sp_acl_atcam_region_type region_type,unsigned long index)326 mlxsw_sp_acl_erp_table_free(struct mlxsw_sp_acl_erp_core *erp_core,
327 unsigned int num_erps,
328 enum mlxsw_sp_acl_atcam_region_type region_type,
329 unsigned long index)
330 {
331 unsigned long base_index;
332 unsigned int entry_size;
333 size_t size;
334
335 entry_size = erp_core->erpt_entries_size[region_type];
336 base_index = index + MLXSW_SP_ACL_ERP_GENALLOC_OFFSET;
337 size = num_erps / erp_core->num_erp_banks * entry_size;
338 gen_pool_free(erp_core->erp_tables, base_index, size);
339 }
340
341 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_table_master_rp(struct mlxsw_sp_acl_erp_table * erp_table)342 mlxsw_sp_acl_erp_table_master_rp(struct mlxsw_sp_acl_erp_table *erp_table)
343 {
344 if (!list_is_singular(&erp_table->atcam_erps_list))
345 return NULL;
346
347 return list_first_entry(&erp_table->atcam_erps_list,
348 struct mlxsw_sp_acl_erp, list);
349 }
350
mlxsw_sp_acl_erp_index_get(struct mlxsw_sp_acl_erp_table * erp_table,u8 * p_index)351 static int mlxsw_sp_acl_erp_index_get(struct mlxsw_sp_acl_erp_table *erp_table,
352 u8 *p_index)
353 {
354 u8 index;
355
356 index = find_first_zero_bit(erp_table->erp_index_bitmap,
357 erp_table->num_max_atcam_erps);
358 if (index < erp_table->num_max_atcam_erps) {
359 __set_bit(index, erp_table->erp_index_bitmap);
360 *p_index = index;
361 return 0;
362 }
363
364 return -ENOBUFS;
365 }
366
mlxsw_sp_acl_erp_index_put(struct mlxsw_sp_acl_erp_table * erp_table,u8 index)367 static void mlxsw_sp_acl_erp_index_put(struct mlxsw_sp_acl_erp_table *erp_table,
368 u8 index)
369 {
370 __clear_bit(index, erp_table->erp_index_bitmap);
371 }
372
373 static void
mlxsw_sp_acl_erp_table_locate(const struct mlxsw_sp_acl_erp_table * erp_table,const struct mlxsw_sp_acl_erp * erp,u8 * p_erpt_bank,u8 * p_erpt_index)374 mlxsw_sp_acl_erp_table_locate(const struct mlxsw_sp_acl_erp_table *erp_table,
375 const struct mlxsw_sp_acl_erp *erp,
376 u8 *p_erpt_bank, u8 *p_erpt_index)
377 {
378 unsigned int entry_size = mlxsw_sp_acl_erp_table_entry_size(erp_table);
379 struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
380 unsigned int row;
381
382 *p_erpt_bank = erp->index % erp_core->num_erp_banks;
383 row = erp->index / erp_core->num_erp_banks;
384 *p_erpt_index = erp_table->base_index + row * entry_size;
385 }
386
387 static int
mlxsw_sp_acl_erp_table_erp_add(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)388 mlxsw_sp_acl_erp_table_erp_add(struct mlxsw_sp_acl_erp_table *erp_table,
389 struct mlxsw_sp_acl_erp *erp)
390 {
391 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
392 enum mlxsw_reg_perpt_key_size key_size;
393 char perpt_pl[MLXSW_REG_PERPT_LEN];
394 u8 erpt_bank, erpt_index;
395
396 mlxsw_sp_acl_erp_table_locate(erp_table, erp, &erpt_bank, &erpt_index);
397 key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type;
398 mlxsw_reg_perpt_pack(perpt_pl, erpt_bank, erpt_index, key_size, erp->id,
399 0, erp_table->base_index, erp->index,
400 erp->key.mask);
401 mlxsw_reg_perpt_erp_vector_pack(perpt_pl, erp_table->erp_index_bitmap,
402 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
403 mlxsw_reg_perpt_erp_vector_set(perpt_pl, erp->index, true);
404 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(perpt), perpt_pl);
405 }
406
mlxsw_sp_acl_erp_table_erp_del(struct mlxsw_sp_acl_erp * erp)407 static void mlxsw_sp_acl_erp_table_erp_del(struct mlxsw_sp_acl_erp *erp)
408 {
409 char empty_mask[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN] = { 0 };
410 struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
411 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
412 enum mlxsw_reg_perpt_key_size key_size;
413 char perpt_pl[MLXSW_REG_PERPT_LEN];
414 u8 erpt_bank, erpt_index;
415
416 mlxsw_sp_acl_erp_table_locate(erp_table, erp, &erpt_bank, &erpt_index);
417 key_size = (enum mlxsw_reg_perpt_key_size) erp_table->aregion->type;
418 mlxsw_reg_perpt_pack(perpt_pl, erpt_bank, erpt_index, key_size, erp->id,
419 0, erp_table->base_index, erp->index, empty_mask);
420 mlxsw_reg_perpt_erp_vector_pack(perpt_pl, erp_table->erp_index_bitmap,
421 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
422 mlxsw_reg_perpt_erp_vector_set(perpt_pl, erp->index, false);
423 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(perpt), perpt_pl);
424 }
425
426 static int
mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table * erp_table,bool ctcam_le)427 mlxsw_sp_acl_erp_table_enable(struct mlxsw_sp_acl_erp_table *erp_table,
428 bool ctcam_le)
429 {
430 struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
431 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
432 char pererp_pl[MLXSW_REG_PERERP_LEN];
433
434 mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
435 erp_table->base_index, 0);
436 mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
437 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
438
439 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
440 }
441
442 static void
mlxsw_sp_acl_erp_table_disable(struct mlxsw_sp_acl_erp_table * erp_table)443 mlxsw_sp_acl_erp_table_disable(struct mlxsw_sp_acl_erp_table *erp_table)
444 {
445 struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
446 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
447 char pererp_pl[MLXSW_REG_PERERP_LEN];
448 struct mlxsw_sp_acl_erp *master_rp;
449
450 master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
451 /* It is possible we do not have a master RP when we disable the
452 * table when there are no rules in the A-TCAM and the last C-TCAM
453 * rule is deleted
454 */
455 mlxsw_reg_pererp_pack(pererp_pl, region->id, false, false, 0, 0,
456 master_rp ? master_rp->id : 0);
457 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
458 }
459
460 static int
mlxsw_sp_acl_erp_table_relocate(struct mlxsw_sp_acl_erp_table * erp_table)461 mlxsw_sp_acl_erp_table_relocate(struct mlxsw_sp_acl_erp_table *erp_table)
462 {
463 struct mlxsw_sp_acl_erp *erp;
464 int err;
465
466 list_for_each_entry(erp, &erp_table->atcam_erps_list, list) {
467 err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
468 if (err)
469 goto err_table_erp_add;
470 }
471
472 return 0;
473
474 err_table_erp_add:
475 list_for_each_entry_continue_reverse(erp, &erp_table->atcam_erps_list,
476 list)
477 mlxsw_sp_acl_erp_table_erp_del(erp);
478 return err;
479 }
480
481 static int
mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table * erp_table)482 mlxsw_sp_acl_erp_table_expand(struct mlxsw_sp_acl_erp_table *erp_table)
483 {
484 unsigned int num_erps, old_num_erps = erp_table->num_max_atcam_erps;
485 struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
486 unsigned long old_base_index = erp_table->base_index;
487 bool ctcam_le = erp_table->num_ctcam_erps > 0;
488 int err;
489
490 if (erp_table->num_atcam_erps < erp_table->num_max_atcam_erps)
491 return 0;
492
493 if (erp_table->num_max_atcam_erps == MLXSW_SP_ACL_ERP_MAX_PER_REGION)
494 return -ENOBUFS;
495
496 num_erps = old_num_erps + erp_core->num_erp_banks;
497 err = mlxsw_sp_acl_erp_table_alloc(erp_core, num_erps,
498 erp_table->aregion->type,
499 &erp_table->base_index);
500 if (err)
501 return err;
502 erp_table->num_max_atcam_erps = num_erps;
503
504 err = mlxsw_sp_acl_erp_table_relocate(erp_table);
505 if (err)
506 goto err_table_relocate;
507
508 err = mlxsw_sp_acl_erp_table_enable(erp_table, ctcam_le);
509 if (err)
510 goto err_table_enable;
511
512 mlxsw_sp_acl_erp_table_free(erp_core, old_num_erps,
513 erp_table->aregion->type, old_base_index);
514
515 return 0;
516
517 err_table_enable:
518 err_table_relocate:
519 erp_table->num_max_atcam_erps = old_num_erps;
520 mlxsw_sp_acl_erp_table_free(erp_core, num_erps,
521 erp_table->aregion->type,
522 erp_table->base_index);
523 erp_table->base_index = old_base_index;
524 return err;
525 }
526
527 static int
mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table * erp_table)528 mlxsw_sp_acl_erp_region_table_trans(struct mlxsw_sp_acl_erp_table *erp_table)
529 {
530 struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
531 struct mlxsw_sp_acl_erp *master_rp;
532 int err;
533
534 /* Initially, allocate a single eRP row. Expand later as needed */
535 err = mlxsw_sp_acl_erp_table_alloc(erp_core, erp_core->num_erp_banks,
536 erp_table->aregion->type,
537 &erp_table->base_index);
538 if (err)
539 return err;
540 erp_table->num_max_atcam_erps = erp_core->num_erp_banks;
541
542 /* Transition the sole RP currently configured (the master RP)
543 * to the eRP table
544 */
545 master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
546 if (!master_rp) {
547 err = -EINVAL;
548 goto err_table_master_rp;
549 }
550
551 /* Maintain the same eRP bank for the master RP, so that we
552 * wouldn't need to update the bloom filter
553 */
554 master_rp->index = master_rp->index % erp_core->num_erp_banks;
555 __set_bit(master_rp->index, erp_table->erp_index_bitmap);
556
557 err = mlxsw_sp_acl_erp_table_erp_add(erp_table, master_rp);
558 if (err)
559 goto err_table_master_rp_add;
560
561 err = mlxsw_sp_acl_erp_table_enable(erp_table, false);
562 if (err)
563 goto err_table_enable;
564
565 return 0;
566
567 err_table_enable:
568 mlxsw_sp_acl_erp_table_erp_del(master_rp);
569 err_table_master_rp_add:
570 __clear_bit(master_rp->index, erp_table->erp_index_bitmap);
571 err_table_master_rp:
572 mlxsw_sp_acl_erp_table_free(erp_core, erp_table->num_max_atcam_erps,
573 erp_table->aregion->type,
574 erp_table->base_index);
575 return err;
576 }
577
578 static void
mlxsw_sp_acl_erp_region_master_mask_trans(struct mlxsw_sp_acl_erp_table * erp_table)579 mlxsw_sp_acl_erp_region_master_mask_trans(struct mlxsw_sp_acl_erp_table *erp_table)
580 {
581 struct mlxsw_sp_acl_erp_core *erp_core = erp_table->erp_core;
582 struct mlxsw_sp_acl_erp *master_rp;
583
584 mlxsw_sp_acl_erp_table_disable(erp_table);
585 master_rp = mlxsw_sp_acl_erp_table_master_rp(erp_table);
586 if (!master_rp)
587 return;
588 mlxsw_sp_acl_erp_table_erp_del(master_rp);
589 __clear_bit(master_rp->index, erp_table->erp_index_bitmap);
590 mlxsw_sp_acl_erp_table_free(erp_core, erp_table->num_max_atcam_erps,
591 erp_table->aregion->type,
592 erp_table->base_index);
593 }
594
595 static int
mlxsw_sp_acl_erp_region_erp_add(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)596 mlxsw_sp_acl_erp_region_erp_add(struct mlxsw_sp_acl_erp_table *erp_table,
597 struct mlxsw_sp_acl_erp *erp)
598 {
599 struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
600 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
601 bool ctcam_le = erp_table->num_ctcam_erps > 0;
602 char pererp_pl[MLXSW_REG_PERERP_LEN];
603
604 mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
605 erp_table->base_index, 0);
606 mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
607 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
608 mlxsw_reg_pererp_erpt_vector_set(pererp_pl, erp->index, true);
609
610 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
611 }
612
mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp * erp)613 static void mlxsw_sp_acl_erp_region_erp_del(struct mlxsw_sp_acl_erp *erp)
614 {
615 struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
616 struct mlxsw_sp_acl_tcam_region *region = erp_table->aregion->region;
617 struct mlxsw_sp *mlxsw_sp = erp_table->erp_core->mlxsw_sp;
618 bool ctcam_le = erp_table->num_ctcam_erps > 0;
619 char pererp_pl[MLXSW_REG_PERERP_LEN];
620
621 mlxsw_reg_pererp_pack(pererp_pl, region->id, ctcam_le, true, 0,
622 erp_table->base_index, 0);
623 mlxsw_reg_pererp_erp_vector_pack(pererp_pl, erp_table->erp_index_bitmap,
624 MLXSW_SP_ACL_ERP_MAX_PER_REGION);
625 mlxsw_reg_pererp_erpt_vector_set(pererp_pl, erp->index, false);
626
627 mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
628 }
629
630 static int
mlxsw_sp_acl_erp_region_ctcam_enable(struct mlxsw_sp_acl_erp_table * erp_table)631 mlxsw_sp_acl_erp_region_ctcam_enable(struct mlxsw_sp_acl_erp_table *erp_table)
632 {
633 /* No need to re-enable lookup in the C-TCAM */
634 if (erp_table->num_ctcam_erps > 1)
635 return 0;
636
637 return mlxsw_sp_acl_erp_table_enable(erp_table, true);
638 }
639
640 static void
mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table * erp_table)641 mlxsw_sp_acl_erp_region_ctcam_disable(struct mlxsw_sp_acl_erp_table *erp_table)
642 {
643 /* Only disable C-TCAM lookup when last C-TCAM eRP is deleted */
644 if (erp_table->num_ctcam_erps > 1)
645 return;
646
647 mlxsw_sp_acl_erp_table_enable(erp_table, false);
648 }
649
650 static void
mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table * erp_table)651 mlxsw_sp_acl_erp_ctcam_table_ops_set(struct mlxsw_sp_acl_erp_table *erp_table)
652 {
653 switch (erp_table->num_atcam_erps) {
654 case 2:
655 /* Keep using the eRP table, but correctly set the
656 * operations pointer so that when an A-TCAM eRP is
657 * deleted we will transition to use the master mask
658 */
659 erp_table->ops = &erp_two_masks_ops;
660 break;
661 case 1:
662 /* We only kept the eRP table because we had C-TCAM
663 * eRPs in use. Now that the last C-TCAM eRP is gone we
664 * can stop using the table and transition to use the
665 * master mask
666 */
667 mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
668 erp_table->ops = &erp_single_mask_ops;
669 break;
670 case 0:
671 /* There are no more eRPs of any kind used by the region
672 * so free its eRP table and transition to initial state
673 */
674 mlxsw_sp_acl_erp_table_disable(erp_table);
675 mlxsw_sp_acl_erp_table_free(erp_table->erp_core,
676 erp_table->num_max_atcam_erps,
677 erp_table->aregion->type,
678 erp_table->base_index);
679 erp_table->ops = &erp_no_mask_ops;
680 break;
681 default:
682 break;
683 }
684 }
685
686 static struct mlxsw_sp_acl_erp *
__mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)687 __mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
688 struct mlxsw_sp_acl_erp_key *key)
689 {
690 struct mlxsw_sp_acl_erp *erp;
691 int err;
692
693 erp = kzalloc(sizeof(*erp), GFP_KERNEL);
694 if (!erp)
695 return ERR_PTR(-ENOMEM);
696
697 memcpy(&erp->key, key, sizeof(*key));
698 bitmap_from_arr32(erp->mask_bitmap, (u32 *) key->mask,
699 MLXSW_SP_ACL_TCAM_MASK_LEN);
700 refcount_set(&erp->refcnt, 1);
701 erp_table->num_ctcam_erps++;
702 erp->erp_table = erp_table;
703
704 err = mlxsw_sp_acl_erp_master_mask_set(erp_table, erp);
705 if (err)
706 goto err_master_mask_set;
707
708 err = rhashtable_insert_fast(&erp_table->erp_ht, &erp->ht_node,
709 mlxsw_sp_acl_erp_ht_params);
710 if (err)
711 goto err_rhashtable_insert;
712
713 err = mlxsw_sp_acl_erp_region_ctcam_enable(erp_table);
714 if (err)
715 goto err_erp_region_ctcam_enable;
716
717 /* When C-TCAM is used, the eRP table must be used */
718 erp_table->ops = &erp_multiple_masks_ops;
719
720 return erp;
721
722 err_erp_region_ctcam_enable:
723 rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
724 mlxsw_sp_acl_erp_ht_params);
725 err_rhashtable_insert:
726 mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
727 err_master_mask_set:
728 erp_table->num_ctcam_erps--;
729 kfree(erp);
730 return ERR_PTR(err);
731 }
732
733 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)734 mlxsw_sp_acl_erp_ctcam_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
735 struct mlxsw_sp_acl_erp_key *key)
736 {
737 struct mlxsw_sp_acl_erp *erp;
738 int err;
739
740 /* There is a special situation where we need to spill rules
741 * into the C-TCAM, yet the region is still using a master
742 * mask and thus not performing a lookup in the C-TCAM. This
743 * can happen when two rules that only differ in priority - and
744 * thus sharing the same key - are programmed. In this case
745 * we transition the region to use an eRP table
746 */
747 err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
748 if (err)
749 return ERR_PTR(err);
750
751 erp = __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
752 if (IS_ERR(erp)) {
753 err = PTR_ERR(erp);
754 goto err_erp_create;
755 }
756
757 return erp;
758
759 err_erp_create:
760 mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
761 return ERR_PTR(err);
762 }
763
764 static void
mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp * erp)765 mlxsw_sp_acl_erp_ctcam_mask_destroy(struct mlxsw_sp_acl_erp *erp)
766 {
767 struct mlxsw_sp_acl_erp_table *erp_table = erp->erp_table;
768
769 mlxsw_sp_acl_erp_region_ctcam_disable(erp_table);
770 rhashtable_remove_fast(&erp_table->erp_ht, &erp->ht_node,
771 mlxsw_sp_acl_erp_ht_params);
772 mlxsw_sp_acl_erp_master_mask_clear(erp_table, erp);
773 erp_table->num_ctcam_erps--;
774 kfree(erp);
775
776 /* Once the last C-TCAM eRP was destroyed, the state we
777 * transition to depends on the number of A-TCAM eRPs currently
778 * in use
779 */
780 if (erp_table->num_ctcam_erps > 0)
781 return;
782 mlxsw_sp_acl_erp_ctcam_table_ops_set(erp_table);
783 }
784
785 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)786 mlxsw_sp_acl_erp_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
787 struct mlxsw_sp_acl_erp_key *key)
788 {
789 struct mlxsw_sp_acl_erp *erp;
790 int err;
791
792 if (key->ctcam)
793 return __mlxsw_sp_acl_erp_ctcam_mask_create(erp_table, key);
794
795 /* Expand the eRP table for the new eRP, if needed */
796 err = mlxsw_sp_acl_erp_table_expand(erp_table);
797 if (err)
798 return ERR_PTR(err);
799
800 erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
801 if (IS_ERR(erp))
802 return erp;
803
804 err = mlxsw_sp_acl_erp_index_get(erp_table, &erp->index);
805 if (err)
806 goto err_erp_index_get;
807
808 err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
809 if (err)
810 goto err_table_erp_add;
811
812 err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp);
813 if (err)
814 goto err_region_erp_add;
815
816 erp_table->ops = &erp_multiple_masks_ops;
817
818 return erp;
819
820 err_region_erp_add:
821 mlxsw_sp_acl_erp_table_erp_del(erp);
822 err_table_erp_add:
823 mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
824 err_erp_index_get:
825 mlxsw_sp_acl_erp_generic_destroy(erp);
826 return ERR_PTR(err);
827 }
828
829 static void
mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)830 mlxsw_sp_acl_erp_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
831 struct mlxsw_sp_acl_erp *erp)
832 {
833 if (erp->key.ctcam)
834 return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp);
835
836 mlxsw_sp_acl_erp_region_erp_del(erp);
837 mlxsw_sp_acl_erp_table_erp_del(erp);
838 mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
839 mlxsw_sp_acl_erp_generic_destroy(erp);
840
841 if (erp_table->num_atcam_erps == 2 && erp_table->num_ctcam_erps == 0)
842 erp_table->ops = &erp_two_masks_ops;
843 }
844
845 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_second_mask_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)846 mlxsw_sp_acl_erp_second_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 /* Transition to use eRP table instead of master mask */
856 err = mlxsw_sp_acl_erp_region_table_trans(erp_table);
857 if (err)
858 return ERR_PTR(err);
859
860 erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
861 if (IS_ERR(erp)) {
862 err = PTR_ERR(erp);
863 goto err_erp_create;
864 }
865
866 err = mlxsw_sp_acl_erp_index_get(erp_table, &erp->index);
867 if (err)
868 goto err_erp_index_get;
869
870 err = mlxsw_sp_acl_erp_table_erp_add(erp_table, erp);
871 if (err)
872 goto err_table_erp_add;
873
874 err = mlxsw_sp_acl_erp_region_erp_add(erp_table, erp);
875 if (err)
876 goto err_region_erp_add;
877
878 erp_table->ops = &erp_two_masks_ops;
879
880 return erp;
881
882 err_region_erp_add:
883 mlxsw_sp_acl_erp_table_erp_del(erp);
884 err_table_erp_add:
885 mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
886 err_erp_index_get:
887 mlxsw_sp_acl_erp_generic_destroy(erp);
888 err_erp_create:
889 mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
890 return ERR_PTR(err);
891 }
892
893 static void
mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)894 mlxsw_sp_acl_erp_second_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
895 struct mlxsw_sp_acl_erp *erp)
896 {
897 if (erp->key.ctcam)
898 return mlxsw_sp_acl_erp_ctcam_mask_destroy(erp);
899
900 mlxsw_sp_acl_erp_region_erp_del(erp);
901 mlxsw_sp_acl_erp_table_erp_del(erp);
902 mlxsw_sp_acl_erp_index_put(erp_table, erp->index);
903 mlxsw_sp_acl_erp_generic_destroy(erp);
904 /* Transition to use master mask instead of eRP table */
905 mlxsw_sp_acl_erp_region_master_mask_trans(erp_table);
906
907 erp_table->ops = &erp_single_mask_ops;
908 }
909
910 static struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp_key * key)911 mlxsw_sp_acl_erp_first_mask_create(struct mlxsw_sp_acl_erp_table *erp_table,
912 struct mlxsw_sp_acl_erp_key *key)
913 {
914 struct mlxsw_sp_acl_erp *erp;
915
916 if (key->ctcam)
917 return ERR_PTR(-EINVAL);
918
919 erp = mlxsw_sp_acl_erp_generic_create(erp_table, key);
920 if (IS_ERR(erp))
921 return erp;
922
923 erp_table->ops = &erp_single_mask_ops;
924
925 return erp;
926 }
927
928 static void
mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)929 mlxsw_sp_acl_erp_first_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
930 struct mlxsw_sp_acl_erp *erp)
931 {
932 mlxsw_sp_acl_erp_generic_destroy(erp);
933 erp_table->ops = &erp_no_mask_ops;
934 }
935
936 static void
mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table * erp_table,struct mlxsw_sp_acl_erp * erp)937 mlxsw_sp_acl_erp_no_mask_destroy(struct mlxsw_sp_acl_erp_table *erp_table,
938 struct mlxsw_sp_acl_erp *erp)
939 {
940 WARN_ON(1);
941 }
942
943 struct mlxsw_sp_acl_erp *
mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region * aregion,const char * mask,bool ctcam)944 mlxsw_sp_acl_erp_get(struct mlxsw_sp_acl_atcam_region *aregion,
945 const char *mask, bool ctcam)
946 {
947 struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
948 struct mlxsw_sp_acl_erp_key key;
949 struct mlxsw_sp_acl_erp *erp;
950
951 /* eRPs are allocated from a shared resource, but currently all
952 * allocations are done under RTNL.
953 */
954 ASSERT_RTNL();
955
956 memcpy(key.mask, mask, MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN);
957 key.ctcam = ctcam;
958 erp = rhashtable_lookup_fast(&erp_table->erp_ht, &key,
959 mlxsw_sp_acl_erp_ht_params);
960 if (erp) {
961 refcount_inc(&erp->refcnt);
962 return erp;
963 }
964
965 return erp_table->ops->erp_create(erp_table, &key);
966 }
967
mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region * aregion,struct mlxsw_sp_acl_erp * erp)968 void mlxsw_sp_acl_erp_put(struct mlxsw_sp_acl_atcam_region *aregion,
969 struct mlxsw_sp_acl_erp *erp)
970 {
971 struct mlxsw_sp_acl_erp_table *erp_table = aregion->erp_table;
972
973 ASSERT_RTNL();
974
975 if (!refcount_dec_and_test(&erp->refcnt))
976 return;
977
978 erp_table->ops->erp_destroy(erp_table, erp);
979 }
980
981 static struct mlxsw_sp_acl_erp_table *
mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region * aregion)982 mlxsw_sp_acl_erp_table_create(struct mlxsw_sp_acl_atcam_region *aregion)
983 {
984 struct mlxsw_sp_acl_erp_table *erp_table;
985 int err;
986
987 erp_table = kzalloc(sizeof(*erp_table), GFP_KERNEL);
988 if (!erp_table)
989 return ERR_PTR(-ENOMEM);
990
991 err = rhashtable_init(&erp_table->erp_ht, &mlxsw_sp_acl_erp_ht_params);
992 if (err)
993 goto err_rhashtable_init;
994
995 erp_table->erp_core = aregion->atcam->erp_core;
996 erp_table->ops = &erp_no_mask_ops;
997 INIT_LIST_HEAD(&erp_table->atcam_erps_list);
998 erp_table->aregion = aregion;
999
1000 return erp_table;
1001
1002 err_rhashtable_init:
1003 kfree(erp_table);
1004 return ERR_PTR(err);
1005 }
1006
1007 static void
mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table * erp_table)1008 mlxsw_sp_acl_erp_table_destroy(struct mlxsw_sp_acl_erp_table *erp_table)
1009 {
1010 WARN_ON(!list_empty(&erp_table->atcam_erps_list));
1011 rhashtable_destroy(&erp_table->erp_ht);
1012 kfree(erp_table);
1013 }
1014
1015 static int
mlxsw_sp_acl_erp_master_mask_init(struct mlxsw_sp_acl_atcam_region * aregion)1016 mlxsw_sp_acl_erp_master_mask_init(struct mlxsw_sp_acl_atcam_region *aregion)
1017 {
1018 struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
1019 char percr_pl[MLXSW_REG_PERCR_LEN];
1020
1021 mlxsw_reg_percr_pack(percr_pl, aregion->region->id);
1022 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(percr), percr_pl);
1023 }
1024
1025 static int
mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region * aregion)1026 mlxsw_sp_acl_erp_region_param_init(struct mlxsw_sp_acl_atcam_region *aregion)
1027 {
1028 struct mlxsw_sp *mlxsw_sp = aregion->region->mlxsw_sp;
1029 char pererp_pl[MLXSW_REG_PERERP_LEN];
1030
1031 mlxsw_reg_pererp_pack(pererp_pl, aregion->region->id, false, false, 0,
1032 0, 0);
1033 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pererp), pererp_pl);
1034 }
1035
mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region * aregion)1036 int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion)
1037 {
1038 struct mlxsw_sp_acl_erp_table *erp_table;
1039 int err;
1040
1041 erp_table = mlxsw_sp_acl_erp_table_create(aregion);
1042 if (IS_ERR(erp_table))
1043 return PTR_ERR(erp_table);
1044 aregion->erp_table = erp_table;
1045
1046 /* Initialize the region's master mask to all zeroes */
1047 err = mlxsw_sp_acl_erp_master_mask_init(aregion);
1048 if (err)
1049 goto err_erp_master_mask_init;
1050
1051 /* Initialize the region to not use the eRP table */
1052 err = mlxsw_sp_acl_erp_region_param_init(aregion);
1053 if (err)
1054 goto err_erp_region_param_init;
1055
1056 return 0;
1057
1058 err_erp_region_param_init:
1059 err_erp_master_mask_init:
1060 mlxsw_sp_acl_erp_table_destroy(erp_table);
1061 return err;
1062 }
1063
mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region * aregion)1064 void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion)
1065 {
1066 mlxsw_sp_acl_erp_table_destroy(aregion->erp_table);
1067 }
1068
1069 static int
mlxsw_sp_acl_erp_tables_sizes_query(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_erp_core * erp_core)1070 mlxsw_sp_acl_erp_tables_sizes_query(struct mlxsw_sp *mlxsw_sp,
1071 struct mlxsw_sp_acl_erp_core *erp_core)
1072 {
1073 unsigned int size;
1074
1075 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB) ||
1076 !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB) ||
1077 !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB) ||
1078 !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB))
1079 return -EIO;
1080
1081 size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_2KB);
1082 erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_2KB] = size;
1083
1084 size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_4KB);
1085 erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_4KB] = size;
1086
1087 size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_8KB);
1088 erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB] = size;
1089
1090 size = MLXSW_CORE_RES_GET(mlxsw_sp->core, ACL_ERPT_ENTRIES_12KB);
1091 erp_core->erpt_entries_size[MLXSW_SP_ACL_ATCAM_REGION_TYPE_12KB] = size;
1092
1093 return 0;
1094 }
1095
mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_erp_core * erp_core)1096 static int mlxsw_sp_acl_erp_tables_init(struct mlxsw_sp *mlxsw_sp,
1097 struct mlxsw_sp_acl_erp_core *erp_core)
1098 {
1099 unsigned int erpt_bank_size;
1100 int err;
1101
1102 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANK_SIZE) ||
1103 !MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_ERPT_BANKS))
1104 return -EIO;
1105 erpt_bank_size = MLXSW_CORE_RES_GET(mlxsw_sp->core,
1106 ACL_MAX_ERPT_BANK_SIZE);
1107 erp_core->num_erp_banks = MLXSW_CORE_RES_GET(mlxsw_sp->core,
1108 ACL_MAX_ERPT_BANKS);
1109
1110 erp_core->erp_tables = gen_pool_create(0, -1);
1111 if (!erp_core->erp_tables)
1112 return -ENOMEM;
1113 gen_pool_set_algo(erp_core->erp_tables, gen_pool_best_fit, NULL);
1114
1115 err = gen_pool_add(erp_core->erp_tables,
1116 MLXSW_SP_ACL_ERP_GENALLOC_OFFSET, erpt_bank_size,
1117 -1);
1118 if (err)
1119 goto err_gen_pool_add;
1120
1121 /* Different regions require masks of different sizes */
1122 err = mlxsw_sp_acl_erp_tables_sizes_query(mlxsw_sp, erp_core);
1123 if (err)
1124 goto err_erp_tables_sizes_query;
1125
1126 return 0;
1127
1128 err_erp_tables_sizes_query:
1129 err_gen_pool_add:
1130 gen_pool_destroy(erp_core->erp_tables);
1131 return err;
1132 }
1133
mlxsw_sp_acl_erp_tables_fini(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_erp_core * erp_core)1134 static void mlxsw_sp_acl_erp_tables_fini(struct mlxsw_sp *mlxsw_sp,
1135 struct mlxsw_sp_acl_erp_core *erp_core)
1136 {
1137 gen_pool_destroy(erp_core->erp_tables);
1138 }
1139
mlxsw_sp_acl_erps_init(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_atcam * atcam)1140 int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
1141 struct mlxsw_sp_acl_atcam *atcam)
1142 {
1143 struct mlxsw_sp_acl_erp_core *erp_core;
1144 int err;
1145
1146 erp_core = kzalloc(sizeof(*erp_core), GFP_KERNEL);
1147 if (!erp_core)
1148 return -ENOMEM;
1149 erp_core->mlxsw_sp = mlxsw_sp;
1150 atcam->erp_core = erp_core;
1151
1152 err = mlxsw_sp_acl_erp_tables_init(mlxsw_sp, erp_core);
1153 if (err)
1154 goto err_erp_tables_init;
1155
1156 return 0;
1157
1158 err_erp_tables_init:
1159 kfree(erp_core);
1160 return err;
1161 }
1162
mlxsw_sp_acl_erps_fini(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_atcam * atcam)1163 void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
1164 struct mlxsw_sp_acl_atcam *atcam)
1165 {
1166 mlxsw_sp_acl_erp_tables_fini(mlxsw_sp, atcam->erp_core);
1167 kfree(atcam->erp_core);
1168 }
1169