281 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			281 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
 | |
| /* Copyright (c) 2018 Mellanox Technologies. All rights reserved */
 | |
| 
 | |
| #include <linux/kernel.h>
 | |
| 
 | |
| #include "spectrum.h"
 | |
| #include "spectrum_acl_tcam.h"
 | |
| #include "core_acl_flex_actions.h"
 | |
| 
 | |
| struct mlxsw_sp2_acl_tcam {
 | |
| 	struct mlxsw_sp_acl_atcam atcam;
 | |
| 	u32 kvdl_index;
 | |
| 	unsigned int kvdl_count;
 | |
| };
 | |
| 
 | |
| struct mlxsw_sp2_acl_tcam_region {
 | |
| 	struct mlxsw_sp_acl_atcam_region aregion;
 | |
| 	struct mlxsw_sp_acl_tcam_region *region;
 | |
| };
 | |
| 
 | |
| struct mlxsw_sp2_acl_tcam_chunk {
 | |
| 	struct mlxsw_sp_acl_atcam_chunk achunk;
 | |
| };
 | |
| 
 | |
| struct mlxsw_sp2_acl_tcam_entry {
 | |
| 	struct mlxsw_sp_acl_atcam_entry aentry;
 | |
| 	struct mlxsw_afa_block *act_block;
 | |
| };
 | |
| 
 | |
| 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)
 | |
| {
 | |
| 	struct mlxsw_sp_acl_atcam_region *aregion;
 | |
| 	struct mlxsw_sp_acl_atcam_entry *aentry;
 | |
| 	struct mlxsw_sp_acl_erp_mask *erp_mask;
 | |
| 
 | |
| 	aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
 | |
| 	aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 | |
| 
 | |
| 	erp_mask = mlxsw_sp_acl_erp_mask_get(aregion, mask, true);
 | |
| 	if (IS_ERR(erp_mask))
 | |
| 		return PTR_ERR(erp_mask);
 | |
| 	aentry->erp_mask = erp_mask;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void
 | |
| mlxsw_sp2_acl_ctcam_region_entry_remove(struct mlxsw_sp_acl_ctcam_region *cregion,
 | |
| 					struct mlxsw_sp_acl_ctcam_entry *centry)
 | |
| {
 | |
| 	struct mlxsw_sp_acl_atcam_region *aregion;
 | |
| 	struct mlxsw_sp_acl_atcam_entry *aentry;
 | |
| 
 | |
| 	aregion = mlxsw_sp_acl_tcam_cregion_aregion(cregion);
 | |
| 	aentry = mlxsw_sp_acl_tcam_centry_aentry(centry);
 | |
| 
 | |
| 	mlxsw_sp_acl_erp_mask_put(aregion, aentry->erp_mask);
 | |
| }
 | |
| 
 | |
| static const struct mlxsw_sp_acl_ctcam_region_ops
 | |
| mlxsw_sp2_acl_ctcam_region_ops = {
 | |
| 	.entry_insert = mlxsw_sp2_acl_ctcam_region_entry_insert,
 | |
| 	.entry_remove = mlxsw_sp2_acl_ctcam_region_entry_remove,
 | |
| };
 | |
| 
 | |
| static int mlxsw_sp2_acl_tcam_init(struct mlxsw_sp *mlxsw_sp, void *priv,
 | |
| 				   struct mlxsw_sp_acl_tcam *_tcam)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam *tcam = priv;
 | |
| 	struct mlxsw_afa_block *afa_block;
 | |
| 	char pefa_pl[MLXSW_REG_PEFA_LEN];
 | |
| 	char pgcr_pl[MLXSW_REG_PGCR_LEN];
 | |
| 	char *enc_actions;
 | |
| 	int i;
 | |
| 	int err;
 | |
| 
 | |
| 	/* Some TCAM regions are not exposed to the host and used internally
 | |
| 	 * by the device. Allocate KVDL entries for the default actions of
 | |
| 	 * these regions to avoid the host from overwriting them.
 | |
| 	 */
 | |
| 	tcam->kvdl_count = _tcam->max_regions;
 | |
| 	if (MLXSW_CORE_RES_VALID(mlxsw_sp->core, ACL_MAX_DEFAULT_ACTIONS))
 | |
| 		tcam->kvdl_count = MLXSW_CORE_RES_GET(mlxsw_sp->core,
 | |
| 						      ACL_MAX_DEFAULT_ACTIONS);
 | |
| 	err = mlxsw_sp_kvdl_alloc(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
 | |
| 				  tcam->kvdl_count, &tcam->kvdl_index);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	/* Create flex action block, set default action (continue)
 | |
| 	 * but don't commit. We need just the current set encoding
 | |
| 	 * to be written using PEFA register to all indexes for all regions.
 | |
| 	 */
 | |
| 	afa_block = mlxsw_afa_block_create(mlxsw_sp->afa);
 | |
| 	if (IS_ERR(afa_block)) {
 | |
| 		err = PTR_ERR(afa_block);
 | |
| 		goto err_afa_block;
 | |
| 	}
 | |
| 	err = mlxsw_afa_block_continue(afa_block);
 | |
| 	if (WARN_ON(err))
 | |
| 		goto err_afa_block_continue;
 | |
| 	enc_actions = mlxsw_afa_block_cur_set(afa_block);
 | |
| 
 | |
| 	/* Only write to KVDL entries used by TCAM regions exposed to the
 | |
| 	 * host.
 | |
| 	 */
 | |
| 	for (i = 0; i < _tcam->max_regions; i++) {
 | |
| 		mlxsw_reg_pefa_pack(pefa_pl, tcam->kvdl_index + i,
 | |
| 				    true, enc_actions);
 | |
| 		err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pefa), pefa_pl);
 | |
