On Fri, Mar 19, 2021 at 03:08:46PM -0700, Florian Fainelli wrote: > > > On 3/18/2021 4:18 PM, Vladimir Oltean wrote: > > From: Vladimir Oltean <vladimir.oltean@xxxxxxx> > > > > DSA currently assumes that the bridge port starts off with this > > constellation of bridge port flags: > > > > - learning on > > - unicast flooding on > > - multicast flooding on > > - broadcast flooding on > > > > just by virtue of code copy-pasta from the bridge layer (new_nbp). > > This was a simple enough strategy thus far, because the 'bridge join' > > moment always coincided with the 'bridge port creation' moment. > > > > But with sandwiched interfaces, such as: > > > > br0 > > | > > bond0 > > | > > swp0 > > > > it may happen that the user has had time to change the bridge port flags > > of bond0 before enslaving swp0 to it. In that case, swp0 will falsely > > assume that the bridge port flags are those determined by new_nbp, when > > in fact this can happen: > > > > ip link add br0 type bridge > > ip link add bond0 type bond > > ip link set bond0 master br0 > > ip link set bond0 type bridge_slave learning off > > ip link set swp0 master br0 > > > > Now swp0 has learning enabled, bond0 has learning disabled. Not nice. > > > > Fix this by "dumpster diving" through the actual bridge port flags with > > br_port_flag_is_set, at bridge join time. > > > > We use this opportunity to split dsa_port_change_brport_flags into two > > distinct functions called dsa_port_inherit_brport_flags and > > dsa_port_clear_brport_flags, now that the implementation for the two > > cases is no longer similar. > > > > Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx> > > --- > > net/dsa/port.c | 123 ++++++++++++++++++++++++++++++++----------------- > > 1 file changed, 82 insertions(+), 41 deletions(-) > > > > diff --git a/net/dsa/port.c b/net/dsa/port.c > > index fcbe5b1545b8..346c50467810 100644 > > --- a/net/dsa/port.c > > +++ b/net/dsa/port.c > > @@ -122,26 +122,82 @@ void dsa_port_disable(struct dsa_port *dp) > > rtnl_unlock(); > > } > > > > -static void dsa_port_change_brport_flags(struct dsa_port *dp, > > - bool bridge_offload) > > +static void dsa_port_clear_brport_flags(struct dsa_port *dp, > > + struct netlink_ext_ack *extack) > > { > > struct switchdev_brport_flags flags; > > - int flag; > > > > - flags.mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; > > - if (bridge_offload) > > - flags.val = flags.mask; > > - else > > - flags.val = flags.mask & ~BR_LEARNING; > > + flags.mask = BR_LEARNING; > > + flags.val = 0; > > + dsa_port_bridge_flags(dp, flags, extack); > > Would not you want to use the same for_each_set_bit() loop that > dsa_port_change_br_flags() uses, that would be a tad more compact. > -- > Florian The reworded version has an equal number of lines, but at least it catches errors now: static void dsa_port_clear_brport_flags(struct dsa_port *dp, struct netlink_ext_ack *extack) { const unsigned long val = BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; const unsigned long mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD; int flag, err; for_each_set_bit(flag, &mask, 32) { struct switchdev_brport_flags flags = {0}; flags.mask = BIT(flag); flags.val = val & BIT(flag); err = dsa_port_bridge_flags(dp, flags, extack); if (err && err != -EOPNOTSUPP) dev_err(dp->ds->dev, "failed to clear bridge port flag %d: %d (%pe)\n", flag, err, ERR_PTR(err)); } }