Skip to content

Commit

Permalink
net: mscc: ocelot: offload bridge port flags to device
Browse files Browse the repository at this point in the history
We should not be unconditionally enabling address learning, since doing
that is actively detrimential when a port is standalone and not offloading
a bridge. Namely, if a port in the switch is standalone and others are
offloading the bridge, then we could enter a situation where we learn an
address towards the standalone port, but the bridged ports could not
forward the packet there, because the CPU is the only path between the
standalone and the bridged ports. The solution of course is to not
enable address learning unless the bridge asks for it.

We need to set up the initial port flags for no learning and flooding
everything, and also when the port joins and leaves the bridge.
The flood configuration was already configured ok for standalone mode
in ocelot_init, we just need to disable learning in ocelot_init_port.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Reviewed-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Feb 13, 2021
1 parent b360d94 commit 421741e
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 5 deletions.
22 changes: 22 additions & 0 deletions drivers/net/dsa/ocelot/felix.c
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,26 @@ static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
return ocelot_bridge_stp_state_set(ocelot, port, state);
}

static int felix_pre_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags val,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;

return ocelot_port_pre_bridge_flags(ocelot, port, val);
}

static int felix_bridge_flags(struct dsa_switch *ds, int port,
struct switchdev_brport_flags val,
struct netlink_ext_ack *extack)
{
struct ocelot *ocelot = ds->priv;

ocelot_port_bridge_flags(ocelot, port, val);

return 0;
}

static int felix_bridge_join(struct dsa_switch *ds, int port,
struct net_device *br)
{
Expand Down Expand Up @@ -1376,6 +1396,8 @@ const struct dsa_switch_ops felix_switch_ops = {
.port_fdb_del = felix_fdb_del,
.port_mdb_add = felix_mdb_add,
.port_mdb_del = felix_mdb_del,
.port_pre_bridge_flags = felix_pre_bridge_flags,
.port_bridge_flags = felix_bridge_flags,
.port_bridge_join = felix_bridge_join,
.port_bridge_leave = felix_bridge_leave,
.port_lag_join = felix_lag_join,
Expand Down
87 changes: 86 additions & 1 deletion drivers/net/ethernet/mscc/ocelot.c
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,7 @@ EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);

void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 port_cfg;

if (!(BIT(port) & ocelot->bridge_mask))
Expand All @@ -1050,7 +1051,8 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
ocelot->bridge_fwd_mask |= BIT(port);
fallthrough;
case BR_STATE_LEARNING:
port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
if (ocelot_port->learn_ena)
port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
break;

default:
Expand Down Expand Up @@ -1534,6 +1536,86 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port)
}
EXPORT_SYMBOL(ocelot_get_max_mtu);

static void ocelot_port_set_learning(struct ocelot *ocelot, int port,
bool enabled)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 val = 0;

if (enabled)
val = ANA_PORT_PORT_CFG_LEARN_ENA;

ocelot_rmw_gix(ocelot, val, ANA_PORT_PORT_CFG_LEARN_ENA,
ANA_PORT_PORT_CFG, port);

ocelot_port->learn_ena = enabled;
}

static void ocelot_port_set_ucast_flood(struct ocelot *ocelot, int port,
bool enabled)
{
u32 val = 0;

if (enabled)
val = BIT(port);

ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_UC);
}

static void ocelot_port_set_mcast_flood(struct ocelot *ocelot, int port,
bool enabled)
{
u32 val = 0;

if (enabled)
val = BIT(port);

ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_MC);
}

static void ocelot_port_set_bcast_flood(struct ocelot *ocelot, int port,
bool enabled)
{
u32 val = 0;

if (enabled)
val = BIT(port);

ocelot_rmw_rix(ocelot, val, BIT(port), ANA_PGID_PGID, PGID_BC);
}

int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags flags)
{
if (flags.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD |
BR_BCAST_FLOOD))
return -EINVAL;

return 0;
}
EXPORT_SYMBOL(ocelot_port_pre_bridge_flags);

void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags flags)
{
if (flags.mask & BR_LEARNING)
ocelot_port_set_learning(ocelot, port,
!!(flags.val & BR_LEARNING));

if (flags.mask & BR_FLOOD)
ocelot_port_set_ucast_flood(ocelot, port,
!!(flags.val & BR_FLOOD));

if (flags.mask & BR_MCAST_FLOOD)
ocelot_port_set_mcast_flood(ocelot, port,
!!(flags.val & BR_MCAST_FLOOD));

if (flags.mask & BR_BCAST_FLOOD)
ocelot_port_set_bcast_flood(ocelot, port,
!!(flags.val & BR_BCAST_FLOOD));
}
EXPORT_SYMBOL(ocelot_port_bridge_flags);

void ocelot_init_port(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
Expand Down Expand Up @@ -1583,6 +1665,9 @@ void ocelot_init_port(struct ocelot *ocelot, int port)
REW_PORT_VLAN_CFG_PORT_TPID_M,
REW_PORT_VLAN_CFG, port);

/* Disable source address learning for standalone mode */
ocelot_port_set_learning(ocelot, port, false);

/* Enable vcap lookups */
ocelot_vcap_enable(ocelot, port);
}
Expand Down
49 changes: 45 additions & 4 deletions drivers/net/ethernet/mscc/ocelot_net.c
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,13 @@ static int ocelot_port_attr_set(struct net_device *dev,
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
break;
case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
err = ocelot_port_pre_bridge_flags(ocelot, port,
attr->u.brport_flags);
break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
ocelot_port_bridge_flags(ocelot, port, attr->u.brport_flags);
break;
default:
err = -EOPNOTSUPP;
break;
Expand Down Expand Up @@ -1111,6 +1118,40 @@ static int ocelot_port_obj_del(struct net_device *dev,
return ret;
}

static int ocelot_netdevice_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge)
{
struct switchdev_brport_flags flags;
int err;

flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
flags.val = flags.mask;

err = ocelot_port_bridge_join(ocelot, port, bridge);
if (err)
return err;

ocelot_port_bridge_flags(ocelot, port, flags);

return 0;
}

static int ocelot_netdevice_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge)
{
struct switchdev_brport_flags flags;
int err;

flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
flags.val = flags.mask & ~BR_LEARNING;

err = ocelot_port_bridge_leave(ocelot, port, bridge);

ocelot_port_bridge_flags(ocelot, port, flags);

return err;
}

static int ocelot_netdevice_changeupper(struct net_device *dev,
struct netdev_notifier_changeupper_info *info)
{
Expand All @@ -1122,11 +1163,11 @@ static int ocelot_netdevice_changeupper(struct net_device *dev,

if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking) {
err = ocelot_port_bridge_join(ocelot, port,
info->upper_dev);
err = ocelot_netdevice_bridge_join(ocelot, port,
info->upper_dev);
} else {
err = ocelot_port_bridge_leave(ocelot, port,
info->upper_dev);
err = ocelot_netdevice_bridge_leave(ocelot, port,
info->upper_dev);
}
}
if (netif_is_lag_master(info->upper_dev)) {
Expand Down
5 changes: 5 additions & 0 deletions include/soc/mscc/ocelot.h
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,7 @@ struct ocelot_port {

u8 *xmit_template;
bool is_dsa_8021q_cpu;
bool learn_ena;

struct net_device *bond;
bool lag_tx_active;
Expand Down Expand Up @@ -766,6 +767,10 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot);
int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val);
void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
Expand Down

0 comments on commit 421741e

Please sign in to comment.