From 4abaa5cc4d7c42ae61ce482acc5aa1e1cededc73 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:39:58 +0300 Subject: [PATCH 01/10] mlxsw: Align PGT index to legacy bridge model FID code reserves about 15K entries in PGT table for flooding. These entries are just allocated and are not used yet because the code that uses them is skipped now. The next patches will convert MDB code to use PGT APIs. The allocation of indexes for multicast is done after FID code reserves 15K entries. Currently, legacy bridge model is used and firmware manages PGT table. That means that the indexes which are allocated using PGT API are too high when legacy bridge model is used. To not exceed firmware limitation for MDB entries, add an API that returns the correct 'mid_index', based on bridge model. For legacy model, subtract the number of flood entries from PGT index. Use it to write the correct MID to SMID register. This API will be used also from MDB code in the next patches. PGT should not be aware of MDB and FID different usage, this API is temporary and will be removed once unified bridge model will be used. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 1 + .../net/ethernet/mellanox/mlxsw/spectrum_pgt.c | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index b128692611d9..b7709e759080 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -1485,6 +1485,7 @@ void mlxsw_sp_pgt_mid_free_range(struct mlxsw_sp *mlxsw_sp, u16 mid_base, u16 count); int mlxsw_sp_pgt_entry_port_set(struct mlxsw_sp *mlxsw_sp, u16 mid, u16 smpe, u16 local_port, bool member); +u16 mlxsw_sp_pgt_index_to_mid(const struct mlxsw_sp *mlxsw_sp, u16 pgt_index); int mlxsw_sp_pgt_init(struct mlxsw_sp *mlxsw_sp); void mlxsw_sp_pgt_fini(struct mlxsw_sp *mlxsw_sp); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c index 3b7265b539b2..e6bbe08ef379 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_pgt.c @@ -182,6 +182,16 @@ static void mlxsw_sp_pgt_entry_put(struct mlxsw_sp_pgt *pgt, u16 mid) mlxsw_sp_pgt_entry_destroy(pgt, pgt_entry); } +#define MLXSW_SP_FID_PGT_FLOOD_ENTRIES 15354 /* Reserved for flooding. */ + +u16 mlxsw_sp_pgt_index_to_mid(const struct mlxsw_sp *mlxsw_sp, u16 pgt_index) +{ + if (mlxsw_sp->ubridge) + return pgt_index; + + return pgt_index - MLXSW_SP_FID_PGT_FLOOD_ENTRIES; +} + static void mlxsw_sp_pgt_smid2_port_set(char *smid2_pl, u16 local_port, bool member) { @@ -196,7 +206,7 @@ mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp *mlxsw_sp, { bool smpe_index_valid; char *smid2_pl; - u16 smpe; + u16 smpe, mid; int err; smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL); @@ -206,9 +216,9 @@ mlxsw_sp_pgt_entry_port_write(struct mlxsw_sp *mlxsw_sp, smpe_index_valid = mlxsw_sp->ubridge ? mlxsw_sp->pgt->smpe_index_valid : false; smpe = mlxsw_sp->ubridge ? pgt_entry->smpe_index : 0; + mid = mlxsw_sp_pgt_index_to_mid(mlxsw_sp, pgt_entry->index); - mlxsw_reg_smid2_pack(smid2_pl, pgt_entry->index, 0, 0, smpe_index_valid, - smpe); + mlxsw_reg_smid2_pack(smid2_pl, mid, 0, 0, smpe_index_valid, smpe); mlxsw_sp_pgt_smid2_port_set(smid2_pl, local_port, member); err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl); From eede53a49b3c52a95a9f11145ff0e3d86f192f39 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:39:59 +0300 Subject: [PATCH 02/10] mlxsw: spectrum_switchdev: Rename MID structure Currently the structure which represents MDB entry is called 'struct mlxsw_sp_mid'. This name is not accurate as a MID entry stores a bitmap of ports to which a packet needs to be replicated and a MDB entry stores the mapping from {MAC, FID} to PGT index (MID). Rename the structure to 'struct mlxsw_sp_mdb_entry'. The structure 'mlxsw_sp_mid' is defined as part of spectrum.h. The only file which uses it is spectrum_switchdev.c, so there is no reason to expose it to other files. Move the definition to spectrum_switchdev.c. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum.h | 9 - .../mellanox/mlxsw/spectrum_switchdev.c | 179 ++++++++++-------- 2 files changed, 97 insertions(+), 91 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index b7709e759080..8de3bdcdf143 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -112,15 +112,6 @@ enum mlxsw_sp_nve_type { MLXSW_SP_NVE_TYPE_VXLAN, }; -struct mlxsw_sp_mid { - struct list_head list; - unsigned char addr[ETH_ALEN]; - u16 fid; - u16 mid; - bool in_hw; - unsigned long *ports_in_mid; /* bits array */ -}; - struct mlxsw_sp_sb; struct mlxsw_sp_bridge; struct mlxsw_sp_router; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index e153b6f2783a..70b48b922520 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -102,6 +102,15 @@ struct mlxsw_sp_switchdev_ops { void (*init)(struct mlxsw_sp *mlxsw_sp); }; +struct mlxsw_sp_mdb_entry { + struct list_head list; + unsigned char addr[ETH_ALEN]; + u16 fid; + u16 mid; + bool in_hw; + unsigned long *ports_in_mid; /* bits array */ +}; + static int mlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_port *bridge_port, @@ -971,10 +980,10 @@ mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, bool add) { - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; - list_for_each_entry(mid, &bridge_device->mids_list, list) - mlxsw_sp_smid_router_port_set(mlxsw_sp, mid->mid, add); + list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) + mlxsw_sp_smid_router_port_set(mlxsw_sp, mdb_entry->mid, add); } static int @@ -1696,16 +1705,16 @@ static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, return err; } -static struct -mlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, - const unsigned char *addr, - u16 fid) +static struct mlxsw_sp_mdb_entry * +__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, + const unsigned char *addr, u16 fid) { - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; - list_for_each_entry(mid, &bridge_device->mids_list, list) { - if (ether_addr_equal(mid->addr, addr) && mid->fid == fid) - return mid; + list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { + if (ether_addr_equal(mdb_entry->addr, addr) && + mdb_entry->fid == fid) + return mdb_entry; } return NULL; } @@ -1753,7 +1762,7 @@ mlxsw_sp_mc_get_mrouters_bitmap(struct mlxsw_sp_ports_bitmap *flood_bm, static int mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_mid *mid, + struct mlxsw_sp_mdb_entry *mdb_entry, struct mlxsw_sp_bridge_device *bridge_device) { struct mlxsw_sp_ports_bitmap flood_bitmap; @@ -1769,91 +1778,91 @@ mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, if (err) return err; - bitmap_copy(flood_bitmap.bitmap, mid->ports_in_mid, flood_bitmap.nbits); + bitmap_copy(flood_bitmap.bitmap, mdb_entry->ports_in_mid, + flood_bitmap.nbits); mlxsw_sp_mc_get_mrouters_bitmap(&flood_bitmap, bridge_device, mlxsw_sp); - mid->mid = mid_idx; + mdb_entry->mid = mid_idx; err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, &flood_bitmap, bridge_device->mrouter); mlxsw_sp_port_bitmap_fini(&flood_bitmap); if (err) return err; - err = mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid_idx, - true); + err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->addr, mdb_entry->fid, + mid_idx, true); if (err) return err; set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap); - mid->in_hw = true; + mdb_entry->in_hw = true; return 0; } static int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_mid *mid) + struct mlxsw_sp_mdb_entry *mdb_entry) { - if (!mid->in_hw) + if (!mdb_entry->in_hw) return 0; - clear_bit(mid->mid, mlxsw_sp->bridge->mids_bitmap); - mid->in_hw = false; - return mlxsw_sp_port_mdb_op(mlxsw_sp, mid->addr, mid->fid, mid->mid, - false); + clear_bit(mdb_entry->mid, mlxsw_sp->bridge->mids_bitmap); + mdb_entry->in_hw = false; + return mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->addr, mdb_entry->fid, + mdb_entry->mid, false); } -static struct -mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_bridge_device *bridge_device, - const unsigned char *addr, - u16 fid) +static struct mlxsw_sp_mdb_entry * +__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_bridge_device *bridge_device, + const unsigned char *addr, u16 fid) { - struct mlxsw_sp_mid *mid; + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); + struct mlxsw_sp_mdb_entry *mdb_entry; int err; - mid = kzalloc(sizeof(*mid), GFP_KERNEL); - if (!mid) + mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); + if (!mdb_entry) return NULL; - mid->ports_in_mid = bitmap_zalloc(mlxsw_core_max_ports(mlxsw_sp->core), - GFP_KERNEL); - if (!mid->ports_in_mid) + mdb_entry->ports_in_mid = bitmap_zalloc(max_ports, GFP_KERNEL); + if (!mdb_entry->ports_in_mid) goto err_ports_in_mid_alloc; - ether_addr_copy(mid->addr, addr); - mid->fid = fid; - mid->in_hw = false; + ether_addr_copy(mdb_entry->addr, addr); + mdb_entry->fid = fid; + mdb_entry->in_hw = false; if (!bridge_device->multicast_enabled) goto out; - err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, bridge_device); + err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, bridge_device); if (err) goto err_write_mdb_entry; out: - list_add_tail(&mid->list, &bridge_device->mids_list); - return mid; + list_add_tail(&mdb_entry->list, &bridge_device->mids_list); + return mdb_entry; err_write_mdb_entry: - bitmap_free(mid->ports_in_mid); + bitmap_free(mdb_entry->ports_in_mid); err_ports_in_mid_alloc: - kfree(mid); + kfree(mdb_entry); return NULL; } static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_mid *mid) + struct mlxsw_sp_mdb_entry *mdb_entry) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; int err = 0; - clear_bit(mlxsw_sp_port->local_port, mid->ports_in_mid); - if (bitmap_empty(mid->ports_in_mid, + clear_bit(mlxsw_sp_port->local_port, mdb_entry->ports_in_mid); + if (bitmap_empty(mdb_entry->ports_in_mid, mlxsw_core_max_ports(mlxsw_sp->core))) { - err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); - list_del(&mid->list); - bitmap_free(mid->ports_in_mid); - kfree(mid); + err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); + list_del(&mdb_entry->list); + bitmap_free(mdb_entry->ports_in_mid); + kfree(mdb_entry); } return err; } @@ -1867,7 +1876,7 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, struct net_device *dev = mlxsw_sp_port->dev; struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_port *bridge_port; - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; u16 fid_index; int err = 0; @@ -1884,16 +1893,16 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); - if (!mid) { - mid = __mlxsw_sp_mc_alloc(mlxsw_sp, bridge_device, mdb->addr, - fid_index); - if (!mid) { + mdb_entry = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); + if (!mdb_entry) { + mdb_entry = __mlxsw_sp_mc_alloc(mlxsw_sp, bridge_device, + mdb->addr, fid_index); + if (!mdb_entry) { netdev_err(dev, "Unable to allocate MC group\n"); return -ENOMEM; } } - set_bit(mlxsw_sp_port->local_port, mid->ports_in_mid); + set_bit(mlxsw_sp_port->local_port, mdb_entry->ports_in_mid); if (!bridge_device->multicast_enabled) return 0; @@ -1901,7 +1910,7 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, if (bridge_port->mrouter) return 0; - err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true); + err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, true); if (err) { netdev_err(dev, "Unable to set SMID\n"); goto err_out; @@ -1910,7 +1919,7 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, return 0; err_out: - mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid); + mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mdb_entry); return err; } @@ -1919,15 +1928,15 @@ mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, bool mc_enabled) { - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; int err; - list_for_each_entry(mid, &bridge_device->mids_list, list) { + list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { if (mc_enabled) - err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, + err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, bridge_device); else - err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); + err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); if (err) goto err_mdb_entry_update; @@ -1936,12 +1945,12 @@ mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, return 0; err_mdb_entry_update: - list_for_each_entry_continue_reverse(mid, &bridge_device->mids_list, - list) { + list_for_each_entry_continue_reverse(mdb_entry, + &bridge_device->mids_list, list) { if (mc_enabled) - mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid); + mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); else - mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, + mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, bridge_device); } return err; @@ -1953,13 +1962,15 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, bool add) { struct mlxsw_sp_bridge_device *bridge_device; - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; bridge_device = bridge_port->bridge_device; - list_for_each_entry(mid, &bridge_device->mids_list, list) { - if (!test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid)) - mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, add); + list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { + if (!test_bit(mlxsw_sp_port->local_port, + mdb_entry->ports_in_mid)) + mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, + add); } } @@ -2040,19 +2051,20 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, static int __mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_port *bridge_port, - struct mlxsw_sp_mid *mid) + struct mlxsw_sp_mdb_entry *mdb_entry) { struct net_device *dev = mlxsw_sp_port->dev; int err; if (bridge_port->bridge_device->multicast_enabled && !bridge_port->mrouter) { - err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false); + err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, + false); if (err) netdev_err(dev, "Unable to remove port from SMID\n"); } - err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid); + err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mdb_entry); if (err) netdev_err(dev, "Unable to remove MC SFD\n"); @@ -2068,7 +2080,7 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_device *bridge_device; struct net_device *dev = mlxsw_sp_port->dev; struct mlxsw_sp_bridge_port *bridge_port; - struct mlxsw_sp_mid *mid; + struct mlxsw_sp_mdb_entry *mdb_entry; u16 fid_index; bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); @@ -2084,13 +2096,13 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - mid = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); - if (!mid) { + mdb_entry = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); + if (!mdb_entry) { netdev_err(dev, "Unable to remove port from MC DB\n"); return -EINVAL; } - return __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mid); + return __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mdb_entry); } static void @@ -2098,17 +2110,20 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_port *bridge_port) { struct mlxsw_sp_bridge_device *bridge_device; - struct mlxsw_sp_mid *mid, *tmp; + struct mlxsw_sp_mdb_entry *mdb_entry, *tmp; + u16 local_port = mlxsw_sp_port->local_port; bridge_device = bridge_port->bridge_device; - list_for_each_entry_safe(mid, tmp, &bridge_device->mids_list, list) { - if (test_bit(mlxsw_sp_port->local_port, mid->ports_in_mid)) { + list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mids_list, + list) { + if (test_bit(local_port, mdb_entry->ports_in_mid)) { __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, - mid); + mdb_entry); } else if (bridge_device->multicast_enabled && bridge_port->mrouter) { - mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false); + mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, + false); } } } From eaa0791aed8bf95ac50c8e3c79f14c7879235623 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:00 +0300 Subject: [PATCH 03/10] mlxsw: spectrum_switchdev: Rename MIDs list Currently, the list which stores the MDB entries for a given bridge instance is called 'mids_list'. This name is not accurate as a MID entry stores a bitmap of ports to which a packet needs to be replicated and a MDB entry stores the mapping from {MAC, FID} to PGT index (MID) Rename it to 'mdb_list'. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 70b48b922520..bd182736f44d 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -48,7 +48,7 @@ struct mlxsw_sp_bridge_device { struct net_device *dev; struct list_head list; struct list_head ports_list; - struct list_head mids_list; + struct list_head mdb_list; u8 vlan_enabled:1, multicast_enabled:1, mrouter:1; @@ -263,7 +263,8 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, } else { bridge_device->ops = bridge->bridge_8021d_ops; } - INIT_LIST_HEAD(&bridge_device->mids_list); + INIT_LIST_HEAD(&bridge_device->mdb_list); + if (list_empty(&bridge->bridges_list)) mlxsw_sp_fdb_notify_work_schedule(bridge->mlxsw_sp, false); list_add(&bridge_device->list, &bridge->bridges_list); @@ -299,7 +300,7 @@ mlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge, if (bridge_device->vlan_enabled) bridge->vlan_enabled_exists = false; WARN_ON(!list_empty(&bridge_device->ports_list)); - WARN_ON(!list_empty(&bridge_device->mids_list)); + WARN_ON(!list_empty(&bridge_device->mdb_list)); kfree(bridge_device); } @@ -982,7 +983,7 @@ mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, { struct mlxsw_sp_mdb_entry *mdb_entry; - list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) + list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) mlxsw_sp_smid_router_port_set(mlxsw_sp, mdb_entry->mid, add); } @@ -1711,7 +1712,7 @@ __mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, { struct mlxsw_sp_mdb_entry *mdb_entry; - list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { + list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { if (ether_addr_equal(mdb_entry->addr, addr) && mdb_entry->fid == fid) return mdb_entry; @@ -1840,7 +1841,7 @@ __mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, goto err_write_mdb_entry; out: - list_add_tail(&mdb_entry->list, &bridge_device->mids_list); + list_add_tail(&mdb_entry->list, &bridge_device->mdb_list); return mdb_entry; err_write_mdb_entry: @@ -1931,7 +1932,7 @@ mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_mdb_entry *mdb_entry; int err; - list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { + list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { if (mc_enabled) err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, bridge_device); @@ -1946,7 +1947,7 @@ mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, err_mdb_entry_update: list_for_each_entry_continue_reverse(mdb_entry, - &bridge_device->mids_list, list) { + &bridge_device->mdb_list, list) { if (mc_enabled) mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); else @@ -1966,7 +1967,7 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, bridge_device = bridge_port->bridge_device; - list_for_each_entry(mdb_entry, &bridge_device->mids_list, list) { + list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { if (!test_bit(mlxsw_sp_port->local_port, mdb_entry->ports_in_mid)) mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, @@ -2115,7 +2116,7 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, bridge_device = bridge_port->bridge_device; - list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mids_list, + list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mdb_list, list) { if (test_bit(local_port, mdb_entry->ports_in_mid)) { __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, From 0ac985436eb99056e8248b12caa4b006ed93d8b3 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:01 +0300 Subject: [PATCH 04/10] mlxsw: spectrum_switchdev: Save MAC and FID as a key in 'struct mlxsw_sp_mdb_entry' The next patch will add support for storing all the MDB entries in a hash table. As a preparation, save the MAC address and the FID in a separate structure. This structure will be used later as a key for the hash table. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index bd182736f44d..d1a1d55b0068 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -102,10 +102,14 @@ struct mlxsw_sp_switchdev_ops { void (*init)(struct mlxsw_sp *mlxsw_sp); }; -struct mlxsw_sp_mdb_entry { - struct list_head list; +struct mlxsw_sp_mdb_entry_key { unsigned char addr[ETH_ALEN]; u16 fid; +}; + +struct mlxsw_sp_mdb_entry { + struct list_head list; + struct mlxsw_sp_mdb_entry_key key; u16 mid; bool in_hw; unsigned long *ports_in_mid; /* bits array */ @@ -1713,8 +1717,8 @@ __mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, struct mlxsw_sp_mdb_entry *mdb_entry; list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { - if (ether_addr_equal(mdb_entry->addr, addr) && - mdb_entry->fid == fid) + if (ether_addr_equal(mdb_entry->key.addr, addr) && + mdb_entry->key.fid == fid) return mdb_entry; } return NULL; @@ -1790,8 +1794,8 @@ mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, if (err) return err; - err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->addr, mdb_entry->fid, - mid_idx, true); + err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->key.addr, + mdb_entry->key.fid, mid_idx, true); if (err) return err; @@ -1808,8 +1812,8 @@ static int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp, clear_bit(mdb_entry->mid, mlxsw_sp->bridge->mids_bitmap); mdb_entry->in_hw = false; - return mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->addr, mdb_entry->fid, - mdb_entry->mid, false); + return mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->key.addr, + mdb_entry->key.fid, mdb_entry->mid, false); } static struct mlxsw_sp_mdb_entry * @@ -1829,8 +1833,8 @@ __mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, if (!mdb_entry->ports_in_mid) goto err_ports_in_mid_alloc; - ether_addr_copy(mdb_entry->addr, addr); - mdb_entry->fid = fid; + ether_addr_copy(mdb_entry->key.addr, addr); + mdb_entry->key.fid = fid; mdb_entry->in_hw = false; if (!bridge_device->multicast_enabled) From 5d0512e5cf7482998dab45273c6542b4c25155aa Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:02 +0300 Subject: [PATCH 05/10] mlxsw: spectrum_switchdev: Add support for maintaining hash table of MDB entries Currently MDB entries are stored in a list as part of 'struct mlxsw_sp_bridge_device'. Storing them in a hash table in addition to the list will allow finding a specific entry more efficiently. Add support for the required hash table, the next patches will insert and remove MDB entries from the table. The existing code which adds and removes entries will be removed and replaced by new code in the next patches, so there is no point to adjust the existing code. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../ethernet/mellanox/mlxsw/spectrum_switchdev.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index d1a1d55b0068..617ec3312fd8 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -49,6 +49,7 @@ struct mlxsw_sp_bridge_device { struct list_head list; struct list_head ports_list; struct list_head mdb_list; + struct rhashtable mdb_ht; u8 vlan_enabled:1, multicast_enabled:1, mrouter:1; @@ -109,12 +110,19 @@ struct mlxsw_sp_mdb_entry_key { struct mlxsw_sp_mdb_entry { struct list_head list; + struct rhash_head ht_node; struct mlxsw_sp_mdb_entry_key key; u16 mid; bool in_hw; unsigned long *ports_in_mid; /* bits array */ }; +static const struct rhashtable_params mlxsw_sp_mdb_ht_params = { + .key_offset = offsetof(struct mlxsw_sp_mdb_entry, key), + .head_offset = offsetof(struct mlxsw_sp_mdb_entry, ht_node), + .key_len = sizeof(struct mlxsw_sp_mdb_entry_key), +}; + static int mlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_port *bridge_port, @@ -250,6 +258,10 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, if (!bridge_device) return ERR_PTR(-ENOMEM); + err = rhashtable_init(&bridge_device->mdb_ht, &mlxsw_sp_mdb_ht_params); + if (err) + goto err_mdb_rhashtable_init; + bridge_device->dev = br_dev; bridge_device->vlan_enabled = vlan_enabled; bridge_device->multicast_enabled = br_multicast_enabled(br_dev); @@ -287,6 +299,8 @@ mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, list_del(&bridge_device->list); if (bridge_device->vlan_enabled) bridge->vlan_enabled_exists = false; + rhashtable_destroy(&bridge_device->mdb_ht); +err_mdb_rhashtable_init: kfree(bridge_device); return ERR_PTR(err); } @@ -305,6 +319,7 @@ mlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge, bridge->vlan_enabled_exists = false; WARN_ON(!list_empty(&bridge_device->ports_list)); WARN_ON(!list_empty(&bridge_device->mdb_list)); + rhashtable_destroy(&bridge_device->mdb_ht); kfree(bridge_device); } From d2994e13058582a7ca20fc39e6e56c6c021a6eca Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:03 +0300 Subject: [PATCH 06/10] mlxsw: spectrum_switchdev: Add support for maintaining list of ports per MDB entry As part of converting MDB code to use PGT APIs, PGT code stores which ports are mapped to each PGT entry. PGT code is not aware of the type of the port (multicast router or not), as it is not relevant there. To be able to release an MDB entry when the there are no ports which are not multicast routers, the entry should be aware of the state of its ports. Add support for maintaining list of ports per MDB entry. Each port will hold a reference count as multiple MDB entries can use the same hardware MDB entry. It occurs because MDB entries in the Linux bridge are keyed according to their multicast IP, when these entries are notified to device drivers via switchdev, the multicast IP is converted to a multicast MAC. This conversion might cause collisions, for example, ff0e::1 and ff0e:1234::1 are both mapped to the multicast MAC 33:33:00:00:00:01. Multicast router port will take a reference once, and will be marked as 'mrouter', then when port in the list is multicast router and its reference value is one, it means that the entry can be removed in case that there are no other ports which are not multicast routers. For that, maintain a counter per MDB entry to count ports in the list, which were added to the multicast group, and not because they are multicast routers. When this counter is zero, the entry can be removed. Add mlxsw_sp_mdb_entry_port_{get,put}() for regular ports and mlxsw_sp_mdb_entry_mrouter_port_{get,put}() for multicast router ports. Call PGT API to add or remove port from PGT entry when port is first added or removed, according to the reference counting. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 153 ++++++++++++++++++ 1 file changed, 153 insertions(+) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 617ec3312fd8..d1b0eddad504 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -113,10 +113,19 @@ struct mlxsw_sp_mdb_entry { struct rhash_head ht_node; struct mlxsw_sp_mdb_entry_key key; u16 mid; + struct list_head ports_list; + u16 ports_count; bool in_hw; unsigned long *ports_in_mid; /* bits array */ }; +struct mlxsw_sp_mdb_entry_port { + struct list_head list; /* Member of 'ports_list'. */ + u16 local_port; + refcount_t refcount; + bool mrouter; +}; + static const struct rhashtable_params mlxsw_sp_mdb_ht_params = { .key_offset = offsetof(struct mlxsw_sp_mdb_entry, key), .head_offset = offsetof(struct mlxsw_sp_mdb_entry, ht_node), @@ -995,6 +1004,150 @@ static int mlxsw_sp_smid_router_port_set(struct mlxsw_sp *mlxsw_sp, return err; } +static struct mlxsw_sp_mdb_entry_port * +mlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry, + u16 local_port) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + + list_for_each_entry(mdb_entry_port, &mdb_entry->ports_list, list) { + if (mdb_entry_port->local_port == local_port) + return mdb_entry_port; + } + + return NULL; +} + +static __always_unused struct mlxsw_sp_mdb_entry_port * +mlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mdb_entry *mdb_entry, + u16 local_port) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + int err; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); + if (mdb_entry_port) { + if (mdb_entry_port->mrouter && + refcount_read(&mdb_entry_port->refcount) == 1) + mdb_entry->ports_count++; + + refcount_inc(&mdb_entry_port->refcount); + return mdb_entry_port; + } + + err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, true); + if (err) + return ERR_PTR(err); + + mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL); + if (!mdb_entry_port) { + err = -ENOMEM; + goto err_mdb_entry_port_alloc; + } + + mdb_entry_port->local_port = local_port; + refcount_set(&mdb_entry_port->refcount, 1); + list_add(&mdb_entry_port->list, &mdb_entry->ports_list); + mdb_entry->ports_count++; + + return mdb_entry_port; + +err_mdb_entry_port_alloc: + mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, false); + return ERR_PTR(err); +} + +static __always_unused void +mlxsw_sp_mdb_entry_port_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mdb_entry *mdb_entry, + u16 local_port, bool force) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); + if (!mdb_entry_port) + return; + + if (!force && !refcount_dec_and_test(&mdb_entry_port->refcount)) { + if (mdb_entry_port->mrouter && + refcount_read(&mdb_entry_port->refcount) == 1) + mdb_entry->ports_count--; + return; + } + + mdb_entry->ports_count--; + list_del(&mdb_entry_port->list); + kfree(mdb_entry_port); + mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, false); +} + +static __always_unused struct mlxsw_sp_mdb_entry_port * +mlxsw_sp_mdb_entry_mrouter_port_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mdb_entry *mdb_entry, + u16 local_port) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + int err; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); + if (mdb_entry_port) { + if (!mdb_entry_port->mrouter) + refcount_inc(&mdb_entry_port->refcount); + return mdb_entry_port; + } + + err = mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, true); + if (err) + return ERR_PTR(err); + + mdb_entry_port = kzalloc(sizeof(*mdb_entry_port), GFP_KERNEL); + if (!mdb_entry_port) { + err = -ENOMEM; + goto err_mdb_entry_port_alloc; + } + + mdb_entry_port->local_port = local_port; + refcount_set(&mdb_entry_port->refcount, 1); + mdb_entry_port->mrouter = true; + list_add(&mdb_entry_port->list, &mdb_entry->ports_list); + + return mdb_entry_port; + +err_mdb_entry_port_alloc: + mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, false); + return ERR_PTR(err); +} + +static __always_unused void +mlxsw_sp_mdb_entry_mrouter_port_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mdb_entry *mdb_entry, + u16 local_port) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); + if (!mdb_entry_port) + return; + + if (!mdb_entry_port->mrouter) + return; + + mdb_entry_port->mrouter = false; + if (!refcount_dec_and_test(&mdb_entry_port->refcount)) + return; + + list_del(&mdb_entry_port->list); + kfree(mdb_entry_port); + mlxsw_sp_pgt_entry_port_set(mlxsw_sp, mdb_entry->mid, + mdb_entry->key.fid, local_port, false); +} + static void mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, From ea0f58d6c54325b9d9ff4f5e2649e07a67faad59 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:04 +0300 Subject: [PATCH 07/10] mlxsw: spectrum_switchdev: Implement mlxsw_sp_mc_mdb_entry_{init, fini}() The next patches will convert MDB code to use PGT APIs. The change will move the responsibility of allocating MID indexes and writing PGT configurations to hardware to PGT code. As part of this change, most of the MDB code will be changed and improved. As a preparation for the above mentioned change, implement mlxsw_sp_mc_mdb_entry_{init, fini}(). Currently, there is a function __mlxsw_sp_mc_alloc(), which does not only allocate MID. In addition, there is no an equivalent function to free the MID. When mlxsw_sp_port_remove_from_mid() removes the last port, it handles MID removal. Instead, add init() and fini() functions, which use PGT APIs. The differences between the existing and the new functions are as follows: 1. Today MDB code does not update SMID when port is added/removed while multicast is disabled. It maintains a bitmap of ports and once multicast is enabled, it writes the entry to hardware. Instead, using PGT APIs, the entry will be updated also when multicast is disabled, but the mapping between {MAC, FID}->{MID} (is configured using SFD) will be updated according to multicast state. It means that SMID will be updated all the time and disable/enable multicast will impact only SFD configuration. 2. Today the allocation of MID index is done as part of mlxsw_sp_mc_write_mdb_entry(). The fact that the entry will be written in hardware all the time, moves the allocation of the index to be as part of the MDB entry initialization. PGT API is used for the allocation. 3. Today the update of multicast router ports is done as part of mlxsw_sp_mc_write_mdb_entry(). Instead, add functions to add/remove all multicast router ports when entry is first added or removed. When new multicast router port will be added/removed, the dedicated API will be used to add/remove it from the existing entries. 4. A list of ports will be stored per MDB entry instead of the exiting bitmap. The list will contain the multicast router ports and maintain reference counter per port. Add mlxsw_sp_mdb_entry_write() which is almost identical to mlxsw_sp_port_mdb_op(). Use more clear name and align the MID index to bridge model using PGT API. The existing function will be removed in the next patches. Note that PGT APIs configure the firmware using SMID register, like the driver already does today for MDB entries, so PGT APIs can be used also using legacy bridge model. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 176 +++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index d1b0eddad504..bb2694ef6220 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -1018,7 +1018,7 @@ mlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry, return NULL; } -static __always_unused struct mlxsw_sp_mdb_entry_port * +static struct mlxsw_sp_mdb_entry_port * mlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port) @@ -1060,7 +1060,7 @@ mlxsw_sp_mdb_entry_port_get(struct mlxsw_sp *mlxsw_sp, return ERR_PTR(err); } -static __always_unused void +static void mlxsw_sp_mdb_entry_port_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port, bool force) @@ -1801,6 +1801,37 @@ mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port, vid, adding, false); } +static int mlxsw_sp_mdb_entry_write(struct mlxsw_sp *mlxsw_sp, + const struct mlxsw_sp_mdb_entry *mdb_entry, + bool adding) +{ + char *sfd_pl; + u16 mid_idx; + u8 num_rec; + int err; + + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); + if (!sfd_pl) + return -ENOMEM; + + mid_idx = mlxsw_sp_pgt_index_to_mid(mlxsw_sp, mdb_entry->mid); + mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); + mlxsw_reg_sfd_mc_pack(sfd_pl, 0, mdb_entry->key.addr, + mdb_entry->key.fid, MLXSW_REG_SFD_REC_ACTION_NOP, + mid_idx); + num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); + if (err) + goto out; + + if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) + err = -EBUSY; + +out: + kfree(sfd_pl); + return err; +} + static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, u16 fid, u16 mid_idx, bool adding) { @@ -2040,6 +2071,147 @@ static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, return err; } +static int mlxsw_sp_mc_mdb_mrouters_add(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ports_bitmap *ports_bm, + struct mlxsw_sp_mdb_entry *mdb_entry) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + unsigned int nbits = ports_bm->nbits; + int i; + + for_each_set_bit(i, ports_bm->bitmap, nbits) { + mdb_entry_port = mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, + mdb_entry, + i); + if (IS_ERR(mdb_entry_port)) { + nbits = i; + goto err_mrouter_port_get; + } + } + + return 0; + +err_mrouter_port_get: + for_each_set_bit(i, ports_bm->bitmap, nbits) + mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i); + return PTR_ERR(mdb_entry_port); +} + +static void mlxsw_sp_mc_mdb_mrouters_del(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_ports_bitmap *ports_bm, + struct mlxsw_sp_mdb_entry *mdb_entry) +{ + int i; + + for_each_set_bit(i, ports_bm->bitmap, ports_bm->nbits) + mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, i); +} + +static int +mlxsw_sp_mc_mdb_mrouters_set(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_bridge_device *bridge_device, + struct mlxsw_sp_mdb_entry *mdb_entry, bool add) +{ + struct mlxsw_sp_ports_bitmap ports_bm; + int err; + + err = mlxsw_sp_port_bitmap_init(mlxsw_sp, &ports_bm); + if (err) + return err; + + mlxsw_sp_mc_get_mrouters_bitmap(&ports_bm, bridge_device, mlxsw_sp); + + if (add) + err = mlxsw_sp_mc_mdb_mrouters_add(mlxsw_sp, &ports_bm, + mdb_entry); + else + mlxsw_sp_mc_mdb_mrouters_del(mlxsw_sp, &ports_bm, mdb_entry); + + mlxsw_sp_port_bitmap_fini(&ports_bm); + return err; +} + +static __always_unused struct mlxsw_sp_mdb_entry * +mlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_bridge_device *bridge_device, + const unsigned char *addr, u16 fid, u16 local_port) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + struct mlxsw_sp_mdb_entry *mdb_entry; + int err; + + mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); + if (!mdb_entry) + return ERR_PTR(-ENOMEM); + + ether_addr_copy(mdb_entry->key.addr, addr); + mdb_entry->key.fid = fid; + err = mlxsw_sp_pgt_mid_alloc(mlxsw_sp, &mdb_entry->mid); + if (err) + goto err_pgt_mid_alloc; + + INIT_LIST_HEAD(&mdb_entry->ports_list); + + err = mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, + true); + if (err) + goto err_mdb_mrouters_set; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, mdb_entry, + local_port); + if (IS_ERR(mdb_entry_port)) { + err = PTR_ERR(mdb_entry_port); + goto err_mdb_entry_port_get; + } + + if (bridge_device->multicast_enabled) { + err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, true); + if (err) + goto err_mdb_entry_write; + } + + err = rhashtable_insert_fast(&bridge_device->mdb_ht, + &mdb_entry->ht_node, + mlxsw_sp_mdb_ht_params); + if (err) + goto err_rhashtable_insert; + + list_add_tail(&mdb_entry->list, &bridge_device->mdb_list); + + return mdb_entry; + +err_rhashtable_insert: + if (bridge_device->multicast_enabled) + mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false); +err_mdb_entry_write: + mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, false); +err_mdb_entry_port_get: + mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false); +err_mdb_mrouters_set: + mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid); +err_pgt_mid_alloc: + kfree(mdb_entry); + return ERR_PTR(err); +} + +static __always_unused void +mlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_mdb_entry *mdb_entry, + struct mlxsw_sp_bridge_device *bridge_device, + u16 local_port, bool force) +{ + list_del(&mdb_entry->list); + rhashtable_remove_fast(&bridge_device->mdb_ht, &mdb_entry->ht_node, + mlxsw_sp_mdb_ht_params); + if (bridge_device->multicast_enabled) + mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, false); + mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, force); + mlxsw_sp_mc_mdb_mrouters_set(mlxsw_sp, bridge_device, mdb_entry, false); + WARN_ON(!list_empty(&mdb_entry->ports_list)); + mlxsw_sp_pgt_mid_free(mlxsw_sp, mdb_entry->mid); + kfree(mdb_entry); +} + static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_mdb *mdb) { From 7434ed6102c1a9b898affaeb0685cafd4cd7597c Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:05 +0300 Subject: [PATCH 08/10] mlxsw: spectrum_switchdev: Add support for getting and putting MDB entry A previous patch added support for init() and fini() for MDB entries. MDB entry can be updated, ports can be added and removed from the entry. Add get() and put() functions, the first one checks if the entry already exists and otherwise initializes the entry. The second removes the entry just in case that there are no more ports in this entry. Use the list of the ports which was added in a previous patch. When the list contains only one port which is not multicast router, and this port is removed, the MDB entry can be removed. Use 'struct mlxsw_sp_mdb_entry.ports_count' to know how many ports use the entry, regardless the use of multicast router ports. When mlxsw_sp_mc_mdb_entry_put() is called with specific port which supposed to be removed, check if the removal will cause a deletion of the entry. If this is the case, call mlxsw_sp_mc_mdb_entry_fini() which first deletes the MDB entry and then releases the PGT entry, to avoid a temporary situation in which the MDB entry points to an empty PGT entry, as otherwise packets will be temporarily dropped instead of being flooded. The new functions will be used in the next patches. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 80 ++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index bb2694ef6220..5f8136a8db13 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -2131,7 +2131,7 @@ mlxsw_sp_mc_mdb_mrouters_set(struct mlxsw_sp *mlxsw_sp, return err; } -static __always_unused struct mlxsw_sp_mdb_entry * +static struct mlxsw_sp_mdb_entry * mlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, const unsigned char *addr, u16 fid, u16 local_port) @@ -2194,7 +2194,7 @@ mlxsw_sp_mc_mdb_entry_init(struct mlxsw_sp *mlxsw_sp, return ERR_PTR(err); } -static __always_unused void +static void mlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_mdb_entry *mdb_entry, struct mlxsw_sp_bridge_device *bridge_device, @@ -2212,6 +2212,82 @@ mlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp, kfree(mdb_entry); } +static __always_unused struct mlxsw_sp_mdb_entry * +mlxsw_sp_mc_mdb_entry_get(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_bridge_device *bridge_device, + const unsigned char *addr, u16 fid, u16 local_port) +{ + struct mlxsw_sp_mdb_entry_key key = {}; + struct mlxsw_sp_mdb_entry *mdb_entry; + + ether_addr_copy(key.addr, addr); + key.fid = fid; + mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key, + mlxsw_sp_mdb_ht_params); + if (mdb_entry) { + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_get(mlxsw_sp, + mdb_entry, + local_port); + if (IS_ERR(mdb_entry_port)) + return ERR_CAST(mdb_entry_port); + + return mdb_entry; + } + + return mlxsw_sp_mc_mdb_entry_init(mlxsw_sp, bridge_device, addr, fid, + local_port); +} + +static bool +mlxsw_sp_mc_mdb_entry_remove(struct mlxsw_sp_mdb_entry *mdb_entry, + struct mlxsw_sp_mdb_entry_port *removed_entry_port, + bool force) +{ + if (mdb_entry->ports_count > 1) + return false; + + if (force) + return true; + + if (!removed_entry_port->mrouter && + refcount_read(&removed_entry_port->refcount) > 1) + return false; + + if (removed_entry_port->mrouter && + refcount_read(&removed_entry_port->refcount) > 2) + return false; + + return true; +} + +static __always_unused void +mlxsw_sp_mc_mdb_entry_put(struct mlxsw_sp *mlxsw_sp, + struct mlxsw_sp_bridge_device *bridge_device, + struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port, + bool force) +{ + struct mlxsw_sp_mdb_entry_port *mdb_entry_port; + + mdb_entry_port = mlxsw_sp_mdb_entry_port_lookup(mdb_entry, local_port); + if (!mdb_entry_port) + return; + + /* Avoid a temporary situation in which the MDB entry points to an empty + * PGT entry, as otherwise packets will be temporarily dropped instead + * of being flooded. Instead, in this situation, call + * mlxsw_sp_mc_mdb_entry_fini(), which first deletes the MDB entry and + * then releases the PGT entry. + */ + if (mlxsw_sp_mc_mdb_entry_remove(mdb_entry, mdb_entry_port, force)) + mlxsw_sp_mc_mdb_entry_fini(mlxsw_sp, mdb_entry, bridge_device, + local_port, force); + else + mlxsw_sp_mdb_entry_port_put(mlxsw_sp, mdb_entry, local_port, + force); +} + static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_mdb *mdb) { From 4c3f7442770b6f9b83c88f83d7d43126340ae303 Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:06 +0300 Subject: [PATCH 09/10] mlxsw: spectrum_switchdev: Flush port from MDB entries according to FID index Currently, flushing port from all MDB entries is done when the last VLAN is removed. This behavior is inaccurate, as port can be removed while there is another port which uses the same VLAN, in such case, this is not the last port which uses this VLAN and removed, but this port is supposed to be removed from the MDB entries. Flush the port from MDB when it is removed, regardless the state of other ports. Flush only the MDB entries which are relevant for the same FID index. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 5f8136a8db13..0cf442e0dce0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -139,7 +139,8 @@ mlxsw_sp_bridge_port_fdb_flush(struct mlxsw_sp *mlxsw_sp, static void mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_bridge_port *bridge_port); + struct mlxsw_sp_bridge_port *bridge_port, + u16 fid_index); static int mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, @@ -1382,14 +1383,13 @@ mlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) struct mlxsw_sp_bridge_vlan *bridge_vlan; struct mlxsw_sp_bridge_port *bridge_port; u16 vid = mlxsw_sp_port_vlan->vid; - bool last_port, last_vlan; + bool last_port; if (WARN_ON(mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021Q && mlxsw_sp_fid_type(fid) != MLXSW_SP_FID_TYPE_8021D)) return; bridge_port = mlxsw_sp_port_vlan->bridge_port; - last_vlan = list_is_singular(&bridge_port->vlans_list); bridge_vlan = mlxsw_sp_bridge_vlan_find(bridge_port, vid); last_port = list_is_singular(&bridge_vlan->port_vlan_list); @@ -1401,8 +1401,9 @@ mlxsw_sp_port_vlan_bridge_leave(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan) mlxsw_sp_bridge_port_fdb_flush(mlxsw_sp_port->mlxsw_sp, bridge_port, mlxsw_sp_fid_index(fid)); - if (last_vlan) - mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port); + + mlxsw_sp_bridge_port_mdb_flush(mlxsw_sp_port, bridge_port, + mlxsw_sp_fid_index(fid)); mlxsw_sp_port_vlan_fid_leave(mlxsw_sp_port_vlan); @@ -2528,7 +2529,8 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, static void mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_bridge_port *bridge_port) + struct mlxsw_sp_bridge_port *bridge_port, + u16 fid_index) { struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_mdb_entry *mdb_entry, *tmp; @@ -2538,6 +2540,9 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mdb_list, list) { + if (mdb_entry->key.fid != fid_index) + continue; + if (test_bit(local_port, mdb_entry->ports_in_mid)) { __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mdb_entry); From e28cd993b9a46bc1394d0e46646444b12ee5b69b Mon Sep 17 00:00:00 2001 From: Amit Cohen Date: Wed, 29 Jun 2022 12:40:07 +0300 Subject: [PATCH 10/10] mlxsw: spectrum_switchdev: Convert MDB code to use PGT APIs The previous patches added common APIs for maintaining PGT (Port Group Table) table. In the legacy model, software did not interact with this table directly. Instead, it was accessed by firmware in response to registers such as SFTR and SMID. In the new model, software has full control over the PGT table using the SMID register. The configuration of MDB entries is already done via SMID, so the new PGT APIs can be used also using the legacy model, the only difference is that MID index should be aligned to bridge model. See a previous patch which added API for that. The main changes are: - MDB code does not maintain bitmap of ports in MDB entry anymore, instead, it stores a list of ports with additional information. - MDB code does not configure SMID register directly anymore, it will be done via PGT API when port is first added or removed. - Today MDB code does not update SMID when port is added/removed while multicast is disabled. Instead, it maintains bitmap of ports and once multicast is enabled, it rewrite the entry to hardware. Using PGT APIs, the entry will be updated also when multicast is disabled, but the mapping between {MAC, FID}->{MID} will not appear in SFD register. It means that SMID will be updated all the time and disable/enable multicast will impact only SFD configuration. - For multicast router, today only SMID is updated and the bitmap is not updated. Using the new list of ports, there is a reference count for each port, so it can be saved in software also. For such port, 'struct mlxsw_sp_mdb_entry.ports_count' will not be updated and the port in the list will be marked as 'mrouter'. - Finally, `struct mlxsw_sp_mid.in_hw` is not needed anymore. Signed-off-by: Amit Cohen Signed-off-by: Ido Schimmel Signed-off-by: David S. Miller --- .../mellanox/mlxsw/spectrum_switchdev.c | 352 +++--------------- 1 file changed, 48 insertions(+), 304 deletions(-) diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index 0cf442e0dce0..7cabe6e8edeb 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -115,8 +115,6 @@ struct mlxsw_sp_mdb_entry { u16 mid; struct list_head ports_list; u16 ports_count; - bool in_hw; - unsigned long *ports_in_mid; /* bits array */ }; struct mlxsw_sp_mdb_entry_port { @@ -910,6 +908,9 @@ static int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, if (!bridge_port) return 0; + mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port, + is_port_mrouter); + if (!bridge_port->bridge_device->multicast_enabled) goto out; @@ -919,8 +920,6 @@ static int mlxsw_sp_port_attr_mrouter_set(struct mlxsw_sp_port *mlxsw_sp_port, if (err) return err; - mlxsw_sp_port_mrouter_update_mdb(mlxsw_sp_port, bridge_port, - is_port_mrouter); out: bridge_port->mrouter = is_port_mrouter; return 0; @@ -988,23 +987,6 @@ static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port, return err; } -static int mlxsw_sp_smid_router_port_set(struct mlxsw_sp *mlxsw_sp, - u16 mid_idx, bool add) -{ - char *smid2_pl; - int err; - - smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL); - if (!smid2_pl) - return -ENOMEM; - - mlxsw_reg_smid2_pack(smid2_pl, mid_idx, - mlxsw_sp_router_port(mlxsw_sp), add, false, 0); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl); - kfree(smid2_pl); - return err; -} - static struct mlxsw_sp_mdb_entry_port * mlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port) @@ -1154,10 +1136,17 @@ mlxsw_sp_bridge_mrouter_update_mdb(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, bool add) { + u16 local_port = mlxsw_sp_router_port(mlxsw_sp); struct mlxsw_sp_mdb_entry *mdb_entry; - list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) - mlxsw_sp_smid_router_port_set(mlxsw_sp, mdb_entry->mid, add); + list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { + if (add) + mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry, + local_port); + else + mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, + local_port); + } } static int @@ -1833,97 +1822,6 @@ static int mlxsw_sp_mdb_entry_write(struct mlxsw_sp *mlxsw_sp, return err; } -static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr, - u16 fid, u16 mid_idx, bool adding) -{ - char *sfd_pl; - u8 num_rec; - int err; - - sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); - if (!sfd_pl) - return -ENOMEM; - - mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); - mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid, - MLXSW_REG_SFD_REC_ACTION_NOP, mid_idx); - num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); - if (err) - goto out; - - if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) - err = -EBUSY; - -out: - kfree(sfd_pl); - return err; -} - -static int -mlxsw_sp_port_smid_full_entry(struct mlxsw_sp *mlxsw_sp, u16 mid_idx, - const struct mlxsw_sp_ports_bitmap *ports_bm, - bool set_router_port) -{ - char *smid2_pl; - int err, i; - - smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL); - if (!smid2_pl) - return -ENOMEM; - - mlxsw_reg_smid2_pack(smid2_pl, mid_idx, 0, false, false, 0); - for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++) { - if (mlxsw_sp->ports[i]) - mlxsw_reg_smid2_port_mask_set(smid2_pl, i, 1); - } - - mlxsw_reg_smid2_port_mask_set(smid2_pl, - mlxsw_sp_router_port(mlxsw_sp), 1); - - for_each_set_bit(i, ports_bm->bitmap, ports_bm->nbits) - mlxsw_reg_smid2_port_set(smid2_pl, i, 1); - - mlxsw_reg_smid2_port_set(smid2_pl, mlxsw_sp_router_port(mlxsw_sp), - set_router_port); - - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl); - kfree(smid2_pl); - return err; -} - -static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, - u16 mid_idx, bool add) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - char *smid2_pl; - int err; - - smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL); - if (!smid2_pl) - return -ENOMEM; - - mlxsw_reg_smid2_pack(smid2_pl, mid_idx, mlxsw_sp_port->local_port, add, - false, 0); - err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid2), smid2_pl); - kfree(smid2_pl); - return err; -} - -static struct mlxsw_sp_mdb_entry * -__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device, - const unsigned char *addr, u16 fid) -{ - struct mlxsw_sp_mdb_entry *mdb_entry; - - list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { - if (ether_addr_equal(mdb_entry->key.addr, addr) && - mdb_entry->key.fid == fid) - return mdb_entry; - } - return NULL; -} - static void mlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_port *bridge_port, @@ -1965,113 +1863,6 @@ mlxsw_sp_mc_get_mrouters_bitmap(struct mlxsw_sp_ports_bitmap *flood_bm, } } -static int -mlxsw_sp_mc_write_mdb_entry(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_mdb_entry *mdb_entry, - struct mlxsw_sp_bridge_device *bridge_device) -{ - struct mlxsw_sp_ports_bitmap flood_bitmap; - u16 mid_idx; - int err; - - mid_idx = find_first_zero_bit(mlxsw_sp->bridge->mids_bitmap, - MLXSW_SP_MID_MAX); - if (mid_idx == MLXSW_SP_MID_MAX) - return -ENOBUFS; - - err = mlxsw_sp_port_bitmap_init(mlxsw_sp, &flood_bitmap); - if (err) - return err; - - bitmap_copy(flood_bitmap.bitmap, mdb_entry->ports_in_mid, - flood_bitmap.nbits); - mlxsw_sp_mc_get_mrouters_bitmap(&flood_bitmap, bridge_device, mlxsw_sp); - - mdb_entry->mid = mid_idx; - err = mlxsw_sp_port_smid_full_entry(mlxsw_sp, mid_idx, &flood_bitmap, - bridge_device->mrouter); - mlxsw_sp_port_bitmap_fini(&flood_bitmap); - if (err) - return err; - - err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->key.addr, - mdb_entry->key.fid, mid_idx, true); - if (err) - return err; - - set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap); - mdb_entry->in_hw = true; - return 0; -} - -static int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_mdb_entry *mdb_entry) -{ - if (!mdb_entry->in_hw) - return 0; - - clear_bit(mdb_entry->mid, mlxsw_sp->bridge->mids_bitmap); - mdb_entry->in_hw = false; - return mlxsw_sp_port_mdb_op(mlxsw_sp, mdb_entry->key.addr, - mdb_entry->key.fid, mdb_entry->mid, false); -} - -static struct mlxsw_sp_mdb_entry * -__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp, - struct mlxsw_sp_bridge_device *bridge_device, - const unsigned char *addr, u16 fid) -{ - unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); - struct mlxsw_sp_mdb_entry *mdb_entry; - int err; - - mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL); - if (!mdb_entry) - return NULL; - - mdb_entry->ports_in_mid = bitmap_zalloc(max_ports, GFP_KERNEL); - if (!mdb_entry->ports_in_mid) - goto err_ports_in_mid_alloc; - - ether_addr_copy(mdb_entry->key.addr, addr); - mdb_entry->key.fid = fid; - mdb_entry->in_hw = false; - - if (!bridge_device->multicast_enabled) - goto out; - - err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, bridge_device); - if (err) - goto err_write_mdb_entry; - -out: - list_add_tail(&mdb_entry->list, &bridge_device->mdb_list); - return mdb_entry; - -err_write_mdb_entry: - bitmap_free(mdb_entry->ports_in_mid); -err_ports_in_mid_alloc: - kfree(mdb_entry); - return NULL; -} - -static int mlxsw_sp_port_remove_from_mid(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_mdb_entry *mdb_entry) -{ - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; - int err = 0; - - clear_bit(mlxsw_sp_port->local_port, mdb_entry->ports_in_mid); - if (bitmap_empty(mdb_entry->ports_in_mid, - mlxsw_core_max_ports(mlxsw_sp->core))) { - err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); - list_del(&mdb_entry->list); - bitmap_free(mdb_entry->ports_in_mid); - kfree(mdb_entry); - } - return err; -} - static int mlxsw_sp_mc_mdb_mrouters_add(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_ports_bitmap *ports_bm, struct mlxsw_sp_mdb_entry *mdb_entry) @@ -2213,7 +2004,7 @@ mlxsw_sp_mc_mdb_entry_fini(struct mlxsw_sp *mlxsw_sp, kfree(mdb_entry); } -static __always_unused struct mlxsw_sp_mdb_entry * +static struct mlxsw_sp_mdb_entry * mlxsw_sp_mc_mdb_entry_get(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, const unsigned char *addr, u16 fid, u16 local_port) @@ -2263,7 +2054,7 @@ mlxsw_sp_mc_mdb_entry_remove(struct mlxsw_sp_mdb_entry *mdb_entry, return true; } -static __always_unused void +static void mlxsw_sp_mc_mdb_entry_put(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_bridge_device *bridge_device, struct mlxsw_sp_mdb_entry *mdb_entry, u16 local_port, @@ -2295,12 +2086,10 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *orig_dev = mdb->obj.orig_dev; struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; - struct net_device *dev = mlxsw_sp_port->dev; struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_bridge_port *bridge_port; struct mlxsw_sp_mdb_entry *mdb_entry; u16 fid_index; - int err = 0; bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); if (!bridge_port) @@ -2315,34 +2104,13 @@ static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port, fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - mdb_entry = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); - if (!mdb_entry) { - mdb_entry = __mlxsw_sp_mc_alloc(mlxsw_sp, bridge_device, - mdb->addr, fid_index); - if (!mdb_entry) { - netdev_err(dev, "Unable to allocate MC group\n"); - return -ENOMEM; - } - } - set_bit(mlxsw_sp_port->local_port, mdb_entry->ports_in_mid); - - if (!bridge_device->multicast_enabled) - return 0; - - if (bridge_port->mrouter) - return 0; - - err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, true); - if (err) { - netdev_err(dev, "Unable to set SMID\n"); - goto err_out; - } + mdb_entry = mlxsw_sp_mc_mdb_entry_get(mlxsw_sp, bridge_device, + mdb->addr, fid_index, + mlxsw_sp_port->local_port); + if (IS_ERR(mdb_entry)) + return PTR_ERR(mdb_entry); return 0; - -err_out: - mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mdb_entry); - return err; } static int @@ -2354,27 +2122,16 @@ mlxsw_sp_bridge_mdb_mc_enable_sync(struct mlxsw_sp *mlxsw_sp, int err; list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { - if (mc_enabled) - err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, - bridge_device); - else - err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); - + err = mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, mc_enabled); if (err) - goto err_mdb_entry_update; + goto err_mdb_entry_write; } - return 0; -err_mdb_entry_update: +err_mdb_entry_write: list_for_each_entry_continue_reverse(mdb_entry, - &bridge_device->mdb_list, list) { - if (mc_enabled) - mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mdb_entry); - else - mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mdb_entry, - bridge_device); - } + &bridge_device->mdb_list, list) + mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, !mc_enabled); return err; } @@ -2383,16 +2140,20 @@ mlxsw_sp_port_mrouter_update_mdb(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_port *bridge_port, bool add) { + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_bridge_device *bridge_device; + u16 local_port = mlxsw_sp_port->local_port; struct mlxsw_sp_mdb_entry *mdb_entry; bridge_device = bridge_port->bridge_device; list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) { - if (!test_bit(mlxsw_sp_port->local_port, - mdb_entry->ports_in_mid)) - mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, - add); + if (add) + mlxsw_sp_mdb_entry_mrouter_port_get(mlxsw_sp, mdb_entry, + local_port); + else + mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, mdb_entry, + local_port); } } @@ -2470,29 +2231,6 @@ static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, return 0; } -static int -__mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, - struct mlxsw_sp_bridge_port *bridge_port, - struct mlxsw_sp_mdb_entry *mdb_entry) -{ - struct net_device *dev = mlxsw_sp_port->dev; - int err; - - if (bridge_port->bridge_device->multicast_enabled && - !bridge_port->mrouter) { - err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, - false); - if (err) - netdev_err(dev, "Unable to remove port from SMID\n"); - } - - err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mdb_entry); - if (err) - netdev_err(dev, "Unable to remove MC SFD\n"); - - return err; -} - static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, const struct switchdev_obj_port_mdb *mdb) { @@ -2502,6 +2240,7 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_device *bridge_device; struct net_device *dev = mlxsw_sp_port->dev; struct mlxsw_sp_bridge_port *bridge_port; + struct mlxsw_sp_mdb_entry_key key = {}; struct mlxsw_sp_mdb_entry *mdb_entry; u16 fid_index; @@ -2518,13 +2257,18 @@ static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port, fid_index = mlxsw_sp_fid_index(mlxsw_sp_port_vlan->fid); - mdb_entry = __mlxsw_sp_mc_get(bridge_device, mdb->addr, fid_index); + ether_addr_copy(key.addr, mdb->addr); + key.fid = fid_index; + mdb_entry = rhashtable_lookup_fast(&bridge_device->mdb_ht, &key, + mlxsw_sp_mdb_ht_params); if (!mdb_entry) { netdev_err(dev, "Unable to remove port from MC DB\n"); return -EINVAL; } - return __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, mdb_entry); + mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry, + mlxsw_sp_port->local_port, false); + return 0; } static void @@ -2532,6 +2276,7 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, struct mlxsw_sp_bridge_port *bridge_port, u16 fid_index) { + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct mlxsw_sp_bridge_device *bridge_device; struct mlxsw_sp_mdb_entry *mdb_entry, *tmp; u16 local_port = mlxsw_sp_port->local_port; @@ -2543,14 +2288,13 @@ mlxsw_sp_bridge_port_mdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, if (mdb_entry->key.fid != fid_index) continue; - if (test_bit(local_port, mdb_entry->ports_in_mid)) { - __mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port, - mdb_entry); - } else if (bridge_device->multicast_enabled && - bridge_port->mrouter) { - mlxsw_sp_port_smid_set(mlxsw_sp_port, mdb_entry->mid, - false); - } + if (bridge_port->mrouter) + mlxsw_sp_mdb_entry_mrouter_port_put(mlxsw_sp, + mdb_entry, + local_port); + + mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry, + local_port, true); } }