Commit e4bd44e8 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by David S. Miller

net: ocelot: replay switchdev events when joining bridge

The premise of this change is that the switchdev port attributes and
objects offloaded by ocelot might have been missed when we are joining
an already existing bridge port, such as a bonding interface.

The patch pulls these switchdev attributes and objects from the bridge,
on behalf of the 'bridge port' net device which might be either the
ocelot switch interface, or the bonding upper interface.

The ocelot_net.c belongs strictly to the switchdev ocelot driver, while
ocelot.c is part of a library shared with the DSA felix driver.
The ocelot_port_bridge_leave function (part of the common library) used
to call ocelot_port_vlan_filtering(false), something which is not
necessary for DSA, since the framework deals with that already there.
So we move this function to ocelot_switchdev_unsync, which is specific
to the switchdev driver.

The code movement described above makes ocelot_port_bridge_leave no
longer return an error code, so we change its type from int to void.
Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 81ef35e7
...@@ -719,7 +719,9 @@ static int felix_bridge_join(struct dsa_switch *ds, int port, ...@@ -719,7 +719,9 @@ static int felix_bridge_join(struct dsa_switch *ds, int port,
{ {
struct ocelot *ocelot = ds->priv; struct ocelot *ocelot = ds->priv;
return ocelot_port_bridge_join(ocelot, port, br); ocelot_port_bridge_join(ocelot, port, br);
return 0;
} }
static void felix_bridge_leave(struct dsa_switch *ds, int port, static void felix_bridge_leave(struct dsa_switch *ds, int port,
......
...@@ -11,7 +11,7 @@ config NET_VENDOR_MICROSEMI ...@@ -11,7 +11,7 @@ config NET_VENDOR_MICROSEMI
if NET_VENDOR_MICROSEMI if NET_VENDOR_MICROSEMI
# Users should depend on NET_SWITCHDEV, HAS_IOMEM # Users should depend on NET_SWITCHDEV, HAS_IOMEM, BRIDGE
config MSCC_OCELOT_SWITCH_LIB config MSCC_OCELOT_SWITCH_LIB
select NET_DEVLINK select NET_DEVLINK
select REGMAP_MMIO select REGMAP_MMIO
...@@ -24,6 +24,7 @@ config MSCC_OCELOT_SWITCH_LIB ...@@ -24,6 +24,7 @@ config MSCC_OCELOT_SWITCH_LIB
config MSCC_OCELOT_SWITCH config MSCC_OCELOT_SWITCH
tristate "Ocelot switch driver" tristate "Ocelot switch driver"
depends on BRIDGE || BRIDGE=n
depends on NET_SWITCHDEV depends on NET_SWITCHDEV
depends on HAS_IOMEM depends on HAS_IOMEM
depends on OF_NET depends on OF_NET
......
...@@ -1514,34 +1514,28 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port, ...@@ -1514,34 +1514,28 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
} }
EXPORT_SYMBOL(ocelot_port_mdb_del); EXPORT_SYMBOL(ocelot_port_mdb_del);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port, void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge) struct net_device *bridge)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
ocelot_port->bridge = bridge; ocelot_port->bridge = bridge;
return 0; ocelot_apply_bridge_fwd_mask(ocelot);
} }
EXPORT_SYMBOL(ocelot_port_bridge_join); EXPORT_SYMBOL(ocelot_port_bridge_join);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port, void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge) struct net_device *bridge)
{ {
struct ocelot_port *ocelot_port = ocelot->ports[port]; struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_vlan pvid = {0}, native_vlan = {0}; struct ocelot_vlan pvid = {0}, native_vlan = {0};
int ret;
ocelot_port->bridge = NULL; ocelot_port->bridge = NULL;
ret = ocelot_port_vlan_filtering(ocelot, port, false);
if (ret)
return ret;
ocelot_port_set_pvid(ocelot, port, pvid); ocelot_port_set_pvid(ocelot, port, pvid);
ocelot_port_set_native_vlan(ocelot, port, native_vlan); ocelot_port_set_native_vlan(ocelot, port, native_vlan);
ocelot_apply_bridge_fwd_mask(ocelot);
return 0;
} }
EXPORT_SYMBOL(ocelot_port_bridge_leave); EXPORT_SYMBOL(ocelot_port_bridge_leave);
......
...@@ -1117,47 +1117,126 @@ static int ocelot_port_obj_del(struct net_device *dev, ...@@ -1117,47 +1117,126 @@ static int ocelot_port_obj_del(struct net_device *dev,
return ret; return ret;
} }
static void ocelot_inherit_brport_flags(struct ocelot *ocelot, int port,
struct net_device *brport_dev)
{
struct switchdev_brport_flags flags = {0};
int flag;
flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
for_each_set_bit(flag, &flags.mask, 32)
if (br_port_flag_is_set(brport_dev, BIT(flag)))
flags.val |= BIT(flag);
ocelot_port_bridge_flags(ocelot, port, flags);
}
static void ocelot_clear_brport_flags(struct ocelot *ocelot, int port)
{
struct switchdev_brport_flags flags;
flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
flags.val = flags.mask & ~BR_LEARNING;
ocelot_port_bridge_flags(ocelot, port, flags);
}
static int ocelot_switchdev_sync(struct ocelot *ocelot, int port,
struct net_device *brport_dev,
struct net_device *bridge_dev,
struct netlink_ext_ack *extack)
{
clock_t ageing_time;
u8 stp_state;
int err;
ocelot_inherit_brport_flags(ocelot, port, brport_dev);
stp_state = br_port_get_stp_state(brport_dev);
ocelot_bridge_stp_state_set(ocelot, port, stp_state);
err = ocelot_port_vlan_filtering(ocelot, port,
br_vlan_enabled(bridge_dev));
if (err)
return err;
ageing_time = br_get_ageing_time(bridge_dev);
ocelot_port_attr_ageing_set(ocelot, port, ageing_time);
err = br_mdb_replay(bridge_dev, brport_dev,
&ocelot_switchdev_blocking_nb, extack);
if (err && err != -EOPNOTSUPP)
return err;
err = br_fdb_replay(bridge_dev, brport_dev, &ocelot_switchdev_nb);
if (err)
return err;
err = br_vlan_replay(bridge_dev, brport_dev,
&ocelot_switchdev_blocking_nb, extack);
if (err && err != -EOPNOTSUPP)
return err;
return 0;
}
static int ocelot_switchdev_unsync(struct ocelot *ocelot, int port)
{
int err;
err = ocelot_port_vlan_filtering(ocelot, port, false);
if (err)
return err;
ocelot_clear_brport_flags(ocelot, port);
ocelot_bridge_stp_state_set(ocelot, port, BR_STATE_FORWARDING);
return 0;
}
static int ocelot_netdevice_bridge_join(struct net_device *dev, static int ocelot_netdevice_bridge_join(struct net_device *dev,
struct net_device *brport_dev,
struct net_device *bridge, struct net_device *bridge,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot; struct ocelot *ocelot = ocelot_port->ocelot;
struct switchdev_brport_flags flags;
int port = priv->chip_port; int port = priv->chip_port;
int err; int err;
flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; ocelot_port_bridge_join(ocelot, port, bridge);
flags.val = flags.mask;
err = ocelot_port_bridge_join(ocelot, port, bridge); err = ocelot_switchdev_sync(ocelot, port, brport_dev, bridge, extack);
if (err) if (err)
return err; goto err_switchdev_sync;
ocelot_port_bridge_flags(ocelot, port, flags);
return 0; return 0;
err_switchdev_sync:
ocelot_port_bridge_leave(ocelot, port, bridge);
return err;
} }
static int ocelot_netdevice_bridge_leave(struct net_device *dev, static int ocelot_netdevice_bridge_leave(struct net_device *dev,
struct net_device *brport_dev,
struct net_device *bridge) struct net_device *bridge)
{ {
struct ocelot_port_private *priv = netdev_priv(dev); struct ocelot_port_private *priv = netdev_priv(dev);
struct ocelot_port *ocelot_port = &priv->port; struct ocelot_port *ocelot_port = &priv->port;
struct ocelot *ocelot = ocelot_port->ocelot; struct ocelot *ocelot = ocelot_port->ocelot;
struct switchdev_brport_flags flags;
int port = priv->chip_port; int port = priv->chip_port;
int err; int err;
flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; err = ocelot_switchdev_unsync(ocelot, port);
flags.val = flags.mask & ~BR_LEARNING; if (err)
return err;
err = ocelot_port_bridge_leave(ocelot, port, bridge); ocelot_port_bridge_leave(ocelot, port, bridge);
ocelot_port_bridge_flags(ocelot, port, flags); return 0;
return err;
} }
static int ocelot_netdevice_lag_join(struct net_device *dev, static int ocelot_netdevice_lag_join(struct net_device *dev,
...@@ -1182,7 +1261,7 @@ static int ocelot_netdevice_lag_join(struct net_device *dev, ...@@ -1182,7 +1261,7 @@ static int ocelot_netdevice_lag_join(struct net_device *dev,
if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
return 0; return 0;
err = ocelot_netdevice_bridge_join(dev, bridge_dev, extack); err = ocelot_netdevice_bridge_join(dev, bond, bridge_dev, extack);
if (err) if (err)
goto err_bridge_join; goto err_bridge_join;
...@@ -1208,7 +1287,7 @@ static int ocelot_netdevice_lag_leave(struct net_device *dev, ...@@ -1208,7 +1287,7 @@ static int ocelot_netdevice_lag_leave(struct net_device *dev,
if (!bridge_dev || !netif_is_bridge_master(bridge_dev)) if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
return 0; return 0;
return ocelot_netdevice_bridge_leave(dev, bridge_dev); return ocelot_netdevice_bridge_leave(dev, bond, bridge_dev);
} }
static int ocelot_netdevice_changeupper(struct net_device *dev, static int ocelot_netdevice_changeupper(struct net_device *dev,
...@@ -1221,10 +1300,12 @@ static int ocelot_netdevice_changeupper(struct net_device *dev, ...@@ -1221,10 +1300,12 @@ static int ocelot_netdevice_changeupper(struct net_device *dev,
if (netif_is_bridge_master(info->upper_dev)) { if (netif_is_bridge_master(info->upper_dev)) {
if (info->linking) if (info->linking)
err = ocelot_netdevice_bridge_join(dev, info->upper_dev, err = ocelot_netdevice_bridge_join(dev, dev,
info->upper_dev,
extack); extack);
else else
err = ocelot_netdevice_bridge_leave(dev, info->upper_dev); err = ocelot_netdevice_bridge_leave(dev, dev,
info->upper_dev);
} }
if (netif_is_lag_master(info->upper_dev)) { if (netif_is_lag_master(info->upper_dev)) {
if (info->linking) if (info->linking)
......
...@@ -803,10 +803,10 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port, ...@@ -803,10 +803,10 @@ int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val); struct switchdev_brport_flags val);
void ocelot_port_bridge_flags(struct ocelot *ocelot, int port, void ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
struct switchdev_brport_flags val); struct switchdev_brport_flags val);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port, void ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge); struct net_device *bridge);
void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_fdb_dump(struct ocelot *ocelot, int port, int ocelot_fdb_dump(struct ocelot *ocelot, int port,
dsa_fdb_dump_cb_t *cb, void *data); dsa_fdb_dump_cb_t *cb, void *data);
int ocelot_fdb_add(struct ocelot *ocelot, int port, int ocelot_fdb_add(struct ocelot *ocelot, int port,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment