Commit cfe908c1 authored by David S. Miller's avatar David S. Miller

Merge branch 'sja1105-fast-ageing'

Vladimir Oltean says:

====================
Fast ageing support for SJA1105 DSA driver

While adding support for flushing dynamically learned FDB entries in the
sja1105 driver, I noticed a few things that could be improved in DSA.
Most notably, drivers could omit a fast age when address learning is
turned off, which might mean that ports leaving a bridge and becoming
standalone could still have FDB entries pointing towards them. Secondly,
when DSA fast ages a port after the 'learning' flag has been turned off,
the software bridge still has the dynamically learned 'master' FDB
entries installed, and those should be deleted too.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 64ec13ec 5126ec72
...@@ -5797,7 +5797,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, ...@@ -5797,7 +5797,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct mv88e6xxx_chip *chip = ds->priv; struct mv88e6xxx_chip *chip = ds->priv;
bool do_fast_age = false;
int err = -EOPNOTSUPP; int err = -EOPNOTSUPP;
mv88e6xxx_reg_lock(chip); mv88e6xxx_reg_lock(chip);
...@@ -5809,9 +5808,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, ...@@ -5809,9 +5808,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
err = mv88e6xxx_port_set_assoc_vector(chip, port, pav); err = mv88e6xxx_port_set_assoc_vector(chip, port, pav);
if (err) if (err)
goto out; goto out;
if (!learning)
do_fast_age = true;
} }
if (flags.mask & BR_FLOOD) { if (flags.mask & BR_FLOOD) {
...@@ -5843,9 +5839,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port, ...@@ -5843,9 +5839,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
out: out:
mv88e6xxx_reg_unlock(chip); mv88e6xxx_reg_unlock(chip);
if (do_fast_age)
mv88e6xxx_port_fast_age(ds, port);
return err; return err;
} }
......
...@@ -233,7 +233,6 @@ struct sja1105_private { ...@@ -233,7 +233,6 @@ struct sja1105_private {
phy_interface_t phy_mode[SJA1105_MAX_NUM_PORTS]; phy_interface_t phy_mode[SJA1105_MAX_NUM_PORTS];
bool fixed_link[SJA1105_MAX_NUM_PORTS]; bool fixed_link[SJA1105_MAX_NUM_PORTS];
bool vlan_aware; bool vlan_aware;
unsigned long learn_ena;
unsigned long ucast_egress_floods; unsigned long ucast_egress_floods;
unsigned long bcast_egress_floods; unsigned long bcast_egress_floods;
const struct sja1105_info *info; const struct sja1105_info *info;
......
...@@ -176,7 +176,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) ...@@ -176,7 +176,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
struct sja1105_mac_config_entry *mac; struct sja1105_mac_config_entry *mac;
struct dsa_switch *ds = priv->ds; struct dsa_switch *ds = priv->ds;
struct sja1105_table *table; struct sja1105_table *table;
int i; struct dsa_port *dp;
table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG]; table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG];
...@@ -195,8 +195,11 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) ...@@ -195,8 +195,11 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
mac = table->entries; mac = table->entries;
for (i = 0; i < ds->num_ports; i++) { list_for_each_entry(dp, &ds->dst->ports, list) {
mac[i] = default_mac; if (dp->ds != ds)
continue;
mac[dp->index] = default_mac;
/* Let sja1105_bridge_stp_state_set() keep address learning /* Let sja1105_bridge_stp_state_set() keep address learning
* enabled for the DSA ports. CPU ports use software-assisted * enabled for the DSA ports. CPU ports use software-assisted
...@@ -205,8 +208,8 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv) ...@@ -205,8 +208,8 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
* CPU ports in a cross-chip topology if multiple CPU ports * CPU ports in a cross-chip topology if multiple CPU ports
* exist. * exist.
*/ */
if (dsa_is_dsa_port(ds, i)) if (dsa_port_is_dsa(dp))
priv->learn_ena |= BIT(i); dp->learning = true;
} }
return 0; return 0;
...@@ -1791,6 +1794,46 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, ...@@ -1791,6 +1794,46 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
return 0; return 0;
} }
static void sja1105_fast_age(struct dsa_switch *ds, int port)
{
struct sja1105_private *priv = ds->priv;
int i;
for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) {
struct sja1105_l2_lookup_entry l2_lookup = {0};
u8 macaddr[ETH_ALEN];
int rc;
rc = sja1105_dynamic_config_read(priv, BLK_IDX_L2_LOOKUP,
i, &l2_lookup);
/* No fdb entry at i, not an issue */
if (rc == -ENOENT)
continue;
if (rc) {
dev_err(ds->dev, "Failed to read FDB: %pe\n",
ERR_PTR(rc));
return;
}
if (!(l2_lookup.destports & BIT(port)))
continue;
/* Don't delete static FDB entries */
if (l2_lookup.lockeds)
continue;
u64_to_ether_addr(l2_lookup.macaddr, macaddr);
rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid);
if (rc) {
dev_err(ds->dev,
"Failed to delete FDB entry %pM vid %lld: %pe\n",
macaddr, l2_lookup.vlanid, ERR_PTR(rc));
return;
}
}
}
static int sja1105_mdb_add(struct dsa_switch *ds, int port, static int sja1105_mdb_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_mdb *mdb) const struct switchdev_obj_port_mdb *mdb)
{ {
...@@ -1899,6 +1942,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, ...@@ -1899,6 +1942,7 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port,
static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
u8 state) u8 state)
{ {
struct dsa_port *dp = dsa_to_port(ds, port);
struct sja1105_private *priv = ds->priv; struct sja1105_private *priv = ds->priv;
struct sja1105_mac_config_entry *mac; struct sja1105_mac_config_entry *mac;
...@@ -1924,12 +1968,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port, ...@@ -1924,12 +1968,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
case BR_STATE_LEARNING: case BR_STATE_LEARNING:
mac[port].ingress = true; mac[port].ingress = true;
mac[port].egress = false; mac[port].egress = false;
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port)); mac[port].dyn_learn = dp->learning;
break; break;
case BR_STATE_FORWARDING: case BR_STATE_FORWARDING:
mac[port].ingress = true; mac[port].ingress = true;
mac[port].egress = true; mac[port].egress = true;
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port)); mac[port].dyn_learn = dp->learning;
break; break;
default: default:
dev_err(ds->dev, "invalid STP state: %d\n", state); dev_err(ds->dev, "invalid STP state: %d\n", state);
...@@ -2891,23 +2935,13 @@ static int sja1105_port_set_learning(struct sja1105_private *priv, int port, ...@@ -2891,23 +2935,13 @@ static int sja1105_port_set_learning(struct sja1105_private *priv, int port,
bool enabled) bool enabled)
{ {
struct sja1105_mac_config_entry *mac; struct sja1105_mac_config_entry *mac;
int rc;
mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;
mac[port].dyn_learn = enabled; mac[port].dyn_learn = enabled;
rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port, return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
&mac[port], true); &mac[port], true);
if (rc)
return rc;
if (enabled)
priv->learn_ena |= BIT(port);
else
priv->learn_ena &= ~BIT(port);
return 0;
} }
static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to, static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to,
...@@ -3042,6 +3076,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = { ...@@ -3042,6 +3076,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_fdb_dump = sja1105_fdb_dump, .port_fdb_dump = sja1105_fdb_dump,
.port_fdb_add = sja1105_fdb_add, .port_fdb_add = sja1105_fdb_add,
.port_fdb_del = sja1105_fdb_del, .port_fdb_del = sja1105_fdb_del,
.port_fast_age = sja1105_fast_age,
.port_bridge_join = sja1105_bridge_join, .port_bridge_join = sja1105_bridge_join,
.port_bridge_leave = sja1105_bridge_leave, .port_bridge_leave = sja1105_bridge_leave,
.port_pre_bridge_flags = sja1105_port_pre_bridge_flags, .port_pre_bridge_flags = sja1105_port_pre_bridge_flags,
......
...@@ -254,6 +254,8 @@ struct dsa_port { ...@@ -254,6 +254,8 @@ struct dsa_port {
struct device_node *dn; struct device_node *dn;
unsigned int ageing_time; unsigned int ageing_time;
bool vlan_filtering; bool vlan_filtering;
/* Managed by DSA on user ports and by drivers on CPU and DSA ports */
bool learning;
u8 stp_state; u8 stp_state;
struct net_device *bridge_dev; struct net_device *bridge_dev;
int bridge_num; int bridge_num;
......
...@@ -241,7 +241,7 @@ int dsa_port_host_mdb_del(const struct dsa_port *dp, ...@@ -241,7 +241,7 @@ int dsa_port_host_mdb_del(const struct dsa_port *dp,
int dsa_port_pre_bridge_flags(const struct dsa_port *dp, int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
struct switchdev_brport_flags flags, struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int dsa_port_bridge_flags(const struct dsa_port *dp, int dsa_port_bridge_flags(struct dsa_port *dp,
struct switchdev_brport_flags flags, struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack); struct netlink_ext_ack *extack);
int dsa_port_vlan_add(struct dsa_port *dp, int dsa_port_vlan_add(struct dsa_port *dp,
......
...@@ -30,6 +30,36 @@ static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v) ...@@ -30,6 +30,36 @@ static int dsa_port_notify(const struct dsa_port *dp, unsigned long e, void *v)
return dsa_tree_notify(dp->ds->dst, e, v); return dsa_tree_notify(dp->ds->dst, e, v);
} }
static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp)
{
struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
struct switchdev_notifier_fdb_info info = {
/* flush all VLANs */
.vid = 0,
};
/* When the port becomes standalone it has already left the bridge.
* Don't notify the bridge in that case.
*/
if (!brport_dev)
return;
call_switchdev_notifiers(SWITCHDEV_FDB_FLUSH_TO_BRIDGE,
brport_dev, &info.info, NULL);
}
static void dsa_port_fast_age(const struct dsa_port *dp)
{
struct dsa_switch *ds = dp->ds;
if (!ds->ops->port_fast_age)
return;
ds->ops->port_fast_age(ds, dp->index);
dsa_port_notify_bridge_fdb_flush(dp);
}
int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age) int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age)
{ {
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
...@@ -40,7 +70,7 @@ int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age) ...@@ -40,7 +70,7 @@ int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age)
ds->ops->port_stp_state_set(ds, port, state); ds->ops->port_stp_state_set(ds, port, state);
if (do_fast_age && ds->ops->port_fast_age) { if (do_fast_age && dp->learning) {
/* Fast age FDB entries or flush appropriate forwarding database /* Fast age FDB entries or flush appropriate forwarding database
* for the given port, if we are moving it from Learning or * for the given port, if we are moving it from Learning or
* Forwarding state, to Disabled or Blocking or Listening state. * Forwarding state, to Disabled or Blocking or Listening state.
...@@ -54,7 +84,7 @@ int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age) ...@@ -54,7 +84,7 @@ int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age)
(state == BR_STATE_DISABLED || (state == BR_STATE_DISABLED ||
state == BR_STATE_BLOCKING || state == BR_STATE_BLOCKING ||
state == BR_STATE_LISTENING)) state == BR_STATE_LISTENING))
ds->ops->port_fast_age(ds, port); dsa_port_fast_age(dp);
} }
dp->stp_state = state; dp->stp_state = state;
...@@ -633,16 +663,33 @@ int dsa_port_pre_bridge_flags(const struct dsa_port *dp, ...@@ -633,16 +663,33 @@ int dsa_port_pre_bridge_flags(const struct dsa_port *dp,
return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack); return ds->ops->port_pre_bridge_flags(ds, dp->index, flags, extack);
} }
int dsa_port_bridge_flags(const struct dsa_port *dp, int dsa_port_bridge_flags(struct dsa_port *dp,
struct switchdev_brport_flags flags, struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack) struct netlink_ext_ack *extack)
{ {
struct dsa_switch *ds = dp->ds; struct dsa_switch *ds = dp->ds;
int err;
if (!ds->ops->port_bridge_flags) if (!ds->ops->port_bridge_flags)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return ds->ops->port_bridge_flags(ds, dp->index, flags, extack); err = ds->ops->port_bridge_flags(ds, dp->index, flags, extack);
if (err)
return err;
if (flags.mask & BR_LEARNING) {
bool learning = flags.val & BR_LEARNING;
if (learning == dp->learning)
return 0;
if (dp->learning && !learning)
dsa_port_fast_age(dp);
dp->learning = learning;
}
return 0;
} }
int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu, int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
......
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