diff mbox series

[net,2/2] net: dsa: Always react to global bridge attribute changes

Message ID 20210306002455.1582593-3-tobias@waldekranz.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: dsa: Avoid VLAN config corruption | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net
netdev/subject_prefix success Link
netdev/cc_maintainers success CCed 8 of 8 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 23 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Tobias Waldekranz March 6, 2021, 12:24 a.m. UTC
This is the second attempt to provide a fix for the issue described in
99b8202b179f, which was reverted in the previous commit.

When a change is made to some global bridge attribute, such as VLAN
filtering, accept events where orig_dev is the bridge master netdev.

Separate the validation of orig_dev based on whether the attribute in
question is global or per-port.

Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
 net/dsa/slave.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

Comments

Vladimir Oltean March 6, 2021, 2 p.m. UTC | #1
Hi Tobias,

On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
> This is the second attempt to provide a fix for the issue described in
> 99b8202b179f, which was reverted in the previous commit.
> 
> When a change is made to some global bridge attribute, such as VLAN
> filtering, accept events where orig_dev is the bridge master netdev.
> 
> Separate the validation of orig_dev based on whether the attribute in
> question is global or per-port.
> 
> Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
> Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
> ---

What do you think about this alternative?

-----------------------------[ cut here ]-----------------------------
>From af528ac6de2b16df4c8ba21bc7d978984883f319 Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Sat, 6 Mar 2021 15:47:01 +0200
Subject: [PATCH] net: dsa: fix switchdev objects on bridge master mistakenly
 being applied on ports

Tobias reports that the blamed patch was too broad, and now, VLAN
objects being added to a bridge:

ip link add br0 type bridge vlan_filtering 1
ip link set swp2 master br0
ip link set swp3 master br0
bridge vlan add dev br0 vid 100 self

are being added to all slave ports instead (swp2, swp3).

This is because the fix was too broad: we made dsa_port_offloads_netdev
say "yes, I offload the br0 bridge" for all slave ports, but we didn't
add the checks whether the switchdev object was in fact meant for the
physical port or for the bridge itself.

Let's keep that definition of dsa_port_offloads_netdev, and just add the
checks for the individual switchdev object types and attributes that can
be notified both on a physical port as well as on the bridge network
interface. This will allow us in the future to do things such as offload
VLANs on the bridge interface by sending them to the CPU port, instead
of the crude "all VLANs on user ports must be added to the CPU port too"
logic that we have now. It will also allow us to properly support the
drivers that don't implement .port_bridge_add and .port_bridge_leave,
because we'll still have an accurate answer to the question
"dsa_port_offloads_netdev".

Fixes: 99b8202b179f ("net: dsa: fix SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING getting ignored")
Reported-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 net/dsa/slave.c | 60 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 42 insertions(+), 18 deletions(-)

diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 491e3761b5f4..6e6384b2d5d2 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -283,6 +283,9 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
 
 	switch (attr->id) {
 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+		if (!dsa_slave_dev_check(dev))
+			return 0;
+
 		ret = dsa_port_set_state(dp, attr->u.stp_state);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
@@ -290,13 +293,22 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
 					      extack);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+		if (!dsa_slave_dev_check(attr->orig_dev))
+			return 0;
+
 		ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
 		break;
 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
+		if (!dsa_slave_dev_check(attr->orig_dev))
+			return 0;
+
 		ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
 						extack);
 		break;
 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		if (!dsa_slave_dev_check(attr->orig_dev))
+			return 0;
+
 		ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
