root/drivers/net/ethernet/mellanox/mlxsw/spectrum_acl_tcam.h
/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */

#ifndef _MLXSW_SPECTRUM_ACL_TCAM_H
#define _MLXSW_SPECTRUM_ACL_TCAM_H

#include <linux/list.h>
#include <linux/parman.h>
#include <linux/idr.h>

#include "reg.h"
#include "spectrum.h"
#include "core_acl_flex_keys.h"

struct mlxsw_sp_acl_tcam {
        struct ida used_regions;
        unsigned int max_regions;
        struct ida used_groups;
        unsigned int max_groups;
        unsigned int max_group_size;
        struct mutex lock; /* guards vregion list */
        struct list_head vregion_list;
        u32 vregion_rehash_intrvl;   /* ms */
        unsigned long priv[];
        /* priv has to be always the last item */
};

size_t mlxsw_sp_acl_tcam_priv_size(struct mlxsw_sp *mlxsw_sp);
int mlxsw_sp_acl_tcam_init(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_tcam *tcam);
void mlxsw_sp_acl_tcam_fini(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_tcam *tcam);
int mlxsw_sp_acl_tcam_priority_get(struct mlxsw_sp *mlxsw_sp,
                                   struct mlxsw_sp_acl_rule_info *rulei,
                                   u32 *priority, bool fillup_priority);

struct mlxsw_sp_acl_profile_ops {
        size_t ruleset_priv_size;
        int (*ruleset_add)(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_tcam *tcam, void *ruleset_priv,
                           struct mlxsw_afk_element_usage *tmplt_elusage,
                           unsigned int *p_min_prio, unsigned int *p_max_prio);
        void (*ruleset_del)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv);
        int (*ruleset_bind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,
                            struct mlxsw_sp_port *mlxsw_sp_port,
                            bool ingress);
        void (*ruleset_unbind)(struct mlxsw_sp *mlxsw_sp, void *ruleset_priv,
                               struct mlxsw_sp_port *mlxsw_sp_port,
                               bool ingress);
        u16 (*ruleset_group_id)(void *ruleset_priv);
        size_t rule_priv_size;
        int (*rule_add)(struct mlxsw_sp *mlxsw_sp,
                        void *ruleset_priv, void *rule_priv,
                        struct mlxsw_sp_acl_rule_info *rulei);
        void (*rule_del)(struct mlxsw_sp *mlxsw_sp, void *rule_priv);
        int (*rule_action_replace)(struct mlxsw_sp *mlxsw_sp, void *rule_priv,
                                   struct mlxsw_sp_acl_rule_info *rulei);
        int (*rule_activity_get)(struct mlxsw_sp *mlxsw_sp, void *rule_priv,
                                 bool *activity);
};

const struct mlxsw_sp_acl_profile_ops *
mlxsw_sp_acl_tcam_profile_ops(struct mlxsw_sp *mlxsw_sp,
                              enum mlxsw_sp_acl_profile profile);

#define MLXSW_SP_ACL_TCAM_REGION_BASE_COUNT 16
#define MLXSW_SP_ACL_TCAM_REGION_RESIZE_STEP 16

#define MLXSW_SP_ACL_TCAM_CATCHALL_PRIO (~0U)

#define MLXSW_SP_ACL_TCAM_MASK_LEN \
        (MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN * BITS_PER_BYTE)

struct mlxsw_sp_acl_tcam_group;
struct mlxsw_sp_acl_tcam_vregion;

struct mlxsw_sp_acl_tcam_region {
        struct mlxsw_sp_acl_tcam_vregion *vregion;
        struct mlxsw_sp_acl_tcam_group *group;
        struct list_head list; /* Member of a TCAM group */
        enum mlxsw_reg_ptar_key_type key_type;
        u16 id; /* ACL ID and region ID - they are same */
        char tcam_region_info[MLXSW_REG_PXXX_TCAM_REGION_INFO_LEN];
        struct mlxsw_afk_key_info *key_info;
        struct mlxsw_sp *mlxsw_sp;
        unsigned long priv[];
        /* priv has to be always the last item */
};

