realtek: drop patches of upstreamed fix and changes from 5.15
- 007-5.16-gpio-realtek...: upstreamed on 5.16 and backported to 5.15.3 - 708-brflood-spi.patch : upstreamed - 709-lag-offloading.patch: upstreamed - 713-v5.12-net-dsa-... : upstreamed and some implementations are replaced Signed-off-by: INAGAKI Hiroshi <musashino.open@gmail.com>
This commit is contained in:
parent
a9d5a8bc79
commit
23881c91e5
4 changed files with 0 additions and 1113 deletions
|
@ -1,22 +0,0 @@
|
||||||
From: Sander Vanheule <sander@svanheule.net>
|
|
||||||
Subject: gpio: realtek-otto: fix GPIO line IRQ offset
|
|
||||||
|
|
||||||
The irqchip uses one domain for all GPIO lines, so th line offset should be
|
|
||||||
determined w.r.t. the first line of the first port, not the first line of the
|
|
||||||
triggered port.
|
|
||||||
|
|
||||||
Fixes: 0d82fb1127fb ("gpio: Add Realtek Otto GPIO support")
|
|
||||||
Signed-off-by: Sander Vanheule <sander@svanheule.net>
|
|
||||||
Link: https://lore.kernel.org/linux-gpio/20211028085243.34360-1-sander@svanheule.net/
|
|
||||||
|
|
||||||
--- a/drivers/gpio/gpio-realtek-otto.c
|
|
||||||
+++ b/drivers/gpio/gpio-realtek-otto.c
|
|
||||||
@@ -206,7 +206,7 @@ static void realtek_gpio_irq_handler(str
|
|
||||||
status = realtek_gpio_read_isr(ctrl, lines_done / 8);
|
|
||||||
port_pin_count = min(gc->ngpio - lines_done, 8U);
|
|
||||||
for_each_set_bit(offset, &status, port_pin_count) {
|
|
||||||
- irq = irq_find_mapping(gc->irq.domain, offset);
|
|
||||||
+ irq = irq_find_mapping(gc->irq.domain, offset + lines_done);
|
|
||||||
generic_handle_irq(irq);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,162 +0,0 @@
|
||||||
From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001
|
|
||||||
From: Birger Koblitz <git@birger-koblitz.de>
|
|
||||||
Date: Tue, 18 Jan 2022 17:18:43 +0100
|
|
||||||
Subject: net: brflood API
|
|
||||||
|
|
||||||
Adds the DSA API for bridge configuration (flooding, L2 learning,
|
|
||||||
and aging) offload as found in Linux 5.12 so that we can implement
|
|
||||||
it in our drivver.
|
|
||||||
|
|
||||||
Submitted-by: Sebastian Gottschall <s.gottschall@dd-wrt.com>
|
|
||||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
|
||||||
---
|
|
||||||
include/net/dsa.h | 6 +++++++--
|
|
||||||
net/dsa/dsa_priv.h | 6 +++---
|
|
||||||
net/dsa/port.c | 28 ++++++++----
|
|
||||||
net/dsa/slave.c | 6 +++---
|
|
||||||
4 file changed, 29 insertions(+), 13 deletions(-)
|
|
||||||
|
|
||||||
--- a/include/net/dsa.h
|
|
||||||
+++ b/include/net/dsa.h
|
|
||||||
@@ -552,8 +552,14 @@ 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 flags,
|
|
||||||
+ struct netlink_ext_ack *extack);
|
|
||||||
+ int (*port_bridge_flags)(struct dsa_switch *ds, int port,
|
|
||||||
+ unsigned long flags,
|
|
||||||
+ struct netlink_ext_ack *extack);
|
|
||||||
+ int (*port_set_mrouter)(struct dsa_switch *ds, int port, bool mrouter,
|
|
||||||
+ struct netlink_ext_ack *extack);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* VLAN support
|
|
||||||
--- a/net/dsa/dsa_priv.h
|
|
||||||
+++ b/net/dsa/dsa_priv.h
|
|
||||||
@@ -167,11 +167,11 @@ int dsa_port_mdb_add(const struct dsa_po
|
|
||||||
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,
|
|
||||||
- struct switchdev_trans *trans);
|
|
||||||
+ struct switchdev_trans *trans, struct netlink_ext_ack *extack);
|
|
||||||
int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
|
|
||||||
- struct switchdev_trans *trans);
|
|
||||||
+ struct switchdev_trans *trans, struct netlink_ext_ack *extack);
|
|
||||||
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
|
|
||||||
- struct switchdev_trans *trans);
|
|
||||||
+ struct switchdev_trans *trans, struct netlink_ext_ack *extack);
|
|
||||||
int dsa_port_vlan_add(struct dsa_port *dp,
|
|
||||||
const struct switchdev_obj_port_vlan *vlan,
|
|
||||||
struct switchdev_trans *trans);
|
|
||||||
--- a/net/dsa/port.c
|
|
||||||
+++ b/net/dsa/port.c
|
|
||||||
@@ -145,7 +145,7 @@ int dsa_port_bridge_join(struct dsa_port
|
|
||||||
int err;
|
|
||||||
|
|
||||||
/* Set the flooding mode before joining the port in the switch */
|
|
||||||
- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL);
|
|
||||||
+ err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ int dsa_port_bridge_join(struct dsa_port
|
|
||||||
|
|
||||||
/* The bridging is rolled back on error */
|
|
||||||
if (err) {
|
|
||||||
- dsa_port_bridge_flags(dp, 0, NULL);
|
|
||||||
+ dsa_port_bridge_flags(dp, 0, NULL, NULL);
|
|
||||||
dp->bridge_dev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -185,7 +185,7 @@ void dsa_port_bridge_leave(struct dsa_po
|
|
||||||
pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
|
|
||||||
|
|
||||||
/* Port is leaving the bridge, disable flooding */
|
|
||||||
- dsa_port_bridge_flags(dp, 0, NULL);
|
|
||||||
+ dsa_port_bridge_flags(dp, 0, NULL, NULL);
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
@@ -333,44 +333,44 @@ int dsa_port_ageing_time(struct dsa_port
|
|
||||||
}
|
|
||||||
|
|
||||||
int dsa_port_pre_bridge_flags(const struct dsa_port *dp, unsigned long flags,
|
|
||||||
- struct switchdev_trans *trans)
|
|
||||||
+ struct switchdev_trans *trans, struct netlink_ext_ack *extack)
|
|
||||||
{
|
|
||||||
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, flags, extack);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dsa_port_bridge_flags(const struct dsa_port *dp, unsigned long flags,
|
|
||||||
- struct switchdev_trans *trans)
|
|
||||||
+ struct switchdev_trans *trans, struct netlink_ext_ack *extack)
|
|
||||||
{
|
|
||||||
struct dsa_switch *ds = dp->ds;
|
|
||||||
- int port = dp->index;
|
|
||||||
- int err = 0;
|
|
||||||
|
|
||||||
if (switchdev_trans_ph_prepare(trans))
|
|
||||||
return 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 ds->ops->port_bridge_flags(ds, dp->index, flags, extack);
|
|
||||||
|
|
||||||
- return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dsa_port_mrouter(struct dsa_port *dp, bool mrouter,
|
|
||||||
- struct switchdev_trans *trans)
|
|
||||||
+ struct switchdev_trans *trans,
|
|
||||||
+ struct netlink_ext_ack *extack)
|
|
||||||
{
|
|
||||||
struct dsa_switch *ds = dp->ds;
|
|
||||||
- int port = dp->index;
|
|
||||||
|
|
||||||
if (switchdev_trans_ph_prepare(trans))
|
|
||||||
- return ds->ops->port_egress_floods ? 0 : -EOPNOTSUPP;
|
|
||||||
+ return ds->ops->port_set_mrouter ? 0 : -EOPNOTSUPP;
|
|
||||||
+
|
|
||||||
+ 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, extack);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dsa_port_mtu_change(struct dsa_port *dp, int new_mtu,
|
|
||||||
--- a/net/dsa/slave.c
|
|
||||||
+++ b/net/dsa/slave.c
|
|
||||||
@@ -290,13 +290,13 @@ static int dsa_slave_port_attr_set(struc
|
|
||||||
break;
|
|
||||||
case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
|
|
||||||
ret = dsa_port_pre_bridge_flags(dp, attr->u.brport_flags,
|
|
||||||
- trans);
|
|
||||||
+ trans, NULL);
|
|
||||||
break;
|
|
||||||
case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
|
|
||||||
- ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans);
|
|
||||||
+ ret = dsa_port_bridge_flags(dp, attr->u.brport_flags, trans, NULL);
|
|
||||||
break;
|
|
||||||
case SWITCHDEV_ATTR_ID_BRIDGE_MROUTER:
|
|
||||||
- ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans);
|
|
||||||
+ ret = dsa_port_mrouter(dp->cpu_dp, attr->u.mrouter, trans, NULL);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = -EOPNOTSUPP;
|
|
|
@ -1,781 +0,0 @@
|
||||||
From afa3ab54c03d5126b14651f367b38165fab5b3cc Mon Sep 17 00:00:00 2001
|
|
||||||
From: Birger Koblitz <git@birger-koblitz.de>
|
|
||||||
Date: Tue, 18 Jan 2022 17:18:43 +0100
|
|
||||||
Subject: [PATCH] realtek: Backport bridge configuration for DSA
|
|
||||||
|
|
||||||
Adds the DSA API for bridge configuration (flooding, L2 learning,
|
|
||||||
and aging) offload as found in Linux 5.12 so that we can implement
|
|
||||||
it in our drivver.
|
|
||||||
|
|
||||||
Submitted-by: Sebastian Gottschall <s.gottschall@dd-wrt.com>
|
|
||||||
Submitted-by: Birger Koblitz <git@birger-koblitz.de>
|
|
||||||
---
|
|
||||||
drivers/net/bonding/bond_main.c | 2 ++
|
|
||||||
include/net/dsa.h | 79 ++++++++++++++++-
|
|
||||||
net/dsa/dsa2.c | 88 +++++++++++++++++++
|
|
||||||
net/dsa/dsa_priv.h | 74 ++++++++++++++
|
|
||||||
net/dsa/port.c | 92 ++++++++++++++++++++
|
|
||||||
net/dsa/slave.c | 88 ++++++++++++++++---
|
|
||||||
net/dsa/switch.c | 49 ++++++++++
|
|
||||||
net/sda/tag_dsa.c | 13 +++++-
|
|
||||||
8 file changed, 460 insertions(+), 25 deletions(-)
|
|
||||||
|
|
||||||
--- a/drivers/net/bonding/bond_main.c
|
|
||||||
+++ b/drivers/net/bonding/bond_main.c
|
|
||||||
@@ -2045,6 +2045,8 @@ int bond_enslave(struct net_device *bond
|
|
||||||
goto err_unregister;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ bond_lower_state_changed(new_slave);
|
|
||||||
+
|
|
||||||
res = bond_sysfs_slave_add(new_slave);
|
|
||||||
if (res) {
|
|
||||||
slave_dbg(bond_dev, slave_dev, "Error %d calling bond_sysfs_slave_add\n", res);
|
|
||||||
--- a/include/net/dsa.h
|
|
||||||
+++ b/include/net/dsa.h
|
|
||||||
@@ -149,8 +149,41 @@ struct dsa_switch_tree {
|
|
||||||
|
|
||||||
/* List of DSA links composing the routing table */
|
|
||||||
struct list_head rtable;
|
|
||||||
+
|
|
||||||
+ /* Maps offloaded LAG netdevs to a zero-based linear ID for
|
|
||||||
+ * drivers that need it.
|
|
||||||
+ */
|
|
||||||
+ struct net_device **lags;
|
|
||||||
+ unsigned int lags_len;
|
|
||||||
};
|
|
||||||
|
|
||||||
+#define dsa_lags_foreach_id(_id, _dst) \
|
|
||||||
+ for ((_id) = 0; (_id) < (_dst)->lags_len; (_id)++) \
|
|
||||||
+ if ((_dst)->lags[(_id)])
|
|
||||||
+
|
|
||||||
+#define dsa_lag_foreach_port(_dp, _dst, _lag) \
|
|
||||||
+ list_for_each_entry((_dp), &(_dst)->ports, list) \
|
|
||||||
+ if ((_dp)->lag_dev == (_lag))
|
|
||||||
+
|
|
||||||
+static inline struct net_device *dsa_lag_dev(struct dsa_switch_tree *dst,
|
|
||||||
+ unsigned int id)
|
|
||||||
+{
|
|
||||||
+ return dst->lags[id];
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static inline int dsa_lag_id(struct dsa_switch_tree *dst,
|
|
||||||
+ struct net_device *lag)
|
|
||||||
+{
|
|
||||||
+ unsigned int id;
|
|
||||||
+
|
|
||||||
+ dsa_lags_foreach_id(id, dst) {
|
|
||||||
+ if (dsa_lag_dev(dst, id) == lag)
|
|
||||||
+ return id;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return -ENODEV;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/* TC matchall action types */
|
|
||||||
enum dsa_port_mall_action_type {
|
|
||||||
DSA_PORT_MALL_MIRROR,
|
|
||||||
@@ -220,6 +253,8 @@ struct dsa_port {
|
|
||||||
bool devlink_port_setup;
|
|
||||||
struct phylink *pl;
|
|
||||||
struct phylink_config pl_config;
|
|
||||||
+ struct net_device *lag_dev;
|
|
||||||
+ bool lag_tx_enabled;
|
|
||||||
|
|
||||||
struct list_head list;
|
|
||||||
|
|
||||||
@@ -340,6 +375,14 @@ struct dsa_switch {
|
|
||||||
*/
|
|
||||||
bool mtu_enforcement_ingress;
|
|
||||||
|
|
||||||
+ /* Drivers that benefit from having an ID associated with each
|
|
||||||
+ * offloaded LAG should set this to the maximum number of
|
|
||||||
+ * supported IDs. DSA will then maintain a mapping of _at
|
|
||||||
+ * least_ these many IDs, accessible to drivers via
|
|
||||||
+ * dsa_lag_id().
|
|
||||||
+ */
|
|
||||||
+ unsigned int num_lag_ids;
|
|
||||||
+
|
|
||||||
size_t num_ports;
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -432,6 +475,18 @@ static inline bool dsa_port_is_vlan_filt
|
|
||||||
return dp->vlan_filtering;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static inline
|
|
||||||
+struct net_device *dsa_port_to_bridge_port(const struct dsa_port *dp)
|
|
||||||
+{
|
|
||||||
+ if (!dp->bridge_dev)
|
|
||||||
+ return NULL;
|
|
||||||
+
|
|
||||||
+ if (dp->lag_dev)
|
|
||||||
+ return dp->lag_dev;
|
|
||||||
+
|
|
||||||
+ return dp->slave;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
|
|
||||||
bool is_static, void *data);
|
|
||||||
struct dsa_switch_ops {
|
|
||||||
@@ -629,6 +684,13 @@ struct dsa_switch_ops {
|
|
||||||
void (*crosschip_bridge_leave)(struct dsa_switch *ds, int tree_index,
|
|
||||||
int sw_index, int port,
|
|
||||||
struct net_device *br);
|
|
||||||
+ int (*crosschip_lag_change)(struct dsa_switch *ds, int sw_index,
|
|
||||||
+ int port);
|
|
||||||
+ int (*crosschip_lag_join)(struct dsa_switch *ds, int sw_index,
|
|
||||||
+ int port, struct net_device *lag,
|
|
||||||
+ struct netdev_lag_upper_info *info);
|
|
||||||
+ int (*crosschip_lag_leave)(struct dsa_switch *ds, int sw_index,
|
|
||||||
+ int port, struct net_device *lag);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* PTP functionality
|
|
||||||
@@ -660,6 +722,16 @@ struct dsa_switch_ops {
|
|
||||||
int (*port_change_mtu)(struct dsa_switch *ds, int port,
|
|
||||||
int new_mtu);
|
|
||||||
int (*port_max_mtu)(struct dsa_switch *ds, int port);
|
|
||||||
+
|
|
||||||
+ /*
|
|
||||||
+ * LAG integration
|
|
||||||
+ */
|
|
||||||
+ int (*port_lag_change)(struct dsa_switch *ds, int port);
|
|
||||||
+ int (*port_lag_join)(struct dsa_switch *ds, int port,
|
|
||||||
+ struct net_device *lag,
|
|
||||||
+ struct netdev_lag_upper_info *info);
|
|
||||||
+ int (*port_lag_leave)(struct dsa_switch *ds, int port,
|
|
||||||
+ struct net_device *lag);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
|
|
||||||
--- a/net/dsa/dsa.c
|
|
||||||
+++ b/net/dsa/dsa.c
|
|
||||||
@@ -220,11 +220,21 @@ static int dsa_switch_rcv(struct sk_buff
|
|
||||||
}
|
|
||||||
|
|
||||||
skb = nskb;
|
|
||||||
- p = netdev_priv(skb->dev);
|
|
||||||
skb_push(skb, ETH_HLEN);
|
|
||||||
skb->pkt_type = PACKET_HOST;
|
|
||||||
skb->protocol = eth_type_trans(skb, skb->dev);
|
|
||||||
|
|
||||||
+ if (unlikely(!dsa_slave_dev_check(skb->dev))) {
|
|
||||||
+ /* Packet is to be injected directly on an upper
|
|
||||||
+ * device, e.g. a team/bond, so skip all DSA-port
|
|
||||||
+ * specific actions.
|
|
||||||
+ */
|
|
||||||
+ netif_rx(skb);
|
|
||||||
+ return 0;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ p = netdev_priv(skb->dev);
|
|
||||||
+
|
|
||||||
if (unlikely(cpu_dp->ds->untag_bridge_pvid)) {
|
|
||||||
nskb = dsa_untag_bridge_pvid(skb);
|
|
||||||
if (!nskb) {
|
|
||||||
--- a/net/dsa/dsa2.c
|
|
||||||
+++ b/net/dsa/dsa2.c
|
|
||||||
@@ -21,6 +21,65 @@
|
|
||||||
static DEFINE_MUTEX(dsa2_mutex);
|
|
||||||
LIST_HEAD(dsa_tree_list);
|
|
||||||
|
|
||||||
+/**
|
|
||||||
+ * dsa_lag_map() - Map LAG netdev to a linear LAG ID
|
|
||||||
+ * @dst: Tree in which to record the mapping.
|
|
||||||
+ * @lag: Netdev that is to be mapped to an ID.
|
|
||||||
+ *
|
|
||||||
+ * dsa_lag_id/dsa_lag_dev can then be used to translate between the
|
|
||||||
+ * two spaces. The size of the mapping space is determined by the
|
|
||||||
+ * driver by setting ds->num_lag_ids. It is perfectly legal to leave
|
|
||||||
+ * it unset if it is not needed, in which case these functions become
|
|
||||||
+ * no-ops.
|
|
||||||
+ */
|
|
||||||
+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag)
|
|
||||||
+{
|
|
||||||
+ unsigned int id;
|
|
||||||
+
|
|
||||||
+ if (dsa_lag_id(dst, lag) >= 0)
|
|
||||||
+ /* Already mapped */
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ for (id = 0; id < dst->lags_len; id++) {
|
|
||||||
+ if (!dsa_lag_dev(dst, id)) {
|
|
||||||
+ dst->lags[id] = lag;
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* No IDs left, which is OK. Some drivers do not need it. The
|
|
||||||
+ * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id
|
|
||||||
+ * returns an error for this device when joining the LAG. The
|
|
||||||
+ * driver can then return -EOPNOTSUPP back to DSA, which will
|
|
||||||
+ * fall back to a software LAG.
|
|
||||||
+ */
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/**
|
|
||||||
+ * dsa_lag_unmap() - Remove a LAG ID mapping
|
|
||||||
+ * @dst: Tree in which the mapping is recorded.
|
|
||||||
+ * @lag: Netdev that was mapped.
|
|
||||||
+ *
|
|
||||||
+ * As there may be multiple users of the mapping, it is only removed
|
|
||||||
+ * if there are no other references to it.
|
|
||||||
+ */
|
|
||||||
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag)
|
|
||||||
+{
|
|
||||||
+ struct dsa_port *dp;
|
|
||||||
+ unsigned int id;
|
|
||||||
+
|
|
||||||
+ dsa_lag_foreach_port(dp, dst, lag)
|
|
||||||
+ /* There are remaining users of this mapping */
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ dsa_lags_foreach_id(id, dst) {
|
|
||||||
+ if (dsa_lag_dev(dst, id) == lag) {
|
|
||||||
+ dst->lags[id] = NULL;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
struct dsa_switch *dsa_switch_find(int tree_index, int sw_index)
|
|
||||||
{
|
|
||||||
struct dsa_switch_tree *dst;
|
|
||||||
@@ -597,6 +656,32 @@ static void dsa_tree_teardown_master(str
|
|
||||||
dsa_master_teardown(dp->master);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int dsa_tree_setup_lags(struct dsa_switch_tree *dst)
|
|
||||||
+{
|
|
||||||
+ unsigned int len = 0;
|
|
||||||
+ struct dsa_port *dp;
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(dp, &dst->ports, list) {
|
|
||||||
+ if (dp->ds->num_lag_ids > len)
|
|
||||||
+ len = dp->ds->num_lag_ids;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!len)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL);
|
|
||||||
+ if (!dst->lags)
|
|
||||||
+ return -ENOMEM;
|
|
||||||
+
|
|
||||||
+ dst->lags_len = len;
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst)
|
|
||||||
+{
|
|
||||||
+ kfree(dst->lags);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int dsa_tree_setup(struct dsa_switch_tree *dst)
|
|
||||||
{
|
|
||||||
bool complete;
|
|
||||||
@@ -624,12 +709,18 @@ static int dsa_tree_setup(struct dsa_swi
|
|
||||||
if (err)
|
|
||||||
goto teardown_switches;
|
|
||||||
|
|
||||||
+ err = dsa_tree_setup_lags(dst);
|
|
||||||
+ if (err)
|
|
||||||
+ goto teardown_master;
|
|
||||||
+
|
|
||||||
dst->setup = true;
|
|
||||||
|
|
||||||
pr_info("DSA: tree %d setup\n", dst->index);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
+teardown_master:
|
|
||||||
+ dsa_tree_teardown_master(dst);
|
|
||||||
teardown_switches:
|
|
||||||
dsa_tree_teardown_switches(dst);
|
|
||||||
teardown_default_cpu:
|
|
||||||
@@ -645,6 +736,8 @@ static void dsa_tree_teardown(struct dsa
|
|
||||||
if (!dst->setup)
|
|
||||||
return;
|
|
||||||
|
|
||||||
+ dsa_tree_teardown_lags(dst);
|
|
||||||
+
|
|
||||||
dsa_tree_teardown_master(dst);
|
|
||||||
|
|
||||||
dsa_tree_teardown_switches(dst);
|
|
||||||
--- a/net/dsa/dsa_priv.h
|
|
||||||
+++ b/net/dsa/dsa_priv.h
|
|
||||||
@@ -20,6 +20,9 @@ enum {
|
|
||||||
DSA_NOTIFIER_BRIDGE_LEAVE,
|
|
||||||
DSA_NOTIFIER_FDB_ADD,
|
|
||||||
DSA_NOTIFIER_FDB_DEL,
|
|
||||||
+ DSA_NOTIFIER_LAG_CHANGE,
|
|
||||||
+ DSA_NOTIFIER_LAG_JOIN,
|
|
||||||
+ DSA_NOTIFIER_LAG_LEAVE,
|
|
||||||
DSA_NOTIFIER_MDB_ADD,
|
|
||||||
DSA_NOTIFIER_MDB_DEL,
|
|
||||||
DSA_NOTIFIER_VLAN_ADD,
|
|
||||||
@@ -57,6 +60,15 @@ struct dsa_notifier_mdb_info {
|
|
||||||
int port;
|
|
||||||
};
|
|
||||||
|
|
||||||
+/* DSA_NOTIFIER_LAG_* */
|
|
||||||
+struct dsa_notifier_lag_info {
|
|
||||||
+ struct net_device *lag;
|
|
||||||
+ int sw_index;
|
|
||||||
+ int port;
|
|
||||||
+
|
|
||||||
+ struct netdev_lag_upper_info *info;
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
/* DSA_NOTIFIER_VLAN_* */
|
|
||||||
struct dsa_notifier_vlan_info {
|
|
||||||
const struct switchdev_obj_port_vlan *vlan;
|
|
||||||
@@ -149,6 +161,11 @@ void dsa_port_disable_rt(struct dsa_port
|
|
||||||
void dsa_port_disable(struct dsa_port *dp);
|
|
||||||
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br);
|
|
||||||
void dsa_port_bridge_leave(struct dsa_port *dp, struct net_device *br);
|
|
||||||
+int dsa_port_lag_change(struct dsa_port *dp,
|
|
||||||
+ struct netdev_lag_lower_state_info *linfo);
|
|
||||||
+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag_dev,
|
|
||||||
+ struct netdev_lag_upper_info *uinfo);
|
|
||||||
+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag_dev);
|
|
||||||
int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering,
|
|
||||||
struct switchdev_trans *trans);
|
|
||||||
bool dsa_port_skip_vlan_configuration(struct dsa_port *dp);
|
|
||||||
@@ -181,6 +198,71 @@ int dsa_port_link_register_of(struct dsa
|
|
||||||
void dsa_port_link_unregister_of(struct dsa_port *dp);
|
|
||||||
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)
|
|
||||||
+{
|
|
||||||
+ /* Switchdev offloading can be configured on: */
|
|
||||||
+
|
|
||||||
+ if (dev == dp->slave)
|
|
||||||
+ /* DSA ports directly connected to a bridge, and event
|
|
||||||
+ * was emitted for the ports themselves.
|
|
||||||
+ */
|
|
||||||
+ 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;
|
|
||||||
+
|
|
||||||
+ return false;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static inline bool dsa_port_offloads_bridge_port(struct dsa_port *dp,
|
|
||||||
+ struct net_device *dev)
|
|
||||||
+{
|
|
||||||
+ return dsa_port_to_bridge_port(dp) == dev;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+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_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_bridge_port(dp, dev))
|
|
||||||
+ return true;
|
|
||||||
+
|
|
||||||
+ return false;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* 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)
|
|
||||||
+{
|
|
||||||
+ struct dsa_port *dp;
|
|
||||||
+
|
|
||||||
+ list_for_each_entry(dp, &dst->ports, list)
|
|
||||||
+ if (dsa_port_offloads_netdev(dp, dev))
|
|
||||||
+ return true;
|
|
||||||
+
|
|
||||||
+ return false;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/* slave.c */
|
|
||||||
extern const struct dsa_device_ops notag_netdev_ops;
|
|
||||||
void dsa_slave_mii_bus_init(struct dsa_switch *ds);
|
|
||||||
@@ -285,6 +367,9 @@ int dsa_switch_register_notifier(struct
|
|
||||||
void dsa_switch_unregister_notifier(struct dsa_switch *ds);
|
|
||||||
|
|
||||||
/* dsa2.c */
|
|
||||||
+void dsa_lag_map(struct dsa_switch_tree *dst, struct net_device *lag);
|
|
||||||
+void dsa_lag_unmap(struct dsa_switch_tree *dst, struct net_device *lag);
|
|
||||||
+
|
|
||||||
extern struct list_head dsa_tree_list;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
--- a/net/dsa/port.c
|
|
||||||
+++ b/net/dsa/port.c
|
|
||||||
@@ -193,6 +193,99 @@ void dsa_port_bridge_leave(struct dsa_po
|
|
||||||
dsa_port_set_state_now(dp, BR_STATE_FORWARDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
+int dsa_port_lag_change(struct dsa_port *dp,
|
|
||||||
+ struct netdev_lag_lower_state_info *linfo)
|
|
||||||
+{
|
|
||||||
+ struct dsa_notifier_lag_info info = {
|
|
||||||
+ .sw_index = dp->ds->index,
|
|
||||||
+ .port = dp->index,
|
|
||||||
+ };
|
|
||||||
+ bool tx_enabled;
|
|
||||||
+
|
|
||||||
+ if (!dp->lag_dev)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ /* On statically configured aggregates (e.g. loadbalance
|
|
||||||
+ * without LACP) ports will always be tx_enabled, even if the
|
|
||||||
+ * link is down. Thus we require both link_up and tx_enabled
|
|
||||||
+ * in order to include it in the tx set.
|
|
||||||
+ */
|
|
||||||
+ tx_enabled = linfo->link_up && linfo->tx_enabled;
|
|
||||||
+
|
|
||||||
+ if (tx_enabled == dp->lag_tx_enabled)
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ dp->lag_tx_enabled = tx_enabled;
|
|
||||||
+
|
|
||||||
+ return dsa_port_notify(dp, DSA_NOTIFIER_LAG_CHANGE, &info);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+int dsa_port_lag_join(struct dsa_port *dp, struct net_device *lag,
|
|
||||||
+ struct netdev_lag_upper_info *uinfo)
|
|
||||||
+{
|
|
||||||
+ struct dsa_notifier_lag_info info = {
|
|
||||||
+ .sw_index = dp->ds->index,
|
|
||||||
+ .port = dp->index,
|
|
||||||
+ .lag = lag,
|
|
||||||
+ .info = uinfo,
|
|
||||||
+ };
|
|
||||||
+ struct net_device *bridge_dev;
|
|
||||||
+ int err;
|
|
||||||
+
|
|
||||||
+ dsa_lag_map(dp->ds->dst, lag);
|
|
||||||
+ dp->lag_dev = lag;
|
|
||||||
+
|
|
||||||
+ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_JOIN, &info);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err_lag_join;
|
|
||||||
+
|
|
||||||
+ bridge_dev = netdev_master_upper_dev_get(lag);
|
|
||||||
+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+ err = dsa_port_bridge_join(dp, bridge_dev);
|
|
||||||
+ if (err)
|
|
||||||
+ goto err_bridge_join;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+
|
|
||||||
+err_bridge_join:
|
|
||||||
+ dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
|
|
||||||
+err_lag_join:
|
|
||||||
+ dp->lag_dev = NULL;
|
|
||||||
+ dsa_lag_unmap(dp->ds->dst, lag);
|
|
||||||
+ return err;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+void dsa_port_lag_leave(struct dsa_port *dp, struct net_device *lag)
|
|
||||||
+{
|
|
||||||
+ struct dsa_notifier_lag_info info = {
|
|
||||||
+ .sw_index = dp->ds->index,
|
|
||||||
+ .port = dp->index,
|
|
||||||
+ .lag = lag,
|
|
||||||
+ };
|
|
||||||
+ int err;
|
|
||||||
+
|
|
||||||
+ if (!dp->lag_dev)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ /* Port might have been part of a LAG that in turn was
|
|
||||||
+ * attached to a bridge.
|
|
||||||
+ */
|
|
||||||
+ if (dp->bridge_dev)
|
|
||||||
+ dsa_port_bridge_leave(dp, dp->bridge_dev);
|
|
||||||
+
|
|
||||||
+ dp->lag_tx_enabled = false;
|
|
||||||
+ dp->lag_dev = NULL;
|
|
||||||
+
|
|
||||||
+ err = dsa_port_notify(dp, DSA_NOTIFIER_LAG_LEAVE, &info);
|
|
||||||
+ if (err)
|
|
||||||
+ pr_err("DSA: failed to notify DSA_NOTIFIER_LAG_LEAVE: %d\n",
|
|
||||||
+ err);
|
|
||||||
+
|
|
||||||
+ dsa_lag_unmap(dp->ds->dst, lag);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
/* Must be called under rcu_read_lock() */
|
|
||||||
static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp,
|
|
||||||
bool vlan_filtering)
|
|
||||||
--- a/net/dsa/slave.c
|
|
||||||
+++ b/net/dsa/slave.c
|
|
||||||
@@ -337,9 +337,6 @@ static int dsa_slave_vlan_add(struct net
|
|
||||||
struct switchdev_obj_port_vlan vlan;
|
|
||||||
int vid, err;
|
|
||||||
|
|
||||||
- if (obj->orig_dev != dev)
|
|
||||||
- return -EOPNOTSUPP;
|
|
||||||
-
|
|
||||||
if (dsa_port_skip_vlan_configuration(dp))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
@@ -394,11 +391,13 @@ static int dsa_slave_port_obj_add(struct
|
|
||||||
|
|
||||||
switch (obj->id) {
|
|
||||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
|
||||||
- if (obj->orig_dev != dev)
|
|
||||||
+ if (!dsa_port_offloads_bridge_port(dp, obj->orig_dev))
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans);
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
@@ -406,6 +405,9 @@ static int dsa_slave_port_obj_add(struct
|
|
||||||
trans);
|
|
||||||
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, trans);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
@@ -424,9 +426,6 @@ static int dsa_slave_vlan_del(struct net
|
|
||||||
struct switchdev_obj_port_vlan *vlan;
|
|
||||||
int vid, err;
|
|
||||||
|
|
||||||
- if (obj->orig_dev != dev)
|
|
||||||
- return -EOPNOTSUPP;
|
|
||||||
-
|
|
||||||
if (dsa_port_skip_vlan_configuration(dp))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
@@ -453,17 +452,22 @@ static int dsa_slave_port_obj_del(struct
|
|
||||||
|
|
||||||
switch (obj->id) {
|
|
||||||
case SWITCHDEV_OBJ_ID_PORT_MDB:
|
|
||||||
- if (obj->orig_dev != 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;
|
|
||||||
default:
|
|
||||||
@@ -1993,6 +1997,46 @@ static int dsa_slave_changeupper(struct
|
|
||||||
dsa_port_bridge_leave(dp, info->upper_dev);
|
|
||||||
err = NOTIFY_OK;
|
|
||||||
}
|
|
||||||
+ } else if (netif_is_lag_master(info->upper_dev)) {
|
|
||||||
+ if (info->linking) {
|
|
||||||
+ err = dsa_port_lag_join(dp, info->upper_dev,
|
|
||||||
+ info->upper_info);
|
|
||||||
+ if (err == -EOPNOTSUPP) {
|
|
||||||
+ NL_SET_ERR_MSG_MOD(info->info.extack,
|
|
||||||
+ "Offloading not supported");
|
|
||||||
+ err = 0;
|
|
||||||
+ }
|
|
||||||
+ err = notifier_from_errno(err);
|
|
||||||
+ } else {
|
|
||||||
+ dsa_port_lag_leave(dp, info->upper_dev);
|
|
||||||
+ err = NOTIFY_OK;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return err;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int
|
|
||||||
+dsa_slave_lag_changeupper(struct net_device *dev,
|
|
||||||
+ struct netdev_notifier_changeupper_info *info)
|
|
||||||
+{
|
|
||||||
+ struct net_device *lower;
|
|
||||||
+ struct list_head *iter;
|
|
||||||
+ int err = NOTIFY_DONE;
|
|
||||||
+ struct dsa_port *dp;
|
|
||||||
+
|
|
||||||
+ netdev_for_each_lower_dev(dev, lower, iter) {
|
|
||||||
+ if (!dsa_slave_dev_check(lower))
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ dp = dsa_slave_to_port(lower);
|
|
||||||
+ if (!dp->lag_dev)
|
|
||||||
+ /* Software LAG */
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ err = dsa_slave_changeupper(lower, info);
|
|
||||||
+ if (notifier_to_errno(err))
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return err;
|
|
||||||
@@ -2078,10 +2122,26 @@ static int dsa_slave_netdevice_event(str
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NETDEV_CHANGEUPPER:
|
|
||||||
+ if (dsa_slave_dev_check(dev))
|
|
||||||
+ return dsa_slave_changeupper(dev, ptr);
|
|
||||||
+
|
|
||||||
+ if (netif_is_lag_master(dev))
|
|
||||||
+ return dsa_slave_lag_changeupper(dev, ptr);
|
|
||||||
+
|
|
||||||
+ break;
|
|
||||||
+ case NETDEV_CHANGELOWERSTATE: {
|
|
||||||
+ struct netdev_notifier_changelowerstate_info *info = ptr;
|
|
||||||
+ struct dsa_port *dp;
|
|
||||||
+ int err;
|
|
||||||
+
|
|
||||||
if (!dsa_slave_dev_check(dev))
|
|
||||||
- return NOTIFY_DONE;
|
|
||||||
+ break;
|
|
||||||
|
|
||||||
- return dsa_slave_changeupper(dev, ptr);
|
|
||||||
+ dp = dsa_slave_to_port(dev);
|
|
||||||
+
|
|
||||||
+ err = dsa_port_lag_change(dp, info->lower_state_info);
|
|
||||||
+ return notifier_from_errno(err);
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
@@ -2229,6 +2289,15 @@ static int dsa_slave_switchdev_event(str
|
|
||||||
if (!fdb_info->added_by_user &&
|
|
||||||
!dp->ds->assisted_learning_on_cpu_port)
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
+
|
|
||||||
+ /* When the bridge learns an address on an offloaded
|
|
||||||
+ * LAG we don't want to send traffic to the CPU, the
|
|
||||||
+ * other ports bridged with the LAG should be able to
|
|
||||||
+ * autonomously forward towards it.
|
|
||||||
+ */
|
|
||||||
+ if (dsa_tree_offloads_netdev(dp->ds->dst, dev))
|
|
||||||
+ return NOTIFY_DONE;
|
|
||||||
+
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del)
|
|
||||||
--- a/net/dsa/switch.c
|
|
||||||
+++ b/net/dsa/switch.c
|
|
||||||
@@ -193,6 +193,47 @@ static int dsa_switch_fdb_del(struct dsa
|
|
||||||
return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
|
|
||||||
}
|
|
||||||
|
|
||||||
+static int dsa_switch_lag_change(struct dsa_switch *ds,
|
|
||||||
+ struct dsa_notifier_lag_info *info)
|
|
||||||
+{
|
|
||||||
+ if (ds->index == info->sw_index && ds->ops->port_lag_change)
|
|
||||||
+ return ds->ops->port_lag_change(ds, info->port);
|
|
||||||
+
|
|
||||||
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_change)
|
|
||||||
+ return ds->ops->crosschip_lag_change(ds, info->sw_index,
|
|
||||||
+ info->port);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int dsa_switch_lag_join(struct dsa_switch *ds,
|
|
||||||
+ struct dsa_notifier_lag_info *info)
|
|
||||||
+{
|
|
||||||
+ if (ds->index == info->sw_index && ds->ops->port_lag_join)
|
|
||||||
+ return ds->ops->port_lag_join(ds, info->port, info->lag,
|
|
||||||
+ info->info);
|
|
||||||
+
|
|
||||||
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_join)
|
|
||||||
+ return ds->ops->crosschip_lag_join(ds, info->sw_index,
|
|
||||||
+ info->port, info->lag,
|
|
||||||
+ info->info);
|
|
||||||
+
|
|
||||||
+ return -EOPNOTSUPP;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static int dsa_switch_lag_leave(struct dsa_switch *ds,
|
|
||||||
+ struct dsa_notifier_lag_info *info)
|
|
||||||
+{
|
|
||||||
+ if (ds->index == info->sw_index && ds->ops->port_lag_leave)
|
|
||||||
+ return ds->ops->port_lag_leave(ds, info->port, info->lag);
|
|
||||||
+
|
|
||||||
+ if (ds->index != info->sw_index && ds->ops->crosschip_lag_leave)
|
|
||||||
+ return ds->ops->crosschip_lag_leave(ds, info->sw_index,
|
|
||||||
+ info->port, info->lag);
|
|
||||||
+
|
|
||||||
+ return -EOPNOTSUPP;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static bool dsa_switch_mdb_match(struct dsa_switch *ds, int port,
|
|
||||||
struct dsa_notifier_mdb_info *info)
|
|
||||||
{
|
|
||||||
@@ -340,6 +381,15 @@ static int dsa_switch_event(struct notif
|
|
||||||
case DSA_NOTIFIER_FDB_DEL:
|
|
||||||
err = dsa_switch_fdb_del(ds, info);
|
|
||||||
break;
|
|
||||||
+ case DSA_NOTIFIER_LAG_CHANGE:
|
|
||||||
+ err = dsa_switch_lag_change(ds, info);
|
|
||||||
+ break;
|
|
||||||
+ case DSA_NOTIFIER_LAG_JOIN:
|
|
||||||
+ err = dsa_switch_lag_join(ds, info);
|
|
||||||
+ break;
|
|
||||||
+ case DSA_NOTIFIER_LAG_LEAVE:
|
|
||||||
+ err = dsa_switch_lag_leave(ds, info);
|
|
||||||
+ break;
|
|
||||||
case DSA_NOTIFIER_MDB_ADD:
|
|
||||||
err = dsa_switch_mdb_add(ds, info);
|
|
||||||
break;
|
|
||||||
--- a/net/dsa/tag_dsa.c
|
|
||||||
+++ b/net/dsa/tag_dsa.c
|
|
||||||
@@ -82,7 +82,19 @@ static struct sk_buff *dsa_rcv(struct sk
|
|
||||||
source_device = dsa_header[0] & 0x1f;
|
|
||||||
source_port = (dsa_header[1] >> 3) & 0x1f;
|
|
||||||
|
|
||||||
- skb->dev = dsa_master_find_slave(dev, source_device, source_port);
|
|
||||||
+ if (trunk) {
|
|
||||||
+ struct dsa_port *cpu_dp = dev->dsa_ptr;
|
|
||||||
+
|
|
||||||
+ /* The exact source port is not available in the tag,
|
|
||||||
+ * so we inject the frame directly on the upper
|
|
||||||
+ * team/bond.
|
|
||||||
+ */
|
|
||||||
+ skb->dev = dsa_lag_dev(cpu_dp->dst, source_port);
|
|
||||||
+ } else {
|
|
||||||
+ skb->dev = dsa_master_find_slave(dev, source_device,
|
|
||||||
+ source_port);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (!skb->dev)
|
|
||||||
return NULL;
|
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
||||||
Date: Fri, 12 Feb 2021 17:15:54 +0200
|
|
||||||
Subject: [PATCH] net: dsa: configure better brport flags when ports leave the
|
|
||||||
bridge
|
|
||||||
|
|
||||||
Bugfixed version of upstream commit 5e38c15856e9 ("net: dsa: configure
|
|
||||||
better brport flags when ports leave the bridge")
|
|
||||||
|
|
||||||
For a DSA switch 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 switch 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 DSA drivers operating in standalone mode should still configure a
|
|
||||||
list of bridge port flags even when they are standalone. Currently DSA
|
|
||||||
attempts to call dsa_port_bridge_flags with 0, which disables egress
|
|
||||||
flooding of unknown unicast and multicast, something which doesn't make
|
|
||||||
much sense. For the switches that implement .port_egress_floods - b53
|
|
||||||
and mv88e6xxx, it probably doesn't matter too much either, since they
|
|
||||||
can possibly inject traffic from the CPU into a standalone port,
|
|
||||||
regardless of MAC DA, even if egress flooding is turned off for that
|
|
||||||
port, but certainly not all DSA switches can do that - sja1105, for
|
|
||||||
example, can't. So it makes sense to use a better common default there,
|
|
||||||
such as "flood everything".
|
|
||||||
|
|
||||||
It should also be noted that what DSA calls "dsa_port_bridge_flags()"
|
|
||||||
is a degenerate name for just calling .port_egress_floods(), since
|
|
||||||
nothing else is implemented - not learning, in particular. But disabling
|
|
||||||
address learning, something that this driver is also coding up for, will
|
|
||||||
be supported by individual drivers once .port_egress_floods is replaced
|
|
||||||
with a more generic .port_bridge_flags.
|
|
||||||
|
|
||||||
Previous attempts to code up this logic have been in the common bridge
|
|
||||||
layer, but as pointed out by Ido Schimmel, there are corner cases that
|
|
||||||
are missed when doing that:
|
|
||||||
https://patchwork.kernel.org/project/netdevbpf/patch/20210209151936.97382-5-olteanv@gmail.com/
|
|
||||||
|
|
||||||
So, at least for now, let's leave DSA in charge of setting port flags
|
|
||||||
before and after the bridge join and leave.
|
|
||||||
|
|
||||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
||||||
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
|
|
||||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
||||||
[ backport and bugfix: break dsa_port_bridge_flags() out of loop ]
|
|
||||||
Signed-off-by: Bjørn Mork <bjorn@mork.no>
|
|
||||||
---
|
|
||||||
net/dsa/port.c | 45 ++++++++++++++++++++++++++++++++++++++-------
|
|
||||||
1 file changed, 38 insertions(+), 7 deletions(-)
|
|
||||||
|
|
||||||
--- a/net/dsa/port.c
|
|
||||||
+++ b/net/dsa/port.c
|
|
||||||
@@ -134,6 +134,27 @@ void dsa_port_disable(struct dsa_port *d
|
|
||||||
rtnl_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
+static void dsa_port_change_brport_flags(struct dsa_port *dp,
|
|
||||||
+ bool bridge_offload)
|
|
||||||
+{
|
|
||||||
+ unsigned long mask, flags;
|
|
||||||
+ int flag, err;
|
|
||||||
+
|
|
||||||
+ mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
|
|
||||||
+ if (bridge_offload)
|
|
||||||
+ flags = mask;
|
|
||||||
+ else
|
|
||||||
+ flags = mask & ~BR_LEARNING;
|
|
||||||
+
|
|
||||||
+ for_each_set_bit(flag, &mask, 32) {
|
|
||||||
+ err = dsa_port_pre_bridge_flags(dp, BIT(flag), NULL, NULL);
|
|
||||||
+ if (err)
|
|
||||||
+ flags &= ~BIT(flag);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ dsa_port_bridge_flags(dp, flags, NULL, NULL);
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
|
|
||||||
{
|
|
||||||
struct dsa_notifier_bridge_info info = {
|
|
||||||
@@ -144,10 +165,10 @@ int dsa_port_bridge_join(struct dsa_port
|
|
||||||
};
|
|
||||||
int err;
|
|
||||||
|
|
||||||
- /* Set the flooding mode before joining the port in the switch */
|
|
||||||
- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL);
|
|
||||||
- if (err)
|
|
||||||
- return err;
|
|
||||||
+ /* Notify the port driver to set its configurable flags in a way that
|
|
||||||
+ * matches the initial settings of a bridge port.
|
|
||||||
+ */
|
|
||||||
+ dsa_port_change_brport_flags(dp, true);
|
|
||||||
|
|
||||||
/* Here the interface is already bridged. Reflect the current
|
|
||||||
* configuration so that drivers can program their chips accordingly.
|
|
||||||
@@ -158,7 +179,7 @@ int dsa_port_bridge_join(struct dsa_port
|
|
||||||
|
|
||||||
/* The bridging is rolled back on error */
|
|
||||||
if (err) {
|
|
||||||
- dsa_port_bridge_flags(dp, 0, NULL, NULL);
|
|
||||||
+ dsa_port_change_brport_flags(dp, false);
|
|
||||||
dp->bridge_dev = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -184,8 +205,18 @@ void dsa_port_bridge_leave(struct dsa_po
|
|
||||||
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, NULL, NULL);
|
|
||||||
+ /* Configure the port for standalone mode (no address learning,
|
|
||||||
+ * flood everything).
|
|
||||||
+ * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events
|
|
||||||
+ * when the user requests it through netlink or sysfs, but not
|
|
||||||
+ * automatically at port join or leave, so we need to handle resetting
|
|
||||||
+ * the brport flags ourselves. But we even prefer it that way, because
|
|
||||||
+ * otherwise, some setups might never get the notification they need,
|
|
||||||
+ * for example, when a port leaves a LAG that offloads the bridge,
|
|
||||||
+ * it becomes standalone, but as far as the bridge is concerned, no
|
|
||||||
+ * port ever left.
|
|
||||||
+ */
|
|
||||||
+ dsa_port_change_brport_flags(dp, false);
|
|
||||||
|
|
||||||
/* 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
|
|
Loading…
Reference in a new issue