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: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
cfe908c116
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
|
Загрузка…
Ссылка в новой задаче