diff mbox series

[v4,net-next,12/15] net: dsa: Handle MST state changes

Message ID 20220315002543.190587-13-tobias@waldekranz.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net: bridge: Multiple Spanning Trees | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 18 this patch: 18
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 24 this patch: 24
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 23 this patch: 23
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 127 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Tobias Waldekranz March 15, 2022, 12:25 a.m. UTC
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>
---
 include/net/dsa.h  |  3 ++
 net/dsa/dsa_priv.h |  2 ++
 net/dsa/port.c     | 70 +++++++++++++++++++++++++++++++++++++++++++---
 net/dsa/slave.c    |  6 ++++
 4 files changed, 77 insertions(+), 4 deletions(-)

Comments

Vladimir Oltean March 15, 2022, 4:42 p.m. UTC | #1
On Tue, Mar 15, 2022 at 01:25:40AM +0100, Tobias Waldekranz wrote:
> 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>
> ---
>  include/net/dsa.h  |  3 ++
>  net/dsa/dsa_priv.h |  2 ++
>  net/dsa/port.c     | 70 +++++++++++++++++++++++++++++++++++++++++++---
>  net/dsa/slave.c    |  6 ++++
>  4 files changed, 77 insertions(+), 4 deletions(-)
> 
> diff --git a/include/net/dsa.h b/include/net/dsa.h
> index 1ddaa2cc5842..0f369f2e9a97 100644
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -945,7 +945,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);
> diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
> index d90b4cf0c9d2..2ae8996cf7c8 100644
> --- a/net/dsa/dsa_priv.h
> +++ b/net/dsa/dsa_priv.h
> @@ -215,6 +215,8 @@ 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);
>  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);
> diff --git a/net/dsa/port.c b/net/dsa/port.c
> index 3ac114f6fc22..a2a817bb77b1 100644
> --- a/net/dsa/port.c
> +++ b/net/dsa/port.c
> @@ -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,32 @@ 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 dsa_switch *ds = dp->ds;
> +	int err;
> +
> +	if (!ds->ops->port_mst_state_set)
> +		return -EOPNOTSUPP;
> +
> +	err = ds->ops->port_mst_state_set(ds, dp->index, state);
> +	if (err)
> +		return err;
> +
> +	if (dp->learning) {
> +		switch (state->state) {
> +		case BR_STATE_DISABLED:
> +		case BR_STATE_BLOCKING:
> +		case BR_STATE_LISTENING:

Is there a requirement in br_mst_set_state() to put the switchdev
notifier at the end instead of at the beginning?

I'm tempted to ask you to introduce br_mst_get_state(), then assign
old_state = br_mst_get_state(dsa_port_bridge_dev_get(dp), state->msti),
then perform the VLAN fast age only on the appropriate state transitions,
just like the regular fast age.

> +			err = dsa_port_msti_fast_age(dp, state->msti);
> +			break;
> +		}
> +	}
> +
> +	return err;
> +}
> +
>  int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
>  {
>  	struct dsa_switch *ds = dp->ds;
> @@ -326,6 +386,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);
>  }
>  
> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
> index 5e986cdeaae5..4300fc76f3af 100644
> --- a/net/dsa/slave.c
> +++ b/net/dsa/slave.c
> @@ -450,6 +450,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);
> +		break;
>  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
>  		if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
>  			return -EOPNOTSUPP;
> -- 
> 2.25.1
>
Vladimir Oltean March 15, 2022, 4:44 p.m. UTC | #2
On Tue, Mar 15, 2022 at 06:42:49PM +0200, Vladimir Oltean wrote:
> Is there a requirement in br_mst_set_state() to put the switchdev
> notifier at the end instead of at the beginning?
> 
> I'm tempted to ask you to introduce br_mst_get_state(), then assign
> old_state = br_mst_get_state(dsa_port_bridge_dev_get(dp), state->msti),

dsa_port_to_bridge_port(dp), excuse me.

