diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 0fa8da58484597ae381d8005ea3b1413668d51c1..09ce451c283bb8dfb5dd9c4c69bdeb04c0e796b3 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -2123,6 +2123,8 @@ static int mlxsw_sp_port_bridge_leave(struct mlxsw_sp_port *mlxsw_sp_port, if (flush_fdb && mlxsw_sp_port_fdb_flush(mlxsw_sp_port)) netdev_err(mlxsw_sp_port->dev, "Failed to flush FDB\n"); + mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); + mlxsw_sp_port->learning = 0; mlxsw_sp_port->learning_sync = 0; mlxsw_sp_port->uc_flood = 0; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index 7f42eb1c320e1c02ff12892c94261800e29eed64..3b89ed2f3c76b8b6eebd5fa6f7d1f52a70d75585 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -254,5 +254,6 @@ int mlxsw_sp_port_kill_vid(struct net_device *dev, int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 vfid, bool set, bool only_uc); void mlxsw_sp_port_active_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port); +int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid); #endif diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c index e492ca2cdecd9953d5f0ec4124f7077e839803dc..7b56098acc58eb9e1d5cd4d586e75989ae83f01a 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c @@ -370,7 +370,8 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev, return err; } -static int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) +static int __mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, + u16 vid) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; char spvid_pl[MLXSW_REG_SPVID_LEN]; @@ -379,6 +380,53 @@ static int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spvid), spvid_pl); } +static int mlxsw_sp_port_allow_untagged_set(struct mlxsw_sp_port *mlxsw_sp_port, + bool allow) +{ + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; + char spaft_pl[MLXSW_REG_SPAFT_LEN]; + + mlxsw_reg_spaft_pack(spaft_pl, mlxsw_sp_port->local_port, allow); + return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(spaft), spaft_pl); +} + +int mlxsw_sp_port_pvid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid) +{ + struct net_device *dev = mlxsw_sp_port->dev; + int err; + + if (!vid) { + err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, false); + if (err) { + netdev_err(dev, "Failed to disallow untagged traffic\n"); + return err; + } + } else { + err = __mlxsw_sp_port_pvid_set(mlxsw_sp_port, vid); + if (err) { + netdev_err(dev, "Failed to set PVID\n"); + return err; + } + + /* Only allow if not already allowed. */ + if (!mlxsw_sp_port->pvid) { + err = mlxsw_sp_port_allow_untagged_set(mlxsw_sp_port, + true); + if (err) { + netdev_err(dev, "Failed to allow untagged traffic\n"); + goto err_port_allow_untagged_set; + } + } + } + + mlxsw_sp_port->pvid = vid; + return 0; + +err_port_allow_untagged_set: + __mlxsw_sp_port_pvid_set(mlxsw_sp_port, mlxsw_sp_port->pvid); + return err; +} + static int mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid) { char sfmr_pl[MLXSW_REG_SFMR_LEN]; @@ -540,7 +588,12 @@ static int __mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, netdev_err(dev, "Unable to add PVID %d\n", vid_begin); goto err_port_pvid_set; } - mlxsw_sp_port->pvid = vid_begin; + } else if (!flag_pvid && old_pvid >= vid_begin && old_pvid <= vid_end) { + err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 0); + if (err) { + netdev_err(dev, "Unable to del PVID\n"); + goto err_port_pvid_set; + } } /* Changing activity bits only if HW operation succeded */ @@ -892,20 +945,18 @@ static int __mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, return err; } + if (init) + goto out; + pvid = mlxsw_sp_port->pvid; - if (pvid >= vid_begin && pvid <= vid_end && pvid != 1) { - /* Default VLAN is always 1 */ - err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); + 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; } - mlxsw_sp_port->pvid = 1; } - if (init) - goto out; - err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, vid_begin, vid_end, false, false); if (err) {