diff mbox series

[net-next,1/3] net: dsa: add support for bridge flags

Message ID E1gvNNT-0006Xr-JA@rmk-PC.armlinux.org.uk
State Superseded
Delegated to: David Miller
Headers show
Series net: dsa: mv88e6xxx: fix IPv6 | expand

Commit Message

Russell King (Oracle) Feb. 17, 2019, 2:25 p.m. UTC
The Linux bridge implementation allows various properties of the bridge
to be controlled, such as flooding unknown unicast and multicast frames.
This patch adds the necessary DSA infrastructure to allow the Linux
bridge support to control these properties for DSA switches.

We implement this by providing two new methods: one to get the switch-
wide support bitmask, and another to set the properties.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 include/net/dsa.h  |  3 +++
 net/dsa/dsa_priv.h |  2 ++
 net/dsa/port.c     | 15 +++++++++++++++
 net/dsa/slave.c    |  6 ++++++
 4 files changed, 26 insertions(+)

Comments

Florian Fainelli Feb. 17, 2019, 9:37 p.m. UTC | #1
On 2/17/2019 6:25 AM, Russell King wrote:
> The Linux bridge implementation allows various properties of the bridge
> to be controlled, such as flooding unknown unicast and multicast frames.
> This patch adds the necessary DSA infrastructure to allow the Linux
> bridge support to control these properties for DSA switches.
> 
> We implement this by providing two new methods: one to get the switch-
> wide support bitmask, and another to set the properties.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---

[snip]

