mlxsw: spectrum: Configure FIDs based on bridge events

Before introducing support for L3 interfaces on top of the VLAN-aware
bridge we need to add some missing infrastructure.

Such an interface can either be the bridge device itself or a VLAN
device on top of it. In the first case the router interface (RIF) is
associated with FID 1, which is created whenever the first port netdev
joins the bridge. We currently assume the default PVID is 1 and that
it's already created, as it seems reasonable. This can be extended in
the future.

However, in the second case it's entirely possible we've yet to create a
matching FID. This can happen if the VLAN device was configured before
making any bridge port member in the VLAN.

Prevent such ordering problems by using the VLAN device's CHANGEUPPER
event to configure the FID. Make the VLAN device hold a reference to the
FID and prevent it from being destroyed even if none of the port netdevs
is using it.

Signed-off-by: Ido Schimmel <idosch@mellanox.com>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Ido Schimmel 2016-07-04 08:23:16 +02:00 committed by David S. Miller
parent 3ba2ebf4a2
commit 701b186ebf
3 changed files with 107 additions and 24 deletions

View File

@ -2887,6 +2887,17 @@ int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid); return mlxsw_sp_port_fdb_flush_by_port_fid(mlxsw_sp_port, fid);
} }
static void mlxsw_sp_master_bridge_gone_sync(struct mlxsw_sp *mlxsw_sp)
{
struct mlxsw_sp_fid *f, *tmp;
list_for_each_entry_safe(f, tmp, &mlxsw_sp->fids, list)
if (--f->ref_count == 0)
mlxsw_sp_fid_destroy(mlxsw_sp, f);
else
WARN_ON_ONCE(1);
}
static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp, static bool mlxsw_sp_master_bridge_check(struct mlxsw_sp *mlxsw_sp,
struct net_device *br_dev) struct net_device *br_dev)
{ {
@ -2903,8 +2914,15 @@ static void mlxsw_sp_master_bridge_inc(struct mlxsw_sp *mlxsw_sp,
static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp) static void mlxsw_sp_master_bridge_dec(struct mlxsw_sp *mlxsw_sp)
{ {
if (--mlxsw_sp->master_bridge.ref_count == 0) if (--mlxsw_sp->master_bridge.ref_count == 0) {
mlxsw_sp->master_bridge.dev = NULL; mlxsw_sp->master_bridge.dev = NULL;
/* It's possible upper VLAN devices are still holding
* references to underlying FIDs. Drop the reference
* and release the resources if it was the last one.
* If it wasn't, then something bad happened.
*/
mlxsw_sp_master_bridge_gone_sync(mlxsw_sp);
}
} }
static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port,
@ -3373,18 +3391,68 @@ static int mlxsw_sp_netdevice_lag_event(struct net_device *lag_dev,
return 0; return 0;
} }
static struct mlxsw_sp_fid * static int mlxsw_sp_master_bridge_vlan_link(struct mlxsw_sp *mlxsw_sp,
mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp, struct net_device *vlan_dev)
const struct net_device *br_dev)
{ {
u16 fid = vlan_dev_vlan_id(vlan_dev);
struct mlxsw_sp_fid *f; struct mlxsw_sp_fid *f;
list_for_each_entry(f, &mlxsw_sp->vfids.list, list) { f = mlxsw_sp_fid_find(mlxsw_sp, fid);
if (f->dev == br_dev) if (!f) {
return f; f = mlxsw_sp_fid_create(mlxsw_sp, fid);
if (IS_ERR(f))
return PTR_ERR(f);
} }
return NULL; f->ref_count++;
return 0;
}
static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp,
struct net_device *vlan_dev)
{
u16 fid = vlan_dev_vlan_id(vlan_dev);
struct mlxsw_sp_fid *f;
f = mlxsw_sp_fid_find(mlxsw_sp, fid);
if (f && --f->ref_count == 0)
mlxsw_sp_fid_destroy(mlxsw_sp, f);
}
static int mlxsw_sp_netdevice_bridge_event(struct net_device *br_dev,
unsigned long event, void *ptr)
{
struct netdev_notifier_changeupper_info *info;
struct net_device *upper_dev;
struct mlxsw_sp *mlxsw_sp;
int err;
mlxsw_sp = mlxsw_sp_lower_get(br_dev);
if (!mlxsw_sp)
return 0;
if (br_dev != mlxsw_sp->master_bridge.dev)
return 0;
info = ptr;
switch (event) {
case NETDEV_CHANGEUPPER:
upper_dev = info->upper_dev;
if (!is_vlan_dev(upper_dev))
break;
if (info->linking) {
err = mlxsw_sp_master_bridge_vlan_link(mlxsw_sp,
upper_dev);
if (err)
return err;
} else {
mlxsw_sp_master_bridge_vlan_unlink(mlxsw_sp, upper_dev);
}
break;
}
return 0;
} }
static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp) static u16 mlxsw_sp_avail_vfid_get(const struct mlxsw_sp *mlxsw_sp)
@ -3675,6 +3743,8 @@ static int mlxsw_sp_netdevice_event(struct notifier_block *unused,
err = mlxsw_sp_netdevice_port_event(dev, event, ptr); err = mlxsw_sp_netdevice_port_event(dev, event, ptr);
else if (netif_is_lag_master(dev)) else if (netif_is_lag_master(dev))
err = mlxsw_sp_netdevice_lag_event(dev, event, ptr); err = mlxsw_sp_netdevice_lag_event(dev, event, ptr);
else if (netif_is_bridge_master(dev))
err = mlxsw_sp_netdevice_bridge_event(dev, event, ptr);
else if (is_vlan_dev(dev)) else if (is_vlan_dev(dev))
err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr); err = mlxsw_sp_netdevice_vlan_event(dev, event, ptr);