struct mlxsw_sp_acl_ctcam_region {
        struct parman *parman;
        const struct mlxsw_sp_acl_ctcam_region_ops *ops;
        struct mlxsw_sp_acl_tcam_region *region;
};

struct mlxsw_sp_acl_ctcam_chunk {
        struct parman_prio parman_prio;
};

struct mlxsw_sp_acl_ctcam_entry {
        struct parman_item parman_item;
};

struct mlxsw_sp_acl_ctcam_region_ops {
        int (*entry_insert)(struct mlxsw_sp_acl_ctcam_region *cregion,
                            struct mlxsw_sp_acl_ctcam_entry *centry,
                            const char *mask);
        void (*entry_remove)(struct mlxsw_sp_acl_ctcam_region *cregion,
                             struct mlxsw_sp_acl_ctcam_entry *centry);
};

int
mlxsw_sp_acl_ctcam_region_init(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_ctcam_region *cregion,
                               struct mlxsw_sp_acl_tcam_region *region,
                               const struct mlxsw_sp_acl_ctcam_region_ops *ops);
void mlxsw_sp_acl_ctcam_region_fini(struct mlxsw_sp_acl_ctcam_region *cregion);
void mlxsw_sp_acl_ctcam_chunk_init(struct mlxsw_sp_acl_ctcam_region *cregion,
                                   struct mlxsw_sp_acl_ctcam_chunk *cchunk,
                                   unsigned int priority);
void mlxsw_sp_acl_ctcam_chunk_fini(struct mlxsw_sp_acl_ctcam_chunk *cchunk);
int mlxsw_sp_acl_ctcam_entry_add(struct mlxsw_sp *mlxsw_sp,
                                 struct mlxsw_sp_acl_ctcam_region *cregion,
                                 struct mlxsw_sp_acl_ctcam_chunk *cchunk,
                                 struct mlxsw_sp_acl_ctcam_entry *centry,
                                 struct mlxsw_sp_acl_rule_info *rulei,
                                 bool fillup_priority);
void mlxsw_sp_acl_ctcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_ctcam_region *cregion,
                                  struct mlxsw_sp_acl_ctcam_chunk *cchunk,
                                  struct mlxsw_sp_acl_ctcam_entry *centry);
int mlxsw_sp_acl_ctcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
                                            struct mlxsw_sp_acl_ctcam_region *cregion,
                                            struct mlxsw_sp_acl_ctcam_entry *centry,
                                            struct mlxsw_sp_acl_rule_info *rulei);
static inline unsigned int
mlxsw_sp_acl_ctcam_entry_offset(struct mlxsw_sp_acl_ctcam_entry *centry)
{
        return centry->parman_item.index;
}

enum mlxsw_sp_acl_atcam_region_type {
        MLXSW_SP_ACL_ATCAM_REGION_TYPE_2KB,
        MLXSW_SP_ACL_ATCAM_REGION_TYPE_4KB,
        MLXSW_SP_ACL_ATCAM_REGION_TYPE_8KB,
        MLXSW_SP_ACL_ATCAM_REGION_TYPE_12KB,
        __MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX,
};

#define MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX \
        (__MLXSW_SP_ACL_ATCAM_REGION_TYPE_MAX - 1)

struct mlxsw_sp_acl_atcam {
        struct mlxsw_sp_acl_erp_core *erp_core;
};

struct mlxsw_sp_acl_atcam_region {
        struct rhashtable entries_ht; /* A-TCAM only */
        struct list_head entries_list; /* A-TCAM only */
        struct mlxsw_sp_acl_ctcam_region cregion;
        const struct mlxsw_sp_acl_atcam_region_ops *ops;
        struct mlxsw_sp_acl_tcam_region *region;
        struct mlxsw_sp_acl_atcam *atcam;
        enum mlxsw_sp_acl_atcam_region_type type;
        struct mlxsw_sp_acl_erp_table *erp_table;
        void *priv;
};

