Skip to content

Commit

Permalink
Merge branch 'sja1105-fast-ageing'
Browse files Browse the repository at this point in the history
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: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Aug 8, 2021
2 parents 64ec13e + 5126ec7 commit cfe908c
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 32 deletions.
7 changes: 0 additions & 7 deletions drivers/net/dsa/mv88e6xxx/chip.c
Original file line number Diff line number Diff line change
Expand Up @@ -5797,7 +5797,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
struct netlink_ext_ack *extack)
{
struct mv88e6xxx_chip *chip = ds->priv;
bool do_fast_age = false;
int err = -EOPNOTSUPP;

mv88e6xxx_reg_lock(chip);
Expand All @@ -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);
if (err)
goto out;

if (!learning)
do_fast_age = true;
}

if (flags.mask & BR_FLOOD) {
Expand Down Expand Up @@ -5843,9 +5839,6 @@ static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
out:
mv88e6xxx_reg_unlock(chip);

if (do_fast_age)
mv88e6xxx_port_fast_age(ds, port);

return err;
}

Expand Down
1 change: 0 additions & 1 deletion drivers/net/dsa/sja1105/sja1105.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,6 @@ struct sja1105_private {
phy_interface_t phy_mode[SJA1105_MAX_NUM_PORTS];
bool fixed_link[SJA1105_MAX_NUM_PORTS];
bool vlan_aware;
unsigned long learn_ena;
unsigned long ucast_egress_floods;
unsigned long bcast_egress_floods;
const struct sja1105_info *info;
Expand Down
73 changes: 54 additions & 19 deletions drivers/net/dsa/sja1105/sja1105_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)
struct sja1105_mac_config_entry *mac;
struct dsa_switch *ds = priv->ds;
struct sja1105_table *table;
int i;
struct dsa_port *dp;

table = &priv->static_config.tables[BLK_IDX_MAC_CONFIG];

Expand All @@ -195,8 +195,11 @@ static int sja1105_init_mac_settings(struct sja1105_private *priv)

mac = table->entries;

for (i = 0; i < ds->num_ports; i++) {
mac[i] = default_mac;
list_for_each_entry(dp, &ds->dst->ports, list) {
if (dp->ds != ds)
continue;

mac[dp->index] = default_mac;

/* Let sja1105_bridge_stp_state_set() keep address learning
* enabled for the DSA ports. CPU ports use software-assisted
Expand All @@ -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
* exist.
*/
if (dsa_is_dsa_port(ds, i))
priv->learn_ena |= BIT(i);
if (dsa_port_is_dsa(dp))
dp->learning = true;
}

return 0;
Expand Down Expand Up @@ -1791,6 +1794,46 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port,
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,
const struct switchdev_obj_port_mdb *mdb)
{
Expand Down Expand Up @@ -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,
u8 state)
{
struct dsa_port *dp = dsa_to_port(ds, port);
struct sja1105_private *priv = ds->priv;
struct sja1105_mac_config_entry *mac;

Expand All @@ -1924,12 +1968,12 @@ static void sja1105_bridge_stp_state_set(struct dsa_switch *ds, int port,
case BR_STATE_LEARNING:
mac[port].ingress = true;
mac[port].egress = false;
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port));
mac[port].dyn_learn = dp->learning;
break;
case BR_STATE_FORWARDING:
mac[port].ingress = true;
mac[port].egress = true;
mac[port].dyn_learn = !!(priv->learn_ena & BIT(port));
mac[port].dyn_learn = dp->learning;
break;
default:
dev_err(ds->dev, "invalid STP state: %d\n", state);
Expand Down Expand Up @@ -2891,23 +2935,13 @@ static int sja1105_port_set_learning(struct sja1105_private *priv, int port,
bool enabled)
{
struct sja1105_mac_config_entry *mac;
int rc;

mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;

mac[port].dyn_learn = enabled;

rc = sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
&mac[port], true);
if (rc)
return rc;

if (enabled)
priv->learn_ena |= BIT(port);
else
priv->learn_ena &= ~BIT(port);

return 0;
return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
&mac[port], true);
}

static int sja1105_port_ucast_bcast_flood(struct sja1105_private *priv, int to,
Expand Down Expand Up @@ -3042,6 +3076,7 @@ static const struct dsa_switch_ops sja1105_switch_ops = {
.port_fdb_dump = sja1105_fdb_dump,
.port_fdb_add = sja1105_fdb_add,
.port_fdb_del = sja1105_fdb_del,
.port_fast_age = sja1105_fast_age,
.port_bridge_join = sja1105_bridge_join,
.port_bridge_leave = sja1105_bridge_leave,
.port_pre_bridge_flags = sja1105_port_pre_bridge_flags,
Expand Down
2 changes: 2 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ struct dsa_port {
struct device_node *dn;
unsigned int ageing_time;
bool vlan_filtering;
/* Managed by DSA on user ports and by drivers on CPU and DSA ports */
bool learning;
u8 stp_state;
struct net_device *bridge_dev;
int bridge_num;
Expand Down
2 changes: 1 addition & 1 deletion net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
struct switchdev_brport_flags flags,
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 netlink_ext_ack *extack);
int dsa_port_vlan_add(struct dsa_port *dp,
Expand Down
55 changes: 51 additions & 4 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

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)
{
struct dsa_switch *ds = dp->ds;
Expand All @@ -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);

if (do_fast_age && ds->ops->port_fast_age) {
if (do_fast_age && dp->learning) {
/* Fast age FDB entries or flush appropriate forwarding database
* for the given port, if we are moving it from Learning or
* Forwarding state, to Disabled or Blocking or Listening state.
Expand All @@ -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_BLOCKING ||
state == BR_STATE_LISTENING))
ds->ops->port_fast_age(ds, port);
dsa_port_fast_age(dp);
}

dp->stp_state = state;
Expand Down Expand Up @@ -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);
}

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 netlink_ext_ack *extack)
{
struct dsa_switch *ds = dp->ds;
int err;

if (!ds->ops->port_bridge_flags)
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,
Expand Down

0 comments on commit cfe908c

Please sign in to comment.