diff options
author | Ioana Ciornei <ioana.ciornei@nxp.com> | 2021-03-22 22:58:56 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2021-03-22 16:37:44 -0700 |
commit | 1e7cbabfdb12aa944ae0cb03871f8b55ede1341a (patch) | |
tree | 5835454d5da8c9ad2d3ccbf3e85e4e28e4286725 /drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | |
parent | f054e3e217e40ed343a0cea8c68cc053d40f81bd (diff) |
dpaa2-switch: add support for configuring learning state per port
Add support for configuring the learning state of a switch port.
When the user requests the HW learning to be disabled, a fast-age
procedure on that specific port is run so that previously learnt
addresses do not linger.
At device probe as well as on a bridge leave action, the ports are
configured with HW learning disabled since they are basically a
standalone port.
At the same time, at bridge join we inherit the bridge port BR_LEARNING
flag state and configure it on the switch port.
There were already some MC firmware ABI functions for changing the
learning state, but those were per FDB (bridging domain) and not per
port so we need to adjust those to use the new MC fw command which is
per port.
Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c')
-rw-r--r-- | drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c index 2db9cd78201d..2ea3a4dac49d 100644 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c @@ -1238,6 +1238,56 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev, return dpaa2_switch_port_set_stp_state(port_priv, state); } +static int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, bool enable) +{ + struct ethsw_core *ethsw = port_priv->ethsw_data; + enum dpsw_learning_mode learn_mode; + int err; + + if (enable) + learn_mode = DPSW_LEARNING_MODE_HW; + else + learn_mode = DPSW_LEARNING_MODE_DIS; + + err = dpsw_if_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle, + port_priv->idx, learn_mode); + if (err) + netdev_err(port_priv->netdev, "dpsw_if_set_learning_mode err %d\n", err); + + if (!enable) + dpaa2_switch_port_fast_age(port_priv); + + return err; +} + +static int dpaa2_switch_port_pre_bridge_flags(struct net_device *netdev, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + if (flags.mask & ~(BR_LEARNING)) + return -EINVAL; + + return 0; +} + +static int dpaa2_switch_port_bridge_flags(struct net_device *netdev, + struct switchdev_brport_flags flags, + struct netlink_ext_ack *extack) +{ + struct ethsw_port_priv *port_priv = netdev_priv(netdev); + int err; + + if (flags.mask & BR_LEARNING) { + bool learn_ena = !!(flags.val & BR_LEARNING); + + err = dpaa2_switch_port_set_learning(port_priv, learn_ena); + if (err) + return err; + } + + return 0; +} + static int dpaa2_switch_port_attr_set(struct net_device *netdev, const struct switchdev_attr *attr, struct netlink_ext_ack *extack) @@ -1256,6 +1306,12 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev, return -EOPNOTSUPP; } break; + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: + err = dpaa2_switch_port_pre_bridge_flags(netdev, attr->u.brport_flags, extack); + break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: + err = dpaa2_switch_port_bridge_flags(netdev, attr->u.brport_flags, extack); + break; default: err = -EOPNOTSUPP; break; @@ -1504,6 +1560,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, struct ethsw_port_priv *other_port_priv; struct net_device *other_dev; struct list_head *iter; + bool learn_ena; int err; netdev_for_each_lower_dev(upper_dev, other_dev, iter) { @@ -1525,6 +1582,10 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev, dpaa2_switch_port_set_fdb(port_priv, upper_dev); + /* Inherit the initial bridge port learning state */ + learn_ena = br_port_flag_is_set(netdev, BR_LEARNING); + err = dpaa2_switch_port_set_learning(port_priv, learn_ena); + /* Setup the egress flood policy (broadcast, unknown unicast) */ err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id); if (err) @@ -1595,6 +1656,11 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev) if (err) return err; + /* No HW learning when not under a bridge */ + err = dpaa2_switch_port_set_learning(port_priv, false); + if (err) + return err; + /* Add the VLAN 1 as PVID when not under a bridge. We need this since * the dpaa2 switch interfaces are not capable to be VLAN unaware */ @@ -2684,6 +2750,10 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw, if (err) goto err_port_probe; + err = dpaa2_switch_port_set_learning(port_priv, false); + if (err) + goto err_port_probe; + return 0; err_port_probe: |