Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/arch/arm/boot/dts/nxp/imx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 15 kB image not shown  

Quelle  spectrum_fid.c   Sprache: unbekannt

 
// 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 "spectrum_router.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; /* For flood_mode!=CFF. */
 int table_index;
};

struct mlxsw_sp_fid_ops {
 int (*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);
 int (*flood_table_init)(struct mlxsw_sp_fid_family *fid_family,
    const struct mlxsw_sp_flood_table *flood_table);
 int (*pgt_size)(const struct mlxsw_sp_fid_family *fid_family,
   u16 *p_pgt_size);
 u16 (*fid_mid)(const struct mlxsw_sp_fid *fid,
         const struct mlxsw_sp_flood_table *flood_table);
 void (*fid_pack)(char *sfmr_pl, const struct mlxsw_sp_fid *fid,
    enum mlxsw_reg_sfmr_op op);

 /* These are specific to RFID families and we assume are only
 * implemented by RFID families, if at all.
 */

 int (*fid_port_init)(const struct mlxsw_sp_fid_family *fid_family,
        const struct mlxsw_sp_port *mlxsw_sp_port);
 void (*fid_port_fini)(const struct mlxsw_sp_fid_family *fid_family,
         const struct mlxsw_sp_port *mlxsw_sp_port);
};

enum mlxsw_sp_fid_flood_profile_id {
 MLXSW_SP_FID_FLOOD_PROFILE_ID_BRIDGE = 1,
 MLXSW_SP_FID_FLOOD_PROFILE_ID_RSP,
 MLXSW_SP_FID_FLOOD_PROFILE_ID_NVE,
};

struct mlxsw_sp_fid_flood_profile {
 const struct mlxsw_sp_flood_table *flood_tables;
 int nr_flood_tables;
 const enum mlxsw_sp_fid_flood_profile_id profile_id; /* For CFF mode. */
};

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_fid_flood_profile *flood_profile;
 enum mlxsw_sp_rif_type rif_type;
 const struct mlxsw_sp_fid_ops *ops;
 struct mlxsw_sp *mlxsw_sp;
 bool flood_rsp; /* For flood_mode!=CFF. */
 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_sfgc_not_uc_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,
 [MLXSW_REG_SFGC_TYPE_UNREGISTERED_MULTICAST_IPV4] = 1,
};

static const int mlxsw_sp_sfgc_any_packet_types[MLXSW_REG_SFGC_TYPE_MAX] = {
 [MLXSW_REG_SFGC_TYPE_UNKNOWN_UNICAST]   = 1,
 [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,
 [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,
 [MLXSW_SP_FLOOD_TYPE_NOT_UC] = mlxsw_sp_sfgc_not_uc_packet_types,
 [MLXSW_SP_FLOOD_TYPE_ANY] = mlxsw_sp_sfgc_any_packet_types,
};

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->flood_profile->nr_flood_tables; i++) {
  const struct mlxsw_sp_flood_table *flood_table;

  flood_table = &fid_family->flood_profile->flood_tables[i];
  if (flood_table->packet_type != packet_type)
   continue;
  return flood_table;
 }

 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 int
mlxsw_sp_fid_8021d_pgt_size(const struct mlxsw_sp_fid_family *fid_family,
       u16 *p_pgt_size)
{
 u16 num_fids = mlxsw_sp_fid_family_num_fids(fid_family);

 *p_pgt_size = num_fids * fid_family->flood_profile->nr_flood_tables;
 return 0;
}

static unsigned int mlxsw_sp_fid_rfid_port_offset_cff(unsigned int local_port)
{
 /* Port 0 is the CPU port. Since we never create RIFs based off that
 * port, we don't need to count it.
 */

 return WARN_ON_ONCE(!local_port) ? 0 : local_port - 1;
}

static int
mlxsw_sp_fid_rfid_pgt_size_cff(const struct mlxsw_sp_fid_family *fid_family,
          u16 *p_pgt_size)
{
 struct mlxsw_core *core = fid_family->mlxsw_sp->core;
 unsigned int max_ports;
 u16 pgt_size;
 u16 max_lags;
 int err;

 max_ports = mlxsw_core_max_ports(core);

 err = mlxsw_core_max_lag(core, &max_lags);
 if (err)
  return err;

 pgt_size = (mlxsw_sp_fid_rfid_port_offset_cff(max_ports) + max_lags) *
     fid_family->flood_profile->nr_flood_tables;
 *p_pgt_size = pgt_size;
 return 0;
}

static u16
mlxsw_sp_fid_pgt_base_ctl(const struct mlxsw_sp_fid_family *fid_family,
     const struct mlxsw_sp_flood_table *flood_table)
{
 u16 num_fids;

 num_fids = mlxsw_sp_fid_family_num_fids(fid_family);
 return fid_family->pgt_base + num_fids * flood_table->table_index;
}

static u16
mlxsw_sp_fid_fid_mid_ctl(const struct mlxsw_sp_fid *fid,
    const struct mlxsw_sp_flood_table *flood_table)
{
 return mlxsw_sp_fid_pgt_base_ctl(fid->fid_family, flood_table) +
        fid->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_profile))
  return -EINVAL;

 flood_table = mlxsw_sp_fid_flood_table_lookup(fid, packet_type);
 if (!flood_table)
  return -ESRCH;

 mid_index = fid_family->ops->fid_mid(fid, flood_table);
 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 int 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;
 return 0;
}

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 void mlxsw_sp_fid_pack(char *sfmr_pl,
         const struct mlxsw_sp_fid *fid,
         enum mlxsw_reg_sfmr_op op)
{
 u16 smpe;