@@ -341,9 +353,6 @@ static int dsa_slave_vlan_add(struct net_device *dev,
 	struct switchdev_obj_port_vlan vlan;
 	int err;
 
-	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-		return -EOPNOTSUPP;
-
 	if (dsa_port_skip_vlan_configuration(dp)) {
 		NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");
 		return 0;
@@ -389,10 +398,14 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
 	struct dsa_port *dp = dsa_slave_to_port(dev);
 	int err;
 
+	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		return -EOPNOTSUPP;
+
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_HOST_MDB:
@@ -402,16 +415,21 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
 		err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_slave_vlan_add(dev, obj, extack);
 		break;
 	case SWITCHDEV_OBJ_ID_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mrp_add_ring_role(dp,
 						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
 		break;
@@ -431,9 +449,6 @@ static int dsa_slave_vlan_del(struct net_device *dev,
 	struct switchdev_obj_port_vlan *vlan;
 	int err;
 
-	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-		return -EOPNOTSUPP;
-
 	if (dsa_port_skip_vlan_configuration(dp))
 		return 0;
 
@@ -457,10 +472,14 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 	struct dsa_port *dp = dsa_slave_to_port(dev);
 	int err;
 
+	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		return -EOPNOTSUPP;
+
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_HOST_MDB:
@@ -470,16 +489,21 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 		err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_slave_vlan_del(dev, obj);
 		break;
 	case SWITCHDEV_OBJ_ID_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-			return -EOPNOTSUPP;
+		if (!dsa_slave_dev_check(obj->orig_dev))
+			return 0;
+
 		err = dsa_port_mrp_del_ring_role(dp,
 						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
 		break;
-----------------------------[ cut here ]-----------------------------
Vladimir Oltean March 6, 2021, 2:04 p.m. UTC | #2
On Sat, Mar 06, 2021 at 04:00:33PM +0200, Vladimir Oltean wrote:
> Hi Tobias,
>
> On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
> > This is the second attempt to provide a fix for the issue described in
> > 99b8202b179f, which was reverted in the previous commit.
> >
> > When a change is made to some global bridge attribute, such as VLAN
> > filtering, accept events where orig_dev is the bridge master netdev.
> >
> > Separate the validation of orig_dev based on whether the attribute in
> > question is global or per-port.
> >
> > Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
> > Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
> > ---
>
> What do you think about this alternative?

Ah, wait, this won't work when offloading objects/attributes on a LAG.
Let me actually test your patch.
Tobias Waldekranz March 6, 2021, 6:17 p.m. UTC | #3
On Sat, Mar 06, 2021 at 16:04, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Sat, Mar 06, 2021 at 04:00:33PM +0200, Vladimir Oltean wrote:
>> Hi Tobias,
>>
>> On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
>> > This is the second attempt to provide a fix for the issue described in
>> > 99b8202b179f, which was reverted in the previous commit.
>> >
>> > When a change is made to some global bridge attribute, such as VLAN
>> > filtering, accept events where orig_dev is the bridge master netdev.
>> >
>> > Separate the validation of orig_dev based on whether the attribute in
>> > question is global or per-port.
>> >
>> > Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
>> > Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
>> > ---
>>
>> What do you think about this alternative?
>
> Ah, wait, this won't work when offloading objects/attributes on a LAG.
> Let me actually test your patch.

Right. But you made me realize that my v1 is also flawed, because it
does not guard against trying to apply attributes to non-offloaded
ports. ...the original issue :facepalm:

I have a version ready which reuses the exact predicate that you
previously added to dsa_port_offloads_netdev:

-               if (netif_is_bridge_master(attr->orig_dev))
+               if (dp->bridge_dev == attr->orig_dev)

Do you think anything else needs to be changed, or should I send that as
v2?
Vladimir Oltean March 7, 2021, 12:58 a.m. UTC | #4
On Sat, Mar 06, 2021 at 07:17:09PM +0100, Tobias Waldekranz wrote:
> On Sat, Mar 06, 2021 at 16:04, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Sat, Mar 06, 2021 at 04:00:33PM +0200, Vladimir Oltean wrote:
> >> Hi Tobias,
> >>
> >> On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
> >> > This is the second attempt to provide a fix for the issue described in
> >> > 99b8202b179f, which was reverted in the previous commit.
> >> >
> >> > When a change is made to some global bridge attribute, such as VLAN
> >> > filtering, accept events where orig_dev is the bridge master netdev.
> >> >
> >> > Separate the validation of orig_dev based on whether the attribute in
> >> > question is global or per-port.
> >> >
> >> > Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
> >> > Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
> >> > ---
> >>
> >> What do you think about this alternative?
> >
> > Ah, wait, this won't work when offloading objects/attributes on a LAG.
> > Let me actually test your patch.
> 
> Right. But you made me realize that my v1 is also flawed, because it
> does not guard against trying to apply attributes to non-offloaded
> ports. ...the original issue :facepalm:
> 
> I have a version ready which reuses the exact predicate that you
> previously added to dsa_port_offloads_netdev:
> 
> -               if (netif_is_bridge_master(attr->orig_dev))
> +               if (dp->bridge_dev == attr->orig_dev)
> 
> Do you think anything else needs to be changed, or should I send that as
> v2?

Sorry, I just get a blank stare when I look at that blob of code you've
added at the beginning of dsa_slave_port_attr_set, it might as well be
correct but I'm not smart enough to process it and say "yes it is".

What do you think about this one? At least for me it's easier to
understand what's going on, and would leave a lot more room for further
fixups if needed.

-----------------------------[ cut here ]-----------------------------
>From 1f1d463788ace3434878f16aa2f083db2b007990 Mon Sep 17 00:00:00 2001
From: Vladimir Oltean <vladimir.oltean@nxp.com>
Date: Sat, 6 Mar 2021 01:24:54 +0100
Subject: [PATCH] net: dsa: fix switchdev objects on bridge master mistakenly
 being applied on ports

Tobias reports that the blamed patch was too broad, and now, VLAN
objects being added to a bridge:

ip link add br0 type bridge vlan_filtering 1
ip link set swp2 master br0
ip link set swp3 master br0
bridge vlan add dev br0 vid 100 self

are being added to all slave ports instead (swp2, swp3).

This is because the fix was too broad: we made dsa_port_offloads_netdev
say "yes, I offload the br0 bridge" for all slave ports, but we didn't
add the checks whether the switchdev object was in fact meant for the
physical port or for the bridge itself.

The reason why the fix was too broad is because the question itself,
"does this DSA port offload this netdev", was too broad in the first
place. The solution is to disambiguate the question and separate it into
two different functions, one to be called for each switchdev attribute /
object that has an orig_dev == net_bridge (dsa_port_offloads_bridge),
and the other for orig_dev == net_bridge_port (*_offloads_bridge_port).

Fixes: 99b8202b179f ("net: dsa: fix SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING getting ignored")
Reported-by: Tobias Waldekranz <tobias@waldekranz.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
 net/dsa/dsa_priv.h | 25 +++++++++++---------
 net/dsa/slave.c    | 59 +++++++++++++++++++++++++++++++++-------------
 2 files changed, 57 insertions(+), 27 deletions(-)

diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 2eeaa42f2e08..9d4b0e9b1aa1 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -230,8 +230,8 @@ int dsa_port_hsr_join(struct dsa_port *dp, struct net_device *hsr);
 void dsa_port_hsr_leave(struct dsa_port *dp, struct net_device *hsr);
 extern const struct phylink_mac_ops dsa_port_phylink_mac_ops;
 
-static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
-					    struct net_device *dev)
+static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp,
+						 struct net_device *dev)
 {
 	/* Switchdev offloading can be configured on: */
 
@@ -241,12 +241,6 @@ static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
 		 */
 		return true;
 
-	if (dp->bridge_dev == dev)
-		/* DSA ports connected to a bridge, and event was emitted
-		 * for the bridge.
-		 */
-		return true;
-
 	if (dp->lag_dev == dev)
 		/* DSA ports connected to a bridge via a LAG */
 		return true;
@@ -254,14 +248,23 @@ static inline bool dsa_port_offloads_netdev(struct dsa_port *dp,
 	return false;
 }
 
+static inline bool dsa_port_offloads_bridge(struct dsa_port *dp,
+					    struct net_device *bridge_dev)
+{
+	/* DSA ports connected to a bridge, and event was emitted
+	 * for the bridge.
+	 */
+	return dp->bridge_dev == bridge_dev;
+}
+
 /* Returns true if any port of this tree offloads the given net_device */
-static inline bool dsa_tree_offloads_netdev(struct dsa_switch_tree *dst,
-					    struct net_device *dev)
+static inline bool dsa_tree_offloads_bridge_port(struct dsa_switch_tree *dst,
+						 struct net_device *dev)
 {
 	struct dsa_port *dp;
 
 	list_for_each_entry(dp, &dst->ports, list)
-		if (dsa_port_offloads_netdev(dp, dev))
+		if (dsa_port_offloads_bridge_port(dp, dev))
 			return true;
 
 	return false;
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 491e3761b5f4..992fcab4b552 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -278,28 +278,43 @@ static int dsa_slave_port_attr_set(struct net_device *dev,
 	struct dsa_port *dp = dsa_slave_to_port(dev);
 	int ret;
 
-	if (!dsa_port_offloads_netdev(dp, attr->orig_dev))
-		return -EOPNOTSUPP;
-
 	switch (attr->id) {
 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
+		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_set_state(dp, attr->u.stp_state);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+		if (!dsa_port_offloads_bridge(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_vlan_filtering(dp, attr->u.vlan_filtering,
 					      extack);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+		if (!dsa_port_offloads_bridge(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_ageing_time(dp, attr->u.ageing_time);
 		break;
 	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
+		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
 						extack);
 		break;
 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+		if (!dsa_port_offloads_bridge_port(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, extack);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
+		if (!dsa_port_offloads_bridge(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+
 		ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, extack);
 		break;
 	default:
@@ -341,9 +356,6 @@ static int dsa_slave_vlan_add(struct net_device *dev,
 	struct switchdev_obj_port_vlan vlan;
 	int err;
 
-	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-		return -EOPNOTSUPP;
-
 	if (dsa_port_skip_vlan_configuration(dp)) {
 		NL_SET_ERR_MSG_MOD(extack, "skipping configuration of VLAN");
 		return 0;
@@ -391,27 +403,36 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
 
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_HOST_MDB:
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
+			return -EOPNOTSUPP;
+
 		/* DSA can directly translate this to a normal MDB add,
 		 * but on the CPU port.
 		 */
 		err = dsa_port_mdb_add(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
+			return -EOPNOTSUPP;
+
 		err = dsa_slave_vlan_add(dev, obj, extack);
 		break;
 	case SWITCHDEV_OBJ_ID_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mrp_add(dp, SWITCHDEV_OBJ_MRP(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mrp_add_ring_role(dp,
 						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
 		break;
@@ -431,9 +452,6 @@ static int dsa_slave_vlan_del(struct net_device *dev,
 	struct switchdev_obj_port_vlan *vlan;
 	int err;
 
-	if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
-		return -EOPNOTSUPP;
-
 	if (dsa_port_skip_vlan_configuration(dp))
 		return 0;
 
@@ -459,27 +477,36 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
 
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_HOST_MDB:
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
+			return -EOPNOTSUPP;
+
 		/* DSA can directly translate this to a normal MDB add,
 		 * but on the CPU port.
 		 */
 		err = dsa_port_mdb_del(dp->cpu_dp, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
+		if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
+			return -EOPNOTSUPP;
+
 		err = dsa_slave_vlan_del(dev, obj);
 		break;
 	case SWITCHDEV_OBJ_ID_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mrp_del(dp, SWITCHDEV_OBJ_MRP(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_RING_ROLE_MRP:
-		if (!dsa_port_offloads_netdev(dp, obj->orig_dev))
+		if (!dsa_port_offloads_bridge(dp, obj->orig_dev))
 			return -EOPNOTSUPP;
+
 		err = dsa_port_mrp_del_ring_role(dp,
 						 SWITCHDEV_OBJ_RING_ROLE_MRP(obj));
 		break;
@@ -2298,7 +2325,7 @@ static int dsa_slave_switchdev_event(struct notifier_block *unused,
 			 * other ports bridged with the LAG should be able to
 			 * autonomously forward towards it.
 			 */
-			if (dsa_tree_offloads_netdev(dp->ds->dst, dev))
+			if (dsa_tree_offloads_bridge_port(dp->ds->dst, dev))
 				return NOTIFY_DONE;
 		}
 
-----------------------------[ cut here ]-----------------------------
Tobias Waldekranz March 7, 2021, 9:51 a.m. UTC | #5
On Sun, Mar 07, 2021 at 02:58, Vladimir Oltean <olteanv@gmail.com> wrote:
> On Sat, Mar 06, 2021 at 07:17:09PM +0100, Tobias Waldekranz wrote:
>> On Sat, Mar 06, 2021 at 16:04, Vladimir Oltean <olteanv@gmail.com> wrote:
>> > On Sat, Mar 06, 2021 at 04:00:33PM +0200, Vladimir Oltean wrote:
>> >> Hi Tobias,
>> >>
>> >> On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
>> >> > This is the second attempt to provide a fix for the issue described in
>> >> > 99b8202b179f, which was reverted in the previous commit.
>> >> >
>> >> > When a change is made to some global bridge attribute, such as VLAN
>> >> > filtering, accept events where orig_dev is the bridge master netdev.
>> >> >
>> >> > Separate the validation of orig_dev based on whether the attribute in
>> >> > question is global or per-port.
>> >> >
>> >> > Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
>> >> > Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
>> >> > ---
>> >>
>> >> What do you think about this alternative?
>> >
>> > Ah, wait, this won't work when offloading objects/attributes on a LAG.
>> > Let me actually test your patch.
>> 
>> Right. But you made me realize that my v1 is also flawed, because it
>> does not guard against trying to apply attributes to non-offloaded
>> ports. ...the original issue :facepalm:
>> 
>> I have a version ready which reuses the exact predicate that you
>> previously added to dsa_port_offloads_netdev:
>> 
>> -               if (netif_is_bridge_master(attr->orig_dev))
>> +               if (dp->bridge_dev == attr->orig_dev)
>> 
>> Do you think anything else needs to be changed, or should I send that as
>> v2?
>
> Sorry, I just get a blank stare when I look at that blob of code you've
> added at the beginning of dsa_slave_port_attr_set, it might as well be
> correct but I'm not smart enough to process it and say "yes it is".
>
> What do you think about this one? At least for me it's easier to
> understand what's going on, and would leave a lot more room for further
> fixups if needed.

I like the approach of having to explicitly state the supported orig_dev
per attribute or object. I think we should go with your fix.
Vladimir Oltean March 7, 2021, 10:07 a.m. UTC | #6
On Sun, Mar 07, 2021 at 10:51:18AM +0100, Tobias Waldekranz wrote:
> On Sun, Mar 07, 2021 at 02:58, Vladimir Oltean <olteanv@gmail.com> wrote:
> > On Sat, Mar 06, 2021 at 07:17:09PM +0100, Tobias Waldekranz wrote:
> >> On Sat, Mar 06, 2021 at 16:04, Vladimir Oltean <olteanv@gmail.com> wrote:
> >> > On Sat, Mar 06, 2021 at 04:00:33PM +0200, Vladimir Oltean wrote:
> >> >> Hi Tobias,
> >> >>
> >> >> On Sat, Mar 06, 2021 at 01:24:55AM +0100, Tobias Waldekranz wrote:
> >> >> > This is the second attempt to provide a fix for the issue described in
> >> >> > 99b8202b179f, which was reverted in the previous commit.
> >> >> >
> >> >> > When a change is made to some global bridge attribute, such as VLAN
> >> >> > filtering, accept events where orig_dev is the bridge master netdev.
> >> >> >
> >> >> > Separate the validation of orig_dev based on whether the attribute in
> >> >> > question is global or per-port.
> >> >> >
> >> >> > Fixes: 5696c8aedfcc ("net: dsa: Don't offload port attributes on standalone ports")
> >> >> > Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
> >> >> > ---
> >> >>
> >> >> What do you think about this alternative?
> >> >
> >> > Ah, wait, this won't work when offloading objects/attributes on a LAG.
> >> > Let me actually test your patch.
> >>
> >> Right. But you made me realize that my v1 is also flawed, because it
> >> does not guard against trying to apply attributes to non-offloaded
> >> ports. ...the original issue :facepalm:
> >>
> >> I have a version ready which reuses the exact predicate that you
> >> previously added to dsa_port_offloads_netdev:
> >>
> >> -               if (netif_is_bridge_master(attr->orig_dev))
> >> +               if (dp->bridge_dev == attr->orig_dev)
> >>
> >> Do you think anything else needs to be changed, or should I send that as
> >> v2?
> >
> > Sorry, I just get a blank stare when I look at that blob of code you've
> > added at the beginning of dsa_slave_port_attr_set, it might as well be
> > correct but I'm not smart enough to process it and say "yes it is".
> >
> > What do you think about this one? At least for me it's easier to
> > understand what's going on, and would leave a lot more room for further
> > fixups if needed.
>
> I like the approach of having to explicitly state the supported orig_dev
> per attribute or object. I think we should go with your fix.

Ok, I'm sending it as-is. Thanks again!
diff mbox series

Patch

diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 491e3761b5f4..63ee2cae4d8e 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -278,8 +278,21 @@  static int dsa_slave_port_attr_set(struct net_device *dev,
 	struct dsa_port *dp = dsa_slave_to_port(dev);
 	int ret;
 
-	if (!dsa_port_offloads_netdev(dp, attr->orig_dev))
-		return -EOPNOTSUPP;
+	switch (attr->id) {
+	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
+	case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
+	case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
+		/* For global bridge settings, the originating device
+		 * may be the bridge itself.
+		 */
+		if (netif_is_bridge_master(attr->orig_dev))
+			break;
+
+		fallthrough;
+	default:
+		if (!dsa_port_offloads_netdev(dp, attr->orig_dev))
+			return -EOPNOTSUPP;
+	}
 
 	switch (attr->id) {
 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE: