1  // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2  /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
3  
4  #include <linux/kernel.h>
5  
6  #include "spectrum.h"
7  #include "spectrum_acl_tcam.h"
8  #include "core_acl_flex_actions.h"
9  
10  struct mlxsw_sp2_acl_tcam {
11  	struct mlxsw_sp_acl_atcam atcam;
12  	u32 kvdl_index;
13  	unsigned int kvdl_count;
14  };
15  
16  struct mlxsw_sp2_acl_tcam_region {
17  	struct mlxsw_sp_acl_atcam_region aregion;
18  	struct mlxsw_sp_acl_tcam_region *region;
19  };
20  
21  struct mlxsw_sp2_acl_tcam_chunk {
22  	struct mlxsw_sp_acl_atcam_chunk achunk;
23  };
24  
25  struct mlxsw_sp2_acl_tcam_entry {
26  	struct mlxsw_sp_acl_atcam_entry aentry;
27  	struct mlxsw_afa_block *act_block;
28  };
29  
30  static int
mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region * cregion,struct mlxsw_sp_acl_ctcam_entry * centry,const char * mask)31  mlxsw_sp2_acl_ctcam_region_entry_insert(struct mlxsw_sp_acl_ctcam_region *cregion,
32  					struct mlxsw_sp_acl_ctcam_entry *centry,
33  					const char *mask)
34  {
35  	struct mlxsw_sp_acl_atcam_region *aregion;
36  	struct mlxsw_sp_acl_atcam_entry *aentry;
37  	struct mlxsw_sp_acl_erp_mask *erp_mask;
38  
39  	aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
40  	aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
41  
42  	erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, true);
43  	if (IS_ERR(erp_mask))
44  		return PTR_ERR(erp_mask);
45  	aentry->erp_mask = erp_mask;
46  
47  	return 0;
48  }
49  
50  static void
mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region * cregion,struct mlxsw_sp_acl_ctcam_entry * centry)51  mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregion,
52  					struct mlxsw_sp_acl_ctcam_entry *centry)
53  {
54  	struct mlxsw_sp_acl_atcam_region *aregion;
55  	struct mlxsw_sp_acl_atcam_entry *aentry;
56  
57  	aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
58  	aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
59  
60  	mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
61  }
62  
63  static const struct mlxsw_sp_acl_ctcam_region_ops
64  mlxsw_sp2_acl_ctcam_region_ops = {
65  	.entry_insert = mlxsw_sp2_acl_ctcam_region_entry_insert,
66  	.entry_remove = mlxsw_sp2_acl_ctcam_region_entry_remove,
67  };
68  
mlxsw_sp2_acl_tcam_init(struct mlxsw_sp * mlxsw_sp,void * priv,struct mlxsw_sp_acl_tcam * _tcam)69  static int mlxsw_sp2_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv,
70  				   struct mlxsw_sp_acl_tcam *_tcam)
71  {
72  	struct mlxsw_sp2_acl_tcam *tcam = priv;
73  	struct mlxsw_afa_block *afa_block;
74  	char pefa_pl[MLXSW_REG_PEFA_LEN];
75  	char pgcr_pl[MLXSW_REG_PGCR_LEN];
76  	char *enc_actions;
77  	int i;
78  	int err;
79  
80  	/* Some TCAM regions are not exposed to the host and used internally
81  	 * by the device. Allocate KVDL entries for the default actions of
82  	 * these regions to avoid the host from overwriting them.
83  	 */
84  	tcam->kvdl_count = _tcam->max_regions;
85  	if (MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_DEFAULT_ACTIONS))
86  		tcam->kvdl_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
87  						      ACL_MAX_DEFAULT_ACTIONS);
88  	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
89  				  tcam->kvdl_count, &tcam->kvdl_index);
90  	if (err)
91  		return err;
92  
93  	/* Create flex action block, set default action (continue)
94  	 * but don't commit. We need just the current set encoding
95  	 * to be written using PEFA register to all indexes for all regions.
96  	 */
97  	afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
98  	if (IS_ERR(afa_block)) {
99  		err = PTR_ERR(afa_block);
100  		goto err_afa_block;
101  	}
102  	err = mlxsw_afa_block_continue(afa_block);
103  	if (WARN_ON(err))
104  		goto err_afa_block_continue;
105  	enc_actions = mlxsw_afa_block_cur_set(afa_block);
106  
107  	/* Only write to KVDL entries used by TCAM regions exposed to the
108  	 * host.
109  	 */
110  	for (i = 0; i < _tcam->max_regions; i++) {
111  		mlxsw_reg_pefa_pack(pefa_pl, tcam->kvdl_index + i,
112  				    true, enc_actions);
113  		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl);
114  		if (err)
115  			goto err_pefa_write;
116  	}
117  	mlxsw_reg_pgcr_pack(pgcr_pl, tcam->kvdl_index);
118  	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pgcr), pgcr_pl);
119  	if (err)
120  		goto err_pgcr_write;
121  
122  	err = mlxsw_sp_acl_atcam_init(mlxsw_sp, &tcam->atcam);
123  	if (err)
124  		goto err_atcam_init;
125  
126  	mlxsw_afa_block_destroy(afa_block);
127  	return 0;
128  
129  err_atcam_init:
130  err_pgcr_write:
131  err_pefa_write:
132  err_afa_block_continue:
133  	mlxsw_afa_block_destroy(afa_block);
134  err_afa_block:
135  	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
136  			   tcam->kvdl_count, tcam->kvdl_index);
137  	return err;
138  }
139  
mlxsw_sp2_acl_tcam_fini(struct mlxsw_sp * mlxsw_sp,void * priv)140  static void mlxsw_sp2_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
141  {
142  	struct mlxsw_sp2_acl_tcam *tcam = priv;
143  
144  	mlxsw_sp_acl_atcam_fini(mlxsw_sp, &tcam->atcam);
145  	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
146  			   tcam->kvdl_count, tcam->kvdl_index);
147  }
148  
149  static int
mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp * mlxsw_sp,void * region_priv,void * tcam_priv,struct mlxsw_sp_acl_tcam_region * _region,void * hints_priv)150  mlxsw_sp2_acl_tcam_region_init(struct mlxsw_sp *mlxsw_sp, void *region_priv,
151  			       void *tcam_priv,
152  			       struct mlxsw_sp_acl_tcam_region *_region,
153  			       void *hints_priv)
154  {
155  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
156  	struct mlxsw_sp2_acl_tcam *tcam = tcam_priv;
157  
158  	region->region = _region;
159  
160  	return mlxsw_sp_acl_atcam_region_init(mlxsw_sp, &tcam->atcam,
161  					      &region->aregion,
162  					      _region, hints_priv,
163  					      &mlxsw_sp2_acl_ctcam_region_ops);
164  }
165  
166  static void
mlxsw_sp2_acl_tcam_region_fini(struct mlxsw_sp * mlxsw_sp,void * region_priv)167  mlxsw_sp2_acl_tcam_region_fini(struct mlxsw_sp *mlxsw_sp, void *region_priv)
168  {
169  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
170  
171  	mlxsw_sp_acl_atcam_region_fini(&region->aregion);
172  }
173  
174  static int
mlxsw_sp2_acl_tcam_region_associate(struct mlxsw_sp * mlxsw_sp,struct mlxsw_sp_acl_tcam_region * region)175  mlxsw_sp2_acl_tcam_region_associate(struct mlxsw_sp *mlxsw_sp,
176  				    struct mlxsw_sp_acl_tcam_region *region)
177  {
178  	return mlxsw_sp_acl_atcam_region_associate(mlxsw_sp, region->id);
179  }
180  
mlxsw_sp2_acl_tcam_region_rehash_hints_get(void * region_priv)181  static void *mlxsw_sp2_acl_tcam_region_rehash_hints_get(void *region_priv)
182  {
183  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
184  
185  	return mlxsw_sp_acl_atcam_rehash_hints_get(&region->aregion);
186  }
187  
mlxsw_sp2_acl_tcam_region_rehash_hints_put(void * hints_priv)188  static void mlxsw_sp2_acl_tcam_region_rehash_hints_put(void *hints_priv)
189  {
190  	mlxsw_sp_acl_atcam_rehash_hints_put(hints_priv);
191  }
192  
mlxsw_sp2_acl_tcam_chunk_init(void * region_priv,void * chunk_priv,unsigned int priority)193  static void mlxsw_sp2_acl_tcam_chunk_init(void *region_priv, void *chunk_priv,
194  					  unsigned int priority)
195  {
196  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
197  	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
198  
199  	mlxsw_sp_acl_atcam_chunk_init(&region->aregion, &chunk->achunk,
200  				      priority);
201  }
202  
mlxsw_sp2_acl_tcam_chunk_fini(void * chunk_priv)203  static void mlxsw_sp2_acl_tcam_chunk_fini(void *chunk_priv)
204  {
205  	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
206  
207  	mlxsw_sp_acl_atcam_chunk_fini(&chunk->achunk);
208  }
209  
mlxsw_sp2_acl_tcam_entry_add(struct mlxsw_sp * mlxsw_sp,void * region_priv,void * chunk_priv,void * entry_priv,struct mlxsw_sp_acl_rule_info * rulei)210  static int mlxsw_sp2_acl_tcam_entry_add(struct mlxsw_sp *mlxsw_sp,
211  					void *region_priv, void *chunk_priv,
212  					void *entry_priv,
213  					struct mlxsw_sp_acl_rule_info *rulei)
214  {
215  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
216  	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
217  	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
218  
219  	entry->act_block = rulei->act_block;
220  	return mlxsw_sp_acl_atcam_entry_add(mlxsw_sp, &region->aregion,
221  					    &chunk->achunk, &entry->aentry,
222  					    rulei);
223  }
224  
mlxsw_sp2_acl_tcam_entry_del(struct mlxsw_sp * mlxsw_sp,void * region_priv,void * chunk_priv,void * entry_priv)225  static void mlxsw_sp2_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
226  					 void *region_priv, void *chunk_priv,
227  					 void *entry_priv)
228  {
229  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
230  	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
231  	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
232  
233  	mlxsw_sp_acl_atcam_entry_del(mlxsw_sp, &region->aregion, &chunk->achunk,
234  				     &entry->aentry);
235  }
236  
237  static int
mlxsw_sp2_acl_tcam_entry_action_replace(struct mlxsw_sp * mlxsw_sp,void * region_priv,void * entry_priv,struct mlxsw_sp_acl_rule_info * rulei)238  mlxsw_sp2_acl_tcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
239  					void *region_priv, void *entry_priv,
240  					struct mlxsw_sp_acl_rule_info *rulei)
241  {
242  	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
243  	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
244  
245  	entry->act_block = rulei->act_block;
246  	return mlxsw_sp_acl_atcam_entry_action_replace(mlxsw_sp,
247  						       &region->aregion,
248  						       &entry->aentry, rulei);
249  }
250  
251  static int
mlxsw_sp2_acl_tcam_entry_activity_get(struct mlxsw_sp * mlxsw_sp,void * region_priv,void * entry_priv,bool * activity)252  mlxsw_sp2_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
253  				      void *region_priv, void *entry_priv,
254  				      bool *activity)
255  {
256  	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
257  
258  	return mlxsw_afa_block_activity_get(entry->act_block, activity);
259  }
260  
261  const struct mlxsw_sp_acl_tcam_ops mlxsw_sp2_acl_tcam_ops = {
262  	.key_type		= MLXSW_REG_PTAR_KEY_TYPE_FLEX2,
263  	.priv_size		= sizeof(struct mlxsw_sp2_acl_tcam),
264  	.init			= mlxsw_sp2_acl_tcam_init,
265  	.fini			= mlxsw_sp2_acl_tcam_fini,
266  	.region_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_region),
267  	.region_init		= mlxsw_sp2_acl_tcam_region_init,
268  	.region_fini		= mlxsw_sp2_acl_tcam_region_fini,
269  	.region_associate	= mlxsw_sp2_acl_tcam_region_associate,
270  	.region_rehash_hints_get = mlxsw_sp2_acl_tcam_region_rehash_hints_get,
271  	.region_rehash_hints_put = mlxsw_sp2_acl_tcam_region_rehash_hints_put,
272  	.chunk_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_chunk),
273  	.chunk_init		= mlxsw_sp2_acl_tcam_chunk_init,
274  	.chunk_fini		= mlxsw_sp2_acl_tcam_chunk_fini,
275  	.entry_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_entry),
276  	.entry_add		= mlxsw_sp2_acl_tcam_entry_add,
277  	.entry_del		= mlxsw_sp2_acl_tcam_entry_del,
278  	.entry_action_replace	= mlxsw_sp2_acl_tcam_entry_action_replace,
279  	.entry_activity_get	= mlxsw_sp2_acl_tcam_entry_activity_get,
280  };
281