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