Commit 7414af30 authored by Tobias Waldekranz's avatar Tobias Waldekranz Committed by Jakub Kicinski
Browse files

net: dsa: Handle MST state changes



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: default avatarTobias Waldekranz <tobias@waldekranz.com>
Reviewed-by: default avatarVladimir Oltean <olteanv@gmail.com>
Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 8e6598a7
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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);
+77 −8
Original line number Diff line number Diff line
@@ -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.
@@ -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)
@@ -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;
@@ -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);
}

@@ -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;
	}
+6 −0
Original line number Diff line number Diff line
@@ -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;