| 		if (err)
 | |
| 			goto err_pefa_write;
 | |
| 	}
 | |
| 	mlxsw_reg_pgcr_pack(pgcr_pl, tcam->kvdl_index);
 | |
| 	err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(pgcr), pgcr_pl);
 | |
| 	if (err)
 | |
| 		goto err_pgcr_write;
 | |
| 
 | |
| 	err = mlxsw_sp_acl_atcam_init(mlxsw_sp, &tcam->atcam);
 | |
| 	if (err)
 | |
| 		goto err_atcam_init;
 | |
| 
 | |
| 	mlxsw_afa_block_destroy(afa_block);
 | |
| 	return 0;
 | |
| 
 | |
| err_atcam_init:
 | |
| err_pgcr_write:
 | |
| err_pefa_write:
 | |
| err_afa_block_continue:
 | |
| 	mlxsw_afa_block_destroy(afa_block);
 | |
| err_afa_block:
 | |
| 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
 | |
| 			   tcam->kvdl_count, tcam->kvdl_index);
 | |
| 	return err;
 | |
| }
 | |
| 
 | |
| static void mlxsw_sp2_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp, void *priv)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam *tcam = priv;
 | |
| 
 | |
| 	mlxsw_sp_acl_atcam_fini(mlxsw_sp, &tcam->atcam);
 | |
| 	mlxsw_sp_kvdl_free(mlxsw_sp, MLXSW_SP_KVDL_ENTRY_TYPE_ACTSET,
 | |
| 			   tcam->kvdl_count, tcam->kvdl_index);
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam *tcam = tcam_priv;
 | |
| 
 | |
| 	region->region = _region;
 | |
| 
 | |
| 	return mlxsw_sp_acl_atcam_region_init(mlxsw_sp, &tcam->atcam,
 | |
| 					      ®ion->aregion,
 | |
| 					      _region, hints_priv,
 | |
| 					      &mlxsw_sp2_acl_ctcam_region_ops);
 | |
| }
 | |
| 
 | |
| static void
 | |
| mlxsw_sp2_acl_tcam_region_fini(struct mlxsw_sp *mlxsw_sp, void *region_priv)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 
 | |
| 	mlxsw_sp_acl_atcam_region_fini(®ion->aregion);
 | |
| }
 | |
| 
 | |
| static int
 | |
| mlxsw_sp2_acl_tcam_region_associate(struct mlxsw_sp *mlxsw_sp,
 | |
| 				    struct mlxsw_sp_acl_tcam_region *region)
 | |
| {
 | |
| 	return mlxsw_sp_acl_atcam_region_associate(mlxsw_sp, region->id);
 | |
| }
 | |
| 
 | |
| static void *mlxsw_sp2_acl_tcam_region_rehash_hints_get(void *region_priv)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 
 | |
| 	return mlxsw_sp_acl_atcam_rehash_hints_get(®ion->aregion);
 | |
| }
 | |
| 
 | |
