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 | 14 | |
47d2ce03 | 15 | #include "dsa.h" |
5917bfe6 | 16 | #include "netlink.h" |
022bba63 | 17 | #include "port.h" |
0c603136 | 18 | #include "switch.h" |
19d05ea7 | 19 | #include "tag_8021q.h" |
9538ebce | 20 | #include "trace.h" |
6ca80638 | 21 | #include "user.h" |
f515f192 | 22 | |
1faabf74 VD |
23 | static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds, |
24 | unsigned int ageing_time) | |
25 | { | |
d0004a02 | 26 | struct dsa_port *dp; |
1faabf74 | 27 | |
d0004a02 | 28 | dsa_switch_for_each_port(dp, ds) |
1faabf74 VD |
29 | if (dp->ageing_time && dp->ageing_time < ageing_time) |
30 | ageing_time = dp->ageing_time; | |
1faabf74 VD |
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; | |
77b61365 VO |
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; | |
1faabf74 VD |
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 | ||
fac6abd5 VO |
55 | static bool dsa_port_mtu_match(struct dsa_port *dp, |
56 | struct dsa_notifier_mtu_info *info) | |
bfcb8132 | 57 | { |
be6ff966 | 58 | return dp == info->dp || dsa_port_is_dsa(dp) || dsa_port_is_cpu(dp); |
bfcb8132 VO |
59 | } |
60 | ||
61 | static int dsa_switch_mtu(struct dsa_switch *ds, | |
62 | struct dsa_notifier_mtu_info *info) | |
63 | { | |
fac6abd5 VO |
64 | struct dsa_port *dp; |
65 | int ret; | |
bfcb8132 VO |
66 | |
67 | if (!ds->ops->port_change_mtu) | |
68 | return -EOPNOTSUPP; | |
69 | ||
fac6abd5 VO |
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); | |
bfcb8132 VO |
74 | if (ret) |
75 | return ret; | |
76 | } | |
77 | } | |
78 | ||
79 | return 0; | |
80 | } | |
81 | ||
04d3a4c6 VD |
82 | static int dsa_switch_bridge_join(struct dsa_switch *ds, |
83 | struct dsa_notifier_bridge_info *info) | |
84 | { | |
e19cc13c | 85 | int err; |
f66a6a69 | 86 | |
726816a1 | 87 | if (info->dp->ds == ds) { |
67b5fb5d VO |
88 | if (!ds->ops->port_bridge_join) |
89 | return -EOPNOTSUPP; | |
90 | ||
726816a1 VO |
91 | err = ds->ops->port_bridge_join(ds, info->dp->index, |
92 | info->bridge, | |
06b9cce4 VO |
93 | &info->tx_fwd_offload, |
94 | info->extack); | |
e19cc13c VO |
95 | if (err) |
96 | return err; | |
97 | } | |
04d3a4c6 | 98 | |
726816a1 VO |
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, | |
06b9cce4 | 105 | info->extack); |
e19cc13c VO |
106 | if (err) |
107 | return err; | |
108 | } | |
04d3a4c6 | 109 | |
91495f21 | 110 | return 0; |
04d3a4c6 VD |
111 | } |
112 | ||
381a7301 TW |
113 | static int dsa_switch_bridge_leave(struct dsa_switch *ds, |
114 | struct dsa_notifier_bridge_info *info) | |
115 | { | |
726816a1 VO |
116 | if (info->dp->ds == ds && ds->ops->port_bridge_leave) |
117 | ds->ops->port_bridge_leave(ds, info->dp->index, info->bridge); | |
381a7301 | 118 | |
726816a1 VO |
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, | |
381a7301 TW |
123 | info->bridge); |
124 | ||
91495f21 | 125 | return 0; |
04d3a4c6 VD |
126 | } |
127 | ||
b8e997c4 VO |
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 | */ | |
fac6abd5 | 132 | static bool dsa_port_host_address_match(struct dsa_port *dp, |
726816a1 | 133 | const struct dsa_port *targeted_dp) |
b8e997c4 | 134 | { |
726816a1 | 135 | struct dsa_port *cpu_dp = targeted_dp->cpu_dp; |
b8e997c4 | 136 | |
726816a1 | 137 | if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) |
fac6abd5 VO |
138 | return dp->index == dsa_towards_port(dp->ds, cpu_dp->ds->index, |
139 | cpu_dp->index); | |
b8e997c4 VO |
140 | |
141 | return false; | |
142 | } | |
143 | ||
161ca59d | 144 | static struct dsa_mac_addr *dsa_mac_addr_find(struct list_head *addr_list, |
c2693363 VO |
145 | const unsigned char *addr, u16 vid, |
146 | struct dsa_db db) | |
161ca59d VO |
147 | { |
148 | struct dsa_mac_addr *a; | |
149 | ||
150 | list_for_each_entry(a, addr_list, list) | |
c2693363 VO |
151 | if (ether_addr_equal(a->addr, addr) && a->vid == vid && |
152 | dsa_db_equal(&a->db, &db)) | |
161ca59d VO |
153 | return a; |
154 | ||
155 | return NULL; | |
156 | } | |
157 | ||
fac6abd5 | 158 | static int dsa_port_do_mdb_add(struct dsa_port *dp, |
c2693363 VO |
159 | const struct switchdev_obj_port_mdb *mdb, |
160 | struct dsa_db db) | |
161ca59d | 161 | { |
fac6abd5 | 162 | struct dsa_switch *ds = dp->ds; |
161ca59d | 163 | struct dsa_mac_addr *a; |
fac6abd5 | 164 | int port = dp->index; |
338a3a47 | 165 | int err = 0; |
161ca59d VO |
166 | |
167 | /* No need to bother with refcounting for user ports */ | |
9538ebce VO |
168 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { |
169 | err = ds->ops->port_mdb_add(ds, port, mdb, db); | |
170 | trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err); | |
171 | ||
172 | return err; | |
173 | } | |
161ca59d | 174 | |
338a3a47 VO |
175 | mutex_lock(&dp->addr_lists_lock); |
176 | ||
c2693363 | 177 | a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); |
161ca59d VO |
178 | if (a) { |
179 | refcount_inc(&a->refcount); | |
9538ebce VO |
180 | trace_dsa_mdb_add_bump(dp, mdb->addr, mdb->vid, &db, |
181 | &a->refcount); | |
338a3a47 | 182 | goto out; |
161ca59d VO |
183 | } |
184 | ||
185 | a = kzalloc(sizeof(*a), GFP_KERNEL); | |
338a3a47 VO |
186 | if (!a) { |
187 | err = -ENOMEM; | |
188 | goto out; | |
189 | } | |
161ca59d | 190 | |
c2693363 | 191 | err = ds->ops->port_mdb_add(ds, port, mdb, db); |
9538ebce | 192 | trace_dsa_mdb_add_hw(dp, mdb->addr, mdb->vid, &db, err); |
161ca59d VO |
193 | if (err) { |
194 | kfree(a); | |
338a3a47 | 195 | goto out; |
161ca59d VO |
196 | } |
197 | ||
198 | ether_addr_copy(a->addr, mdb->addr); | |
199 | a->vid = mdb->vid; | |
c2693363 | 200 | a->db = db; |
161ca59d VO |
201 | refcount_set(&a->refcount, 1); |
202 | list_add_tail(&a->list, &dp->mdbs); | |
203 | ||
338a3a47 VO |
204 | out: |
205 | mutex_unlock(&dp->addr_lists_lock); | |
206 | ||
207 | return err; | |
161ca59d VO |
208 | } |
209 | ||
fac6abd5 | 210 | static int dsa_port_do_mdb_del(struct dsa_port *dp, |
c2693363 VO |
211 | const struct switchdev_obj_port_mdb *mdb, |
212 | struct dsa_db db) | |
161ca59d | 213 | { |
fac6abd5 | 214 | struct dsa_switch *ds = dp->ds; |
161ca59d | 215 | struct dsa_mac_addr *a; |
fac6abd5 | 216 | int port = dp->index; |
338a3a47 | 217 | int err = 0; |
161ca59d VO |
218 | |
219 | /* No need to bother with refcounting for user ports */ | |
9538ebce VO |
220 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { |
221 | err = ds->ops->port_mdb_del(ds, port, mdb, db); | |
222 | trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err); | |
223 | ||
224 | return err; | |
225 | } | |
161ca59d | 226 | |
338a3a47 VO |
227 | mutex_lock(&dp->addr_lists_lock); |
228 | ||
c2693363 | 229 | a = dsa_mac_addr_find(&dp->mdbs, mdb->addr, mdb->vid, db); |
338a3a47 | 230 | if (!a) { |
9538ebce | 231 | trace_dsa_mdb_del_not_found(dp, mdb->addr, mdb->vid, &db); |
338a3a47 VO |
232 | err = -ENOENT; |
233 | goto out; | |
234 | } | |
161ca59d | 235 | |
9538ebce VO |
236 | if (!refcount_dec_and_test(&a->refcount)) { |
237 | trace_dsa_mdb_del_drop(dp, mdb->addr, mdb->vid, &db, | |
238 | &a->refcount); | |
338a3a47 | 239 | goto out; |
9538ebce | 240 | } |
161ca59d | 241 | |
c2693363 | 242 | err = ds->ops->port_mdb_del(ds, port, mdb, db); |
9538ebce | 243 | trace_dsa_mdb_del_hw(dp, mdb->addr, mdb->vid, &db, err); |
161ca59d | 244 | if (err) { |
232deb3f | 245 | refcount_set(&a->refcount, 1); |
338a3a47 | 246 | goto out; |
161ca59d VO |
247 | } |
248 | ||
249 | list_del(&a->list); | |
250 | kfree(a); | |
251 | ||
338a3a47 VO |
252 | out: |
253 | mutex_unlock(&dp->addr_lists_lock); | |
254 | ||
255 | return err; | |
161ca59d VO |
256 | } |
257 | ||
fac6abd5 | 258 | static int dsa_port_do_fdb_add(struct dsa_port *dp, const unsigned char *addr, |
c2693363 | 259 | u16 vid, struct dsa_db db) |
3f6e32f9 | 260 | { |
fac6abd5 | 261 | struct dsa_switch *ds = dp->ds; |
3f6e32f9 | 262 | struct dsa_mac_addr *a; |
fac6abd5 | 263 | int port = dp->index; |
338a3a47 | 264 | int err = 0; |
3f6e32f9 VO |
265 | |
266 | /* No need to bother with refcounting for user ports */ | |
9538ebce VO |
267 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { |
268 | err = ds->ops->port_fdb_add(ds, port, addr, vid, db); | |
269 | trace_dsa_fdb_add_hw(dp, addr, vid, &db, err); | |
270 | ||
271 | return err; | |
272 | } | |
3f6e32f9 | 273 | |
338a3a47 VO |
274 | mutex_lock(&dp->addr_lists_lock); |
275 | ||
c2693363 | 276 | a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); |
3f6e32f9 VO |
277 | if (a) { |
278 | refcount_inc(&a->refcount); | |
9538ebce | 279 | trace_dsa_fdb_add_bump(dp, addr, vid, &db, &a->refcount); |
338a3a47 | 280 | goto out; |
3f6e32f9 VO |
281 | } |
282 | ||
283 | a = kzalloc(sizeof(*a), GFP_KERNEL); | |
338a3a47 VO |
284 | if (!a) { |
285 | err = -ENOMEM; | |
286 | goto out; | |
287 | } | |
3f6e32f9 | 288 | |
c2693363 | 289 | err = ds->ops->port_fdb_add(ds, port, addr, vid, db); |
9538ebce | 290 | trace_dsa_fdb_add_hw(dp, addr, vid, &db, err); |
3f6e32f9 VO |
291 | if (err) { |
292 | kfree(a); | |
338a3a47 | 293 | goto out; |
3f6e32f9 VO |
294 | } |
295 | ||
296 | ether_addr_copy(a->addr, addr); | |
297 | a->vid = vid; | |
c2693363 | 298 | a->db = db; |
3f6e32f9 VO |
299 | refcount_set(&a->refcount, 1); |
300 | list_add_tail(&a->list, &dp->fdbs); | |
301 | ||
338a3a47 VO |
302 | out: |
303 | mutex_unlock(&dp->addr_lists_lock); | |
304 | ||
305 | return err; | |
3f6e32f9 VO |
306 | } |
307 | ||
fac6abd5 | 308 | static int dsa_port_do_fdb_del(struct dsa_port *dp, const unsigned char *addr, |
c2693363 | 309 | u16 vid, struct dsa_db db) |
3f6e32f9 | 310 | { |
fac6abd5 | 311 | struct dsa_switch *ds = dp->ds; |
3f6e32f9 | 312 | struct dsa_mac_addr *a; |
fac6abd5 | 313 | int port = dp->index; |
338a3a47 | 314 | int err = 0; |
3f6e32f9 VO |
315 | |
316 | /* No need to bother with refcounting for user ports */ | |
9538ebce VO |
317 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(dp))) { |
318 | err = ds->ops->port_fdb_del(ds, port, addr, vid, db); | |
319 | trace_dsa_fdb_del_hw(dp, addr, vid, &db, err); | |
320 | ||
321 | return err; | |
322 | } | |
3f6e32f9 | 323 | |
338a3a47 VO |
324 | mutex_lock(&dp->addr_lists_lock); |
325 | ||
c2693363 | 326 | a = dsa_mac_addr_find(&dp->fdbs, addr, vid, db); |
338a3a47 | 327 | if (!a) { |
9538ebce | 328 | trace_dsa_fdb_del_not_found(dp, addr, vid, &db); |
338a3a47 VO |
329 | err = -ENOENT; |
330 | goto out; | |
331 | } | |
3f6e32f9 | 332 | |
9538ebce VO |
333 | if (!refcount_dec_and_test(&a->refcount)) { |
334 | trace_dsa_fdb_del_drop(dp, addr, vid, &db, &a->refcount); | |
338a3a47 | 335 | goto out; |
9538ebce | 336 | } |
3f6e32f9 | 337 | |
c2693363 | 338 | err = ds->ops->port_fdb_del(ds, port, addr, vid, db); |
9538ebce | 339 | trace_dsa_fdb_del_hw(dp, addr, vid, &db, err); |
3f6e32f9 | 340 | if (err) { |
232deb3f | 341 | refcount_set(&a->refcount, 1); |
338a3a47 | 342 | goto out; |
3f6e32f9 VO |
343 | } |
344 | ||
345 | list_del(&a->list); | |
346 | kfree(a); | |
347 | ||
338a3a47 VO |
348 | out: |
349 | mutex_unlock(&dp->addr_lists_lock); | |
350 | ||
351 | return err; | |
3f6e32f9 VO |
352 | } |
353 | ||
e212fa7c | 354 | static int dsa_switch_do_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag *lag, |
c2693363 VO |
355 | const unsigned char *addr, u16 vid, |
356 | struct dsa_db db) | |
e212fa7c VO |
357 | { |
358 | struct dsa_mac_addr *a; | |
359 | int err = 0; | |
360 | ||
361 | mutex_lock(&lag->fdb_lock); | |
362 | ||
c2693363 | 363 | a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); |
e212fa7c VO |
364 | if (a) { |
365 | refcount_inc(&a->refcount); | |
9538ebce VO |
366 | trace_dsa_lag_fdb_add_bump(lag->dev, addr, vid, &db, |
367 | &a->refcount); | |
e212fa7c VO |
368 | goto out; |
369 | } | |
370 | ||
371 | a = kzalloc(sizeof(*a), GFP_KERNEL); | |
372 | if (!a) { | |
373 | err = -ENOMEM; | |
374 | goto out; | |
375 | } | |
376 | ||
c2693363 | 377 | err = ds->ops->lag_fdb_add(ds, *lag, addr, vid, db); |
9538ebce | 378 | trace_dsa_lag_fdb_add_hw(lag->dev, addr, vid, &db, err); |
e212fa7c VO |
379 | if (err) { |
380 | kfree(a); | |
381 | goto out; | |
382 | } | |
383 | ||
384 | ether_addr_copy(a->addr, addr); | |
385 | a->vid = vid; | |
c7560d12 | 386 | a->db = db; |
e212fa7c VO |
387 | refcount_set(&a->refcount, 1); |
388 | list_add_tail(&a->list, &lag->fdbs); | |
389 | ||
390 | out: | |
391 | mutex_unlock(&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, | |
c2693363 VO |
397 | const unsigned char *addr, u16 vid, |
398 | struct dsa_db db) | |
e212fa7c VO |
399 | { |
400 | struct dsa_mac_addr *a; | |
401 | int err = 0; | |
402 | ||
403 | mutex_lock(&lag->fdb_lock); | |
404 | ||
c2693363 | 405 | a = dsa_mac_addr_find(&lag->fdbs, addr, vid, db); |
e212fa7c | 406 | if (!a) { |
9538ebce | 407 | trace_dsa_lag_fdb_del_not_found(lag->dev, addr, vid, &db); |
e212fa7c VO |
408 | err = -ENOENT; |
409 | goto out; | |
410 | } | |
411 | ||
9538ebce VO |
412 | if (!refcount_dec_and_test(&a->refcount)) { |
413 | trace_dsa_lag_fdb_del_drop(lag->dev, addr, vid, &db, | |
414 | &a->refcount); | |
e212fa7c | 415 | goto out; |
9538ebce | 416 | } |
e212fa7c | 417 | |
c2693363 | 418 | err = ds->ops->lag_fdb_del(ds, *lag, addr, vid, db); |
9538ebce | 419 | trace_dsa_lag_fdb_del_hw(lag->dev, addr, vid, &db, err); |
e212fa7c VO |
420 | if (err) { |
421 | refcount_set(&a->refcount, 1); | |
422 | goto out; | |
423 | } | |
424 | ||
425 | list_del(&a->list); | |
426 | kfree(a); | |
427 | ||
428 | out: | |
429 | mutex_unlock(&lag->fdb_lock); | |
430 | ||
431 | return err; | |
432 | } | |
433 | ||
3dc80afc VO |
434 | static int dsa_switch_host_fdb_add(struct dsa_switch *ds, |
435 | struct dsa_notifier_fdb_info *info) | |
436 | { | |
fac6abd5 | 437 | struct dsa_port *dp; |
3dc80afc | 438 | int err = 0; |
3dc80afc VO |
439 | |
440 | if (!ds->ops->port_fdb_add) | |
441 | return -EOPNOTSUPP; | |
442 | ||
fac6abd5 | 443 | dsa_switch_for_each_port(dp, ds) { |
726816a1 | 444 | if (dsa_port_host_address_match(dp, info->dp)) { |
acc43b7b VO |
445 | if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { |
446 | err = dsa_switch_do_lag_fdb_add(ds, dp->lag, | |
447 | info->addr, | |
448 | info->vid, | |
449 | info->db); | |
450 | } else { | |
451 | err = dsa_port_do_fdb_add(dp, info->addr, | |
452 | info->vid, info->db); | |
453 | } | |
3dc80afc VO |
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 | { | |
fac6abd5 | 465 | struct dsa_port *dp; |
3f6e32f9 | 466 | int err = 0; |
3f6e32f9 | 467 | |
3dc80afc VO |
468 | if (!ds->ops->port_fdb_del) |
469 | return -EOPNOTSUPP; | |
470 | ||
fac6abd5 | 471 | dsa_switch_for_each_port(dp, ds) { |
726816a1 | 472 | if (dsa_port_host_address_match(dp, info->dp)) { |
acc43b7b VO |
473 | if (dsa_port_is_cpu(dp) && info->dp->cpu_port_in_lag) { |
474 | err = dsa_switch_do_lag_fdb_del(ds, dp->lag, | |
475 | info->addr, | |
476 | info->vid, | |
477 | info->db); | |
478 | } else { | |
479 | err = dsa_port_do_fdb_del(dp, info->addr, | |
480 | info->vid, info->db); | |
481 | } | |
3f6e32f9 VO |
482 | if (err) |
483 | break; | |
484 | } | |
485 | } | |
3dc80afc | 486 | |
3f6e32f9 | 487 | return err; |
3dc80afc VO |
488 | } |
489 | ||
685fb6a4 VD |
490 | static int dsa_switch_fdb_add(struct dsa_switch *ds, |
491 | struct dsa_notifier_fdb_info *info) | |
492 | { | |
726816a1 | 493 | int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); |
fac6abd5 | 494 | struct dsa_port *dp = dsa_to_port(ds, port); |
685fb6a4 | 495 | |
1b6dd556 AS |
496 | if (!ds->ops->port_fdb_add) |
497 | return -EOPNOTSUPP; | |
685fb6a4 | 498 | |
c2693363 | 499 | return dsa_port_do_fdb_add(dp, info->addr, info->vid, info->db); |
685fb6a4 VD |
500 | } |
501 | ||
502 | static int dsa_switch_fdb_del(struct dsa_switch *ds, | |
503 | struct dsa_notifier_fdb_info *info) | |
504 | { | |
726816a1 | 505 | int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); |
fac6abd5 | 506 | struct dsa_port *dp = dsa_to_port(ds, port); |
685fb6a4 VD |
507 | |
508 | if (!ds->ops->port_fdb_del) | |
509 | return -EOPNOTSUPP; | |
510 | ||
c2693363 | 511 | return dsa_port_do_fdb_del(dp, info->addr, info->vid, info->db); |
685fb6a4 VD |
512 | } |
513 | ||
e212fa7c VO |
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, info->lag)) | |
525 | return dsa_switch_do_lag_fdb_add(ds, info->lag, | |
c2693363 VO |
526 | info->addr, info->vid, |
527 | info->db); | |
e212fa7c VO |
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, info->lag)) | |
543 | return dsa_switch_do_lag_fdb_del(ds, info->lag, | |
c2693363 VO |
544 | info->addr, info->vid, |
545 | info->db); | |
e212fa7c VO |
546 | |
547 | return 0; | |
548 | } | |
549 | ||
058102a6 TW |
550 | static int dsa_switch_lag_change(struct dsa_switch *ds, |
551 | struct dsa_notifier_lag_info *info) | |
552 | { | |
726816a1 VO |
553 | if (info->dp->ds == ds && ds->ops->port_lag_change) |
554 | return ds->ops->port_lag_change(ds, info->dp->index); | |
058102a6 | 555 | |
726816a1 VO |
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); | |
058102a6 TW |
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 | { | |
726816a1 VO |
566 | if (info->dp->ds == ds && ds->ops->port_lag_join) |
567 | return ds->ops->port_lag_join(ds, info->dp->index, info->lag, | |
2e359b00 | 568 | info->info, info->extack); |
058102a6 | 569 | |
726816a1 VO |
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, | |
2e359b00 | 573 | info->info, info->extack); |
058102a6 | 574 | |
b71d0987 | 575 | return -EOPNOTSUPP; |
058102a6 TW |
576 | } |
577 | ||
578 | static int dsa_switch_lag_leave(struct dsa_switch *ds, | |
579 | struct dsa_notifier_lag_info *info) | |
580 | { | |
726816a1 VO |
581 | if (info->dp->ds == ds && ds->ops->port_lag_leave) |
582 | return ds->ops->port_lag_leave(ds, info->dp->index, info->lag); | |
058102a6 | 583 | |
726816a1 VO |
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); | |
058102a6 | 587 | |
b71d0987 | 588 | return -EOPNOTSUPP; |
058102a6 TW |
589 | } |
590 | ||
ffb68fc5 VO |
591 | static int dsa_switch_mdb_add(struct dsa_switch *ds, |
592 | struct dsa_notifier_mdb_info *info) | |
e6db98db | 593 | { |
726816a1 | 594 | int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); |
fac6abd5 | 595 | struct dsa_port *dp = dsa_to_port(ds, port); |
e6db98db | 596 | |
a52b2da7 | 597 | if (!ds->ops->port_mdb_add) |
e6db98db VD |
598 | return -EOPNOTSUPP; |
599 | ||
c2693363 | 600 | return dsa_port_do_mdb_add(dp, info->mdb, info->db); |
8ae5bcdc VD |
601 | } |
602 | ||
603 | static int dsa_switch_mdb_del(struct dsa_switch *ds, | |
604 | struct dsa_notifier_mdb_info *info) | |
605 | { | |
726816a1 | 606 | int port = dsa_towards_port(ds, info->dp->ds->index, info->dp->index); |
fac6abd5 | 607 | struct dsa_port *dp = dsa_to_port(ds, port); |
161ca59d | 608 | |
8ae5bcdc VD |
609 | if (!ds->ops->port_mdb_del) |
610 | return -EOPNOTSUPP; | |
611 | ||
c2693363 | 612 | return dsa_port_do_mdb_del(dp, info->mdb, info->db); |
8ae5bcdc VD |
613 | } |
614 | ||
b8e997c4 VO |
615 | static int dsa_switch_host_mdb_add(struct dsa_switch *ds, |
616 | struct dsa_notifier_mdb_info *info) | |
617 | { | |
fac6abd5 | 618 | struct dsa_port *dp; |
b8e997c4 | 619 | int err = 0; |
b8e997c4 VO |
620 | |
621 | if (!ds->ops->port_mdb_add) | |
622 | return -EOPNOTSUPP; | |
623 | ||
fac6abd5 | 624 | dsa_switch_for_each_port(dp, ds) { |
726816a1 | 625 | if (dsa_port_host_address_match(dp, info->dp)) { |
c2693363 | 626 | err = dsa_port_do_mdb_add(dp, info->mdb, info->db); |
b8e997c4 VO |
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 | { | |
fac6abd5 | 638 | struct dsa_port *dp; |
161ca59d | 639 | int err = 0; |
161ca59d | 640 | |
b8e997c4 VO |
641 | if (!ds->ops->port_mdb_del) |
642 | return -EOPNOTSUPP; | |
643 | ||
fac6abd5 | 644 | dsa_switch_for_each_port(dp, ds) { |
726816a1 | 645 | if (dsa_port_host_address_match(dp, info->dp)) { |
c2693363 | 646 | err = dsa_port_do_mdb_del(dp, info->mdb, info->db); |
161ca59d VO |
647 | if (err) |
648 | break; | |
649 | } | |
650 | } | |
b8e997c4 | 651 | |
161ca59d | 652 | return err; |
b8e997c4 VO |
653 | } |
654 | ||
134ef238 | 655 | /* Port VLANs match on the targeted port and on all DSA ports */ |
fac6abd5 VO |
656 | static bool dsa_port_vlan_match(struct dsa_port *dp, |
657 | struct dsa_notifier_vlan_info *info) | |
e65d45cc | 658 | { |
726816a1 | 659 | return dsa_port_is_dsa(dp) || dp == info->dp; |
e65d45cc VD |
660 | } |
661 | ||
134ef238 VO |
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, | |
726816a1 | 666 | const struct dsa_port *targeted_dp) |
134ef238 | 667 | { |
726816a1 | 668 | struct dsa_port *cpu_dp = targeted_dp->cpu_dp; |
134ef238 | 669 | |
726816a1 | 670 | if (dsa_switch_is_upstream_of(dp->ds, targeted_dp->ds)) |
134ef238 VO |
671 | return dsa_port_is_dsa(dp) || dp == cpu_dp; |
672 | ||
673 | return false; | |
674 | } | |
675 | ||
d06f925f VO |
676 | struct dsa_vlan *dsa_vlan_find(struct list_head *vlan_list, |
677 | const struct switchdev_obj_port_vlan *vlan) | |
134ef238 VO |
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. */ | |
02020bd7 VO |
698 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(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 | } | |
134ef238 VO |
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(&dp->vlans, vlan); | |
716 | if (v) { | |
717 | refcount_inc(&v->refcount); | |
02020bd7 | 718 | trace_dsa_vlan_add_bump(dp, vlan, &v->refcount); |
134ef238 VO |
719 | goto out; |
720 | } | |
721 | ||
722 | v = kzalloc(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); | |
02020bd7 | 729 | trace_dsa_vlan_add_hw(dp, vlan, err); |
134ef238 VO |
730 | if (err) { |
731 | kfree(v); | |
732 | goto out; | |
733 | } | |
734 | ||
735 | v->vid = vlan->vid; | |
736 | refcount_set(&v->refcount, 1); | |
737 | list_add_tail(&v->list, &dp->vlans); | |
738 | ||
739 | out: | |
740 | mutex_unlock(&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 */ | |
02020bd7 VO |
754 | if (!(dsa_port_is_cpu(dp) || dsa_port_is_dsa(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 | } | |
134ef238 VO |
760 | |
761 | mutex_lock(&dp->vlans_lock); | |
762 | ||
763 | v = dsa_vlan_find(&dp->vlans, vlan); | |
764 | if (!v) { | |
02020bd7 | 765 | trace_dsa_vlan_del_not_found(dp, vlan); |
134ef238 VO |
766 | err = -ENOENT; |
767 | goto out; | |
768 | } | |
769 | ||
02020bd7 VO |
770 | if (!refcount_dec_and_test(&v->refcount)) { |
771 | trace_dsa_vlan_del_drop(dp, vlan, &v->refcount); | |
134ef238 | 772 | goto out; |
02020bd7 | 773 | } |
134ef238 VO |
774 | |
775 | err = ds->ops->port_vlan_del(ds, port, vlan); | |
02020bd7 | 776 | trace_dsa_vlan_del_hw(dp, vlan, err); |
134ef238 VO |
777 | if (err) { |
778 | refcount_set(&v->refcount, 1); | |
779 | goto out; | |
780 | } | |
781 | ||
782 | list_del(&v->list); | |
783 | kfree(v); | |
784 | ||
785 | out: | |
786 | mutex_unlock(&dp->vlans_lock); | |
787 | ||
788 | return err; | |
789 | } | |
790 | ||
ffb68fc5 VO |
791 | static int dsa_switch_vlan_add(struct dsa_switch *ds, |
792 | struct dsa_notifier_vlan_info *info) | |
9c428c59 | 793 | { |
fac6abd5 VO |
794 | struct dsa_port *dp; |
795 | int err; | |
9c428c59 | 796 | |
1958d581 | 797 | if (!ds->ops->port_vlan_add) |
9c428c59 VD |
798 | return -EOPNOTSUPP; |
799 | ||
fac6abd5 VO |
800 | dsa_switch_for_each_port(dp, ds) { |
801 | if (dsa_port_vlan_match(dp, info)) { | |
134ef238 VO |
802 | err = dsa_port_do_vlan_add(dp, info->vlan, |
803 | info->extack); | |
e65d45cc VD |
804 | if (err) |
805 | return err; | |
806 | } | |
9c428c59 VD |
807 | } |
808 | ||
d0c627b8 VD |
809 | return 0; |
810 | } | |
811 | ||
812 | static int dsa_switch_vlan_del(struct dsa_switch *ds, | |
813 | struct dsa_notifier_vlan_info *info) | |
814 | { | |
134ef238 VO |
815 | struct dsa_port *dp; |
816 | int err; | |
817 | ||
d0c627b8 VD |
818 | if (!ds->ops->port_vlan_del) |
819 | return -EOPNOTSUPP; | |
820 | ||
134ef238 VO |
821 | dsa_switch_for_each_port(dp, ds) { |
822 | if (dsa_port_vlan_match(dp, info)) { | |
823 | err = dsa_port_do_vlan_del(dp, 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) { | |
726816a1 | 842 | if (dsa_port_host_vlan_match(dp, info->dp)) { |
134ef238 VO |
843 | err = dsa_port_do_vlan_add(dp, info->vlan, |
844 | 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) { | |
726816a1 | 863 | if (dsa_port_host_vlan_match(dp, info->dp)) { |
134ef238 VO |
864 | err = dsa_port_do_vlan_del(dp, info->vlan); |
865 | if (err) | |
866 | return err; | |
867 | } | |
868 | } | |
1ca4aa9c VD |
869 | |
870 | return 0; | |
d0c627b8 VD |
871 | } |
872 | ||
53da0eba VO |
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; | |
d0004a02 VO |
877 | struct dsa_port *dp, *cpu_dp; |
878 | int err; | |
53da0eba VO |
879 | |
880 | if (!ds->ops->change_tag_protocol) | |
881 | return -EOPNOTSUPP; | |
882 | ||
883 | ASSERT_RTNL(); | |
884 | ||
bacf93b0 VO |
885 | err = ds->ops->change_tag_protocol(ds, tag_ops->proto); |
886 | if (err) | |
887 | return err; | |
21e0b508 | 888 | |
bacf93b0 | 889 | dsa_switch_for_each_cpu_port(cpu_dp, ds) |
d0004a02 | 890 | dsa_port_set_tag_protocol(cpu_dp, tag_ops); |
53da0eba VO |
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 | */ | |
d0004a02 | 896 | dsa_switch_for_each_user_port(dp, ds) { |
6ca80638 | 897 | struct net_device *user = dp->user; |
53da0eba | 898 | |
6ca80638 | 899 | dsa_user_setup_tagger(user); |
53da0eba | 900 | |
d0004a02 | 901 | /* rtnl_mutex is held in dsa_tree_change_tag_proto */ |
6ca80638 | 902 | dsa_user_change_mtu(user, user->mtu); |
53da0eba VO |
903 | } |
904 | ||
905 | return 0; | |
906 | } | |
907 | ||
7f297314 VO |
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) | |
dc452a47 VO |
920 | { |
921 | const struct dsa_device_ops *tag_ops = info->tag_ops; | |
7f297314 VO |
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 | } | |
dc452a47 VO |
930 | |
931 | if (!ds->ops->connect_tag_protocol) | |
932 | return -EOPNOTSUPP; | |
933 | ||
7f297314 VO |
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; | |
dc452a47 VO |
960 | } |
961 | ||
295ab96f | 962 | static int |
6ca80638 FF |
963 | dsa_switch_conduit_state_change(struct dsa_switch *ds, |
964 | struct dsa_notifier_conduit_state_info *info) | |
295ab96f | 965 | { |
6ca80638 | 966 | if (!ds->ops->conduit_state_change) |
295ab96f VO |
967 | return 0; |
968 | ||
6ca80638 | 969 | ds->ops->conduit_state_change(ds, info->conduit, info->operational); |
295ab96f VO |
970 | |
971 | return 0; | |
972 | } | |
973 | ||
f515f192 VD |
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) { | |
1faabf74 VD |
981 | case DSA_NOTIFIER_AGEING_TIME: |
982 | err = dsa_switch_ageing_time(ds, info); | |
983 | break; | |
04d3a4c6 VD |
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; | |
685fb6a4 VD |
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; | |
3dc80afc VO |
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; | |
e212fa7c VO |
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; | |
058102a6 TW |
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; | |
8ae5bcdc VD |
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; | |
b8e997c4 VO |
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; | |
d0c627b8 VD |
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; | |
134ef238 VO |
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; | |
bfcb8132 VO |
1041 | case DSA_NOTIFIER_MTU: |
1042 | err = dsa_switch_mtu(ds, info); | |
1043 | break; | |
53da0eba VO |
1044 | case DSA_NOTIFIER_TAG_PROTO: |
1045 | err = dsa_switch_change_tag_proto(ds, info); | |
1046 | break; | |
dc452a47 VO |
1047 | case DSA_NOTIFIER_TAG_PROTO_CONNECT: |
1048 | err = dsa_switch_connect_tag_proto(ds, info); | |
1049 | break; | |
7f297314 VO |
1050 | case DSA_NOTIFIER_TAG_PROTO_DISCONNECT: |
1051 | err = dsa_switch_disconnect_tag_proto(ds, info); | |
1052 | break; | |
c64b9c05 VO |
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; | |
6ca80638 FF |
1059 | case DSA_NOTIFIER_CONDUIT_STATE_CHANGE: |
1060 | err = dsa_switch_conduit_state_change(ds, info); | |
295ab96f | 1061 | break; |
f515f192 VD |
1062 | default: |
1063 | err = -EOPNOTSUPP; | |
1064 | break; | |
1065 | } | |
1066 | ||
f515f192 VD |
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 | ||
6dbdfce7 VO |
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, e, v); | |
1090 | ||
1091 | return notifier_to_errno(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 | ||
f515f192 VD |
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(&ds->dst->nh, &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(&ds->dst->nh, &ds->nb); | |
1132 | if (err) | |
1133 | dev_err(ds->dev, "failed to unregister notifier (%d)\n", err); | |
1134 | } |