1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Handling of a single switch chip, part of a switch fabric |
4 | * |
5 | * Copyright (c) 2017 Savoir-faire Linux Inc. |
6 | * Vivien Didelot <vivien.didelot@savoirfairelinux.com> |
7 | */ |
8 | |
9 | #include <linux/if_bridge.h> |
10 | #include <linux/netdevice.h> |
11 | #include <linux/notifier.h> |
12 | #include <linux/if_vlan.h> |
13 | #include <net/switchdev.h> |
14 | |
15 | #include "dsa.h" |
16 | #include "netlink.h" |
17 | #include "port.h" |
18 | #include "switch.h" |
19 | #include "tag_8021q.h" |
20 | #include "trace.h" |
21 | #include "user.h" |
22 | |
23 | static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, |
24 | unsigned int ageing_time) |
25 | { |
26 | struct dsa_port *dp; |
27 | |
28 | dsa_switch_for_each_port(dp, ds) |
29 | if (dp->ageing_time && dp->ageing_time < ageing_time) |
30 | ageing_time = dp->ageing_time; |
31 | |
32 | return ageing_time; |
33 | } |
34 | |
35 | static int dsa_switch_ageing_time(struct dsa_switch *ds, |
36 | struct dsa_notifier_ageing_time_info *info) |
37 | { |
38 | unsigned int ageing_time = info->ageing_time; |
39 | |
40 | if (ds->ageing_time_min && ageing_time < ds->ageing_time_min) |
41 | return -ERANGE; |
42 | |
43 | if (ds->ageing_time_max && ageing_time > ds->ageing_time_max) |
44 | return -ERANGE; |
45 | |
46 | /* Program the fastest ageing time in case of multiple bridges */ |
47 | ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time); |
48 | |
49 | if (ds->ops->set_ageing_time) |
50 | return ds->ops->set_ageing_time(ds, ageing_time); |
51 | |
52 | return 0; |
53 | } |
54 | |
55 | static bool dsa_port_mtu_match(struct dsa_port *dp, |
56 | struct dsa_notifier_mtu_info *info) |
57 | { |
58 | return dp == info->dp || dsa_port_is_dsa(port: dp) || dsa_port_is_cpu(port: dp); |
59 | } |
60 | |
61 | static int dsa_switch_mtu(struct dsa_switch *ds, |
62 | struct dsa_notifier_mtu_info *info) |
63 | { |
64 | struct dsa_port *dp; |
65 | int ret; |
66 | |
67 | if (!ds->ops->port_change_mtu) |
68 | return -EOPNOTSUPP; |
69 | |
70 | dsa_switch_for_each_port(dp, ds) { |
71 | if (dsa_port_mtu_match(dp, info)) { |
72 | ret = ds->ops->port_change_mtu(ds, dp->index, |
73 | info->mtu); |
74 | if (ret) |
75 | return ret; |
76 | } |
77 | } |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static int dsa_switch_bridge_join(struct dsa_switch *ds, |
83 | struct dsa_notifier_bridge_info *info) |
84 | { |
85 | int err; |
86 | |
87 | if (info->dp->ds == ds) { |
88 | if (!ds->ops->port_bridge_join) |
89 | return -EOPNOTSUPP; |
90 | |
91 | err = ds->ops->port_bridge_join(ds, info->dp->index, |
92 | info->bridge, |
93 | &info->tx_fwd_offload, |
94 | info->extack); |
95 | if (err) |
96 | return err; |
97 | } |
98 | |
99 | if (info->dp->ds != ds && ds->ops->crosschip_bridge_join) { |
100 | err = ds->ops->crosschip_bridge_join(ds, |
101 | info->dp->ds->dst->index, |
102 | info->dp->ds->index, |
103 | info->dp->index, |
104 | info->bridge, |
105 | info->extack); |
106 | if (err) |
107 | return err; |
108 | } |
109 | |
110 | return 0; |
111 | } |
112 | |
113 | static int dsa_switch_bridge_leave(struct dsa_switch *ds, |
114 | struct dsa_notifier_bridge_info *info) |
115 | { |
116 | if (info->dp->ds == ds && ds->ops->port_bridge_leave) |
117 | ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge); |
118 | |
119 | if (info->dp->ds != ds && ds->ops->crosschip_bridge_leave) |
120 | ds->ops->crosschip_bridge_leave(ds, info->dp->ds->dst->index, |
121 | info->dp->ds->index, |
122 | info->dp->index, |
123 | info->bridge); |
124 | |
125 | return 0; |
126 | } |
127 | |
128 | /* Matches for all upstream-facing ports (the CPU port and all upstream-facing |
129 | * DSA links) that sit between the targeted port on which the notifier was |
130 | * emitted and its dedicated CPU port. |
131 | */ |
132 | static bool dsa_port_host_address_match(struct dsa_port *dp, |
133 | const struct dsa_port *targeted_dp) |
134 | { |
135 | struct dsa_port *cpu_dp = targeted_dp->cpu_dp; |
136 | |
137 | if (dsa_switch_is_upstream_of(upstream_ds: dp->ds, downstream_ds: targeted_dp->ds)) |
138 | return dp->index == dsa_towards_port(ds: dp->ds, device: cpu_dp->ds->index, |
139 | port: cpu_dp->index); |
140 | |
141 | return false; |
142 | } |
143 | |
144 | static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, |
145 | const unsigned char *addr, u16 vid, |
146 | struct dsa_db db) |
147 | { |
148 | struct dsa_mac_addr *a; |
149 | |
150 | list_for_each_entry(a, addr_list, list) |
151 | if (ether_addr_equal(addr1: a->addr, addr2: addr) && a->vid == vid && |
152 | dsa_db_equal(a: &a->db, b: &db)) |
153 | return a; |
154 | |
155 | return NULL; |
156 | } |
157 | |
158 | static int dsa_port_do_mdb_add(struct dsa_port *dp, |
159 | const struct switchdev_obj_port_mdb *mdb, |
160 | struct dsa_db db) |
161 | { |
162 | struct dsa_switch *ds = dp->ds; |
163 | struct dsa_mac_addr *a; |
164 | int port = dp->index; |
165 | int err = 0; |
166 | |
167 | /* No need to bother with refcounting for user ports */ |
168 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
169 | err = ds->ops->port_mdb_add(ds, port, mdb, db); |
170 | trace_dsa_mdb_add_hw(dp, addr: mdb->addr, vid: mdb->vid, db: &db, err); |
171 | |
172 | return err; |
173 | } |
174 | |
175 | mutex_lock(&dp->addr_lists_lock); |
176 | |
177 | a = dsa_mac_addr_find(addr_list: &dp->mdbs, addr: mdb->addr, vid: mdb->vid, db); |
178 | if (a) { |
179 | refcount_inc(r: &a->refcount); |
180 | trace_dsa_mdb_add_bump(dp, addr: mdb->addr, vid: mdb->vid, db: &db, |
181 | refcount: &a->refcount); |
182 | goto out; |
183 | } |
184 | |
185 | a = kzalloc(size: sizeof(*a), GFP_KERNEL); |
186 | if (!a) { |
187 | err = -ENOMEM; |
188 | goto out; |
189 | } |
190 | |
191 | err = ds->ops->port_mdb_add(ds, port, mdb, db); |
192 | trace_dsa_mdb_add_hw(dp, addr: mdb->addr, vid: mdb->vid, db: &db, err); |
193 | if (err) { |
194 | kfree(objp: a); |
195 | goto out; |
196 | } |
197 | |
198 | ether_addr_copy(dst: a->addr, src: mdb->addr); |
199 | a->vid = mdb->vid; |
200 | a->db = db; |
201 | refcount_set(r: &a->refcount, n: 1); |
202 | list_add_tail(new: &a->list, head: &dp->mdbs); |
203 | |
204 | out: |
205 | mutex_unlock(lock: &dp->addr_lists_lock); |
206 | |
207 | return err; |
208 | } |
209 | |
210 | static int dsa_port_do_mdb_del(struct dsa_port *dp, |
211 | const struct switchdev_obj_port_mdb *mdb, |
212 | struct dsa_db db) |
213 | { |
214 | struct dsa_switch *ds = dp->ds; |
215 | struct dsa_mac_addr *a; |
216 | int port = dp->index; |
217 | int err = 0; |
218 | |
219 | /* No need to bother with refcounting for user ports */ |
220 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
221 | err = ds->ops->port_mdb_del(ds, port, mdb, db); |
222 | trace_dsa_mdb_del_hw(dp, addr: mdb->addr, vid: mdb->vid, db: &db, err); |
223 | |
224 | return err; |
225 | } |
226 | |
227 | mutex_lock(&dp->addr_lists_lock); |
228 | |
229 | a = dsa_mac_addr_find(addr_list: &dp->mdbs, addr: mdb->addr, vid: mdb->vid, db); |
230 | if (!a) { |
231 | trace_dsa_mdb_del_not_found(dp, addr: mdb->addr, vid: mdb->vid, db: &db); |
232 | err = -ENOENT; |
233 | goto out; |
234 | } |
235 | |
236 | if (!refcount_dec_and_test(r: &a->refcount)) { |
237 | trace_dsa_mdb_del_drop(dp, addr: mdb->addr, vid: mdb->vid, db: &db, |
238 | refcount: &a->refcount); |
239 | goto out; |
240 | } |
241 | |
242 | err = ds->ops->port_mdb_del(ds, port, mdb, db); |
243 | trace_dsa_mdb_del_hw(dp, addr: mdb->addr, vid: mdb->vid, db: &db, err); |
244 | if (err) { |
245 | refcount_set(r: &a->refcount, n: 1); |
246 | goto out; |
247 | } |
248 | |
249 | list_del(entry: &a->list); |
250 | kfree(objp: a); |
251 | |
252 | out: |
253 | mutex_unlock(lock: &dp->addr_lists_lock); |
254 | |
255 | return err; |
256 | } |
257 | |
258 | static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, |
259 | u16 vid, struct dsa_db db) |
260 | { |
261 | struct dsa_switch *ds = dp->ds; |
262 | struct dsa_mac_addr *a; |
263 | int port = dp->index; |
264 | int err = 0; |
265 | |
266 | /* No need to bother with refcounting for user ports */ |
267 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
268 | err = ds->ops->port_fdb_add(ds, port, addr, vid, db); |
269 | trace_dsa_fdb_add_hw(dp, addr, vid, db: &db, err); |
270 | |
271 | return err; |
272 | } |
273 | |
274 | mutex_lock(&dp->addr_lists_lock); |
275 | |
276 | a = dsa_mac_addr_find(addr_list: &dp->fdbs, addr, vid, db); |
277 | if (a) { |
278 | refcount_inc(r: &a->refcount); |
279 | trace_dsa_fdb_add_bump(dp, addr, vid, db: &db, refcount: &a->refcount); |
280 | goto out; |
281 | } |
282 | |
283 | a = kzalloc(size: sizeof(*a), GFP_KERNEL); |
284 | if (!a) { |
285 | err = -ENOMEM; |
286 | goto out; |
287 | } |
288 | |
289 | err = ds->ops->port_fdb_add(ds, port, addr, vid, db); |
290 | trace_dsa_fdb_add_hw(dp, addr, vid, db: &db, err); |
291 | if (err) { |
292 | kfree(objp: a); |
293 | goto out; |
294 | } |
295 | |
296 | ether_addr_copy(dst: a->addr, src: addr); |
297 | a->vid = vid; |
298 | a->db = db; |
299 | refcount_set(r: &a->refcount, n: 1); |
300 | list_add_tail(new: &a->list, head: &dp->fdbs); |
301 | |
302 | out: |
303 | mutex_unlock(lock: &dp->addr_lists_lock); |
304 | |
305 | return err; |
306 | } |
307 | |
308 | static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, |
309 | u16 vid, struct dsa_db db) |
310 | { |
311 | struct dsa_switch *ds = dp->ds; |
312 | struct dsa_mac_addr *a; |
313 | int port = dp->index; |
314 | int err = 0; |
315 | |
316 | /* No need to bother with refcounting for user ports */ |
317 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
318 | err = ds->ops->port_fdb_del(ds, port, addr, vid, db); |
319 | trace_dsa_fdb_del_hw(dp, addr, vid, db: &db, err); |
320 | |
321 | return err; |
322 | } |
323 | |
324 | mutex_lock(&dp->addr_lists_lock); |
325 | |
326 | a = dsa_mac_addr_find(addr_list: &dp->fdbs, addr, vid, db); |
327 | if (!a) { |
328 | trace_dsa_fdb_del_not_found(dp, addr, vid, db: &db); |
329 | err = -ENOENT; |
330 | goto out; |
331 | } |
332 | |
333 | if (!refcount_dec_and_test(r: &a->refcount)) { |
334 | trace_dsa_fdb_del_drop(dp, addr, vid, db: &db, refcount: &a->refcount); |
335 | goto out; |
336 | } |
337 | |
338 | err = ds->ops->port_fdb_del(ds, port, addr, vid, db); |
339 | trace_dsa_fdb_del_hw(dp, addr, vid, db: &db, err); |
340 | if (err) { |
341 | refcount_set(r: &a->refcount, n: 1); |
342 | goto out; |
343 | } |
344 | |
345 | list_del(entry: &a->list); |
346 | kfree(objp: a); |
347 | |
348 | out: |
349 | mutex_unlock(lock: &dp->addr_lists_lock); |
350 | |
351 | return err; |
352 | } |
353 | |
354 | static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, |
355 | const unsigned char *addr, u16 vid, |
356 | struct dsa_db db) |
357 | { |
358 | struct dsa_mac_addr *a; |
359 | int err = 0; |
360 | |
361 | mutex_lock(&lag->fdb_lock); |
362 | |
363 | a = dsa_mac_addr_find(addr_list: &lag->fdbs, addr, vid, db); |
364 | if (a) { |
365 | refcount_inc(r: &a->refcount); |
366 | trace_dsa_lag_fdb_add_bump(lag_dev: lag->dev, addr, vid, db: &db, |
367 | refcount: &a->refcount); |
368 | goto out; |
369 | } |
370 | |
371 | a = kzalloc(size: sizeof(*a), GFP_KERNEL); |
372 | if (!a) { |
373 | err = -ENOMEM; |
374 | goto out; |
375 | } |
376 | |
377 | err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); |
378 | trace_dsa_lag_fdb_add_hw(lag_dev: lag->dev, addr, vid, db: &db, err); |
379 | if (err) { |
380 | kfree(objp: a); |
381 | goto out; |
382 | } |
383 | |
384 | ether_addr_copy(dst: a->addr, src: addr); |
385 | a->vid = vid; |
386 | a->db = db; |
387 | refcount_set(r: &a->refcount, n: 1); |
388 | list_add_tail(new: &a->list, head: &lag->fdbs); |
389 | |
390 | out: |
391 | mutex_unlock(lock: &lag->fdb_lock); |
392 | |
393 | return err; |
394 | } |
395 | |
396 | static int dsa_switch_do_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag *lag, |
397 | const unsigned char *addr, u16 vid, |
398 | struct dsa_db db) |
399 | { |
400 | struct dsa_mac_addr *a; |
401 | int err = 0; |
402 | |
403 | mutex_lock(&lag->fdb_lock); |
404 | |
405 | a = dsa_mac_addr_find(addr_list: &lag->fdbs, addr, vid, db); |
406 | if (!a) { |
407 | trace_dsa_lag_fdb_del_not_found(lag_dev: lag->dev, addr, vid, db: &db); |
408 | err = -ENOENT; |
409 | goto out; |
410 | } |
411 | |
412 | if (!refcount_dec_and_test(r: &a->refcount)) { |
413 | trace_dsa_lag_fdb_del_drop(lag_dev: lag->dev, addr, vid, db: &db, |
414 | refcount: &a->refcount); |
415 | goto out; |
416 | } |
417 | |
418 | err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); |
419 | trace_dsa_lag_fdb_del_hw(lag_dev: lag->dev, addr, vid, db: &db, err); |
420 | if (err) { |
421 | refcount_set(r: &a->refcount, n: 1); |
422 | goto out; |
423 | } |
424 | |
425 | list_del(entry: &a->list); |
426 | kfree(objp: a); |
427 | |
428 | out: |
429 | mutex_unlock(lock: &lag->fdb_lock); |
430 | |
431 | return err; |
432 | } |
433 | |
434 | static int dsa_switch_host_fdb_add(struct dsa_switch *ds, |
435 | struct dsa_notifier_fdb_info *info) |
436 | { |
437 | struct dsa_port *dp; |
438 | int err = 0; |
439 | |
440 | if (!ds->ops->port_fdb_add) |
441 | return -EOPNOTSUPP; |
442 | |
443 | dsa_switch_for_each_port(dp, ds) { |
444 | if (dsa_port_host_address_match(dp, targeted_dp: info->dp)) { |
445 | if (dsa_port_is_cpu(port: dp) && info->dp->cpu_port_in_lag) { |
446 | err = dsa_switch_do_lag_fdb_add(ds, lag: dp->lag, |
447 | addr: info->addr, |
448 | vid: info->vid, |
449 | db: info->db); |
450 | } else { |
451 | err = dsa_port_do_fdb_add(dp, addr: info->addr, |
452 | vid: info->vid, db: info->db); |
453 | } |
454 | if (err) |
455 | break; |
456 | } |
457 | } |
458 | |
459 | return err; |
460 | } |
461 | |
462 | static int dsa_switch_host_fdb_del(struct dsa_switch *ds, |
463 | struct dsa_notifier_fdb_info *info) |
464 | { |
465 | struct dsa_port *dp; |
466 | int err = 0; |
467 | |
468 | if (!ds->ops->port_fdb_del) |
469 | return -EOPNOTSUPP; |
470 | |
471 | dsa_switch_for_each_port(dp, ds) { |
472 | if (dsa_port_host_address_match(dp, targeted_dp: info->dp)) { |
473 | if (dsa_port_is_cpu(port: dp) && info->dp->cpu_port_in_lag) { |
474 | err = dsa_switch_do_lag_fdb_del(ds, lag: dp->lag, |
475 | addr: info->addr, |
476 | vid: info->vid, |
477 | db: info->db); |
478 | } else { |
479 | err = dsa_port_do_fdb_del(dp, addr: info->addr, |
480 | vid: info->vid, db: info->db); |
481 | } |
482 | if (err) |
483 | break; |
484 | } |
485 | } |
486 | |
487 | return err; |
488 | } |
489 | |
490 | static int dsa_switch_fdb_add(struct dsa_switch *ds, |
491 | struct dsa_notifier_fdb_info *info) |
492 | { |
493 | int port = dsa_towards_port(ds, device: info->dp->ds->index, port: info->dp->index); |
494 | struct dsa_port *dp = dsa_to_port(ds, p: port); |
495 | |
496 | if (!ds->ops->port_fdb_add) |
497 | return -EOPNOTSUPP; |
498 | |
499 | return dsa_port_do_fdb_add(dp, addr: info->addr, vid: info->vid, db: info->db); |
500 | } |
501 | |
502 | static int dsa_switch_fdb_del(struct dsa_switch *ds, |
503 | struct dsa_notifier_fdb_info *info) |
504 | { |
505 | int port = dsa_towards_port(ds, device: info->dp->ds->index, port: info->dp->index); |
506 | struct dsa_port *dp = dsa_to_port(ds, p: port); |
507 | |
508 | if (!ds->ops->port_fdb_del) |
509 | return -EOPNOTSUPP; |
510 | |
511 | return dsa_port_do_fdb_del(dp, addr: info->addr, vid: info->vid, db: info->db); |
512 | } |
513 | |
514 | static int dsa_switch_lag_fdb_add(struct dsa_switch *ds, |
515 | struct dsa_notifier_lag_fdb_info *info) |
516 | { |
517 | struct dsa_port *dp; |
518 | |
519 | if (!ds->ops->lag_fdb_add) |
520 | return -EOPNOTSUPP; |
521 | |
522 | /* Notify switch only if it has a port in this LAG */ |
523 | dsa_switch_for_each_port(dp, ds) |
524 | if (dsa_port_offloads_lag(dp, lag: info->lag)) |
525 | return dsa_switch_do_lag_fdb_add(ds, lag: info->lag, |
526 | addr: info->addr, vid: info->vid, |
527 | db: info->db); |
528 | |
529 | return 0; |
530 | } |
531 | |
532 | static int dsa_switch_lag_fdb_del(struct dsa_switch *ds, |
533 | struct dsa_notifier_lag_fdb_info *info) |
534 | { |
535 | struct dsa_port *dp; |
536 | |
537 | if (!ds->ops->lag_fdb_del) |
538 | return -EOPNOTSUPP; |
539 | |
540 | /* Notify switch only if it has a port in this LAG */ |
541 | dsa_switch_for_each_port(dp, ds) |
542 | if (dsa_port_offloads_lag(dp, lag: info->lag)) |
543 | return dsa_switch_do_lag_fdb_del(ds, lag: info->lag, |
544 | addr: info->addr, vid: info->vid, |
545 | db: info->db); |
546 | |
547 | return 0; |
548 | } |
549 | |
550 | static int dsa_switch_lag_change(struct dsa_switch *ds, |
551 | struct dsa_notifier_lag_info *info) |
552 | { |
553 | if (info->dp->ds == ds && ds->ops->port_lag_change) |
554 | return ds->ops->port_lag_change(ds, info->dp->index); |
555 | |
556 | if (info->dp->ds != ds && ds->ops->crosschip_lag_change) |
557 | return ds->ops->crosschip_lag_change(ds, info->dp->ds->index, |
558 | info->dp->index); |
559 | |
560 | return 0; |
561 | } |
562 | |
563 | static int dsa_switch_lag_join(struct dsa_switch *ds, |
564 | struct dsa_notifier_lag_info *info) |
565 | { |
566 | if (info->dp->ds == ds && ds->ops->port_lag_join) |
567 | return ds->ops->port_lag_join(ds, info->dp->index, info->lag, |
568 | info->info, info->extack); |
569 | |
570 | if (info->dp->ds != ds && ds->ops->crosschip_lag_join) |
571 | return ds->ops->crosschip_lag_join(ds, info->dp->ds->index, |
572 | info->dp->index, info->lag, |
573 | info->info, info->extack); |
574 | |
575 | return -EOPNOTSUPP; |
576 | } |
577 | |
578 | static int dsa_switch_lag_leave(struct dsa_switch *ds, |
579 | struct dsa_notifier_lag_info *info) |
580 | { |
581 | if (info->dp->ds == ds && ds->ops->port_lag_leave) |
582 | return ds->ops->port_lag_leave(ds, info->dp->index, info->lag); |
583 | |
584 | if (info->dp->ds != ds && ds->ops->crosschip_lag_leave) |
585 | return ds->ops->crosschip_lag_leave(ds, info->dp->ds->index, |
586 | info->dp->index, info->lag); |
587 | |
588 | return -EOPNOTSUPP; |
589 | } |
590 | |
591 | static int dsa_switch_mdb_add(struct dsa_switch *ds, |
592 | struct dsa_notifier_mdb_info *info) |
593 | { |
594 | int port = dsa_towards_port(ds, device: info->dp->ds->index, port: info->dp->index); |
595 | struct dsa_port *dp = dsa_to_port(ds, p: port); |
596 | |
597 | if (!ds->ops->port_mdb_add) |
598 | return -EOPNOTSUPP; |
599 | |
600 | return dsa_port_do_mdb_add(dp, mdb: info->mdb, db: info->db); |
601 | } |
602 | |
603 | static int dsa_switch_mdb_del(struct dsa_switch *ds, |
604 | struct dsa_notifier_mdb_info *info) |
605 | { |
606 | int port = dsa_towards_port(ds, device: info->dp->ds->index, port: info->dp->index); |
607 | struct dsa_port *dp = dsa_to_port(ds, p: port); |
608 | |
609 | if (!ds->ops->port_mdb_del) |
610 | return -EOPNOTSUPP; |
611 | |
612 | return dsa_port_do_mdb_del(dp, mdb: info->mdb, db: info->db); |
613 | } |
614 | |
615 | static int dsa_switch_host_mdb_add(struct dsa_switch *ds, |
616 | struct dsa_notifier_mdb_info *info) |
617 | { |
618 | struct dsa_port *dp; |
619 | int err = 0; |
620 | |
621 | if (!ds->ops->port_mdb_add) |
622 | return -EOPNOTSUPP; |
623 | |
624 | dsa_switch_for_each_port(dp, ds) { |
625 | if (dsa_port_host_address_match(dp, targeted_dp: info->dp)) { |
626 | err = dsa_port_do_mdb_add(dp, mdb: info->mdb, db: info->db); |
627 | if (err) |
628 | break; |
629 | } |
630 | } |
631 | |
632 | return err; |
633 | } |
634 | |
635 | static int dsa_switch_host_mdb_del(struct dsa_switch *ds, |
636 | struct dsa_notifier_mdb_info *info) |
637 | { |
638 | struct dsa_port *dp; |
639 | int err = 0; |
640 | |
641 | if (!ds->ops->port_mdb_del) |
642 | return -EOPNOTSUPP; |
643 | |
644 | dsa_switch_for_each_port(dp, ds) { |
645 | if (dsa_port_host_address_match(dp, targeted_dp: info->dp)) { |
646 | err = dsa_port_do_mdb_del(dp, mdb: info->mdb, db: info->db); |
647 | if (err) |
648 | break; |
649 | } |
650 | } |
651 | |
652 | return err; |
653 | } |
654 | |
655 | /* Port VLANs match on the targeted port and on all DSA ports */ |
656 | static bool dsa_port_vlan_match(struct dsa_port *dp, |
657 | struct dsa_notifier_vlan_info *info) |
658 | { |
659 | return dsa_port_is_dsa(port: dp) || dp == info->dp; |
660 | } |
661 | |
662 | /* Host VLANs match on the targeted port's CPU port, and on all DSA ports |
663 | * (upstream and downstream) of that switch and its upstream switches. |
664 | */ |
665 | static bool dsa_port_host_vlan_match(struct dsa_port *dp, |
666 | const struct dsa_port *targeted_dp) |
667 | { |
668 | struct dsa_port *cpu_dp = targeted_dp->cpu_dp; |
669 | |
670 | if (dsa_switch_is_upstream_of(upstream_ds: dp->ds, downstream_ds: targeted_dp->ds)) |
671 | return dsa_port_is_dsa(port: dp) || dp == cpu_dp; |
672 | |
673 | return false; |
674 | } |
675 | |
676 | struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, |
677 | const struct switchdev_obj_port_vlan *vlan) |
678 | { |
679 | struct dsa_vlan *v; |
680 | |
681 | list_for_each_entry(v, vlan_list, list) |
682 | if (v->vid == vlan->vid) |
683 | return v; |
684 | |
685 | return NULL; |
686 | } |
687 | |
688 | static int dsa_port_do_vlan_add(struct dsa_port *dp, |
689 | const struct switchdev_obj_port_vlan *vlan, |
690 | struct netlink_ext_ack *extack) |
691 | { |
692 | struct dsa_switch *ds = dp->ds; |
693 | int port = dp->index; |
694 | struct dsa_vlan *v; |
695 | int err = 0; |
696 | |
697 | /* No need to bother with refcounting for user ports. */ |
698 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
699 | err = ds->ops->port_vlan_add(ds, port, vlan, extack); |
700 | trace_dsa_vlan_add_hw(dp, vlan, err); |
701 | |
702 | return err; |
703 | } |
704 | |
705 | /* No need to propagate on shared ports the existing VLANs that were |
706 | * re-notified after just the flags have changed. This would cause a |
707 | * refcount bump which we need to avoid, since it unbalances the |
708 | * additions with the deletions. |
709 | */ |
710 | if (vlan->changed) |
711 | return 0; |
712 | |
713 | mutex_lock(&dp->vlans_lock); |
714 | |
715 | v = dsa_vlan_find(vlan_list: &dp->vlans, vlan); |
716 | if (v) { |
717 | refcount_inc(r: &v->refcount); |
718 | trace_dsa_vlan_add_bump(dp, vlan, refcount: &v->refcount); |
719 | goto out; |
720 | } |
721 | |
722 | v = kzalloc(size: sizeof(*v), GFP_KERNEL); |
723 | if (!v) { |
724 | err = -ENOMEM; |
725 | goto out; |
726 | } |
727 | |
728 | err = ds->ops->port_vlan_add(ds, port, vlan, extack); |
729 | trace_dsa_vlan_add_hw(dp, vlan, err); |
730 | if (err) { |
731 | kfree(objp: v); |
732 | goto out; |
733 | } |
734 | |
735 | v->vid = vlan->vid; |
736 | refcount_set(r: &v->refcount, n: 1); |
737 | list_add_tail(new: &v->list, head: &dp->vlans); |
738 | |
739 | out: |
740 | mutex_unlock(lock: &dp->vlans_lock); |
741 | |
742 | return err; |
743 | } |
744 | |
745 | static int dsa_port_do_vlan_del(struct dsa_port *dp, |
746 | const struct switchdev_obj_port_vlan *vlan) |
747 | { |
748 | struct dsa_switch *ds = dp->ds; |
749 | int port = dp->index; |
750 | struct dsa_vlan *v; |
751 | int err = 0; |
752 | |
753 | /* No need to bother with refcounting for user ports */ |
754 | if (!(dsa_port_is_cpu(port: dp) || dsa_port_is_dsa(port: dp))) { |
755 | err = ds->ops->port_vlan_del(ds, port, vlan); |
756 | trace_dsa_vlan_del_hw(dp, vlan, err); |
757 | |
758 | return err; |
759 | } |
760 | |
761 | mutex_lock(&dp->vlans_lock); |
762 | |
763 | v = dsa_vlan_find(vlan_list: &dp->vlans, vlan); |
764 | if (!v) { |
765 | trace_dsa_vlan_del_not_found(dp, vlan); |
766 | err = -ENOENT; |
767 | goto out; |
768 | } |
769 | |
770 | if (!refcount_dec_and_test(r: &v->refcount)) { |
771 | trace_dsa_vlan_del_drop(dp, vlan, refcount: &v->refcount); |
772 | goto out; |
773 | } |
774 | |
775 | err = ds->ops->port_vlan_del(ds, port, vlan); |
776 | trace_dsa_vlan_del_hw(dp, vlan, err); |
777 | if (err) { |
778 | refcount_set(r: &v->refcount, n: 1); |
779 | goto out; |
780 | } |
781 | |
782 | list_del(entry: &v->list); |
783 | kfree(objp: v); |
784 | |
785 | out: |
786 | mutex_unlock(lock: &dp->vlans_lock); |
787 | |
788 | return err; |
789 | } |
790 | |
791 | static int dsa_switch_vlan_add(struct dsa_switch *ds, |
792 | struct dsa_notifier_vlan_info *info) |
793 | { |
794 | struct dsa_port *dp; |
795 | int err; |
796 | |
797 | if (!ds->ops->port_vlan_add) |
798 | return -EOPNOTSUPP; |
799 | |
800 | dsa_switch_for_each_port(dp, ds) { |
801 | if (dsa_port_vlan_match(dp, info)) { |
802 | err = dsa_port_do_vlan_add(dp, vlan: info->vlan, |
803 | extack: info->extack); |
804 | if (err) |
805 | return err; |
806 | } |
807 | } |
808 | |
809 | return 0; |
810 | } |
811 | |
812 | static int dsa_switch_vlan_del(struct dsa_switch *ds, |
813 | struct dsa_notifier_vlan_info *info) |
814 | { |
815 | struct dsa_port *dp; |
816 | int err; |
817 | |
818 | if (!ds->ops->port_vlan_del) |
819 | return -EOPNOTSUPP; |
820 | |
821 | dsa_switch_for_each_port(dp, ds) { |
822 | if (dsa_port_vlan_match(dp, info)) { |
823 | err = dsa_port_do_vlan_del(dp, vlan: info->vlan); |
824 | if (err) |
825 | return err; |
826 | } |
827 | } |
828 | |
829 | return 0; |
830 | } |
831 | |
832 | static int dsa_switch_host_vlan_add(struct dsa_switch *ds, |
833 | struct dsa_notifier_vlan_info *info) |
834 | { |
835 | struct dsa_port *dp; |
836 | int err; |
837 | |
838 | if (!ds->ops->port_vlan_add) |
839 | return -EOPNOTSUPP; |
840 | |
841 | dsa_switch_for_each_port(dp, ds) { |
842 | if (dsa_port_host_vlan_match(dp, targeted_dp: info->dp)) { |
843 | err = dsa_port_do_vlan_add(dp, vlan: info->vlan, |
844 | extack: info->extack); |
845 | if (err) |
846 | return err; |
847 | } |
848 | } |
849 | |
850 | return 0; |
851 | } |
852 | |
853 | static int dsa_switch_host_vlan_del(struct dsa_switch *ds, |
854 | struct dsa_notifier_vlan_info *info) |
855 | { |
856 | struct dsa_port *dp; |
857 | int err; |
858 | |
859 | if (!ds->ops->port_vlan_del) |
860 | return -EOPNOTSUPP; |
861 | |
862 | dsa_switch_for_each_port(dp, ds) { |
863 | if (dsa_port_host_vlan_match(dp, targeted_dp: info->dp)) { |
864 | err = dsa_port_do_vlan_del(dp, vlan: info->vlan); |
865 | if (err) |
866 | return err; |
867 | } |
868 | } |
869 | |
870 | return 0; |
871 | } |
872 | |
873 | static int dsa_switch_change_tag_proto(struct dsa_switch *ds, |
874 | struct dsa_notifier_tag_proto_info *info) |
875 | { |
876 | const struct dsa_device_ops *tag_ops = info->tag_ops; |
877 | struct dsa_port *dp, *cpu_dp; |
878 | int err; |
879 | |
880 | if (!ds->ops->change_tag_protocol) |
881 | return -EOPNOTSUPP; |
882 | |
883 | ASSERT_RTNL(); |
884 | |
885 | err = ds->ops->change_tag_protocol(ds, tag_ops->proto); |
886 | if (err) |
887 | return err; |
888 | |
889 | dsa_switch_for_each_cpu_port(cpu_dp, ds) |
890 | dsa_port_set_tag_protocol(cpu_dp, tag_ops); |
891 | |
892 | /* Now that changing the tag protocol can no longer fail, let's update |
893 | * the remaining bits which are "duplicated for faster access", and the |
894 | * bits that depend on the tagger, such as the MTU. |
895 | */ |
896 | dsa_switch_for_each_user_port(dp, ds) { |
897 | struct net_device *user = dp->user; |
898 | |
899 | dsa_user_setup_tagger(user); |
900 | |
901 | /* rtnl_mutex is held in dsa_tree_change_tag_proto */ |
902 | dsa_user_change_mtu(dev: user, new_mtu: user->mtu); |
903 | } |
904 | |
905 | return 0; |
906 | } |
907 | |
908 | /* We use the same cross-chip notifiers to inform both the tagger side, as well |
909 | * as the switch side, of connection and disconnection events. |
910 | * Since ds->tagger_data is owned by the tagger, it isn't a hard error if the |
911 | * switch side doesn't support connecting to this tagger, and therefore, the |
912 | * fact that we don't disconnect the tagger side doesn't constitute a memory |
913 | * leak: the tagger will still operate with persistent per-switch memory, just |
914 | * with the switch side unconnected to it. What does constitute a hard error is |
915 | * when the switch side supports connecting but fails. |
916 | */ |
917 | static int |
918 | dsa_switch_connect_tag_proto(struct dsa_switch *ds, |
919 | struct dsa_notifier_tag_proto_info *info) |
920 | { |
921 | const struct dsa_device_ops *tag_ops = info->tag_ops; |
922 | int err; |
923 | |
924 | /* Notify the new tagger about the connection to this switch */ |
925 | if (tag_ops->connect) { |
926 | err = tag_ops->connect(ds); |
927 | if (err) |
928 | return err; |
929 | } |
930 | |
931 | if (!ds->ops->connect_tag_protocol) |
932 | return -EOPNOTSUPP; |
933 | |
934 | /* Notify the switch about the connection to the new tagger */ |
935 | err = ds->ops->connect_tag_protocol(ds, tag_ops->proto); |
936 | if (err) { |
937 | /* Revert the new tagger's connection to this tree */ |
938 | if (tag_ops->disconnect) |
939 | tag_ops->disconnect(ds); |
940 | return err; |
941 | } |
942 | |
943 | return 0; |
944 | } |
945 | |
946 | static int |
947 | dsa_switch_disconnect_tag_proto(struct dsa_switch *ds, |
948 | struct dsa_notifier_tag_proto_info *info) |
949 | { |
950 | const struct dsa_device_ops *tag_ops = info->tag_ops; |
951 | |
952 | /* Notify the tagger about the disconnection from this switch */ |
953 | if (tag_ops->disconnect && ds->tagger_data) |
954 | tag_ops->disconnect(ds); |
955 | |
956 | /* No need to notify the switch, since it shouldn't have any |
957 | * resources to tear down |
958 | */ |
959 | return 0; |
960 | } |
961 | |
962 | static int |
963 | dsa_switch_conduit_state_change(struct dsa_switch *ds, |
964 | struct dsa_notifier_conduit_state_info *info) |
965 | { |
966 | if (!ds->ops->conduit_state_change) |
967 | return 0; |
968 | |
969 | ds->ops->conduit_state_change(ds, info->conduit, info->operational); |
970 | |
971 | return 0; |
972 | } |
973 | |
974 | static int dsa_switch_event(struct notifier_block *nb, |
975 | unsigned long event, void *info) |
976 | { |
977 | struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb); |
978 | int err; |
979 | |
980 | switch (event) { |
981 | case DSA_NOTIFIER_AGEING_TIME: |
982 | err = dsa_switch_ageing_time(ds, info); |
983 | break; |
984 | case DSA_NOTIFIER_BRIDGE_JOIN: |
985 | err = dsa_switch_bridge_join(ds, info); |
986 | break; |
987 | case DSA_NOTIFIER_BRIDGE_LEAVE: |
988 | err = dsa_switch_bridge_leave(ds, info); |
989 | break; |
990 | case DSA_NOTIFIER_FDB_ADD: |
991 | err = dsa_switch_fdb_add(ds, info); |
992 | break; |
993 | case DSA_NOTIFIER_FDB_DEL: |
994 | err = dsa_switch_fdb_del(ds, info); |
995 | break; |
996 | case DSA_NOTIFIER_HOST_FDB_ADD: |
997 | err = dsa_switch_host_fdb_add(ds, info); |
998 | break; |
999 | case DSA_NOTIFIER_HOST_FDB_DEL: |
1000 | err = dsa_switch_host_fdb_del(ds, info); |
1001 | break; |
1002 | case DSA_NOTIFIER_LAG_FDB_ADD: |
1003 | err = dsa_switch_lag_fdb_add(ds, info); |
1004 | break; |
1005 | case DSA_NOTIFIER_LAG_FDB_DEL: |
1006 | err = dsa_switch_lag_fdb_del(ds, info); |
1007 | break; |
1008 | case DSA_NOTIFIER_LAG_CHANGE: |
1009 | err = dsa_switch_lag_change(ds, info); |
1010 | break; |
1011 | case DSA_NOTIFIER_LAG_JOIN: |
1012 | err = dsa_switch_lag_join(ds, info); |
1013 | break; |
1014 | case DSA_NOTIFIER_LAG_LEAVE: |
1015 | err = dsa_switch_lag_leave(ds, info); |
1016 | break; |
1017 | case DSA_NOTIFIER_MDB_ADD: |
1018 | err = dsa_switch_mdb_add(ds, info); |
1019 | break; |
1020 | case DSA_NOTIFIER_MDB_DEL: |
1021 | err = dsa_switch_mdb_del(ds, info); |
1022 | break; |
1023 | case DSA_NOTIFIER_HOST_MDB_ADD: |
1024 | err = dsa_switch_host_mdb_add(ds, info); |
1025 | break; |
1026 | case DSA_NOTIFIER_HOST_MDB_DEL: |
1027 | err = dsa_switch_host_mdb_del(ds, info); |
1028 | break; |
1029 | case DSA_NOTIFIER_VLAN_ADD: |
1030 | err = dsa_switch_vlan_add(ds, info); |
1031 | break; |
1032 | case DSA_NOTIFIER_VLAN_DEL: |
1033 | err = dsa_switch_vlan_del(ds, info); |
1034 | break; |
1035 | case DSA_NOTIFIER_HOST_VLAN_ADD: |
1036 | err = dsa_switch_host_vlan_add(ds, info); |
1037 | break; |
1038 | case DSA_NOTIFIER_HOST_VLAN_DEL: |
1039 | err = dsa_switch_host_vlan_del(ds, info); |
1040 | break; |
1041 | case DSA_NOTIFIER_MTU: |
1042 | err = dsa_switch_mtu(ds, info); |
1043 | break; |
1044 | case DSA_NOTIFIER_TAG_PROTO: |
1045 | err = dsa_switch_change_tag_proto(ds, info); |
1046 | break; |
1047 | case DSA_NOTIFIER_TAG_PROTO_CONNECT: |
1048 | err = dsa_switch_connect_tag_proto(ds, info); |
1049 | break; |
1050 | case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: |
1051 | err = dsa_switch_disconnect_tag_proto(ds, info); |
1052 | break; |
1053 | case DSA_NOTIFIER_TAG_8021Q_VLAN_ADD: |
1054 | err = dsa_switch_tag_8021q_vlan_add(ds, info); |
1055 | break; |
1056 | case DSA_NOTIFIER_TAG_8021Q_VLAN_DEL: |
1057 | err = dsa_switch_tag_8021q_vlan_del(ds, info); |
1058 | break; |
1059 | case DSA_NOTIFIER_CONDUIT_STATE_CHANGE: |
1060 | err = dsa_switch_conduit_state_change(ds, info); |
1061 | break; |
1062 | default: |
1063 | err = -EOPNOTSUPP; |
1064 | break; |
1065 | } |
1066 | |
1067 | if (err) |
1068 | dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n" , |
1069 | event, err); |
1070 | |
1071 | return notifier_from_errno(err); |
1072 | } |
1073 | |
1074 | /** |
1075 | * dsa_tree_notify - Execute code for all switches in a DSA switch tree. |
1076 | * @dst: collection of struct dsa_switch devices to notify. |
1077 | * @e: event, must be of type DSA_NOTIFIER_* |
1078 | * @v: event-specific value. |
1079 | * |
1080 | * Given a struct dsa_switch_tree, this can be used to run a function once for |
1081 | * each member DSA switch. The other alternative of traversing the tree is only |
1082 | * through its ports list, which does not uniquely list the switches. |
1083 | */ |
1084 | int dsa_tree_notify(struct dsa_switch_tree *dst, unsigned long e, void *v) |
1085 | { |
1086 | struct raw_notifier_head *nh = &dst->nh; |
1087 | int err; |
1088 | |
1089 | err = raw_notifier_call_chain(nh, val: e, v); |
1090 | |
1091 | return notifier_to_errno(ret: err); |
1092 | } |
1093 | |
1094 | /** |
1095 | * dsa_broadcast - Notify all DSA trees in the system. |
1096 | * @e: event, must be of type DSA_NOTIFIER_* |
1097 | * @v: event-specific value. |
1098 | * |
1099 | * Can be used to notify the switching fabric of events such as cross-chip |
1100 | * bridging between disjoint trees (such as islands of tagger-compatible |
1101 | * switches bridged by an incompatible middle switch). |
1102 | * |
1103 | * WARNING: this function is not reliable during probe time, because probing |
1104 | * between trees is asynchronous and not all DSA trees might have probed. |
1105 | */ |
1106 | int dsa_broadcast(unsigned long e, void *v) |
1107 | { |
1108 | struct dsa_switch_tree *dst; |
1109 | int err = 0; |
1110 | |
1111 | list_for_each_entry(dst, &dsa_tree_list, list) { |
1112 | err = dsa_tree_notify(dst, e, v); |
1113 | if (err) |
1114 | break; |
1115 | } |
1116 | |
1117 | return err; |
1118 | } |
1119 | |
1120 | int dsa_switch_register_notifier(struct dsa_switch *ds) |
1121 | { |
1122 | ds->nb.notifier_call = dsa_switch_event; |
1123 | |
1124 | return raw_notifier_chain_register(nh: &ds->dst->nh, nb: &ds->nb); |
1125 | } |
1126 | |
1127 | void dsa_switch_unregister_notifier(struct dsa_switch *ds) |
1128 | { |
1129 | int err; |
1130 | |
1131 | err = raw_notifier_chain_unregister(nh: &ds->dst->nh, nb: &ds->nb); |
1132 | if (err) |
1133 | dev_err(ds->dev, "failed to unregister notifier (%d)\n" , err); |
1134 | } |
1135 | |