1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
2 | /* Copyright (c) 2019-2020 Marvell International Ltd. All rights reserved */ |
3 | |
4 | #include <linux/if_bridge.h> |
5 | #include <linux/if_vlan.h> |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/notifier.h> |
9 | #include <net/netevent.h> |
10 | #include <net/switchdev.h> |
11 | |
12 | #include "prestera.h" |
13 | #include "prestera_hw.h" |
14 | #include "prestera_switchdev.h" |
15 | |
16 | #define PRESTERA_VID_ALL (0xffff) |
17 | |
18 | #define PRESTERA_DEFAULT_AGEING_TIME_MS 300000 |
19 | #define PRESTERA_MAX_AGEING_TIME_MS 1000000000 |
20 | #define PRESTERA_MIN_AGEING_TIME_MS 32000 |
21 | |
22 | struct prestera_fdb_event_work { |
23 | struct work_struct work; |
24 | struct switchdev_notifier_fdb_info fdb_info; |
25 | struct net_device *dev; |
26 | unsigned long event; |
27 | }; |
28 | |
29 | struct prestera_switchdev { |
30 | struct prestera_switch *sw; |
31 | struct list_head bridge_list; |
32 | bool bridge_8021q_exists; |
33 | struct notifier_block swdev_nb_blk; |
34 | struct notifier_block swdev_nb; |
35 | }; |
36 | |
37 | struct prestera_bridge { |
38 | struct list_head head; |
39 | struct net_device *dev; |
40 | struct prestera_switchdev *swdev; |
41 | struct list_head port_list; |
42 | struct list_head br_mdb_entry_list; |
43 | bool mrouter_exist; |
44 | bool vlan_enabled; |
45 | bool multicast_enabled; |
46 | u16 bridge_id; |
47 | }; |
48 | |
49 | struct prestera_bridge_port { |
50 | struct list_head head; |
51 | struct net_device *dev; |
52 | struct prestera_bridge *bridge; |
53 | struct list_head vlan_list; |
54 | struct list_head br_mdb_port_list; |
55 | refcount_t ref_count; |
56 | unsigned long flags; |
57 | bool mrouter; |
58 | u8 stp_state; |
59 | }; |
60 | |
61 | struct prestera_bridge_vlan { |
62 | struct list_head head; |
63 | struct list_head port_vlan_list; |
64 | u16 vid; |
65 | }; |
66 | |
67 | struct prestera_port_vlan { |
68 | struct list_head br_vlan_head; |
69 | struct list_head port_head; |
70 | struct prestera_port *port; |
71 | struct prestera_bridge_port *br_port; |
72 | u16 vid; |
73 | }; |
74 | |
75 | struct prestera_br_mdb_port { |
76 | struct prestera_bridge_port *br_port; |
77 | struct list_head br_mdb_port_node; |
78 | }; |
79 | |
80 | /* Software representation of MDB table. */ |
81 | struct prestera_br_mdb_entry { |
82 | struct prestera_bridge *bridge; |
83 | struct prestera_mdb_entry *mdb; |
84 | struct list_head br_mdb_port_list; |
85 | struct list_head br_mdb_entry_node; |
86 | bool enabled; |
87 | }; |
88 | |
89 | static struct workqueue_struct *swdev_wq; |
90 | |
91 | static void prestera_bridge_port_put(struct prestera_bridge_port *br_port); |
92 | |
93 | static int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, |
94 | u8 state); |
95 | |
96 | static struct prestera_bridge * |
97 | prestera_bridge_find(const struct prestera_switch *sw, |
98 | const struct net_device *br_dev) |
99 | { |
100 | struct prestera_bridge *bridge; |
101 | |
102 | list_for_each_entry(bridge, &sw->swdev->bridge_list, head) |
103 | if (bridge->dev == br_dev) |
104 | return bridge; |
105 | |
106 | return NULL; |
107 | } |
108 | |
109 | static struct prestera_bridge_port * |
110 | __prestera_bridge_port_find(const struct prestera_bridge *bridge, |
111 | const struct net_device *brport_dev) |
112 | { |
113 | struct prestera_bridge_port *br_port; |
114 | |
115 | list_for_each_entry(br_port, &bridge->port_list, head) |
116 | if (br_port->dev == brport_dev) |
117 | return br_port; |
118 | |
119 | return NULL; |
120 | } |
121 | |
122 | static struct prestera_bridge_port * |
123 | prestera_bridge_port_find(struct prestera_switch *sw, |
124 | struct net_device *brport_dev) |
125 | { |
126 | struct net_device *br_dev = netdev_master_upper_dev_get(dev: brport_dev); |
127 | struct prestera_bridge *bridge; |
128 | |
129 | if (!br_dev) |
130 | return NULL; |
131 | |
132 | bridge = prestera_bridge_find(sw, br_dev); |
133 | if (!bridge) |
134 | return NULL; |
135 | |
136 | return __prestera_bridge_port_find(bridge, brport_dev); |
137 | } |
138 | |
139 | static void |
140 | prestera_br_port_flags_reset(struct prestera_bridge_port *br_port, |
141 | struct prestera_port *port) |
142 | { |
143 | prestera_port_uc_flood_set(port, flood: false); |
144 | prestera_port_mc_flood_set(port, flood: false); |
145 | prestera_port_learning_set(port, learn_enable: false); |
146 | prestera_port_br_locked_set(port, br_locked: false); |
147 | } |
148 | |
149 | static int prestera_br_port_flags_set(struct prestera_bridge_port *br_port, |
150 | struct prestera_port *port) |
151 | { |
152 | int err; |
153 | |
154 | err = prestera_port_uc_flood_set(port, flood: br_port->flags & BR_FLOOD); |
155 | if (err) |
156 | goto err_out; |
157 | |
158 | err = prestera_port_mc_flood_set(port, flood: br_port->flags & BR_MCAST_FLOOD); |
159 | if (err) |
160 | goto err_out; |
161 | |
162 | err = prestera_port_learning_set(port, learn_enable: br_port->flags & BR_LEARNING); |
163 | if (err) |
164 | goto err_out; |
165 | |
166 | err = prestera_port_br_locked_set(port, |
167 | br_locked: br_port->flags & BR_PORT_LOCKED); |
168 | if (err) |
169 | goto err_out; |
170 | |
171 | return 0; |
172 | |
173 | err_out: |
174 | prestera_br_port_flags_reset(br_port, port); |
175 | return err; |
176 | } |
177 | |
178 | static struct prestera_bridge_vlan * |
179 | prestera_bridge_vlan_create(struct prestera_bridge_port *br_port, u16 vid) |
180 | { |
181 | struct prestera_bridge_vlan *br_vlan; |
182 | |
183 | br_vlan = kzalloc(size: sizeof(*br_vlan), GFP_KERNEL); |
184 | if (!br_vlan) |
185 | return NULL; |
186 | |
187 | INIT_LIST_HEAD(list: &br_vlan->port_vlan_list); |
188 | br_vlan->vid = vid; |
189 | list_add(new: &br_vlan->head, head: &br_port->vlan_list); |
190 | |
191 | return br_vlan; |
192 | } |
193 | |
194 | static void prestera_bridge_vlan_destroy(struct prestera_bridge_vlan *br_vlan) |
195 | { |
196 | list_del(entry: &br_vlan->head); |
197 | WARN_ON(!list_empty(&br_vlan->port_vlan_list)); |
198 | kfree(objp: br_vlan); |
199 | } |
200 | |
201 | static struct prestera_bridge_vlan * |
202 | prestera_bridge_vlan_by_vid(struct prestera_bridge_port *br_port, u16 vid) |
203 | { |
204 | struct prestera_bridge_vlan *br_vlan; |
205 | |
206 | list_for_each_entry(br_vlan, &br_port->vlan_list, head) { |
207 | if (br_vlan->vid == vid) |
208 | return br_vlan; |
209 | } |
210 | |
211 | return NULL; |
212 | } |
213 | |
214 | static int prestera_bridge_vlan_port_count(struct prestera_bridge *bridge, |
215 | u16 vid) |
216 | { |
217 | struct prestera_bridge_port *br_port; |
218 | struct prestera_bridge_vlan *br_vlan; |
219 | int count = 0; |
220 | |
221 | list_for_each_entry(br_port, &bridge->port_list, head) { |
222 | list_for_each_entry(br_vlan, &br_port->vlan_list, head) { |
223 | if (br_vlan->vid == vid) { |
224 | count += 1; |
225 | break; |
226 | } |
227 | } |
228 | } |
229 | |
230 | return count; |
231 | } |
232 | |
233 | static void prestera_bridge_vlan_put(struct prestera_bridge_vlan *br_vlan) |
234 | { |
235 | if (list_empty(head: &br_vlan->port_vlan_list)) |
236 | prestera_bridge_vlan_destroy(br_vlan); |
237 | } |
238 | |
239 | static struct prestera_port_vlan * |
240 | prestera_port_vlan_by_vid(struct prestera_port *port, u16 vid) |
241 | { |
242 | struct prestera_port_vlan *port_vlan; |
243 | |
244 | list_for_each_entry(port_vlan, &port->vlans_list, port_head) { |
245 | if (port_vlan->vid == vid) |
246 | return port_vlan; |
247 | } |
248 | |
249 | return NULL; |
250 | } |
251 | |
252 | static struct prestera_port_vlan * |
253 | prestera_port_vlan_create(struct prestera_port *port, u16 vid, bool untagged) |
254 | { |
255 | struct prestera_port_vlan *port_vlan; |
256 | int err; |
257 | |
258 | port_vlan = prestera_port_vlan_by_vid(port, vid); |
259 | if (port_vlan) |
260 | return ERR_PTR(error: -EEXIST); |
261 | |
262 | err = prestera_hw_vlan_port_set(port, vid, is_member: true, untagged); |
263 | if (err) |
264 | return ERR_PTR(error: err); |
265 | |
266 | port_vlan = kzalloc(size: sizeof(*port_vlan), GFP_KERNEL); |
267 | if (!port_vlan) { |
268 | err = -ENOMEM; |
269 | goto err_port_vlan_alloc; |
270 | } |
271 | |
272 | port_vlan->port = port; |
273 | port_vlan->vid = vid; |
274 | |
275 | list_add(new: &port_vlan->port_head, head: &port->vlans_list); |
276 | |
277 | return port_vlan; |
278 | |
279 | err_port_vlan_alloc: |
280 | prestera_hw_vlan_port_set(port, vid, is_member: false, untagged: false); |
281 | return ERR_PTR(error: err); |
282 | } |
283 | |
284 | static int prestera_fdb_add(struct prestera_port *port, |
285 | const unsigned char *mac, u16 vid, bool dynamic) |
286 | { |
287 | if (prestera_port_is_lag_member(port)) |
288 | return prestera_hw_lag_fdb_add(sw: port->sw, lag_id: prestera_port_lag_id(port), |
289 | mac, vid, dynamic); |
290 | |
291 | return prestera_hw_fdb_add(port, mac, vid, dynamic); |
292 | } |
293 | |
294 | static int prestera_fdb_del(struct prestera_port *port, |
295 | const unsigned char *mac, u16 vid) |
296 | { |
297 | if (prestera_port_is_lag_member(port)) |
298 | return prestera_hw_lag_fdb_del(sw: port->sw, lag_id: prestera_port_lag_id(port), |
299 | mac, vid); |
300 | else |
301 | return prestera_hw_fdb_del(port, mac, vid); |
302 | } |
303 | |
304 | static int prestera_fdb_flush_port_vlan(struct prestera_port *port, u16 vid, |
305 | u32 mode) |
306 | { |
307 | if (prestera_port_is_lag_member(port)) |
308 | return prestera_hw_fdb_flush_lag_vlan(sw: port->sw, lag_id: prestera_port_lag_id(port), |
309 | vid, mode); |
310 | else |
311 | return prestera_hw_fdb_flush_port_vlan(port, vid, mode); |
312 | } |
313 | |
314 | static int prestera_fdb_flush_port(struct prestera_port *port, u32 mode) |
315 | { |
316 | if (prestera_port_is_lag_member(port)) |
317 | return prestera_hw_fdb_flush_lag(sw: port->sw, lag_id: prestera_port_lag_id(port), |
318 | mode); |
319 | else |
320 | return prestera_hw_fdb_flush_port(port, mode); |
321 | } |
322 | |
323 | static void |
324 | prestera_mdb_port_del(struct prestera_mdb_entry *mdb, |
325 | struct net_device *orig_dev) |
326 | { |
327 | struct prestera_flood_domain *fl_domain = mdb->flood_domain; |
328 | struct prestera_flood_domain_port *flood_domain_port; |
329 | |
330 | flood_domain_port = prestera_flood_domain_port_find(flood_domain: fl_domain, |
331 | dev: orig_dev, |
332 | vid: mdb->vid); |
333 | if (flood_domain_port) |
334 | prestera_flood_domain_port_destroy(port: flood_domain_port); |
335 | } |
336 | |
337 | static void |
338 | prestera_br_mdb_entry_put(struct prestera_br_mdb_entry *br_mdb) |
339 | { |
340 | struct prestera_bridge_port *br_port; |
341 | |
342 | if (list_empty(head: &br_mdb->br_mdb_port_list)) { |
343 | list_for_each_entry(br_port, &br_mdb->bridge->port_list, head) |
344 | prestera_mdb_port_del(mdb: br_mdb->mdb, orig_dev: br_port->dev); |
345 | |
346 | prestera_mdb_entry_destroy(mdb_entry: br_mdb->mdb); |
347 | list_del(entry: &br_mdb->br_mdb_entry_node); |
348 | kfree(objp: br_mdb); |
349 | } |
350 | } |
351 | |
352 | static void |
353 | prestera_br_mdb_port_del(struct prestera_br_mdb_entry *br_mdb, |
354 | struct prestera_bridge_port *br_port) |
355 | { |
356 | struct prestera_br_mdb_port *br_mdb_port, *tmp; |
357 | |
358 | list_for_each_entry_safe(br_mdb_port, tmp, &br_mdb->br_mdb_port_list, |
359 | br_mdb_port_node) { |
360 | if (br_mdb_port->br_port == br_port) { |
361 | list_del(entry: &br_mdb_port->br_mdb_port_node); |
362 | kfree(objp: br_mdb_port); |
363 | } |
364 | } |
365 | } |
366 | |
367 | static void |
368 | prestera_mdb_flush_bridge_port(struct prestera_bridge_port *br_port) |
369 | { |
370 | struct prestera_br_mdb_port *br_mdb_port, *tmp_port; |
371 | struct prestera_br_mdb_entry *br_mdb, *br_mdb_tmp; |
372 | struct prestera_bridge *br_dev = br_port->bridge; |
373 | |
374 | list_for_each_entry_safe(br_mdb, br_mdb_tmp, &br_dev->br_mdb_entry_list, |
375 | br_mdb_entry_node) { |
376 | list_for_each_entry_safe(br_mdb_port, tmp_port, |
377 | &br_mdb->br_mdb_port_list, |
378 | br_mdb_port_node) { |
379 | prestera_mdb_port_del(mdb: br_mdb->mdb, |
380 | orig_dev: br_mdb_port->br_port->dev); |
381 | prestera_br_mdb_port_del(br_mdb, br_port: br_mdb_port->br_port); |
382 | } |
383 | prestera_br_mdb_entry_put(br_mdb); |
384 | } |
385 | } |
386 | |
387 | static void |
388 | prestera_port_vlan_bridge_leave(struct prestera_port_vlan *port_vlan) |
389 | { |
390 | u32 fdb_flush_mode = PRESTERA_FDB_FLUSH_MODE_DYNAMIC; |
391 | struct prestera_port *port = port_vlan->port; |
392 | struct prestera_bridge_vlan *br_vlan; |
393 | struct prestera_bridge_port *br_port; |
394 | bool last_port, last_vlan; |
395 | u16 vid = port_vlan->vid; |
396 | int port_count; |
397 | |
398 | br_port = port_vlan->br_port; |
399 | port_count = prestera_bridge_vlan_port_count(bridge: br_port->bridge, vid); |
400 | br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); |
401 | |
402 | last_vlan = list_is_singular(head: &br_port->vlan_list); |
403 | last_port = port_count == 1; |
404 | |
405 | if (last_vlan) |
406 | prestera_fdb_flush_port(port, mode: fdb_flush_mode); |
407 | else if (last_port) |
408 | prestera_hw_fdb_flush_vlan(sw: port->sw, vid, mode: fdb_flush_mode); |
409 | else |
410 | prestera_fdb_flush_port_vlan(port, vid, mode: fdb_flush_mode); |
411 | |
412 | prestera_mdb_flush_bridge_port(br_port); |
413 | |
414 | list_del(entry: &port_vlan->br_vlan_head); |
415 | prestera_bridge_vlan_put(br_vlan); |
416 | prestera_bridge_port_put(br_port); |
417 | port_vlan->br_port = NULL; |
418 | } |
419 | |
420 | static void prestera_port_vlan_destroy(struct prestera_port_vlan *port_vlan) |
421 | { |
422 | struct prestera_port *port = port_vlan->port; |
423 | u16 vid = port_vlan->vid; |
424 | |
425 | if (port_vlan->br_port) |
426 | prestera_port_vlan_bridge_leave(port_vlan); |
427 | |
428 | prestera_hw_vlan_port_set(port, vid, is_member: false, untagged: false); |
429 | list_del(entry: &port_vlan->port_head); |
430 | kfree(objp: port_vlan); |
431 | } |
432 | |
433 | static struct prestera_bridge * |
434 | prestera_bridge_create(struct prestera_switchdev *swdev, struct net_device *dev) |
435 | { |
436 | bool vlan_enabled = br_vlan_enabled(dev); |
437 | struct prestera_bridge *bridge; |
438 | u16 bridge_id; |
439 | int err; |
440 | |
441 | if (vlan_enabled && swdev->bridge_8021q_exists) { |
442 | netdev_err(dev, format: "Only one VLAN-aware bridge is supported\n" ); |
443 | return ERR_PTR(error: -EINVAL); |
444 | } |
445 | |
446 | bridge = kzalloc(size: sizeof(*bridge), GFP_KERNEL); |
447 | if (!bridge) |
448 | return ERR_PTR(error: -ENOMEM); |
449 | |
450 | if (vlan_enabled) { |
451 | swdev->bridge_8021q_exists = true; |
452 | } else { |
453 | err = prestera_hw_bridge_create(sw: swdev->sw, bridge_id: &bridge_id); |
454 | if (err) { |
455 | kfree(objp: bridge); |
456 | return ERR_PTR(error: err); |
457 | } |
458 | |
459 | bridge->bridge_id = bridge_id; |
460 | } |
461 | |
462 | bridge->vlan_enabled = vlan_enabled; |
463 | bridge->swdev = swdev; |
464 | bridge->dev = dev; |
465 | bridge->multicast_enabled = br_multicast_enabled(dev); |
466 | |
467 | INIT_LIST_HEAD(list: &bridge->port_list); |
468 | INIT_LIST_HEAD(list: &bridge->br_mdb_entry_list); |
469 | |
470 | list_add(new: &bridge->head, head: &swdev->bridge_list); |
471 | |
472 | return bridge; |
473 | } |
474 | |
475 | static void prestera_bridge_destroy(struct prestera_bridge *bridge) |
476 | { |
477 | struct prestera_switchdev *swdev = bridge->swdev; |
478 | |
479 | list_del(entry: &bridge->head); |
480 | |
481 | if (bridge->vlan_enabled) |
482 | swdev->bridge_8021q_exists = false; |
483 | else |
484 | prestera_hw_bridge_delete(sw: swdev->sw, bridge_id: bridge->bridge_id); |
485 | |
486 | WARN_ON(!list_empty(&bridge->br_mdb_entry_list)); |
487 | WARN_ON(!list_empty(&bridge->port_list)); |
488 | kfree(objp: bridge); |
489 | } |
490 | |
491 | static void prestera_bridge_put(struct prestera_bridge *bridge) |
492 | { |
493 | if (list_empty(head: &bridge->port_list)) |
494 | prestera_bridge_destroy(bridge); |
495 | } |
496 | |
497 | static |
498 | struct prestera_bridge *prestera_bridge_by_dev(struct prestera_switchdev *swdev, |
499 | const struct net_device *dev) |
500 | { |
501 | struct prestera_bridge *bridge; |
502 | |
503 | list_for_each_entry(bridge, &swdev->bridge_list, head) |
504 | if (bridge->dev == dev) |
505 | return bridge; |
506 | |
507 | return NULL; |
508 | } |
509 | |
510 | static struct prestera_bridge_port * |
511 | __prestera_bridge_port_by_dev(struct prestera_bridge *bridge, |
512 | struct net_device *dev) |
513 | { |
514 | struct prestera_bridge_port *br_port; |
515 | |
516 | list_for_each_entry(br_port, &bridge->port_list, head) { |
517 | if (br_port->dev == dev) |
518 | return br_port; |
519 | } |
520 | |
521 | return NULL; |
522 | } |
523 | |
524 | static int prestera_match_upper_bridge_dev(struct net_device *dev, |
525 | struct netdev_nested_priv *priv) |
526 | { |
527 | if (netif_is_bridge_master(dev)) |
528 | priv->data = dev; |
529 | |
530 | return 0; |
531 | } |
532 | |
533 | static struct net_device *prestera_get_upper_bridge_dev(struct net_device *dev) |
534 | { |
535 | struct netdev_nested_priv priv = { }; |
536 | |
537 | netdev_walk_all_upper_dev_rcu(dev, fn: prestera_match_upper_bridge_dev, |
538 | priv: &priv); |
539 | return priv.data; |
540 | } |
541 | |
542 | static struct prestera_bridge_port * |
543 | prestera_bridge_port_by_dev(struct prestera_switchdev *swdev, |
544 | struct net_device *dev) |
545 | { |
546 | struct net_device *br_dev = prestera_get_upper_bridge_dev(dev); |
547 | struct prestera_bridge *bridge; |
548 | |
549 | if (!br_dev) |
550 | return NULL; |
551 | |
552 | bridge = prestera_bridge_by_dev(swdev, dev: br_dev); |
553 | if (!bridge) |
554 | return NULL; |
555 | |
556 | return __prestera_bridge_port_by_dev(bridge, dev); |
557 | } |
558 | |
559 | static struct prestera_bridge_port * |
560 | prestera_bridge_port_create(struct prestera_bridge *bridge, |
561 | struct net_device *dev) |
562 | { |
563 | struct prestera_bridge_port *br_port; |
564 | |
565 | br_port = kzalloc(size: sizeof(*br_port), GFP_KERNEL); |
566 | if (!br_port) |
567 | return NULL; |
568 | |
569 | br_port->flags = BR_LEARNING | BR_FLOOD | BR_LEARNING_SYNC | |
570 | BR_MCAST_FLOOD; |
571 | br_port->stp_state = BR_STATE_DISABLED; |
572 | refcount_set(r: &br_port->ref_count, n: 1); |
573 | br_port->bridge = bridge; |
574 | br_port->dev = dev; |
575 | |
576 | INIT_LIST_HEAD(list: &br_port->vlan_list); |
577 | list_add(new: &br_port->head, head: &bridge->port_list); |
578 | INIT_LIST_HEAD(list: &br_port->br_mdb_port_list); |
579 | |
580 | return br_port; |
581 | } |
582 | |
583 | static void |
584 | prestera_bridge_port_destroy(struct prestera_bridge_port *br_port) |
585 | { |
586 | list_del(entry: &br_port->head); |
587 | WARN_ON(!list_empty(&br_port->vlan_list)); |
588 | WARN_ON(!list_empty(&br_port->br_mdb_port_list)); |
589 | kfree(objp: br_port); |
590 | } |
591 | |
592 | static void prestera_bridge_port_get(struct prestera_bridge_port *br_port) |
593 | { |
594 | refcount_inc(r: &br_port->ref_count); |
595 | } |
596 | |
597 | static void prestera_bridge_port_put(struct prestera_bridge_port *br_port) |
598 | { |
599 | struct prestera_bridge *bridge = br_port->bridge; |
600 | |
601 | if (refcount_dec_and_test(r: &br_port->ref_count)) { |
602 | prestera_bridge_port_destroy(br_port); |
603 | prestera_bridge_put(bridge); |
604 | } |
605 | } |
606 | |
607 | static struct prestera_bridge_port * |
608 | prestera_bridge_port_add(struct prestera_bridge *bridge, struct net_device *dev) |
609 | { |
610 | struct prestera_bridge_port *br_port; |
611 | |
612 | br_port = __prestera_bridge_port_by_dev(bridge, dev); |
613 | if (br_port) { |
614 | prestera_bridge_port_get(br_port); |
615 | return br_port; |
616 | } |
617 | |
618 | br_port = prestera_bridge_port_create(bridge, dev); |
619 | if (!br_port) |
620 | return ERR_PTR(error: -ENOMEM); |
621 | |
622 | return br_port; |
623 | } |
624 | |
625 | static int |
626 | prestera_bridge_1d_port_join(struct prestera_bridge_port *br_port) |
627 | { |
628 | struct prestera_port *port = netdev_priv(dev: br_port->dev); |
629 | struct prestera_bridge *bridge = br_port->bridge; |
630 | int err; |
631 | |
632 | err = prestera_hw_bridge_port_add(port, bridge_id: bridge->bridge_id); |
633 | if (err) |
634 | return err; |
635 | |
636 | err = prestera_br_port_flags_set(br_port, port); |
637 | if (err) |
638 | goto err_flags2port_set; |
639 | |
640 | return 0; |
641 | |
642 | err_flags2port_set: |
643 | prestera_hw_bridge_port_delete(port, bridge_id: bridge->bridge_id); |
644 | |
645 | return err; |
646 | } |
647 | |
648 | int prestera_bridge_port_join(struct net_device *br_dev, |
649 | struct prestera_port *port, |
650 | struct netlink_ext_ack *extack) |
651 | { |
652 | struct prestera_switchdev *swdev = port->sw->swdev; |
653 | struct prestera_bridge_port *br_port; |
654 | struct prestera_bridge *bridge; |
655 | int err; |
656 | |
657 | bridge = prestera_bridge_by_dev(swdev, dev: br_dev); |
658 | if (!bridge) { |
659 | bridge = prestera_bridge_create(swdev, dev: br_dev); |
660 | if (IS_ERR(ptr: bridge)) |
661 | return PTR_ERR(ptr: bridge); |
662 | } |
663 | |
664 | br_port = prestera_bridge_port_add(bridge, dev: port->dev); |
665 | if (IS_ERR(ptr: br_port)) { |
666 | prestera_bridge_put(bridge); |
667 | return PTR_ERR(ptr: br_port); |
668 | } |
669 | |
670 | err = switchdev_bridge_port_offload(brport_dev: br_port->dev, dev: port->dev, NULL, |
671 | NULL, NULL, tx_fwd_offload: false, extack); |
672 | if (err) |
673 | goto err_switchdev_offload; |
674 | |
675 | if (bridge->vlan_enabled) |
676 | return 0; |
677 | |
678 | err = prestera_bridge_1d_port_join(br_port); |
679 | if (err) |
680 | goto err_port_join; |
681 | |
682 | return 0; |
683 | |
684 | err_port_join: |
685 | switchdev_bridge_port_unoffload(brport_dev: br_port->dev, NULL, NULL, NULL); |
686 | err_switchdev_offload: |
687 | prestera_bridge_port_put(br_port); |
688 | return err; |
689 | } |
690 | |
691 | static void prestera_bridge_1q_port_leave(struct prestera_bridge_port *br_port) |
692 | { |
693 | struct prestera_port *port = netdev_priv(dev: br_port->dev); |
694 | |
695 | prestera_hw_fdb_flush_port(port, mode: PRESTERA_FDB_FLUSH_MODE_ALL); |
696 | prestera_port_pvid_set(port, PRESTERA_DEFAULT_VID); |
697 | } |
698 | |
699 | static void prestera_bridge_1d_port_leave(struct prestera_bridge_port *br_port) |
700 | { |
701 | struct prestera_port *port = netdev_priv(dev: br_port->dev); |
702 | |
703 | prestera_hw_fdb_flush_port(port, mode: PRESTERA_FDB_FLUSH_MODE_ALL); |
704 | prestera_hw_bridge_port_delete(port, bridge_id: br_port->bridge->bridge_id); |
705 | } |
706 | |
707 | static int prestera_port_vid_stp_set(struct prestera_port *port, u16 vid, |
708 | u8 state) |
709 | { |
710 | u8 hw_state = state; |
711 | |
712 | switch (state) { |
713 | case BR_STATE_DISABLED: |
714 | hw_state = PRESTERA_STP_DISABLED; |
715 | break; |
716 | |
717 | case BR_STATE_BLOCKING: |
718 | case BR_STATE_LISTENING: |
719 | hw_state = PRESTERA_STP_BLOCK_LISTEN; |
720 | break; |
721 | |
722 | case BR_STATE_LEARNING: |
723 | hw_state = PRESTERA_STP_LEARN; |
724 | break; |
725 | |
726 | case BR_STATE_FORWARDING: |
727 | hw_state = PRESTERA_STP_FORWARD; |
728 | break; |
729 | |
730 | default: |
731 | return -EINVAL; |
732 | } |
733 | |
734 | return prestera_hw_vlan_port_stp_set(port, vid, state: hw_state); |
735 | } |
736 | |
737 | void prestera_bridge_port_leave(struct net_device *br_dev, |
738 | struct prestera_port *port) |
739 | { |
740 | struct prestera_switchdev *swdev = port->sw->swdev; |
741 | struct prestera_bridge_port *br_port; |
742 | struct prestera_bridge *bridge; |
743 | |
744 | bridge = prestera_bridge_by_dev(swdev, dev: br_dev); |
745 | if (!bridge) |
746 | return; |
747 | |
748 | br_port = __prestera_bridge_port_by_dev(bridge, dev: port->dev); |
749 | if (!br_port) |
750 | return; |
751 | |
752 | bridge = br_port->bridge; |
753 | |
754 | if (bridge->vlan_enabled) |
755 | prestera_bridge_1q_port_leave(br_port); |
756 | else |
757 | prestera_bridge_1d_port_leave(br_port); |
758 | |
759 | switchdev_bridge_port_unoffload(brport_dev: br_port->dev, NULL, NULL, NULL); |
760 | |
761 | prestera_mdb_flush_bridge_port(br_port); |
762 | |
763 | prestera_br_port_flags_reset(br_port, port); |
764 | prestera_port_vid_stp_set(port, PRESTERA_VID_ALL, BR_STATE_FORWARDING); |
765 | prestera_bridge_port_put(br_port); |
766 | } |
767 | |
768 | static int prestera_port_attr_br_flags_set(struct prestera_port *port, |
769 | struct net_device *dev, |
770 | struct switchdev_brport_flags flags) |
771 | { |
772 | struct prestera_bridge_port *br_port; |
773 | |
774 | br_port = prestera_bridge_port_by_dev(swdev: port->sw->swdev, dev); |
775 | if (!br_port) |
776 | return 0; |
777 | |
778 | br_port->flags &= ~flags.mask; |
779 | br_port->flags |= flags.val & flags.mask; |
780 | return prestera_br_port_flags_set(br_port, port); |
781 | } |
782 | |
783 | static int prestera_port_attr_br_ageing_set(struct prestera_port *port, |
784 | unsigned long ageing_clock_t) |
785 | { |
786 | unsigned long ageing_jiffies = clock_t_to_jiffies(x: ageing_clock_t); |
787 | u32 ageing_time_ms = jiffies_to_msecs(j: ageing_jiffies); |
788 | struct prestera_switch *sw = port->sw; |
789 | |
790 | if (ageing_time_ms < PRESTERA_MIN_AGEING_TIME_MS || |
791 | ageing_time_ms > PRESTERA_MAX_AGEING_TIME_MS) |
792 | return -ERANGE; |
793 | |
794 | return prestera_hw_switch_ageing_set(sw, ageing_ms: ageing_time_ms); |
795 | } |
796 | |
797 | static int prestera_port_attr_br_vlan_set(struct prestera_port *port, |
798 | struct net_device *dev, |
799 | bool vlan_enabled) |
800 | { |
801 | struct prestera_switch *sw = port->sw; |
802 | struct prestera_bridge *bridge; |
803 | |
804 | bridge = prestera_bridge_by_dev(swdev: sw->swdev, dev); |
805 | if (WARN_ON(!bridge)) |
806 | return -EINVAL; |
807 | |
808 | if (bridge->vlan_enabled == vlan_enabled) |
809 | return 0; |
810 | |
811 | netdev_err(dev: bridge->dev, format: "VLAN filtering can't be changed for existing bridge\n" ); |
812 | |
813 | return -EINVAL; |
814 | } |
815 | |
816 | static int prestera_port_bridge_vlan_stp_set(struct prestera_port *port, |
817 | struct prestera_bridge_vlan *br_vlan, |
818 | u8 state) |
819 | { |
820 | struct prestera_port_vlan *port_vlan; |
821 | |
822 | list_for_each_entry(port_vlan, &br_vlan->port_vlan_list, br_vlan_head) { |
823 | if (port_vlan->port != port) |
824 | continue; |
825 | |
826 | return prestera_port_vid_stp_set(port, vid: br_vlan->vid, state); |
827 | } |
828 | |
829 | return 0; |
830 | } |
831 | |
832 | static int prestera_port_attr_stp_state_set(struct prestera_port *port, |
833 | struct net_device *dev, |
834 | u8 state) |
835 | { |
836 | struct prestera_bridge_port *br_port; |
837 | struct prestera_bridge_vlan *br_vlan; |
838 | int err; |
839 | u16 vid; |
840 | |
841 | br_port = prestera_bridge_port_by_dev(swdev: port->sw->swdev, dev); |
842 | if (!br_port) |
843 | return 0; |
844 | |
845 | if (!br_port->bridge->vlan_enabled) { |
846 | vid = br_port->bridge->bridge_id; |
847 | err = prestera_port_vid_stp_set(port, vid, state); |
848 | if (err) |
849 | goto err_port_stp_set; |
850 | } else { |
851 | list_for_each_entry(br_vlan, &br_port->vlan_list, head) { |
852 | err = prestera_port_bridge_vlan_stp_set(port, br_vlan, |
853 | state); |
854 | if (err) |
855 | goto err_port_vlan_stp_set; |
856 | } |
857 | } |
858 | |
859 | br_port->stp_state = state; |
860 | |
861 | return 0; |
862 | |
863 | err_port_vlan_stp_set: |
864 | list_for_each_entry_continue_reverse(br_vlan, &br_port->vlan_list, head) |
865 | prestera_port_bridge_vlan_stp_set(port, br_vlan, state: br_port->stp_state); |
866 | return err; |
867 | |
868 | err_port_stp_set: |
869 | prestera_port_vid_stp_set(port, vid, state: br_port->stp_state); |
870 | |
871 | return err; |
872 | } |
873 | |
874 | static int |
875 | prestera_br_port_lag_mdb_mc_enable_sync(struct prestera_bridge_port *br_port, |
876 | bool enabled) |
877 | { |
878 | struct prestera_port *pr_port; |
879 | struct prestera_switch *sw; |
880 | u16 lag_id; |
881 | int err; |
882 | |
883 | pr_port = prestera_port_dev_lower_find(dev: br_port->dev); |
884 | if (!pr_port) |
885 | return 0; |
886 | |
887 | sw = pr_port->sw; |
888 | err = prestera_lag_id(sw, lag_dev: br_port->dev, lag_id: &lag_id); |
889 | if (err) |
890 | return err; |
891 | |
892 | list_for_each_entry(pr_port, &sw->port_list, list) { |
893 | if (pr_port->lag->lag_id == lag_id) { |
894 | err = prestera_port_mc_flood_set(port: pr_port, flood: enabled); |
895 | if (err) |
896 | return err; |
897 | } |
898 | } |
899 | |
900 | return 0; |
901 | } |
902 | |
903 | static int prestera_br_mdb_mc_enable_sync(struct prestera_bridge *br_dev) |
904 | { |
905 | struct prestera_bridge_port *br_port; |
906 | struct prestera_port *port; |
907 | bool enabled; |
908 | int err; |
909 | |
910 | /* if mrouter exists: |
911 | * - make sure every mrouter receives unreg mcast traffic; |
912 | * if mrouter doesn't exists: |
913 | * - make sure every port receives unreg mcast traffic; |
914 | */ |
915 | list_for_each_entry(br_port, &br_dev->port_list, head) { |
916 | if (br_dev->multicast_enabled && br_dev->mrouter_exist) |
917 | enabled = br_port->mrouter; |
918 | else |
919 | enabled = br_port->flags & BR_MCAST_FLOOD; |
920 | |
921 | if (netif_is_lag_master(dev: br_port->dev)) { |
922 | err = prestera_br_port_lag_mdb_mc_enable_sync(br_port, |
923 | enabled); |
924 | if (err) |
925 | return err; |
926 | continue; |
927 | } |
928 | |
929 | port = prestera_port_dev_lower_find(dev: br_port->dev); |
930 | if (!port) |
931 | continue; |
932 | |
933 | err = prestera_port_mc_flood_set(port, flood: enabled); |
934 | if (err) |
935 | return err; |
936 | } |
937 | |
938 | return 0; |
939 | } |
940 | |
941 | static bool |
942 | prestera_br_mdb_port_is_member(struct prestera_br_mdb_entry *br_mdb, |
943 | struct net_device *orig_dev) |
944 | { |
945 | struct prestera_br_mdb_port *tmp_port; |
946 | |
947 | list_for_each_entry(tmp_port, &br_mdb->br_mdb_port_list, |
948 | br_mdb_port_node) |
949 | if (tmp_port->br_port->dev == orig_dev) |
950 | return true; |
951 | |
952 | return false; |
953 | } |
954 | |
955 | static int |
956 | prestera_mdb_port_add(struct prestera_mdb_entry *mdb, |
957 | struct net_device *orig_dev, |
958 | const unsigned char addr[ETH_ALEN], u16 vid) |
959 | { |
960 | struct prestera_flood_domain *flood_domain = mdb->flood_domain; |
961 | int err; |
962 | |
963 | if (!prestera_flood_domain_port_find(flood_domain, |
964 | dev: orig_dev, vid)) { |
965 | err = prestera_flood_domain_port_create(flood_domain, dev: orig_dev, |
966 | vid); |
967 | if (err) |
968 | return err; |
969 | } |
970 | |
971 | return 0; |
972 | } |
973 | |
974 | /* Sync bridge mdb (software table) with HW table (if MC is enabled). */ |
975 | static int prestera_br_mdb_sync(struct prestera_bridge *br_dev) |
976 | { |
977 | struct prestera_br_mdb_port *br_mdb_port; |
978 | struct prestera_bridge_port *br_port; |
979 | struct prestera_br_mdb_entry *br_mdb; |
980 | struct prestera_mdb_entry *mdb; |
981 | struct prestera_port *pr_port; |
982 | int err = 0; |
983 | |
984 | if (!br_dev->multicast_enabled) |
985 | return 0; |
986 | |
987 | list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, |
988 | br_mdb_entry_node) { |
989 | mdb = br_mdb->mdb; |
990 | /* Make sure every port that explicitly been added to the mdb |
991 | * joins the specified group. |
992 | */ |
993 | list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, |
994 | br_mdb_port_node) { |
995 | br_port = br_mdb_port->br_port; |
996 | pr_port = prestera_port_dev_lower_find(dev: br_port->dev); |
997 | |
998 | /* Match only mdb and br_mdb ports that belong to the |
999 | * same broadcast domain. |
1000 | */ |
1001 | if (br_dev->vlan_enabled && |
1002 | !prestera_port_vlan_by_vid(port: pr_port, |
1003 | vid: mdb->vid)) |
1004 | continue; |
1005 | |
1006 | /* If port is not in MDB or there's no Mrouter |
1007 | * clear HW mdb. |
1008 | */ |
1009 | if (prestera_br_mdb_port_is_member(br_mdb, |
1010 | orig_dev: br_mdb_port->br_port->dev) && |
1011 | br_dev->mrouter_exist) |
1012 | err = prestera_mdb_port_add(mdb, orig_dev: br_port->dev, |
1013 | addr: mdb->addr, |
1014 | vid: mdb->vid); |
1015 | else |
1016 | prestera_mdb_port_del(mdb, orig_dev: br_port->dev); |
1017 | |
1018 | if (err) |
1019 | return err; |
1020 | } |
1021 | |
1022 | /* Make sure that every mrouter port joins every MC group int |
1023 | * broadcast domain. If it's not an mrouter - it should leave |
1024 | */ |
1025 | list_for_each_entry(br_port, &br_dev->port_list, head) { |
1026 | pr_port = prestera_port_dev_lower_find(dev: br_port->dev); |
1027 | |
1028 | /* Make sure mrouter woudln't receive traffci from |
1029 | * another broadcast domain (e.g. from a vlan, which |
1030 | * mrouter port is not a member of). |
1031 | */ |
1032 | if (br_dev->vlan_enabled && |
1033 | !prestera_port_vlan_by_vid(port: pr_port, |
1034 | vid: mdb->vid)) |
1035 | continue; |
1036 | |
1037 | if (br_port->mrouter) { |
1038 | err = prestera_mdb_port_add(mdb, orig_dev: br_port->dev, |
1039 | addr: mdb->addr, |
1040 | vid: mdb->vid); |
1041 | if (err) |
1042 | return err; |
1043 | } else if (!br_port->mrouter && |
1044 | !prestera_br_mdb_port_is_member |
1045 | (br_mdb, orig_dev: br_port->dev)) { |
1046 | prestera_mdb_port_del(mdb, orig_dev: br_port->dev); |
1047 | } |
1048 | } |
1049 | } |
1050 | |
1051 | return 0; |
1052 | } |
1053 | |
1054 | static int |
1055 | prestera_mdb_enable_set(struct prestera_br_mdb_entry *br_mdb, bool enable) |
1056 | { |
1057 | int err; |
1058 | |
1059 | if (enable != br_mdb->enabled) { |
1060 | if (enable) |
1061 | err = prestera_hw_mdb_create(mdb: br_mdb->mdb); |
1062 | else |
1063 | err = prestera_hw_mdb_destroy(mdb: br_mdb->mdb); |
1064 | |
1065 | if (err) |
1066 | return err; |
1067 | |
1068 | br_mdb->enabled = enable; |
1069 | } |
1070 | |
1071 | return 0; |
1072 | } |
1073 | |
1074 | static int |
1075 | prestera_br_mdb_enable_set(struct prestera_bridge *br_dev, bool enable) |
1076 | { |
1077 | struct prestera_br_mdb_entry *br_mdb; |
1078 | int err; |
1079 | |
1080 | list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, |
1081 | br_mdb_entry_node) { |
1082 | err = prestera_mdb_enable_set(br_mdb, enable); |
1083 | if (err) |
1084 | return err; |
1085 | } |
1086 | |
1087 | return 0; |
1088 | } |
1089 | |
1090 | static int prestera_port_attr_br_mc_disabled_set(struct prestera_port *port, |
1091 | struct net_device *orig_dev, |
1092 | bool mc_disabled) |
1093 | { |
1094 | struct prestera_switch *sw = port->sw; |
1095 | struct prestera_bridge *br_dev; |
1096 | |
1097 | br_dev = prestera_bridge_find(sw, br_dev: orig_dev); |
1098 | if (!br_dev) |
1099 | return 0; |
1100 | |
1101 | br_dev->multicast_enabled = !mc_disabled; |
1102 | |
1103 | /* There's no point in enabling mdb back if router is missing. */ |
1104 | WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && |
1105 | br_dev->mrouter_exist)); |
1106 | |
1107 | WARN_ON(prestera_br_mdb_sync(br_dev)); |
1108 | |
1109 | WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); |
1110 | |
1111 | return 0; |
1112 | } |
1113 | |
1114 | static bool |
1115 | prestera_bridge_mdb_mc_mrouter_exists(struct prestera_bridge *br_dev) |
1116 | { |
1117 | struct prestera_bridge_port *br_port; |
1118 | |
1119 | list_for_each_entry(br_port, &br_dev->port_list, head) |
1120 | if (br_port->mrouter) |
1121 | return true; |
1122 | |
1123 | return false; |
1124 | } |
1125 | |
1126 | static int |
1127 | prestera_port_attr_mrouter_set(struct prestera_port *port, |
1128 | struct net_device *orig_dev, |
1129 | bool is_port_mrouter) |
1130 | { |
1131 | struct prestera_bridge_port *br_port; |
1132 | struct prestera_bridge *br_dev; |
1133 | |
1134 | br_port = prestera_bridge_port_find(sw: port->sw, brport_dev: orig_dev); |
1135 | if (!br_port) |
1136 | return 0; |
1137 | |
1138 | br_dev = br_port->bridge; |
1139 | br_port->mrouter = is_port_mrouter; |
1140 | |
1141 | br_dev->mrouter_exist = prestera_bridge_mdb_mc_mrouter_exists(br_dev); |
1142 | |
1143 | /* Enable MDB processing if both mrouter exists and mc is enabled. |
1144 | * In case if MC enabled, but there is no mrouter, device would flood |
1145 | * all multicast traffic (even if MDB table is not empty) with the use |
1146 | * of bridge's flood capabilities (without the use of flood_domain). |
1147 | */ |
1148 | WARN_ON(prestera_br_mdb_enable_set(br_dev, br_dev->multicast_enabled && |
1149 | br_dev->mrouter_exist)); |
1150 | |
1151 | WARN_ON(prestera_br_mdb_sync(br_dev)); |
1152 | |
1153 | WARN_ON(prestera_br_mdb_mc_enable_sync(br_dev)); |
1154 | |
1155 | return 0; |
1156 | } |
1157 | |
1158 | static int prestera_port_obj_attr_set(struct net_device *dev, const void *ctx, |
1159 | const struct switchdev_attr *attr, |
1160 | struct netlink_ext_ack *extack) |
1161 | { |
1162 | struct prestera_port *port = netdev_priv(dev); |
1163 | int err = 0; |
1164 | |
1165 | switch (attr->id) { |
1166 | case SWITCHDEV_ATTR_ID_PORT_STP_STATE: |
1167 | err = prestera_port_attr_stp_state_set(port, dev: attr->orig_dev, |
1168 | state: attr->u.stp_state); |
1169 | break; |
1170 | case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: |
1171 | if (attr->u.brport_flags.mask & |
1172 | ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_PORT_LOCKED)) |
1173 | err = -EINVAL; |
1174 | break; |
1175 | case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: |
1176 | err = prestera_port_attr_br_flags_set(port, dev: attr->orig_dev, |
1177 | flags: attr->u.brport_flags); |
1178 | break; |
1179 | case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME: |
1180 | err = prestera_port_attr_br_ageing_set(port, |
1181 | ageing_clock_t: attr->u.ageing_time); |
1182 | break; |
1183 | case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: |
1184 | err = prestera_port_attr_br_vlan_set(port, dev: attr->orig_dev, |
1185 | vlan_enabled: attr->u.vlan_filtering); |
1186 | break; |
1187 | case SWITCHDEV_ATTR_ID_PORT_MROUTER: |
1188 | err = prestera_port_attr_mrouter_set(port, orig_dev: attr->orig_dev, |
1189 | is_port_mrouter: attr->u.mrouter); |
1190 | break; |
1191 | case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED: |
1192 | err = prestera_port_attr_br_mc_disabled_set(port, orig_dev: attr->orig_dev, |
1193 | mc_disabled: attr->u.mc_disabled); |
1194 | break; |
1195 | default: |
1196 | err = -EOPNOTSUPP; |
1197 | } |
1198 | |
1199 | return err; |
1200 | } |
1201 | |
1202 | static void |
1203 | prestera_fdb_offload_notify(struct prestera_port *port, |
1204 | struct switchdev_notifier_fdb_info *info) |
1205 | { |
1206 | struct switchdev_notifier_fdb_info send_info = {}; |
1207 | |
1208 | send_info.addr = info->addr; |
1209 | send_info.vid = info->vid; |
1210 | send_info.offloaded = true; |
1211 | |
1212 | call_switchdev_notifiers(val: SWITCHDEV_FDB_OFFLOADED, dev: port->dev, |
1213 | info: &send_info.info, NULL); |
1214 | } |
1215 | |
1216 | static int prestera_port_fdb_set(struct prestera_port *port, |
1217 | struct switchdev_notifier_fdb_info *fdb_info, |
1218 | bool adding) |
1219 | { |
1220 | struct prestera_switch *sw = port->sw; |
1221 | struct prestera_bridge_port *br_port; |
1222 | struct prestera_bridge *bridge; |
1223 | int err; |
1224 | u16 vid; |
1225 | |
1226 | br_port = prestera_bridge_port_by_dev(swdev: sw->swdev, dev: port->dev); |
1227 | if (!br_port) |
1228 | return -EINVAL; |
1229 | |
1230 | bridge = br_port->bridge; |
1231 | |
1232 | if (bridge->vlan_enabled) |
1233 | vid = fdb_info->vid; |
1234 | else |
1235 | vid = bridge->bridge_id; |
1236 | |
1237 | if (adding) |
1238 | err = prestera_fdb_add(port, mac: fdb_info->addr, vid, dynamic: false); |
1239 | else |
1240 | err = prestera_fdb_del(port, mac: fdb_info->addr, vid); |
1241 | |
1242 | return err; |
1243 | } |
1244 | |
1245 | static void prestera_fdb_event_work(struct work_struct *work) |
1246 | { |
1247 | struct switchdev_notifier_fdb_info *fdb_info; |
1248 | struct prestera_fdb_event_work *swdev_work; |
1249 | struct prestera_port *port; |
1250 | struct net_device *dev; |
1251 | int err; |
1252 | |
1253 | swdev_work = container_of(work, struct prestera_fdb_event_work, work); |
1254 | dev = swdev_work->dev; |
1255 | |
1256 | rtnl_lock(); |
1257 | |
1258 | port = prestera_port_dev_lower_find(dev); |
1259 | if (!port) |
1260 | goto out_unlock; |
1261 | |
1262 | switch (swdev_work->event) { |
1263 | case SWITCHDEV_FDB_ADD_TO_DEVICE: |
1264 | fdb_info = &swdev_work->fdb_info; |
1265 | if (!fdb_info->added_by_user || fdb_info->is_local) |
1266 | break; |
1267 | |
1268 | err = prestera_port_fdb_set(port, fdb_info, adding: true); |
1269 | if (err) |
1270 | break; |
1271 | |
1272 | prestera_fdb_offload_notify(port, info: fdb_info); |
1273 | break; |
1274 | |
1275 | case SWITCHDEV_FDB_DEL_TO_DEVICE: |
1276 | fdb_info = &swdev_work->fdb_info; |
1277 | prestera_port_fdb_set(port, fdb_info, adding: false); |
1278 | break; |
1279 | } |
1280 | |
1281 | out_unlock: |
1282 | rtnl_unlock(); |
1283 | |
1284 | kfree(objp: swdev_work->fdb_info.addr); |
1285 | kfree(objp: swdev_work); |
1286 | dev_put(dev); |
1287 | } |
1288 | |
1289 | static int prestera_switchdev_event(struct notifier_block *unused, |
1290 | unsigned long event, void *ptr) |
1291 | { |
1292 | struct net_device *dev = switchdev_notifier_info_to_dev(info: ptr); |
1293 | struct switchdev_notifier_fdb_info *fdb_info; |
1294 | struct switchdev_notifier_info *info = ptr; |
1295 | struct prestera_fdb_event_work *swdev_work; |
1296 | struct net_device *upper; |
1297 | int err; |
1298 | |
1299 | if (event == SWITCHDEV_PORT_ATTR_SET) { |
1300 | err = switchdev_handle_port_attr_set(dev, port_attr_info: ptr, |
1301 | check_cb: prestera_netdev_check, |
1302 | set_cb: prestera_port_obj_attr_set); |
1303 | return notifier_from_errno(err); |
1304 | } |
1305 | |
1306 | if (!prestera_netdev_check(dev)) |
1307 | return NOTIFY_DONE; |
1308 | |
1309 | upper = netdev_master_upper_dev_get_rcu(dev); |
1310 | if (!upper) |
1311 | return NOTIFY_DONE; |
1312 | |
1313 | if (!netif_is_bridge_master(dev: upper)) |
1314 | return NOTIFY_DONE; |
1315 | |
1316 | swdev_work = kzalloc(size: sizeof(*swdev_work), GFP_ATOMIC); |
1317 | if (!swdev_work) |
1318 | return NOTIFY_BAD; |
1319 | |
1320 | swdev_work->event = event; |
1321 | swdev_work->dev = dev; |
1322 | |
1323 | switch (event) { |
1324 | case SWITCHDEV_FDB_ADD_TO_DEVICE: |
1325 | case SWITCHDEV_FDB_DEL_TO_DEVICE: |
1326 | fdb_info = container_of(info, |
1327 | struct switchdev_notifier_fdb_info, |
1328 | info); |
1329 | |
1330 | INIT_WORK(&swdev_work->work, prestera_fdb_event_work); |
1331 | memcpy(&swdev_work->fdb_info, ptr, |
1332 | sizeof(swdev_work->fdb_info)); |
1333 | |
1334 | swdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); |
1335 | if (!swdev_work->fdb_info.addr) |
1336 | goto out_bad; |
1337 | |
1338 | ether_addr_copy(dst: (u8 *)swdev_work->fdb_info.addr, |
1339 | src: fdb_info->addr); |
1340 | dev_hold(dev); |
1341 | break; |
1342 | |
1343 | default: |
1344 | kfree(objp: swdev_work); |
1345 | return NOTIFY_DONE; |
1346 | } |
1347 | |
1348 | queue_work(wq: swdev_wq, work: &swdev_work->work); |
1349 | return NOTIFY_DONE; |
1350 | |
1351 | out_bad: |
1352 | kfree(objp: swdev_work); |
1353 | return NOTIFY_BAD; |
1354 | } |
1355 | |
1356 | static int |
1357 | prestera_port_vlan_bridge_join(struct prestera_port_vlan *port_vlan, |
1358 | struct prestera_bridge_port *br_port) |
1359 | { |
1360 | struct prestera_port *port = port_vlan->port; |
1361 | struct prestera_bridge_vlan *br_vlan; |
1362 | u16 vid = port_vlan->vid; |
1363 | int err; |
1364 | |
1365 | if (port_vlan->br_port) |
1366 | return 0; |
1367 | |
1368 | err = prestera_br_port_flags_set(br_port, port); |
1369 | if (err) |
1370 | goto err_flags2port_set; |
1371 | |
1372 | err = prestera_port_vid_stp_set(port, vid, state: br_port->stp_state); |
1373 | if (err) |
1374 | goto err_port_vid_stp_set; |
1375 | |
1376 | br_vlan = prestera_bridge_vlan_by_vid(br_port, vid); |
1377 | if (!br_vlan) { |
1378 | br_vlan = prestera_bridge_vlan_create(br_port, vid); |
1379 | if (!br_vlan) { |
1380 | err = -ENOMEM; |
1381 | goto err_bridge_vlan_get; |
1382 | } |
1383 | } |
1384 | |
1385 | list_add(new: &port_vlan->br_vlan_head, head: &br_vlan->port_vlan_list); |
1386 | |
1387 | prestera_bridge_port_get(br_port); |
1388 | port_vlan->br_port = br_port; |
1389 | |
1390 | return 0; |
1391 | |
1392 | err_bridge_vlan_get: |
1393 | prestera_port_vid_stp_set(port, vid, BR_STATE_FORWARDING); |
1394 | err_port_vid_stp_set: |
1395 | prestera_br_port_flags_reset(br_port, port); |
1396 | err_flags2port_set: |
1397 | return err; |
1398 | } |
1399 | |
1400 | static int |
1401 | prestera_bridge_port_vlan_add(struct prestera_port *port, |
1402 | struct prestera_bridge_port *br_port, |
1403 | u16 vid, bool is_untagged, bool is_pvid, |
1404 | struct netlink_ext_ack *extack) |
1405 | { |
1406 | struct prestera_port_vlan *port_vlan; |
1407 | u16 old_pvid = port->pvid; |
1408 | u16 pvid; |
1409 | int err; |
1410 | |
1411 | if (is_pvid) |
1412 | pvid = vid; |
1413 | else |
1414 | pvid = port->pvid == vid ? 0 : port->pvid; |
1415 | |
1416 | port_vlan = prestera_port_vlan_by_vid(port, vid); |
1417 | if (port_vlan && port_vlan->br_port != br_port) |
1418 | return -EEXIST; |
1419 | |
1420 | if (!port_vlan) { |
1421 | port_vlan = prestera_port_vlan_create(port, vid, untagged: is_untagged); |
1422 | if (IS_ERR(ptr: port_vlan)) |
1423 | return PTR_ERR(ptr: port_vlan); |
1424 | } else { |
1425 | err = prestera_hw_vlan_port_set(port, vid, is_member: true, untagged: is_untagged); |
1426 | if (err) |
1427 | goto err_port_vlan_set; |
1428 | } |
1429 | |
1430 | err = prestera_port_pvid_set(port, vid: pvid); |
1431 | if (err) |
1432 | goto err_port_pvid_set; |
1433 | |
1434 | err = prestera_port_vlan_bridge_join(port_vlan, br_port); |
1435 | if (err) |
1436 | goto err_port_vlan_bridge_join; |
1437 | |
1438 | return 0; |
1439 | |
1440 | err_port_vlan_bridge_join: |
1441 | prestera_port_pvid_set(port, vid: old_pvid); |
1442 | err_port_pvid_set: |
1443 | prestera_hw_vlan_port_set(port, vid, is_member: false, untagged: false); |
1444 | err_port_vlan_set: |
1445 | prestera_port_vlan_destroy(port_vlan); |
1446 | |
1447 | return err; |
1448 | } |
1449 | |
1450 | static void |
1451 | prestera_bridge_port_vlan_del(struct prestera_port *port, |
1452 | struct prestera_bridge_port *br_port, u16 vid) |
1453 | { |
1454 | u16 pvid = port->pvid == vid ? 0 : port->pvid; |
1455 | struct prestera_port_vlan *port_vlan; |
1456 | |
1457 | port_vlan = prestera_port_vlan_by_vid(port, vid); |
1458 | if (WARN_ON(!port_vlan)) |
1459 | return; |
1460 | |
1461 | prestera_port_vlan_bridge_leave(port_vlan); |
1462 | prestera_port_pvid_set(port, vid: pvid); |
1463 | prestera_port_vlan_destroy(port_vlan); |
1464 | } |
1465 | |
1466 | static int prestera_port_vlans_add(struct prestera_port *port, |
1467 | const struct switchdev_obj_port_vlan *vlan, |
1468 | struct netlink_ext_ack *extack) |
1469 | { |
1470 | bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; |
1471 | bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; |
1472 | struct net_device *orig_dev = vlan->obj.orig_dev; |
1473 | struct prestera_bridge_port *br_port; |
1474 | struct prestera_switch *sw = port->sw; |
1475 | struct prestera_bridge *bridge; |
1476 | |
1477 | if (netif_is_bridge_master(dev: orig_dev)) |
1478 | return 0; |
1479 | |
1480 | br_port = prestera_bridge_port_by_dev(swdev: sw->swdev, dev: port->dev); |
1481 | if (WARN_ON(!br_port)) |
1482 | return -EINVAL; |
1483 | |
1484 | bridge = br_port->bridge; |
1485 | if (!bridge->vlan_enabled) |
1486 | return 0; |
1487 | |
1488 | return prestera_bridge_port_vlan_add(port, br_port, |
1489 | vid: vlan->vid, is_untagged: flag_untagged, |
1490 | is_pvid: flag_pvid, extack); |
1491 | } |
1492 | |
1493 | static struct prestera_br_mdb_entry * |
1494 | prestera_br_mdb_entry_create(struct prestera_switch *sw, |
1495 | struct prestera_bridge *br_dev, |
1496 | const unsigned char *addr, u16 vid) |
1497 | { |
1498 | struct prestera_br_mdb_entry *br_mdb_entry; |
1499 | struct prestera_mdb_entry *mdb_entry; |
1500 | |
1501 | br_mdb_entry = kzalloc(size: sizeof(*br_mdb_entry), GFP_KERNEL); |
1502 | if (!br_mdb_entry) |
1503 | return NULL; |
1504 | |
1505 | mdb_entry = prestera_mdb_entry_create(sw, addr, vid); |
1506 | if (!mdb_entry) |
1507 | goto err_mdb_alloc; |
1508 | |
1509 | br_mdb_entry->mdb = mdb_entry; |
1510 | br_mdb_entry->bridge = br_dev; |
1511 | br_mdb_entry->enabled = true; |
1512 | INIT_LIST_HEAD(list: &br_mdb_entry->br_mdb_port_list); |
1513 | |
1514 | list_add(new: &br_mdb_entry->br_mdb_entry_node, head: &br_dev->br_mdb_entry_list); |
1515 | |
1516 | return br_mdb_entry; |
1517 | |
1518 | err_mdb_alloc: |
1519 | kfree(objp: br_mdb_entry); |
1520 | return NULL; |
1521 | } |
1522 | |
1523 | static int prestera_br_mdb_port_add(struct prestera_br_mdb_entry *br_mdb, |
1524 | struct prestera_bridge_port *br_port) |
1525 | { |
1526 | struct prestera_br_mdb_port *br_mdb_port; |
1527 | |
1528 | list_for_each_entry(br_mdb_port, &br_mdb->br_mdb_port_list, |
1529 | br_mdb_port_node) |
1530 | if (br_mdb_port->br_port == br_port) |
1531 | return 0; |
1532 | |
1533 | br_mdb_port = kzalloc(size: sizeof(*br_mdb_port), GFP_KERNEL); |
1534 | if (!br_mdb_port) |
1535 | return -ENOMEM; |
1536 | |
1537 | br_mdb_port->br_port = br_port; |
1538 | list_add(new: &br_mdb_port->br_mdb_port_node, |
1539 | head: &br_mdb->br_mdb_port_list); |
1540 | |
1541 | return 0; |
1542 | } |
1543 | |
1544 | static struct prestera_br_mdb_entry * |
1545 | prestera_br_mdb_entry_find(struct prestera_bridge *br_dev, |
1546 | const unsigned char *addr, u16 vid) |
1547 | { |
1548 | struct prestera_br_mdb_entry *br_mdb; |
1549 | |
1550 | list_for_each_entry(br_mdb, &br_dev->br_mdb_entry_list, |
1551 | br_mdb_entry_node) |
1552 | if (ether_addr_equal(addr1: &br_mdb->mdb->addr[0], addr2: addr) && |
1553 | vid == br_mdb->mdb->vid) |
1554 | return br_mdb; |
1555 | |
1556 | return NULL; |
1557 | } |
1558 | |
1559 | static struct prestera_br_mdb_entry * |
1560 | prestera_br_mdb_entry_get(struct prestera_switch *sw, |
1561 | struct prestera_bridge *br_dev, |
1562 | const unsigned char *addr, u16 vid) |
1563 | { |
1564 | struct prestera_br_mdb_entry *br_mdb; |
1565 | |
1566 | br_mdb = prestera_br_mdb_entry_find(br_dev, addr, vid); |
1567 | if (br_mdb) |
1568 | return br_mdb; |
1569 | |
1570 | return prestera_br_mdb_entry_create(sw, br_dev, addr, vid); |
1571 | } |
1572 | |
1573 | static int |
1574 | prestera_mdb_port_addr_obj_add(const struct switchdev_obj_port_mdb *mdb) |
1575 | { |
1576 | struct prestera_br_mdb_entry *br_mdb; |
1577 | struct prestera_bridge_port *br_port; |
1578 | struct prestera_bridge *br_dev; |
1579 | struct prestera_switch *sw; |
1580 | struct prestera_port *port; |
1581 | int err; |
1582 | |
1583 | sw = prestera_switch_get(dev: mdb->obj.orig_dev); |
1584 | port = prestera_port_dev_lower_find(dev: mdb->obj.orig_dev); |
1585 | |
1586 | br_port = prestera_bridge_port_find(sw, brport_dev: mdb->obj.orig_dev); |
1587 | if (!br_port) |
1588 | return 0; |
1589 | |
1590 | br_dev = br_port->bridge; |
1591 | |
1592 | if (mdb->vid && !prestera_port_vlan_by_vid(port, vid: mdb->vid)) |
1593 | return 0; |
1594 | |
1595 | if (mdb->vid) |
1596 | br_mdb = prestera_br_mdb_entry_get(sw, br_dev, addr: &mdb->addr[0], |
1597 | vid: mdb->vid); |
1598 | else |
1599 | br_mdb = prestera_br_mdb_entry_get(sw, br_dev, addr: &mdb->addr[0], |
1600 | vid: br_dev->bridge_id); |
1601 | |
1602 | if (!br_mdb) |
1603 | return -ENOMEM; |
1604 | |
1605 | /* Make sure newly allocated MDB entry gets disabled if either MC is |
1606 | * disabled, or the mrouter does not exist. |
1607 | */ |
1608 | WARN_ON(prestera_mdb_enable_set(br_mdb, br_dev->multicast_enabled && |
1609 | br_dev->mrouter_exist)); |
1610 | |
1611 | err = prestera_br_mdb_port_add(br_mdb, br_port); |
1612 | if (err) { |
1613 | prestera_br_mdb_entry_put(br_mdb); |
1614 | return err; |
1615 | } |
1616 | |
1617 | err = prestera_br_mdb_sync(br_dev); |
1618 | if (err) |
1619 | return err; |
1620 | |
1621 | return 0; |
1622 | } |
1623 | |
1624 | static int prestera_port_obj_add(struct net_device *dev, const void *ctx, |
1625 | const struct switchdev_obj *obj, |
1626 | struct netlink_ext_ack *extack) |
1627 | { |
1628 | struct prestera_port *port = netdev_priv(dev); |
1629 | const struct switchdev_obj_port_vlan *vlan; |
1630 | const struct switchdev_obj_port_mdb *mdb; |
1631 | int err = 0; |
1632 | |
1633 | switch (obj->id) { |
1634 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
1635 | vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
1636 | return prestera_port_vlans_add(port, vlan, extack); |
1637 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
1638 | mdb = SWITCHDEV_OBJ_PORT_MDB(obj); |
1639 | err = prestera_mdb_port_addr_obj_add(mdb); |
1640 | break; |
1641 | case SWITCHDEV_OBJ_ID_HOST_MDB: |
1642 | fallthrough; |
1643 | default: |
1644 | err = -EOPNOTSUPP; |
1645 | break; |
1646 | } |
1647 | |
1648 | return err; |
1649 | } |
1650 | |
1651 | static int prestera_port_vlans_del(struct prestera_port *port, |
1652 | const struct switchdev_obj_port_vlan *vlan) |
1653 | { |
1654 | struct net_device *orig_dev = vlan->obj.orig_dev; |
1655 | struct prestera_bridge_port *br_port; |
1656 | struct prestera_switch *sw = port->sw; |
1657 | |
1658 | if (netif_is_bridge_master(dev: orig_dev)) |
1659 | return -EOPNOTSUPP; |
1660 | |
1661 | br_port = prestera_bridge_port_by_dev(swdev: sw->swdev, dev: port->dev); |
1662 | if (WARN_ON(!br_port)) |
1663 | return -EINVAL; |
1664 | |
1665 | if (!br_port->bridge->vlan_enabled) |
1666 | return 0; |
1667 | |
1668 | prestera_bridge_port_vlan_del(port, br_port, vid: vlan->vid); |
1669 | |
1670 | return 0; |
1671 | } |
1672 | |
1673 | static int |
1674 | prestera_mdb_port_addr_obj_del(struct prestera_port *port, |
1675 | const struct switchdev_obj_port_mdb *mdb) |
1676 | { |
1677 | struct prestera_br_mdb_entry *br_mdb; |
1678 | struct prestera_bridge_port *br_port; |
1679 | struct prestera_bridge *br_dev; |
1680 | int err; |
1681 | |
1682 | /* Bridge port no longer exists - and so does this MDB entry */ |
1683 | br_port = prestera_bridge_port_find(sw: port->sw, brport_dev: mdb->obj.orig_dev); |
1684 | if (!br_port) |
1685 | return 0; |
1686 | |
1687 | /* Removing MDB with non-existing VLAN - not supported; */ |
1688 | if (mdb->vid && !prestera_port_vlan_by_vid(port, vid: mdb->vid)) |
1689 | return 0; |
1690 | |
1691 | br_dev = br_port->bridge; |
1692 | |
1693 | if (br_port->bridge->vlan_enabled) |
1694 | br_mdb = prestera_br_mdb_entry_find(br_dev, addr: &mdb->addr[0], |
1695 | vid: mdb->vid); |
1696 | else |
1697 | br_mdb = prestera_br_mdb_entry_find(br_dev, addr: &mdb->addr[0], |
1698 | vid: br_port->bridge->bridge_id); |
1699 | |
1700 | if (!br_mdb) |
1701 | return 0; |
1702 | |
1703 | /* Since there might be a situation that this port was the last in the |
1704 | * MDB group, we have to both remove this port from software and HW MDB, |
1705 | * sync MDB table, and then destroy software MDB (if needed). |
1706 | */ |
1707 | prestera_br_mdb_port_del(br_mdb, br_port); |
1708 | |
1709 | prestera_br_mdb_entry_put(br_mdb); |
1710 | |
1711 | err = prestera_br_mdb_sync(br_dev); |
1712 | if (err) |
1713 | return err; |
1714 | |
1715 | return 0; |
1716 | } |
1717 | |
1718 | static int prestera_port_obj_del(struct net_device *dev, const void *ctx, |
1719 | const struct switchdev_obj *obj) |
1720 | { |
1721 | struct prestera_port *port = netdev_priv(dev); |
1722 | const struct switchdev_obj_port_mdb *mdb; |
1723 | int err = 0; |
1724 | |
1725 | switch (obj->id) { |
1726 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
1727 | return prestera_port_vlans_del(port, SWITCHDEV_OBJ_PORT_VLAN(obj)); |
1728 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
1729 | mdb = SWITCHDEV_OBJ_PORT_MDB(obj); |
1730 | err = prestera_mdb_port_addr_obj_del(port, mdb); |
1731 | break; |
1732 | default: |
1733 | err = -EOPNOTSUPP; |
1734 | break; |
1735 | } |
1736 | |
1737 | return err; |
1738 | } |
1739 | |
1740 | static int prestera_switchdev_blk_event(struct notifier_block *unused, |
1741 | unsigned long event, void *ptr) |
1742 | { |
1743 | struct net_device *dev = switchdev_notifier_info_to_dev(info: ptr); |
1744 | int err; |
1745 | |
1746 | switch (event) { |
1747 | case SWITCHDEV_PORT_OBJ_ADD: |
1748 | err = switchdev_handle_port_obj_add(dev, port_obj_info: ptr, |
1749 | check_cb: prestera_netdev_check, |
1750 | add_cb: prestera_port_obj_add); |
1751 | break; |
1752 | case SWITCHDEV_PORT_OBJ_DEL: |
1753 | err = switchdev_handle_port_obj_del(dev, port_obj_info: ptr, |
1754 | check_cb: prestera_netdev_check, |
1755 | del_cb: prestera_port_obj_del); |
1756 | break; |
1757 | case SWITCHDEV_PORT_ATTR_SET: |
1758 | err = switchdev_handle_port_attr_set(dev, port_attr_info: ptr, |
1759 | check_cb: prestera_netdev_check, |
1760 | set_cb: prestera_port_obj_attr_set); |
1761 | break; |
1762 | default: |
1763 | return NOTIFY_DONE; |
1764 | } |
1765 | |
1766 | return notifier_from_errno(err); |
1767 | } |
1768 | |
1769 | static void prestera_fdb_event(struct prestera_switch *sw, |
1770 | struct prestera_event *evt, void *arg) |
1771 | { |
1772 | struct switchdev_notifier_fdb_info info = {}; |
1773 | struct net_device *dev = NULL; |
1774 | struct prestera_port *port; |
1775 | struct prestera_lag *lag; |
1776 | |
1777 | switch (evt->fdb_evt.type) { |
1778 | case PRESTERA_FDB_ENTRY_TYPE_REG_PORT: |
1779 | port = prestera_find_port(sw, id: evt->fdb_evt.dest.port_id); |
1780 | if (port) |
1781 | dev = port->dev; |
1782 | break; |
1783 | case PRESTERA_FDB_ENTRY_TYPE_LAG: |
1784 | lag = prestera_lag_by_id(sw, id: evt->fdb_evt.dest.lag_id); |
1785 | if (lag) |
1786 | dev = lag->dev; |
1787 | break; |
1788 | default: |
1789 | return; |
1790 | } |
1791 | |
1792 | if (!dev) |
1793 | return; |
1794 | |
1795 | info.addr = evt->fdb_evt.data.mac; |
1796 | info.vid = evt->fdb_evt.vid; |
1797 | info.offloaded = true; |
1798 | |
1799 | rtnl_lock(); |
1800 | |
1801 | switch (evt->id) { |
1802 | case PRESTERA_FDB_EVENT_LEARNED: |
1803 | call_switchdev_notifiers(val: SWITCHDEV_FDB_ADD_TO_BRIDGE, |
1804 | dev, info: &info.info, NULL); |
1805 | break; |
1806 | case PRESTERA_FDB_EVENT_AGED: |
1807 | call_switchdev_notifiers(val: SWITCHDEV_FDB_DEL_TO_BRIDGE, |
1808 | dev, info: &info.info, NULL); |
1809 | break; |
1810 | } |
1811 | |
1812 | rtnl_unlock(); |
1813 | } |
1814 | |
1815 | static int prestera_fdb_init(struct prestera_switch *sw) |
1816 | { |
1817 | int err; |
1818 | |
1819 | err = prestera_hw_event_handler_register(sw, type: PRESTERA_EVENT_TYPE_FDB, |
1820 | fn: prestera_fdb_event, NULL); |
1821 | if (err) |
1822 | return err; |
1823 | |
1824 | err = prestera_hw_switch_ageing_set(sw, PRESTERA_DEFAULT_AGEING_TIME_MS); |
1825 | if (err) |
1826 | goto err_ageing_set; |
1827 | |
1828 | return 0; |
1829 | |
1830 | err_ageing_set: |
1831 | prestera_hw_event_handler_unregister(sw, type: PRESTERA_EVENT_TYPE_FDB, |
1832 | fn: prestera_fdb_event); |
1833 | return err; |
1834 | } |
1835 | |
1836 | static void prestera_fdb_fini(struct prestera_switch *sw) |
1837 | { |
1838 | prestera_hw_event_handler_unregister(sw, type: PRESTERA_EVENT_TYPE_FDB, |
1839 | fn: prestera_fdb_event); |
1840 | } |
1841 | |
1842 | static int prestera_switchdev_handler_init(struct prestera_switchdev *swdev) |
1843 | { |
1844 | int err; |
1845 | |
1846 | swdev->swdev_nb.notifier_call = prestera_switchdev_event; |
1847 | err = register_switchdev_notifier(nb: &swdev->swdev_nb); |
1848 | if (err) |
1849 | goto err_register_swdev_notifier; |
1850 | |
1851 | swdev->swdev_nb_blk.notifier_call = prestera_switchdev_blk_event; |
1852 | err = register_switchdev_blocking_notifier(nb: &swdev->swdev_nb_blk); |
1853 | if (err) |
1854 | goto err_register_blk_swdev_notifier; |
1855 | |
1856 | return 0; |
1857 | |
1858 | err_register_blk_swdev_notifier: |
1859 | unregister_switchdev_notifier(nb: &swdev->swdev_nb); |
1860 | err_register_swdev_notifier: |
1861 | destroy_workqueue(wq: swdev_wq); |
1862 | return err; |
1863 | } |
1864 | |
1865 | static void prestera_switchdev_handler_fini(struct prestera_switchdev *swdev) |
1866 | { |
1867 | unregister_switchdev_blocking_notifier(nb: &swdev->swdev_nb_blk); |
1868 | unregister_switchdev_notifier(nb: &swdev->swdev_nb); |
1869 | } |
1870 | |
1871 | int prestera_switchdev_init(struct prestera_switch *sw) |
1872 | { |
1873 | struct prestera_switchdev *swdev; |
1874 | int err; |
1875 | |
1876 | swdev = kzalloc(size: sizeof(*swdev), GFP_KERNEL); |
1877 | if (!swdev) |
1878 | return -ENOMEM; |
1879 | |
1880 | sw->swdev = swdev; |
1881 | swdev->sw = sw; |
1882 | |
1883 | INIT_LIST_HEAD(list: &swdev->bridge_list); |
1884 | |
1885 | swdev_wq = alloc_ordered_workqueue("%s_ordered" , 0, "prestera_br" ); |
1886 | if (!swdev_wq) { |
1887 | err = -ENOMEM; |
1888 | goto err_alloc_wq; |
1889 | } |
1890 | |
1891 | err = prestera_switchdev_handler_init(swdev); |
1892 | if (err) |
1893 | goto err_swdev_init; |
1894 | |
1895 | err = prestera_fdb_init(sw); |
1896 | if (err) |
1897 | goto err_fdb_init; |
1898 | |
1899 | return 0; |
1900 | |
1901 | err_fdb_init: |
1902 | err_swdev_init: |
1903 | destroy_workqueue(wq: swdev_wq); |
1904 | err_alloc_wq: |
1905 | kfree(objp: swdev); |
1906 | |
1907 | return err; |
1908 | } |
1909 | |
1910 | void prestera_switchdev_fini(struct prestera_switch *sw) |
1911 | { |
1912 | struct prestera_switchdev *swdev = sw->swdev; |
1913 | |
1914 | prestera_fdb_fini(sw); |
1915 | prestera_switchdev_handler_fini(swdev); |
1916 | destroy_workqueue(wq: swdev_wq); |
1917 | kfree(objp: swdev); |
1918 | } |
1919 | |