Merge branch 'mlxsw-unified-bridge-conversion-part-5'

Ido Schimmel says:

====================
mlxsw: Unified bridge conversion - part 5/6

This is the fifth part of the conversion of mlxsw to the unified bridge
model.

The previous part that was merged in commit d521bc0a0f ("Merge branch
'mlxsw-unified-bridge-conversion-part-4-6'") converted the flooding code
to use the new APIs of the unified bridge model. As part of this
conversion, the flooding code started accessing the port group table
(PGT) directly in order to allocate MID indexes and configure the ports
via which a packet needs to be replicated.

MDB entries in the device also make use of the PGT table, but the
related code has its own PGT allocator and does not make use of the
common core that was added in the previous patchset. This patchset
converts the MDB code to use the common PGT code.

The first nine patches prepare the MDB code for the conversion that is
performed by the last patch.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2022-06-29 13:35:47 +01:00
commit da8ff2a278
3 changed files with 480 additions and 293 deletions

View file

@ -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;
@ -1485,6 +1476,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);

View file

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

View file

@ -48,7 +48,8 @@ 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;
struct rhashtable mdb_ht;
u8 vlan_enabled:1,
multicast_enabled:1,
mrouter:1;
@ -102,6 +103,33 @@ struct mlxsw_sp_switchdev_ops {
void (*init)(struct mlxsw_sp *mlxsw_sp);
};
struct mlxsw_sp_mdb_entry_key {
unsigned char addr[ETH_ALEN];
u16 fid;
};
struct mlxsw_sp_mdb_entry {
struct list_head list;
struct rhash_head ht_node;
struct mlxsw_sp_mdb_entry_key key;
u16 mid;
struct list_head ports_list;
u16 ports_count;
};
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),
.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,
@ -109,7 +137,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,
@ -237,6 +266,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);
@ -254,7 +287,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);
@ -273,6 +307,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);
}
@ -290,7 +326,8 @@ 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));
rhashtable_destroy(&bridge_device->mdb_ht);
kfree(bridge_device);
}
@ -871,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;
@ -880,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;
@ -949,21 +987,148 @@ 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)
static struct mlxsw_sp_mdb_entry_port *
mlxsw_sp_mdb_entry_port_lookup(struct mlxsw_sp_mdb_entry *mdb_entry,
u16 local_port)
{
char *smid2_pl;
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 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;
smid2_pl = kmalloc(MLXSW_REG_SMID2_LEN, GFP_KERNEL);
if (!smid2_pl)
return -ENOMEM;
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++;
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;
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 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
@ -971,10 +1136,17 @@ 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;
u16 local_port = mlxsw_sp_router_port(mlxsw_sp);
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->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
@ -1200,14 +1372,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);
@ -1219,8 +1390,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);
@ -1619,10 +1791,12 @@ mlxsw_sp_port_fdb_set(struct mlxsw_sp_port *mlxsw_sp_port,
vid, adding, false);
}
static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
u16 fid, u16 mid_idx, bool adding)
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;
@ -1630,9 +1804,11 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
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, addr, fid,
MLXSW_REG_SFD_REC_ACTION_NOP, mid_idx);
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)
@ -1646,70 +1822,6 @@ static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
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_mid *__mlxsw_sp_mc_get(struct mlxsw_sp_bridge_device *bridge_device,
const unsigned char *addr,
u16 fid)
{
struct mlxsw_sp_mid *mid;
list_for_each_entry(mid, &bridge_device->mids_list, list) {
if (ether_addr_equal(mid->addr, addr) && mid->fid == fid)
return mid;
}
return NULL;
}
static void
mlxsw_sp_bridge_port_get_ports_bitmap(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_bridge_port *bridge_port,
@ -1751,125 +1863,233 @@ 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_bridge_device *bridge_device)
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_ports_bitmap flood_bitmap;
u16 mid_idx;
int err;
struct mlxsw_sp_mdb_entry_port *mdb_entry_port;
unsigned int nbits = ports_bm->nbits;
int i;
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, mid->ports_in_mid, flood_bitmap.nbits);
mlxsw_sp_mc_get_mrouters_bitmap(&flood_bitmap, bridge_device, mlxsw_sp);
mid->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);
if (err)
return err;
set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap);
mid->in_hw = true;
return 0;
}
static int mlxsw_sp_mc_remove_mdb_entry(struct mlxsw_sp *mlxsw_sp,
struct mlxsw_sp_mid *mid)
{
if (!mid->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);
}
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)
{
struct mlxsw_sp_mid *mid;
int err;
mid = kzalloc(sizeof(*mid), GFP_KERNEL);
if (!mid)
return NULL;
mid->ports_in_mid = bitmap_zalloc(mlxsw_core_max_ports(mlxsw_sp->core),
GFP_KERNEL);
if (!mid->ports_in_mid)
goto err_ports_in_mid_alloc;
ether_addr_copy(mid->addr, addr);
mid->fid = fid;
mid->in_hw = false;
if (!bridge_device->multicast_enabled)
goto out;
err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid, bridge_device);
if (err)
goto err_write_mdb_entry;
out:
list_add_tail(&mid->list, &bridge_device->mids_list);
return mid;
err_write_mdb_entry:
bitmap_free(mid->ports_in_mid);
err_ports_in_mid_alloc:
kfree(mid);
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 *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,
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);
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 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 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 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 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)
{
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_mid *mid;
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)
@ -1884,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);
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) {
netdev_err(dev, "Unable to allocate MC group\n");
return -ENOMEM;
}
}
set_bit(mlxsw_sp_port->local_port, mid->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, mid->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, mid);
return err;
}
static int
@ -1919,31 +2118,20 @@ 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) {
if (mc_enabled)
err = mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid,
bridge_device);
else
err = mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid);
list_for_each_entry(mdb_entry, &bridge_device->mdb_list, list) {
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:
list_for_each_entry_continue_reverse(mid, &bridge_device->mids_list,
list) {
if (mc_enabled)
mlxsw_sp_mc_remove_mdb_entry(mlxsw_sp, mid);
else
mlxsw_sp_mc_write_mdb_entry(mlxsw_sp, mid,
bridge_device);
}
err_mdb_entry_write:
list_for_each_entry_continue_reverse(mdb_entry,
&bridge_device->mdb_list, list)
mlxsw_sp_mdb_entry_write(mlxsw_sp, mdb_entry, !mc_enabled);
return err;
}
@ -1952,14 +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;
struct mlxsw_sp_mid *mid;
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(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->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);
}
}
@ -2037,28 +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_mid *mid)
{
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);
if (err)
netdev_err(dev, "Unable to remove port from SMID\n");
}
err = mlxsw_sp_port_remove_from_mid(mlxsw_sp_port, mid);
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)
{
@ -2068,7 +2240,8 @@ 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_key key = {};
struct mlxsw_sp_mdb_entry *mdb_entry;
u16 fid_index;
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev);
@ -2084,32 +2257,44 @@ 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) {
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, mid);
mlxsw_sp_mc_mdb_entry_put(mlxsw_sp, bridge_device, mdb_entry,
mlxsw_sp_port->local_port, false);
return 0;
}
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 *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
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)) {
__mlxsw_sp_port_mdb_del(mlxsw_sp_port, bridge_port,
mid);
} else if (bridge_device->multicast_enabled &&
bridge_port->mrouter) {
mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false);
}
list_for_each_entry_safe(mdb_entry, tmp, &bridge_device->mdb_list,
list) {
if (mdb_entry->key.fid != fid_index)
continue;
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);
}
}