 smpe = fid->fid_family->smpe_index_valid ? fid->fid_index : 0;

 mlxsw_reg_sfmr_pack(sfmr_pl, op, fid->fid_index,
       fid->fid_family->smpe_index_valid, smpe);
}

static void mlxsw_sp_fid_pack_ctl(char *sfmr_pl,
      const struct mlxsw_sp_fid *fid,
      enum mlxsw_reg_sfmr_op op)
{
 mlxsw_sp_fid_pack(sfmr_pl, fid, op);
 mlxsw_reg_sfmr_fid_offset_set(sfmr_pl, fid->fid_offset);
 mlxsw_reg_sfmr_flood_rsp_set(sfmr_pl, fid->fid_family->flood_rsp);
 mlxsw_reg_sfmr_flood_bridge_type_set(sfmr_pl,
          fid->fid_family->bridge_type);
}

static u16
mlxsw_sp_fid_off_pgt_base_cff(const struct mlxsw_sp_fid_family *fid_family,
         u16 fid_offset)
{
 return fid_family->pgt_base +
  fid_offset * fid_family->flood_profile->nr_flood_tables;
}

static u16 mlxsw_sp_fid_pgt_base_cff(const struct mlxsw_sp_fid *fid)
{
 return mlxsw_sp_fid_off_pgt_base_cff(fid->fid_family, fid->fid_offset);
}

static void mlxsw_sp_fid_fid_pack_cff(char *sfmr_pl,
          const struct mlxsw_sp_fid *fid,
          enum mlxsw_reg_sfmr_op op)
{
 struct mlxsw_sp_fid_family *fid_family = fid->fid_family;
 u16 pgt_base = mlxsw_sp_fid_pgt_base_cff(fid);

 mlxsw_sp_fid_pack(sfmr_pl, fid, op);
 mlxsw_reg_sfmr_cff_mid_base_set(sfmr_pl, pgt_base);
 mlxsw_reg_sfmr_cff_prf_id_set(sfmr_pl,
          fid_family->flood_profile->profile_id);
 mlxsw_reg_sfmr_nve_flood_prf_id_set(sfmr_pl,
         MLXSW_SP_FID_FLOOD_PROFILE_ID_NVE);
}