struct mlxsw_sp_acl_atcam_entry_ht_key {
        char enc_key[MLXSW_REG_PTCEX_FLEX_KEY_BLOCKS_LEN]; /* Encoded key, minus
                                                            * delta bits.
                                                            */
        u8 erp_id;
};

struct mlxsw_sp_acl_atcam_chunk {
        struct mlxsw_sp_acl_ctcam_chunk cchunk;
};

struct mlxsw_sp_acl_atcam_entry {
        struct rhash_head ht_node;
        struct list_head list; /* Member in entries_list */
        struct mlxsw_sp_acl_atcam_entry_ht_key ht_key;
        struct {
                u16 start;
                u8 mask;
                u8 value;
        } delta_info;
        struct mlxsw_sp_acl_ctcam_entry centry;
        struct mlxsw_sp_acl_atcam_lkey_id *lkey_id;
        struct mlxsw_sp_acl_erp_mask *erp_mask;
};

static inline struct mlxsw_sp_acl_atcam_region *
mlxsw_sp_acl_tcam_cregion_aregion(struct mlxsw_sp_acl_ctcam_region *cregion)
{
        return container_of(cregion, struct mlxsw_sp_acl_atcam_region, cregion);
}

static inline struct mlxsw_sp_acl_atcam_entry *
mlxsw_sp_acl_tcam_centry_aentry(struct mlxsw_sp_acl_ctcam_entry *centry)
{
        return container_of(centry, struct mlxsw_sp_acl_atcam_entry, centry);
}

int mlxsw_sp_acl_atcam_region_associate(struct mlxsw_sp *mlxsw_sp,
                                        u16 region_id);
int
mlxsw_sp_acl_atcam_region_init(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam *atcam,
                               struct mlxsw_sp_acl_atcam_region *aregion,
                               struct mlxsw_sp_acl_tcam_region *region,
                               void *hints_priv,
                               const struct mlxsw_sp_acl_ctcam_region_ops *ops);
void mlxsw_sp_acl_atcam_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_atcam_chunk_init(struct mlxsw_sp_acl_atcam_region *aregion,
                                   struct mlxsw_sp_acl_atcam_chunk *achunk,
                                   unsigned int priority);
void mlxsw_sp_acl_atcam_chunk_fini(struct mlxsw_sp_acl_atcam_chunk *achunk);
int mlxsw_sp_acl_atcam_entry_add(struct mlxsw_sp *mlxsw_sp,
                                 struct mlxsw_sp_acl_atcam_region *aregion,
                                 struct mlxsw_sp_acl_atcam_chunk *achunk,
                                 struct mlxsw_sp_acl_atcam_entry *aentry,
                                 struct mlxsw_sp_acl_rule_info *rulei);
void mlxsw_sp_acl_atcam_entry_del(struct mlxsw_sp *mlxsw_sp,
                                  struct mlxsw_sp_acl_atcam_region *aregion,
                                  struct mlxsw_sp_acl_atcam_chunk *achunk,
                                  struct mlxsw_sp_acl_atcam_entry *aentry);
int mlxsw_sp_acl_atcam_entry_action_replace(struct mlxsw_sp *mlxsw_sp,
                                            struct mlxsw_sp_acl_atcam_region *aregion,
                                            struct mlxsw_sp_acl_atcam_entry *aentry,
                                            struct mlxsw_sp_acl_rule_info *rulei);
int mlxsw_sp_acl_atcam_init(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_atcam *atcam);
void mlxsw_sp_acl_atcam_fini(struct mlxsw_sp *mlxsw_sp,
                             struct mlxsw_sp_acl_atcam *atcam);
void *
mlxsw_sp_acl_atcam_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_atcam_rehash_hints_put(void *hints_priv);

struct mlxsw_sp_acl_erp_delta;