> then perform the VLAN fast age only on the appropriate state transitions,
> just like the regular fast age.
Tobias Waldekranz March 16, 2022, 9:45 a.m. UTC | #3
On Tue, Mar 15, 2022 at 18:42, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Tue, Mar 15, 2022 at 01:25:40AM +0100, Tobias Waldekranz wrote:
>> 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>
>> ---
>>  include/net/dsa.h  |  3 ++
>>  net/dsa/dsa_priv.h |  2 ++
>>  net/dsa/port.c     | 70 +++++++++++++++++++++++++++++++++++++++++++---
>>  net/dsa/slave.c    |  6 ++++
>>  4 files changed, 77 insertions(+), 4 deletions(-)
>> 
>> diff --git a/include/net/dsa.h b/include/net/dsa.h
>> index 1ddaa2cc5842..0f369f2e9a97 100644
>> --- a/include/net/dsa.h
>> +++ b/include/net/dsa.h
>> @@ -945,7 +945,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);
>> diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
>> index d90b4cf0c9d2..2ae8996cf7c8 100644
>> --- a/net/dsa/dsa_priv.h
>> +++ b/net/dsa/dsa_priv.h
>> @@ -215,6 +215,8 @@ 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);
>>  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);
>> diff --git a/net/dsa/port.c b/net/dsa/port.c
>> index 3ac114f6fc22..a2a817bb77b1 100644
>> --- a/net/dsa/port.c
>> +++ b/net/dsa/port.c
>> @@ -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,32 @@ 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 dsa_switch *ds = dp->ds;
>> +	int err;
>> +
>> +	if (!ds->ops->port_mst_state_set)
>> +		return -EOPNOTSUPP;
>> +
>> +	err = ds->ops->port_mst_state_set(ds, dp->index, state);
>> +	if (err)
>> +		return err;
>> +
>> +	if (dp->learning) {
>> +		switch (state->state) {
>> +		case BR_STATE_DISABLED:
>> +		case BR_STATE_BLOCKING:
>> +		case BR_STATE_LISTENING:
>
> Is there a requirement in br_mst_set_state() to put the switchdev
> notifier at the end instead of at the beginning?

Not that I can think of. Moving it.

> I'm tempted to ask you to introduce br_mst_get_state(), then assign
> old_state = br_mst_get_state(dsa_port_bridge_dev_get(dp), state->msti),
> then perform the VLAN fast age only on the appropriate state transitions,
> just like the regular fast age.

No time like the present!

Question though:

>> +			err = dsa_port_msti_fast_age(dp, state->msti);

If _msti_fast_age returns an error here, do we want that to bubble up to
the bridge? It seems more important to keep the bridge in sync with the
hardware. I.e. the hardware state has already been successfully synced,
we just weren't able to flush all VLANs for some reason. We could revert
the state I guess, but what if that fails?

Should we settle for a log message?

>> +			break;
>> +		}
>> +	}
>> +
>> +	return err;
>> +}
>> +
>>  int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
>>  {
>>  	struct dsa_switch *ds = dp->ds;
>> @@ -326,6 +386,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);
>>  }
>>  
>> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
>> index 5e986cdeaae5..4300fc76f3af 100644
>> --- a/net/dsa/slave.c
>> +++ b/net/dsa/slave.c
>> @@ -450,6 +450,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);
>> +		break;
>>  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
>>  		if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
>>  			return -EOPNOTSUPP;
>> -- 
>> 2.25.1
>>
Tobias Waldekranz March 16, 2022, 9:51 a.m. UTC | #4
On Wed, Mar 16, 2022 at 10:45, Tobias Waldekranz <tobias@waldekranz.com> wrote:
> On Tue, Mar 15, 2022 at 18:42, Vladimir Oltean <olteanv@gmail.com> wrote:
>> On Tue, Mar 15, 2022 at 01:25:40AM +0100, Tobias Waldekranz wrote:
>>> 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>
>>> ---
>>>  include/net/dsa.h  |  3 ++
>>>  net/dsa/dsa_priv.h |  2 ++
>>>  net/dsa/port.c     | 70 +++++++++++++++++++++++++++++++++++++++++++---
>>>  net/dsa/slave.c    |  6 ++++
>>>  4 files changed, 77 insertions(+), 4 deletions(-)
>>> 
>>> diff --git a/include/net/dsa.h b/include/net/dsa.h
>>> index 1ddaa2cc5842..0f369f2e9a97 100644
>>> --- a/include/net/dsa.h
>>> +++ b/include/net/dsa.h
>>> @@ -945,7 +945,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);
>>> diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
>>> index d90b4cf0c9d2..2ae8996cf7c8 100644
>>> --- a/net/dsa/dsa_priv.h
>>> +++ b/net/dsa/dsa_priv.h
>>> @@ -215,6 +215,8 @@ 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);
>>>  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);
>>> diff --git a/net/dsa/port.c b/net/dsa/port.c
>>> index 3ac114f6fc22..a2a817bb77b1 100644
>>> --- a/net/dsa/port.c
>>> +++ b/net/dsa/port.c
>>> @@ -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,32 @@ 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 dsa_switch *ds = dp->ds;
>>> +	int err;
>>> +
>>> +	if (!ds->ops->port_mst_state_set)
>>> +		return -EOPNOTSUPP;
>>> +
>>> +	err = ds->ops->port_mst_state_set(ds, dp->index, state);
>>> +	if (err)
>>> +		return err;
>>> +
>>> +	if (dp->learning) {
>>> +		switch (state->state) {
>>> +		case BR_STATE_DISABLED:
>>> +		case BR_STATE_BLOCKING:
>>> +		case BR_STATE_LISTENING:
>>
>> Is there a requirement in br_mst_set_state() to put the switchdev
>> notifier at the end instead of at the beginning?
>
> Not that I can think of. Moving it.
>
>> I'm tempted to ask you to introduce br_mst_get_state(), then assign
>> old_state = br_mst_get_state(dsa_port_bridge_dev_get(dp), state->msti),
>> then perform the VLAN fast age only on the appropriate state transitions,
>> just like the regular fast age.
>
> No time like the present!
>
> Question though:
>
>>> +			err = dsa_port_msti_fast_age(dp, state->msti);
>
> If _msti_fast_age returns an error here, do we want that to bubble up to
> the bridge? It seems more important to keep the bridge in sync with the
> hardware. I.e. the hardware state has already been successfully synced,
> we just weren't able to flush all VLANs for some reason. We could revert
> the state I guess, but what if that fails?
>
> Should we settle for a log message?

Or should we set the extack message? Similar to how we report software
fallback of bridging/LAGs?

>>> +			break;
>>> +		}
>>> +	}
>>> +
>>> +	return err;
>>> +}
>>> +
>>>  int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
>>>  {
>>>  	struct dsa_switch *ds = dp->ds;
>>> @@ -326,6 +386,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);
>>>  }
>>>  
>>> diff --git a/net/dsa/slave.c b/net/dsa/slave.c
>>> index 5e986cdeaae5..4300fc76f3af 100644
>>> --- a/net/dsa/slave.c
>>> +++ b/net/dsa/slave.c
>>> @@ -450,6 +450,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);
>>> +		break;
>>>  	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
>>>  		if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
>>>  			return -EOPNOTSUPP;
>>> -- 
>>> 2.25.1
>>>
Vladimir Oltean March 16, 2022, 1:02 p.m. UTC | #5
On Wed, Mar 16, 2022 at 10:51:17AM +0100, Tobias Waldekranz wrote:
> > Question though:
> >
> >>> +			err = dsa_port_msti_fast_age(dp, state->msti);
> >
> > If _msti_fast_age returns an error here, do we want that to bubble up to
> > the bridge? It seems more important to keep the bridge in sync with the
> > hardware. I.e. the hardware state has already been successfully synced,
> > we just weren't able to flush all VLANs for some reason. We could revert
> > the state I guess, but what if that fails?
> >
> > Should we settle for a log message?
> 
> Or should we set the extack message? Similar to how we report software
> fallback of bridging/LAGs?

