diff mbox series

[net-next,03/14] net: dsa: b53: Properly account for VLAN filtering

Message ID 20190116200102.2749-4-f.fainelli@gmail.com
State Changes Requested
Delegated to: David Miller
Headers show
Series net: dsa: management mode for bcm_sf2 | expand

Commit Message

Florian Fainelli Jan. 16, 2019, 8 p.m. UTC
VLAN filtering can be built into the kernel, and also dynamically turned
on/off through the bridge master device. Allow re-configuring the switch
appropriately to account for that by deciding whether VLAN table
(v_table) misses should lead to a drop or forward.

Fixes: a2482d2ce349 ("net: dsa: b53: Plug in VLAN support")
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/net/dsa/b53/b53_common.c | 59 +++++++++++++++++++++++++++++---
 drivers/net/dsa/b53/b53_priv.h   |  3 ++
 2 files changed, 57 insertions(+), 5 deletions(-)

Comments

Vivien Didelot Jan. 17, 2019, 4:36 p.m. UTC | #1
Hi Florian,

On Wed, 16 Jan 2019 12:00:51 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:

> +	/* Handle the case were multiple bridges span the same switch device
> +	 * and one of them has a different setting than what is being requested
> +	 * which would be breaking filtering semantics for any of the other
> +	 * bridge devices.
> +	 */
> +	b53_for_each_port(dev, i) {
> +		bridge_dev = dsa_to_port(ds, i)->bridge_dev;
> +		if (bridge_dev &&
> +		    bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
> +		    br_vlan_enabled(bridge_dev) != vlan_filtering) {
> +			netdev_err(bridge_dev,
> +				   "VLAN filtering is global to the switch!\n");
> +			return -EINVAL;
> +		}
> +	}
 
Unbridged ports must act as standard NICs and thus forward taggued frames.
What happens to them if there's a bridge with VLAN filtering enabled spawned
on other ports of your switch? Will the unbridged ports filter VLAN?


Thanks,

	Vivien
Florian Fainelli Jan. 17, 2019, 5:48 p.m. UTC | #2
On 1/17/19 8:36 AM, Vivien Didelot wrote:
> Hi Florian,
> 
> On Wed, 16 Jan 2019 12:00:51 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:
> 
>> +	/* Handle the case were multiple bridges span the same switch device
>> +	 * and one of them has a different setting than what is being requested
>> +	 * which would be breaking filtering semantics for any of the other
>> +	 * bridge devices.
>> +	 */
>> +	b53_for_each_port(dev, i) {
>> +		bridge_dev = dsa_to_port(ds, i)->bridge_dev;
>> +		if (bridge_dev &&
>> +		    bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
>> +		    br_vlan_enabled(bridge_dev) != vlan_filtering) {
>> +			netdev_err(bridge_dev,
>> +				   "VLAN filtering is global to the switch!\n");
>> +			return -EINVAL;
>> +		}
>> +	}
>  
> Unbridged ports must act as standard NICs and thus forward taggued frames.
> What happens to them if there's a bridge with VLAN filtering enabled spawned
> on other ports of your switch? Will the unbridged ports filter VLAN?
Because VLAN filtering a global setting to the switch, unbridged network
ports will effectively have VLAN filtering enabled, which is why the
ndo_vlan_rx_{add,kill}_vid functions to permit that use case.
Vivien Didelot Jan. 17, 2019, 6:47 p.m. UTC | #3
On Thu, 17 Jan 2019 09:48:53 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:
> On 1/17/19 8:36 AM, Vivien Didelot wrote:
> > Hi Florian,
> > 
> > On Wed, 16 Jan 2019 12:00:51 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:
> > 
> >> +	/* Handle the case were multiple bridges span the same switch device
> >> +	 * and one of them has a different setting than what is being requested
> >> +	 * which would be breaking filtering semantics for any of the other
> >> +	 * bridge devices.
> >> +	 */
> >> +	b53_for_each_port(dev, i) {
> >> +		bridge_dev = dsa_to_port(ds, i)->bridge_dev;
> >> +		if (bridge_dev &&
> >> +		    bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
> >> +		    br_vlan_enabled(bridge_dev) != vlan_filtering) {
> >> +			netdev_err(bridge_dev,
> >> +				   "VLAN filtering is global to the switch!\n");
> >> +			return -EINVAL;
> >> +		}
> >> +	}
> >  
> > Unbridged ports must act as standard NICs and thus forward taggued frames.
> > What happens to them if there's a bridge with VLAN filtering enabled spawned
> > on other ports of your switch? Will the unbridged ports filter VLAN?
> Because VLAN filtering a global setting to the switch, unbridged network
> ports will effectively have VLAN filtering enabled, which is why the
> ndo_vlan_rx_{add,kill}_vid functions to permit that use case.