static u16 mlxsw_sp_fid_rfid_fid_offset_cff(struct mlxsw_sp *mlxsw_sp,
         u16 port_lag_id, bool is_lag)
{
 u16 max_ports = mlxsw_core_max_ports(mlxsw_sp->core);

 if (is_lag)
  return mlxsw_sp_fid_rfid_port_offset_cff(max_ports) +
         port_lag_id;
 else
  return mlxsw_sp_fid_rfid_port_offset_cff(port_lag_id);
}

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];

 fid->fid_family->ops->fid_pack(sfmr_pl, fid,
           mlxsw_sp_sfmr_op(valid));
 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];

 fid->fid_family->ops->fid_pack(sfmr_pl, fid,
           MLXSW_REG_SFMR_OP_CREATE_FID);

 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, false0);
 }

 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, false0);
 }

 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 int 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;
 return 0;
}

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 int
mlxsw_sp_fid_flood_table_init_ctl(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 mid_base;
 int err, i;

 mid_base = mlxsw_sp_fid_pgt_base_ctl(fid_family, flood_table);

 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)
   return err;
 }

 return 0;
}

static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops_ctl = {
 .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,
 .flood_table_init = mlxsw_sp_fid_flood_table_init_ctl,
 .pgt_size  = mlxsw_sp_fid_8021d_pgt_size,
 .fid_mid  = mlxsw_sp_fid_fid_mid_ctl,
 .fid_pack  = mlxsw_sp_fid_pack_ctl,
};

static u16
mlxsw_sp_fid_fid_mid_cff(const struct mlxsw_sp_fid *fid,
    const struct mlxsw_sp_flood_table *flood_table)
{
 return mlxsw_sp_fid_pgt_base_cff(fid) + flood_table->table_index;
}

static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops_cff = {
 .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,
 .pgt_size  = mlxsw_sp_fid_8021d_pgt_size,
 .fid_mid  = mlxsw_sp_fid_fid_mid_cff,
 .fid_pack  = mlxsw_sp_fid_fid_pack_cff,
};

#define MLXSW_SP_FID_8021Q_MAX (VLAN_N_VID - 2)
#define MLXSW_SP_FID_RFID_MAX (11 * 1024)

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 const
struct mlxsw_sp_fid_flood_profile mlxsw_sp_fid_8021d_flood_profile = {
 .flood_tables  = mlxsw_sp_fid_8021d_flood_tables,
 .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables),
 .profile_id  = MLXSW_SP_FID_FLOOD_PROFILE_ID_BRIDGE,
};

static const struct mlxsw_sp_flood_table mlxsw_sp_fid_rsp_flood_tables_cff[] = {
 {
  .packet_type = MLXSW_SP_FLOOD_TYPE_UC,
  .table_index = 0,
 },
 {
  .packet_type = MLXSW_SP_FLOOD_TYPE_NOT_UC,
  .table_index = 1,
 },
};

static const
struct mlxsw_sp_fid_flood_profile mlxsw_sp_fid_rsp_flood_profile_cff = {
 .flood_tables  = mlxsw_sp_fid_rsp_flood_tables_cff,
 .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_rsp_flood_tables_cff),
 .profile_id  = MLXSW_SP_FID_FLOOD_PROFILE_ID_RSP,
};

static const struct mlxsw_sp_flood_table mlxsw_sp_fid_nve_flood_tables_cff[] = {
 {
  .packet_type = MLXSW_SP_FLOOD_TYPE_ANY,
  .table_index = 0,
 },
};

static const
struct mlxsw_sp_fid_flood_profile mlxsw_sp_fid_nve_flood_profile_cff = {
 .flood_tables  = mlxsw_sp_fid_nve_flood_tables_cff,
 .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_nve_flood_tables_cff),
 .profile_id  = MLXSW_SP_FID_FLOOD_PROFILE_ID_NVE,
};

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 int mlxsw_sp_fid_rfid_setup_ctl(struct mlxsw_sp_fid *fid,
           const void *arg)
{
 /* In controlled mode, the FW takes care of FID placement. */
 fid->fid_offset = 0;
 return 0;
}