View File

@ -387,6 +387,31 @@ mlxsw_sp_port_vport_find_by_fid(const struct mlxsw_sp_port *mlxsw_sp_port,
return NULL; return NULL;
} }
static inline struct mlxsw_sp_fid *mlxsw_sp_fid_find(struct mlxsw_sp *mlxsw_sp,
u16 fid)
{
struct mlxsw_sp_fid *f;
list_for_each_entry(f, &mlxsw_sp->fids, list)
if (f->fid == fid)
return f;
return NULL;
}
static inline struct mlxsw_sp_fid *
mlxsw_sp_vfid_find(const struct mlxsw_sp *mlxsw_sp,
const struct net_device *br_dev)
{
struct mlxsw_sp_fid *f;
list_for_each_entry(f, &mlxsw_sp->vfids.list, list)
if (f->dev == br_dev)
return f;
return NULL;
}
static inline struct mlxsw_sp_rif * static inline struct mlxsw_sp_rif *
mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp, mlxsw_sp_rif_find_by_dev(const struct mlxsw_sp *mlxsw_sp,
const struct net_device *dev) const struct net_device *dev)
@ -459,6 +484,8 @@ int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid);
int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid); int mlxsw_sp_port_fdb_flush(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid);
int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid, int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
bool adding); bool adding);
struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid);
void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f);
int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port, int mlxsw_sp_port_ets_set(struct mlxsw_sp_port *mlxsw_sp_port,
enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index, enum mlxsw_reg_qeec_hr hr, u8 index, u8 next_index,
bool dwrr, u8 dwrr_weight); bool dwrr, u8 dwrr_weight);

View File

@ -374,18 +374,6 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
return err; return err;
} }
static struct mlxsw_sp_fid *mlxsw_sp_fid_find(struct mlxsw_sp *mlxsw_sp,
u16 fid)
{
struct mlxsw_sp_fid *f;
list_for_each_entry(f, &mlxsw_sp->fids, list)
if (f->fid == fid)
return f;
return NULL;
}
static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create) static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create)
{ {
char sfmr_pl[MLXSW_REG_SFMR_LEN]; char sfmr_pl[MLXSW_REG_SFMR_LEN];
@ -416,8 +404,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_alloc(u16 fid)
return f; return f;
} }
static struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid)
u16 fid)
{ {
struct mlxsw_sp_fid *f; struct mlxsw_sp_fid *f;
int err; int err;
@ -452,8 +439,7 @@ static struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp,
return ERR_PTR(err); return ERR_PTR(err);
} }
static void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f)
struct mlxsw_sp_fid *f)
{ {
u16 fid = f->fid; u16 fid = f->fid;