1877 lines
50 KiB
C
1877 lines
50 KiB
C
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
|
|
/* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/if_vlan.h>
|
|
#include <linux/if_bridge.h>
|
|
#include <linux/netdevice.h>
|
|
#include <linux/rhashtable.h>
|
|
#include <linux/rtnetlink.h>
|
|
#include <linux/refcount.h>
|
|
|
|
#include "spectrum.h"
|
|
#include "reg.h"
|
|
|
|
struct mlxsw_sp_fid_family;
|
|
|
|
struct mlxsw_sp_fid_core {
|
|
struct rhashtable fid_ht;
|
|
struct rhashtable vni_ht;
|
|
struct mlxsw_sp_fid_family *fid_family_arr[MLXSW_SP_FID_TYPE_MAX];
|
|
unsigned int *port_fid_mappings;
|
|
};
|
|
|
|
struct mlxsw_sp_fid_port_vid {
|
|
struct list_head list;
|
|
u16 local_port;
|
|
u16 vid;
|
|
};
|
|
|
|
struct mlxsw_sp_fid {
|
|
struct list_head list;
|
|
struct mlxsw_sp_rif *rif;
|
|
refcount_t ref_count;
|
|
u16 fid_index;
|
|
u16 fid_offset;
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
struct rhash_head ht_node;
|
|
|
|
struct rhash_head vni_ht_node;
|
|
enum mlxsw_sp_nve_type nve_type;
|
|
__be32 vni;
|
|
u32 nve_flood_index;
|
|
int nve_ifindex;
|
|
u8 vni_valid:1,
|
|
nve_flood_index_valid:1;
|
|
struct list_head port_vid_list; /* Ordered by local port. */
|
|
};
|
|
|
|
struct mlxsw_sp_fid_8021q {
|
|
struct mlxsw_sp_fid common;
|
|
u16 vid;
|
|
};
|
|
|
|
struct mlxsw_sp_fid_8021d {
|
|
struct mlxsw_sp_fid common;
|
|
int br_ifindex;
|
|
};
|
|
|
|
static const struct rhashtable_params mlxsw_sp_fid_ht_params = {
|
|
.key_len = sizeof_field(struct mlxsw_sp_fid, fid_index),
|
|
.key_offset = offsetof(struct mlxsw_sp_fid, fid_index),
|
|
.head_offset = offsetof(struct mlxsw_sp_fid, ht_node),
|
|
};
|
|
|
|
static const struct rhashtable_params mlxsw_sp_fid_vni_ht_params = {
|
|
.key_len = sizeof_field(struct mlxsw_sp_fid, vni),
|
|
.key_offset = offsetof(struct mlxsw_sp_fid, vni),
|
|
.head_offset = offsetof(struct mlxsw_sp_fid, vni_ht_node),
|
|
};
|
|
|
|
struct mlxsw_sp_flood_table {
|
|
enum mlxsw_sp_flood_type packet_type;
|
|
enum mlxsw_flood_table_type table_type;
|
|
int table_index;
|
|
};
|
|
|
|
struct mlxsw_sp_fid_ops {
|
|
void (*setup)(struct mlxsw_sp_fid *fid, const void *arg);
|
|
int (*configure)(struct mlxsw_sp_fid *fid);
|
|
void (*deconfigure)(struct mlxsw_sp_fid *fid);
|
|
int (*index_alloc)(struct mlxsw_sp_fid *fid, const void *arg,
|
|
u16 *p_fid_index);
|
|
bool (*compare)(const struct mlxsw_sp_fid *fid,
|
|
const void *arg);
|
|
int (*port_vid_map)(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *port, u16 vid);
|
|
void (*port_vid_unmap)(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *port, u16 vid);
|
|
int (*vni_set)(struct mlxsw_sp_fid *fid);
|
|
void (*vni_clear)(struct mlxsw_sp_fid *fid);
|
|
int (*nve_flood_index_set)(struct mlxsw_sp_fid *fid);
|
|
void (*nve_flood_index_clear)(struct mlxsw_sp_fid *fid);
|
|
void (*fdb_clear_offload)(const struct mlxsw_sp_fid *fid,
|
|
const struct net_device *nve_dev);
|
|
int (*vid_to_fid_rif_update)(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif);
|
|
};
|
|
|
|
struct mlxsw_sp_fid_family {
|
|
enum mlxsw_sp_fid_type type;
|
|
size_t fid_size;
|
|
u16 start_index;
|
|
u16 end_index;
|
|
struct list_head fids_list;
|
|
unsigned long *fids_bitmap;
|
|
const struct mlxsw_sp_flood_table *flood_tables;
|
|
int nr_flood_tables;
|
|
enum mlxsw_sp_rif_type rif_type;
|
|
const struct mlxsw_sp_fid_ops *ops;
|
|
struct mlxsw_sp *mlxsw_sp;
|
|
bool flood_rsp;
|
|
enum mlxsw_reg_bridge_type bridge_type;
|
|
u16 pgt_base;
|
|
bool smpe_index_valid;
|
|
};
|
|
|
|
static const int mlxsw_sp_sfgc_uc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
|
|
[MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST] = 1,
|
|
};
|
|
|
|
static const int mlxsw_sp_sfgc_bc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
|
|
[MLXSW_REG_SFGC_TYPE_BROADCAST] = 1,
|
|
[MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_NON_IP] = 1,
|
|
[MLXSW_REG_SFGC_TYPE_IPV4_LINK_LOCAL] = 1,
|
|
[MLXSW_REG_SFGC_TYPE_IPV6_ALL_HOST] = 1,
|
|
[MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV6] = 1,
|
|
};
|
|
|
|
static const int mlxsw_sp_sfgc_mc_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
|
|
[MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1,
|
|
};
|
|
|
|
static const int *mlxsw_sp_packet_type_sfgc_types[] = {
|
|
[MLXSW_SP_FLOOD_TYPE_UC] = mlxsw_sp_sfgc_uc_packet_types,
|
|
[MLXSW_SP_FLOOD_TYPE_BC] = mlxsw_sp_sfgc_bc_packet_types,
|
|
[MLXSW_SP_FLOOD_TYPE_MC] = mlxsw_sp_sfgc_mc_packet_types,
|
|
};
|
|
|
|
bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index)
|
|
{
|
|
enum mlxsw_sp_fid_type fid_type = MLXSW_SP_FID_TYPE_DUMMY;
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
|
|
fid_family = mlxsw_sp->fid_core->fid_family_arr[fid_type];
|
|
|
|
return fid_family->start_index == fid_index;
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp,
|
|
u16 fid_index)
|
|
{
|
|
struct mlxsw_sp_fid *fid;
|
|
|
|
fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->fid_ht, &fid_index,
|
|
mlxsw_sp_fid_ht_params);
|
|
if (fid)
|
|
refcount_inc(&fid->ref_count);
|
|
|
|
return fid;
|
|
}
|
|
|
|
int mlxsw_sp_fid_nve_ifindex(const struct mlxsw_sp_fid *fid, int *nve_ifindex)
|
|
{
|
|
if (!fid->vni_valid)
|
|
return -EINVAL;
|
|
|
|
*nve_ifindex = fid->nve_ifindex;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mlxsw_sp_fid_nve_type(const struct mlxsw_sp_fid *fid,
|
|
enum mlxsw_sp_nve_type *p_type)
|
|
{
|
|
if (!fid->vni_valid)
|
|
return -EINVAL;
|
|
|
|
*p_type = fid->nve_type;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_vni(struct mlxsw_sp *mlxsw_sp,
|
|
__be32 vni)
|
|
{
|
|
struct mlxsw_sp_fid *fid;
|
|
|
|
fid = rhashtable_lookup_fast(&mlxsw_sp->fid_core->vni_ht, &vni,
|
|
mlxsw_sp_fid_vni_ht_params);
|
|
if (fid)
|
|
refcount_inc(&fid->ref_count);
|
|
|
|
return fid;
|
|
}
|
|
|
|
int mlxsw_sp_fid_vni(const struct mlxsw_sp_fid *fid, __be32 *vni)
|
|
{
|
|
if (!fid->vni_valid)
|
|
return -EINVAL;
|
|
|
|
*vni = fid->vni;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid,
|
|
u32 nve_flood_index)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
|
|
int err;
|
|
|
|
if (WARN_ON(fid->nve_flood_index_valid))
|
|
return -EINVAL;
|
|
|
|
fid->nve_flood_index = nve_flood_index;
|
|
fid->nve_flood_index_valid = true;
|
|
err = ops->nve_flood_index_set(fid);
|
|
if (err)
|
|
goto err_nve_flood_index_set;
|
|
|
|
return 0;
|
|
|
|
err_nve_flood_index_set:
|
|
fid->nve_flood_index_valid = false;
|
|
return err;
|
|
}
|
|
|
|
void mlxsw_sp_fid_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
|
|
|
|
if (WARN_ON(!fid->nve_flood_index_valid))
|
|
return;
|
|
|
|
fid->nve_flood_index_valid = false;
|
|
ops->nve_flood_index_clear(fid);
|
|
}
|
|
|
|
bool mlxsw_sp_fid_nve_flood_index_is_set(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return fid->nve_flood_index_valid;
|
|
}
|
|
|
|
int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type,
|
|
__be32 vni, int nve_ifindex)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
|
|
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
|
|
int err;
|
|
|
|
if (WARN_ON(fid->vni_valid))
|
|
return -EINVAL;
|
|
|
|
fid->nve_type = type;
|
|
fid->nve_ifindex = nve_ifindex;
|
|
fid->vni = vni;
|
|
err = rhashtable_lookup_insert_fast(&mlxsw_sp->fid_core->vni_ht,
|
|
&fid->vni_ht_node,
|
|
mlxsw_sp_fid_vni_ht_params);
|
|
if (err)
|
|
return err;
|
|
|
|
fid->vni_valid = true;
|
|
err = ops->vni_set(fid);
|
|
if (err)
|
|
goto err_vni_set;
|
|
|
|
return 0;
|
|
|
|
err_vni_set:
|
|
fid->vni_valid = false;
|
|
rhashtable_remove_fast(&mlxsw_sp->fid_core->vni_ht, &fid->vni_ht_node,
|
|
mlxsw_sp_fid_vni_ht_params);
|
|
return err;
|
|
}
|
|
|
|
void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
|
|
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
|
|
|
|
if (WARN_ON(!fid->vni_valid))
|
|
return;
|
|
|
|
fid->vni_valid = false;
|
|
ops->vni_clear(fid);
|
|
rhashtable_remove_fast(&mlxsw_sp->fid_core->vni_ht, &fid->vni_ht_node,
|
|
mlxsw_sp_fid_vni_ht_params);
|
|
}
|
|
|
|
bool mlxsw_sp_fid_vni_is_set(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return fid->vni_valid;
|
|
}
|
|
|
|
void mlxsw_sp_fid_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
|
|
const struct net_device *nve_dev)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_fid_ops *ops = fid_family->ops;
|
|
|
|
if (ops->fdb_clear_offload)
|
|
ops->fdb_clear_offload(fid, nve_dev);
|
|
}
|
|
|
|
static const struct mlxsw_sp_flood_table *
|
|
mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid,
|
|
enum mlxsw_sp_flood_type packet_type)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
int i;
|
|
|
|
for (i = 0; i < fid_family->nr_flood_tables; i++) {
|
|
if (fid_family->flood_tables[i].packet_type != packet_type)
|
|
continue;
|
|
return &fid_family->flood_tables[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static u16
|
|
mlxsw_sp_fid_family_num_fids(const struct mlxsw_sp_fid_family *fid_family)
|
|
{
|
|
return fid_family->end_index - fid_family->start_index + 1;
|
|
}
|
|
|
|
static u16
|
|
mlxsw_sp_fid_flood_table_mid(const struct mlxsw_sp_fid_family *fid_family,
|
|
const struct mlxsw_sp_flood_table *flood_table,
|
|
u16 fid_offset)
|
|
{
|
|
u16 num_fids;
|
|
|
|
num_fids = mlxsw_sp_fid_family_num_fids(fid_family);
|
|
return fid_family->pgt_base + num_fids * flood_table->table_index +
|
|
fid_offset;
|
|
}
|
|
|
|
int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid,
|
|
enum mlxsw_sp_flood_type packet_type, u16 local_port,
|
|
bool member)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
const struct mlxsw_sp_flood_table *flood_table;
|
|
u16 mid_index;
|
|
|
|
if (WARN_ON(!fid_family->flood_tables))
|
|
return -EINVAL;
|
|
|
|
flood_table = mlxsw_sp_fid_flood_table_lookup(fid, packet_type);
|
|
if (!flood_table)
|
|
return -ESRCH;
|
|
|
|
mid_index = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table,
|
|
fid->fid_offset);
|
|
return mlxsw_sp_pgt_entry_port_set(fid_family->mlxsw_sp, mid_index,
|
|
fid->fid_index, local_port, member);
|
|
}
|
|
|
|
int mlxsw_sp_fid_port_vid_map(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
|
|
{
|
|
if (WARN_ON(!fid->fid_family->ops->port_vid_map))
|
|
return -EINVAL;
|
|
return fid->fid_family->ops->port_vid_map(fid, mlxsw_sp_port, vid);
|
|
}
|
|
|
|
void mlxsw_sp_fid_port_vid_unmap(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
|
|
{
|
|
fid->fid_family->ops->port_vid_unmap(fid, mlxsw_sp_port, vid);
|
|
}
|
|
|
|
u16 mlxsw_sp_fid_index(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return fid->fid_index;
|
|
}
|
|
|
|
enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return fid->fid_family->type;
|
|
}
|
|
|
|
struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return fid->rif;
|
|
}
|
|
|
|
enum mlxsw_sp_rif_type
|
|
mlxsw_sp_fid_type_rif_type(const struct mlxsw_sp *mlxsw_sp,
|
|
enum mlxsw_sp_fid_type type)
|
|
{
|
|
struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core;
|
|
|
|
return fid_core->fid_family_arr[type]->rif_type;
|
|
}
|
|
|
|
static struct mlxsw_sp_fid_8021q *
|
|
mlxsw_sp_fid_8021q_fid(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return container_of(fid, struct mlxsw_sp_fid_8021q, common);
|
|
}
|
|
|
|
u16 mlxsw_sp_fid_8021q_vid(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_8021q_fid(fid)->vid;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021q_setup(struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
u16 vid = *(u16 *) arg;
|
|
|
|
mlxsw_sp_fid_8021q_fid(fid)->vid = vid;
|
|
fid->fid_offset = fid->fid_index - fid->fid_family->start_index;
|
|
}
|
|
|
|
static enum mlxsw_reg_sfmr_op mlxsw_sp_sfmr_op(bool valid)
|
|
{
|
|
return valid ? MLXSW_REG_SFMR_OP_CREATE_FID :
|
|
MLXSW_REG_SFMR_OP_DESTROY_FID;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_op(const struct mlxsw_sp_fid *fid, bool valid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char sfmr_pl[MLXSW_REG_SFMR_LEN];
|
|
u16 smpe;
|
|
|
|
smpe = fid->fid_family->smpe_index_valid ? fid->fid_index : 0;
|
|
|
|
mlxsw_reg_sfmr_pack(sfmr_pl, mlxsw_sp_sfmr_op(valid), fid->fid_index,
|
|
fid->fid_offset, fid->fid_family->flood_rsp,
|
|
fid->fid_family->bridge_type,
|
|
fid->fid_family->smpe_index_valid, smpe);
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_edit_op(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char sfmr_pl[MLXSW_REG_SFMR_LEN];
|
|
u16 smpe;
|
|
|
|
smpe = fid->fid_family->smpe_index_valid ? fid->fid_index : 0;
|
|
|
|
mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID,
|
|
fid->fid_index, fid->fid_offset,
|
|
fid->fid_family->flood_rsp,
|
|
fid->fid_family->bridge_type,
|
|
fid->fid_family->smpe_index_valid, smpe);
|
|
mlxsw_reg_sfmr_vv_set(sfmr_pl, fid->vni_valid);
|
|
mlxsw_reg_sfmr_vni_set(sfmr_pl, be32_to_cpu(fid->vni));
|
|
mlxsw_reg_sfmr_vtfp_set(sfmr_pl, fid->nve_flood_index_valid);
|
|
mlxsw_reg_sfmr_nve_tunnel_flood_ptr_set(sfmr_pl, fid->nve_flood_index);
|
|
|
|
if (rif) {
|
|
mlxsw_reg_sfmr_irif_v_set(sfmr_pl, true);
|
|
mlxsw_reg_sfmr_irif_set(sfmr_pl, mlxsw_sp_rif_index(rif));
|
|
}
|
|
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_vni_to_fid_map(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif,
|
|
bool valid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char svfa_pl[MLXSW_REG_SVFA_LEN];
|
|
bool irif_valid;
|
|
u16 irif_index;
|
|
|
|
irif_valid = !!rif;
|
|
irif_index = rif ? mlxsw_sp_rif_index(rif) : 0;
|
|
|
|
mlxsw_reg_svfa_vni_pack(svfa_pl, valid, fid->fid_index,
|
|
be32_to_cpu(fid->vni), irif_valid, irif_index);
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_to_fid_rif_update(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
return mlxsw_sp_fid_edit_op(fid, rif);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_vni_to_fid_rif_update(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
if (!fid->vni_valid)
|
|
return 0;
|
|
|
|
return mlxsw_sp_fid_vni_to_fid_map(fid, rif, fid->vni_valid);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_vid_to_fid_map(const struct mlxsw_sp_fid *fid, u16 vid, bool valid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char svfa_pl[MLXSW_REG_SVFA_LEN];
|
|
bool irif_valid;
|
|
u16 irif_index;
|
|
|
|
irif_valid = !!rif;
|
|
irif_index = rif ? mlxsw_sp_rif_index(rif) : 0;
|
|
|
|
mlxsw_reg_svfa_vid_pack(svfa_pl, valid, fid->fid_index, vid, irif_valid,
|
|
irif_index);
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_8021q_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid);
|
|
|
|
/* Update the global VID => FID mapping we created when the FID was
|
|
* configured.
|
|
*/
|
|
return mlxsw_sp_fid_vid_to_fid_map(fid, fid_8021q->vid, true, rif);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_port_vid_to_fid_rif_update_one(const struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_fid_port_vid *pv,
|
|
bool irif_valid, u16 irif_index)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char svfa_pl[MLXSW_REG_SVFA_LEN];
|
|
|
|
mlxsw_reg_svfa_port_vid_pack(svfa_pl, pv->local_port, true,
|
|
fid->fid_index, pv->vid, irif_valid,
|
|
irif_index);
|
|
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_vid_to_fid_rif_set(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
struct mlxsw_sp_fid_port_vid *pv;
|
|
u16 irif_index;
|
|
int err;
|
|
|
|
err = fid->fid_family->ops->vid_to_fid_rif_update(fid, rif);
|
|
if (err)
|
|
return err;
|
|
|
|
irif_index = mlxsw_sp_rif_index(rif);
|
|
|
|
list_for_each_entry(pv, &fid->port_vid_list, list) {
|
|
/* If port is not in virtual mode, then it does not have any
|
|
* {Port, VID}->FID mappings that need to be updated with the
|
|
* ingress RIF.
|
|
*/
|
|
if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port])
|
|
continue;
|
|
|
|
err = mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv,
|
|
true,
|
|
irif_index);
|
|
if (err)
|
|
goto err_port_vid_to_fid_rif_update_one;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_port_vid_to_fid_rif_update_one:
|
|
list_for_each_entry_continue_reverse(pv, &fid->port_vid_list, list) {
|
|
if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port])
|
|
continue;
|
|
|
|
mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv, false, 0);
|
|
}
|
|
|
|
fid->fid_family->ops->vid_to_fid_rif_update(fid, NULL);
|
|
return err;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_vid_to_fid_rif_unset(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
struct mlxsw_sp_fid_port_vid *pv;
|
|
|
|
list_for_each_entry(pv, &fid->port_vid_list, list) {
|
|
/* If port is not in virtual mode, then it does not have any
|
|
* {Port, VID}->FID mappings that need to be updated.
|
|
*/
|
|
if (!mlxsw_sp->fid_core->port_fid_mappings[pv->local_port])
|
|
continue;
|
|
|
|
mlxsw_sp_fid_port_vid_to_fid_rif_update_one(fid, pv, false, 0);
|
|
}
|
|
|
|
fid->fid_family->ops->vid_to_fid_rif_update(fid, NULL);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_reiv_handle(struct mlxsw_sp_fid *fid, u16 rif_index,
|
|
bool valid, u8 port_page)
|
|
{
|
|
u16 local_port_end = (port_page + 1) * MLXSW_REG_REIV_REC_MAX_COUNT - 1;
|
|
u16 local_port_start = port_page * MLXSW_REG_REIV_REC_MAX_COUNT;
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
struct mlxsw_sp_fid_port_vid *port_vid;
|
|
u8 rec_num, entries_num = 0;
|
|
char *reiv_pl;
|
|
int err;
|
|
|
|
reiv_pl = kmalloc(MLXSW_REG_REIV_LEN, GFP_KERNEL);
|
|
if (!reiv_pl)
|
|
return -ENOMEM;
|
|
|
|
mlxsw_reg_reiv_pack(reiv_pl, port_page, rif_index);
|
|
|
|
list_for_each_entry(port_vid, &fid->port_vid_list, list) {
|
|
/* port_vid_list is sorted by local_port. */
|
|
if (port_vid->local_port < local_port_start)
|
|
continue;
|
|
|
|
if (port_vid->local_port > local_port_end)
|
|
break;
|
|
|
|
rec_num = port_vid->local_port % MLXSW_REG_REIV_REC_MAX_COUNT;
|
|
mlxsw_reg_reiv_rec_update_set(reiv_pl, rec_num, true);
|
|
mlxsw_reg_reiv_rec_evid_set(reiv_pl, rec_num,
|
|
valid ? port_vid->vid : 0);
|
|
entries_num++;
|
|
}
|
|
|
|
if (!entries_num) {
|
|
kfree(reiv_pl);
|
|
return 0;
|
|
}
|
|
|
|
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(reiv), reiv_pl);
|
|
if (err)
|
|
goto err_reg_write;
|
|
|
|
kfree(reiv_pl);
|
|
return 0;
|
|
|
|
err_reg_write:
|
|
kfree(reiv_pl);
|
|
return err;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_erif_eport_to_vid_map(struct mlxsw_sp_fid *fid,
|
|
u16 rif_index, bool valid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
u8 num_port_pages;
|
|
int err, i;
|
|
|
|
num_port_pages = mlxsw_core_max_ports(mlxsw_sp->core) /
|
|
MLXSW_REG_REIV_REC_MAX_COUNT + 1;
|
|
|
|
for (i = 0; i < num_port_pages; i++) {
|
|
err = mlxsw_sp_fid_reiv_handle(fid, rif_index, valid, i);
|
|
if (err)
|
|
goto err_reiv_handle;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_reiv_handle:
|
|
for (; i >= 0; i--)
|
|
mlxsw_sp_fid_reiv_handle(fid, rif_index, !valid, i);
|
|
return err;
|
|
}
|
|
|
|
int mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif)
|
|
{
|
|
u16 rif_index = mlxsw_sp_rif_index(rif);
|
|
int err;
|
|
|
|
err = mlxsw_sp_fid_to_fid_rif_update(fid, rif);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mlxsw_sp_fid_vni_to_fid_rif_update(fid, rif);
|
|
if (err)
|
|
goto err_vni_to_fid_rif_update;
|
|
|
|
err = mlxsw_sp_fid_vid_to_fid_rif_set(fid, rif);
|
|
if (err)
|
|
goto err_vid_to_fid_rif_set;
|
|
|
|
err = mlxsw_sp_fid_erif_eport_to_vid_map(fid, rif_index, true);
|
|
if (err)
|
|
goto err_erif_eport_to_vid_map;
|
|
|
|
fid->rif = rif;
|
|
return 0;
|
|
|
|
err_erif_eport_to_vid_map:
|
|
mlxsw_sp_fid_vid_to_fid_rif_unset(fid);
|
|
err_vid_to_fid_rif_set:
|
|
mlxsw_sp_fid_vni_to_fid_rif_update(fid, NULL);
|
|
err_vni_to_fid_rif_update:
|
|
mlxsw_sp_fid_to_fid_rif_update(fid, NULL);
|
|
return err;
|
|
}
|
|
|
|
void mlxsw_sp_fid_rif_unset(struct mlxsw_sp_fid *fid)
|
|
{
|
|
u16 rif_index;
|
|
|
|
if (!fid->rif)
|
|
return;
|
|
|
|
rif_index = mlxsw_sp_rif_index(fid->rif);
|
|
fid->rif = NULL;
|
|
|
|
mlxsw_sp_fid_erif_eport_to_vid_map(fid, rif_index, false);
|
|
mlxsw_sp_fid_vid_to_fid_rif_unset(fid);
|
|
mlxsw_sp_fid_vni_to_fid_rif_update(fid, NULL);
|
|
mlxsw_sp_fid_to_fid_rif_update(fid, NULL);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_vni_op(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
int err;
|
|
|
|
err = mlxsw_sp_fid_vni_to_fid_map(fid, fid->rif, fid->vni_valid);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mlxsw_sp_fid_edit_op(fid, fid->rif);
|
|
if (err)
|
|
goto err_fid_edit_op;
|
|
|
|
return 0;
|
|
|
|
err_fid_edit_op:
|
|
mlxsw_sp_fid_vni_to_fid_map(fid, fid->rif, !fid->vni_valid);
|
|
return err;
|
|
}
|
|
|
|
static int __mlxsw_sp_fid_port_vid_map(const struct mlxsw_sp_fid *fid,
|
|
u16 local_port, u16 vid, bool valid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char svfa_pl[MLXSW_REG_SVFA_LEN];
|
|
bool irif_valid = false;
|
|
u16 irif_index = 0;
|
|
|
|
if (fid->rif) {
|
|
irif_valid = true;
|
|
irif_index = mlxsw_sp_rif_index(fid->rif);
|
|
}
|
|
|
|
mlxsw_reg_svfa_port_vid_pack(svfa_pl, local_port, valid, fid->fid_index,
|
|
vid, irif_valid, irif_index);
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
|
|
}
|
|
|
|
static struct mlxsw_sp_fid_8021d *
|
|
mlxsw_sp_fid_8021d_fid(const struct mlxsw_sp_fid *fid)
|
|
{
|
|
return container_of(fid, struct mlxsw_sp_fid_8021d, common);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021d_setup(struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
int br_ifindex = *(int *) arg;
|
|
|
|
mlxsw_sp_fid_8021d_fid(fid)->br_ifindex = br_ifindex;
|
|
fid->fid_offset = fid->fid_index - fid->fid_family->start_index;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021d_configure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_op(fid, true);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021d_deconfigure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
if (fid->vni_valid)
|
|
mlxsw_sp_nve_fid_disable(fid->fid_family->mlxsw_sp, fid);
|
|
mlxsw_sp_fid_op(fid, false);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021d_index_alloc(struct mlxsw_sp_fid *fid,
|
|
const void *arg, u16 *p_fid_index)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
u16 nr_fids, fid_index;
|
|
|
|
nr_fids = fid_family->end_index - fid_family->start_index + 1;
|
|
fid_index = find_first_zero_bit(fid_family->fids_bitmap, nr_fids);
|
|
if (fid_index == nr_fids)
|
|
return -ENOBUFS;
|
|
*p_fid_index = fid_family->start_index + fid_index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool
|
|
mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
int br_ifindex = *(int *) arg;
|
|
|
|
return mlxsw_sp_fid_8021d_fid(fid)->br_ifindex == br_ifindex;
|
|
}
|
|
|
|
static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
{
|
|
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
|
|
int err;
|
|
|
|
list_for_each_entry(mlxsw_sp_port_vlan, &mlxsw_sp_port->vlans_list,
|
|
list) {
|
|
struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
|
|
u16 vid = mlxsw_sp_port_vlan->vid;
|
|
|
|
if (!fid)
|
|
continue;
|
|
|
|
err = __mlxsw_sp_fid_port_vid_map(fid,
|
|
mlxsw_sp_port->local_port,
|
|
vid, true);
|
|
if (err)
|
|
goto err_fid_port_vid_map;
|
|
}
|
|
|
|
err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, true);
|
|
if (err)
|
|
goto err_port_vp_mode_set;
|
|
|
|
return 0;
|
|
|
|
err_port_vp_mode_set:
|
|
err_fid_port_vid_map:
|
|
list_for_each_entry_continue_reverse(mlxsw_sp_port_vlan,
|
|
&mlxsw_sp_port->vlans_list, list) {
|
|
struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
|
|
u16 vid = mlxsw_sp_port_vlan->vid;
|
|
|
|
if (!fid)
|
|
continue;
|
|
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid,
|
|
false);
|
|
}
|
|
return err;
|
|
}
|
|
|
|
static void mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
{
|
|
struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan;
|
|
|
|
mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
|
|
|
|
list_for_each_entry_reverse(mlxsw_sp_port_vlan,
|
|
&mlxsw_sp_port->vlans_list, list) {
|
|
struct mlxsw_sp_fid *fid = mlxsw_sp_port_vlan->fid;
|
|
u16 vid = mlxsw_sp_port_vlan->vid;
|
|
|
|
if (!fid)
|
|
continue;
|
|
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid,
|
|
false);
|
|
}
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_port_vid_list_add(struct mlxsw_sp_fid *fid, u16 local_port,
|
|
u16 vid)
|
|
{
|
|
struct mlxsw_sp_fid_port_vid *port_vid, *tmp_port_vid;
|
|
|
|
port_vid = kzalloc(sizeof(*port_vid), GFP_KERNEL);
|
|
if (!port_vid)
|
|
return -ENOMEM;
|
|
|
|
port_vid->local_port = local_port;
|
|
port_vid->vid = vid;
|
|
|
|
list_for_each_entry(tmp_port_vid, &fid->port_vid_list, list) {
|
|
if (tmp_port_vid->local_port > local_port)
|
|
break;
|
|
}
|
|
|
|
list_add_tail(&port_vid->list, &tmp_port_vid->list);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_port_vid_list_del(struct mlxsw_sp_fid *fid, u16 local_port,
|
|
u16 vid)
|
|
{
|
|
struct mlxsw_sp_fid_port_vid *port_vid, *tmp;
|
|
|
|
list_for_each_entry_safe(port_vid, tmp, &fid->port_vid_list, list) {
|
|
if (port_vid->local_port != local_port || port_vid->vid != vid)
|
|
continue;
|
|
|
|
list_del(&port_vid->list);
|
|
kfree(port_vid);
|
|
return;
|
|
}
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_mpe_table_map(const struct mlxsw_sp_fid *fid, u16 local_port,
|
|
u16 vid, bool valid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
char smpe_pl[MLXSW_REG_SMPE_LEN];
|
|
|
|
mlxsw_reg_smpe_pack(smpe_pl, local_port, fid->fid_index,
|
|
valid ? vid : 0);
|
|
return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smpe), smpe_pl);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_erif_eport_to_vid_map_one(const struct mlxsw_sp_fid *fid,
|
|
u16 local_port, u16 vid, bool valid)
|
|
{
|
|
u8 port_page = local_port / MLXSW_REG_REIV_REC_MAX_COUNT;
|
|
u8 rec_num = local_port % MLXSW_REG_REIV_REC_MAX_COUNT;
|
|
struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
|
|
u16 rif_index = mlxsw_sp_rif_index(fid->rif);
|
|
char *reiv_pl;
|
|
int err;
|
|
|
|
reiv_pl = kmalloc(MLXSW_REG_REIV_LEN, GFP_KERNEL);
|
|
if (!reiv_pl)
|
|
return -ENOMEM;
|
|
|
|
mlxsw_reg_reiv_pack(reiv_pl, port_page, rif_index);
|
|
mlxsw_reg_reiv_rec_update_set(reiv_pl, rec_num, true);
|
|
mlxsw_reg_reiv_rec_evid_set(reiv_pl, rec_num, valid ? vid : 0);
|
|
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(reiv), reiv_pl);
|
|
kfree(reiv_pl);
|
|
return err;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_evid_map(const struct mlxsw_sp_fid *fid, u16 local_port,
|
|
u16 vid, bool valid)
|
|
{
|
|
int err;
|
|
|
|
err = mlxsw_sp_fid_mpe_table_map(fid, local_port, vid, valid);
|
|
if (err)
|
|
return err;
|
|
|
|
if (!fid->rif)
|
|
return 0;
|
|
|
|
err = mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid,
|
|
valid);
|
|
if (err)
|
|
goto err_erif_eport_to_vid_map_one;
|
|
|
|
return 0;
|
|
|
|
err_erif_eport_to_vid_map_one:
|
|
mlxsw_sp_fid_mpe_table_map(fid, local_port, vid, !valid);
|
|
return err;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021d_port_vid_map(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port,
|
|
u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
int err;
|
|
|
|
err = __mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid,
|
|
true);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mlxsw_sp_fid_evid_map(fid, local_port, vid, true);
|
|
if (err)
|
|
goto err_fid_evid_map;
|
|
|
|
err = mlxsw_sp_fid_port_vid_list_add(fid, mlxsw_sp_port->local_port,
|
|
vid);
|
|
if (err)
|
|
goto err_port_vid_list_add;
|
|
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) {
|
|
err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
|
|
if (err)
|
|
goto err_port_vp_mode_trans;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_port_vp_mode_trans:
|
|
mlxsw_sp->fid_core->port_fid_mappings[local_port]--;
|
|
mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid);
|
|
err_port_vid_list_add:
|
|
mlxsw_sp_fid_evid_map(fid, local_port, vid, false);
|
|
err_fid_evid_map:
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, false);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_8021d_port_vid_unmap(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1)
|
|
mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
|
|
mlxsw_sp->fid_core->port_fid_mappings[local_port]--;
|
|
mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid);
|
|
mlxsw_sp_fid_evid_map(fid, local_port, vid, false);
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, false);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021d_vni_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_vni_op(fid);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021d_vni_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
mlxsw_sp_fid_vni_op(fid);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021d_nve_flood_index_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_edit_op(fid, fid->rif);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
mlxsw_sp_fid_edit_op(fid, fid->rif);
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
|
|
const struct net_device *nve_dev)
|
|
{
|
|
br_fdb_clear_offload(nve_dev, 0);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_8021d_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = {
|
|
.setup = mlxsw_sp_fid_8021d_setup,
|
|
.configure = mlxsw_sp_fid_8021d_configure,
|
|
.deconfigure = mlxsw_sp_fid_8021d_deconfigure,
|
|
.index_alloc = mlxsw_sp_fid_8021d_index_alloc,
|
|
.compare = mlxsw_sp_fid_8021d_compare,
|
|
.port_vid_map = mlxsw_sp_fid_8021d_port_vid_map,
|
|
.port_vid_unmap = mlxsw_sp_fid_8021d_port_vid_unmap,
|
|
.vni_set = mlxsw_sp_fid_8021d_vni_set,
|
|
.vni_clear = mlxsw_sp_fid_8021d_vni_clear,
|
|
.nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set,
|
|
.nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear,
|
|
.fdb_clear_offload = mlxsw_sp_fid_8021d_fdb_clear_offload,
|
|
.vid_to_fid_rif_update = mlxsw_sp_fid_8021d_vid_to_fid_rif_update,
|
|
};
|
|
|
|
#define MLXSW_SP_FID_8021Q_MAX (VLAN_N_VID - 2)
|
|
#define MLXSW_SP_FID_RFID_MAX (11 * 1024)
|
|
#define MLXSW_SP_FID_8021Q_PGT_BASE 0
|
|
#define MLXSW_SP_FID_8021D_PGT_BASE (3 * MLXSW_SP_FID_8021Q_MAX)
|
|
|
|
static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = {
|
|
{
|
|
.packet_type = MLXSW_SP_FLOOD_TYPE_UC,
|
|
.table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET,
|
|
.table_index = 0,
|
|
},
|
|
{
|
|
.packet_type = MLXSW_SP_FLOOD_TYPE_MC,
|
|
.table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET,
|
|
.table_index = 1,
|
|
},
|
|
{
|
|
.packet_type = MLXSW_SP_FLOOD_TYPE_BC,
|
|
.table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET,
|
|
.table_index = 2,
|
|
},
|
|
};
|
|
|
|
static bool
|
|
mlxsw_sp_fid_8021q_compare(const struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
u16 vid = *(u16 *) arg;
|
|
|
|
return mlxsw_sp_fid_8021q_fid(fid)->vid == vid;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid,
|
|
const struct net_device *nve_dev)
|
|
{
|
|
br_fdb_clear_offload(nve_dev, mlxsw_sp_fid_8021q_vid(fid));
|
|
}
|
|
|
|
static void mlxsw_sp_fid_rfid_setup(struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
fid->fid_offset = 0;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_rfid_configure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_op(fid, true);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_rfid_deconfigure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
mlxsw_sp_fid_op(fid, false);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_rfid_index_alloc(struct mlxsw_sp_fid *fid,
|
|
const void *arg, u16 *p_fid_index)
|
|
{
|
|
u16 rif_index = *(u16 *) arg;
|
|
|
|
*p_fid_index = fid->fid_family->start_index + rif_index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool mlxsw_sp_fid_rfid_compare(const struct mlxsw_sp_fid *fid,
|
|
const void *arg)
|
|
{
|
|
u16 rif_index = *(u16 *) arg;
|
|
|
|
return fid->fid_index == rif_index + fid->fid_family->start_index;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_rfid_port_vid_map(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port,
|
|
u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
int err;
|
|
|
|
err = mlxsw_sp_fid_port_vid_list_add(fid, mlxsw_sp_port->local_port,
|
|
vid);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Using legacy bridge model, we only need to transition the port to
|
|
* virtual mode since {Port, VID} => FID is done by the firmware upon
|
|
* RIF creation. Using unified bridge model, we need to map
|
|
* {Port, VID} => FID and map egress VID.
|
|
*/
|
|
err = __mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid,
|
|
true);
|
|
if (err)
|
|
goto err_port_vid_map;
|
|
|
|
if (fid->rif) {
|
|
err = mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port,
|
|
vid, true);
|
|
if (err)
|
|
goto err_erif_eport_to_vid_map_one;
|
|
}
|
|
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port]++ == 0) {
|
|
err = mlxsw_sp_port_vp_mode_trans(mlxsw_sp_port);
|
|
if (err)
|
|
goto err_port_vp_mode_trans;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_port_vp_mode_trans:
|
|
mlxsw_sp->fid_core->port_fid_mappings[local_port]--;
|
|
if (fid->rif)
|
|
mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid,
|
|
false);
|
|
err_erif_eport_to_vid_map_one:
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, false);
|
|
err_port_vid_map:
|
|
mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_rfid_port_vid_unmap(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port] == 1)
|
|
mlxsw_sp_port_vlan_mode_trans(mlxsw_sp_port);
|
|
mlxsw_sp->fid_core->port_fid_mappings[local_port]--;
|
|
|
|
if (fid->rif)
|
|
mlxsw_sp_fid_erif_eport_to_vid_map_one(fid, local_port, vid,
|
|
false);
|
|
__mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, false);
|
|
mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_rfid_vni_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_rfid_vni_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
WARN_ON_ONCE(1);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_rfid_nve_flood_index_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_rfid_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
WARN_ON_ONCE(1);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_rfid_vid_to_fid_rif_update(const struct mlxsw_sp_fid *fid,
|
|
const struct mlxsw_sp_rif *rif)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_rfid_ops = {
|
|
.setup = mlxsw_sp_fid_rfid_setup,
|
|
.configure = mlxsw_sp_fid_rfid_configure,
|
|
.deconfigure = mlxsw_sp_fid_rfid_deconfigure,
|
|
.index_alloc = mlxsw_sp_fid_rfid_index_alloc,
|
|
.compare = mlxsw_sp_fid_rfid_compare,
|
|
.port_vid_map = mlxsw_sp_fid_rfid_port_vid_map,
|
|
.port_vid_unmap = mlxsw_sp_fid_rfid_port_vid_unmap,
|
|
.vni_set = mlxsw_sp_fid_rfid_vni_set,
|
|
.vni_clear = mlxsw_sp_fid_rfid_vni_clear,
|
|
.nve_flood_index_set = mlxsw_sp_fid_rfid_nve_flood_index_set,
|
|
.nve_flood_index_clear = mlxsw_sp_fid_rfid_nve_flood_index_clear,
|
|
.vid_to_fid_rif_update = mlxsw_sp_fid_rfid_vid_to_fid_rif_update,
|
|
};
|
|
|
|
static void mlxsw_sp_fid_dummy_setup(struct mlxsw_sp_fid *fid, const void *arg)
|
|
{
|
|
fid->fid_offset = 0;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_dummy_configure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return mlxsw_sp_fid_op(fid, true);
|
|
}
|
|
|
|
static void mlxsw_sp_fid_dummy_deconfigure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
mlxsw_sp_fid_op(fid, false);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_dummy_index_alloc(struct mlxsw_sp_fid *fid,
|
|
const void *arg, u16 *p_fid_index)
|
|
{
|
|
*p_fid_index = fid->fid_family->start_index;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool mlxsw_sp_fid_dummy_compare(const struct mlxsw_sp_fid *fid,
|
|
const void *arg)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
static int mlxsw_sp_fid_dummy_vni_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_dummy_vni_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
WARN_ON_ONCE(1);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_dummy_nve_flood_index_set(struct mlxsw_sp_fid *fid)
|
|
{
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_dummy_nve_flood_index_clear(struct mlxsw_sp_fid *fid)
|
|
{
|
|
WARN_ON_ONCE(1);
|
|
}
|
|
|
|
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_dummy_ops = {
|
|
.setup = mlxsw_sp_fid_dummy_setup,
|
|
.configure = mlxsw_sp_fid_dummy_configure,
|
|
.deconfigure = mlxsw_sp_fid_dummy_deconfigure,
|
|
.index_alloc = mlxsw_sp_fid_dummy_index_alloc,
|
|
.compare = mlxsw_sp_fid_dummy_compare,
|
|
.vni_set = mlxsw_sp_fid_dummy_vni_set,
|
|
.vni_clear = mlxsw_sp_fid_dummy_vni_clear,
|
|
.nve_flood_index_set = mlxsw_sp_fid_dummy_nve_flood_index_set,
|
|
.nve_flood_index_clear = mlxsw_sp_fid_dummy_nve_flood_index_clear,
|
|
};
|
|
|
|
static int mlxsw_sp_fid_8021q_configure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid);
|
|
int err;
|
|
|
|
err = mlxsw_sp_fid_op(fid, true);
|
|
if (err)
|
|
return err;
|
|
|
|
err = mlxsw_sp_fid_vid_to_fid_map(fid, fid_8021q->vid, true, fid->rif);
|
|
if (err)
|
|
goto err_vid_to_fid_map;
|
|
|
|
return 0;
|
|
|
|
err_vid_to_fid_map:
|
|
mlxsw_sp_fid_op(fid, false);
|
|
return err;
|
|
}
|
|
|
|
static void mlxsw_sp_fid_8021q_deconfigure(struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp_fid_8021q *fid_8021q = mlxsw_sp_fid_8021q_fid(fid);
|
|
|
|
if (fid->vni_valid)
|
|
mlxsw_sp_nve_fid_disable(fid->fid_family->mlxsw_sp, fid);
|
|
|
|
mlxsw_sp_fid_vid_to_fid_map(fid, fid_8021q->vid, false, NULL);
|
|
mlxsw_sp_fid_op(fid, false);
|
|
}
|
|
|
|
static int mlxsw_sp_fid_8021q_port_vid_map(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port,
|
|
u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
int err;
|
|
|
|
/* In case there are no {Port, VID} => FID mappings on the port,
|
|
* we can use the global VID => FID mapping we created when the
|
|
* FID was configured, otherwise, configure new mapping.
|
|
*/
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port]) {
|
|
err = __mlxsw_sp_fid_port_vid_map(fid, local_port, vid, true);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
err = mlxsw_sp_fid_evid_map(fid, local_port, vid, true);
|
|
if (err)
|
|
goto err_fid_evid_map;
|
|
|
|
err = mlxsw_sp_fid_port_vid_list_add(fid, mlxsw_sp_port->local_port,
|
|
vid);
|
|
if (err)
|
|
goto err_port_vid_list_add;
|
|
|
|
return 0;
|
|
|
|
err_port_vid_list_add:
|
|
mlxsw_sp_fid_evid_map(fid, local_port, vid, false);
|
|
err_fid_evid_map:
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port])
|
|
__mlxsw_sp_fid_port_vid_map(fid, local_port, vid, false);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_8021q_port_vid_unmap(struct mlxsw_sp_fid *fid,
|
|
struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
u16 local_port = mlxsw_sp_port->local_port;
|
|
|
|
mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid);
|
|
mlxsw_sp_fid_evid_map(fid, local_port, vid, false);
|
|
if (mlxsw_sp->fid_core->port_fid_mappings[local_port])
|
|
__mlxsw_sp_fid_port_vid_map(fid, local_port, vid, false);
|
|
}
|
|
|
|
static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_ops = {
|
|
.setup = mlxsw_sp_fid_8021q_setup,
|
|
.configure = mlxsw_sp_fid_8021q_configure,
|
|
.deconfigure = mlxsw_sp_fid_8021q_deconfigure,
|
|
.index_alloc = mlxsw_sp_fid_8021d_index_alloc,
|
|
.compare = mlxsw_sp_fid_8021q_compare,
|
|
.port_vid_map = mlxsw_sp_fid_8021q_port_vid_map,
|
|
.port_vid_unmap = mlxsw_sp_fid_8021q_port_vid_unmap,
|
|
.vni_set = mlxsw_sp_fid_8021d_vni_set,
|
|
.vni_clear = mlxsw_sp_fid_8021d_vni_clear,
|
|
.nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set,
|
|
.nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear,
|
|
.fdb_clear_offload = mlxsw_sp_fid_8021q_fdb_clear_offload,
|
|
.vid_to_fid_rif_update = mlxsw_sp_fid_8021q_vid_to_fid_rif_update,
|
|
};
|
|
|
|
/* There are 4K-2 802.1Q FIDs */
|
|
#define MLXSW_SP_FID_8021Q_START 1 /* FID 0 is reserved. */
|
|
#define MLXSW_SP_FID_8021Q_END (MLXSW_SP_FID_8021Q_START + \
|
|
MLXSW_SP_FID_8021Q_MAX - 1)
|
|
|
|
/* There are 1K 802.1D FIDs */
|
|
#define MLXSW_SP_FID_8021D_START (MLXSW_SP_FID_8021Q_END + 1)
|
|
#define MLXSW_SP_FID_8021D_END (MLXSW_SP_FID_8021D_START + \
|
|
MLXSW_SP_FID_8021D_MAX - 1)
|
|
|
|
/* There is one dummy FID */
|
|
#define MLXSW_SP_FID_DUMMY (MLXSW_SP_FID_8021D_END + 1)
|
|
|
|
/* There are 11K rFIDs */
|
|
#define MLXSW_SP_RFID_START (MLXSW_SP_FID_DUMMY + 1)
|
|
#define MLXSW_SP_RFID_END (MLXSW_SP_RFID_START + \
|
|
MLXSW_SP_FID_RFID_MAX - 1)
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021q_family = {
|
|
.type = MLXSW_SP_FID_TYPE_8021Q,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid_8021q),
|
|
.start_index = MLXSW_SP_FID_8021Q_START,
|
|
.end_index = MLXSW_SP_FID_8021Q_END,
|
|
.flood_tables = mlxsw_sp_fid_8021d_flood_tables,
|
|
.nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
|
|
.rif_type = MLXSW_SP_RIF_TYPE_VLAN,
|
|
.ops = &mlxsw_sp_fid_8021q_ops,
|
|
.flood_rsp = false,
|
|
.bridge_type = MLXSW_REG_BRIDGE_TYPE_0,
|
|
.pgt_base = MLXSW_SP_FID_8021Q_PGT_BASE,
|
|
.smpe_index_valid = false,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021d_family = {
|
|
.type = MLXSW_SP_FID_TYPE_8021D,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid_8021d),
|
|
.start_index = MLXSW_SP_FID_8021D_START,
|
|
.end_index = MLXSW_SP_FID_8021D_END,
|
|
.flood_tables = mlxsw_sp_fid_8021d_flood_tables,
|
|
.nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
|
|
.rif_type = MLXSW_SP_RIF_TYPE_FID,
|
|
.ops = &mlxsw_sp_fid_8021d_ops,
|
|
.bridge_type = MLXSW_REG_BRIDGE_TYPE_1,
|
|
.pgt_base = MLXSW_SP_FID_8021D_PGT_BASE,
|
|
.smpe_index_valid = false,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_dummy_family = {
|
|
.type = MLXSW_SP_FID_TYPE_DUMMY,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid),
|
|
.start_index = MLXSW_SP_FID_DUMMY,
|
|
.end_index = MLXSW_SP_FID_DUMMY,
|
|
.ops = &mlxsw_sp_fid_dummy_ops,
|
|
.smpe_index_valid = false,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family = {
|
|
.type = MLXSW_SP_FID_TYPE_RFID,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid),
|
|
.start_index = MLXSW_SP_RFID_START,
|
|
.end_index = MLXSW_SP_RFID_END,
|
|
.rif_type = MLXSW_SP_RIF_TYPE_SUBPORT,
|
|
.ops = &mlxsw_sp_fid_rfid_ops,
|
|
.flood_rsp = true,
|
|
.smpe_index_valid = false,
|
|
};
|
|
|
|
const struct mlxsw_sp_fid_family *mlxsw_sp1_fid_family_arr[] = {
|
|
[MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp1_fid_8021q_family,
|
|
[MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp1_fid_8021d_family,
|
|
[MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp1_fid_dummy_family,
|
|
[MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021q_family = {
|
|
.type = MLXSW_SP_FID_TYPE_8021Q,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid_8021q),
|
|
.start_index = MLXSW_SP_FID_8021Q_START,
|
|
.end_index = MLXSW_SP_FID_8021Q_END,
|
|
.flood_tables = mlxsw_sp_fid_8021d_flood_tables,
|
|
.nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
|
|
.rif_type = MLXSW_SP_RIF_TYPE_VLAN,
|
|
.ops = &mlxsw_sp_fid_8021q_ops,
|
|
.flood_rsp = false,
|
|
.bridge_type = MLXSW_REG_BRIDGE_TYPE_0,
|
|
.pgt_base = MLXSW_SP_FID_8021Q_PGT_BASE,
|
|
.smpe_index_valid = true,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021d_family = {
|
|
.type = MLXSW_SP_FID_TYPE_8021D,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid_8021d),
|
|
.start_index = MLXSW_SP_FID_8021D_START,
|
|
.end_index = MLXSW_SP_FID_8021D_END,
|
|
.flood_tables = mlxsw_sp_fid_8021d_flood_tables,
|
|
.nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
|
|
.rif_type = MLXSW_SP_RIF_TYPE_FID,
|
|
.ops = &mlxsw_sp_fid_8021d_ops,
|
|
.bridge_type = MLXSW_REG_BRIDGE_TYPE_1,
|
|
.pgt_base = MLXSW_SP_FID_8021D_PGT_BASE,
|
|
.smpe_index_valid = true,
|
|
};
|
|
|
|
static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_dummy_family = {
|
|
.type = MLXSW_SP_FID_TYPE_DUMMY,
|
|
.fid_size = sizeof(struct mlxsw_sp_fid),
|
|
.start_index = MLXSW_SP_FID_DUMMY,
|
|
.end_index = MLXSW_SP_FID_DUMMY,
|
|
.ops = &mlxsw_sp_fid_dummy_ops,
|
|
.smpe_index_valid = false,
|
|
};
|
|
|
|
const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr[] = {
|
|
[MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp2_fid_8021q_family,
|
|
[MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp2_fid_8021d_family,
|
|
[MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp2_fid_dummy_family,
|
|
[MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family,
|
|
};
|
|
|
|
static struct mlxsw_sp_fid *mlxsw_sp_fid_lookup(struct mlxsw_sp *mlxsw_sp,
|
|
enum mlxsw_sp_fid_type type,
|
|
const void *arg)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
struct mlxsw_sp_fid *fid;
|
|
|
|
fid_family = mlxsw_sp->fid_core->fid_family_arr[type];
|
|
list_for_each_entry(fid, &fid_family->fids_list, list) {
|
|
if (!fid->fid_family->ops->compare(fid, arg))
|
|
continue;
|
|
refcount_inc(&fid->ref_count);
|
|
return fid;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp,
|
|
enum mlxsw_sp_fid_type type,
|
|
const void *arg)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
struct mlxsw_sp_fid *fid;
|
|
u16 fid_index;
|
|
int err;
|
|
|
|
fid = mlxsw_sp_fid_lookup(mlxsw_sp, type, arg);
|
|
if (fid)
|
|
return fid;
|
|
|
|
fid_family = mlxsw_sp->fid_core->fid_family_arr[type];
|
|
fid = kzalloc(fid_family->fid_size, GFP_KERNEL);
|
|
if (!fid)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
INIT_LIST_HEAD(&fid->port_vid_list);
|
|
fid->fid_family = fid_family;
|
|
|
|
err = fid->fid_family->ops->index_alloc(fid, arg, &fid_index);
|
|
if (err)
|
|
goto err_index_alloc;
|
|
fid->fid_index = fid_index;
|
|
__set_bit(fid_index - fid_family->start_index, fid_family->fids_bitmap);
|
|
|
|
fid->fid_family->ops->setup(fid, arg);
|
|
|
|
err = fid->fid_family->ops->configure(fid);
|
|
if (err)
|
|
goto err_configure;
|
|
|
|
err = rhashtable_insert_fast(&mlxsw_sp->fid_core->fid_ht, &fid->ht_node,
|
|
mlxsw_sp_fid_ht_params);
|
|
if (err)
|
|
goto err_rhashtable_insert;
|
|
|
|
list_add(&fid->list, &fid_family->fids_list);
|
|
refcount_set(&fid->ref_count, 1);
|
|
return fid;
|
|
|
|
err_rhashtable_insert:
|
|
fid->fid_family->ops->deconfigure(fid);
|
|
err_configure:
|
|
__clear_bit(fid_index - fid_family->start_index,
|
|
fid_family->fids_bitmap);
|
|
err_index_alloc:
|
|
kfree(fid);
|
|
return ERR_PTR(err);
|
|
}
|
|
|
|
void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid)
|
|
{
|
|
struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
|
|
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
|
|
|
|
if (!refcount_dec_and_test(&fid->ref_count))
|
|
return;
|
|
|
|
list_del(&fid->list);
|
|
rhashtable_remove_fast(&mlxsw_sp->fid_core->fid_ht,
|
|
&fid->ht_node, mlxsw_sp_fid_ht_params);
|
|
fid->fid_family->ops->deconfigure(fid);
|
|
__clear_bit(fid->fid_index - fid_family->start_index,
|
|
fid_family->fids_bitmap);
|
|
WARN_ON_ONCE(!list_empty(&fid->port_vid_list));
|
|
kfree(fid);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_get(struct mlxsw_sp *mlxsw_sp, u16 vid)
|
|
{
|
|
return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021Q, &vid);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_get(struct mlxsw_sp *mlxsw_sp,
|
|
int br_ifindex)
|
|
{
|
|
return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_8021D, &br_ifindex);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_8021q_lookup(struct mlxsw_sp *mlxsw_sp,
|
|
u16 vid)
|
|
{
|
|
return mlxsw_sp_fid_lookup(mlxsw_sp, MLXSW_SP_FID_TYPE_8021Q, &vid);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_8021d_lookup(struct mlxsw_sp *mlxsw_sp,
|
|
int br_ifindex)
|
|
{
|
|
return mlxsw_sp_fid_lookup(mlxsw_sp, MLXSW_SP_FID_TYPE_8021D,
|
|
&br_ifindex);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_rfid_get(struct mlxsw_sp *mlxsw_sp,
|
|
u16 rif_index)
|
|
{
|
|
return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_RFID, &rif_index);
|
|
}
|
|
|
|
struct mlxsw_sp_fid *mlxsw_sp_fid_dummy_get(struct mlxsw_sp *mlxsw_sp)
|
|
{
|
|
return mlxsw_sp_fid_get(mlxsw_sp, MLXSW_SP_FID_TYPE_DUMMY, NULL);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_flood_table_init(struct mlxsw_sp_fid_family *fid_family,
|
|
const struct mlxsw_sp_flood_table *flood_table)
|
|
{
|
|
enum mlxsw_sp_flood_type packet_type = flood_table->packet_type;
|
|
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
|
|
const int *sfgc_packet_types;
|
|
u16 num_fids, mid_base;
|
|
int err, i;
|
|
|
|
mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, 0);
|
|
num_fids = mlxsw_sp_fid_family_num_fids(fid_family);
|
|
err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, mid_base, num_fids);
|
|
if (err)
|
|
return err;
|
|
|
|
sfgc_packet_types = mlxsw_sp_packet_type_sfgc_types[packet_type];
|
|
for (i = 0; i < MLXSW_REG_SFGC_TYPE_MAX; i++) {
|
|
char sfgc_pl[MLXSW_REG_SFGC_LEN];
|
|
|
|
if (!sfgc_packet_types[i])
|
|
continue;
|
|
|
|
mlxsw_reg_sfgc_pack(sfgc_pl, i, fid_family->bridge_type,
|
|
flood_table->table_type, 0, mid_base);
|
|
|
|
err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfgc), sfgc_pl);
|
|
if (err)
|
|
goto err_reg_write;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_reg_write:
|
|
mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base, num_fids);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_flood_table_fini(struct mlxsw_sp_fid_family *fid_family,
|
|
const struct mlxsw_sp_flood_table *flood_table)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
|
|
u16 num_fids, mid_base;
|
|
|
|
mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, 0);
|
|
num_fids = mlxsw_sp_fid_family_num_fids(fid_family);
|
|
mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base, num_fids);
|
|
}
|
|
|
|
static int
|
|
mlxsw_sp_fid_flood_tables_init(struct mlxsw_sp_fid_family *fid_family)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < fid_family->nr_flood_tables; i++) {
|
|
const struct mlxsw_sp_flood_table *flood_table;
|
|
int err;
|
|
|
|
flood_table = &fid_family->flood_tables[i];
|
|
err = mlxsw_sp_fid_flood_table_init(fid_family, flood_table);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_flood_tables_fini(struct mlxsw_sp_fid_family *fid_family)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < fid_family->nr_flood_tables; i++) {
|
|
const struct mlxsw_sp_flood_table *flood_table;
|
|
|
|
flood_table = &fid_family->flood_tables[i];
|
|
mlxsw_sp_fid_flood_table_fini(fid_family, flood_table);
|
|
}
|
|
}
|
|
|
|
static int mlxsw_sp_fid_family_register(struct mlxsw_sp *mlxsw_sp,
|
|
const struct mlxsw_sp_fid_family *tmpl)
|
|
{
|
|
u16 nr_fids = tmpl->end_index - tmpl->start_index + 1;
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
int err;
|
|
|
|
fid_family = kmemdup(tmpl, sizeof(*fid_family), GFP_KERNEL);
|
|
if (!fid_family)
|
|
return -ENOMEM;
|
|
|
|
fid_family->mlxsw_sp = mlxsw_sp;
|
|
INIT_LIST_HEAD(&fid_family->fids_list);
|
|
fid_family->fids_bitmap = bitmap_zalloc(nr_fids, GFP_KERNEL);
|
|
if (!fid_family->fids_bitmap) {
|
|
err = -ENOMEM;
|
|
goto err_alloc_fids_bitmap;
|
|
}
|
|
|
|
if (fid_family->flood_tables) {
|
|
err = mlxsw_sp_fid_flood_tables_init(fid_family);
|
|
if (err)
|
|
goto err_fid_flood_tables_init;
|
|
}
|
|
|
|
mlxsw_sp->fid_core->fid_family_arr[tmpl->type] = fid_family;
|
|
|
|
return 0;
|
|
|
|
err_fid_flood_tables_init:
|
|
bitmap_free(fid_family->fids_bitmap);
|
|
err_alloc_fids_bitmap:
|
|
kfree(fid_family);
|
|
return err;
|
|
}
|
|
|
|
static void
|
|
mlxsw_sp_fid_family_unregister(struct mlxsw_sp *mlxsw_sp,
|
|
struct mlxsw_sp_fid_family *fid_family)
|
|
{
|
|
mlxsw_sp->fid_core->fid_family_arr[fid_family->type] = NULL;
|
|
|
|
if (fid_family->flood_tables)
|
|
mlxsw_sp_fid_flood_tables_fini(fid_family);
|
|
|
|
bitmap_free(fid_family->fids_bitmap);
|
|
WARN_ON_ONCE(!list_empty(&fid_family->fids_list));
|
|
kfree(fid_family);
|
|
}
|
|
|
|
int mlxsw_sp_port_fids_init(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
/* Track number of FIDs configured on the port with mapping type
|
|
* PORT_VID_TO_FID, so that we know when to transition the port
|
|
* back to non-virtual (VLAN) mode.
|
|
*/
|
|
mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0;
|
|
|
|
return mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
|
|
}
|
|
|
|
void mlxsw_sp_port_fids_fini(struct mlxsw_sp_port *mlxsw_sp_port)
|
|
{
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
|
mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0;
|
|
}
|
|
|
|
int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp)
|
|
{
|
|
unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core);
|
|
struct mlxsw_sp_fid_core *fid_core;
|
|
int err, i;
|
|
|
|
fid_core = kzalloc(sizeof(*mlxsw_sp->fid_core), GFP_KERNEL);
|
|
if (!fid_core)
|
|
return -ENOMEM;
|
|
mlxsw_sp->fid_core = fid_core;
|
|
|
|
err = rhashtable_init(&fid_core->fid_ht, &mlxsw_sp_fid_ht_params);
|
|
if (err)
|
|
goto err_rhashtable_fid_init;
|
|
|
|
err = rhashtable_init(&fid_core->vni_ht, &mlxsw_sp_fid_vni_ht_params);
|
|
if (err)
|
|
goto err_rhashtable_vni_init;
|
|
|
|
fid_core->port_fid_mappings = kcalloc(max_ports, sizeof(unsigned int),
|
|
GFP_KERNEL);
|
|
if (!fid_core->port_fid_mappings) {
|
|
err = -ENOMEM;
|
|
goto err_alloc_port_fid_mappings;
|
|
}
|
|
|
|
for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) {
|
|
err = mlxsw_sp_fid_family_register(mlxsw_sp,
|
|
mlxsw_sp->fid_family_arr[i]);
|
|
|
|
if (err)
|
|
goto err_fid_ops_register;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err_fid_ops_register:
|
|
for (i--; i >= 0; i--) {
|
|
struct mlxsw_sp_fid_family *fid_family;
|
|
|
|
fid_family = fid_core->fid_family_arr[i];
|
|
mlxsw_sp_fid_family_unregister(mlxsw_sp, fid_family);
|
|
}
|
|
kfree(fid_core->port_fid_mappings);
|
|
err_alloc_port_fid_mappings:
|
|
rhashtable_destroy(&fid_core->vni_ht);
|
|
err_rhashtable_vni_init:
|
|
rhashtable_destroy(&fid_core->fid_ht);
|
|
err_rhashtable_fid_init:
|
|
kfree(fid_core);
|
|
return err;
|
|
}
|
|
|
|
void mlxsw_sp_fids_fini(struct mlxsw_sp *mlxsw_sp)
|
|
{
|
|
struct mlxsw_sp_fid_core *fid_core = mlxsw_sp->fid_core;
|
|
int i;
|
|
|
|
for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++)
|
|
mlxsw_sp_fid_family_unregister(mlxsw_sp,
|
|
fid_core->fid_family_arr[i]);
|
|
kfree(fid_core->port_fid_mappings);
|
|
rhashtable_destroy(&fid_core->vni_ht);
|
|
rhashtable_destroy(&fid_core->fid_ht);
|
|
kfree(fid_core);
|
|
}
|