mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	All patches rebased automatically. Build system: x86_64 Build-tested: ramips/tplink_archer-a6-v3, filogic/xiaomi_redmi-router-ax6000-ubootmod Run-tested: ramips/tplink_archer-a6-v3, filogic/xiaomi_redmi-router-ax6000-ubootmod Signed-off-by: John Audia <therealgraysky@proton.me>
		
			
				
	
	
		
			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;
 | 
						|
@@ -949,6 +959,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;
 | 
						|
@@ -549,6 +556,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
 | 
						|
@@ -2311,6 +2311,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;
 | 
						|
@@ -2322,6 +2352,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;
 |