u16 mlxsw_sp_acl_erp_delta_start(const struct mlxsw_sp_acl_erp_delta *delta);
u8 mlxsw_sp_acl_erp_delta_mask(const struct mlxsw_sp_acl_erp_delta *delta);
u8 mlxsw_sp_acl_erp_delta_value(const struct mlxsw_sp_acl_erp_delta *delta,
                                const char *enc_key);
void mlxsw_sp_acl_erp_delta_clear(const struct mlxsw_sp_acl_erp_delta *delta,
                                  const char *enc_key);

struct mlxsw_sp_acl_erp_mask;

bool
mlxsw_sp_acl_erp_mask_is_ctcam(const struct mlxsw_sp_acl_erp_mask *erp_mask);
u8 mlxsw_sp_acl_erp_mask_erp_id(const struct mlxsw_sp_acl_erp_mask *erp_mask);
const struct mlxsw_sp_acl_erp_delta *
mlxsw_sp_acl_erp_delta(const struct mlxsw_sp_acl_erp_mask *erp_mask);
struct mlxsw_sp_acl_erp_mask *
mlxsw_sp_acl_erp_mask_get(struct mlxsw_sp_acl_atcam_region *aregion,
                          const char *mask, bool ctcam);
void mlxsw_sp_acl_erp_mask_put(struct mlxsw_sp_acl_atcam_region *aregion,
                               struct mlxsw_sp_acl_erp_mask *erp_mask);
int mlxsw_sp_acl_erp_bf_insert(struct mlxsw_sp *mlxsw_sp,
                               struct mlxsw_sp_acl_atcam_region *aregion,
                               struct mlxsw_sp_acl_erp_mask *erp_mask,
                               struct mlxsw_sp_acl_atcam_entry *aentry);
void mlxsw_sp_acl_erp_bf_remove(struct mlxsw_sp *mlxsw_sp,
                                struct mlxsw_sp_acl_atcam_region *aregion,
                                struct mlxsw_sp_acl_erp_mask *erp_mask,
                                struct mlxsw_sp_acl_atcam_entry *aentry);
void *
mlxsw_sp_acl_erp_rehash_hints_get(struct mlxsw_sp_acl_atcam_region *aregion);
void mlxsw_sp_acl_erp_rehash_hints_put(void *hints_priv);
int mlxsw_sp_acl_erp_region_init(struct mlxsw_sp_acl_atcam_region *aregion,
                                 void *hints_priv);
void mlxsw_sp_acl_erp_region_fini(struct mlxsw_sp_acl_atcam_region *aregion);
int mlxsw_sp_acl_erps_init(struct mlxsw_sp *mlxsw_sp,
                           struct mlxsw_sp_acl_atcam *atcam);
void mlxsw_sp_acl_erps_fini(struct mlxsw_sp *mlxsw_sp,
                            struct mlxsw_sp_acl_atcam *atcam);

struct mlxsw_sp_acl_bf;

struct mlxsw_sp_acl_bf_ops {
        unsigned int (*index_get)(struct mlxsw_sp_acl_bf *bf,
                                  struct mlxsw_sp_acl_atcam_region *aregion,
                                  struct mlxsw_sp_acl_atcam_entry *aentry);
};

int
mlxsw_sp_acl_bf_entry_add(struct mlxsw_sp *mlxsw_sp,
                          struct mlxsw_sp_acl_bf *bf,
                          struct mlxsw_sp_acl_atcam_region *aregion,
                          unsigned int erp_bank,
                          struct mlxsw_sp_acl_atcam_entry *aentry);
void
mlxsw_sp_acl_bf_entry_del(struct mlxsw_sp *mlxsw_sp,
                          struct mlxsw_sp_acl_bf *bf,
                          struct mlxsw_sp_acl_atcam_region *aregion,
                          unsigned int erp_bank,
                          struct mlxsw_sp_acl_atcam_entry *aentry);
struct mlxsw_sp_acl_bf *
mlxsw_sp_acl_bf_init(struct mlxsw_sp *mlxsw_sp, unsigned int num_erp_banks);
void mlxsw_sp_acl_bf_fini(struct mlxsw_sp_acl_bf *bf);

#endif