// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */ #include #include #include #include #include #include #include #include #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, }; 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_family_pgt_size(const struct mlxsw_sp_fid_family *fid_family) { u16 num_fids = mlxsw_sp_fid_family_num_fids(fid_family); return num_fids * fid_family->nr_flood_tables; } 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) 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, .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, .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, .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, .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 mid_base; int err, i; mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, 0); 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 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; if (!fid_family->nr_flood_tables) return 0; pgt_size = mlxsw_sp_fid_family_pgt_size(fid_family); err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, &fid_family->pgt_base, pgt_size); if (err) return err; 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]; err = mlxsw_sp_fid_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; if (!fid_family->nr_flood_tables) return; pgt_size = mlxsw_sp_fid_family_pgt_size(fid_family); 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_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); }