From: Vladimir Oltean <[email protected]>
The initial goal of this series was to have better support for
standalone ports mode and multiple bridges on the Ocelot/Felix DSA
driver. Proper support for standalone mode requires disabling address
learning, which in turn requires interaction with the switchdev notifier,
which is actually where most of the patches are.
Vladimir Oltean (9):
net: bridge: don't print in br_switchdev_set_port_flag
net: bridge: offload initial and final port flags through switchdev
net: dsa: stop setting initial and final brport flags
net: dsa: kill .port_egress_floods overengineering
net: squash switchdev attributes PRE_BRIDGE_FLAGS and BRIDGE_FLAGS
net: bridge: stop treating EOPNOTSUPP as special in
br_switchdev_set_port_flag
net: mscc: ocelot: use separate flooding PGID for broadcast
net: mscc: ocelot: offload bridge port flags to device
net: mscc: ocelot: support multiple bridges
drivers/net/dsa/b53/b53_common.c | 18 ++-
drivers/net/dsa/mv88e6xxx/chip.c | 19 ++-
drivers/net/dsa/ocelot/felix.c | 9 ++
.../marvell/prestera/prestera_switchdev.c | 16 +--
.../mellanox/mlxsw/spectrum_switchdev.c | 28 ++--
drivers/net/ethernet/mscc/ocelot.c | 135 +++++++++++++-----
drivers/net/ethernet/mscc/ocelot_net.c | 4 +
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 2 +-
drivers/net/ethernet/rocker/rocker_main.c | 24 +---
drivers/net/ethernet/ti/cpsw_switchdev.c | 20 +--
drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 22 +--
include/net/dsa.h | 6 +-
include/net/switchdev.h | 8 +-
include/soc/mscc/ocelot.h | 26 ++--
net/bridge/br_if.c | 24 +++-
net/bridge/br_netlink.c | 67 +++++----
net/bridge/br_private.h | 8 +-
net/bridge/br_switchdev.c | 35 ++---
net/dsa/dsa_priv.h | 4 +-
net/dsa/port.c | 40 ++----
net/dsa/slave.c | 3 -
21 files changed, 285 insertions(+), 233 deletions(-)
--
2.25.1
From: Vladimir Oltean <[email protected]>
Currently br_switchdev_set_port_flag has two options for error handling
and neither is good:
- The driver returns -EOPNOTSUPP in PRE_BRIDGE_FLAGS if it doesn't
support offloading that flag, and this gets silently ignored and
converted to an errno of 0. Nobody does this.
- The driver returns some other error code, like -EINVAL, in
PRE_BRIDGE_FLAGS, and br_switchdev_set_port_flag shouts loudly.
The problem is that we'd like to offload some port flags during bridge
join and leave, but also not have the bridge shout at us if those fail.
But on the other hand we'd like the user to know that we can't offload
something when they set that through netlink. And since we can't have
the driver return -EOPNOTSUPP or -EINVAL depending on whether it's
called by the user or internally by the bridge, let's just add an extack
argument to br_switchdev_set_port_flag and propagate it to its callers.
Then, when we need offloading to really fail silently, this can simply
be passed a NULL argument.
This is just a temporary patch because it's rather noisy. We'll
propagate the extack through the switchdev notifier next.
Signed-off-by: Vladimir Oltean <[email protected]>
---
net/bridge/br_netlink.c | 53 +++++++++++++++++++++++++--------------
net/bridge/br_private.h | 6 +++--
net/bridge/br_switchdev.c | 9 +++----
3 files changed, 42 insertions(+), 26 deletions(-)
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index bd3962da345a..02aa95c08b77 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -854,7 +854,8 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
/* Set/clear or port flags based on attribute */
static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
- int attrtype, unsigned long mask)
+ int attrtype, unsigned long mask,
+ struct netlink_ext_ack *extack)
{
unsigned long flags;
int err;
@@ -867,7 +868,7 @@ static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
else
flags = p->flags & ~mask;
- err = br_switchdev_set_port_flag(p, flags, mask);
+ err = br_switchdev_set_port_flag(p, flags, mask, extack);
if (err)
return err;
@@ -876,58 +877,71 @@ static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
}
/* Process bridge protocol info on port */
-static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
+static int br_setport(struct net_bridge_port *p, struct nlattr *tb[],
+ struct netlink_ext_ack *extack)
{
unsigned long old_flags = p->flags;
bool br_vlan_tunnel_old = false;
int err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE,
+ BR_MULTICAST_FAST_LEAVE, extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_PROTECT, BR_ROOT_BLOCK,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_LEARNING, BR_LEARNING,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_UNICAST_FLOOD, BR_FLOOD,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_FLOOD, BR_MCAST_FLOOD,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST, BR_MULTICAST_TO_UNICAST);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_MCAST_TO_UCAST,
+ BR_MULTICAST_TO_UNICAST, extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD, BR_BCAST_FLOOD);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_BCAST_FLOOD,
+ BR_BCAST_FLOOD, extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP, BR_PROXYARP,
+ extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI, BR_PROXYARP_WIFI);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_PROXYARP_WIFI,
+ BR_PROXYARP_WIFI, extack);
if (err)
return err;
br_vlan_tunnel_old = (p->flags & BR_VLAN_TUNNEL) ? true : false;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_VLAN_TUNNEL, BR_VLAN_TUNNEL,
+ extack);
if (err)
return err;
@@ -983,11 +997,12 @@ static int br_setport(struct net_bridge_port *p, struct nlattr *tb[])
}
err = br_set_port_flag(p, tb, IFLA_BRPORT_NEIGH_SUPPRESS,
- BR_NEIGH_SUPPRESS);
+ BR_NEIGH_SUPPRESS, extack);
if (err)
return err;
- err = br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED);
+ err = br_set_port_flag(p, tb, IFLA_BRPORT_ISOLATED, BR_ISOLATED,
+ extack);
if (err)
return err;
@@ -1046,7 +1061,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh, u16 flags,
return err;
spin_lock_bh(&p->br->lock);
- err = br_setport(p, tb);
+ err = br_setport(p, tb, extack);
spin_unlock_bh(&p->br->lock);
} else {
/* Binary compatibility with old RSTP */
@@ -1141,7 +1156,7 @@ static int br_port_slave_changelink(struct net_device *brdev,
return 0;
spin_lock_bh(&br->lock);
- ret = br_setport(br_port_get_rtnl(dev), data);
+ ret = br_setport(br_port_get_rtnl(dev), data, extack);
spin_unlock_bh(&br->lock);
return ret;
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index d242ba668e47..a1639d41188b 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -1575,7 +1575,8 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
const struct sk_buff *skb);
int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long flags,
- unsigned long mask);
+ unsigned long mask,
+ struct netlink_ext_ack *extack);
void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb,
int type);
int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags,
@@ -1605,7 +1606,8 @@ static inline bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
static inline int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long flags,
- unsigned long mask)
+ unsigned long mask,
+ struct netlink_ext_ack *extack)
{
return 0;
}
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index a9c23ef83443..c18e1d600dc6 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -60,7 +60,8 @@ bool nbp_switchdev_allowed_egress(const struct net_bridge_port *p,
int br_switchdev_set_port_flag(struct net_bridge_port *p,
unsigned long flags,
- unsigned long mask)
+ unsigned long mask,
+ struct netlink_ext_ack *extack)
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
@@ -83,8 +84,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
return 0;
if (err) {
- br_warn(p->br, "bridge flag offload is not supported %u(%s)\n",
- (unsigned int)p->port_no, p->dev->name);
+ NL_SET_ERR_MSG_MOD(extack, "bridge flag offload is not supported");
return -EOPNOTSUPP;
}
@@ -94,8 +94,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
err = switchdev_port_attr_set(p->dev, &attr);
if (err) {
- br_warn(p->br, "error setting offload flag on port %u(%s)\n",
- (unsigned int)p->port_no, p->dev->name);
+ NL_SET_ERR_MSG_MOD(extack, "error setting offload flag on port");
return err;
}
--
2.25.1
From: Vladimir Oltean <[email protected]>
It must first be admitted that switchdev device drivers have a life
beyond the bridge, and when they aren't offloading the bridge driver
they are operating with forwarding disabled between ports, emulating as
closely as possible N standalone network interfaces.
Now it must be said that for a switchdev port operating in standalone
mode, address learning doesn't make much sense since that is a bridge
function. In fact, address learning even breaks setups such as this one:
+---------------------------------------------+
| |
| +-------------------+ |
| | br0 | send receive |
| +--------+-+--------+ +--------+ +--------+ |
| | | | | | | | | |
| | swp0 | | swp1 | | swp2 | | swp3 | |
| | | | | | | | | |
+-+--------+-+--------+-+--------+-+--------+-+
| ^ | ^
| | | |
| +-----------+ |
| |
+--------------------------------+
because if the ASIC has a single FDB (can offload a single bridge)
then source address learning on swp3 can "steal" the source MAC address
of swp2 from br0's FDB, because learning frames coming from swp2 will be
done twice: first on the swp1 ingress port, second on the swp3 ingress
port. So the hardware FDB will become out of sync with the software
bridge, and when swp2 tries to send one more packet towards swp1, the
ASIC will attempt to short-circuit the forwarding path and send it
directly to swp3 (since that's the last port it learned that address on),
which it obviously can't, because swp3 operates in standalone mode.
So switchdev drivers operating in standalone mode should disable address
learning. As a matter of practicality, we can reduce code duplication in
drivers by having the bridge notify through switchdev of the initial and
final brport flags. Then, drivers can simply start up hardcoded for no
address learning (similar to how they already start up hardcoded for no
forwarding), then they only need to listen for
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS and their job is basically done, no
need for special cases when the port joins or leaves the bridge etc.
When a port leaves the bridge (and therefore becomes standalone), we
issue a switchdev attribute that apart from disabling address learning,
enables flooding of all kinds. This is also done for pragmatic reasons,
because even though standalone switchdev ports might not need to have
flooding enabled in order to inject traffic with any MAC DA from the
control interface, it certainly doesn't hurt either, and it even makes
more sense than disabling flooding of unknown traffic towards that port.
Note that the implementation is a bit wacky because the switchdev API
for port attributes is very counterproductive. Instead of issuing a
single switchdev notification with a bitwise OR of all flags that we're
modifying, we need to issue 4 individual notifications, one for each bit.
This is because the SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS notifier
forces you to refuse the entire operation if there's at least one bit
which you can't offload, and that is currently BR_BCAST_FLOOD which
nobody does. So this change would do nothing for no one if we offloaded
all flags at once, but the idea is to offload as much as possible
instead of all or nothing.
Signed-off-by: Vladimir Oltean <[email protected]>
---
net/bridge/br_if.c | 24 +++++++++++++++++++++++-
net/bridge/br_netlink.c | 16 ++++------------
net/bridge/br_private.h | 2 ++
3 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index f7d2f472ae24..8903333654f0 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -89,6 +89,21 @@ void br_port_carrier_check(struct net_bridge_port *p, bool *notified)
spin_unlock_bh(&br->lock);
}
+int nbp_flags_change(struct net_bridge_port *p, unsigned long flags,
+ unsigned long mask, struct netlink_ext_ack *extack)
+{
+ int err;
+
+ err = br_switchdev_set_port_flag(p, flags, mask, extack);
+ if (err)
+ return err;
+
+ p->flags &= ~mask;
+ p->flags |= flags;
+
+ return 0;
+}
+
static void br_port_set_promisc(struct net_bridge_port *p)
{
int err = 0;
@@ -343,6 +358,10 @@ static void del_nbp(struct net_bridge_port *p)
update_headroom(br, get_max_headroom(br));
netdev_reset_rx_headroom(dev);
+ nbp_flags_change(p, 0, BR_LEARNING, NULL);
+ nbp_flags_change(p, BR_FLOOD, BR_FLOOD, NULL);
+ nbp_flags_change(p, BR_MCAST_FLOOD, BR_MCAST_FLOOD, NULL);
+ nbp_flags_change(p, BR_BCAST_FLOOD, BR_BCAST_FLOOD, NULL);
nbp_vlan_flush(p);
br_fdb_delete_by_port(br, p, 0, 1);
switchdev_deferred_process();
@@ -428,7 +447,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
p->path_cost = port_cost(dev);
p->priority = 0x8000 >> BR_PORT_BITS;
p->port_no = index;
- p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
+ nbp_flags_change(p, BR_LEARNING, BR_LEARNING, NULL);
+ nbp_flags_change(p, BR_FLOOD, BR_FLOOD, NULL);
+ nbp_flags_change(p, BR_MCAST_FLOOD, BR_MCAST_FLOOD, NULL);
+ nbp_flags_change(p, BR_BCAST_FLOOD, BR_BCAST_FLOOD, NULL);
br_init_port(p);
br_set_state(p, BR_STATE_DISABLED);
br_stp_port_timer_init(p);
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
index 02aa95c08b77..ab54d1daa9b4 100644
--- a/net/bridge/br_netlink.c
+++ b/net/bridge/br_netlink.c
@@ -852,28 +852,20 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
return 0;
}
-/* Set/clear or port flags based on attribute */
+/* Set/clear or port flags based on netlink attribute */
static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
int attrtype, unsigned long mask,
struct netlink_ext_ack *extack)
{
- unsigned long flags;
- int err;
+ unsigned long flags = 0;
if (!tb[attrtype])
return 0;
if (nla_get_u8(tb[attrtype]))
- flags = p->flags | mask;
- else
- flags = p->flags & ~mask;
-
- err = br_switchdev_set_port_flag(p, flags, mask, extack);
- if (err)
- return err;
+ flags = mask;
- p->flags = flags;
- return 0;
+ return nbp_flags_change(p, flags, mask, extack);
}
/* Process bridge protocol info on port */
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index a1639d41188b..f064abd86bdf 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -749,6 +749,8 @@ netdev_features_t br_features_recompute(struct net_bridge *br,
void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
void br_manage_promisc(struct net_bridge *br);
int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev);
+int nbp_flags_change(struct net_bridge_port *p, unsigned long flags,
+ unsigned long mask, struct netlink_ext_ack *extack);
/* br_input.c */
int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
--
2.25.1
From: Vladimir Oltean <[email protected]>
The bridge offloads the port flags through a single bit mask using
switchdev, which among others, contains learning and flooding settings.
The commit 57652796aa97 ("net: dsa: add support for bridge flags")
missed one crucial aspect of the SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS API
when designing the API one level lower, towards the drivers.
This is that the bitmask of passed brport flags never has more than one
bit set at a time. On the other hand, the prototype passed to the driver
is .port_egress_floods(int port, bool unicast, bool multicast), which
configures two flags at a time.
If the bridge wants to see what flags are supported by the device, it
emits a SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS attribute. DSA currently
checks if .port_egress_floods is implemented, and if it is, reports both
BR_FLOOD and BR_MCAST_FLOOD. So the driver has no choice if it wants to
inform the bridge that it can configure unicast flooding but not
multicast flooding - the DSA mid layer is standing in the way.
Secondly, as outlined in the first paragraph, flooding is not all that
is passed through the brport flags bitmask, but also learning. If a DSA
switch wanted to configure its learning parameter in spirit of the
current API, new logic would need to be added to DSA for
.port_set_learning. This implies that dsa_port_pre_bridge_flags would
have to deduce, based on which of .port_set_learning and
.port_egress_floods are implemented, what to mark as supported, and then
figure out which of .port_set_learning or .port_egress_floods to call
from dsa_port_bridge_flags. The latter is non-trivial because, as
mentioned, all brport flags finally end up offloaded in the same bitmask,
and ideally we wouldn't want DSA to call an unneeded method, i.e.
.port_set_learning when nothing learning-related has changed, or
.port_egress_floods when nothing flooding-related has changed,
especially when the reconfiguration for that might involve a switch reset.
So to avoid that, DSA's only option would be to perform 'edge detection'
by keeping an unsigned long dsa_port::brport_flags and check for the XOR
between the cached value and the new flags in order to determine what
changed. Either that, or make the drivers check early if anything has
changed, and return if not. But either way, this is fairly overengineered.
Thirdly, currently DSA deems the driver too dumb to deserve knowing that
a SWITCHDEV_ATTR_ID_BRIDGE_MROUTER attribute was offloaded, because it
just calls .port_egress_floods for the CPU port. When we'll add support
for the plain SWITCHDEV_ATTR_ID_PORT_MROUTER, that will become a real
problem because the flood settings will need to be held statefully in
the DSA middle layer, otherwise changing the mrouter port attribute will
impact the flooding attribute. And that's _assuming_ that the underlying
hardware doesn't have anything else to do when a multicast router
attaches to a port than flood unknown traffic to it. If it does, there
will need to be a dedicated .port_set_mrouter anyway.
Lastly, we have DSA drivers that have a backlink into a pure switchdev
driver (felix -> ocelot). It seems reasonable that the other switchdev
drivers should not have to suffer from the oddities of DSA overengineering,
so keeping DSA a pass-through layer makes more sense there.
To simplify the brport flags situation we just delete .port_egress_floods
and we introduce .port_pre_bridge_flags and .port_bridge_flags which are
passed to the driver. Also, the logic from dsa_port_mrouter is removed
and a .port_set_mrouter is created. The .port_pre_bridge_flags callback
is temporary and will be removed in a later patch, together with
SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS.
Functionally speaking, we simply move the calls to .port_egress_floods
one step lower, in the two drivers that implement it: mv88e6xxx and b53,
so things should work just as before.
Signed-off-by: Vladimir Oltean <[email protected]>
---
drivers/net/dsa/b53/b53_common.c | 24 +++++++++++++++++++++++-
drivers/net/dsa/mv88e6xxx/chip.c | 26 +++++++++++++++++++++++++-
include/net/dsa.h | 8 ++++++--
net/dsa/dsa_priv.h | 2 +-
net/dsa/port.c | 21 ++++++++-------------
5 files changed, 63 insertions(+), 18 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 23fc7225c8d1..0c4000783b15 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1948,6 +1948,26 @@ int b53_br_egress_floods(struct dsa_switch *ds, int port,
}
EXPORT_SYMBOL(b53_br_egress_floods);
+static int b53_pre_br_flags(struct dsa_switch *ds, int port,
+ unsigned long mask)
+{
+ if (mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int b53_br_flags(struct dsa_switch *ds, int port, unsigned long flags)
+{
+ return b53_br_egress_floods(ds, port, flags & BR_FLOOD,
+ flags & BR_MCAST_FLOOD);
+}
+
+static int b53_set_mrouter(struct dsa_switch *ds, int port, bool mrouter)
+{
+ return b53_br_egress_floods(ds, port, true, mrouter);
+}
+
static bool b53_possible_cpu_port(struct dsa_switch *ds, int port)
{
/* Broadcom switches will accept enabling Broadcom tags on the
@@ -2187,9 +2207,11 @@ static const struct dsa_switch_ops b53_switch_ops = {
.set_mac_eee = b53_set_mac_eee,
.port_bridge_join = b53_br_join,
.port_bridge_leave = b53_br_leave,
+ .port_bridge_flags = b53_br_flags,
+ .port_pre_bridge_flags = b53_pre_br_flags,
+ .port_set_mrouter = b53_set_mrouter,
.port_stp_state_set = b53_br_set_stp_state,
.port_fast_age = b53_br_fast_age,
- .port_egress_floods = b53_br_egress_floods,
.port_vlan_filtering = b53_vlan_filtering,
.port_vlan_add = b53_vlan_add,
.port_vlan_del = b53_vlan_del,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index ae0b490f00cd..bd986244aa27 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -5380,6 +5380,28 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
return err;
}
+static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+ unsigned long mask)
+{
+ if (mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
+ return -EINVAL;
+
+ return 0;
+}
+
+static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
+ unsigned long flags)
+{
+ return mv88e6xxx_port_egress_floods(ds, port, flags & BR_FLOOD,
+ flags & BR_MCAST_FLOOD);
+}
+
+static int mv88e6xxx_port_set_mrouter(struct dsa_switch *ds, int port,
+ bool mrouter)
+{
+ return mv88e6xxx_port_egress_floods(ds, port, true, mrouter);
+}
+
static bool mv88e6xxx_lag_can_offload(struct dsa_switch *ds,
struct net_device *lag,
struct netdev_lag_upper_info *info)
@@ -5678,7 +5700,9 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.set_ageing_time = mv88e6xxx_set_ageing_time,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_egress_floods = mv88e6xxx_port_egress_floods,
+ .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags,
+ .port_bridge_flags = mv88e6xxx_port_bridge_flags,
+ .port_set_mrouter = mv88e6xxx_port_set_mrouter,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
.port_fast_age = mv88e6xxx_port_fast_age,
.port_vlan_filtering = mv88e6xxx_port_vlan_filtering,
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 60acb9fca124..e37fee22caab 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -621,8 +621,12 @@ 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_egress_floods)(struct dsa_switch *ds, int port,
- bool unicast, bool multicast);
+ int (*port_pre_bridge_flags)(struct dsa_switch *ds, int port,
+ unsigned long mask);
+ int (*port_bridge_flags)(struct dsa_switch *ds, int port,
+ unsigned long flags);
+ int (*port_set_mrouter)(struct dsa_switch *ds, int port,
+ bool mrouter);
/*
* VLAN support
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index 8a1bcb2b4208..da99961599de 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -174,7 +174,7 @@ int dsa_port_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags);
+int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long mask);
int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags);
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter);
int dsa_port_vlan_add(struct dsa_port *dp,
diff --git a/net/dsa/port.c b/net/dsa/port.c
index aa1cbba7f89f..8df492412138 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -382,39 +382,34 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
return 0;
}
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags)
+int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long mask)
{
struct dsa_switch *ds = dp->ds;
- if (!ds->ops->port_egress_floods ||
- (flags & ~(BR_FLOOD | BR_MCAST_FLOOD)))
+ if (!ds->ops->port_pre_bridge_flags)
return -EINVAL;
- return 0;
+ return ds->ops->port_pre_bridge_flags(ds, dp->index, mask);
}
int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags)
{
struct dsa_switch *ds = dp->ds;
- int port = dp->index;
- int err = 0;
- if (ds->ops->port_egress_floods)
- err = ds->ops->port_egress_floods(ds, port, flags & BR_FLOOD,
- flags & BR_MCAST_FLOOD);
+ if (!ds->ops->port_bridge_flags)
+ return -EINVAL;
- return err;
+ return ds->ops->port_bridge_flags(ds, dp->index, flags);
}
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter)
{
struct dsa_switch *ds = dp->ds;
- int port = dp->index;
- if (!ds->ops->port_egress_floods)
+ if (!ds->ops->port_set_mrouter)
return -EOPNOTSUPP;
- return ds->ops->port_egress_floods(ds, port, true, mrouter);
+ return ds->ops->port_set_mrouter(ds, dp->index, mrouter);
}
int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
--
2.25.1
From: Vladimir Oltean <[email protected]>
It is customary to return -EOPNOTSUPP when an operation is not supported.
Sometimes the fact that an operation is not supported is irrelevant to
upper layers, and in that case the return code is ignored.
However, in the case of br_switchdev_set_port_flag, it took it upon
itself to treat -EOPNOTSUPP differently and replace it with 0.
Due to that, users won't be notified that a switchdev port doesn't
support changing a certain bridge port flag (learning, flooding etc).
However, no one is returning -EOPNOTSUPP, seemingly as a workaround to
the fact that -EOPNOTSUPP will be ignored.
So let's stop treating -EOPNOTSUPP special, and just propagate that
error, and convert all drivers to use EOPNOTSUPP instead of EINVAL.
We already made br_switchdev_set_port_flag stop printing, and we have
callers that shouldn't fail already ignore the error code.
Signed-off-by: Vladimir Oltean <[email protected]>
---
.../ethernet/marvell/prestera/prestera_switchdev.c | 2 +-
.../net/ethernet/mellanox/mlxsw/spectrum_switchdev.c | 2 +-
drivers/net/ethernet/rocker/rocker_main.c | 2 +-
drivers/net/ethernet/ti/cpsw_switchdev.c | 2 +-
drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 2 +-
net/bridge/br_switchdev.c | 11 +----------
net/dsa/port.c | 2 +-
7 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
index ab62945c7183..9acd6907454d 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
@@ -587,7 +587,7 @@ static int prestera_port_attr_br_flags_set(struct prestera_port *port,
int err;
if (val.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
- err = -EINVAL;
+ err = -EOPNOTSUPP;
br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
if (!br_port)
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index f19be04704e7..a2b2dd7bf6b3 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -661,7 +661,7 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
int err;
if (val.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
- return -EINVAL;
+ return -EOPNOTSUPP;
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
orig_dev);
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index b8087dd0b284..e755c9ac8716 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -1591,7 +1591,7 @@ rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
return err;
if (val.mask & ~brport_flags_s)
- return -EINVAL;
+ return -EOPNOTSUPP;
return wops->port_attr_bridge_flags_set(rocker_port, val.flags);
}
diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c
index dd4b1e161dde..b47ec2ac5b17 100644
--- a/drivers/net/ethernet/ti/cpsw_switchdev.c
+++ b/drivers/net/ethernet/ti/cpsw_switchdev.c
@@ -63,7 +63,7 @@ static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
bool unreg_mcast_add = false;
if (val.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
- return -EINVAL;
+ return -EOPNOTSUPP;
if (val.flags & BR_MCAST_FLOOD)
unreg_mcast_add = true;
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index c4bcd63b68b8..25ebb127db3c 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -915,7 +915,7 @@ static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev,
int err = 0;
if (val.mask & ~(BR_LEARNING | BR_FLOOD))
- return -EINVAL;
+ return -EOPNOTSUPP;
/* Learning is enabled per switch */
err = dpaa2_switch_set_learning(port_priv->ethsw_data,
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index 3b460eb225dd..e1774f1b0a44 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -82,16 +82,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
/* We run from atomic context here */
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
&info.info, extack);
- err = notifier_to_errno(err);
- if (err == -EOPNOTSUPP)
- return 0;
-
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "bridge flag offload is not supported");
- return -EOPNOTSUPP;
- }
-
- return 0;
+ return notifier_to_errno(err);
}
static void
diff --git a/net/dsa/port.c b/net/dsa/port.c
index fffe5f14ec0a..26be06ba1461 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -388,7 +388,7 @@ int dsa_port_bridge_flags(const struct dsa_port *dp,
struct dsa_switch *ds = dp->ds;
if (!ds->ops->port_bridge_flags)
- return -EINVAL;
+ return -EOPNOTSUPP;
return ds->ops->port_bridge_flags(ds, dp->index, val);
}
--
2.25.1
From: Vladimir Oltean <[email protected]>
With the bridge driver doing that for us now, we can simplify our
mid-layer logic a little bit, which would have otherwise needed some
tuning for the disabling of address learning that is necessary in
standalone mode.
Signed-off-by: Vladimir Oltean <[email protected]>
---
net/dsa/port.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 5e079a61528e..aa1cbba7f89f 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -132,11 +132,6 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
};
int err;
- /* Set the flooding mode before joining the port in the switch */
- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD);
- if (err)
- return err;
-
/* Here the interface is already bridged. Reflect the current
* configuration so that drivers can program their chips accordingly.
*/
@@ -145,10 +140,8 @@ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
err = dsa_broadcast(DSA_NOTIFIER_BRIDGE_JOIN, &info);
/* The bridging is rolled back on error */
- if (err) {
- dsa_port_bridge_flags(dp, 0);
+ if (err)
dp->bridge_dev = NULL;
- }
return err;
}
@@ -172,9 +165,6 @@ void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br)
if (err)
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
- /* Port is leaving the bridge, disable flooding */
- dsa_port_bridge_flags(dp, 0);
-
/* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
* so allow it to be in BR_STATE_FORWARDING to be kept functional
*/
--
2.25.1
From: Vladimir Oltean <[email protected]>
The ocelot switches are a bit odd in that they do not have an STP state
to put the ports into. Instead, the forwarding configuration is delayed
from the typical port_bridge_join into stp_state_set, when the port enters
the BR_STATE_FORWARDING state.
I can only guess that the implementation of this quirk is the reason that
led to the simplification of the driver such that only one bridge could
be offloaded at a time.
We can simplify the data structures somewhat, and introduce a per-port
bridge device pointer and STP state, similar to how the LAG offload
works now (there we have a per-port bonding device pointer and TX
enabled state). This allows offloading multiple bridges with relative
ease, while still keeping in place the quirk to delay the programming of
the PGIDs.
Signed-off-by: Vladimir Oltean <[email protected]>
---
drivers/net/ethernet/mscc/ocelot.c | 69 +++++++++++-----------
drivers/net/ethernet/mscc/ocelot_vsc7514.c | 2 +-
include/soc/mscc/ocelot.h | 7 +--
3 files changed, 38 insertions(+), 40 deletions(-)
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index c8bfc2f9534a..6f2967376210 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -912,6 +912,26 @@ static u32 ocelot_get_bond_mask(struct ocelot *ocelot, struct net_device *bond,
return mask;
}
+static u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot,
+ struct net_device *bridge)
+{
+ u32 mask = 0;
+ int port;
+
+ for (port = 0; port < ocelot->num_phys_ports; port++) {
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+ if (!ocelot_port)
+ continue;
+
+ if (ocelot_port->stp_state == BR_STATE_FORWARDING &&
+ ocelot_port->bridge == bridge)
+ mask |= BIT(port);
+ }
+
+ return mask;
+}
+
static u32 ocelot_get_dsa_8021q_cpu_mask(struct ocelot *ocelot)
{
u32 mask = 0;
@@ -961,10 +981,12 @@ void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot)
*/
mask = GENMASK(ocelot->num_phys_ports - 1, 0);
mask &= ~cpu_fwd_mask;
- } else if (ocelot->bridge_fwd_mask & BIT(port)) {
+ } else if (ocelot_port->bridge) {
+ struct net_device *bridge = ocelot_port->bridge;
struct net_device *bond = ocelot_port->bond;
- mask = ocelot->bridge_fwd_mask & ~BIT(port);
+ mask = ocelot_get_bridge_fwd_mask(ocelot, bridge);
+ mask &= ~BIT(port);
if (bond) {
mask &= ~ocelot_get_bond_mask(ocelot, bond,
false);
@@ -985,29 +1007,16 @@ EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
- u32 port_cfg;
-
- if (!(BIT(port) & ocelot->bridge_mask))
- return;
+ u32 learn_ena = 0;
- port_cfg = ocelot_read_gix(ocelot, ANA_PORT_PORT_CFG, port);
+ ocelot_port->stp_state = state;
- switch (state) {
- case BR_STATE_FORWARDING:
- ocelot->bridge_fwd_mask |= BIT(port);
- fallthrough;
- case BR_STATE_LEARNING:
- if (ocelot_port->brport_flags & BR_LEARNING)
- port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
- break;
-
- default:
- port_cfg &= ~ANA_PORT_PORT_CFG_LEARN_ENA;
- ocelot->bridge_fwd_mask &= ~BIT(port);
- break;
- }
+ if ((state == BR_STATE_LEARNING || state == BR_STATE_FORWARDING) &&
+ ocelot_port->brport_flags & BR_LEARNING)
+ learn_ena = ANA_PORT_PORT_CFG_LEARN_ENA;
- ocelot_write_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG, port);
+ ocelot_rmw_gix(ocelot, learn_ena, ANA_PORT_PORT_CFG_LEARN_ENA,
+ ANA_PORT_PORT_CFG, port);
ocelot_apply_bridge_fwd_mask(ocelot);
}
@@ -1237,16 +1246,9 @@ EXPORT_SYMBOL(ocelot_port_mdb_del);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge)
{
- if (!ocelot->bridge_mask) {
- ocelot->hw_bridge_dev = bridge;
- } else {
- if (ocelot->hw_bridge_dev != bridge)
- /* This is adding the port to a second bridge, this is
- * unsupported */
- return -ENODEV;
- }
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
- ocelot->bridge_mask |= BIT(port);
+ ocelot_port->bridge = bridge;
return 0;
}
@@ -1259,10 +1261,7 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct ocelot_vlan pvid = {0}, native_vlan = {0};
int ret;
- ocelot->bridge_mask &= ~BIT(port);
-
- if (!ocelot->bridge_mask)
- ocelot->hw_bridge_dev = NULL;
+ ocelot_port->bridge = NULL;
ret = ocelot_port_vlan_filtering(ocelot, port, false);
if (ret)
diff --git a/drivers/net/ethernet/mscc/ocelot_vsc7514.c b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
index 6b6eb92149ba..c366d96fc945 100644
--- a/drivers/net/ethernet/mscc/ocelot_vsc7514.c
+++ b/drivers/net/ethernet/mscc/ocelot_vsc7514.c
@@ -694,7 +694,7 @@ static irqreturn_t ocelot_xtr_irq_handler(int irq, void *arg)
/* Everything we see on an interface that is in the HW bridge
* has already been forwarded.
*/
- if (ocelot->bridge_mask & BIT(info.port))
+ if (ocelot_port->bridge)
skb->offload_fwd_mark = 1;
skb->protocol = eth_type_trans(skb, dev);
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index d8b4b1d3be15..333300b14a97 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -617,6 +617,9 @@ struct ocelot_port {
struct net_device *bond;
bool lag_tx_active;
+
+ struct net_device *bridge;
+ u8 stp_state;
};
struct ocelot {
@@ -636,10 +639,6 @@ struct ocelot {
int num_frame_refs;
int num_mact_rows;
- struct net_device *hw_bridge_dev;
- u16 bridge_mask;
- u16 bridge_fwd_mask;
-
struct ocelot_port **ports;
u8 base_mac[ETH_ALEN];
--
2.25.1
From: Vladimir Oltean <[email protected]>
There does not appear to be any strong reason why
br_switchdev_set_port_flag issues a separate notification for checking
the supported brport flags rather than just attempting to apply them and
propagating the error if that fails.
However, there is a reason why this switchdev API is counterproductive
for a driver writer, and that is because although br_switchdev_set_port_flag
gets passed a "flags" and a "mask", those are passed piecemeal to the
driver, so while the PRE_BRIDGE_FLAGS listener knows what changed
because it has the "mask", the BRIDGE_FLAGS listener doesn't, because it
only has the final value. This means that "edge detection" needs to be
done by each individual BRIDGE_FLAGS listener by XOR-ing the old and the
new flags, which in turn means that copying the flags into a driver
private variable is strictly necessary.
This can be solved by passing the "flags" and the "value" together into
a single switchdev attribute, and it also reduces some boilerplate in
the drivers that offload this.
Signed-off-by: Vladimir Oltean <[email protected]>
---
drivers/net/dsa/b53/b53_common.c | 16 ++++-------
drivers/net/dsa/mv88e6xxx/chip.c | 17 ++++-------
.../marvell/prestera/prestera_switchdev.c | 16 +++++------
.../mellanox/mlxsw/spectrum_switchdev.c | 28 ++++++-------------
drivers/net/ethernet/rocker/rocker_main.c | 24 +++-------------
drivers/net/ethernet/ti/cpsw_switchdev.c | 20 ++++---------
drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 22 ++++-----------
include/net/dsa.h | 4 +--
include/net/switchdev.h | 8 ++++--
net/bridge/br_switchdev.c | 19 ++++---------
net/dsa/dsa_priv.h | 4 +--
net/dsa/port.c | 15 ++--------
net/dsa/slave.c | 3 --
13 files changed, 58 insertions(+), 138 deletions(-)
diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c
index 0c4000783b15..a713117111c6 100644
--- a/drivers/net/dsa/b53/b53_common.c
+++ b/drivers/net/dsa/b53/b53_common.c
@@ -1948,19 +1948,14 @@ int b53_br_egress_floods(struct dsa_switch *ds, int port,
}
EXPORT_SYMBOL(b53_br_egress_floods);
-static int b53_pre_br_flags(struct dsa_switch *ds, int port,
- unsigned long mask)
+static int b53_br_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags val)
{
- if (mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
+ if (val.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
return -EINVAL;
- return 0;
-}
-
-static int b53_br_flags(struct dsa_switch *ds, int port, unsigned long flags)
-{
- return b53_br_egress_floods(ds, port, flags & BR_FLOOD,
- flags & BR_MCAST_FLOOD);
+ return b53_br_egress_floods(ds, port, val.flags & BR_FLOOD,
+ val.flags & BR_MCAST_FLOOD);
}
static int b53_set_mrouter(struct dsa_switch *ds, int port, bool mrouter)
@@ -2208,7 +2203,6 @@ static const struct dsa_switch_ops b53_switch_ops = {
.port_bridge_join = b53_br_join,
.port_bridge_leave = b53_br_leave,
.port_bridge_flags = b53_br_flags,
- .port_pre_bridge_flags = b53_pre_br_flags,
.port_set_mrouter = b53_set_mrouter,
.port_stp_state_set = b53_br_set_stp_state,
.port_fast_age = b53_br_fast_age,
diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index bd986244aa27..deb68e54fa74 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -5380,20 +5380,14 @@ static int mv88e6xxx_port_egress_floods(struct dsa_switch *ds, int port,
return err;
}
-static int mv88e6xxx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
- unsigned long mask)
+static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags val)
{
- if (mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
+ if (val.mask & ~(BR_FLOOD | BR_MCAST_FLOOD))
return -EINVAL;
- return 0;
-}
-
-static int mv88e6xxx_port_bridge_flags(struct dsa_switch *ds, int port,
- unsigned long flags)
-{
- return mv88e6xxx_port_egress_floods(ds, port, flags & BR_FLOOD,
- flags & BR_MCAST_FLOOD);
+ return mv88e6xxx_port_egress_floods(ds, port, val.flags & BR_FLOOD,
+ val.flags & BR_MCAST_FLOOD);
}
static int mv88e6xxx_port_set_mrouter(struct dsa_switch *ds, int port,
@@ -5700,7 +5694,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = {
.set_ageing_time = mv88e6xxx_set_ageing_time,
.port_bridge_join = mv88e6xxx_port_bridge_join,
.port_bridge_leave = mv88e6xxx_port_bridge_leave,
- .port_pre_bridge_flags = mv88e6xxx_port_pre_bridge_flags,
.port_bridge_flags = mv88e6xxx_port_bridge_flags,
.port_set_mrouter = mv88e6xxx_port_set_mrouter,
.port_stp_state_set = mv88e6xxx_port_stp_state_set,
diff --git a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
index 8c2b03151736..ab62945c7183 100644
--- a/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
+++ b/drivers/net/ethernet/marvell/prestera/prestera_switchdev.c
@@ -581,24 +581,27 @@ int prestera_bridge_port_event(struct net_device *dev, unsigned long event,
static int prestera_port_attr_br_flags_set(struct prestera_port *port,
struct net_device *dev,
- unsigned long flags)
+ struct switchdev_brport_flags val)
{
struct prestera_bridge_port *br_port;
int err;
+ if (val.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
+ err = -EINVAL;
+
br_port = prestera_bridge_port_by_dev(port->sw->swdev, dev);
if (!br_port)
return 0;
- err = prestera_hw_port_flood_set(port, flags & BR_FLOOD);
+ err = prestera_hw_port_flood_set(port, val.flags & BR_FLOOD);
if (err)
return err;
- err = prestera_hw_port_learning_set(port, flags & BR_LEARNING);
+ err = prestera_hw_port_learning_set(port, val.flags & BR_LEARNING);
if (err)
return err;
- memcpy(&br_port->flags, &flags, sizeof(flags));
+ memcpy(&br_port->flags, &val.flags, sizeof(val.flags));
return 0;
}
@@ -705,11 +708,6 @@ static int prestera_port_obj_attr_set(struct net_device *dev,
err = prestera_port_attr_stp_state_set(port, attr->orig_dev,
attr->u.stp_state);
break;
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- if (attr->u.brport_flags &
- ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
- err = -EINVAL;
- break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
err = prestera_port_attr_br_flags_set(port, attr->orig_dev,
attr->u.brport_flags);
diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
index 20c4f3c2cf23..f19be04704e7 100644
--- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
+++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
@@ -653,23 +653,16 @@ mlxsw_sp_bridge_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
return err;
}
-static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port
- *mlxsw_sp_port,
- unsigned long brport_flags)
-{
- if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
- return -EINVAL;
-
- return 0;
-}
-
static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
struct net_device *orig_dev,
- unsigned long brport_flags)
+ struct switchdev_brport_flags val)
{
struct mlxsw_sp_bridge_port *bridge_port;
int err;
+ if (val.mask & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD))
+ return -EINVAL;
+
bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp_port->mlxsw_sp->bridge,
orig_dev);
if (!bridge_port)
@@ -677,12 +670,12 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
MLXSW_SP_FLOOD_TYPE_UC,
- brport_flags & BR_FLOOD);
+ val.flags & BR_FLOOD);
if (err)
return err;
err = mlxsw_sp_bridge_port_learning_set(mlxsw_sp_port, bridge_port,
- brport_flags & BR_LEARNING);
+ val.flags & BR_LEARNING);
if (err)
return err;
@@ -691,13 +684,12 @@ static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
err = mlxsw_sp_bridge_port_flood_table_set(mlxsw_sp_port, bridge_port,
MLXSW_SP_FLOOD_TYPE_MC,
- brport_flags &
- BR_MCAST_FLOOD);
+ val.flags & BR_MCAST_FLOOD);
if (err)
return err;
out:
- memcpy(&bridge_port->flags, &brport_flags, sizeof(brport_flags));
+ memcpy(&bridge_port->flags, &val.flags, sizeof(val.flags));
return 0;
}
@@ -898,10 +890,6 @@ static int mlxsw_sp_port_attr_set(struct net_device *dev,
attr->orig_dev,
attr->u.stp_state);
break;
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port,
- attr->u.brport_flags);
- break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port,
attr->orig_dev,
diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c
index 740a715c49c6..b8087dd0b284 100644
--- a/drivers/net/ethernet/rocker/rocker_main.c
+++ b/drivers/net/ethernet/rocker/rocker_main.c
@@ -1575,8 +1575,8 @@ rocker_world_port_attr_bridge_flags_support_get(const struct rocker_port *
}
static int
-rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port,
- unsigned long brport_flags)
+rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
+ struct switchdev_brport_flags val)
{
struct rocker_world_ops *wops = rocker_port->rocker->wops;
unsigned long brport_flags_s;
@@ -1590,22 +1590,10 @@ rocker_world_port_attr_pre_bridge_flags_set(struct rocker_port *rocker_port,
if (err)
return err;
- if (brport_flags & ~brport_flags_s)
+ if (val.mask & ~brport_flags_s)
return -EINVAL;
- return 0;
-}
-
-static int
-rocker_world_port_attr_bridge_flags_set(struct rocker_port *rocker_port,
- unsigned long brport_flags)
-{
- struct rocker_world_ops *wops = rocker_port->rocker->wops;
-
- if (!wops->port_attr_bridge_flags_set)
- return -EOPNOTSUPP;
-
- return wops->port_attr_bridge_flags_set(rocker_port, brport_flags);
+ return wops->port_attr_bridge_flags_set(rocker_port, val.flags);
}
static int
@@ -2056,10 +2044,6 @@ static int rocker_port_attr_set(struct net_device *dev,
err = rocker_world_port_attr_stp_state_set(rocker_port,
attr->u.stp_state);
break;
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- err = rocker_world_port_attr_pre_bridge_flags_set(rocker_port,
- attr->u.brport_flags);
- break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
err = rocker_world_port_attr_bridge_flags_set(rocker_port,
attr->u.brport_flags);
diff --git a/drivers/net/ethernet/ti/cpsw_switchdev.c b/drivers/net/ethernet/ti/cpsw_switchdev.c
index 9967cf985728..dd4b1e161dde 100644
--- a/drivers/net/ethernet/ti/cpsw_switchdev.c
+++ b/drivers/net/ethernet/ti/cpsw_switchdev.c
@@ -57,12 +57,15 @@ static int cpsw_port_stp_state_set(struct cpsw_priv *priv, u8 state)
static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
struct net_device *orig_dev,
- unsigned long brport_flags)
+ struct switchdev_brport_flags val)
{
struct cpsw_common *cpsw = priv->cpsw;
bool unreg_mcast_add = false;
- if (brport_flags & BR_MCAST_FLOOD)
+ if (val.mask & ~(BR_LEARNING | BR_MCAST_FLOOD))
+ return -EINVAL;
+
+ if (val.flags & BR_MCAST_FLOOD)
unreg_mcast_add = true;
dev_dbg(priv->dev, "BR_MCAST_FLOOD: %d port %u\n",
unreg_mcast_add, priv->emac_port);
@@ -73,15 +76,6 @@ static int cpsw_port_attr_br_flags_set(struct cpsw_priv *priv,
return 0;
}
-static int cpsw_port_attr_br_flags_pre_set(struct net_device *netdev,
- unsigned long flags)
-{
- if (flags & ~(BR_LEARNING | BR_MCAST_FLOOD))
- return -EINVAL;
-
- return 0;
-}
-
static int cpsw_port_attr_set(struct net_device *ndev,
const struct switchdev_attr *attr)
{
@@ -91,10 +85,6 @@ static int cpsw_port_attr_set(struct net_device *ndev,
dev_dbg(priv->dev, "attr: id %u port: %u\n", attr->id, priv->emac_port);
switch (attr->id) {
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- ret = cpsw_port_attr_br_flags_pre_set(ndev,
- attr->u.brport_flags);
- break;
case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
ret = cpsw_port_stp_state_set(priv, attr->u.stp_state);
dev_dbg(priv->dev, "stp state: %u\n", attr->u.stp_state);
diff --git a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index ca3d07fe7f58..c4bcd63b68b8 100644
--- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -908,28 +908,22 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
return dpaa2_switch_port_set_stp_state(port_priv, state);
}
-static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev,
- unsigned long flags)
-{
- if (flags & ~(BR_LEARNING | BR_FLOOD))
- return -EINVAL;
-
- return 0;
-}
-
static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev,
- unsigned long flags)
+ struct switchdev_brport_flags val)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err = 0;
+ if (val.mask & ~(BR_LEARNING | BR_FLOOD))
+ return -EINVAL;
+
/* Learning is enabled per switch */
err = dpaa2_switch_set_learning(port_priv->ethsw_data,
- !!(flags & BR_LEARNING));
+ !!(val.flags & BR_LEARNING));
if (err)
goto exit;
- err = dpaa2_switch_port_set_flood(port_priv, !!(flags & BR_FLOOD));
+ err = dpaa2_switch_port_set_flood(port_priv, !!(val.flags & BR_FLOOD));
exit:
return err;
@@ -945,10 +939,6 @@ static int dpaa2_switch_port_attr_set(struct net_device *netdev,
err = dpaa2_switch_port_attr_stp_state_set(netdev,
attr->u.stp_state);
break;
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- err = dpaa2_switch_port_attr_br_flags_pre_set(netdev,
- attr->u.brport_flags);
- break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
err = dpaa2_switch_port_attr_br_flags_set(netdev,
attr->u.brport_flags);
diff --git a/include/net/dsa.h b/include/net/dsa.h
index e37fee22caab..8af12850337d 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -621,10 +621,8 @@ 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_pre_bridge_flags)(struct dsa_switch *ds, int port,
- unsigned long mask);
int (*port_bridge_flags)(struct dsa_switch *ds, int port,
- unsigned long flags);
+ struct switchdev_brport_flags val);
int (*port_set_mrouter)(struct dsa_switch *ds, int port,
bool mrouter);
diff --git a/include/net/switchdev.h b/include/net/switchdev.h
index 88fcac140966..62f16a6bd313 100644
--- a/include/net/switchdev.h
+++ b/include/net/switchdev.h
@@ -20,7 +20,6 @@ enum switchdev_attr_id {
SWITCHDEV_ATTR_ID_UNDEFINED,
SWITCHDEV_ATTR_ID_PORT_STP_STATE,
SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
- SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
SWITCHDEV_ATTR_ID_PORT_MROUTER,
SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME,
SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING,
@@ -33,6 +32,11 @@ enum switchdev_attr_id {
#endif
};
+struct switchdev_brport_flags {
+ unsigned long flags;
+ unsigned long mask;
+};
+
struct switchdev_attr {
struct net_device *orig_dev;
enum switchdev_attr_id id;
@@ -41,7 +45,7 @@ struct switchdev_attr {
void (*complete)(struct net_device *dev, int err, void *priv);
union {
u8 stp_state; /* PORT_STP_STATE */
- unsigned long brport_flags; /* PORT_{PRE}_BRIDGE_FLAGS */
+ struct switchdev_brport_flags brport_flags; /* PORT_BRIDGE_FLAGS */
bool mrouter; /* PORT_MROUTER */
clock_t ageing_time; /* BRIDGE_AGEING_TIME */
bool vlan_filtering; /* BRIDGE_VLAN_FILTERING */
diff --git a/net/bridge/br_switchdev.c b/net/bridge/br_switchdev.c
index c18e1d600dc6..3b460eb225dd 100644
--- a/net/bridge/br_switchdev.c
+++ b/net/bridge/br_switchdev.c
@@ -65,8 +65,11 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
{
struct switchdev_attr attr = {
.orig_dev = p->dev,
- .id = SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS,
- .u.brport_flags = mask,
+ .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS,
+ .u.brport_flags = {
+ .flags = flags,
+ .mask = mask,
+ },
};
struct switchdev_notifier_port_attr_info info = {
.attr = &attr,
@@ -78,7 +81,7 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
/* We run from atomic context here */
err = call_switchdev_notifiers(SWITCHDEV_PORT_ATTR_SET, p->dev,
- &info.info, NULL);
+ &info.info, extack);
err = notifier_to_errno(err);
if (err == -EOPNOTSUPP)
return 0;
@@ -88,16 +91,6 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p,
return -EOPNOTSUPP;
}
- attr.id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS;
- attr.flags = SWITCHDEV_F_DEFER;
- attr.u.brport_flags = flags;
-
- err = switchdev_port_attr_set(p->dev, &attr);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "error setting offload flag on port");
- return err;
- }
-
return 0;
}
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index da99961599de..4fed6b833936 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -174,8 +174,8 @@ int dsa_port_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long mask);
-int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags);
+int dsa_port_bridge_flags(const struct dsa_port *dp,
+ struct switchdev_brport_flags val);
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter);
int dsa_port_vlan_add(struct dsa_port *dp,
const struct switchdev_obj_port_vlan *vlan);
diff --git a/net/dsa/port.c b/net/dsa/port.c
index 8df492412138..fffe5f14ec0a 100644
--- a/net/dsa/port.c
+++ b/net/dsa/port.c
@@ -382,24 +382,15 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock)
return 0;
}
-int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long mask)
-{
- struct dsa_switch *ds = dp->ds;
-
- if (!ds->ops->port_pre_bridge_flags)
- return -EINVAL;
-
- return ds->ops->port_pre_bridge_flags(ds, dp->index, mask);
-}
-
-int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags)
+int dsa_port_bridge_flags(const struct dsa_port *dp,
+ struct switchdev_brport_flags val)
{
struct dsa_switch *ds = dp->ds;
if (!ds->ops->port_bridge_flags)
return -EINVAL;
- return ds->ops->port_bridge_flags(ds, dp->index, flags);
+ return ds->ops->port_bridge_flags(ds, dp->index, val);
}
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter)
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index 431bdbdd8473..d53c455a73f3 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -289,9 +289,6 @@ 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);
break;
- case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
- ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags);
- break;
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
ret = dsa_port_bridge_flags(dp, attr->u.brport_flags);
break;
--
2.25.1
From: Vladimir Oltean <[email protected]>
We should not be unconditionally enabling address learning, since doing
that is actively detrimential when a port is standalone and not offloading
a bridge. Namely, if a port in the switch is standalone and others are
offloading the bridge, then we could enter a situation where we learn an
address towards the standalone port, but the bridged ports could not
forward the packet there, because the CPU is the only path between the
standalone and the bridged ports. The solution of course is to not
enable address learning unless the bridge asks for it.
Signed-off-by: Vladimir Oltean <[email protected]>
Reviewed-by: Alexandre Belloni <[email protected]>
---
drivers/net/dsa/ocelot/felix.c | 9 ++++
drivers/net/ethernet/mscc/ocelot.c | 57 +++++++++++++++++++++++++-
drivers/net/ethernet/mscc/ocelot_net.c | 4 ++
include/soc/mscc/ocelot.h | 4 ++
4 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index 1bd5aea12b25..4d1fef28b6cf 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -553,6 +553,14 @@ static void felix_bridge_stp_state_set(struct dsa_switch *ds, int port,
return ocelot_bridge_stp_state_set(ocelot, port, state);
}
+static int felix_bridge_flags(struct dsa_switch *ds, int port,
+ struct switchdev_brport_flags val)
+{
+ struct ocelot *ocelot = ds->priv;
+
+ return ocelot_port_bridge_flags(ocelot, port, val);
+}
+
static int felix_bridge_join(struct dsa_switch *ds, int port,
struct net_device *br)
{
@@ -1358,6 +1366,7 @@ const struct dsa_switch_ops felix_switch_ops = {
.port_fdb_del = felix_fdb_del,
.port_mdb_add = felix_mdb_add,
.port_mdb_del = felix_mdb_del,
+ .port_bridge_flags = felix_bridge_flags,
.port_bridge_join = felix_bridge_join,
.port_bridge_leave = felix_bridge_leave,
.port_lag_join = felix_lag_join,
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 8c1052346b58..c8bfc2f9534a 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -984,6 +984,7 @@ EXPORT_SYMBOL(ocelot_apply_bridge_fwd_mask);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 port_cfg;
if (!(BIT(port) & ocelot->bridge_mask))
@@ -996,7 +997,8 @@ void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
ocelot->bridge_fwd_mask |= BIT(port);
fallthrough;
case BR_STATE_LEARNING:
- port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
+ if (ocelot_port->brport_flags & BR_LEARNING)
+ port_cfg |= ANA_PORT_PORT_CFG_LEARN_ENA;
break;
default:
@@ -1253,6 +1255,7 @@ EXPORT_SYMBOL(ocelot_port_bridge_join);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
struct net_device *bridge)
{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_vlan pvid = {0}, native_vlan = {0};
int ret;
@@ -1268,6 +1271,10 @@ int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
ocelot_port_set_pvid(ocelot, port, pvid);
ocelot_port_set_native_vlan(ocelot, port, native_vlan);
+ ocelot_port->brport_flags = 0;
+ ocelot_rmw_gix(ocelot, 0, ANA_PORT_PORT_CFG_LEARN_ENA,
+ ANA_PORT_PORT_CFG, port);
+
return 0;
}
EXPORT_SYMBOL(ocelot_port_bridge_leave);
@@ -1480,6 +1487,54 @@ int ocelot_get_max_mtu(struct ocelot *ocelot, int port)
}
EXPORT_SYMBOL(ocelot_get_max_mtu);
+int ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
+ struct switchdev_brport_flags val)
+{
+ struct ocelot_port *ocelot_port = ocelot->ports[port];
+
+ if (val.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING))
+ return -EOPNOTSUPP;
+
+ if (val.mask & BR_LEARNING) {
+ u32 port_cfg = 0;
+
+ if (val.flags & BR_LEARNING)
+ port_cfg = ANA_PORT_PORT_CFG_LEARN_ENA;
+
+ ocelot_rmw_gix(ocelot, port_cfg, ANA_PORT_PORT_CFG_LEARN_ENA,
+ ANA_PORT_PORT_CFG, port);
+ }
+ if (val.mask & BR_FLOOD) {
+ u32 pgid = 0;
+
+ if (val.flags & BR_FLOOD)
+ pgid = BIT(port);
+
+ ocelot_rmw_rix(ocelot, pgid, BIT(port), ANA_PGID_PGID, PGID_UC);
+ }
+ if (val.mask & BR_MCAST_FLOOD) {
+ u32 pgid = 0;
+
+ if (val.flags & BR_MCAST_FLOOD)
+ pgid = BIT(port);
+
+ ocelot_rmw_rix(ocelot, pgid, BIT(port), ANA_PGID_PGID, PGID_MC);
+ }
+ if (val.mask & BR_BCAST_FLOOD) {
+ u32 pgid = 0;
+
+ if (val.flags & BR_BCAST_FLOOD)
+ pgid = BIT(port);
+
+ ocelot_rmw_rix(ocelot, pgid, BIT(port), ANA_PGID_PGID, PGID_BC);
+ }
+
+ ocelot_port->brport_flags = val.flags;
+
+ return 0;
+}
+EXPORT_SYMBOL(ocelot_port_bridge_flags);
+
void ocelot_init_port(struct ocelot *ocelot, int port)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
diff --git a/drivers/net/ethernet/mscc/ocelot_net.c b/drivers/net/ethernet/mscc/ocelot_net.c
index 8f12fa45b1b5..e5a07a1d7647 100644
--- a/drivers/net/ethernet/mscc/ocelot_net.c
+++ b/drivers/net/ethernet/mscc/ocelot_net.c
@@ -1025,6 +1025,10 @@ static int ocelot_port_attr_set(struct net_device *dev,
case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
ocelot_port_attr_mc_set(ocelot, port, !attr->u.mc_disabled);
break;
+ case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
+ err = ocelot_port_bridge_flags(ocelot, port,
+ attr->u.brport_flags);
+ break;
default:
err = -EOPNOTSUPP;
break;
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index 7ee85527cb5f..d8b4b1d3be15 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -597,6 +597,8 @@ struct ocelot_port {
struct regmap *target;
+ unsigned long brport_flags;
+
bool vlan_aware;
/* VLAN that untagged frames are classified to, on ingress */
struct ocelot_vlan pvid_vlan;
@@ -764,6 +766,8 @@ void ocelot_adjust_link(struct ocelot *ocelot, int port,
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
void ocelot_apply_bridge_fwd_mask(struct ocelot *ocelot);
+int ocelot_port_bridge_flags(struct ocelot *ocelot, int port,
+ struct switchdev_brport_flags val);
int ocelot_port_bridge_join(struct ocelot *ocelot, int port,
struct net_device *bridge);
int ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
--
2.25.1
From: Vladimir Oltean <[email protected]>
In preparation of offloading the bridge port flags which have
independent settings for unknown multicast and for broadcast, we should
also start reserving one destination Port Group ID for the flooding of
broadcast packets, to allow configuring it individually.
Signed-off-by: Vladimir Oltean <[email protected]>
---
drivers/net/ethernet/mscc/ocelot.c | 13 ++++++++-----
include/soc/mscc/ocelot.h | 15 ++++++++-------
2 files changed, 16 insertions(+), 12 deletions(-)
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index f8b85ab8be5d..8c1052346b58 100644
--- a/drivers/net/ethernet/mscc/ocelot.c
+++ b/drivers/net/ethernet/mscc/ocelot.c
@@ -1662,7 +1662,7 @@ int ocelot_init(struct ocelot *ocelot)
/* Setup flooding PGIDs */
for (i = 0; i < ocelot->num_flooding_pgids; i++)
ocelot_write_rix(ocelot, ANA_FLOODING_FLD_MULTICAST(PGID_MC) |
- ANA_FLOODING_FLD_BROADCAST(PGID_MC) |
+ ANA_FLOODING_FLD_BROADCAST(PGID_BC) |
ANA_FLOODING_FLD_UNICAST(PGID_UC),
ANA_FLOODING, i);
ocelot_write(ocelot, ANA_FLOODING_IPMC_FLD_MC6_DATA(PGID_MCIPV6) |
@@ -1683,15 +1683,18 @@ int ocelot_init(struct ocelot *ocelot)
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_SRC + port);
}
- /* Allow broadcast MAC frames. */
for_each_nonreserved_multicast_dest_pgid(ocelot, i) {
u32 val = ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports - 1, 0));
ocelot_write_rix(ocelot, val, ANA_PGID_PGID, i);
}
- ocelot_write_rix(ocelot,
- ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
- ANA_PGID_PGID, PGID_MC);
+ /* Allow broadcast and unknown L2 multicast to the CPU. */
+ ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
+ ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
+ ANA_PGID_PGID, PGID_MC);
+ ocelot_rmw_rix(ocelot, ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
+ ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports)),
+ ANA_PGID_PGID, PGID_BC);
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV4);
ocelot_write_rix(ocelot, 0, ANA_PGID_PGID, PGID_MCIPV6);
diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h
index d0d48e9620fb..7ee85527cb5f 100644
--- a/include/soc/mscc/ocelot.h
+++ b/include/soc/mscc/ocelot.h
@@ -54,16 +54,17 @@
* PGID_CPU: used for whitelisting certain MAC addresses, such as the addresses
* of the switch port net devices, towards the CPU port module.
* PGID_UC: the flooding destinations for unknown unicast traffic.
- * PGID_MC: the flooding destinations for broadcast and non-IP multicast
- * traffic.
+ * PGID_MC: the flooding destinations for non-IP multicast traffic.
* PGID_MCIPV4: the flooding destinations for IPv4 multicast traffic.
* PGID_MCIPV6: the flooding destinations for IPv6 multicast traffic.
+ * PGID_BC: the flooding destinations for broadcast traffic.
*/
-#define PGID_CPU 59
-#define PGID_UC 60
-#define PGID_MC 61
-#define PGID_MCIPV4 62
-#define PGID_MCIPV6 63
+#define PGID_CPU 58
+#define PGID_UC 59
+#define PGID_MC 60
+#define PGID_MCIPV4 61
+#define PGID_MCIPV6 62
+#define PGID_BC 63
#define for_each_unicast_dest_pgid(ocelot, pgid) \
for ((pgid) = 0; \
--
2.25.1
On 08/02/2021 01:21, Vladimir Oltean wrote:
> From: Vladimir Oltean <[email protected]>
>
> It must first be admitted that switchdev device drivers have a life
> beyond the bridge, and when they aren't offloading the bridge driver
> they are operating with forwarding disabled between ports, emulating as
> closely as possible N standalone network interfaces.
>
> Now it must be said that for a switchdev port operating in standalone
> mode, address learning doesn't make much sense since that is a bridge
> function. In fact, address learning even breaks setups such as this one:
>
> +---------------------------------------------+
> | |
> | +-------------------+ |
> | | br0 | send receive |
> | +--------+-+--------+ +--------+ +--------+ |
> | | | | | | | | | |
> | | swp0 | | swp1 | | swp2 | | swp3 | |
> | | | | | | | | | |
> +-+--------+-+--------+-+--------+-+--------+-+
> | ^ | ^
> | | | |
> | +-----------+ |
> | |
> +--------------------------------+
>
> because if the ASIC has a single FDB (can offload a single bridge)
> then source address learning on swp3 can "steal" the source MAC address
> of swp2 from br0's FDB, because learning frames coming from swp2 will be
> done twice: first on the swp1 ingress port, second on the swp3 ingress
> port. So the hardware FDB will become out of sync with the software
> bridge, and when swp2 tries to send one more packet towards swp1, the
> ASIC will attempt to short-circuit the forwarding path and send it
> directly to swp3 (since that's the last port it learned that address on),
> which it obviously can't, because swp3 operates in standalone mode.
>
> So switchdev drivers operating in standalone mode should disable address
> learning. As a matter of practicality, we can reduce code duplication in
> drivers by having the bridge notify through switchdev of the initial and
> final brport flags. Then, drivers can simply start up hardcoded for no
> address learning (similar to how they already start up hardcoded for no
> forwarding), then they only need to listen for
> SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS and their job is basically done, no
> need for special cases when the port joins or leaves the bridge etc.
>
> When a port leaves the bridge (and therefore becomes standalone), we
> issue a switchdev attribute that apart from disabling address learning,
> enables flooding of all kinds. This is also done for pragmatic reasons,
> because even though standalone switchdev ports might not need to have
> flooding enabled in order to inject traffic with any MAC DA from the
> control interface, it certainly doesn't hurt either, and it even makes
> more sense than disabling flooding of unknown traffic towards that port.
>
> Note that the implementation is a bit wacky because the switchdev API
> for port attributes is very counterproductive. Instead of issuing a
> single switchdev notification with a bitwise OR of all flags that we're
> modifying, we need to issue 4 individual notifications, one for each bit.
> This is because the SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS notifier
> forces you to refuse the entire operation if there's at least one bit
> which you can't offload, and that is currently BR_BCAST_FLOOD which
> nobody does. So this change would do nothing for no one if we offloaded
> all flags at once, but the idea is to offload as much as possible
> instead of all or nothing.
>
> Signed-off-by: Vladimir Oltean <[email protected]>
> ---
> net/bridge/br_if.c | 24 +++++++++++++++++++++++-
> net/bridge/br_netlink.c | 16 ++++------------
> net/bridge/br_private.h | 2 ++
> 3 files changed, 29 insertions(+), 13 deletions(-)
>
Hi Vladimir,
I think this patch potentially breaks some use cases. There are a few problems, I'll
start with the more serious one: before the ports would have a set of flags that were
always set when joining, now due to how nbp_flags_change() handles flag setting some might
not be set which would immediately change behaviour w.r.t software fwding. I'll use your
example of BR_BCAST_FLOOD: a lot of drivers will return an error for it and any broadcast
towards these ports will be dropped, we have mixed environments with software ports that
sometimes have traffic (e.g. decapped ARP requests) software forwarded which will stop working.
The other lesser issue is with the style below, I mean these three calls for each flag are
just ugly and look weird as you've also noted, since these APIs are internal can we do better?
Cheers,
Nik
> diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
> index f7d2f472ae24..8903333654f0 100644
> --- a/net/bridge/br_if.c
> +++ b/net/bridge/br_if.c
> @@ -89,6 +89,21 @@ void br_port_carrier_check(struct net_bridge_port *p, bool *notified)
> spin_unlock_bh(&br->lock);
> }
>
> +int nbp_flags_change(struct net_bridge_port *p, unsigned long flags,
> + unsigned long mask, struct netlink_ext_ack *extack)
> +{
> + int err;
> +
> + err = br_switchdev_set_port_flag(p, flags, mask, extack);
> + if (err)
> + return err;
> +
> + p->flags &= ~mask;
> + p->flags |= flags;
> +
> + return 0;
> +}
> +
> static void br_port_set_promisc(struct net_bridge_port *p)
> {
> int err = 0;
> @@ -343,6 +358,10 @@ static void del_nbp(struct net_bridge_port *p)
> update_headroom(br, get_max_headroom(br));
> netdev_reset_rx_headroom(dev);
>
> + nbp_flags_change(p, 0, BR_LEARNING, NULL);
> + nbp_flags_change(p, BR_FLOOD, BR_FLOOD, NULL);
> + nbp_flags_change(p, BR_MCAST_FLOOD, BR_MCAST_FLOOD, NULL);
> + nbp_flags_change(p, BR_BCAST_FLOOD, BR_BCAST_FLOOD, NULL);
> nbp_vlan_flush(p);
> br_fdb_delete_by_port(br, p, 0, 1);
> switchdev_deferred_process();
> @@ -428,7 +447,10 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br,
> p->path_cost = port_cost(dev);
> p->priority = 0x8000 >> BR_PORT_BITS;
> p->port_no = index;
> - p->flags = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
> + nbp_flags_change(p, BR_LEARNING, BR_LEARNING, NULL);
> + nbp_flags_change(p, BR_FLOOD, BR_FLOOD, NULL);
> + nbp_flags_change(p, BR_MCAST_FLOOD, BR_MCAST_FLOOD, NULL);
> + nbp_flags_change(p, BR_BCAST_FLOOD, BR_BCAST_FLOOD, NULL);
> br_init_port(p);
> br_set_state(p, BR_STATE_DISABLED);
> br_stp_port_timer_init(p);
> diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c
> index 02aa95c08b77..ab54d1daa9b4 100644
> --- a/net/bridge/br_netlink.c
> +++ b/net/bridge/br_netlink.c
> @@ -852,28 +852,20 @@ static int br_set_port_state(struct net_bridge_port *p, u8 state)
> return 0;
> }
>
> -/* Set/clear or port flags based on attribute */
> +/* Set/clear or port flags based on netlink attribute */
> static int br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[],
> int attrtype, unsigned long mask,
> struct netlink_ext_ack *extack)
> {
> - unsigned long flags;
> - int err;
> + unsigned long flags = 0;
>
> if (!tb[attrtype])
> return 0;
>
> if (nla_get_u8(tb[attrtype]))
> - flags = p->flags | mask;
> - else
> - flags = p->flags & ~mask;
> -
> - err = br_switchdev_set_port_flag(p, flags, mask, extack);
> - if (err)
> - return err;
> + flags = mask;
>
> - p->flags = flags;
> - return 0;
> + return nbp_flags_change(p, flags, mask, extack);
> }
>
> /* Process bridge protocol info on port */
> diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
> index a1639d41188b..f064abd86bdf 100644
> --- a/net/bridge/br_private.h
> +++ b/net/bridge/br_private.h
> @@ -749,6 +749,8 @@ netdev_features_t br_features_recompute(struct net_bridge *br,
> void br_port_flags_change(struct net_bridge_port *port, unsigned long mask);
> void br_manage_promisc(struct net_bridge *br);
> int nbp_backup_change(struct net_bridge_port *p, struct net_device *backup_dev);
> +int nbp_flags_change(struct net_bridge_port *p, unsigned long flags,
> + unsigned long mask, struct netlink_ext_ack *extack);
>
> /* br_input.c */
> int br_handle_frame_finish(struct net *net, struct sock *sk, struct sk_buff *skb);
>
On Mon, Feb 08, 2021 at 01:37:03PM +0200, Nikolay Aleksandrov wrote:
> Hi Vladimir,
> I think this patch potentially breaks some use cases. There are a few problems, I'll
> start with the more serious one: before the ports would have a set of flags that were
> always set when joining, now due to how nbp_flags_change() handles flag setting some might
> not be set which would immediately change behaviour w.r.t software fwding. I'll use your
> example of BR_BCAST_FLOOD: a lot of drivers will return an error for it and any broadcast
> towards these ports will be dropped, we have mixed environments with software ports that
> sometimes have traffic (e.g. decapped ARP requests) software forwarded which will stop working.
Yes, you're right. The only solution I can think of is to add a "bool ignore_errors"
to nbp_flags_change, set to true from new_nbp and del_nbp, and to false from the
netlink code.
> The other lesser issue is with the style below, I mean these three calls for each flag are
> just ugly and look weird as you've also noted, since these APIs are internal can we do better?
Doing better would mean allowing nbp_flags_change() to have a bit mask with
potentially more brport flags set, and to call br_switchdev_set_port_flag in
a for_each_set_bit() loop?
On 08/02/2021 13:45, Vladimir Oltean wrote:
> On Mon, Feb 08, 2021 at 01:37:03PM +0200, Nikolay Aleksandrov wrote:
>> Hi Vladimir,
>> I think this patch potentially breaks some use cases. There are a few problems, I'll
>> start with the more serious one: before the ports would have a set of flags that were
>> always set when joining, now due to how nbp_flags_change() handles flag setting some might
>> not be set which would immediately change behaviour w.r.t software fwding. I'll use your
>> example of BR_BCAST_FLOOD: a lot of drivers will return an error for it and any broadcast
>> towards these ports will be dropped, we have mixed environments with software ports that
>> sometimes have traffic (e.g. decapped ARP requests) software forwarded which will stop working.
>
> Yes, you're right. The only solution I can think of is to add a "bool ignore_errors"
> to nbp_flags_change, set to true from new_nbp and del_nbp, and to false from the
> netlink code.
>
Indeed, I can't think of any better solution right now, but that would make it more or less
equal to the current situation where the flags are just set. You can read/restore them on add/del
of bridge port, but I guess that's what you'd like to avoid. :)
I don't mind adding the add/del_nbp() notifications, but both of them seem redundant with
the port add/del notifications which you can handle in the driver.
>> The other lesser issue is with the style below, I mean these three calls for each flag are
>> just ugly and look weird as you've also noted, since these APIs are internal can we do better?
>
> Doing better would mean allowing nbp_flags_change() to have a bit mask with
> potentially more brport flags set, and to call br_switchdev_set_port_flag in
> a for_each_set_bit() loop?
>
Sure, that sounds better for now. I think you've described the ideal case in your
commit message.
On Mon, Feb 08, 2021 at 01:21:37AM +0200, Vladimir Oltean wrote:
> From: Vladimir Oltean <[email protected]>
>
> There does not appear to be any strong reason why
> br_switchdev_set_port_flag issues a separate notification for checking
> the supported brport flags rather than just attempting to apply them and
> propagating the error if that fails.
>
> However, there is a reason why this switchdev API is counterproductive
> for a driver writer, and that is because although br_switchdev_set_port_flag
> gets passed a "flags" and a "mask", those are passed piecemeal to the
> driver, so while the PRE_BRIDGE_FLAGS listener knows what changed
> because it has the "mask", the BRIDGE_FLAGS listener doesn't, because it
> only has the final value. This means that "edge detection" needs to be
> done by each individual BRIDGE_FLAGS listener by XOR-ing the old and the
> new flags, which in turn means that copying the flags into a driver
> private variable is strictly necessary.
>
> This can be solved by passing the "flags" and the "value" together into
> a single switchdev attribute, and it also reduces some boilerplate in
> the drivers that offload this.
>
> Signed-off-by: Vladimir Oltean <[email protected]>
> ---
> drivers/net/dsa/b53/b53_common.c | 16 ++++-------
> drivers/net/dsa/mv88e6xxx/chip.c | 17 ++++-------
> .../marvell/prestera/prestera_switchdev.c | 16 +++++------
> .../mellanox/mlxsw/spectrum_switchdev.c | 28 ++++++-------------
> drivers/net/ethernet/rocker/rocker_main.c | 24 +++-------------
> drivers/net/ethernet/ti/cpsw_switchdev.c | 20 ++++---------
> drivers/staging/fsl-dpaa2/ethsw/ethsw.c | 22 ++++-----------
> include/net/dsa.h | 4 +--
> include/net/switchdev.h | 8 ++++--
> net/bridge/br_switchdev.c | 19 ++++---------
> net/dsa/dsa_priv.h | 4 +--
> net/dsa/port.c | 15 ++--------
> net/dsa/slave.c | 3 --
> 13 files changed, 58 insertions(+), 138 deletions(-)
>
(..)
> --- a/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> +++ b/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
> @@ -908,28 +908,22 @@ static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
> return dpaa2_switch_port_set_stp_state(port_priv, state);
> }
>
> -static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev,
> - unsigned long flags)
> -{
> - if (flags & ~(BR_LEARNING | BR_FLOOD))
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev,
> - unsigned long flags)
> + struct switchdev_brport_flags val)
> {
> struct ethsw_port_priv *port_priv = netdev_priv(netdev);
> int err = 0;
>
> + if (val.mask & ~(BR_LEARNING | BR_FLOOD))
> + return -EINVAL;
> +
> /* Learning is enabled per switch */
> err = dpaa2_switch_set_learning(port_priv->ethsw_data,
> - !!(flags & BR_LEARNING));
> + !!(val.flags & BR_LEARNING));
> if (err)
> goto exit;
>
> - err = dpaa2_switch_port_set_flood(port_priv, !!(flags & BR_FLOOD));
> + err = dpaa2_switch_port_set_flood(port_priv, !!(val.flags & BR_FLOOD));
Could you also check the mask to see if the flag needs to be actually changed?
> --- a/include/net/dsa.h
> +++ b/include/net/dsa.h
> @@ -621,10 +621,8 @@ 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_pre_bridge_flags)(struct dsa_switch *ds, int port,
> - unsigned long mask);
> int (*port_bridge_flags)(struct dsa_switch *ds, int port,
> - unsigned long flags);
> + struct switchdev_brport_flags val);
> int (*port_set_mrouter)(struct dsa_switch *ds, int port,
> bool mrouter);
>
In the previous patch you add the .port_pre_bridge_flags()
dsa_switch_ops only to remove it here. Couldn't these two patches be in
reverse order so that there is no need to actually add the callback in
the first place?
Ioana