|
@@ -2706,10 +2706,135 @@ static int mlxsw_sp_inetaddr_lag_event(struct net_device *lag_dev,
|
|
|
return __mlxsw_sp_inetaddr_lag_event(lag_dev, lag_dev, event, 1);
|
|
|
}
|
|
|
|
|
|
+static struct mlxsw_sp_fid *mlxsw_sp_bridge_fid_get(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct net_device *l3_dev)
|
|
|
+{
|
|
|
+ u16 fid;
|
|
|
+
|
|
|
+ if (is_vlan_dev(l3_dev))
|
|
|
+ fid = vlan_dev_vlan_id(l3_dev);
|
|
|
+ else if (mlxsw_sp->master_bridge.dev == l3_dev)
|
|
|
+ fid = 1;
|
|
|
+ else
|
|
|
+ return mlxsw_sp_vfid_find(mlxsw_sp, l3_dev);
|
|
|
+
|
|
|
+ return mlxsw_sp_fid_find(mlxsw_sp, fid);
|
|
|
+}
|
|
|
+
|
|
|
+static enum mlxsw_reg_ritr_if_type mlxsw_sp_rif_type_get(u16 fid)
|
|
|
+{
|
|
|
+ if (mlxsw_sp_fid_is_vfid(fid))
|
|
|
+ return MLXSW_REG_RITR_FID_IF;
|
|
|
+ else
|
|
|
+ return MLXSW_REG_RITR_VLAN_IF;
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_rif_bridge_op(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct net_device *l3_dev,
|
|
|
+ u16 fid, u16 rif,
|
|
|
+ bool create)
|
|
|
+{
|
|
|
+ enum mlxsw_reg_ritr_if_type rif_type;
|
|
|
+ char ritr_pl[MLXSW_REG_RITR_LEN];
|
|
|
+
|
|
|
+ rif_type = mlxsw_sp_rif_type_get(fid);
|
|
|
+ mlxsw_reg_ritr_pack(ritr_pl, create, rif_type, rif, l3_dev->mtu,
|
|
|
+ l3_dev->dev_addr);
|
|
|
+ mlxsw_reg_ritr_fid_set(ritr_pl, rif_type, fid);
|
|
|
+
|
|
|
+ return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(ritr), ritr_pl);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_rif_bridge_create(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct net_device *l3_dev,
|
|
|
+ struct mlxsw_sp_fid *f)
|
|
|
+{
|
|
|
+ struct mlxsw_sp_rif *r;
|
|
|
+ u16 rif;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ rif = mlxsw_sp_avail_rif_get(mlxsw_sp);
|
|
|
+ if (rif == MLXSW_SP_RIF_MAX)
|
|
|
+ return -ERANGE;
|
|
|
+
|
|
|
+ err = mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, true);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ err = mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, true);
|
|
|
+ if (err)
|
|
|
+ goto err_rif_fdb_op;
|
|
|
+
|
|
|
+ r = mlxsw_sp_rif_alloc(rif, l3_dev, f);
|
|
|
+ if (!r) {
|
|
|
+ err = -ENOMEM;
|
|
|
+ goto err_rif_alloc;
|
|
|
+ }
|
|
|
+
|
|
|
+ f->r = r;
|
|
|
+ mlxsw_sp->rifs[rif] = r;
|
|
|
+
|
|
|
+ netdev_dbg(l3_dev, "RIF=%d created\n", rif);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_rif_alloc:
|
|
|
+ mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
|
|
|
+err_rif_fdb_op:
|
|
|
+ mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+void mlxsw_sp_rif_bridge_destroy(struct mlxsw_sp *mlxsw_sp,
|
|
|
+ struct mlxsw_sp_rif *r)
|
|
|
+{
|
|
|
+ struct net_device *l3_dev = r->dev;
|
|
|
+ struct mlxsw_sp_fid *f = r->f;
|
|
|
+ u16 rif = r->rif;
|
|
|
+
|
|
|
+ mlxsw_sp->rifs[rif] = NULL;
|
|
|
+ f->r = NULL;
|
|
|
+
|
|
|
+ kfree(r);
|
|
|
+
|
|
|
+ mlxsw_sp_rif_fdb_op(mlxsw_sp, l3_dev->dev_addr, f->fid, false);
|
|
|
+
|
|
|
+ mlxsw_sp_rif_bridge_op(mlxsw_sp, l3_dev, f->fid, rif, false);
|
|
|
+
|
|
|
+ netdev_dbg(l3_dev, "RIF=%d destroyed\n", rif);
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_inetaddr_bridge_event(struct net_device *l3_dev,
|
|
|
+ struct net_device *br_dev,
|
|
|
+ unsigned long event)
|
|
|
+{
|
|
|
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(l3_dev);
|
|
|
+ struct mlxsw_sp_fid *f;
|
|
|
+
|
|
|
+ /* FID can either be an actual FID if the L3 device is the
|
|
|
+ * VLAN-aware bridge or a VLAN device on top. Otherwise, the
|
|
|
+ * L3 device is a VLAN-unaware bridge and we get a vFID.
|
|
|
+ */
|
|
|
+ f = mlxsw_sp_bridge_fid_get(mlxsw_sp, l3_dev);
|
|
|
+ if (WARN_ON(!f))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ switch (event) {
|
|
|
+ case NETDEV_UP:
|
|
|
+ return mlxsw_sp_rif_bridge_create(mlxsw_sp, l3_dev, f);
|
|
|
+ case NETDEV_DOWN:
|
|
|
+ mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
|
|
|
unsigned long event)
|
|
|
{
|
|
|
struct net_device *real_dev = vlan_dev_real_dev(vlan_dev);
|
|
|
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(vlan_dev);
|
|
|
u16 vid = vlan_dev_vlan_id(vlan_dev);
|
|
|
|
|
|
if (mlxsw_sp_port_dev_check(real_dev))
|
|
@@ -2718,6 +2843,10 @@ static int mlxsw_sp_inetaddr_vlan_event(struct net_device *vlan_dev,
|
|
|
else if (netif_is_lag_master(real_dev))
|
|
|
return __mlxsw_sp_inetaddr_lag_event(vlan_dev, real_dev, event,
|
|
|
vid);
|
|
|
+ else if (netif_is_bridge_master(real_dev) &&
|
|
|
+ mlxsw_sp->master_bridge.dev == real_dev)
|
|
|
+ return mlxsw_sp_inetaddr_bridge_event(vlan_dev, real_dev,
|
|
|
+ event);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -2743,6 +2872,8 @@ static int mlxsw_sp_inetaddr_event(struct notifier_block *unused,
|
|
|
err = mlxsw_sp_inetaddr_port_event(dev, event);
|
|
|
else if (netif_is_lag_master(dev))
|
|
|
err = mlxsw_sp_inetaddr_lag_event(dev, event);
|
|
|
+ else if (netif_is_bridge_master(dev))
|
|
|
+ err = mlxsw_sp_inetaddr_bridge_event(dev, dev, event);
|
|
|
else if (is_vlan_dev(dev))
|
|
|
err = mlxsw_sp_inetaddr_vlan_event(dev, event);
|
|
|
|
|
@@ -3416,6 +3547,8 @@ static void mlxsw_sp_master_bridge_vlan_unlink(struct mlxsw_sp *mlxsw_sp,
|
|
|
struct mlxsw_sp_fid *f;
|
|
|
|
|
|
f = mlxsw_sp_fid_find(mlxsw_sp, fid);
|
|
|
+ if (f && f->r)
|
|
|
+ mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r);
|
|
|
if (f && --f->ref_count == 0)
|
|
|
mlxsw_sp_fid_destroy(mlxsw_sp, f);
|
|
|
}
|
|
@@ -3514,13 +3647,17 @@ static void mlxsw_sp_vfid_destroy(struct mlxsw_sp *mlxsw_sp,
|
|
|
struct mlxsw_sp_fid *f)
|
|
|
{
|
|
|
u16 vfid = mlxsw_sp_fid_to_vfid(f->fid);
|
|
|
+ u16 fid = f->fid;
|
|
|
|
|
|
clear_bit(vfid, mlxsw_sp->vfids.mapped);
|
|
|
list_del(&f->list);
|
|
|
|
|
|
- mlxsw_sp_vfid_op(mlxsw_sp, f->fid, false);
|
|
|
+ if (f->r)
|
|
|
+ mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->r);
|
|
|
|
|
|
kfree(f);
|
|
|
+
|
|
|
+ mlxsw_sp_vfid_op(mlxsw_sp, fid, false);
|
|
|
}
|
|
|
|
|
|
static int mlxsw_sp_vport_fid_map(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
|