|
@@ -66,7 +66,8 @@ static int mlxsw_sp_port_attr_get(struct net_device *dev,
|
|
|
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
|
|
|
attr->u.brport_flags =
|
|
|
(mlxsw_sp_port->learning ? BR_LEARNING : 0) |
|
|
|
- (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0);
|
|
|
+ (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0) |
|
|
|
+ (mlxsw_sp_port->uc_flood ? BR_FLOOD : 0);
|
|
|
break;
|
|
|
default:
|
|
|
return -EOPNOTSUPP;
|
|
@@ -123,15 +124,89 @@ static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
return mlxsw_sp_port_stp_state_set(mlxsw_sp_port, state);
|
|
|
}
|
|
|
|
|
|
+static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
+ u16 fid_begin, u16 fid_end, bool set,
|
|
|
+ bool only_uc)
|
|
|
+{
|
|
|
+ struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
+ u16 range = fid_end - fid_begin + 1;
|
|
|
+ char *sftr_pl;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
|
|
|
+ if (!sftr_pl)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid_begin,
|
|
|
+ MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
|
|
|
+ mlxsw_sp_port->local_port, set);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
|
|
|
+ if (err)
|
|
|
+ goto buffer_out;
|
|
|
+
|
|
|
+ /* Flooding control allows one to decide whether a given port will
|
|
|
+ * flood unicast traffic for which there is no FDB entry.
|
|
|
+ */
|
|
|
+ if (only_uc)
|
|
|
+ goto buffer_out;
|
|
|
+
|
|
|
+ mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid_begin,
|
|
|
+ MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, range,
|
|
|
+ mlxsw_sp_port->local_port, set);
|
|
|
+ err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
|
|
|
+
|
|
|
+buffer_out:
|
|
|
+ kfree(sftr_pl);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int mlxsw_sp_port_uc_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
+ bool set)
|
|
|
+{
|
|
|
+ struct net_device *dev = mlxsw_sp_port->dev;
|
|
|
+ u16 vid, last_visited_vid;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
|
|
|
+ err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, set,
|
|
|
+ true);
|
|
|
+ if (err) {
|
|
|
+ last_visited_vid = vid;
|
|
|
+ goto err_port_flood_set;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+err_port_flood_set:
|
|
|
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid)
|
|
|
+ __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, vid, !set, true);
|
|
|
+ netdev_err(dev, "Failed to configure unicast flooding\n");
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
struct switchdev_trans *trans,
|
|
|
unsigned long brport_flags)
|
|
|
{
|
|
|
+ unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
|
|
|
+ bool set;
|
|
|
+ int err;
|
|
|
+
|
|
|
if (switchdev_trans_ph_prepare(trans))
|
|
|
return 0;
|
|
|
|
|
|
+ if ((uc_flood ^ brport_flags) & BR_FLOOD) {
|
|
|
+ set = mlxsw_sp_port->uc_flood ? false : true;
|
|
|
+ err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port, set);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ mlxsw_sp_port->uc_flood = brport_flags & BR_FLOOD ? 1 : 0;
|
|
|
mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0;
|
|
|
mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -150,9 +225,10 @@ static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
|
|
|
|
|
|
static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
struct switchdev_trans *trans,
|
|
|
- unsigned long ageing_jiffies)
|
|
|
+ unsigned long ageing_clock_t)
|
|
|
{
|
|
|
struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
+ unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
|
|
|
u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
|
|
|
|
|
|
if (switchdev_trans_ph_prepare(trans))
|
|
@@ -247,40 +323,6 @@ static int mlxsw_sp_port_fid_unmap(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
|
|
|
return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, false, fid, fid);
|
|
|
}
|
|
|
|
|
|
-static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
- u16 fid, bool set, bool only_uc)
|
|
|
-{
|
|
|
- struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
|
|
|
- char *sftr_pl;
|
|
|
- int err;
|
|
|
-
|
|
|
- sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
|
|
|
- if (!sftr_pl)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_UC, fid,
|
|
|
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0,
|
|
|
- mlxsw_sp_port->local_port, set);
|
|
|
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
|
|
|
- if (err)
|
|
|
- goto buffer_out;
|
|
|
-
|
|
|
- /* Flooding control allows one to decide whether a given port will
|
|
|
- * flood unicast traffic for which there is no FDB entry.
|
|
|
- */
|
|
|
- if (only_uc)
|
|
|
- goto buffer_out;
|
|
|
-
|
|
|
- mlxsw_reg_sftr_pack(sftr_pl, MLXSW_SP_FLOOD_TABLE_BM, fid,
|
|
|
- MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST, 0,
|
|
|
- mlxsw_sp_port->local_port, set);
|
|
|
- err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
|
|
|
-
|
|
|
-buffer_out:
|
|
|
- kfree(sftr_pl);
|
|
|
- return err;
|
|
|
-}
|
|
|
-
|
|
|
static int mlxsw_sp_port_add_vids(struct net_device *dev, u16 vid_begin,
|
|
|
u16 vid_end)
|
|
|
{
|
|
@@ -345,14 +387,13 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
netdev_err(dev, "Failed to map FID=%d", vid);
|
|
|
return err;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, true,
|
|
|
- false);
|
|
|
- if (err) {
|
|
|
- netdev_err(dev, "Failed to set flooding for FID=%d",
|
|
|
- vid);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
+ true, false);
|
|
|
+ if (err) {
|
|
|
+ netdev_err(dev, "Failed to configure flooding\n");
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
for (vid = vid_begin; vid <= vid_end;
|
|
@@ -530,15 +571,14 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
if (init)
|
|
|
goto out;
|
|
|
|
|
|
- for (vid = vid_begin; vid <= vid_end; vid++) {
|
|
|
- err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid, false,
|
|
|
- false);
|
|
|
- if (err) {
|
|
|
- netdev_err(dev, "Failed to clear flooding for FID=%d",
|
|
|
- vid);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
+ false, false);
|
|
|
+ if (err) {
|
|
|
+ netdev_err(dev, "Failed to clear flooding\n");
|
|
|
+ return err;
|
|
|
+ }
|
|
|
|
|
|
+ for (vid = vid_begin; vid <= vid_end; vid++) {
|
|
|
/* Remove FID mapping in case of Virtual mode */
|
|
|
err = mlxsw_sp_port_fid_unmap(mlxsw_sp_port, vid);
|
|
|
if (err) {
|
|
@@ -692,7 +732,7 @@ static int mlxsw_sp_port_obj_dump(struct net_device *dev,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
|
|
|
+static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
|
|
|
.switchdev_port_attr_get = mlxsw_sp_port_attr_get,
|
|
|
.switchdev_port_attr_set = mlxsw_sp_port_attr_set,
|
|
|
.switchdev_port_obj_add = mlxsw_sp_port_obj_add,
|