static int mlxsw_sp_fid_rfid_setup_cff(struct mlxsw_sp_fid *fid,
           const void *arg)
{
 struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp;
 u16 rif_index = *(const u16 *)arg;
 struct mlxsw_sp_rif *rif;
 bool is_lag;
 u16 port;
 int err;

 rif = mlxsw_sp_rif_by_index(mlxsw_sp, rif_index);
 if (!rif)
  return -ENOENT;

 err = mlxsw_sp_rif_subport_port(rif, &port, &is_lag);
 if (err)
  return err;

 fid->fid_offset = mlxsw_sp_fid_rfid_fid_offset_cff(mlxsw_sp, port,
          is_lag);
 return 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_ctl = {
 .setup   = mlxsw_sp_fid_rfid_setup_ctl,
 .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,
 .fid_pack  = mlxsw_sp_fid_pack_ctl,
};

static int
mlxsw_sp_fid_rfid_port_add_cff(struct mlxsw_sp *mlxsw_sp,
          const struct mlxsw_sp_flood_table *flood_table,
          u16 pgt_addr, u16 smpe, unsigned int local_port)
{
 int err;

 err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
       local_port, true);
 if (err)
  return err;

 if (flood_table->packet_type == MLXSW_SP_FLOOD_TYPE_NOT_UC) {
  u16 router_port = mlxsw_sp_router_port(mlxsw_sp);

  err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
        router_port, true);
  if (err)
   goto err_entry_port_set;
 }

 return 0;

err_entry_port_set:
 mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe, local_port,
        false);
 return err;
}

static void
mlxsw_sp_fid_rfid_port_del_cff(struct mlxsw_sp *mlxsw_sp,
          const struct mlxsw_sp_flood_table *flood_table,
          u16 pgt_addr, u16 smpe, u16 local_port)
{
 if (flood_table->packet_type == MLXSW_SP_FLOOD_TYPE_NOT_UC) {
  u16 router_port = mlxsw_sp_router_port(mlxsw_sp);

  mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe,
         router_port, false);
 }
 mlxsw_sp_pgt_entry_port_set(mlxsw_sp, pgt_addr, smpe, local_port,
        false);
}

static int
mlxsw_sp_fid_rfid_port_memb_ft_cff(const struct mlxsw_sp_fid_family *fid_family,
       const struct mlxsw_sp_flood_table *flood_table,
       const struct mlxsw_sp_port *mlxsw_sp_port,
       bool member)
{
 struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
 u16 local_port = mlxsw_sp_port->local_port;
 u16 fid_pgt_base;
 u16 fid_offset;
 u16 pgt_addr;
 u16 smpe;
 u16 port;

 /* In-PGT SMPE is only valid on Spectrum-1, CFF only on Spectrum>1. */
 smpe = 0;

 port = mlxsw_sp_port->lagged ? mlxsw_sp_port->lag_id : local_port;
 fid_offset = mlxsw_sp_fid_rfid_fid_offset_cff(mlxsw_sp, port,
            mlxsw_sp_port->lagged);
 fid_pgt_base = mlxsw_sp_fid_off_pgt_base_cff(fid_family, fid_offset);
 pgt_addr = fid_pgt_base + flood_table->table_index;

 if (member)
  return mlxsw_sp_fid_rfid_port_add_cff(mlxsw_sp, flood_table,
            pgt_addr, smpe,
            local_port);

 mlxsw_sp_fid_rfid_port_del_cff(mlxsw_sp, flood_table, pgt_addr, smpe,
           local_port);
 return 0;
}

static int
mlxsw_sp_fid_rfid_port_memb_cff(const struct mlxsw_sp_fid_family *fid_family,
    const struct mlxsw_sp_port *mlxsw_sp_port,
    bool member)
{
 int i;

 for (i = 0; i < fid_family->flood_profile->nr_flood_tables; i++) {
  const struct mlxsw_sp_flood_table *flood_table =
   &fid_family->flood_profile->flood_tables[i];
  int err;

  err = mlxsw_sp_fid_rfid_port_memb_ft_cff(fid_family,
        flood_table,
        mlxsw_sp_port, member);
  if (err)
   return err;
 }

 return 0;
}

static int
mlxsw_sp_fid_rfid_port_init_cff(const struct mlxsw_sp_fid_family *fid_family,
    const struct mlxsw_sp_port *mlxsw_sp_port)
{
 return mlxsw_sp_fid_rfid_port_memb_cff(fid_family, mlxsw_sp_port, true);
}

static void
mlxsw_sp_fid_rfid_port_fini_cff(const struct mlxsw_sp_fid_family *fid_family,
    const struct mlxsw_sp_port *mlxsw_sp_port)
{
 mlxsw_sp_fid_rfid_port_memb_cff(fid_family, mlxsw_sp_port, false);
}