| static void mlxsw_sp2_acl_tcam_region_rehash_hints_put(void *hints_priv)
 | |
| {
 | |
| 	mlxsw_sp_acl_atcam_rehash_hints_put(hints_priv);
 | |
| }
 | |
| 
 | |
| static void mlxsw_sp2_acl_tcam_chunk_init(void *region_priv, void *chunk_priv,
 | |
| 					  unsigned int priority)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
 | |
| 
 | |
| 	mlxsw_sp_acl_atcam_chunk_init(®ion->aregion, &chunk->achunk,
 | |
| 				      priority);
 | |
| }
 | |
| 
 | |
| static void mlxsw_sp2_acl_tcam_chunk_fini(void *chunk_priv)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
 | |
| 
 | |
| 	mlxsw_sp_acl_atcam_chunk_fini(&chunk->achunk);
 | |
| }
 | |
| 
 | |
| static int 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)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
 | |
| 
 | |
| 	entry->act_block = rulei->act_block;
 | |
| 	return mlxsw_sp_acl_atcam_entry_add(mlxsw_sp, ®ion->aregion,
 | |
| 					    &chunk->achunk, &entry->aentry,
 | |
| 					    rulei);
 | |
| }
 | |
| 
 | |
| static void mlxsw_sp2_acl_tcam_entry_del(struct mlxsw_sp *mlxsw_sp,
 | |
| 					 void *region_priv, void *chunk_priv,
 | |
| 					 void *entry_priv)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_chunk *chunk = chunk_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
 | |
| 
 | |
| 	mlxsw_sp_acl_atcam_entry_del(mlxsw_sp, ®ion->aregion, &chunk->achunk,
 | |
| 				     &entry->aentry);
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_region *region = region_priv;
 | |
| 	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
 | |
| 
 | |
| 	entry->act_block = rulei->act_block;
 | |
| 	return mlxsw_sp_acl_atcam_entry_action_replace(mlxsw_sp,
 | |
| 						       ®ion->aregion,
 | |
| 						       &entry->aentry, rulei);
 | |
| }
 | |
| 
 | |
| static int
 | |
| mlxsw_sp2_acl_tcam_entry_activity_get(struct mlxsw_sp *mlxsw_sp,
 | |
| 				      void *region_priv, void *entry_priv,
 | |
| 				      bool *activity)
 | |
| {
 | |
| 	struct mlxsw_sp2_acl_tcam_entry *entry = entry_priv;
 | |
| 
 | |
| 	return mlxsw_afa_block_activity_get(entry->act_block, activity);
 | |
| }
 | |
| 
 | |
| const struct mlxsw_sp_acl_tcam_ops mlxsw_sp2_acl_tcam_ops = {
 | |
| 	.key_type		= MLXSW_REG_PTAR_KEY_TYPE_FLEX2,
 | |
| 	.priv_size		= sizeof(struct mlxsw_sp2_acl_tcam),
 | |
| 	.init			= mlxsw_sp2_acl_tcam_init,
 | |
| 	.fini			= mlxsw_sp2_acl_tcam_fini,
 | |
| 	.region_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_region),
 | |
| 	.region_init		= mlxsw_sp2_acl_tcam_region_init,
 | |
| 	.region_fini		= mlxsw_sp2_acl_tcam_region_fini,
 | |
| 	.region_associate	= mlxsw_sp2_acl_tcam_region_associate,
 | |
| 	.region_rehash_hints_get = mlxsw_sp2_acl_tcam_region_rehash_hints_get,
 | |
| 	.region_rehash_hints_put = mlxsw_sp2_acl_tcam_region_rehash_hints_put,
 | |
| 	.chunk_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_chunk),
 | |
| 	.chunk_init		= mlxsw_sp2_acl_tcam_chunk_init,
 | |
| 	.chunk_fini		= mlxsw_sp2_acl_tcam_chunk_fini,
 | |
| 	.entry_priv_size	= sizeof(struct mlxsw_sp2_acl_tcam_entry),
 | |
| 	.entry_add		= mlxsw_sp2_acl_tcam_entry_add,
 | |
| 	.entry_del		= mlxsw_sp2_acl_tcam_entry_del,
 | |
| 	.entry_action_replace	= mlxsw_sp2_acl_tcam_entry_action_replace,
 | |
| 	.entry_activity_get	= mlxsw_sp2_acl_tcam_entry_activity_get,
 | |
| };
 |