mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-20 22:23:27 +00:00
268 lines
8.4 KiB
Diff
268 lines
8.4 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(+)
|
||
|
|
||
|
diff --git a/include/net/dsa.h b/include/net/dsa.h
|
||
|
index 57b3e4e7413b..43c4153ef53a 100644
|
||
|
--- a/include/net/dsa.h
|
||
|
+++ b/include/net/dsa.h
|
||
|
@@ -278,6 +278,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,
|
||
|
@@ -478,6 +482,12 @@ static inline bool dsa_port_is_unused(struct dsa_port *dp)
|
||
|
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;
|
||
|
@@ -1036,6 +1046,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) \
|
||
|
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
|
||
|
index 3d21521453fe..ff998c0ede02 100644
|
||
|
--- a/net/dsa/dsa2.c
|
||
|
+++ b/net/dsa/dsa2.c
|
||
|
@@ -1279,6 +1279,52 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
|
||
|
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;
|
||
|
diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
|
||
|
index 760306f0012f..2bbfa9efe9f8 100644
|
||
|
--- a/net/dsa/dsa_priv.h
|
||
|
+++ b/net/dsa/dsa_priv.h
|
||
|
@@ -40,6 +40,7 @@ enum {
|
||
|
DSA_NOTIFIER_TAG_PROTO_DISCONNECT,
|
||
|
DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
|
||
|
DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
|
||
|
+ DSA_NOTIFIER_MASTER_STATE_CHANGE,
|
||
|
};
|
||
|
|
||
|
/* DSA_NOTIFIER_AGEING_TIME */
|
||
|
@@ -109,6 +110,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;
|
||
|
@@ -482,6 +489,12 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
|
||
|
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);
|
||
|
|
||
|
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
|
||
|
index 22241afcac81..2b5b0f294233 100644
|
||
|
--- a/net/dsa/slave.c
|
||
|
+++ b/net/dsa/slave.c
|
||
|
@@ -2346,6 +2346,36 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||
|
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;
|
||
|
@@ -2357,6 +2387,8 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
|
||
|
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_port_is_user(dp))
|
||
|
continue;
|
||
|
diff --git a/net/dsa/switch.c b/net/dsa/switch.c
|
||
|
index 517cc83d13cc..4866b58649e4 100644
|
||
|
--- a/net/dsa/switch.c
|
||
|
+++ b/net/dsa/switch.c
|
||
|
@@ -697,6 +697,18 @@ dsa_switch_disconnect_tag_proto(struct dsa_switch *ds,
|
||
|
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)
|
||
|
{
|
||
|
@@ -770,6 +782,9 @@ static int dsa_switch_event(struct notifier_block *nb,
|
||
|
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;
|
||
|
--
|
||
|
2.34.1
|
||
|
|