static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_rfid_ops_cff = {
 .setup   = mlxsw_sp_fid_rfid_setup_cff,
 .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,
 .pgt_size  = mlxsw_sp_fid_rfid_pgt_size_cff,
 .fid_port_init  = mlxsw_sp_fid_rfid_port_init_cff,
 .fid_port_fini  = mlxsw_sp_fid_rfid_port_fini_cff,
 .fid_mid  = mlxsw_sp_fid_fid_mid_cff,
 .fid_pack  = mlxsw_sp_fid_fid_pack_cff,
};

static int mlxsw_sp_fid_dummy_setup(struct mlxsw_sp_fid *fid, const void *arg)
{
 fid->fid_offset = 0;
 return 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,
 .fid_pack  = mlxsw_sp_fid_pack,
};

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_ctl = {
 .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,
 .flood_table_init = mlxsw_sp_fid_flood_table_init_ctl,
 .pgt_size  = mlxsw_sp_fid_8021d_pgt_size,
 .fid_mid  = mlxsw_sp_fid_fid_mid_ctl,
 .fid_pack  = mlxsw_sp_fid_pack_ctl,
};

static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_ops_cff = {
 .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,
 .pgt_size  = mlxsw_sp_fid_8021d_pgt_size,
 .fid_mid  = mlxsw_sp_fid_fid_mid_cff,
 .fid_pack  = mlxsw_sp_fid_fid_pack_cff,
};

