Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
83c0afae | 2 | /* |
165c2fb9 VO |
3 | * DSA topology and switch handling |
4 | * | |
83c0afae AL |
5 | * Copyright (c) 2008-2009 Marvell Semiconductor |
6 | * Copyright (c) 2013 Florian Fainelli <florian@openwrt.org> | |
7 | * Copyright (c) 2016 Andrew Lunn <andrew@lunn.ch> | |
83c0afae AL |
8 | */ |
9 | ||
10 | #include <linux/device.h> | |
11 | #include <linux/err.h> | |
12 | #include <linux/list.h> | |
165c2fb9 | 13 | #include <linux/module.h> |
c6e970a0 | 14 | #include <linux/netdevice.h> |
83c0afae AL |
15 | #include <linux/slab.h> |
16 | #include <linux/rtnetlink.h> | |
83c0afae AL |
17 | #include <linux/of.h> |
18 | #include <linux/of_net.h> | |
5a178186 | 19 | #include <net/dsa_stubs.h> |
e83d5653 | 20 | #include <net/sch_generic.h> |
ea5dd34b | 21 | |
6ca80638 | 22 | #include "conduit.h" |
5cf2c75b | 23 | #include "devlink.h" |
47d2ce03 | 24 | #include "dsa.h" |
5917bfe6 | 25 | #include "netlink.h" |
022bba63 | 26 | #include "port.h" |
0c603136 | 27 | #include "switch.h" |
bd954b82 | 28 | #include "tag.h" |
6ca80638 | 29 | #include "user.h" |
83c0afae | 30 | |
5917bfe6 VO |
31 | #define DSA_MAX_NUM_OFFLOADING_BRIDGES BITS_PER_LONG |
32 | ||
83c0afae | 33 | static DEFINE_MUTEX(dsa2_mutex); |
bff33f7e | 34 | LIST_HEAD(dsa_tree_list); |
83c0afae | 35 | |
165c2fb9 VO |
36 | static struct workqueue_struct *dsa_owq; |
37 | ||
f5e165e7 VO |
38 | /* Track the bridges with forwarding offload enabled */ |
39 | static unsigned long dsa_fwd_offloading_bridges; | |
40 | ||
165c2fb9 VO |
41 | bool dsa_schedule_work(struct work_struct *work) |
42 | { | |
43 | return queue_work(dsa_owq, work); | |
44 | } | |
45 | ||
46 | void dsa_flush_workqueue(void) | |
47 | { | |
48 | flush_workqueue(dsa_owq); | |
49 | } | |
50 | EXPORT_SYMBOL_GPL(dsa_flush_workqueue); | |
51 | ||
058102a6 | 52 | /** |
dedd6a00 | 53 | * dsa_lag_map() - Map LAG structure to a linear LAG array |
058102a6 | 54 | * @dst: Tree in which to record the mapping. |
dedd6a00 | 55 | * @lag: LAG structure that is to be mapped to the tree's array. |
058102a6 | 56 | * |
dedd6a00 | 57 | * dsa_lag_id/dsa_lag_by_id can then be used to translate between the |
058102a6 TW |
58 | * two spaces. The size of the mapping space is determined by the |
59 | * driver by setting ds->num_lag_ids. It is perfectly legal to leave | |
60 | * it unset if it is not needed, in which case these functions become | |
61 | * no-ops. | |
62 | */ | |
dedd6a00 | 63 | void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag) |
058102a6 TW |
64 | { |
65 | unsigned int id; | |
66 | ||
3d4a0a2a | 67 | for (id = 1; id <= dst->lags_len; id++) { |
dedd6a00 VO |
68 | if (!dsa_lag_by_id(dst, id)) { |
69 | dst->lags[id - 1] = lag; | |
70 | lag->id = id; | |
058102a6 TW |
71 | return; |
72 | } | |
73 | } | |
74 | ||
75 | /* No IDs left, which is OK. Some drivers do not need it. The | |
76 | * ones that do, e.g. mv88e6xxx, will discover that dsa_lag_id | |
77 | * returns an error for this device when joining the LAG. The | |
78 | * driver can then return -EOPNOTSUPP back to DSA, which will | |
79 | * fall back to a software LAG. | |
80 | */ | |
81 | } | |
82 | ||
83 | /** | |
84 | * dsa_lag_unmap() - Remove a LAG ID mapping | |
85 | * @dst: Tree in which the mapping is recorded. | |
dedd6a00 | 86 | * @lag: LAG structure that was mapped. |
058102a6 TW |
87 | * |
88 | * As there may be multiple users of the mapping, it is only removed | |
89 | * if there are no other references to it. | |
90 | */ | |
dedd6a00 | 91 | void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag) |
058102a6 | 92 | { |
058102a6 TW |
93 | unsigned int id; |
94 | ||
058102a6 | 95 | dsa_lags_foreach_id(id, dst) { |
dedd6a00 | 96 | if (dsa_lag_by_id(dst, id) == lag) { |
3d4a0a2a | 97 | dst->lags[id - 1] = NULL; |
dedd6a00 | 98 | lag->id = 0; |
058102a6 TW |
99 | break; |
100 | } | |
101 | } | |
102 | } | |
103 | ||
dedd6a00 VO |
104 | struct dsa_lag *dsa_tree_lag_find(struct dsa_switch_tree *dst, |
105 | const struct net_device *lag_dev) | |
106 | { | |
107 | struct dsa_port *dp; | |
108 | ||
109 | list_for_each_entry(dp, &dst->ports, list) | |
110 | if (dsa_port_lag_dev_get(dp) == lag_dev) | |
111 | return dp->lag; | |
112 | ||
113 | return NULL; | |
114 | } | |
115 | ||
d3eed0e5 VO |
116 | struct dsa_bridge *dsa_tree_bridge_find(struct dsa_switch_tree *dst, |
117 | const struct net_device *br) | |
118 | { | |
119 | struct dsa_port *dp; | |
120 | ||
121 | list_for_each_entry(dp, &dst->ports, list) | |
122 | if (dsa_port_bridge_dev_get(dp) == br) | |
123 | return dp->bridge; | |
124 | ||
125 | return NULL; | |
126 | } | |
127 | ||
f5e165e7 VO |
128 | static int dsa_bridge_num_find(const struct net_device *bridge_dev) |
129 | { | |
130 | struct dsa_switch_tree *dst; | |
f5e165e7 | 131 | |
d3eed0e5 VO |
132 | list_for_each_entry(dst, &dsa_tree_list, list) { |
133 | struct dsa_bridge *bridge; | |
134 | ||
135 | bridge = dsa_tree_bridge_find(dst, bridge_dev); | |
136 | if (bridge) | |
137 | return bridge->num; | |
138 | } | |
f5e165e7 | 139 | |
3f9bb030 | 140 | return 0; |
f5e165e7 VO |
141 | } |
142 | ||
3f9bb030 | 143 | unsigned int dsa_bridge_num_get(const struct net_device *bridge_dev, int max) |
f5e165e7 | 144 | { |
3f9bb030 | 145 | unsigned int bridge_num = dsa_bridge_num_find(bridge_dev); |
f5e165e7 | 146 | |
d3eed0e5 VO |
147 | /* Switches without FDB isolation support don't get unique |
148 | * bridge numbering | |
149 | */ | |
150 | if (!max) | |
151 | return 0; | |
152 | ||
3f9bb030 | 153 | if (!bridge_num) { |
947c8746 VO |
154 | /* First port that requests FDB isolation or TX forwarding |
155 | * offload for this bridge | |
156 | */ | |
3f9bb030 VO |
157 | bridge_num = find_next_zero_bit(&dsa_fwd_offloading_bridges, |
158 | DSA_MAX_NUM_OFFLOADING_BRIDGES, | |
159 | 1); | |
f5e165e7 | 160 | if (bridge_num >= max) |
3f9bb030 | 161 | return 0; |
f5e165e7 VO |
162 | |
163 | set_bit(bridge_num, &dsa_fwd_offloading_bridges); | |
164 | } | |
165 | ||
166 | return bridge_num; | |
167 | } | |
168 | ||
3f9bb030 VO |
169 | void dsa_bridge_num_put(const struct net_device *bridge_dev, |
170 | unsigned int bridge_num) | |
f5e165e7 | 171 | { |
d3eed0e5 VO |
172 | /* Since we refcount bridges, we know that when we call this function |
173 | * it is no longer in use, so we can just go ahead and remove it from | |
174 | * the bit mask. | |
f5e165e7 | 175 | */ |
d3eed0e5 | 176 | clear_bit(bridge_num, &dsa_fwd_offloading_bridges); |
f5e165e7 VO |
177 | } |
178 | ||
3b7bc1f0 VO |
179 | struct dsa_switch *dsa_switch_find(int tree_index, int sw_index) |
180 | { | |
181 | struct dsa_switch_tree *dst; | |
182 | struct dsa_port *dp; | |
183 | ||
184 | list_for_each_entry(dst, &dsa_tree_list, list) { | |
185 | if (dst->index != tree_index) | |
186 | continue; | |
187 | ||
188 | list_for_each_entry(dp, &dst->ports, list) { | |
189 | if (dp->ds->index != sw_index) | |
190 | continue; | |
191 | ||
192 | return dp->ds; | |
193 | } | |
194 | } | |
195 | ||
196 | return NULL; | |
197 | } | |
198 | EXPORT_SYMBOL_GPL(dsa_switch_find); | |
199 | ||
1ca28ec9 | 200 | static struct dsa_switch_tree *dsa_tree_find(int index) |
83c0afae AL |
201 | { |
202 | struct dsa_switch_tree *dst; | |
203 | ||
1ca28ec9 | 204 | list_for_each_entry(dst, &dsa_tree_list, list) |
8e5bf975 | 205 | if (dst->index == index) |
83c0afae | 206 | return dst; |
8e5bf975 | 207 | |
83c0afae AL |
208 | return NULL; |
209 | } | |
210 | ||
1ca28ec9 | 211 | static struct dsa_switch_tree *dsa_tree_alloc(int index) |
83c0afae AL |
212 | { |
213 | struct dsa_switch_tree *dst; | |
214 | ||
215 | dst = kzalloc(sizeof(*dst), GFP_KERNEL); | |
216 | if (!dst) | |
217 | return NULL; | |
1ca28ec9 | 218 | |
49463b7f | 219 | dst->index = index; |
1ca28ec9 | 220 | |
c5f51765 VD |
221 | INIT_LIST_HEAD(&dst->rtable); |
222 | ||
ab8ccae1 VD |
223 | INIT_LIST_HEAD(&dst->ports); |
224 | ||
83c0afae | 225 | INIT_LIST_HEAD(&dst->list); |
50c7d2ba | 226 | list_add_tail(&dst->list, &dsa_tree_list); |
8e5bf975 | 227 | |
83c0afae AL |
228 | kref_init(&dst->refcount); |
229 | ||
230 | return dst; | |
231 | } | |
232 | ||
65254108 VD |
233 | static void dsa_tree_free(struct dsa_switch_tree *dst) |
234 | { | |
7f297314 | 235 | if (dst->tag_ops) |
357f203b | 236 | dsa_tag_driver_put(dst->tag_ops); |
65254108 VD |
237 | list_del(&dst->list); |
238 | kfree(dst); | |
239 | } | |
240 | ||
9e741045 | 241 | static struct dsa_switch_tree *dsa_tree_get(struct dsa_switch_tree *dst) |
1ca28ec9 | 242 | { |
9e741045 VD |
243 | if (dst) |
244 | kref_get(&dst->refcount); | |
1ca28ec9 VD |
245 | |
246 | return dst; | |
247 | } | |
248 | ||
9e741045 | 249 | static struct dsa_switch_tree *dsa_tree_touch(int index) |
65254108 | 250 | { |
9e741045 VD |
251 | struct dsa_switch_tree *dst; |
252 | ||
253 | dst = dsa_tree_find(index); | |
254 | if (dst) | |
255 | return dsa_tree_get(dst); | |
256 | else | |
257 | return dsa_tree_alloc(index); | |
65254108 VD |
258 | } |
259 | ||
260 | static void dsa_tree_release(struct kref *ref) | |
261 | { | |
262 | struct dsa_switch_tree *dst; | |
263 | ||
264 | dst = container_of(ref, struct dsa_switch_tree, refcount); | |
265 | ||
266 | dsa_tree_free(dst); | |
267 | } | |
268 | ||
269 | static void dsa_tree_put(struct dsa_switch_tree *dst) | |
270 | { | |
9e741045 VD |
271 | if (dst) |
272 | kref_put(&dst->refcount, dsa_tree_release); | |
65254108 VD |
273 | } |
274 | ||
f163da88 VD |
275 | static struct dsa_port *dsa_tree_find_port_by_node(struct dsa_switch_tree *dst, |
276 | struct device_node *dn) | |
83c0afae | 277 | { |
f163da88 | 278 | struct dsa_port *dp; |
83c0afae | 279 | |
764b7e62 VD |
280 | list_for_each_entry(dp, &dst->ports, list) |
281 | if (dp->dn == dn) | |
282 | return dp; | |
83c0afae AL |
283 | |
284 | return NULL; | |
285 | } | |
286 | ||
4e2ce6e5 BDC |
287 | static struct dsa_link *dsa_link_touch(struct dsa_port *dp, |
288 | struct dsa_port *link_dp) | |
c5f51765 VD |
289 | { |
290 | struct dsa_switch *ds = dp->ds; | |
291 | struct dsa_switch_tree *dst; | |
292 | struct dsa_link *dl; | |
293 | ||
294 | dst = ds->dst; | |
295 | ||
296 | list_for_each_entry(dl, &dst->rtable, list) | |
297 | if (dl->dp == dp && dl->link_dp == link_dp) | |
298 | return dl; | |
299 | ||
300 | dl = kzalloc(sizeof(*dl), GFP_KERNEL); | |
301 | if (!dl) | |
302 | return NULL; | |
303 | ||
304 | dl->dp = dp; | |
305 | dl->link_dp = link_dp; | |
306 | ||
307 | INIT_LIST_HEAD(&dl->list); | |
308 | list_add_tail(&dl->list, &dst->rtable); | |
309 | ||
310 | return dl; | |
311 | } | |
312 | ||
34c09a89 | 313 | static bool dsa_port_setup_routing_table(struct dsa_port *dp) |
83c0afae | 314 | { |
34c09a89 VD |
315 | struct dsa_switch *ds = dp->ds; |
316 | struct dsa_switch_tree *dst = ds->dst; | |
317 | struct device_node *dn = dp->dn; | |
c5286665 | 318 | struct of_phandle_iterator it; |
f163da88 | 319 | struct dsa_port *link_dp; |
c5f51765 | 320 | struct dsa_link *dl; |
c5286665 | 321 | int err; |
83c0afae | 322 | |
c5286665 VD |
323 | of_for_each_phandle(&it, err, dn, "link", NULL, 0) { |
324 | link_dp = dsa_tree_find_port_by_node(dst, it.node); | |
325 | if (!link_dp) { | |
326 | of_node_put(it.node); | |
34c09a89 | 327 | return false; |
c5286665 | 328 | } |
83c0afae | 329 | |
c5f51765 VD |
330 | dl = dsa_link_touch(dp, link_dp); |
331 | if (!dl) { | |
332 | of_node_put(it.node); | |
333 | return false; | |
334 | } | |
83c0afae AL |
335 | } |
336 | ||
34c09a89 | 337 | return true; |
83c0afae AL |
338 | } |
339 | ||
3774ecdb | 340 | static bool dsa_tree_setup_routing_table(struct dsa_switch_tree *dst) |
83c0afae | 341 | { |
34c09a89 VD |
342 | bool complete = true; |
343 | struct dsa_port *dp; | |
83c0afae | 344 | |
86bfb2c1 | 345 | list_for_each_entry(dp, &dst->ports, list) { |
3774ecdb | 346 | if (dsa_port_is_dsa(dp)) { |
34c09a89 VD |
347 | complete = dsa_port_setup_routing_table(dp); |
348 | if (!complete) | |
349 | break; | |
350 | } | |
83c0afae AL |
351 | } |
352 | ||
34c09a89 | 353 | return complete; |
83c0afae AL |
354 | } |
355 | ||
f070464c VD |
356 | static struct dsa_port *dsa_tree_find_first_cpu(struct dsa_switch_tree *dst) |
357 | { | |
f070464c | 358 | struct dsa_port *dp; |
f070464c | 359 | |
c0b73628 VD |
360 | list_for_each_entry(dp, &dst->ports, list) |
361 | if (dsa_port_is_cpu(dp)) | |
362 | return dp; | |
f070464c VD |
363 | |
364 | return NULL; | |
365 | } | |
366 | ||
6ca80638 | 367 | struct net_device *dsa_tree_find_first_conduit(struct dsa_switch_tree *dst) |
95f510d0 VO |
368 | { |
369 | struct device_node *ethernet; | |
6ca80638 | 370 | struct net_device *conduit; |
95f510d0 VO |
371 | struct dsa_port *cpu_dp; |
372 | ||
373 | cpu_dp = dsa_tree_find_first_cpu(dst); | |
374 | ethernet = of_parse_phandle(cpu_dp->dn, "ethernet", 0); | |
6ca80638 | 375 | conduit = of_find_net_device_by_node(ethernet); |
95f510d0 VO |
376 | of_node_put(ethernet); |
377 | ||
6ca80638 | 378 | return conduit; |
95f510d0 VO |
379 | } |
380 | ||
2c0b0325 VO |
381 | /* Assign the default CPU port (the first one in the tree) to all ports of the |
382 | * fabric which don't already have one as part of their own switch. | |
383 | */ | |
f070464c VD |
384 | static int dsa_tree_setup_default_cpu(struct dsa_switch_tree *dst) |
385 | { | |
da4561cd | 386 | struct dsa_port *cpu_dp, *dp; |
f070464c | 387 | |
da4561cd VD |
388 | cpu_dp = dsa_tree_find_first_cpu(dst); |
389 | if (!cpu_dp) { | |
390 | pr_err("DSA: tree %d has no CPU port\n", dst->index); | |
f070464c VD |
391 | return -EINVAL; |
392 | } | |
393 | ||
2c0b0325 VO |
394 | list_for_each_entry(dp, &dst->ports, list) { |
395 | if (dp->cpu_dp) | |
396 | continue; | |
397 | ||
da4561cd VD |
398 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) |
399 | dp->cpu_dp = cpu_dp; | |
2c0b0325 | 400 | } |
f070464c VD |
401 | |
402 | return 0; | |
403 | } | |
404 | ||
b79d7c14 VO |
405 | static struct dsa_port * |
406 | dsa_switch_preferred_default_local_cpu_port(struct dsa_switch *ds) | |
407 | { | |
408 | struct dsa_port *cpu_dp; | |
409 | ||
410 | if (!ds->ops->preferred_default_local_cpu_port) | |
411 | return NULL; | |
412 | ||
413 | cpu_dp = ds->ops->preferred_default_local_cpu_port(ds); | |
414 | if (!cpu_dp) | |
415 | return NULL; | |
416 | ||
417 | if (WARN_ON(!dsa_port_is_cpu(cpu_dp) || cpu_dp->ds != ds)) | |
418 | return NULL; | |
419 | ||
420 | return cpu_dp; | |
421 | } | |
422 | ||
2c0b0325 VO |
423 | /* Perform initial assignment of CPU ports to user ports and DSA links in the |
424 | * fabric, giving preference to CPU ports local to each switch. Default to | |
425 | * using the first CPU port in the switch tree if the port does not have a CPU | |
426 | * port local to this switch. | |
427 | */ | |
428 | static int dsa_tree_setup_cpu_ports(struct dsa_switch_tree *dst) | |
429 | { | |
b79d7c14 | 430 | struct dsa_port *preferred_cpu_dp, *cpu_dp, *dp; |
2c0b0325 VO |
431 | |
432 | list_for_each_entry(cpu_dp, &dst->ports, list) { | |
433 | if (!dsa_port_is_cpu(cpu_dp)) | |
434 | continue; | |
435 | ||
b79d7c14 VO |
436 | preferred_cpu_dp = dsa_switch_preferred_default_local_cpu_port(cpu_dp->ds); |
437 | if (preferred_cpu_dp && preferred_cpu_dp != cpu_dp) | |
438 | continue; | |
439 | ||
65c563a6 VO |
440 | /* Prefer a local CPU port */ |
441 | dsa_switch_for_each_port(dp, cpu_dp->ds) { | |
2c0b0325 VO |
442 | /* Prefer the first local CPU port found */ |
443 | if (dp->cpu_dp) | |
444 | continue; | |
445 | ||
446 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) | |
447 | dp->cpu_dp = cpu_dp; | |
448 | } | |
449 | } | |
450 | ||
451 | return dsa_tree_setup_default_cpu(dst); | |
452 | } | |
453 | ||
0e8eb9a1 | 454 | static void dsa_tree_teardown_cpu_ports(struct dsa_switch_tree *dst) |
f070464c | 455 | { |
da4561cd VD |
456 | struct dsa_port *dp; |
457 | ||
458 | list_for_each_entry(dp, &dst->ports, list) | |
459 | if (dsa_port_is_user(dp) || dsa_port_is_dsa(dp)) | |
460 | dp->cpu_dp = NULL; | |
f070464c VD |
461 | } |
462 | ||
1d27732f | 463 | static int dsa_port_setup(struct dsa_port *dp) |
83c0afae | 464 | { |
4ba0ebbc | 465 | bool dsa_port_link_registered = false; |
fd292c18 | 466 | struct dsa_switch *ds = dp->ds; |
4ba0ebbc VO |
467 | bool dsa_port_enabled = false; |
468 | int err = 0; | |
83c0afae | 469 | |
fb35c60c VD |
470 | if (dp->setup) |
471 | return 0; | |
472 | ||
c698a5fb JP |
473 | err = dsa_port_devlink_setup(dp); |
474 | if (err) | |
475 | return err; | |
476 | ||
1d27732f VD |
477 | switch (dp->type) { |
478 | case DSA_PORT_TYPE_UNUSED: | |
0394a63a | 479 | dsa_port_disable(dp); |
1d27732f VD |
480 | break; |
481 | case DSA_PORT_TYPE_CPU: | |
da2c398e | 482 | if (dp->dn) { |
770375ff | 483 | err = dsa_shared_port_link_register_of(dp); |
da2c398e VO |
484 | if (err) |
485 | break; | |
486 | dsa_port_link_registered = true; | |
487 | } else { | |
488 | dev_warn(ds->dev, | |
489 | "skipping link registration for CPU port %d\n", | |
490 | dp->index); | |
491 | } | |
0394a63a VD |
492 | |
493 | err = dsa_port_enable(dp, NULL); | |
e70c7aad | 494 | if (err) |
4ba0ebbc VO |
495 | break; |
496 | dsa_port_enabled = true; | |
497 | ||
da077392 | 498 | break; |
1d27732f | 499 | case DSA_PORT_TYPE_DSA: |
da2c398e | 500 | if (dp->dn) { |
770375ff | 501 | err = dsa_shared_port_link_register_of(dp); |
da2c398e VO |
502 | if (err) |
503 | break; | |
504 | dsa_port_link_registered = true; | |
505 | } else { | |
506 | dev_warn(ds->dev, | |
507 | "skipping link registration for DSA port %d\n", | |
508 | dp->index); | |
509 | } | |
0394a63a VD |
510 | |
511 | err = dsa_port_enable(dp, NULL); | |
e70c7aad | 512 | if (err) |
4ba0ebbc VO |
513 | break; |
514 | dsa_port_enabled = true; | |
515 | ||
1d27732f VD |
516 | break; |
517 | case DSA_PORT_TYPE_USER: | |
83216e39 | 518 | of_get_mac_address(dp->dn, dp->mac); |
6ca80638 | 519 | err = dsa_user_create(dp); |
1d27732f | 520 | break; |
83c0afae AL |
521 | } |
522 | ||
4ba0ebbc VO |
523 | if (err && dsa_port_enabled) |
524 | dsa_port_disable(dp); | |
525 | if (err && dsa_port_link_registered) | |
770375ff | 526 | dsa_shared_port_link_unregister_of(dp); |
d82acd85 | 527 | if (err) { |
c698a5fb | 528 | dsa_port_devlink_teardown(dp); |
d82acd85 JP |
529 | return err; |
530 | } | |
c698a5fb JP |
531 | |
532 | dp->setup = true; | |
3122433e | 533 | |
d82acd85 | 534 | return 0; |
3122433e AL |
535 | } |
536 | ||
537 | static void dsa_port_teardown(struct dsa_port *dp) | |
538 | { | |
fb35c60c VD |
539 | if (!dp->setup) |
540 | return; | |
541 | ||
1d27732f VD |
542 | switch (dp->type) { |
543 | case DSA_PORT_TYPE_UNUSED: | |
544 | break; | |
545 | case DSA_PORT_TYPE_CPU: | |
0394a63a | 546 | dsa_port_disable(dp); |
da2c398e | 547 | if (dp->dn) |
770375ff | 548 | dsa_shared_port_link_unregister_of(dp); |
955222ca | 549 | break; |
1d27732f | 550 | case DSA_PORT_TYPE_DSA: |
0394a63a | 551 | dsa_port_disable(dp); |
da2c398e | 552 | if (dp->dn) |
770375ff | 553 | dsa_shared_port_link_unregister_of(dp); |
1d27732f VD |
554 | break; |
555 | case DSA_PORT_TYPE_USER: | |
6ca80638 FF |
556 | if (dp->user) { |
557 | dsa_user_destroy(dp->user); | |
558 | dp->user = NULL; | |
1d27732f VD |
559 | } |
560 | break; | |
83c0afae | 561 | } |
fb35c60c | 562 | |
c698a5fb | 563 | dsa_port_devlink_teardown(dp); |
3122433e | 564 | |
c698a5fb | 565 | dp->setup = false; |
3122433e AL |
566 | } |
567 | ||
c698a5fb | 568 | static int dsa_port_setup_as_unused(struct dsa_port *dp) |
fd292c18 | 569 | { |
fd292c18 | 570 | dp->type = DSA_PORT_TYPE_UNUSED; |
c698a5fb | 571 | return dsa_port_setup(dp); |
fd292c18 VO |
572 | } |
573 | ||
deff7107 TW |
574 | static int dsa_switch_setup_tag_protocol(struct dsa_switch *ds) |
575 | { | |
576 | const struct dsa_device_ops *tag_ops = ds->dst->tag_ops; | |
577 | struct dsa_switch_tree *dst = ds->dst; | |
d0004a02 | 578 | int err; |
deff7107 TW |
579 | |
580 | if (tag_ops->proto == dst->default_proto) | |
dc452a47 | 581 | goto connect; |
deff7107 | 582 | |
bacf93b0 VO |
583 | rtnl_lock(); |
584 | err = ds->ops->change_tag_protocol(ds, tag_ops->proto); | |
585 | rtnl_unlock(); | |
586 | if (err) { | |
587 | dev_err(ds->dev, "Unable to use tag protocol \"%s\": %pe\n", | |
588 | tag_ops->name, ERR_PTR(err)); | |
589 | return err; | |
deff7107 TW |
590 | } |
591 | ||
dc452a47 | 592 | connect: |
7f297314 VO |
593 | if (tag_ops->connect) { |
594 | err = tag_ops->connect(ds); | |
595 | if (err) | |
596 | return err; | |
597 | } | |
598 | ||
dc452a47 VO |
599 | if (ds->ops->connect_tag_protocol) { |
600 | err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); | |
601 | if (err) { | |
602 | dev_err(ds->dev, | |
603 | "Unable to connect to tag protocol \"%s\": %pe\n", | |
604 | tag_ops->name, ERR_PTR(err)); | |
7f297314 | 605 | goto disconnect; |
dc452a47 VO |
606 | } |
607 | } | |
608 | ||
deff7107 | 609 | return 0; |
7f297314 VO |
610 | |
611 | disconnect: | |
612 | if (tag_ops->disconnect) | |
613 | tag_ops->disconnect(ds); | |
614 | ||
615 | return err; | |
deff7107 TW |
616 | } |
617 | ||
4e0c19fc VO |
618 | static void dsa_switch_teardown_tag_protocol(struct dsa_switch *ds) |
619 | { | |
620 | const struct dsa_device_ops *tag_ops = ds->dst->tag_ops; | |
621 | ||
622 | if (tag_ops->disconnect) | |
623 | tag_ops->disconnect(ds); | |
624 | } | |
625 | ||
1f08f9e9 | 626 | static int dsa_switch_setup(struct dsa_switch *ds) |
83c0afae | 627 | { |
fb35c60c VD |
628 | int err; |
629 | ||
630 | if (ds->setup) | |
631 | return 0; | |
83c0afae | 632 | |
6ca80638 | 633 | /* Initialize ds->phys_mii_mask before registering the user MDIO bus |
9d490b4e | 634 | * driver and before ops->setup() has run, since the switch drivers and |
6ca80638 | 635 | * the user MDIO bus driver rely on these values for probing PHY |
6e830d8f FF |
636 | * devices or not |
637 | */ | |
02bc6e54 | 638 | ds->phys_mii_mask |= dsa_user_ports(ds); |
6e830d8f | 639 | |
7aea535d VO |
640 | err = dsa_switch_devlink_alloc(ds); |
641 | if (err) | |
642 | return err; | |
96567d5d | 643 | |
f515f192 VD |
644 | err = dsa_switch_register_notifier(ds); |
645 | if (err) | |
c698a5fb | 646 | goto devlink_free; |
f515f192 | 647 | |
0ee2af4e VO |
648 | ds->configure_vlan_while_not_filtering = true; |
649 | ||
b2243b36 VO |
650 | err = ds->ops->setup(ds); |
651 | if (err < 0) | |
e70c7aad | 652 | goto unregister_notifier; |
b2243b36 | 653 | |
deff7107 TW |
654 | err = dsa_switch_setup_tag_protocol(ds); |
655 | if (err) | |
656 | goto teardown; | |
657 | ||
6ca80638 FF |
658 | if (!ds->user_mii_bus && ds->ops->phy_read) { |
659 | ds->user_mii_bus = mdiobus_alloc(); | |
660 | if (!ds->user_mii_bus) { | |
e70c7aad | 661 | err = -ENOMEM; |
8fd54a73 | 662 | goto teardown; |
e70c7aad | 663 | } |
1eb59443 | 664 | |
6ca80638 | 665 | dsa_user_mii_bus_init(ds); |
1eb59443 | 666 | |
ae94dc25 | 667 | err = mdiobus_register(ds->user_mii_bus); |
1eb59443 | 668 | if (err < 0) |
6ca80638 | 669 | goto free_user_mii_bus; |
1eb59443 FF |
670 | } |
671 | ||
7aea535d | 672 | dsa_switch_devlink_register(ds); |
d95fa750 VO |
673 | |
674 | ds->setup = true; | |
83c0afae | 675 | return 0; |
e70c7aad | 676 | |
6ca80638 FF |
677 | free_user_mii_bus: |
678 | if (ds->user_mii_bus && ds->ops->phy_read) | |
679 | mdiobus_free(ds->user_mii_bus); | |
8fd54a73 VO |
680 | teardown: |
681 | if (ds->ops->teardown) | |
682 | ds->ops->teardown(ds); | |
e70c7aad IC |
683 | unregister_notifier: |
684 | dsa_switch_unregister_notifier(ds); | |
c698a5fb | 685 | devlink_free: |
7aea535d | 686 | dsa_switch_devlink_free(ds); |
e70c7aad | 687 | return err; |
83c0afae AL |
688 | } |
689 | ||
1f08f9e9 | 690 | static void dsa_switch_teardown(struct dsa_switch *ds) |
83c0afae | 691 | { |
fb35c60c VD |
692 | if (!ds->setup) |
693 | return; | |
694 | ||
7aea535d | 695 | dsa_switch_devlink_unregister(ds); |
bd936bd5 | 696 | |
6ca80638 FF |
697 | if (ds->user_mii_bus && ds->ops->phy_read) { |
698 | mdiobus_unregister(ds->user_mii_bus); | |
699 | mdiobus_free(ds->user_mii_bus); | |
700 | ds->user_mii_bus = NULL; | |
5135e96a | 701 | } |
f515f192 | 702 | |
4e0c19fc VO |
703 | dsa_switch_teardown_tag_protocol(ds); |
704 | ||
5e3f847a VO |
705 | if (ds->ops->teardown) |
706 | ds->ops->teardown(ds); | |
707 | ||
39e222bf VO |
708 | dsa_switch_unregister_notifier(ds); |
709 | ||
7aea535d | 710 | dsa_switch_devlink_free(ds); |
96567d5d | 711 | |
fb35c60c | 712 | ds->setup = false; |
83c0afae AL |
713 | } |
714 | ||
a57d8c21 VO |
715 | /* First tear down the non-shared, then the shared ports. This ensures that |
716 | * all work items scheduled by our switchdev handlers for user ports have | |
717 | * completed before we destroy the refcounting kept on the shared ports. | |
718 | */ | |
719 | static void dsa_tree_teardown_ports(struct dsa_switch_tree *dst) | |
720 | { | |
721 | struct dsa_port *dp; | |
722 | ||
723 | list_for_each_entry(dp, &dst->ports, list) | |
724 | if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) | |
725 | dsa_port_teardown(dp); | |
726 | ||
727 | dsa_flush_workqueue(); | |
728 | ||
729 | list_for_each_entry(dp, &dst->ports, list) | |
730 | if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) | |
731 | dsa_port_teardown(dp); | |
732 | } | |
733 | ||
734 | static void dsa_tree_teardown_switches(struct dsa_switch_tree *dst) | |
735 | { | |
736 | struct dsa_port *dp; | |
737 | ||
738 | list_for_each_entry(dp, &dst->ports, list) | |
739 | dsa_switch_teardown(dp->ds); | |
740 | } | |
741 | ||
1e3f407f VO |
742 | /* Bring shared ports up first, then non-shared ports */ |
743 | static int dsa_tree_setup_ports(struct dsa_switch_tree *dst) | |
1f08f9e9 | 744 | { |
1d27732f | 745 | struct dsa_port *dp; |
1e3f407f | 746 | int err = 0; |
1f08f9e9 | 747 | |
fb35c60c | 748 | list_for_each_entry(dp, &dst->ports, list) { |
1e3f407f VO |
749 | if (dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp)) { |
750 | err = dsa_port_setup(dp); | |
751 | if (err) | |
752 | goto teardown; | |
753 | } | |
fb35c60c | 754 | } |
1d27732f | 755 | |
fb35c60c | 756 | list_for_each_entry(dp, &dst->ports, list) { |
1e3f407f VO |
757 | if (dsa_port_is_user(dp) || dsa_port_is_unused(dp)) { |
758 | err = dsa_port_setup(dp); | |
759 | if (err) { | |
c698a5fb | 760 | err = dsa_port_setup_as_unused(dp); |
1e3f407f VO |
761 | if (err) |
762 | goto teardown; | |
763 | } | |
fb6ec87f | 764 | } |
1f08f9e9 VD |
765 | } |
766 | ||
767 | return 0; | |
e70c7aad | 768 | |
fb35c60c | 769 | teardown: |
a57d8c21 | 770 | dsa_tree_teardown_ports(dst); |
e70c7aad | 771 | |
1e3f407f VO |
772 | return err; |
773 | } | |
774 | ||
775 | static int dsa_tree_setup_switches(struct dsa_switch_tree *dst) | |
776 | { | |
777 | struct dsa_port *dp; | |
778 | int err = 0; | |
779 | ||
780 | list_for_each_entry(dp, &dst->ports, list) { | |
781 | err = dsa_switch_setup(dp->ds); | |
782 | if (err) { | |
783 | dsa_tree_teardown_switches(dst); | |
784 | break; | |
785 | } | |
786 | } | |
e70c7aad IC |
787 | |
788 | return err; | |
1f08f9e9 VD |
789 | } |
790 | ||
6ca80638 | 791 | static int dsa_tree_setup_conduit(struct dsa_switch_tree *dst) |
17a22fcf | 792 | { |
5dc760d1 | 793 | struct dsa_port *cpu_dp; |
afb3cc1a | 794 | int err = 0; |
17a22fcf | 795 | |
c146f9bc VO |
796 | rtnl_lock(); |
797 | ||
5dc760d1 | 798 | dsa_tree_for_each_cpu_port(cpu_dp, dst) { |
6ca80638 FF |
799 | struct net_device *conduit = cpu_dp->conduit; |
800 | bool admin_up = (conduit->flags & IFF_UP) && | |
801 | !qdisc_tx_is_noop(conduit); | |
e83d5653 | 802 | |
6ca80638 | 803 | err = dsa_conduit_setup(conduit, cpu_dp); |
5dc760d1 VO |
804 | if (err) |
805 | break; | |
e83d5653 | 806 | |
6ca80638 FF |
807 | /* Replay conduit state event */ |
808 | dsa_tree_conduit_admin_state_change(dst, conduit, admin_up); | |
809 | dsa_tree_conduit_oper_state_change(dst, conduit, | |
810 | netif_oper_up(conduit)); | |
0cfec588 VD |
811 | } |
812 | ||
c146f9bc VO |
813 | rtnl_unlock(); |
814 | ||
afb3cc1a | 815 | return err; |
17a22fcf VD |
816 | } |
817 | ||
6ca80638 | 818 | static void dsa_tree_teardown_conduit(struct dsa_switch_tree *dst) |
17a22fcf | 819 | { |
5dc760d1 | 820 | struct dsa_port *cpu_dp; |
17a22fcf | 821 | |
c146f9bc VO |
822 | rtnl_lock(); |
823 | ||
5dc760d1 | 824 | dsa_tree_for_each_cpu_port(cpu_dp, dst) { |
6ca80638 | 825 | struct net_device *conduit = cpu_dp->conduit; |
e83d5653 | 826 | |
5dc760d1 | 827 | /* Synthesizing an "admin down" state is sufficient for |
6ca80638 | 828 | * the switches to get a notification if the conduit is |
5dc760d1 VO |
829 | * currently up and running. |
830 | */ | |
6ca80638 | 831 | dsa_tree_conduit_admin_state_change(dst, conduit, false); |
e83d5653 | 832 | |
6ca80638 | 833 | dsa_conduit_teardown(conduit); |
e83d5653 | 834 | } |
c146f9bc VO |
835 | |
836 | rtnl_unlock(); | |
17a22fcf VD |
837 | } |
838 | ||
058102a6 TW |
839 | static int dsa_tree_setup_lags(struct dsa_switch_tree *dst) |
840 | { | |
841 | unsigned int len = 0; | |
842 | struct dsa_port *dp; | |
843 | ||
844 | list_for_each_entry(dp, &dst->ports, list) { | |
845 | if (dp->ds->num_lag_ids > len) | |
846 | len = dp->ds->num_lag_ids; | |
847 | } | |
848 | ||
849 | if (!len) | |
850 | return 0; | |
851 | ||
852 | dst->lags = kcalloc(len, sizeof(*dst->lags), GFP_KERNEL); | |
853 | if (!dst->lags) | |
854 | return -ENOMEM; | |
855 | ||
856 | dst->lags_len = len; | |
857 | return 0; | |
858 | } | |
859 | ||
860 | static void dsa_tree_teardown_lags(struct dsa_switch_tree *dst) | |
861 | { | |
862 | kfree(dst->lags); | |
863 | } | |
864 | ||
8bf108d7 VO |
865 | static void dsa_tree_teardown_routing_table(struct dsa_switch_tree *dst) |
866 | { | |
867 | struct dsa_link *dl, *next; | |
868 | ||
869 | list_for_each_entry_safe(dl, next, &dst->rtable, list) { | |
870 | list_del(&dl->list); | |
871 | kfree(dl); | |
872 | } | |
873 | } | |
874 | ||
ec15dd42 | 875 | static int dsa_tree_setup(struct dsa_switch_tree *dst) |
83c0afae | 876 | { |
34c09a89 | 877 | bool complete; |
83c0afae AL |
878 | int err; |
879 | ||
ec15dd42 VD |
880 | if (dst->setup) { |
881 | pr_err("DSA: tree %d already setup! Disjoint trees?\n", | |
882 | dst->index); | |
883 | return -EEXIST; | |
884 | } | |
885 | ||
34c09a89 VD |
886 | complete = dsa_tree_setup_routing_table(dst); |
887 | if (!complete) | |
888 | return 0; | |
889 | ||
2c0b0325 | 890 | err = dsa_tree_setup_cpu_ports(dst); |
f070464c | 891 | if (err) |
8bf108d7 | 892 | goto teardown_rtable; |
f070464c | 893 | |
1f08f9e9 VD |
894 | err = dsa_tree_setup_switches(dst); |
895 | if (err) | |
0e8eb9a1 | 896 | goto teardown_cpu_ports; |
83c0afae | 897 | |
762c2998 | 898 | err = dsa_tree_setup_ports(dst); |
1943563d | 899 | if (err) |
e70c7aad | 900 | goto teardown_switches; |
1943563d | 901 | |
6ca80638 | 902 | err = dsa_tree_setup_conduit(dst); |
1e3f407f | 903 | if (err) |
762c2998 | 904 | goto teardown_ports; |
1e3f407f | 905 | |
058102a6 TW |
906 | err = dsa_tree_setup_lags(dst); |
907 | if (err) | |
6ca80638 | 908 | goto teardown_conduit; |
058102a6 | 909 | |
ec15dd42 VD |
910 | dst->setup = true; |
911 | ||
912 | pr_info("DSA: tree %d setup\n", dst->index); | |
83c0afae AL |
913 | |
914 | return 0; | |
e70c7aad | 915 | |
6ca80638 FF |
916 | teardown_conduit: |
917 | dsa_tree_teardown_conduit(dst); | |
762c2998 VO |
918 | teardown_ports: |
919 | dsa_tree_teardown_ports(dst); | |
1e3f407f | 920 | teardown_switches: |
e70c7aad | 921 | dsa_tree_teardown_switches(dst); |
0e8eb9a1 VO |
922 | teardown_cpu_ports: |
923 | dsa_tree_teardown_cpu_ports(dst); | |
8bf108d7 VO |
924 | teardown_rtable: |
925 | dsa_tree_teardown_routing_table(dst); | |
e70c7aad IC |
926 | |
927 | return err; | |
83c0afae AL |
928 | } |
929 | ||
ec15dd42 | 930 | static void dsa_tree_teardown(struct dsa_switch_tree *dst) |
83c0afae | 931 | { |
ec15dd42 | 932 | if (!dst->setup) |
83c0afae AL |
933 | return; |
934 | ||
058102a6 TW |
935 | dsa_tree_teardown_lags(dst); |
936 | ||
6ca80638 | 937 | dsa_tree_teardown_conduit(dst); |
11fd667d | 938 | |
762c2998 VO |
939 | dsa_tree_teardown_ports(dst); |
940 | ||
1f08f9e9 | 941 | dsa_tree_teardown_switches(dst); |
83c0afae | 942 | |
0e8eb9a1 | 943 | dsa_tree_teardown_cpu_ports(dst); |
0c73c523 | 944 | |
8bf108d7 | 945 | dsa_tree_teardown_routing_table(dst); |
c5f51765 | 946 | |
ec15dd42 VD |
947 | pr_info("DSA: tree %d torn down\n", dst->index); |
948 | ||
949 | dst->setup = false; | |
83c0afae AL |
950 | } |
951 | ||
dc452a47 VO |
952 | static int dsa_tree_bind_tag_proto(struct dsa_switch_tree *dst, |
953 | const struct dsa_device_ops *tag_ops) | |
954 | { | |
955 | const struct dsa_device_ops *old_tag_ops = dst->tag_ops; | |
956 | struct dsa_notifier_tag_proto_info info; | |
957 | int err; | |
958 | ||
959 | dst->tag_ops = tag_ops; | |
960 | ||
dc452a47 VO |
961 | /* Notify the switches from this tree about the connection |
962 | * to the new tagger | |
963 | */ | |
964 | info.tag_ops = tag_ops; | |
965 | err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_CONNECT, &info); | |
966 | if (err && err != -EOPNOTSUPP) | |
967 | goto out_disconnect; | |
968 | ||
969 | /* Notify the old tagger about the disconnection from this tree */ | |
7f297314 VO |
970 | info.tag_ops = old_tag_ops; |
971 | dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info); | |
dc452a47 VO |
972 | |
973 | return 0; | |
974 | ||
975 | out_disconnect: | |
7f297314 VO |
976 | info.tag_ops = tag_ops; |
977 | dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO_DISCONNECT, &info); | |
dc452a47 VO |
978 | dst->tag_ops = old_tag_ops; |
979 | ||
980 | return err; | |
981 | } | |
982 | ||
6ca80638 | 983 | /* Since the dsa/tagging sysfs device attribute is per conduit, the assumption |
53da0eba VO |
984 | * is that all DSA switches within a tree share the same tagger, otherwise |
985 | * they would have formed disjoint trees (different "dsa,member" values). | |
986 | */ | |
987 | int dsa_tree_change_tag_proto(struct dsa_switch_tree *dst, | |
53da0eba VO |
988 | const struct dsa_device_ops *tag_ops, |
989 | const struct dsa_device_ops *old_tag_ops) | |
990 | { | |
991 | struct dsa_notifier_tag_proto_info info; | |
992 | struct dsa_port *dp; | |
993 | int err = -EBUSY; | |
994 | ||
995 | if (!rtnl_trylock()) | |
996 | return restart_syscall(); | |
997 | ||
998 | /* At the moment we don't allow changing the tag protocol under | |
999 | * traffic. The rtnl_mutex also happens to serialize concurrent | |
1000 | * attempts to change the tagging protocol. If we ever lift the IFF_UP | |
1001 | * restriction, there needs to be another mutex which serializes this. | |
1002 | */ | |
8f6a19c0 | 1003 | dsa_tree_for_each_user_port(dp, dst) { |
6ca80638 | 1004 | if (dsa_port_to_conduit(dp)->flags & IFF_UP) |
f41ec1fd | 1005 | goto out_unlock; |
53da0eba | 1006 | |
6ca80638 | 1007 | if (dp->user->flags & IFF_UP) |
53da0eba VO |
1008 | goto out_unlock; |
1009 | } | |
1010 | ||
dc452a47 | 1011 | /* Notify the tag protocol change */ |
53da0eba VO |
1012 | info.tag_ops = tag_ops; |
1013 | err = dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info); | |
1014 | if (err) | |
e1bec7fa | 1015 | goto out_unwind_tagger; |
53da0eba | 1016 | |
dc452a47 VO |
1017 | err = dsa_tree_bind_tag_proto(dst, tag_ops); |
1018 | if (err) | |
1019 | goto out_unwind_tagger; | |
53da0eba VO |
1020 | |
1021 | rtnl_unlock(); | |
1022 | ||
1023 | return 0; | |
1024 | ||
1025 | out_unwind_tagger: | |
1026 | info.tag_ops = old_tag_ops; | |
1027 | dsa_tree_notify(dst, DSA_NOTIFIER_TAG_PROTO, &info); | |
1028 | out_unlock: | |
1029 | rtnl_unlock(); | |
1030 | return err; | |
1031 | } | |
1032 | ||
6ca80638 FF |
1033 | static void dsa_tree_conduit_state_change(struct dsa_switch_tree *dst, |
1034 | struct net_device *conduit) | |
295ab96f | 1035 | { |
6ca80638 FF |
1036 | struct dsa_notifier_conduit_state_info info; |
1037 | struct dsa_port *cpu_dp = conduit->dsa_ptr; | |
295ab96f | 1038 | |
6ca80638 FF |
1039 | info.conduit = conduit; |
1040 | info.operational = dsa_port_conduit_is_operational(cpu_dp); | |
295ab96f | 1041 | |
6ca80638 | 1042 | dsa_tree_notify(dst, DSA_NOTIFIER_CONDUIT_STATE_CHANGE, &info); |
295ab96f VO |
1043 | } |
1044 | ||
6ca80638 FF |
1045 | void dsa_tree_conduit_admin_state_change(struct dsa_switch_tree *dst, |
1046 | struct net_device *conduit, | |
1047 | bool up) | |
295ab96f | 1048 | { |
6ca80638 | 1049 | struct dsa_port *cpu_dp = conduit->dsa_ptr; |
295ab96f VO |
1050 | bool notify = false; |
1051 | ||
6ca80638 FF |
1052 | /* Don't keep track of admin state on LAG DSA conduits, |
1053 | * but rather just of physical DSA conduits | |
6e61b55c | 1054 | */ |
6ca80638 | 1055 | if (netif_is_lag_master(conduit)) |
6e61b55c VO |
1056 | return; |
1057 | ||
6ca80638 FF |
1058 | if ((dsa_port_conduit_is_operational(cpu_dp)) != |
1059 | (up && cpu_dp->conduit_oper_up)) | |
295ab96f VO |
1060 | notify = true; |
1061 | ||
6ca80638 | 1062 | cpu_dp->conduit_admin_up = up; |
295ab96f VO |
1063 | |
1064 | if (notify) | |
6ca80638 | 1065 | dsa_tree_conduit_state_change(dst, conduit); |
295ab96f VO |
1066 | } |
1067 | ||
6ca80638 FF |
1068 | void dsa_tree_conduit_oper_state_change(struct dsa_switch_tree *dst, |
1069 | struct net_device *conduit, | |
1070 | bool up) | |
295ab96f | 1071 | { |
6ca80638 | 1072 | struct dsa_port *cpu_dp = conduit->dsa_ptr; |
295ab96f VO |
1073 | bool notify = false; |
1074 | ||
6ca80638 FF |
1075 | /* Don't keep track of oper state on LAG DSA conduits, |
1076 | * but rather just of physical DSA conduits | |
6e61b55c | 1077 | */ |
6ca80638 | 1078 | if (netif_is_lag_master(conduit)) |
6e61b55c VO |
1079 | return; |
1080 | ||
6ca80638 FF |
1081 | if ((dsa_port_conduit_is_operational(cpu_dp)) != |
1082 | (cpu_dp->conduit_admin_up && up)) | |
295ab96f VO |
1083 | notify = true; |
1084 | ||
6ca80638 | 1085 | cpu_dp->conduit_oper_up = up; |
295ab96f VO |
1086 | |
1087 | if (notify) | |
6ca80638 | 1088 | dsa_tree_conduit_state_change(dst, conduit); |
295ab96f VO |
1089 | } |
1090 | ||
ab8ccae1 VD |
1091 | static struct dsa_port *dsa_port_touch(struct dsa_switch *ds, int index) |
1092 | { | |
1093 | struct dsa_switch_tree *dst = ds->dst; | |
1094 | struct dsa_port *dp; | |
1095 | ||
65c563a6 VO |
1096 | dsa_switch_for_each_port(dp, ds) |
1097 | if (dp->index == index) | |
05f294a8 VD |
1098 | return dp; |
1099 | ||
1100 | dp = kzalloc(sizeof(*dp), GFP_KERNEL); | |
1101 | if (!dp) | |
1102 | return NULL; | |
ab8ccae1 VD |
1103 | |
1104 | dp->ds = ds; | |
1105 | dp->index = index; | |
1106 | ||
fe95784f VO |
1107 | mutex_init(&dp->addr_lists_lock); |
1108 | mutex_init(&dp->vlans_lock); | |
1109 | INIT_LIST_HEAD(&dp->fdbs); | |
1110 | INIT_LIST_HEAD(&dp->mdbs); | |
d06f925f | 1111 | INIT_LIST_HEAD(&dp->vlans); /* also initializes &dp->user_vlans */ |
ab8ccae1 VD |
1112 | INIT_LIST_HEAD(&dp->list); |
1113 | list_add_tail(&dp->list, &dst->ports); | |
1114 | ||
1115 | return dp; | |
1116 | } | |
1117 | ||
06e24d08 VD |
1118 | static int dsa_port_parse_user(struct dsa_port *dp, const char *name) |
1119 | { | |
06e24d08 VD |
1120 | dp->type = DSA_PORT_TYPE_USER; |
1121 | dp->name = name; | |
1122 | ||
1123 | return 0; | |
1124 | } | |
1125 | ||
1126 | static int dsa_port_parse_dsa(struct dsa_port *dp) | |
1127 | { | |
1128 | dp->type = DSA_PORT_TYPE_DSA; | |
1129 | ||
1130 | return 0; | |
1131 | } | |
1132 | ||
4d776482 | 1133 | static enum dsa_tag_protocol dsa_get_tag_protocol(struct dsa_port *dp, |
6ca80638 | 1134 | struct net_device *conduit) |
4d776482 FF |
1135 | { |
1136 | enum dsa_tag_protocol tag_protocol = DSA_TAG_PROTO_NONE; | |
1137 | struct dsa_switch *mds, *ds = dp->ds; | |
1138 | unsigned int mdp_upstream; | |
1139 | struct dsa_port *mdp; | |
1140 | ||
1141 | /* It is possible to stack DSA switches onto one another when that | |
1142 | * happens the switch driver may want to know if its tagging protocol | |
1143 | * is going to work in such a configuration. | |
1144 | */ | |
6ca80638 FF |
1145 | if (dsa_user_dev_check(conduit)) { |
1146 | mdp = dsa_user_to_port(conduit); | |
4d776482 FF |
1147 | mds = mdp->ds; |
1148 | mdp_upstream = dsa_upstream_port(mds, mdp->index); | |
1149 | tag_protocol = mds->ops->get_tag_protocol(mds, mdp_upstream, | |
1150 | DSA_TAG_PROTO_NONE); | |
1151 | } | |
1152 | ||
6ca80638 | 1153 | /* If the conduit device is not itself a DSA user in a disjoint DSA |
4d776482 FF |
1154 | * tree, then return immediately. |
1155 | */ | |
1156 | return ds->ops->get_tag_protocol(ds, dp->index, tag_protocol); | |
1157 | } | |
1158 | ||
6ca80638 | 1159 | static int dsa_port_parse_cpu(struct dsa_port *dp, struct net_device *conduit, |
deff7107 | 1160 | const char *user_protocol) |
06e24d08 | 1161 | { |
a2c65a9d | 1162 | const struct dsa_device_ops *tag_ops = NULL; |
7354fcb0 VD |
1163 | struct dsa_switch *ds = dp->ds; |
1164 | struct dsa_switch_tree *dst = ds->dst; | |
deff7107 TW |
1165 | enum dsa_tag_protocol default_proto; |
1166 | ||
1167 | /* Find out which protocol the switch would prefer. */ | |
6ca80638 | 1168 | default_proto = dsa_get_tag_protocol(dp, conduit); |
deff7107 TW |
1169 | if (dst->default_proto) { |
1170 | if (dst->default_proto != default_proto) { | |
1171 | dev_err(ds->dev, | |
1172 | "A DSA switch tree can have only one tagging protocol\n"); | |
1173 | return -EINVAL; | |
1174 | } | |
1175 | } else { | |
1176 | dst->default_proto = default_proto; | |
1177 | } | |
1178 | ||
1179 | /* See if the user wants to override that preference. */ | |
1180 | if (user_protocol) { | |
1181 | if (!ds->ops->change_tag_protocol) { | |
1182 | dev_err(ds->dev, "Tag protocol cannot be modified\n"); | |
1183 | return -EINVAL; | |
1184 | } | |
1185 | ||
0184c07a | 1186 | tag_ops = dsa_tag_driver_get_by_name(user_protocol); |
a2c65a9d VO |
1187 | if (IS_ERR(tag_ops)) { |
1188 | dev_warn(ds->dev, | |
1189 | "Failed to find a tagging driver for protocol %s, using default\n", | |
1190 | user_protocol); | |
1191 | tag_ops = NULL; | |
1192 | } | |
deff7107 TW |
1193 | } |
1194 | ||
a2c65a9d | 1195 | if (!tag_ops) |
54c087e8 | 1196 | tag_ops = dsa_tag_driver_get_by_id(default_proto); |
a2c65a9d | 1197 | |
deff7107 TW |
1198 | if (IS_ERR(tag_ops)) { |
1199 | if (PTR_ERR(tag_ops) == -ENOPROTOOPT) | |
1200 | return -EPROBE_DEFER; | |
1201 | ||
1202 | dev_warn(ds->dev, "No tagger for this switch\n"); | |
1203 | return PTR_ERR(tag_ops); | |
1204 | } | |
7354fcb0 | 1205 | |
357f203b | 1206 | if (dst->tag_ops) { |
deff7107 | 1207 | if (dst->tag_ops != tag_ops) { |
357f203b VO |
1208 | dev_err(ds->dev, |
1209 | "A DSA switch tree can have only one tagging protocol\n"); | |
deff7107 TW |
1210 | |
1211 | dsa_tag_driver_put(tag_ops); | |
357f203b VO |
1212 | return -EINVAL; |
1213 | } | |
deff7107 | 1214 | |
357f203b | 1215 | /* In the case of multiple CPU ports per switch, the tagging |
deff7107 | 1216 | * protocol is still reference-counted only per switch tree. |
357f203b | 1217 | */ |
deff7107 | 1218 | dsa_tag_driver_put(tag_ops); |
357f203b | 1219 | } else { |
e0c755a4 | 1220 | dst->tag_ops = tag_ops; |
7354fcb0 VD |
1221 | } |
1222 | ||
6ca80638 | 1223 | dp->conduit = conduit; |
06e24d08 | 1224 | dp->type = DSA_PORT_TYPE_CPU; |
53da0eba | 1225 | dsa_port_set_tag_protocol(dp, dst->tag_ops); |
7354fcb0 | 1226 | dp->dst = dst; |
06e24d08 | 1227 | |
deff7107 TW |
1228 | /* At this point, the tree may be configured to use a different |
1229 | * tagger than the one chosen by the switch driver during | |
1230 | * .setup, in the case when a user selects a custom protocol | |
1231 | * through the DT. | |
1232 | * | |
1233 | * This is resolved by syncing the driver with the tree in | |
1234 | * dsa_switch_setup_tag_protocol once .setup has run and the | |
1235 | * driver is ready to accept calls to .change_tag_protocol. If | |
1236 | * the driver does not support the custom protocol at that | |
1237 | * point, the tree is wholly rejected, thereby ensuring that the | |
1238 | * tree and driver are always in agreement on the protocol to | |
1239 | * use. | |
1240 | */ | |
06e24d08 VD |
1241 | return 0; |
1242 | } | |
1243 | ||
fd223e2e VD |
1244 | static int dsa_port_parse_of(struct dsa_port *dp, struct device_node *dn) |
1245 | { | |
6d4e5c57 | 1246 | struct device_node *ethernet = of_parse_phandle(dn, "ethernet", 0); |
1838fa89 | 1247 | const char *name = of_get_property(dn, "label", NULL); |
54df6fa9 | 1248 | bool link = of_property_read_bool(dn, "link"); |
6d4e5c57 | 1249 | |
06e24d08 VD |
1250 | dp->dn = dn; |
1251 | ||
6d4e5c57 | 1252 | if (ethernet) { |
6ca80638 | 1253 | struct net_device *conduit; |
deff7107 | 1254 | const char *user_protocol; |
cbabb0ac | 1255 | |
6ca80638 | 1256 | conduit = of_find_net_device_by_node(ethernet); |
cb0b430b | 1257 | of_node_put(ethernet); |
6ca80638 | 1258 | if (!conduit) |
cbabb0ac VD |
1259 | return -EPROBE_DEFER; |
1260 | ||
deff7107 | 1261 | user_protocol = of_get_property(dn, "dsa-tag-protocol", NULL); |
6ca80638 | 1262 | return dsa_port_parse_cpu(dp, conduit, user_protocol); |
6d4e5c57 VD |
1263 | } |
1264 | ||
06e24d08 VD |
1265 | if (link) |
1266 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 1267 | |
06e24d08 | 1268 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
1269 | } |
1270 | ||
975e6e32 VD |
1271 | static int dsa_switch_parse_ports_of(struct dsa_switch *ds, |
1272 | struct device_node *dn) | |
83c0afae | 1273 | { |
5b32fe07 | 1274 | struct device_node *ports, *port; |
fd223e2e | 1275 | struct dsa_port *dp; |
9919a363 | 1276 | int err = 0; |
83c0afae | 1277 | u32 reg; |
5b32fe07 VD |
1278 | |
1279 | ports = of_get_child_by_name(dn, "ports"); | |
1280 | if (!ports) { | |
85e05d26 KK |
1281 | /* The second possibility is "ethernet-ports" */ |
1282 | ports = of_get_child_by_name(dn, "ethernet-ports"); | |
1283 | if (!ports) { | |
1284 | dev_err(ds->dev, "no ports child node found\n"); | |
1285 | return -EINVAL; | |
1286 | } | |
5b32fe07 | 1287 | } |
83c0afae AL |
1288 | |
1289 | for_each_available_child_of_node(ports, port) { | |
1290 | err = of_property_read_u32(port, "reg", ®); | |
ba69fd91 CJ |
1291 | if (err) { |
1292 | of_node_put(port); | |
9919a363 | 1293 | goto out_put_node; |
ba69fd91 | 1294 | } |
83c0afae | 1295 | |
9919a363 | 1296 | if (reg >= ds->num_ports) { |
258030ac | 1297 | dev_err(ds->dev, "port %pOF index %u exceeds num_ports (%u)\n", |
8209f5bc | 1298 | port, reg, ds->num_ports); |
ba69fd91 | 1299 | of_node_put(port); |
9919a363 WY |
1300 | err = -EINVAL; |
1301 | goto out_put_node; | |
1302 | } | |
83c0afae | 1303 | |
68bb8ea8 | 1304 | dp = dsa_to_port(ds, reg); |
fd223e2e VD |
1305 | |
1306 | err = dsa_port_parse_of(dp, port); | |
ba69fd91 CJ |
1307 | if (err) { |
1308 | of_node_put(port); | |
9919a363 | 1309 | goto out_put_node; |
ba69fd91 | 1310 | } |
83c0afae AL |
1311 | } |
1312 | ||
9919a363 WY |
1313 | out_put_node: |
1314 | of_node_put(ports); | |
1315 | return err; | |
83c0afae AL |
1316 | } |
1317 | ||
975e6e32 VD |
1318 | static int dsa_switch_parse_member_of(struct dsa_switch *ds, |
1319 | struct device_node *dn) | |
1320 | { | |
1321 | u32 m[2] = { 0, 0 }; | |
1322 | int sz; | |
1323 | ||
1324 | /* Don't error out if this optional property isn't found */ | |
1325 | sz = of_property_read_variable_u32_array(dn, "dsa,member", m, 2, 2); | |
1326 | if (sz < 0 && sz != -EINVAL) | |
1327 | return sz; | |
1328 | ||
1329 | ds->index = m[1]; | |
975e6e32 VD |
1330 | |
1331 | ds->dst = dsa_tree_touch(m[0]); | |
1332 | if (!ds->dst) | |
1333 | return -ENOMEM; | |
1334 | ||
8674f8d3 VO |
1335 | if (dsa_switch_find(ds->dst->index, ds->index)) { |
1336 | dev_err(ds->dev, | |
1337 | "A DSA switch with index %d already exists in tree %d\n", | |
1338 | ds->index, ds->dst->index); | |
1339 | return -EEXIST; | |
1340 | } | |
1341 | ||
5b22d366 VO |
1342 | if (ds->dst->last_switch < ds->index) |
1343 | ds->dst->last_switch = ds->index; | |
1344 | ||
975e6e32 VD |
1345 | return 0; |
1346 | } | |
1347 | ||
ab8ccae1 VD |
1348 | static int dsa_switch_touch_ports(struct dsa_switch *ds) |
1349 | { | |
1350 | struct dsa_port *dp; | |
1351 | int port; | |
1352 | ||
1353 | for (port = 0; port < ds->num_ports; port++) { | |
1354 | dp = dsa_port_touch(ds, port); | |
1355 | if (!dp) | |
1356 | return -ENOMEM; | |
1357 | } | |
1358 | ||
1359 | return 0; | |
1360 | } | |
1361 | ||
975e6e32 VD |
1362 | static int dsa_switch_parse_of(struct dsa_switch *ds, struct device_node *dn) |
1363 | { | |
1364 | int err; | |
1365 | ||
1366 | err = dsa_switch_parse_member_of(ds, dn); | |
1367 | if (err) | |
1368 | return err; | |
1369 | ||
ab8ccae1 VD |
1370 | err = dsa_switch_touch_ports(ds); |
1371 | if (err) | |
1372 | return err; | |
1373 | ||
975e6e32 VD |
1374 | return dsa_switch_parse_ports_of(ds, dn); |
1375 | } | |
1376 | ||
f1e8bf56 | 1377 | static int dev_is_class(struct device *dev, const void *class) |
165c2fb9 VO |
1378 | { |
1379 | if (dev->class != NULL && !strcmp(dev->class->name, class)) | |
1380 | return 1; | |
1381 | ||
1382 | return 0; | |
1383 | } | |
1384 | ||
1385 | static struct device *dev_find_class(struct device *parent, char *class) | |
1386 | { | |
1387 | if (dev_is_class(parent, class)) { | |
1388 | get_device(parent); | |
1389 | return parent; | |
1390 | } | |
1391 | ||
1392 | return device_find_child(parent, class, dev_is_class); | |
1393 | } | |
1394 | ||
1395 | static struct net_device *dsa_dev_to_net_device(struct device *dev) | |
1396 | { | |
1397 | struct device *d; | |
1398 | ||
1399 | d = dev_find_class(dev, "net"); | |
1400 | if (d != NULL) { | |
1401 | struct net_device *nd; | |
1402 | ||
1403 | nd = to_net_dev(d); | |
1404 | dev_hold(nd); | |
1405 | put_device(d); | |
1406 | ||
1407 | return nd; | |
1408 | } | |
1409 | ||
1410 | return NULL; | |
1411 | } | |
1412 | ||
fd223e2e VD |
1413 | static int dsa_port_parse(struct dsa_port *dp, const char *name, |
1414 | struct device *dev) | |
1415 | { | |
6d4e5c57 | 1416 | if (!strcmp(name, "cpu")) { |
6ca80638 | 1417 | struct net_device *conduit; |
cbabb0ac | 1418 | |
6ca80638 FF |
1419 | conduit = dsa_dev_to_net_device(dev); |
1420 | if (!conduit) | |
cbabb0ac VD |
1421 | return -EPROBE_DEFER; |
1422 | ||
6ca80638 | 1423 | dev_put(conduit); |
cbabb0ac | 1424 | |
6ca80638 | 1425 | return dsa_port_parse_cpu(dp, conduit, NULL); |
6d4e5c57 VD |
1426 | } |
1427 | ||
06e24d08 VD |
1428 | if (!strcmp(name, "dsa")) |
1429 | return dsa_port_parse_dsa(dp); | |
fd223e2e | 1430 | |
06e24d08 | 1431 | return dsa_port_parse_user(dp, name); |
fd223e2e VD |
1432 | } |
1433 | ||
975e6e32 VD |
1434 | static int dsa_switch_parse_ports(struct dsa_switch *ds, |
1435 | struct dsa_chip_data *cd) | |
71e0bbde FF |
1436 | { |
1437 | bool valid_name_found = false; | |
fd223e2e VD |
1438 | struct dsa_port *dp; |
1439 | struct device *dev; | |
1440 | const char *name; | |
71e0bbde | 1441 | unsigned int i; |
fd223e2e | 1442 | int err; |
71e0bbde FF |
1443 | |
1444 | for (i = 0; i < DSA_MAX_PORTS; i++) { | |
fd223e2e VD |
1445 | name = cd->port_names[i]; |
1446 | dev = cd->netdev[i]; | |
68bb8ea8 | 1447 | dp = dsa_to_port(ds, i); |
fd223e2e VD |
1448 | |
1449 | if (!name) | |
71e0bbde FF |
1450 | continue; |
1451 | ||
fd223e2e VD |
1452 | err = dsa_port_parse(dp, name, dev); |
1453 | if (err) | |
1454 | return err; | |
1455 | ||
71e0bbde FF |
1456 | valid_name_found = true; |
1457 | } | |
1458 | ||
1459 | if (!valid_name_found && i == DSA_MAX_PORTS) | |
1460 | return -EINVAL; | |
1461 | ||
1462 | return 0; | |
1463 | } | |
1464 | ||
975e6e32 | 1465 | static int dsa_switch_parse(struct dsa_switch *ds, struct dsa_chip_data *cd) |
71e0bbde | 1466 | { |
ab8ccae1 VD |
1467 | int err; |
1468 | ||
975e6e32 | 1469 | ds->cd = cd; |
71e0bbde | 1470 | |
975e6e32 VD |
1471 | /* We don't support interconnected switches nor multiple trees via |
1472 | * platform data, so this is the unique switch of the tree. | |
1473 | */ | |
1474 | ds->index = 0; | |
1475 | ds->dst = dsa_tree_touch(0); | |
1476 | if (!ds->dst) | |
1477 | return -ENOMEM; | |
71e0bbde | 1478 | |
ab8ccae1 VD |
1479 | err = dsa_switch_touch_ports(ds); |
1480 | if (err) | |
1481 | return err; | |
1482 | ||
975e6e32 | 1483 | return dsa_switch_parse_ports(ds, cd); |
71e0bbde FF |
1484 | } |
1485 | ||
6dc43cd3 VO |
1486 | static void dsa_switch_release_ports(struct dsa_switch *ds) |
1487 | { | |
7afb5fb4 | 1488 | struct dsa_mac_addr *a, *tmp; |
6dc43cd3 | 1489 | struct dsa_port *dp, *next; |
7afb5fb4 | 1490 | struct dsa_vlan *v, *n; |
6dc43cd3 | 1491 | |
65c563a6 | 1492 | dsa_switch_for_each_port_safe(dp, next, ds) { |
7afb5fb4 VO |
1493 | /* These are either entries that upper layers lost track of |
1494 | * (probably due to bugs), or installed through interfaces | |
1495 | * where one does not necessarily have to remove them, like | |
1496 | * ndo_dflt_fdb_add(). | |
1497 | */ | |
1498 | list_for_each_entry_safe(a, tmp, &dp->fdbs, list) { | |
1499 | dev_info(ds->dev, | |
1500 | "Cleaning up unicast address %pM vid %u from port %d\n", | |
1501 | a->addr, a->vid, dp->index); | |
1502 | list_del(&a->list); | |
1503 | kfree(a); | |
1504 | } | |
1505 | ||
1506 | list_for_each_entry_safe(a, tmp, &dp->mdbs, list) { | |
1507 | dev_info(ds->dev, | |
1508 | "Cleaning up multicast address %pM vid %u from port %d\n", | |
1509 | a->addr, a->vid, dp->index); | |
1510 | list_del(&a->list); | |
1511 | kfree(a); | |
1512 | } | |
1513 | ||
1514 | /* These are entries that upper layers have lost track of, | |
1515 | * probably due to bugs, but also due to dsa_port_do_vlan_del() | |
1516 | * having failed and the VLAN entry still lingering on. | |
1517 | */ | |
1518 | list_for_each_entry_safe(v, n, &dp->vlans, list) { | |
1519 | dev_info(ds->dev, | |
1520 | "Cleaning up vid %u from port %d\n", | |
1521 | v->vid, dp->index); | |
1522 | list_del(&v->list); | |
1523 | kfree(v); | |
1524 | } | |
1525 | ||
6dc43cd3 VO |
1526 | list_del(&dp->list); |
1527 | kfree(dp); | |
1528 | } | |
1529 | } | |
1530 | ||
b4fbb347 | 1531 | static int dsa_switch_probe(struct dsa_switch *ds) |
83c0afae | 1532 | { |
8e5cb84c | 1533 | struct dsa_switch_tree *dst; |
556f124f CIK |
1534 | struct dsa_chip_data *pdata; |
1535 | struct device_node *np; | |
34c09a89 | 1536 | int err; |
83c0afae | 1537 | |
7e99e347 VD |
1538 | if (!ds->dev) |
1539 | return -ENODEV; | |
1540 | ||
556f124f CIK |
1541 | pdata = ds->dev->platform_data; |
1542 | np = ds->dev->of_node; | |
1543 | ||
7e99e347 VD |
1544 | if (!ds->num_ports) |
1545 | return -EINVAL; | |
1546 | ||
6dc43cd3 | 1547 | if (np) { |
975e6e32 | 1548 | err = dsa_switch_parse_of(ds, np); |
6dc43cd3 VO |
1549 | if (err) |
1550 | dsa_switch_release_ports(ds); | |
1551 | } else if (pdata) { | |
975e6e32 | 1552 | err = dsa_switch_parse(ds, pdata); |
6dc43cd3 VO |
1553 | if (err) |
1554 | dsa_switch_release_ports(ds); | |
1555 | } else { | |
975e6e32 | 1556 | err = -ENODEV; |
6dc43cd3 | 1557 | } |
83c0afae | 1558 | |
975e6e32 VD |
1559 | if (err) |
1560 | return err; | |
d390238c | 1561 | |
8e5cb84c VD |
1562 | dst = ds->dst; |
1563 | dsa_tree_get(dst); | |
1564 | err = dsa_tree_setup(dst); | |
6dc43cd3 VO |
1565 | if (err) { |
1566 | dsa_switch_release_ports(ds); | |
8e5cb84c | 1567 | dsa_tree_put(dst); |
6dc43cd3 | 1568 | } |
8e5cb84c VD |
1569 | |
1570 | return err; | |
83c0afae AL |
1571 | } |
1572 | ||
23c9ee49 | 1573 | int dsa_register_switch(struct dsa_switch *ds) |
83c0afae AL |
1574 | { |
1575 | int err; | |
1576 | ||
1577 | mutex_lock(&dsa2_mutex); | |
b4fbb347 | 1578 | err = dsa_switch_probe(ds); |
9e741045 | 1579 | dsa_tree_put(ds->dst); |
83c0afae AL |
1580 | mutex_unlock(&dsa2_mutex); |
1581 | ||
1582 | return err; | |
1583 | } | |
1584 | EXPORT_SYMBOL_GPL(dsa_register_switch); | |
1585 | ||
b4fbb347 | 1586 | static void dsa_switch_remove(struct dsa_switch *ds) |
83c0afae AL |
1587 | { |
1588 | struct dsa_switch_tree *dst = ds->dst; | |
05f294a8 | 1589 | |
c058f6df | 1590 | dsa_tree_teardown(dst); |
6dc43cd3 | 1591 | dsa_switch_release_ports(ds); |
8e5cb84c | 1592 | dsa_tree_put(dst); |
83c0afae AL |
1593 | } |
1594 | ||
1595 | void dsa_unregister_switch(struct dsa_switch *ds) | |
1596 | { | |
1597 | mutex_lock(&dsa2_mutex); | |
b4fbb347 | 1598 | dsa_switch_remove(ds); |
83c0afae AL |
1599 | mutex_unlock(&dsa2_mutex); |
1600 | } | |
1601 | EXPORT_SYMBOL_GPL(dsa_unregister_switch); | |
0650bf52 | 1602 | |
6ca80638 | 1603 | /* If the DSA conduit chooses to unregister its net_device on .shutdown, DSA is |
0650bf52 | 1604 | * blocking that operation from completion, due to the dev_hold taken inside |
6ca80638 FF |
1605 | * netdev_upper_dev_link. Unlink the DSA user interfaces from being uppers of |
1606 | * the DSA conduit, so that the system can reboot successfully. | |
0650bf52 VO |
1607 | */ |
1608 | void dsa_switch_shutdown(struct dsa_switch *ds) | |
1609 | { | |
6ca80638 | 1610 | struct net_device *conduit, *user_dev; |
6c24a03a | 1611 | LIST_HEAD(close_list); |
0650bf52 VO |
1612 | struct dsa_port *dp; |
1613 | ||
1614 | mutex_lock(&dsa2_mutex); | |
8fd36358 VO |
1615 | |
1616 | if (!ds->setup) | |
1617 | goto out; | |
1618 | ||
0650bf52 VO |
1619 | rtnl_lock(); |
1620 | ||
6c24a03a VO |
1621 | dsa_switch_for_each_cpu_port(dp, ds) |
1622 | list_add(&dp->conduit->close_list, &close_list); | |
1623 | ||
1624 | dev_close_many(&close_list, true); | |
1625 | ||
65c563a6 | 1626 | dsa_switch_for_each_user_port(dp, ds) { |
6ca80638 FF |
1627 | conduit = dsa_port_to_conduit(dp); |
1628 | user_dev = dp->user; | |
0650bf52 | 1629 | |
6c24a03a | 1630 | netif_device_detach(user_dev); |
6ca80638 | 1631 | netdev_upper_dev_unlink(conduit, user_dev); |
0650bf52 | 1632 | } |
ee534378 | 1633 | |
6ca80638 | 1634 | /* Disconnect from further netdevice notifiers on the conduit, |
ee534378 VO |
1635 | * since netdev_uses_dsa() will now return false. |
1636 | */ | |
1637 | dsa_switch_for_each_cpu_port(dp, ds) | |
6ca80638 | 1638 | dp->conduit->dsa_ptr = NULL; |
0650bf52 VO |
1639 | |
1640 | rtnl_unlock(); | |
8fd36358 | 1641 | out: |
0650bf52 VO |
1642 | mutex_unlock(&dsa2_mutex); |
1643 | } | |
1644 | EXPORT_SYMBOL_GPL(dsa_switch_shutdown); | |
165c2fb9 VO |
1645 | |
1646 | #ifdef CONFIG_PM_SLEEP | |
1647 | static bool dsa_port_is_initialized(const struct dsa_port *dp) | |
1648 | { | |
6ca80638 | 1649 | return dp->type == DSA_PORT_TYPE_USER && dp->user; |
165c2fb9 VO |
1650 | } |
1651 | ||
1652 | int dsa_switch_suspend(struct dsa_switch *ds) | |
1653 | { | |
1654 | struct dsa_port *dp; | |
1655 | int ret = 0; | |
1656 | ||
6ca80638 | 1657 | /* Suspend user network devices */ |
165c2fb9 VO |
1658 | dsa_switch_for_each_port(dp, ds) { |
1659 | if (!dsa_port_is_initialized(dp)) | |
1660 | continue; | |
1661 | ||
6ca80638 | 1662 | ret = dsa_user_suspend(dp->user); |
165c2fb9 VO |
1663 | if (ret) |
1664 | return ret; | |
1665 | } | |
1666 | ||
1667 | if (ds->ops->suspend) | |
1668 | ret = ds->ops->suspend(ds); | |
1669 | ||
1670 | return ret; | |
1671 | } | |
1672 | EXPORT_SYMBOL_GPL(dsa_switch_suspend); | |
1673 | ||
1674 | int dsa_switch_resume(struct dsa_switch *ds) | |
1675 | { | |
1676 | struct dsa_port *dp; | |
1677 | int ret = 0; | |
1678 | ||
1679 | if (ds->ops->resume) | |
1680 | ret = ds->ops->resume(ds); | |
1681 | ||
1682 | if (ret) | |
1683 | return ret; | |
1684 | ||
6ca80638 | 1685 | /* Resume user network devices */ |
165c2fb9 VO |
1686 | dsa_switch_for_each_port(dp, ds) { |
1687 | if (!dsa_port_is_initialized(dp)) | |
1688 | continue; | |
1689 | ||
6ca80638 | 1690 | ret = dsa_user_resume(dp->user); |
165c2fb9 VO |
1691 | if (ret) |
1692 | return ret; | |
1693 | } | |
1694 | ||
1695 | return 0; | |
1696 | } | |
1697 | EXPORT_SYMBOL_GPL(dsa_switch_resume); | |
1698 | #endif | |
1699 | ||
1700 | struct dsa_port *dsa_port_from_netdev(struct net_device *netdev) | |
1701 | { | |
6ca80638 | 1702 | if (!netdev || !dsa_user_dev_check(netdev)) |
165c2fb9 VO |
1703 | return ERR_PTR(-ENODEV); |
1704 | ||
6ca80638 | 1705 | return dsa_user_to_port(netdev); |
165c2fb9 VO |
1706 | } |
1707 | EXPORT_SYMBOL_GPL(dsa_port_from_netdev); | |
1708 | ||
1709 | bool dsa_db_equal(const struct dsa_db *a, const struct dsa_db *b) | |
1710 | { | |
1711 | if (a->type != b->type) | |
1712 | return false; | |
1713 | ||
1714 | switch (a->type) { | |
1715 | case DSA_DB_PORT: | |
1716 | return a->dp == b->dp; | |
1717 | case DSA_DB_LAG: | |
1718 | return a->lag.dev == b->lag.dev; | |
1719 | case DSA_DB_BRIDGE: | |
1720 | return a->bridge.num == b->bridge.num; | |
1721 | default: | |
1722 | WARN_ON(1); | |
1723 | return false; | |
1724 | } | |
1725 | } | |
1726 | ||
1727 | bool dsa_fdb_present_in_other_db(struct dsa_switch *ds, int port, | |
1728 | const unsigned char *addr, u16 vid, | |
1729 | struct dsa_db db) | |
1730 | { | |
1731 | struct dsa_port *dp = dsa_to_port(ds, port); | |
1732 | struct dsa_mac_addr *a; | |
1733 | ||
1734 | lockdep_assert_held(&dp->addr_lists_lock); | |
1735 | ||
1736 | list_for_each_entry(a, &dp->fdbs, list) { | |
1737 | if (!ether_addr_equal(a->addr, addr) || a->vid != vid) | |
1738 | continue; | |
1739 | ||
1740 | if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) | |
1741 | return true; | |
1742 | } | |
1743 | ||
1744 | return false; | |
1745 | } | |
1746 | EXPORT_SYMBOL_GPL(dsa_fdb_present_in_other_db); | |
1747 | ||
1748 | bool dsa_mdb_present_in_other_db(struct dsa_switch *ds, int port, | |
1749 | const struct switchdev_obj_port_mdb *mdb, | |
1750 | struct dsa_db db) | |
1751 | { | |
1752 | struct dsa_port *dp = dsa_to_port(ds, port); | |
1753 | struct dsa_mac_addr *a; | |
1754 | ||
1755 | lockdep_assert_held(&dp->addr_lists_lock); | |
1756 | ||
1757 | list_for_each_entry(a, &dp->mdbs, list) { | |
1758 | if (!ether_addr_equal(a->addr, mdb->addr) || a->vid != mdb->vid) | |
1759 | continue; | |
1760 | ||
1761 | if (a->db.type == db.type && !dsa_db_equal(&a->db, &db)) | |
1762 | return true; | |
1763 | } | |
1764 | ||
1765 | return false; | |
1766 | } | |
1767 | EXPORT_SYMBOL_GPL(dsa_mdb_present_in_other_db); | |
1768 | ||
5a178186 | 1769 | static const struct dsa_stubs __dsa_stubs = { |
6ca80638 | 1770 | .conduit_hwtstamp_validate = __dsa_conduit_hwtstamp_validate, |
5a178186 VO |
1771 | }; |
1772 | ||
1773 | static void dsa_register_stubs(void) | |
1774 | { | |
1775 | dsa_stubs = &__dsa_stubs; | |
1776 | } | |
1777 | ||
1778 | static void dsa_unregister_stubs(void) | |
1779 | { | |
1780 | dsa_stubs = NULL; | |
1781 | } | |
1782 | ||
165c2fb9 VO |
1783 | static int __init dsa_init_module(void) |
1784 | { | |
1785 | int rc; | |
1786 | ||
1787 | dsa_owq = alloc_ordered_workqueue("dsa_ordered", | |
1788 | WQ_MEM_RECLAIM); | |
1789 | if (!dsa_owq) | |
1790 | return -ENOMEM; | |
1791 | ||
6ca80638 | 1792 | rc = dsa_user_register_notifier(); |
165c2fb9 VO |
1793 | if (rc) |
1794 | goto register_notifier_fail; | |
1795 | ||
1796 | dev_add_pack(&dsa_pack_type); | |
1797 | ||
1798 | rc = rtnl_link_register(&dsa_link_ops); | |
1799 | if (rc) | |
1800 | goto netlink_register_fail; | |
1801 | ||
5a178186 VO |
1802 | dsa_register_stubs(); |
1803 | ||
165c2fb9 VO |
1804 | return 0; |
1805 | ||
1806 | netlink_register_fail: | |
6ca80638 | 1807 | dsa_user_unregister_notifier(); |
165c2fb9 VO |
1808 | dev_remove_pack(&dsa_pack_type); |
1809 | register_notifier_fail: | |
1810 | destroy_workqueue(dsa_owq); | |
1811 | ||
1812 | return rc; | |
1813 | } | |
1814 | module_init(dsa_init_module); | |
1815 | ||
1816 | static void __exit dsa_cleanup_module(void) | |
1817 | { | |
5a178186 VO |
1818 | dsa_unregister_stubs(); |
1819 | ||
165c2fb9 VO |
1820 | rtnl_link_unregister(&dsa_link_ops); |
1821 | ||
6ca80638 | 1822 | dsa_user_unregister_notifier(); |
165c2fb9 VO |
1823 | dev_remove_pack(&dsa_pack_type); |
1824 | destroy_workqueue(dsa_owq); | |
1825 | } | |
1826 | module_exit(dsa_cleanup_module); | |
1827 | ||
1828 | MODULE_AUTHOR("Lennert Buytenhek <buytenh@wantstofly.org>"); | |
1829 | MODULE_DESCRIPTION("Driver for Distributed Switch Architecture switch chips"); | |
1830 | MODULE_LICENSE("GPL"); | |
1831 | MODULE_ALIAS("platform:dsa"); |