A warning extack and chug along sounds great. The worst that can happen
if flushing a VLAN's FDB fails is that the topology will reconverge slower.
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 1ddaa2cc5842..0f369f2e9a97 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -945,7 +945,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);
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index d90b4cf0c9d2..2ae8996cf7c8 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -215,6 +215,8 @@  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);
 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);
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 3ac114f6fc22..a2a817bb77b1 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -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,32 @@  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 dsa_switch *ds = dp->ds;
+	int err;
+
+	if (!ds->ops->port_mst_state_set)
+		return -EOPNOTSUPP;
+
+	err = ds->ops->port_mst_state_set(ds, dp->index, state);
+	if (err)
+		return err;
+
+	if (dp->learning) {
+		switch (state->state) {
+		case BR_STATE_DISABLED:
+		case BR_STATE_BLOCKING:
+		case BR_STATE_LISTENING:
+			err = dsa_port_msti_fast_age(dp, state->msti);
+			break;
+		}
+	}
+
+	return err;
+}
+
 int dsa_port_enable_rt(struct dsa_port *dp, struct phy_device *phy)
 {
 	struct dsa_switch *ds = dp->ds;
@@ -326,6 +386,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);
 }
 
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 5e986cdeaae5..4300fc76f3af 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -450,6 +450,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);
+		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
 		if (!dsa_port_offloads_bridge_dev(dp, attr->orig_dev))
 			return -EOPNOTSUPP;