/* 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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_VLAN,
 .ops   = &mlxsw_sp_fid_8021q_ops_ctl,
 .flood_rsp              = false,
 .bridge_type            = MLXSW_REG_BRIDGE_TYPE_0,
 .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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_FID,
 .ops   = &mlxsw_sp_fid_8021d_ops_ctl,
 .bridge_type            = MLXSW_REG_BRIDGE_TYPE_1,
 .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_ctl = {
 .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_ctl,
 .flood_rsp              = true,
 .smpe_index_valid       = false,
};

static 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_ctl,
};

static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021q_family_ctl = {
 .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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_VLAN,
 .ops   = &mlxsw_sp_fid_8021q_ops_ctl,
 .flood_rsp              = false,
 .bridge_type            = MLXSW_REG_BRIDGE_TYPE_0,
 .smpe_index_valid = true,
};

static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021d_family_ctl = {
 .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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_FID,
 .ops   = &mlxsw_sp_fid_8021d_ops_ctl,
 .bridge_type            = MLXSW_REG_BRIDGE_TYPE_1,
 .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,
};

static const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr_ctl[] = {
 [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp2_fid_8021q_family_ctl,
 [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp2_fid_8021d_family_ctl,
 [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp2_fid_dummy_family,
 [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family_ctl,
};

static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021q_family_cff = {
 .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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_VLAN,
 .ops   = &mlxsw_sp_fid_8021q_ops_cff,
 .smpe_index_valid = true,
};

static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021d_family_cff = {
 .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_profile  = &mlxsw_sp_fid_8021d_flood_profile,
 .rif_type  = MLXSW_SP_RIF_TYPE_FID,
 .ops   = &mlxsw_sp_fid_8021d_ops_cff,
 .smpe_index_valid = true,
};

static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family_cff = {
 .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,
 .flood_profile  = &mlxsw_sp_fid_rsp_flood_profile_cff,
 .rif_type  = MLXSW_SP_RIF_TYPE_SUBPORT,
 .ops   = &mlxsw_sp_fid_rfid_ops_cff,
 .smpe_index_valid = false,
};

static const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr_cff[] = {
 [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp2_fid_8021q_family_cff,
 [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp2_fid_8021d_family_cff,
 [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp2_fid_dummy_family,
 [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family_cff,
};

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);

 err = fid->fid_family->ops->setup(fid, arg);
 if (err)
  goto err_setup;

 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:
err_setup:
 __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_tables_init(struct mlxsw_sp_fid_family *fid_family)
{
 struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
 u16 pgt_size;
 int err;
 int i;

 err = fid_family->ops->pgt_size(fid_family, &pgt_size);
 if (err)
  return err;

 err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, &fid_family->pgt_base,
        pgt_size);
 if (err)
  return err;

 if (!fid_family->flood_profile)
  return 0;

 for (i = 0; i < fid_family->flood_profile->nr_flood_tables; i++) {
  const struct mlxsw_sp_flood_table *flood_table;

  flood_table = &fid_family->flood_profile->flood_tables[i];
  if (fid_family->ops->flood_table_init) {
   err = fid_family->ops->flood_table_init(fid_family,
        flood_table);
   if (err)
    goto err_flood_table_init;
  }
 }

 return 0;

err_flood_table_init:
 mlxsw_sp_pgt_mid_free_range(mlxsw_sp, fid_family->pgt_base, pgt_size);
 return err;
}

static void
mlxsw_sp_fid_flood_tables_fini(struct mlxsw_sp_fid_family *fid_family)
{
 struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp;
 u16 pgt_size;
 int err;

 err = fid_family->ops->pgt_size(fid_family, &pgt_size);
 if (WARN_ON_ONCE(err))
  return;

 mlxsw_sp_pgt_mid_free_range(mlxsw_sp, fid_family->pgt_base, pgt_size);
}

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_profile) {
  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_profile)
  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);
}

static int mlxsw_sp_fid_port_init(const struct mlxsw_sp_port *mlxsw_sp_port)
{
 const enum mlxsw_sp_fid_type type_rfid = MLXSW_SP_FID_TYPE_RFID;
 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 struct mlxsw_sp_fid_family *rfid_family;

 rfid_family = mlxsw_sp->fid_core->fid_family_arr[type_rfid];
 if (rfid_family->ops->fid_port_init)
  return rfid_family->ops->fid_port_init(rfid_family,
             mlxsw_sp_port);
 return 0;
}

static void mlxsw_sp_fid_port_fini(const struct mlxsw_sp_port *mlxsw_sp_port)
{
 const enum mlxsw_sp_fid_type type_rfid = MLXSW_SP_FID_TYPE_RFID;
 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 struct mlxsw_sp_fid_family *rfid_family;

 rfid_family = mlxsw_sp->fid_core->fid_family_arr[type_rfid];
 if (rfid_family->ops->fid_port_fini)
  rfid_family->ops->fid_port_fini(rfid_family, mlxsw_sp_port);
}

int mlxsw_sp_port_fids_init(struct mlxsw_sp_port *mlxsw_sp_port)
{
 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
 int err;

 /* 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;

 err = mlxsw_sp_fid_port_init(mlxsw_sp_port);
 if (err)
  return err;

 err = mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false);
 if (err)
  goto err_vp_mode_set;

 return 0;

err_vp_mode_set:
 mlxsw_sp_fid_port_fini(mlxsw_sp_port);
 return err;
}

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_port_fini(mlxsw_sp_port);
 mlxsw_sp->fid_core->port_fid_mappings[mlxsw_sp_port->local_port] = 0;
}

int mlxsw_sp_fid_port_join_lag(const struct mlxsw_sp_port *mlxsw_sp_port)
{
 return mlxsw_sp_fid_port_init(mlxsw_sp_port);
}

void mlxsw_sp_fid_port_leave_lag(const struct mlxsw_sp_port *mlxsw_sp_port)
{
 mlxsw_sp_fid_port_fini(mlxsw_sp_port);
}

static int
mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp,
     const struct mlxsw_sp_fid_family *fid_family_arr[])
{
 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, 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;
}

static 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);
}

static int mlxsw_sp1_fids_init(struct mlxsw_sp *mlxsw_sp)
{
 return mlxsw_sp_fids_init(mlxsw_sp, mlxsw_sp1_fid_family_arr);
}

const struct mlxsw_sp_fid_core_ops mlxsw_sp1_fid_core_ops = {
 .init = mlxsw_sp1_fids_init,
 .fini = mlxsw_sp_fids_fini,
};

static int mlxsw_sp_fid_check_flood_profile_id(struct mlxsw_sp *mlxsw_sp,
            int profile_id)
{
 u32 max_profiles;

 if (!MLXSW_CORE_RES_VALID(mlxsw_sp->core, MAX_NVE_FLOOD_PRF))
  return -EIO;

 max_profiles = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_NVE_FLOOD_PRF);
 if (WARN_ON_ONCE(!profile_id) ||
     WARN_ON_ONCE(profile_id >= max_profiles))
  return -EINVAL;

 return 0;
}

static int
mlxsw_sp2_fids_init_flood_table(struct mlxsw_sp *mlxsw_sp,
    enum mlxsw_sp_fid_flood_profile_id profile_id,
    const struct mlxsw_sp_flood_table *flood_table)
{
 enum mlxsw_sp_flood_type packet_type = flood_table->packet_type;
 const int *sfgc_packet_types;
 int err;
 int i;

 sfgc_packet_types = mlxsw_sp_packet_type_sfgc_types[packet_type];
 for (i = 0; i < MLXSW_REG_SFGC_TYPE_MAX; i++) {
  char sffp_pl[MLXSW_REG_SFFP_LEN];

  if (!sfgc_packet_types[i])
   continue;

  mlxsw_reg_sffp_pack(sffp_pl, profile_id, i,
        flood_table->table_index);
  err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sffp), sffp_pl);
  if (err)
   return err;
 }

 return 0;
}

static int
mlxsw_sp2_fids_init_flood_profile(struct mlxsw_sp *mlxsw_sp,
      const struct mlxsw_sp_fid_flood_profile *
     flood_profile)
{
 int err;
 int i;

 err = mlxsw_sp_fid_check_flood_profile_id(mlxsw_sp,
        flood_profile->profile_id);
 if (err)
  return err;

 for (i = 0; i < flood_profile->nr_flood_tables; i++) {
  const struct mlxsw_sp_flood_table *flood_table;

  flood_table = &flood_profile->flood_tables[i];
  err = mlxsw_sp2_fids_init_flood_table(mlxsw_sp,
            flood_profile->profile_id,
            flood_table);
  if (err)
   return err;
 }

 return 0;
}

static const
struct mlxsw_sp_fid_flood_profile *mlxsw_sp_fid_flood_profiles[] = {
 &mlxsw_sp_fid_8021d_flood_profile,
 &mlxsw_sp_fid_rsp_flood_profile_cff,
 &mlxsw_sp_fid_nve_flood_profile_cff,
};

static int
mlxsw_sp2_fids_init_flood_profiles(struct mlxsw_sp *mlxsw_sp)
{
 int err;
 int i;

 for (i = 0; i < ARRAY_SIZE(mlxsw_sp_fid_flood_profiles); i++) {
  const struct mlxsw_sp_fid_flood_profile *flood_profile;

  flood_profile = mlxsw_sp_fid_flood_profiles[i];
  err = mlxsw_sp2_fids_init_flood_profile(mlxsw_sp,
       flood_profile);
  if (err)
   return err;
 }

 return 0;
}

static int mlxsw_sp2_fids_init_ctl(struct mlxsw_sp *mlxsw_sp)
{
 return mlxsw_sp_fids_init(mlxsw_sp, mlxsw_sp2_fid_family_arr_ctl);
}

static int mlxsw_sp2_fids_init_cff(struct mlxsw_sp *mlxsw_sp)
{
 int err;

 err = mlxsw_sp2_fids_init_flood_profiles(mlxsw_sp);
 if (err)
  return err;

 return mlxsw_sp_fids_init(mlxsw_sp, mlxsw_sp2_fid_family_arr_cff);
}

static int mlxsw_sp2_fids_init(struct mlxsw_sp *mlxsw_sp)
{
 switch (mlxsw_core_flood_mode(mlxsw_sp->core)) {
 case MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CONTROLLED:
  return mlxsw_sp2_fids_init_ctl(mlxsw_sp);
 case MLXSW_CMD_MBOX_CONFIG_PROFILE_FLOOD_MODE_CFF:
  return mlxsw_sp2_fids_init_cff(mlxsw_sp);
 default:
  WARN_ON_ONCE(1);
  return -EINVAL;
 }
}

const struct mlxsw_sp_fid_core_ops mlxsw_sp2_fid_core_ops = {
 .init = mlxsw_sp2_fids_init,
 .fini = mlxsw_sp_fids_fini,
};

Messung V0.5 in Prozent
C=96 H=92 G=93

[Dauer der Verarbeitung: 0.43 Sekunden, vorverarbeitet 2026-06-07]