Skip to content

Commit

Permalink
net: dsa: Handle MST state changes
Browse files Browse the repository at this point in the history
Add the usual trampoline functionality from the generic DSA layer down
to the drivers for MST state changes.

When a state changes to disabled/blocking/listening, make sure to fast
age any dynamic entries in the affected VLANs (those controlled by the
MSTI in question).

Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  • Loading branch information
Tobias Waldekranz authored and Jakub Kicinski committed Mar 17, 2022
1 parent 8e6598a commit 7414af3
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 8 deletions.
3 changes: 3 additions & 0 deletions include/net/dsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,10 @@ struct dsa_switch_ops {
struct dsa_bridge bridge);
void (*port_stp_state_set)(struct dsa_switch *ds, int port,
u8 state);
int (*port_mst_state_set)(struct dsa_switch *ds, int port,
const struct switchdev_mst_state *state);
void (*port_fast_age)(struct dsa_switch *ds, int port);
int (*port_vlan_fast_age)(struct dsa_switch *ds, int port, u16 vid);
int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port,
struct switchdev_brport_flags flags,
struct netlink_ext_ack *extack);
Expand Down
3 changes: 3 additions & 0 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ static inline struct net_device *dsa_master_find_slave(struct net_device *dev,
void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp,
const struct dsa_device_ops *tag_ops);
int dsa_port_set_state(struct dsa_port *dp, u8 state, bool do_fast_age);
int dsa_port_set_mst_state(struct dsa_port *dp,
const struct switchdev_mst_state *state,
struct netlink_ext_ack *extack);
int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy);
int dsa_port_enable(struct dsa_port *dp, struct phy_device *phy);
void dsa_port_disable_rt(struct dsa_port *dp);
Expand Down
85 changes: 77 additions & 8 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ 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)
static void dsa_port_notify_bridge_fdb_flush(const struct dsa_port *dp, u16 vid)
{
struct net_device *brport_dev = dsa_port_to_bridge_port(dp);
struct switchdev_notifier_fdb_info info = {
/* flush all VLANs */
.vid = 0,
.vid = vid,
};

/* When the port becomes standalone it has already left the bridge.
Expand All @@ -57,7 +56,42 @@ static void dsa_port_fast_age(const struct dsa_port *dp)

ds->ops->port_fast_age(ds, dp->index);

dsa_port_notify_bridge_fdb_flush(dp);
/* flush all VLANs */
dsa_port_notify_bridge_fdb_flush(dp, 0);
}

static int dsa_port_vlan_fast_age(const struct dsa_port *dp, u16 vid)
{
struct dsa_switch *ds = dp->ds;
int err;

if (!ds->ops->port_vlan_fast_age)
return -EOPNOTSUPP;

err = ds->ops->port_vlan_fast_age(ds, dp->index, vid);

if (!err)
dsa_port_notify_bridge_fdb_flush(dp, vid);

return err;
}

static int dsa_port_msti_fast_age(const struct dsa_port *dp, u16 msti)
{
DECLARE_BITMAP(vids, VLAN_N_VID) = { 0 };
int err, vid;

err = br_mst_get_info(dsa_port_bridge_dev_get(dp), msti, vids);
if (err)
return err;

for_each_set_bit(vid, vids, VLAN_N_VID) {
err = dsa_port_vlan_fast_age(dp, vid);
if (err)
return err;
}

return 0;
}

static bool dsa_port_can_configure_learning(struct dsa_port *dp)
Expand Down Expand Up @@ -118,6 +152,42 @@ static void dsa_port_set_state_now(struct dsa_port *dp, u8 state,
pr_err("DSA: failed to set STP state %u (%d)\n", state, err);
}

int dsa_port_set_mst_state(struct dsa_port *dp,
const struct switchdev_mst_state *state,
struct netlink_ext_ack *extack)
{
struct dsa_switch *ds = dp->ds;
u8 prev_state;
int err;

if (!ds->ops->port_mst_state_set)
return -EOPNOTSUPP;

err = br_mst_get_state(dsa_port_to_bridge_port(dp), state->msti,
&prev_state);
if (err)
return err;

err = ds->ops->port_mst_state_set(ds, dp->index, state);
if (err)
return err;

if (!(dp->learning &&
(prev_state == BR_STATE_LEARNING ||
prev_state == BR_STATE_FORWARDING) &&
(state->state == BR_STATE_DISABLED ||
state->state == BR_STATE_BLOCKING ||
state->state == BR_STATE_LISTENING)))
return 0;

err = dsa_port_msti_fast_age(dp, state->msti);
if (err)
NL_SET_ERR_MSG_MOD(extack,
"Unable to flush associated VLANs");

return 0;
}

int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
{
struct dsa_switch *ds = dp->ds;
Expand Down Expand Up @@ -326,6 +396,8 @@ static bool dsa_port_supports_mst(struct dsa_port *dp)
struct dsa_switch *ds = dp->ds;

return ds->ops->vlan_msti_set &&
ds->ops->port_mst_state_set &&
ds->ops->port_vlan_fast_age &&
dsa_port_can_configure_learning(dp);
}

Expand Down Expand Up @@ -749,10 +821,7 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
int dsa_port_mst_enable(struct dsa_port *dp, bool on,
struct netlink_ext_ack *extack)
{
if (!on)
return 0;

if (!dsa_port_supports_mst(dp)) {
if (on && !dsa_port_supports_mst(dp)) {
NL_SET_ERR_MSG_MOD(extack, "Hardware does not support MST");
return -EINVAL;
}
Expand Down
6 changes: 6 additions & 0 deletions net/dsa/slave.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,12 @@ static int dsa_slave_port_attr_set(struct net_device *dev, const void *ctx,

ret = dsa_port_set_state(dp, attr->u.stp_state, true);
break;
case SWITCHDEV_ATTR_ID_PORT_MST_STATE:
if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
return -EOPNOTSUPP;

ret = dsa_port_set_mst_state(dp, &attr->u.mst_state, extack);
break;
case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
return -EOPNOTSUPP;
Expand Down

0 comments on commit 7414af3

Please sign in to comment.