But then vlan_filtering must simply not be allowed on your switch if you
have unbridged ports, no?

I might be mixing things up here but I don't understand yet how you can
have bridged and unbridged ports working correctly on your switch when it
has global VLAN filtering turned on. I understand that the switch will drop
the tagged frames on ingress.


Thanks,

	Vivien
Florian Fainelli Jan. 17, 2019, 7:06 p.m. UTC | #4
On 1/17/19 10:47 AM, Vivien Didelot wrote:
> On Thu, 17 Jan 2019 09:48:53 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:
>> On 1/17/19 8:36 AM, Vivien Didelot wrote:
>>> Hi Florian,
>>>
>>> On Wed, 16 Jan 2019 12:00:51 -0800, Florian Fainelli <f.fainelli@gmail.com> wrote:
>>>
>>>> +	/* Handle the case were multiple bridges span the same switch device
>>>> +	 * and one of them has a different setting than what is being requested
>>>> +	 * which would be breaking filtering semantics for any of the other
>>>> +	 * bridge devices.
>>>> +	 */
>>>> +	b53_for_each_port(dev, i) {
>>>> +		bridge_dev = dsa_to_port(ds, i)->bridge_dev;
>>>> +		if (bridge_dev &&
>>>> +		    bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
>>>> +		    br_vlan_enabled(bridge_dev) != vlan_filtering) {
>>>> +			netdev_err(bridge_dev,
>>>> +				   "VLAN filtering is global to the switch!\n");
>>>> +			return -EINVAL;
>>>> +		}
>>>> +	}
>>>  
>>> Unbridged ports must act as standard NICs and thus forward taggued frames.
>>> What happens to them if there's a bridge with VLAN filtering enabled spawned
>>> on other ports of your switch? Will the unbridged ports filter VLAN?
>> Because VLAN filtering a global setting to the switch, unbridged network
>> ports will effectively have VLAN filtering enabled, which is why the
>> ndo_vlan_rx_{add,kill}_vid functions to permit that use case.
> 
> But then vlan_filtering must simply not be allowed on your switch if you
> have unbridged ports, no?

VLAN filtering is an attribute that applies to the bridge, not the
non-bridged network ports.

> 
> I might be mixing things up here but I don't understand yet how you can
> have bridged and unbridged ports working correctly on your switch when it
> has global VLAN filtering turned on. I understand that the switch will drop
> the tagged frames on ingress.

With ndo_vlan_rx_{add,kill}_vid(), you get proper VLAN entries
programmed into the switch for non-bridged ports, therefore your switch
can allow VLAN tagged traffic (which these interfaces only support) just
fine, even when a bridge has VLAN filtering turned on on a bridge device
that has other switch ports enslaved.
diff mbox series

Patch

diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 964a9ec4652a..2fef4c564420 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -344,7 +344,8 @@  static void b53_set_forwarding(struct b53_device *dev, int enable)
 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_CTRL, mgmt);
 }
 
-static void b53_enable_vlan(struct b53_device *dev, bool enable)
+static void b53_enable_vlan(struct b53_device *dev, bool enable,
+			    bool enable_filtering)
 {
 	u8 mgmt, vc0, vc1, vc4 = 0, vc5;
 
@@ -369,8 +370,13 @@  static void b53_enable_vlan(struct b53_device *dev, bool enable)
 		vc0 |= VC0_VLAN_EN | VC0_VID_CHK_EN | VC0_VID_HASH_VID;
 		vc1 |= VC1_RX_MCST_UNTAG_EN | VC1_RX_MCST_FWD_EN;
 		vc4 &= ~VC4_ING_VID_CHECK_MASK;
-		vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
-		vc5 |= VC5_DROP_VTABLE_MISS;
+		if (enable_filtering) {
+			vc4 |= VC4_ING_VID_VIO_DROP << VC4_ING_VID_CHECK_S;
+			vc5 |= VC5_DROP_VTABLE_MISS;
+		} else {
+			vc4 |= VC4_ING_VID_VIO_FWD << VC4_ING_VID_CHECK_S;
+			vc5 &= ~VC5_DROP_VTABLE_MISS;
+		}
 
 		if (is5325(dev))
 			vc0 &= ~VC0_RESERVED_1;
@@ -420,6 +426,9 @@  static void b53_enable_vlan(struct b53_device *dev, bool enable)
 	}
 
 	b53_write8(dev, B53_CTRL_PAGE, B53_SWITCH_MODE, mgmt);