>  
> +int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
> +			  struct switchdev_trans *trans)
> +{
> +	struct dsa_switch *ds = dp->ds;
> +	int port = dp->index;
> +
> +	if (switchdev_trans_ph_prepare(trans))
> +		return ds->ops->port_bridge_flags ? 0 : -EOPNOTSUPP;
> +
> +	if (ds->ops->port_bridge_flags)
> +		ds->ops->port_bridge_flags(ds, port, flags);

If you have a switch fabric with multiple switches, it seems to me that
you also need to make sure that the DSA and CPU ports will have
compatible flooding attribute, so just like the port_vlan_add()
callback, you probably need to make this a switch fabric-wide event and
use a notifier here. At least the DSA ports need to have MC flooding
turned on for an user port to also have MC flooding working.

Other than that LGTM.
Russell King (Oracle) Feb. 17, 2019, 10:04 p.m. UTC | #2
On Sun, Feb 17, 2019 at 01:37:19PM -0800, Florian Fainelli wrote:
> 
> 
> On 2/17/2019 6:25 AM, Russell King wrote:
> > The Linux bridge implementation allows various properties of the bridge
> > to be controlled, such as flooding unknown unicast and multicast frames.
> > This patch adds the necessary DSA infrastructure to allow the Linux
> > bridge support to control these properties for DSA switches.
> > 
> > We implement this by providing two new methods: one to get the switch-
> > wide support bitmask, and another to set the properties.
> > 
> > Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> > ---
> 
> [snip]
> 
> >  
> > +int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
> > +			  struct switchdev_trans *trans)
> > +{
> > +	struct dsa_switch *ds = dp->ds;
> > +	int port = dp->index;
> > +
> > +	if (switchdev_trans_ph_prepare(trans))
> > +		return ds->ops->port_bridge_flags ? 0 : -EOPNOTSUPP;
> > +
> > +	if (ds->ops->port_bridge_flags)
> > +		ds->ops->port_bridge_flags(ds, port, flags);
> 
> If you have a switch fabric with multiple switches, it seems to me that
> you also need to make sure that the DSA and CPU ports will have
> compatible flooding attribute, so just like the port_vlan_add()
> callback, you probably need to make this a switch fabric-wide event and
> use a notifier here. At least the DSA ports need to have MC flooding
> turned on for an user port to also have MC flooding working.

mv88e6xxx already today detects CPU and DSA ports and sets unicast
and multicast flooding for these ports - see
mv88e6xxx_setup_egress_floods():

        /* Upstream ports flood frames with unknown unicast or multicast DA */
        flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
        if (chip->info->ops->port_set_egress_floods)
                return chip->info->ops->port_set_egress_floods(chip, port,
                                                               flood, flood);
Florian Fainelli Feb. 17, 2019, 10:07 p.m. UTC | #3
On 2/17/2019 2:04 PM, Russell King - ARM Linux admin wrote:
> On Sun, Feb 17, 2019 at 01:37:19PM -0800, Florian Fainelli wrote:
>>
>>
>> On 2/17/2019 6:25 AM, Russell King wrote:
>>> The Linux bridge implementation allows various properties of the bridge
>>> to be controlled, such as flooding unknown unicast and multicast frames.
>>> This patch adds the necessary DSA infrastructure to allow the Linux
>>> bridge support to control these properties for DSA switches.
>>>
>>> We implement this by providing two new methods: one to get the switch-
>>> wide support bitmask, and another to set the properties.
>>>
>>> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
>>> ---
>>
>> [snip]
>>
>>>  
>>> +int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
>>> +			  struct switchdev_trans *trans)
>>> +{
>>> +	struct dsa_switch *ds = dp->ds;
>>> +	int port = dp->index;
>>> +
>>> +	if (switchdev_trans_ph_prepare(trans))
>>> +		return ds->ops->port_bridge_flags ? 0 : -EOPNOTSUPP;
>>> +
>>> +	if (ds->ops->port_bridge_flags)
>>> +		ds->ops->port_bridge_flags(ds, port, flags);
>>
>> If you have a switch fabric with multiple switches, it seems to me that
>> you also need to make sure that the DSA and CPU ports will have
>> compatible flooding attribute, so just like the port_vlan_add()
>> callback, you probably need to make this a switch fabric-wide event and
>> use a notifier here. At least the DSA ports need to have MC flooding
>> turned on for an user port to also have MC flooding working.
> 
> mv88e6xxx already today detects CPU and DSA ports and sets unicast
> and multicast flooding for these ports - see
> mv88e6xxx_setup_egress_floods():

Indeed, probably for historical reasons, since that type of logic should
ideally be migrated to the core DSA layer, this is fine for now though.

> 
>         /* Upstream ports flood frames with unknown unicast or multicast DA */
>         flood = dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port);
>         if (chip->info->ops->port_set_egress_floods)
>                 return chip->info->ops->port_set_egress_floods(chip, port,
>                                                                flood, flood);
>
Russell King (Oracle) Feb. 18, 2019, 12:50 a.m. UTC | #4
On Sun, Feb 17, 2019 at 02:07:54PM -0800, Florian Fainelli wrote:
> 
> 
> On 2/17/2019 2:04 PM, Russell King - ARM Linux admin wrote:
> > On Sun, Feb 17, 2019 at 01:37:19PM -0800, Florian Fainelli wrote:
> >>
> >>
> >> On 2/17/2019 6:25 AM, Russell King wrote:
> >>> The Linux bridge implementation allows various properties of the bridge
> >>> to be controlled, such as flooding unknown unicast and multicast frames.
> >>> This patch adds the necessary DSA infrastructure to allow the Linux
> >>> bridge support to control these properties for DSA switches.
> >>>
> >>> We implement this by providing two new methods: one to get the switch-
> >>> wide support bitmask, and another to set the properties.
> >>>
> >>> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> >>> ---
> >>
> >> [snip]
> >>
> >>>  
> >>> +int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
> >>> +			  struct switchdev_trans *trans)
> >>> +{
> >>> +	struct dsa_switch *ds = dp->ds;
> >>> +	int port = dp->index;
> >>> +
> >>> +	if (switchdev_trans_ph_prepare(trans))
> >>> +		return ds->ops->port_bridge_flags ? 0 : -EOPNOTSUPP;
> >>> +
> >>> +	if (ds->ops->port_bridge_flags)
> >>> +		ds->ops->port_bridge_flags(ds, port, flags);
> >>
> >> If you have a switch fabric with multiple switches, it seems to me that
> >> you also need to make sure that the DSA and CPU ports will have
> >> compatible flooding attribute, so just like the port_vlan_add()
> >> callback, you probably need to make this a switch fabric-wide event and
> >> use a notifier here. At least the DSA ports need to have MC flooding
> >> turned on for an user port to also have MC flooding working.
> > 
> > mv88e6xxx already today detects CPU and DSA ports and sets unicast
> > and multicast flooding for these ports - see
> > mv88e6xxx_setup_egress_floods():
> 
> Indeed, probably for historical reasons, since that type of logic should
> ideally be migrated to the core DSA layer, this is fine for now though.

From what I can see, the port_vlan_add()/port_vlan_del() implementation
is far from ideal, just like "always enabling flooding for CPU/DSA
ports" is not ideal.

From what I can see, when we add a vlan to any port in a group of
switches, we turn on forwarding of frames within that vlan to all
switches and the CPU port regardless of whether there are any ports
configured on those other switches.

If we apply the same logic to flooding (which is an egress control),
then as soon as we enable flooding on any port in a bridged group of
switches, we start forwarding flooded frames to all switches and the
CPU port regardless of whether anyone there is interested.

What would be more optimal is to only enable flooding in the direction
of switches that have an interest in those patches.  For example, lets
assume we have three daisy-chained switches with three lan ports each,
with interfaces of the format swNlanN.

All of sw1's lan interfaces and one of sw2's lan interfaces are part
of a Linux bridge.  Let's say we have enabled flooding on only one of
sw1's ports and the rest are disabled.  With the way port_vlan_add()
works (which you're suggesting as a model for flooding too) we would
end up enabling flooding across all switches and to the CPU port, even
though:

- sw3 has no interest in any of the flooded frames as none of its ports
  are part of the bridge
- sw2 has no interest in any of the frames flooded from sw1

Doesn't seem optimal.

Given that, is it worth applying the same implementation to flooding
as already exists for vlans?  It would mean that as soon as we have
any single port in the group of switches that has flooding enabled,
we've enabled flooding across the entire group of switches.  As we
are unable to access the neighbouring switches state, we're unable
to turn flooding off (just like we're already unable to disable vlan
forwarding on the CPU/DSA ports).

Maybe if DSA had some infrastructure to know what downstream and
upstream switches wanted in terms of which frames, we could do better
but I don't currently see any infrastructure for that.
Russell King (Oracle) Feb. 18, 2019, 11:23 a.m. UTC | #5
On Mon, Feb 18, 2019 at 12:50:31AM +0000, Russell King - ARM Linux admin wrote:
> From what I can see, the port_vlan_add()/port_vlan_del() implementation
> is far from ideal, just like "always enabling flooding for CPU/DSA
> ports" is not ideal.

There also seems to be a discrepency between what net/dsa wants to do
and some of the implementations in drivers/net/dsa:

dsa_switch_vlan_add() does this:

        bitmap_zero(ds->bitmap, ds->num_ports);
        if (ds->index == info->sw_index)
                set_bit(info->port, ds->bitmap);
        for (port = 0; port < ds->num_ports; port++)
                if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
                        set_bit(port, ds->bitmap);

We then call ds->ops->port_vlan_add() for each port in ds->bitmap,
which will include DSA and CPU ports on every switch in the tree.

For rtl8366, this calls into rtl8366_vlan_add(), which does:

        if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
                dev_err(smi->dev, "port is DSA or CPU port\n");

which is surely a guaranteed error message if we have any CPU or DSA
ports specified on a rtl8366.  The example in the DT documentation
for this driver does suggest that it is capable of having CPU ports.
Vivien Didelot Feb. 19, 2019, 3:42 p.m. UTC | #6
Hi Russell,

On Mon, 18 Feb 2019 11:23:55 +0000, Russell King - ARM Linux admin <linux@armlinux.org.uk> wrote:
> On Mon, Feb 18, 2019 at 12:50:31AM +0000, Russell King - ARM Linux admin wrote:
> > From what I can see, the port_vlan_add()/port_vlan_del() implementation
> > is far from ideal, just like "always enabling flooding for CPU/DSA
> > ports" is not ideal.
> 
> There also seems to be a discrepency between what net/dsa wants to do
> and some of the implementations in drivers/net/dsa:
> 
> dsa_switch_vlan_add() does this:
> 
>         bitmap_zero(ds->bitmap, ds->num_ports);
>         if (ds->index == info->sw_index)
>                 set_bit(info->port, ds->bitmap);
>         for (port = 0; port < ds->num_ports; port++)
>                 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
>                         set_bit(port, ds->bitmap);
> 
> We then call ds->ops->port_vlan_add() for each port in ds->bitmap,
> which will include DSA and CPU ports on every switch in the tree.
> 
> For rtl8366, this calls into rtl8366_vlan_add(), which does:
> 
>         if (dsa_is_dsa_port(ds, port) || dsa_is_cpu_port(ds, port))
>                 dev_err(smi->dev, "port is DSA or CPU port\n");
> 
> which is surely a guaranteed error message if we have any CPU or DSA
> ports specified on a rtl8366.  The example in the DT documentation
> for this driver does suggest that it is capable of having CPU ports.

Correct, at the very beginning, most of the logic was implemented by the DSA
drivers themselves, like programming DSA and CPU ports. But this makes them
more complicated for no value, so the logic is slowly moved up to the core,
which now handles more of the generic logic.

Ideally I would like DSA drivers to be dumb, implementing simple switch-wide
ops reflecting their specs, e.g. switch-wide vlan_add ops taking a bitmap
of ports for a given VID.

This is a work in progress and we are slowly getting there. In the meantime
there are indeed discrepencies between DSA core and drivers as you pointed out.


Thanks,

	Vivien
diff mbox series

Patch

diff --git a/include/net/dsa.h b/include/net/dsa.h
index 7f2a668ef2cc..6cc1222aa2ca 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -400,6 +400,9 @@  struct dsa_switch_ops {
 	void	(*port_stp_state_set)(struct dsa_switch *ds, int port,
 				      u8 state);
 	void	(*port_fast_age)(struct dsa_switch *ds, int port);
+	int	(*port_bridge_flags)(struct dsa_switch *ds, int port,
+				     unsigned long);
+	unsigned long (*bridge_flags_support)(struct dsa_switch *ds);
 
 	/*
 	 * VLAN support
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 1f4972dab9f2..f4f99ec29f5d 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -160,6 +160,8 @@  int dsa_port_mdb_add(const struct dsa_port *dp,
 		     struct switchdev_trans *trans);
 int dsa_port_mdb_del(const struct dsa_port *dp,
 		     const struct switchdev_obj_port_mdb *mdb);
+int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
+			  struct switchdev_trans *trans);
 int dsa_port_vlan_add(struct dsa_port *dp,
 		      const struct switchdev_obj_port_vlan *vlan,
 		      struct switchdev_trans *trans);
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 2d7e01b23572..eb745041b1d9 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -177,6 +177,21 @@  int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock,
 	return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info);
 }
 
+int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
+			  struct switchdev_trans *trans)
+{
+	struct dsa_switch *ds = dp->ds;
+	int port = dp->index;
+
+	if (switchdev_trans_ph_prepare(trans))
+		return ds->ops->port_bridge_flags ? 0 : -EOPNOTSUPP;
+
+	if (ds->ops->port_bridge_flags)
+		ds->ops->port_bridge_flags(ds, port, flags);
+
+	return 0;
+}
+
 int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
 		     u16 vid)
 {
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 2e5e7c04821b..af53cdd14a29 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -295,6 +295,9 @@  static int dsa_slave_port_attr_set(struct net_device *dev,
 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
 		ret = dsa_port_ageing_time(dp, attr->u.ageing_time, trans);
 		break;
+	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans);
+		break;
 	default:
 		ret = -EOPNOTSUPP;
 		break;
@@ -384,6 +387,9 @@  static int dsa_slave_port_attr_get(struct net_device *dev,
 	switch (attr->id) {
 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
 		attr->u.brport_flags_support = 0;
+		if (ds->ops->bridge_flags_support)
+			attr->u.brport_flags_support |=
+				ds->ops->bridge_flags_support(ds);
 		break;
 	default:
 		return -EOPNOTSUPP;