diff options
Diffstat (limited to 'drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c')
-rw-r--r-- | drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c | 1084 |
1 files changed, 888 insertions, 196 deletions
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c index 004c42274e48..045a24cacfa5 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_fid.c @@ -22,11 +22,18 @@ struct mlxsw_sp_fid_core { 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; @@ -37,6 +44,7 @@ struct mlxsw_sp_fid { 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 { @@ -63,7 +71,6 @@ static const struct rhashtable_params mlxsw_sp_fid_vni_ht_params = { struct mlxsw_sp_flood_table { enum mlxsw_sp_flood_type packet_type; - enum mlxsw_reg_sfgc_bridge_type bridge_type; enum mlxsw_flood_table_type table_type; int table_index; }; @@ -76,18 +83,18 @@ struct mlxsw_sp_fid_ops { u16 *p_fid_index); bool (*compare)(const struct mlxsw_sp_fid *fid, const void *arg); - u16 (*flood_index)(const struct mlxsw_sp_fid *fid); 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, __be32 vni); + 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, - u32 nve_flood_index); + 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 { @@ -102,7 +109,10 @@ struct mlxsw_sp_fid_family { enum mlxsw_sp_rif_type rif_type; const struct mlxsw_sp_fid_ops *ops; struct mlxsw_sp *mlxsw_sp; - u8 lag_vid_valid:1; + 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] = { @@ -137,11 +147,6 @@ bool mlxsw_sp_fid_is_dummy(struct mlxsw_sp *mlxsw_sp, u16 fid_index) return fid_family->start_index == fid_index; } -bool mlxsw_sp_fid_lag_vid_valid(const struct mlxsw_sp_fid *fid) -{ - return fid->fid_family->lag_vid_valid; -} - struct mlxsw_sp_fid *mlxsw_sp_fid_lookup_by_index(struct mlxsw_sp *mlxsw_sp, u16 fid_index) { @@ -206,17 +211,20 @@ int mlxsw_sp_fid_nve_flood_index_set(struct mlxsw_sp_fid *fid, const struct mlxsw_sp_fid_ops *ops = fid_family->ops; int err; - if (WARN_ON(!ops->nve_flood_index_set || fid->nve_flood_index_valid)) + if (WARN_ON(fid->nve_flood_index_valid)) return -EINVAL; - err = ops->nve_flood_index_set(fid, nve_flood_index); - if (err) - return err; - 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) @@ -224,7 +232,7 @@ 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(!ops->nve_flood_index_clear || !fid->nve_flood_index_valid)) + if (WARN_ON(!fid->nve_flood_index_valid)) return; fid->nve_flood_index_valid = false; @@ -244,7 +252,7 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type, struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; int err; - if (WARN_ON(!ops->vni_set || fid->vni_valid)) + if (WARN_ON(fid->vni_valid)) return -EINVAL; fid->nve_type = type; @@ -256,15 +264,15 @@ int mlxsw_sp_fid_vni_set(struct mlxsw_sp_fid *fid, enum mlxsw_sp_nve_type type, if (err) return err; - err = ops->vni_set(fid, vni); + fid->vni_valid = true; + err = ops->vni_set(fid); if (err) goto err_vni_set; - fid->vni_valid = true; - 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; @@ -276,7 +284,7 @@ void mlxsw_sp_fid_vni_clear(struct mlxsw_sp_fid *fid) const struct mlxsw_sp_fid_ops *ops = fid_family->ops; struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; - if (WARN_ON(!ops->vni_clear || !fid->vni_valid)) + if (WARN_ON(!fid->vni_valid)) return; fid->vni_valid = false; @@ -316,34 +324,43 @@ mlxsw_sp_fid_flood_table_lookup(const struct mlxsw_sp_fid *fid, return NULL; } +static u16 +mlxsw_sp_fid_family_num_fids(const struct mlxsw_sp_fid_family *fid_family) +{ + return fid_family->end_index - fid_family->start_index + 1; +} + +static u16 +mlxsw_sp_fid_flood_table_mid(const struct mlxsw_sp_fid_family *fid_family, + const struct mlxsw_sp_flood_table *flood_table, + u16 fid_offset) +{ + u16 num_fids; + + num_fids = mlxsw_sp_fid_family_num_fids(fid_family); + return fid_family->pgt_base + num_fids * flood_table->table_index + + fid_offset; +} + int mlxsw_sp_fid_flood_set(struct mlxsw_sp_fid *fid, - enum mlxsw_sp_flood_type packet_type, u8 local_port, + 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_fid_ops *ops = fid_family->ops; const struct mlxsw_sp_flood_table *flood_table; - char *sftr_pl; - int err; + u16 mid_index; - if (WARN_ON(!fid_family->flood_tables || !ops->flood_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; - sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL); - if (!sftr_pl) - return -ENOMEM; - - mlxsw_reg_sftr_pack(sftr_pl, flood_table->table_index, - ops->flood_index(fid), flood_table->table_type, 1, - local_port, member); - err = mlxsw_reg_write(fid_family->mlxsw_sp->core, MLXSW_REG(sftr), - sftr_pl); - kfree(sftr_pl); - return err; + 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, @@ -370,11 +387,6 @@ enum mlxsw_sp_fid_type mlxsw_sp_fid_type(const struct mlxsw_sp_fid *fid) return fid->fid_family->type; } -void mlxsw_sp_fid_rif_set(struct mlxsw_sp_fid *fid, struct mlxsw_sp_rif *rif) -{ - fid->rif = rif; -} - struct mlxsw_sp_rif *mlxsw_sp_fid_rif(const struct mlxsw_sp_fid *fid) { return fid->rif; @@ -405,6 +417,7 @@ 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) @@ -413,38 +426,341 @@ static enum mlxsw_reg_sfmr_op mlxsw_sp_sfmr_op(bool valid) MLXSW_REG_SFMR_OP_DESTROY_FID; } -static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid_index, - u16 fid_offset, bool valid) +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; - mlxsw_reg_sfmr_pack(sfmr_pl, mlxsw_sp_sfmr_op(valid), fid_index, - fid_offset); + 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_vni_op(struct mlxsw_sp *mlxsw_sp, u16 fid_index, - __be32 vni, bool vni_valid, u32 nve_flood_index, - bool nve_flood_index_valid) +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)); + } - mlxsw_reg_sfmr_pack(sfmr_pl, MLXSW_REG_SFMR_OP_CREATE_FID, fid_index, - 0); - mlxsw_reg_sfmr_vv_set(sfmr_pl, vni_valid); - mlxsw_reg_sfmr_vni_set(sfmr_pl, be32_to_cpu(vni)); - mlxsw_reg_sfmr_vtfp_set(sfmr_pl, nve_flood_index_valid); - mlxsw_reg_sfmr_nve_tunnel_flood_ptr_set(sfmr_pl, nve_flood_index); return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl); } -static int __mlxsw_sp_fid_port_vid_map(struct mlxsw_sp *mlxsw_sp, u16 fid_index, - u8 local_port, u16 vid, bool valid) +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) { - enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID; + struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; char svfa_pl[MLXSW_REG_SVFA_LEN]; - mlxsw_reg_svfa_pack(svfa_pl, local_port, mt, valid, fid_index, vid); + 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); } @@ -459,20 +775,19 @@ 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) { - struct mlxsw_sp_fid_family *fid_family = fid->fid_family; - - return mlxsw_sp_fid_op(fid_family->mlxsw_sp, fid->fid_index, 0, true); + 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->fid_family->mlxsw_sp, fid->fid_index, 0, false); + mlxsw_sp_fid_op(fid, false); } static int mlxsw_sp_fid_8021d_index_alloc(struct mlxsw_sp_fid *fid, @@ -498,14 +813,8 @@ mlxsw_sp_fid_8021d_compare(const struct mlxsw_sp_fid *fid, const void *arg) return mlxsw_sp_fid_8021d_fid(fid)->br_ifindex == br_ifindex; } -static u16 mlxsw_sp_fid_8021d_flood_index(const struct mlxsw_sp_fid *fid) -{ - return fid->fid_index - VLAN_N_VID; -} - static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; int err; @@ -517,7 +826,7 @@ static int mlxsw_sp_port_vp_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) if (!fid) continue; - err = __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, + err = __mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, true); if (err) @@ -540,8 +849,7 @@ err_fid_port_vid_map: if (!fid) continue; - __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, - mlxsw_sp_port->local_port, vid, + __mlxsw_sp_fid_port_vid_map(fid, mlxsw_sp_port->local_port, vid, false); } return err; @@ -549,7 +857,6 @@ err_fid_port_vid_map: static void mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) { - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; mlxsw_sp_port_vp_mode_set(mlxsw_sp_port, false); @@ -562,25 +869,130 @@ static void mlxsw_sp_port_vlan_mode_trans(struct mlxsw_sp_port *mlxsw_sp_port) if (!fid) continue; - __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, - mlxsw_sp_port->local_port, vid, + __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; - u8 local_port = mlxsw_sp_port->local_port; + u16 local_port = mlxsw_sp_port->local_port; int err; - err = __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, - mlxsw_sp_port->local_port, vid, true); + 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) @@ -591,8 +1003,11 @@ static int mlxsw_sp_fid_8021d_port_vid_map(struct mlxsw_sp_fid *fid, err_port_vp_mode_trans: mlxsw_sp->fid_core->port_fid_mappings[local_port]--; - __mlxsw_sp_fid_port_vid_map(mlxsw_sp, fid->fid_index, - mlxsw_sp_port->local_port, vid, false); + 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; } @@ -601,48 +1016,34 @@ 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; - u8 local_port = mlxsw_sp_port->local_port; + 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_map(mlxsw_sp, fid->fid_index, - mlxsw_sp_port->local_port, vid, false); + 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, __be32 vni) +static int mlxsw_sp_fid_8021d_vni_set(struct mlxsw_sp_fid *fid) { - struct mlxsw_sp_fid_family *fid_family = fid->fid_family; - - return mlxsw_sp_fid_vni_op(fid_family->mlxsw_sp, fid->fid_index, vni, - true, fid->nve_flood_index, - fid->nve_flood_index_valid); + return mlxsw_sp_fid_vni_op(fid); } static void mlxsw_sp_fid_8021d_vni_clear(struct mlxsw_sp_fid *fid) { - struct mlxsw_sp_fid_family *fid_family = fid->fid_family; - - mlxsw_sp_fid_vni_op(fid_family->mlxsw_sp, fid->fid_index, 0, false, - fid->nve_flood_index, fid->nve_flood_index_valid); + mlxsw_sp_fid_vni_op(fid); } -static int mlxsw_sp_fid_8021d_nve_flood_index_set(struct mlxsw_sp_fid *fid, - u32 nve_flood_index) +static int mlxsw_sp_fid_8021d_nve_flood_index_set(struct mlxsw_sp_fid *fid) { - struct mlxsw_sp_fid_family *fid_family = fid->fid_family; - - return mlxsw_sp_fid_vni_op(fid_family->mlxsw_sp, fid->fid_index, - fid->vni, fid->vni_valid, nve_flood_index, - true); + return mlxsw_sp_fid_edit_op(fid, fid->rif); } static void mlxsw_sp_fid_8021d_nve_flood_index_clear(struct mlxsw_sp_fid *fid) { - struct mlxsw_sp_fid_family *fid_family = fid->fid_family; - - mlxsw_sp_fid_vni_op(fid_family->mlxsw_sp, fid->fid_index, fid->vni, - fid->vni_valid, 0, false); + mlxsw_sp_fid_edit_op(fid, fid->rif); } static void @@ -652,13 +1053,19 @@ mlxsw_sp_fid_8021d_fdb_clear_offload(const struct mlxsw_sp_fid *fid, 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, - .flood_index = mlxsw_sp_fid_8021d_flood_index, .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, @@ -666,42 +1073,32 @@ static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021d_ops = { .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, .fdb_clear_offload = mlxsw_sp_fid_8021d_fdb_clear_offload, + .vid_to_fid_rif_update = mlxsw_sp_fid_8021d_vid_to_fid_rif_update, }; +#define MLXSW_SP_FID_8021Q_MAX (VLAN_N_VID - 2) +#define MLXSW_SP_FID_RFID_MAX (11 * 1024) +#define MLXSW_SP_FID_8021Q_PGT_BASE 0 +#define MLXSW_SP_FID_8021D_PGT_BASE (3 * MLXSW_SP_FID_8021Q_MAX) + static const struct mlxsw_sp_flood_table mlxsw_sp_fid_8021d_flood_tables[] = { { .packet_type = MLXSW_SP_FLOOD_TYPE_UC, - .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, - .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, + .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, .table_index = 0, }, { .packet_type = MLXSW_SP_FLOOD_TYPE_MC, - .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, - .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, + .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, .table_index = 1, }, { .packet_type = MLXSW_SP_FLOOD_TYPE_BC, - .bridge_type = MLXSW_REG_SFGC_BRIDGE_TYPE_VFID, - .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID, + .table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFSET, .table_index = 2, }, }; -/* Range and flood configuration must match mlxsw_config_profile */ -static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021d_family = { - .type = MLXSW_SP_FID_TYPE_8021D, - .fid_size = sizeof(struct mlxsw_sp_fid_8021d), - .start_index = VLAN_N_VID, - .end_index = VLAN_N_VID + MLXSW_SP_FID_8021D_MAX - 1, - .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, - .lag_vid_valid = 1, -}; - static bool mlxsw_sp_fid_8021q_compare(const struct mlxsw_sp_fid *fid, const void *arg) { @@ -717,48 +1114,19 @@ mlxsw_sp_fid_8021q_fdb_clear_offload(const struct mlxsw_sp_fid *fid, br_fdb_clear_offload(nve_dev, mlxsw_sp_fid_8021q_vid(fid)); } -static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_emu_ops = { - .setup = mlxsw_sp_fid_8021q_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_8021q_compare, - .flood_index = mlxsw_sp_fid_8021d_flood_index, - .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_8021q_fdb_clear_offload, -}; - -/* There are 4K-2 emulated 802.1Q FIDs, starting right after the 802.1D FIDs */ -#define MLXSW_SP_FID_8021Q_EMU_START (VLAN_N_VID + MLXSW_SP_FID_8021D_MAX) -#define MLXSW_SP_FID_8021Q_EMU_END (MLXSW_SP_FID_8021Q_EMU_START + \ - VLAN_VID_MASK - 2) - -/* Range and flood configuration must match mlxsw_config_profile */ -static const struct mlxsw_sp_fid_family mlxsw_sp_fid_8021q_emu_family = { - .type = MLXSW_SP_FID_TYPE_8021Q, - .fid_size = sizeof(struct mlxsw_sp_fid_8021q), - .start_index = MLXSW_SP_FID_8021Q_EMU_START, - .end_index = MLXSW_SP_FID_8021Q_EMU_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_emu_ops, - .lag_vid_valid = 1, -}; +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) { - /* rFIDs are allocated by the device during init */ - return 0; + 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, @@ -784,12 +1152,31 @@ static int mlxsw_sp_fid_rfid_port_vid_map(struct mlxsw_sp_fid *fid, u16 vid) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - u8 local_port = mlxsw_sp_port->local_port; + u16 local_port = mlxsw_sp_port->local_port; int err; - /* We only need to transition the port to virtual mode since - * {Port, VID} => FID is done by the firmware upon RIF creation. + 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) @@ -800,6 +1187,13 @@ static int mlxsw_sp_fid_rfid_port_vid_map(struct mlxsw_sp_fid *fid, 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; } @@ -808,44 +1202,74 @@ 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; - u8 local_port = mlxsw_sp_port->local_port; + 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, }; -#define MLXSW_SP_RFID_BASE (15 * 1024) -#define MLXSW_SP_RFID_MAX 1024 - -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_BASE, - .end_index = MLXSW_SP_RFID_BASE + MLXSW_SP_RFID_MAX - 1, - .rif_type = MLXSW_SP_RIF_TYPE_SUBPORT, - .ops = &mlxsw_sp_fid_rfid_ops, -}; +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) { - struct mlxsw_sp *mlxsw_sp = fid->fid_family->mlxsw_sp; - - return mlxsw_sp_fid_op(mlxsw_sp, fid->fid_index, 0, true); + return mlxsw_sp_fid_op(fid, true); } static void mlxsw_sp_fid_dummy_deconfigure(struct mlxsw_sp_fid *fid) { - mlxsw_sp_fid_op(fid->fid_family->mlxsw_sp, fid->fid_index, 0, false); + mlxsw_sp_fid_op(fid, false); } static int mlxsw_sp_fid_dummy_index_alloc(struct mlxsw_sp_fid *fid, @@ -862,26 +1286,252 @@ static bool mlxsw_sp_fid_dummy_compare(const struct mlxsw_sp_fid *fid, 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; + u8 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; + u8 local_port = mlxsw_sp_port->local_port; + + mlxsw_sp_fid_port_vid_list_del(fid, mlxsw_sp_port->local_port, vid); + mlxsw_sp_fid_evid_map(fid, local_port, vid, false); + if (mlxsw_sp->fid_core->port_fid_mappings[local_port]) + __mlxsw_sp_fid_port_vid_map(fid, local_port, vid, false); +} + +static const struct mlxsw_sp_fid_ops mlxsw_sp_fid_8021q_ops = { + .setup = mlxsw_sp_fid_8021q_setup, + .configure = mlxsw_sp_fid_8021q_configure, + .deconfigure = mlxsw_sp_fid_8021q_deconfigure, + .index_alloc = mlxsw_sp_fid_8021d_index_alloc, + .compare = mlxsw_sp_fid_8021q_compare, + .port_vid_map = mlxsw_sp_fid_8021q_port_vid_map, + .port_vid_unmap = mlxsw_sp_fid_8021q_port_vid_unmap, + .vni_set = mlxsw_sp_fid_8021d_vni_set, + .vni_clear = mlxsw_sp_fid_8021d_vni_clear, + .nve_flood_index_set = mlxsw_sp_fid_8021d_nve_flood_index_set, + .nve_flood_index_clear = mlxsw_sp_fid_8021d_nve_flood_index_clear, + .fdb_clear_offload = mlxsw_sp_fid_8021q_fdb_clear_offload, + .vid_to_fid_rif_update = mlxsw_sp_fid_8021q_vid_to_fid_rif_update, +}; + +/* There are 4K-2 802.1Q FIDs */ +#define MLXSW_SP_FID_8021Q_START 1 /* FID 0 is reserved. */ +#define MLXSW_SP_FID_8021Q_END (MLXSW_SP_FID_8021Q_START + \ + MLXSW_SP_FID_8021Q_MAX - 1) + +/* There are 1K 802.1D FIDs */ +#define MLXSW_SP_FID_8021D_START (MLXSW_SP_FID_8021Q_END + 1) +#define MLXSW_SP_FID_8021D_END (MLXSW_SP_FID_8021D_START + \ + MLXSW_SP_FID_8021D_MAX - 1) + +/* There is one dummy FID */ +#define MLXSW_SP_FID_DUMMY (MLXSW_SP_FID_8021D_END + 1) + +/* There are 11K rFIDs */ +#define MLXSW_SP_RFID_START (MLXSW_SP_FID_DUMMY + 1) +#define MLXSW_SP_RFID_END (MLXSW_SP_RFID_START + \ + MLXSW_SP_FID_RFID_MAX - 1) + +static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021q_family = { + .type = MLXSW_SP_FID_TYPE_8021Q, + .fid_size = sizeof(struct mlxsw_sp_fid_8021q), + .start_index = MLXSW_SP_FID_8021Q_START, + .end_index = MLXSW_SP_FID_8021Q_END, + .flood_tables = mlxsw_sp_fid_8021d_flood_tables, + .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), + .rif_type = MLXSW_SP_RIF_TYPE_VLAN, + .ops = &mlxsw_sp_fid_8021q_ops, + .flood_rsp = false, + .bridge_type = MLXSW_REG_BRIDGE_TYPE_0, + .pgt_base = MLXSW_SP_FID_8021Q_PGT_BASE, + .smpe_index_valid = false, }; -static const struct mlxsw_sp_fid_family mlxsw_sp_fid_dummy_family = { +static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_8021d_family = { + .type = MLXSW_SP_FID_TYPE_8021D, + .fid_size = sizeof(struct mlxsw_sp_fid_8021d), + .start_index = MLXSW_SP_FID_8021D_START, + .end_index = MLXSW_SP_FID_8021D_END, + .flood_tables = mlxsw_sp_fid_8021d_flood_tables, + .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), + .rif_type = MLXSW_SP_RIF_TYPE_FID, + .ops = &mlxsw_sp_fid_8021d_ops, + .bridge_type = MLXSW_REG_BRIDGE_TYPE_1, + .pgt_base = MLXSW_SP_FID_8021D_PGT_BASE, + .smpe_index_valid = false, +}; + +static const struct mlxsw_sp_fid_family mlxsw_sp1_fid_dummy_family = { .type = MLXSW_SP_FID_TYPE_DUMMY, .fid_size = sizeof(struct mlxsw_sp_fid), - .start_index = VLAN_N_VID - 1, - .end_index = VLAN_N_VID - 1, + .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_family_arr[] = { - [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp_fid_8021q_emu_family, - [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp_fid_8021d_family, +static const struct mlxsw_sp_fid_family mlxsw_sp_fid_rfid_family = { + .type = MLXSW_SP_FID_TYPE_RFID, + .fid_size = sizeof(struct mlxsw_sp_fid), + .start_index = MLXSW_SP_RFID_START, + .end_index = MLXSW_SP_RFID_END, + .rif_type = MLXSW_SP_RIF_TYPE_SUBPORT, + .ops = &mlxsw_sp_fid_rfid_ops, + .flood_rsp = true, + .smpe_index_valid = false, +}; + +const struct mlxsw_sp_fid_family *mlxsw_sp1_fid_family_arr[] = { + [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp1_fid_8021q_family, + [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp1_fid_8021d_family, + [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp1_fid_dummy_family, + [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family, +}; + +static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021q_family = { + .type = MLXSW_SP_FID_TYPE_8021Q, + .fid_size = sizeof(struct mlxsw_sp_fid_8021q), + .start_index = MLXSW_SP_FID_8021Q_START, + .end_index = MLXSW_SP_FID_8021Q_END, + .flood_tables = mlxsw_sp_fid_8021d_flood_tables, + .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), + .rif_type = MLXSW_SP_RIF_TYPE_VLAN, + .ops = &mlxsw_sp_fid_8021q_ops, + .flood_rsp = false, + .bridge_type = MLXSW_REG_BRIDGE_TYPE_0, + .pgt_base = MLXSW_SP_FID_8021Q_PGT_BASE, + .smpe_index_valid = true, +}; + +static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_8021d_family = { + .type = MLXSW_SP_FID_TYPE_8021D, + .fid_size = sizeof(struct mlxsw_sp_fid_8021d), + .start_index = MLXSW_SP_FID_8021D_START, + .end_index = MLXSW_SP_FID_8021D_END, + .flood_tables = mlxsw_sp_fid_8021d_flood_tables, + .nr_flood_tables = ARRAY_SIZE(mlxsw_sp_fid_8021d_flood_tables), + .rif_type = MLXSW_SP_RIF_TYPE_FID, + .ops = &mlxsw_sp_fid_8021d_ops, + .bridge_type = MLXSW_REG_BRIDGE_TYPE_1, + .pgt_base = MLXSW_SP_FID_8021D_PGT_BASE, + .smpe_index_valid = true, +}; + +static const struct mlxsw_sp_fid_family mlxsw_sp2_fid_dummy_family = { + .type = MLXSW_SP_FID_TYPE_DUMMY, + .fid_size = sizeof(struct mlxsw_sp_fid), + .start_index = MLXSW_SP_FID_DUMMY, + .end_index = MLXSW_SP_FID_DUMMY, + .ops = &mlxsw_sp_fid_dummy_ops, + .smpe_index_valid = false, +}; + +const struct mlxsw_sp_fid_family *mlxsw_sp2_fid_family_arr[] = { + [MLXSW_SP_FID_TYPE_8021Q] = &mlxsw_sp2_fid_8021q_family, + [MLXSW_SP_FID_TYPE_8021D] = &mlxsw_sp2_fid_8021d_family, + [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp2_fid_dummy_family, [MLXSW_SP_FID_TYPE_RFID] = &mlxsw_sp_fid_rfid_family, - [MLXSW_SP_FID_TYPE_DUMMY] = &mlxsw_sp_fid_dummy_family, }; static struct mlxsw_sp_fid *mlxsw_sp_fid_lookup(struct mlxsw_sp *mlxsw_sp, @@ -919,6 +1569,8 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp, 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); @@ -927,8 +1579,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_get(struct mlxsw_sp *mlxsw_sp, fid->fid_index = fid_index; __set_bit(fid_index - fid_family->start_index, fid_family->fids_bitmap); - if (fid->fid_family->ops->setup) - fid->fid_family->ops->setup(fid, arg); + fid->fid_family->ops->setup(fid, arg); err = fid->fid_family->ops->configure(fid); if (err) @@ -967,6 +1618,7 @@ void mlxsw_sp_fid_put(struct mlxsw_sp_fid *fid) 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); } @@ -1010,26 +1662,49 @@ 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; - int i; + u16 num_fids, mid_base; + int err, i; + + mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, 0); + num_fids = mlxsw_sp_fid_family_num_fids(fid_family); + err = mlxsw_sp_pgt_mid_alloc_range(mlxsw_sp, mid_base, num_fids); + if (err) + return err; sfgc_packet_types = mlxsw_sp_packet_type_sfgc_types[packet_type]; for (i = 0; i < MLXSW_REG_SFGC_TYPE_MAX; i++) { - struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; char sfgc_pl[MLXSW_REG_SFGC_LEN]; - int err; if (!sfgc_packet_types[i]) continue; - mlxsw_reg_sfgc_pack(sfgc_pl, i, flood_table->bridge_type, - flood_table->table_type, - flood_table->table_index); + + 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; + goto err_reg_write; } return 0; + +err_reg_write: + mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base, num_fids); + return err; +} + +static void +mlxsw_sp_fid_flood_table_fini(struct mlxsw_sp_fid_family *fid_family, + const struct mlxsw_sp_flood_table *flood_table) +{ + struct mlxsw_sp *mlxsw_sp = fid_family->mlxsw_sp; + u16 num_fids, mid_base; + + mid_base = mlxsw_sp_fid_flood_table_mid(fid_family, flood_table, 0); + num_fids = mlxsw_sp_fid_family_num_fids(fid_family); + mlxsw_sp_pgt_mid_free_range(mlxsw_sp, mid_base, num_fids); } static int @@ -1050,6 +1725,19 @@ mlxsw_sp_fid_flood_tables_init(struct mlxsw_sp_fid_family *fid_family) return 0; } +static void +mlxsw_sp_fid_flood_tables_fini(struct mlxsw_sp_fid_family *fid_family) +{ + int i; + + for (i = 0; i < fid_family->nr_flood_tables; i++) { + const struct mlxsw_sp_flood_table *flood_table; + + flood_table = &fid_family->flood_tables[i]; + mlxsw_sp_fid_flood_table_fini(fid_family, flood_table); + } +} + static int mlxsw_sp_fid_family_register(struct mlxsw_sp *mlxsw_sp, const struct mlxsw_sp_fid_family *tmpl) { @@ -1091,6 +1779,10 @@ 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); @@ -1144,7 +1836,7 @@ int mlxsw_sp_fids_init(struct mlxsw_sp *mlxsw_sp) for (i = 0; i < MLXSW_SP_FID_TYPE_MAX; i++) { err = mlxsw_sp_fid_family_register(mlxsw_sp, - mlxsw_sp_fid_family_arr[i]); + mlxsw_sp->fid_family_arr[i]); if (err) goto err_fid_ops_register; |