+
+	dev->vlan_enabled = enable;
+	dev->vlan_filtering_enabled = enable_filtering;
 }
 
 static int b53_set_jumbo(struct b53_device *dev, bool enable, bool allow_10_100)
@@ -656,7 +665,7 @@  int b53_configure_vlan(struct dsa_switch *ds)
 		b53_do_vlan_op(dev, VTA_CMD_CLEAR);
 	}
 
-	b53_enable_vlan(dev, false);
+	b53_enable_vlan(dev, false, dev->vlan_filtering_enabled);
 
 	b53_for_each_port(dev, i)
 		b53_write16(dev, B53_VLAN_PAGE,
@@ -1265,6 +1274,46 @@  EXPORT_SYMBOL(b53_phylink_mac_link_up);
 
 int b53_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering)
 {
+	struct b53_device *dev = ds->priv;
+	struct net_device *bridge_dev;
+	unsigned int i;
+	u16 pvid, new_pvid;
+
+	/* Handle the case were multiple bridges span the same switch device
+	 * and one of them has a different setting than what is being requested
+	 * which would be breaking filtering semantics for any of the other
+	 * bridge devices.
+	 */
+	b53_for_each_port(dev, i) {
+		bridge_dev = dsa_to_port(ds, i)->bridge_dev;
+		if (bridge_dev &&
+		    bridge_dev != dsa_to_port(ds, port)->bridge_dev &&
+		    br_vlan_enabled(bridge_dev) != vlan_filtering) {
+			netdev_err(bridge_dev,
+				   "VLAN filtering is global to the switch!\n");
+			return -EINVAL;
+		}
+	}
+
+	b53_read16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port), &pvid);
+	new_pvid = pvid;
+	if (dev->vlan_filtering_enabled && !vlan_filtering) {
+		/* Filtering is currently enabled, use the default PVID since
+		 * the bridge does not expect tagging anymore
+		 */
+		dev->ports[port].pvid = pvid;
+		new_pvid = b53_default_pvid(dev);
+	} else if (!dev->vlan_filtering_enabled && vlan_filtering) {
+		/* Filtering is currently disabled, restore the previous PVID */
+		new_pvid = dev->ports[port].pvid;
+	}
+
+	if (pvid != new_pvid)
+		b53_write16(dev, B53_VLAN_PAGE, B53_VLAN_PORT_DEF_TAG(port),
+			    new_pvid);
+
+	b53_enable_vlan(dev, dev->vlan_enabled, vlan_filtering);
+
 	return 0;
 }
 EXPORT_SYMBOL(b53_vlan_filtering);
@@ -1280,7 +1329,7 @@  int b53_vlan_prepare(struct dsa_switch *ds, int port,
 	if (vlan->vid_end > dev->num_vlans)
 		return -ERANGE;
 
-	b53_enable_vlan(dev, true);
+	b53_enable_vlan(dev, true, dev->vlan_filtering_enabled);
 
 	return 0;
 }
diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h
index ec796482792d..4dc7ee38b258 100644
--- a/drivers/net/dsa/b53/b53_priv.h
+++ b/drivers/net/dsa/b53/b53_priv.h
@@ -91,6 +91,7 @@  enum {
 struct b53_port {
 	u16		vlan_ctl_mask;
 	struct ethtool_eee eee;
+	u16		pvid;
 };
 
 struct b53_vlan {
@@ -137,6 +138,8 @@  struct b53_device {
 
 	unsigned int num_vlans;
 	struct b53_vlan *vlans;
+	bool vlan_enabled;
+	bool vlan_filtering_enabled;
 	unsigned int num_ports;
 	struct b53_port *ports;
 };