|
@@ -261,12 +261,40 @@ int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
|
|
false);
|
|
false);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int mlxsw_sp_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ bool set)
|
|
|
|
+{
|
|
|
|
+ u16 vid;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
|
|
|
|
+ vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
|
|
|
|
+
|
|
|
|
+ return __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid,
|
|
|
|
+ set);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
|
|
|
|
+ err = __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid,
|
|
|
|
+ set);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_port_vid_learning_set;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+err_port_vid_learning_set:
|
|
|
|
+ for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
|
|
|
|
+ __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, vid, !set);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
struct switchdev_trans *trans,
|
|
struct switchdev_trans *trans,
|
|
unsigned long brport_flags)
|
|
unsigned long brport_flags)
|
|
{
|
|
{
|
|
|
|
+ unsigned long learning = mlxsw_sp_port->learning ? BR_LEARNING : 0;
|
|
unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
|
|
unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
|
|
- bool set;
|
|
|
|
int err;
|
|
int err;
|
|
|
|
|
|
if (!mlxsw_sp_port->bridged)
|
|
if (!mlxsw_sp_port->bridged)
|
|
@@ -276,17 +304,30 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
if ((uc_flood ^ brport_flags) & BR_FLOOD) {
|
|
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);
|
|
|
|
|
|
+ err = mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
|
|
|
|
+ !mlxsw_sp_port->uc_flood);
|
|
if (err)
|
|
if (err)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if ((learning ^ brport_flags) & BR_LEARNING) {
|
|
|
|
+ err = mlxsw_sp_port_learning_set(mlxsw_sp_port,
|
|
|
|
+ !mlxsw_sp_port->learning);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_port_learning_set;
|
|
|
|
+ }
|
|
|
|
+
|
|
mlxsw_sp_port->uc_flood = brport_flags & BR_FLOOD ? 1 : 0;
|
|
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 = brport_flags & BR_LEARNING ? 1 : 0;
|
|
mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
|
|
mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
+
|
|
|
|
+err_port_learning_set:
|
|
|
|
+ if ((uc_flood ^ brport_flags) & BR_FLOOD)
|
|
|
|
+ mlxsw_sp_port_uc_flood_set(mlxsw_sp_port,
|
|
|
|
+ mlxsw_sp_port->uc_flood);
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
|
|
static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
|
|
@@ -635,6 +676,27 @@ static int __mlxsw_sp_port_vlans_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int mlxsw_sp_port_vid_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
|
|
+ u16 vid_begin, u16 vid_end,
|
|
|
|
+ bool learn_enable)
|
|
|
|
+{
|
|
|
|
+ u16 vid, vid_e;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ for (vid = vid_begin; vid <= vid_end;
|
|
|
|
+ vid += MLXSW_REG_SPVMLR_REC_MAX_COUNT) {
|
|
|
|
+ vid_e = min((u16) (vid + MLXSW_REG_SPVMLR_REC_MAX_COUNT - 1),
|
|
|
|
+ vid_end);
|
|
|
|
+
|
|
|
|
+ err = __mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid,
|
|
|
|
+ vid_e, learn_enable);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
u16 vid_begin, u16 vid_end,
|
|
u16 vid_begin, u16 vid_end,
|
|
bool flag_untagged, bool flag_pvid)
|
|
bool flag_untagged, bool flag_pvid)
|
|
@@ -675,6 +737,14 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
|
+ mlxsw_sp_port->learning);
|
|
|
|
+ if (err) {
|
|
|
|
+ netdev_err(dev, "Failed to set learning for VIDs %d-%d\n",
|
|
|
|
+ vid_begin, vid_end);
|
|
|
|
+ goto err_port_vid_learning_set;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* Changing activity bits only if HW operation succeded */
|
|
/* Changing activity bits only if HW operation succeded */
|
|
for (vid = vid_begin; vid <= vid_end; vid++) {
|
|
for (vid = vid_begin; vid <= vid_end; vid++) {
|
|
set_bit(vid, mlxsw_sp_port->active_vlans);
|
|
set_bit(vid, mlxsw_sp_port->active_vlans);
|
|
@@ -697,6 +767,9 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
err_port_stp_state_set:
|
|
err_port_stp_state_set:
|
|
for (vid = vid_begin; vid <= vid_end; vid++)
|
|
for (vid = vid_begin; vid <= vid_end; vid++)
|
|
clear_bit(vid, mlxsw_sp_port->active_vlans);
|
|
clear_bit(vid, mlxsw_sp_port->active_vlans);
|
|
|
|
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
|
+ false);
|
|
|
|
+err_port_vid_learning_set:
|
|
if (old_pvid != mlxsw_sp_port->pvid)
|
|
if (old_pvid != mlxsw_sp_port->pvid)
|
|
mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
|
|
mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
|
|
err_port_pvid_set:
|
|
err_port_pvid_set:
|
|
@@ -1001,29 +1074,20 @@ static int mlxsw_sp_port_obj_add(struct net_device *dev,
|
|
static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
|
|
u16 vid_begin, u16 vid_end)
|
|
u16 vid_begin, u16 vid_end)
|
|
{
|
|
{
|
|
- struct net_device *dev = mlxsw_sp_port->dev;
|
|
|
|
u16 vid, pvid;
|
|
u16 vid, pvid;
|
|
- int err;
|
|
|
|
|
|
|
|
if (!mlxsw_sp_port->bridged)
|
|
if (!mlxsw_sp_port->bridged)
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
- err = __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
|
- false, false);
|
|
|
|
- if (err) {
|
|
|
|
- netdev_err(dev, "Unable to del VIDs %d-%d\n", vid_begin,
|
|
|
|
- vid_end);
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
|
|
+ mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid_begin, vid_end,
|
|
|
|
+ false);
|
|
|
|
|
|
pvid = mlxsw_sp_port->pvid;
|
|
pvid = mlxsw_sp_port->pvid;
|
|
- if (pvid >= vid_begin && pvid <= vid_end) {
|
|
|
|
- err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0);
|
|
|
|
- if (err) {
|
|
|
|
- netdev_err(dev, "Unable to del PVID %d\n", pvid);
|
|
|
|
- return err;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ if (pvid >= vid_begin && pvid <= vid_end)
|
|
|
|
+ mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0);
|
|
|
|
+
|
|
|
|
+ __mlxsw_sp_port_vlans_set(mlxsw_sp_port, vid_begin, vid_end, false,
|
|
|
|
+ false);
|
|
|
|
|
|
mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid_begin, vid_end);
|
|
mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid_begin, vid_end);
|
|
|
|
|
|
@@ -1366,8 +1430,6 @@ static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
|
|
vid = fid;
|
|
vid = fid;
|
|
}
|
|
}
|
|
|
|
|
|
- adding = adding && mlxsw_sp_port->learning;
|
|
|
|
-
|
|
|
|
do_fdb_op:
|
|
do_fdb_op:
|
|
err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
|
|
err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
|
|
adding, true);
|
|
adding, true);
|
|
@@ -1429,8 +1491,6 @@ static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
|
|
vid = fid;
|
|
vid = fid;
|
|
}
|
|
}
|
|
|
|
|
|
- adding = adding && mlxsw_sp_port->learning;
|
|
|
|
-
|
|
|
|
do_fdb_op:
|
|
do_fdb_op:
|
|
err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
|
|
err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
|
|
adding, true);
|
|
adding, true);
|
|
@@ -1496,20 +1556,18 @@ static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
|
|
mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
|
|
mlxsw_sp = container_of(work, struct mlxsw_sp, fdb_notify.dw.work);
|
|
|
|
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
- do {
|
|
|
|
- mlxsw_reg_sfn_pack(sfn_pl);
|
|
|
|
- err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
|
|
|
|
- if (err) {
|
|
|
|
- dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
|
|
|
|
- for (i = 0; i < num_rec; i++)
|
|
|
|
- mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
|
|
|
|
|
|
+ mlxsw_reg_sfn_pack(sfn_pl);
|
|
|
|
+ err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+ num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
|
|
|
|
+ for (i = 0; i < num_rec; i++)
|
|
|
|
+ mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
|
|
|
|
|
|
- } while (num_rec);
|
|
|
|
|
|
+out:
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
-
|
|
|
|
kfree(sfn_pl);
|
|
kfree(sfn_pl);
|
|
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
|
|
mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
|
|
}
|
|
}
|