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