2cfb488aee02b20860ca77cf7c5969a840ad7238
[openwrt/staging/dedeckeh.git] /
1 From 295ab96f478d0fa56393e85406f19a867e26ce22 Mon Sep 17 00:00:00 2001
2 From: Vladimir Oltean <vladimir.oltean@nxp.com>
3 Date: Wed, 2 Feb 2022 01:03:20 +0100
4 Subject: [PATCH 01/16] net: dsa: provide switch operations for tracking the
5 master state
6
7 Certain drivers may need to send management traffic to the switch for
8 things like register access, FDB dump, etc, to accelerate what their
9 slow bus (SPI, I2C, MDIO) can already do.
10
11 Ethernet is faster (especially in bulk transactions) but is also more
12 unreliable, since the user may decide to bring the DSA master down (or
13 not bring it up), therefore severing the link between the host and the
14 attached switch.
15
16 Drivers needing Ethernet-based register access already should have
17 fallback logic to the slow bus if the Ethernet method fails, but that
18 fallback may be based on a timeout, and the I/O to the switch may slow
19 down to a halt if the master is down, because every Ethernet packet will
20 have to time out. The driver also doesn't have the option to turn off
21 Ethernet-based I/O momentarily, because it wouldn't know when to turn it
22 back on.
23
24 Which is where this change comes in. By tracking NETDEV_CHANGE,
25 NETDEV_UP and NETDEV_GOING_DOWN events on the DSA master, we should know
26 the exact interval of time during which this interface is reliably
27 available for traffic. Provide this information to switches so they can
28 use it as they wish.
29
30 An helper is added dsa_port_master_is_operational() to check if a master
31 port is operational.
32
33 Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
34 Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
35 Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
36 Signed-off-by: David S. Miller <davem@davemloft.net>
37 ---
38 include/net/dsa.h | 17 +++++++++++++++++
39 net/dsa/dsa2.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
40 net/dsa/dsa_priv.h | 13 +++++++++++++
41 net/dsa/slave.c | 32 ++++++++++++++++++++++++++++++++
42 net/dsa/switch.c | 15 +++++++++++++++
43 5 files changed, 123 insertions(+)
44
45 diff --git a/include/net/dsa.h b/include/net/dsa.h
46 index 57b3e4e7413b..43c4153ef53a 100644
47 --- a/include/net/dsa.h
48 +++ b/include/net/dsa.h
49 @@ -278,6 +278,10 @@ struct dsa_port {
50 struct list_head mdbs;
51
52 bool setup;
53 + /* Master state bits, valid only on CPU ports */
54 + u8 master_admin_up:1;
55 + u8 master_oper_up:1;
56 +
57 };
58
59 /* TODO: ideally DSA ports would have a single dp->link_dp member,
60 @@ -478,6 +482,12 @@ static inline bool dsa_port_is_unused(struct dsa_port *dp)
61 return dp->type == DSA_PORT_TYPE_UNUSED;
62 }
63
64 +static inline bool dsa_port_master_is_operational(struct dsa_port *dp)
65 +{
66 + return dsa_port_is_cpu(dp) && dp->master_admin_up &&
67 + dp->master_oper_up;
68 +}
69 +
70 static inline bool dsa_is_unused_port(struct dsa_switch *ds, int p)
71 {
72 return dsa_to_port(ds, p)->type == DSA_PORT_TYPE_UNUSED;
73 @@ -1036,6 +1046,13 @@ struct dsa_switch_ops {
74 int (*tag_8021q_vlan_add)(struct dsa_switch *ds, int port, u16 vid,
75 u16 flags);
76 int (*tag_8021q_vlan_del)(struct dsa_switch *ds, int port, u16 vid);
77 +
78 + /*
79 + * DSA master tracking operations
80 + */
81 + void (*master_state_change)(struct dsa_switch *ds,
82 + const struct net_device *master,
83 + bool operational);
84 };
85
86 #define DSA_DEVLINK_PARAM_DRIVER(_id, _name, _type, _cmodes) \
87 diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
88 index 3d21521453fe..ff998c0ede02 100644
89 --- a/net/dsa/dsa2.c
90 +++ b/net/dsa/dsa2.c
91 @@ -1279,6 +1279,52 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
92 return err;
93 }
94
95 +static void dsa_tree_master_state_change(struct dsa_switch_tree *dst,
96 + struct net_device *master)
97 +{
98 + struct dsa_notifier_master_state_info info;
99 + struct dsa_port *cpu_dp = master->dsa_ptr;
100 +
101 + info.master = master;
102 + info.operational = dsa_port_master_is_operational(cpu_dp);
103 +
104 + dsa_tree_notify(dst, DSA_NOTIFIER_MASTER_STATE_CHANGE, &info);
105 +}
106 +
107 +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
108 + struct net_device *master,
109 + bool up)
110 +{
111 + struct dsa_port *cpu_dp = master->dsa_ptr;
112 + bool notify = false;
113 +
114 + if ((dsa_port_master_is_operational(cpu_dp)) !=
115 + (up && cpu_dp->master_oper_up))
116 + notify = true;
117 +
118 + cpu_dp->master_admin_up = up;
119 +
120 + if (notify)
121 + dsa_tree_master_state_change(dst, master);
122 +}
123 +
124 +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
125 + struct net_device *master,
126 + bool up)
127 +{
128 + struct dsa_port *cpu_dp = master->dsa_ptr;
129 + bool notify = false;
130 +
131 + if ((dsa_port_master_is_operational(cpu_dp)) !=
132 + (cpu_dp->master_admin_up && up))
133 + notify = true;
134 +
135 + cpu_dp->master_oper_up = up;
136 +
137 + if (notify)
138 + dsa_tree_master_state_change(dst, master);
139 +}
140 +
141 static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index)
142 {
143 struct dsa_switch_tree *dst = ds->dst;
144 diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
145 index 760306f0012f..2bbfa9efe9f8 100644
146 --- a/net/dsa/dsa_priv.h
147 +++ b/net/dsa/dsa_priv.h
148 @@ -40,6 +40,7 @@ enum {
149 DSA_NOTIFIER_TAG_PROTO_DISCONNECT,
150 DSA_NOTIFIER_TAG_8021Q_VLAN_ADD,
151 DSA_NOTIFIER_TAG_8021Q_VLAN_DEL,
152 + DSA_NOTIFIER_MASTER_STATE_CHANGE,
153 };
154
155 /* DSA_NOTIFIER_AGEING_TIME */
156 @@ -109,6 +110,12 @@ struct dsa_notifier_tag_8021q_vlan_info {
157 u16 vid;
158 };
159
160 +/* DSA_NOTIFIER_MASTER_STATE_CHANGE */
161 +struct dsa_notifier_master_state_info {
162 + const struct net_device *master;
163 + bool operational;
164 +};
165 +
166 struct dsa_switchdev_event_work {
167 struct dsa_switch *ds;
168 int port;
169 @@ -482,6 +489,12 @@ int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst,
170 struct net_device *master,
171 const struct dsa_device_ops *tag_ops,
172 const struct dsa_device_ops *old_tag_ops);
173 +void dsa_tree_master_admin_state_change(struct dsa_switch_tree *dst,
174 + struct net_device *master,
175 + bool up);
176 +void dsa_tree_master_oper_state_change(struct dsa_switch_tree *dst,
177 + struct net_device *master,
178 + bool up);
179 int dsa_bridge_num_get(const struct net_device *bridge_dev, int max);
180 void dsa_bridge_num_put(const struct net_device *bridge_dev, int bridge_num);
181
182 diff --git a/net/dsa/slave.c b/net/dsa/slave.c
183 index 22241afcac81..2b5b0f294233 100644
184 --- a/net/dsa/slave.c
185 +++ b/net/dsa/slave.c
186 @@ -2346,6 +2346,36 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
187 err = dsa_port_lag_change(dp, info->lower_state_info);
188 return notifier_from_errno(err);
189 }
190 + case NETDEV_CHANGE:
191 + case NETDEV_UP: {
192 + /* Track state of master port.
193 + * DSA driver may require the master port (and indirectly
194 + * the tagger) to be available for some special operation.
195 + */
196 + if (netdev_uses_dsa(dev)) {
197 + struct dsa_port *cpu_dp = dev->dsa_ptr;
198 + struct dsa_switch_tree *dst = cpu_dp->ds->dst;
199 +
200 + /* Track when the master port is UP */
201 + dsa_tree_master_oper_state_change(dst, dev,
202 + netif_oper_up(dev));
203 +
204 + /* Track when the master port is ready and can accept
205 + * packet.
206 + * NETDEV_UP event is not enough to flag a port as ready.
207 + * We also have to wait for linkwatch_do_dev to dev_activate
208 + * and emit a NETDEV_CHANGE event.
209 + * We check if a master port is ready by checking if the dev
210 + * have a qdisc assigned and is not noop.
211 + */
212 + dsa_tree_master_admin_state_change(dst, dev,
213 + !qdisc_tx_is_noop(dev));
214 +
215 + return NOTIFY_OK;
216 + }
217 +
218 + return NOTIFY_DONE;
219 + }
220 case NETDEV_GOING_DOWN: {
221 struct dsa_port *dp, *cpu_dp;
222 struct dsa_switch_tree *dst;
223 @@ -2357,6 +2387,8 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
224 cpu_dp = dev->dsa_ptr;
225 dst = cpu_dp->ds->dst;
226
227 + dsa_tree_master_admin_state_change(dst, dev, false);
228 +
229 list_for_each_entry(dp, &dst->ports, list) {
230 if (!dsa_port_is_user(dp))
231 continue;
232 diff --git a/net/dsa/switch.c b/net/dsa/switch.c
233 index 517cc83d13cc..4866b58649e4 100644
234 --- a/net/dsa/switch.c
235 +++ b/net/dsa/switch.c
236 @@ -697,6 +697,18 @@ dsa_switch_disconnect_tag_proto(struct dsa_switch *ds,
237 return 0;
238 }
239
240 +static int
241 +dsa_switch_master_state_change(struct dsa_switch *ds,
242 + struct dsa_notifier_master_state_info *info)
243 +{
244 + if (!ds->ops->master_state_change)
245 + return 0;
246 +
247 + ds->ops->master_state_change(ds, info->master, info->operational);
248 +
249 + return 0;
250 +}
251 +
252 static int dsa_switch_event(struct notifier_block *nb,
253 unsigned long event, void *info)
254 {
255 @@ -770,6 +782,9 @@ static int dsa_switch_event(struct notifier_block *nb,
256 case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL:
257 err = dsa_switch_tag_8021q_vlan_del(ds, info);
258 break;
259 + case DSA_NOTIFIER_MASTER_STATE_CHANGE:
260 + err = dsa_switch_master_state_change(ds, info);
261 + break;
262 default:
263 err = -EOPNOTSUPP;
264 break;
265 --
266 2.34.1
267