mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 14:13:16 +00:00
39a7c8d809
Use 'make target/linux/refresh' to refresh backport patches. Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
255 lines
7.8 KiB
Diff
255 lines
7.8 KiB
Diff
From 295ab96f478d0fa56393e85406f19a867e26ce22 Mon Sep 17 00:00:00 2001
|
|
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
Date: Wed, 2 Feb 2022 01:03:20 +0100
|
|
Subject: [PATCH 01/16] net: dsa: provide switch operations for tracking the
|
|
master state
|
|
|
|
Certain drivers may need to send management traffic to the switch for
|
|
things like register access, FDB dump, etc, to accelerate what their
|
|
slow bus (SPI, I2C, MDIO) can already do.
|
|
|
|
Ethernet is faster (especially in bulk transactions) but is also more
|
|
unreliable, since the user may decide to bring the DSA master down (or
|
|
not bring it up), therefore severing the link between the host and the
|
|
attached switch.
|
|
|
|
Drivers needing Ethernet-based register access already should have
|
|
fallback logic to the slow bus if the Ethernet method fails, but that
|
|
fallback may be based on a timeout, and the I/O to the switch may slow
|
|
down to a halt if the master is down, because every Ethernet packet will
|
|
have to time out. The driver also doesn't have the option to turn off
|
|
Ethernet-based I/O momentarily, because it wouldn't know when to turn it
|
|
back on.
|
|
|
|
Which is where this change comes in. By tracking NETDEV_CHANGE,
|
|
NETDEV_UP and NETDEV_GOING_DOWN events on the DSA master, we should know
|
|
the exact interval of time during which this interface is reliably
|
|
available for traffic. Provide this information to switches so they can
|
|
use it as they wish.
|
|
|
|
An helper is added dsa_port_master_is_operational() to check if a master
|
|
port is operational.
|
|
|
|
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
|
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
|
|
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
|
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
|
---
|
|
include/net/dsa.h | 17 +++++++++++++++++
|
|
net/dsa/dsa2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
net/dsa/dsa_priv.h | 13 +++++++++++++
|
|
net/dsa/slave.c | 32 ++++++++++++++++++++++++++++++++
|
|
net/dsa/switch.c | 15 +++++++++++++++
|
|
5 files changed, 123 insertions(+)
|
|
|
|
--- a/include/net/dsa.h
|
|
+++ b/include/net/dsa.h
|
|
@@ -291,6 +291,10 @@ struct dsa_port {
|
|
struct list_head mdbs;
|
|
|
|
bool setup;
|
|
+ /* Master state bits, valid only on CPU ports */
|
|
+ u8 master_admin_up:1;
|
|
+ u8 master_oper_up:1;
|
|
+
|
|
};
|
|
|
|
/* TODO: ideally DSA ports would have a single dp->link_dp member,
|
|
@@ -456,6 +460,12 @@ static inline bool dsa_port_is_unused(st
|
|
return dp->type == DSA_PORT_TYPE_UNUSED;
|
|
}
|
|
|
|
+static inline bool dsa_port_master_is_operational(struct dsa_port *dp)
|
|
+{
|
|
+ return dsa_port_is_cpu(dp) && dp->master_admin_up &&
|
|
+ dp->master_oper_up;
|
|
+}
|
|
+
|
|
static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
|
|
{
|
|
return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
|
|
@@ -916,6 +926,13 @@ struct dsa_switch_ops {
|
|
int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
|
|
u16 flags);
|
|
int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
|
|
+
|
|
+ /*
|
|
+ * DSA master tracking operations
|
|
+ */
|
|
+ void (*master_state_change)(struct dsa_switch *ds,
|
|
+ const struct net_device *master,
|
|
+ bool operational);
|
|
};
|
|
|
|
#define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
|
|
--- a/net/dsa/dsa2.c
|
|
+++ b/net/dsa/dsa2.c
|
|
@@ -1275,6 +1275,52 @@ out_unlock:
|
|
return err;
|
|
}
|
|
|
|
+static void dsa_tree_master_state_change(struct dsa_switch_tree *dst,
|
|
+ struct net_device *master)
|
|
+{
|
|
+ struct dsa_notifier_master_state_info info;
|
|
+ struct dsa_port *cpu_dp = master->dsa_ptr;
|
|
+
|
|
+ info.master = master;
|
|
+ info.operational = dsa_port_master_is_operational(cpu_dp);
|
|
+
|
|
+ dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info);
|
|
+}
|
|
+
|
|
+void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
|
|
+ struct net_device *master,
|
|
+ bool up)
|
|
+{
|
|
+ struct dsa_port *cpu_dp = master->dsa_ptr;
|
|
+ bool notify = false;
|
|
+
|
|
+ if ((dsa_port_master_is_operational(cpu_dp)) !=
|
|
+ (up && cpu_dp->master_oper_up))
|
|
+ notify = true;
|
|
+
|
|
+ cpu_dp->master_admin_up = up;
|
|
+
|
|
+ if (notify)
|
|
+ dsa_tree_master_state_change(dst, master);
|
|
+}
|
|
+
|
|
+void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
|
|
+ struct net_device *master,
|
|
+ bool up)
|
|
+{
|
|
+ struct dsa_port *cpu_dp = master->dsa_ptr;
|
|
+ bool notify = false;
|
|
+
|
|
+ if ((dsa_port_master_is_operational(cpu_dp)) !=
|
|
+ (cpu_dp->master_admin_up && up))
|
|
+ notify = true;
|
|
+
|
|
+ cpu_dp->master_oper_up = up;
|
|
+
|
|
+ if (notify)
|
|
+ dsa_tree_master_state_change(dst, master);
|
|
+}
|
|
+
|
|
static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
|
|
{
|
|
struct dsa_switch_tree *dst = ds->dst;
|
|
--- a/net/dsa/dsa_priv.h
|
|
+++ b/net/dsa/dsa_priv.h
|
|
@@ -45,6 +45,7 @@ enum {
|
|
DSA_NOTIFIER_MRP_DEL_RING_ROLE,
|
|
DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
|
|
DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
|
|
+ DSA_NOTIFIER_MASTER_STATE_CHANGE,
|
|
};
|
|
|
|
/* DSA_NOTIFIER_AGEING_TIME */
|
|
@@ -127,6 +128,12 @@ struct dsa_notifier_tag_8021q_vlan_info
|
|
u16 vid;
|
|
};
|
|
|
|
+/* DSA_NOTIFIER_MASTER_STATE_CHANGE */
|
|
+struct dsa_notifier_master_state_info {
|
|
+ const struct net_device *master;
|
|
+ bool operational;
|
|
+};
|
|
+
|
|
struct dsa_switchdev_event_work {
|
|
struct dsa_switch *ds;
|
|
int port;
|
|
@@ -548,6 +555,12 @@ int dsa_tree_change_tag_proto(struct dsa
|
|
struct net_device *master,
|
|
const struct dsa_device_ops *tag_ops,
|
|
const struct dsa_device_ops *old_tag_ops);
|
|
+void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
|
|
+ struct net_device *master,
|
|
+ bool up);
|
|
+void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
|
|
+ struct net_device *master,
|
|
+ bool up);
|
|
int dsa_bridge_num_get(const struct net_device *bridge_dev, int max);
|
|
void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num);
|
|
|
|
--- a/net/dsa/slave.c
|
|
+++ b/net/dsa/slave.c
|
|
@@ -2320,6 +2320,36 @@ static int dsa_slave_netdevice_event(str
|
|
err = dsa_port_lag_change(dp, info->lower_state_info);
|
|
return notifier_from_errno(err);
|
|
}
|
|
+ case NETDEV_CHANGE:
|
|
+ case NETDEV_UP: {
|
|
+ /* Track state of master port.
|
|
+ * DSA driver may require the master port (and indirectly
|
|
+ * the tagger) to be available for some special operation.
|
|
+ */
|
|
+ if (netdev_uses_dsa(dev)) {
|
|
+ struct dsa_port *cpu_dp = dev->dsa_ptr;
|
|
+ struct dsa_switch_tree *dst = cpu_dp->ds->dst;
|
|
+
|
|
+ /* Track when the master port is UP */
|
|
+ dsa_tree_master_oper_state_change(dst, dev,
|
|
+ netif_oper_up(dev));
|
|
+
|
|
+ /* Track when the master port is ready and can accept
|
|
+ * packet.
|
|
+ * NETDEV_UP event is not enough to flag a port as ready.
|
|
+ * We also have to wait for linkwatch_do_dev to dev_activate
|
|
+ * and emit a NETDEV_CHANGE event.
|
|
+ * We check if a master port is ready by checking if the dev
|
|
+ * have a qdisc assigned and is not noop.
|
|
+ */
|
|
+ dsa_tree_master_admin_state_change(dst, dev,
|
|
+ !qdisc_tx_is_noop(dev));
|
|
+
|
|
+ return NOTIFY_OK;
|
|
+ }
|
|
+
|
|
+ return NOTIFY_DONE;
|
|
+ }
|
|
case NETDEV_GOING_DOWN: {
|
|
struct dsa_port *dp, *cpu_dp;
|
|
struct dsa_switch_tree *dst;
|
|
@@ -2331,6 +2361,8 @@ static int dsa_slave_netdevice_event(str
|
|
cpu_dp = dev->dsa_ptr;
|
|
dst = cpu_dp->ds->dst;
|
|
|
|
+ dsa_tree_master_admin_state_change(dst, dev, false);
|
|
+
|
|
list_for_each_entry(dp, &dst->ports, list) {
|
|
if (!dsa_is_user_port(dp->ds, dp->index))
|
|
continue;
|
|
--- a/net/dsa/switch.c
|
|
+++ b/net/dsa/switch.c
|
|
@@ -722,6 +722,18 @@ dsa_switch_mrp_del_ring_role(struct dsa_
|
|
return 0;
|
|
}
|
|
|
|
+static int
|
|
+dsa_switch_master_state_change(struct dsa_switch *ds,
|
|
+ struct dsa_notifier_master_state_info *info)
|
|
+{
|
|
+ if (!ds->ops->master_state_change)
|
|
+ return 0;
|
|
+
|
|
+ ds->ops->master_state_change(ds, info->master, info->operational);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int dsa_switch_event(struct notifier_block *nb,
|
|
unsigned long event, void *info)
|
|
{
|
|
@@ -813,6 +825,9 @@ static int dsa_switch_event(struct notif
|
|
case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
|
|
err = dsa_switch_tag_8021q_vlan_del(ds, info);
|
|
break;
|
|
+ case DSA_NOTIFIER_MASTER_STATE_CHANGE:
|
|
+ err = dsa_switch_master_state_change(ds, info);
|
|
+ break;
|
|
default:
|
|
err = -EOPNOTSUPP;
|
|
break;
|