xref: /openbmc/linux/drivers/net/ethernet/microchip/lan966x/lan966x_fdb.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1811ba277SHoratiu Vultur // SPDX-License-Identifier: GPL-2.0+
2811ba277SHoratiu Vultur 
3811ba277SHoratiu Vultur #include <net/switchdev.h>
4811ba277SHoratiu Vultur 
5811ba277SHoratiu Vultur #include "lan966x_main.h"
6811ba277SHoratiu Vultur 
7811ba277SHoratiu Vultur struct lan966x_fdb_event_work {
8811ba277SHoratiu Vultur 	struct work_struct work;
9811ba277SHoratiu Vultur 	struct switchdev_notifier_fdb_info fdb_info;
10811ba277SHoratiu Vultur 	struct net_device *dev;
119b4ed7d2SHoratiu Vultur 	struct net_device *orig_dev;
12811ba277SHoratiu Vultur 	struct lan966x *lan966x;
13811ba277SHoratiu Vultur 	unsigned long event;
14811ba277SHoratiu Vultur };
15811ba277SHoratiu Vultur 
16811ba277SHoratiu Vultur struct lan966x_fdb_entry {
17811ba277SHoratiu Vultur 	struct list_head list;
18811ba277SHoratiu Vultur 	unsigned char mac[ETH_ALEN] __aligned(2);
19811ba277SHoratiu Vultur 	u16 vid;
20811ba277SHoratiu Vultur 	u32 references;
21811ba277SHoratiu Vultur };
22811ba277SHoratiu Vultur 
23811ba277SHoratiu Vultur static struct lan966x_fdb_entry *
lan966x_fdb_find_entry(struct lan966x * lan966x,struct switchdev_notifier_fdb_info * fdb_info)24811ba277SHoratiu Vultur lan966x_fdb_find_entry(struct lan966x *lan966x,
25811ba277SHoratiu Vultur 		       struct switchdev_notifier_fdb_info *fdb_info)
26811ba277SHoratiu Vultur {
27811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry;
28811ba277SHoratiu Vultur 
29811ba277SHoratiu Vultur 	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
30811ba277SHoratiu Vultur 		if (fdb_entry->vid == fdb_info->vid &&
31811ba277SHoratiu Vultur 		    ether_addr_equal(fdb_entry->mac, fdb_info->addr))
32811ba277SHoratiu Vultur 			return fdb_entry;
33811ba277SHoratiu Vultur 	}
34811ba277SHoratiu Vultur 
35811ba277SHoratiu Vultur 	return NULL;
36811ba277SHoratiu Vultur }
37811ba277SHoratiu Vultur 
lan966x_fdb_add_entry(struct lan966x * lan966x,struct switchdev_notifier_fdb_info * fdb_info)38811ba277SHoratiu Vultur static void lan966x_fdb_add_entry(struct lan966x *lan966x,
39811ba277SHoratiu Vultur 				  struct switchdev_notifier_fdb_info *fdb_info)
40811ba277SHoratiu Vultur {
41811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry;
42811ba277SHoratiu Vultur 
43811ba277SHoratiu Vultur 	fdb_entry = lan966x_fdb_find_entry(lan966x, fdb_info);
44811ba277SHoratiu Vultur 	if (fdb_entry) {
45811ba277SHoratiu Vultur 		fdb_entry->references++;
46811ba277SHoratiu Vultur 		return;
47811ba277SHoratiu Vultur 	}
48811ba277SHoratiu Vultur 
49811ba277SHoratiu Vultur 	fdb_entry = kzalloc(sizeof(*fdb_entry), GFP_KERNEL);
50811ba277SHoratiu Vultur 	if (!fdb_entry)
51811ba277SHoratiu Vultur 		return;
52811ba277SHoratiu Vultur 
53811ba277SHoratiu Vultur 	ether_addr_copy(fdb_entry->mac, fdb_info->addr);
54811ba277SHoratiu Vultur 	fdb_entry->vid = fdb_info->vid;
55811ba277SHoratiu Vultur 	fdb_entry->references = 1;
56811ba277SHoratiu Vultur 	list_add_tail(&fdb_entry->list, &lan966x->fdb_entries);
57811ba277SHoratiu Vultur }
58811ba277SHoratiu Vultur 
lan966x_fdb_del_entry(struct lan966x * lan966x,struct switchdev_notifier_fdb_info * fdb_info)59811ba277SHoratiu Vultur static bool lan966x_fdb_del_entry(struct lan966x *lan966x,
60811ba277SHoratiu Vultur 				  struct switchdev_notifier_fdb_info *fdb_info)
61811ba277SHoratiu Vultur {
62811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry, *tmp;
63811ba277SHoratiu Vultur 
64811ba277SHoratiu Vultur 	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries,
65811ba277SHoratiu Vultur 				 list) {
66811ba277SHoratiu Vultur 		if (fdb_entry->vid == fdb_info->vid &&
67811ba277SHoratiu Vultur 		    ether_addr_equal(fdb_entry->mac, fdb_info->addr)) {
68811ba277SHoratiu Vultur 			fdb_entry->references--;
69811ba277SHoratiu Vultur 			if (!fdb_entry->references) {
70811ba277SHoratiu Vultur 				list_del(&fdb_entry->list);
71811ba277SHoratiu Vultur 				kfree(fdb_entry);
72811ba277SHoratiu Vultur 				return true;
73811ba277SHoratiu Vultur 			}
74811ba277SHoratiu Vultur 			break;
75811ba277SHoratiu Vultur 		}
76811ba277SHoratiu Vultur 	}
77811ba277SHoratiu Vultur 
78811ba277SHoratiu Vultur 	return false;
79811ba277SHoratiu Vultur }
80811ba277SHoratiu Vultur 
lan966x_fdb_write_entries(struct lan966x * lan966x,u16 vid)81811ba277SHoratiu Vultur void lan966x_fdb_write_entries(struct lan966x *lan966x, u16 vid)
82811ba277SHoratiu Vultur {
83811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry;
84811ba277SHoratiu Vultur 
85811ba277SHoratiu Vultur 	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
86811ba277SHoratiu Vultur 		if (fdb_entry->vid != vid)
87811ba277SHoratiu Vultur 			continue;
88811ba277SHoratiu Vultur 
89811ba277SHoratiu Vultur 		lan966x_mac_cpu_learn(lan966x, fdb_entry->mac, fdb_entry->vid);
90811ba277SHoratiu Vultur 	}
91811ba277SHoratiu Vultur }
92811ba277SHoratiu Vultur 
lan966x_fdb_erase_entries(struct lan966x * lan966x,u16 vid)93811ba277SHoratiu Vultur void lan966x_fdb_erase_entries(struct lan966x *lan966x, u16 vid)
94811ba277SHoratiu Vultur {
95811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry;
96811ba277SHoratiu Vultur 
97811ba277SHoratiu Vultur 	list_for_each_entry(fdb_entry, &lan966x->fdb_entries, list) {
98811ba277SHoratiu Vultur 		if (fdb_entry->vid != vid)
99811ba277SHoratiu Vultur 			continue;
100811ba277SHoratiu Vultur 
101811ba277SHoratiu Vultur 		lan966x_mac_cpu_forget(lan966x, fdb_entry->mac, fdb_entry->vid);
102811ba277SHoratiu Vultur 	}
103811ba277SHoratiu Vultur }
104811ba277SHoratiu Vultur 
lan966x_fdb_purge_entries(struct lan966x * lan966x)105811ba277SHoratiu Vultur static void lan966x_fdb_purge_entries(struct lan966x *lan966x)
106811ba277SHoratiu Vultur {
107811ba277SHoratiu Vultur 	struct lan966x_fdb_entry *fdb_entry, *tmp;
108811ba277SHoratiu Vultur 
109811ba277SHoratiu Vultur 	list_for_each_entry_safe(fdb_entry, tmp, &lan966x->fdb_entries, list) {
110811ba277SHoratiu Vultur 		list_del(&fdb_entry->list);
111811ba277SHoratiu Vultur 		kfree(fdb_entry);
112811ba277SHoratiu Vultur 	}
113811ba277SHoratiu Vultur }
114811ba277SHoratiu Vultur 
lan966x_fdb_init(struct lan966x * lan966x)115811ba277SHoratiu Vultur int lan966x_fdb_init(struct lan966x *lan966x)
116811ba277SHoratiu Vultur {
117811ba277SHoratiu Vultur 	INIT_LIST_HEAD(&lan966x->fdb_entries);
118811ba277SHoratiu Vultur 	lan966x->fdb_work = alloc_ordered_workqueue("lan966x_order", 0);
119811ba277SHoratiu Vultur 	if (!lan966x->fdb_work)
120811ba277SHoratiu Vultur 		return -ENOMEM;
121811ba277SHoratiu Vultur 
122811ba277SHoratiu Vultur 	return 0;
123811ba277SHoratiu Vultur }
124811ba277SHoratiu Vultur 
lan966x_fdb_deinit(struct lan966x * lan966x)125811ba277SHoratiu Vultur void lan966x_fdb_deinit(struct lan966x *lan966x)
126811ba277SHoratiu Vultur {
127811ba277SHoratiu Vultur 	destroy_workqueue(lan966x->fdb_work);
128811ba277SHoratiu Vultur 	lan966x_fdb_purge_entries(lan966x);
129811ba277SHoratiu Vultur }
130811ba277SHoratiu Vultur 
lan966x_fdb_flush_workqueue(struct lan966x * lan966x)13186bac7f1SHoratiu Vultur void lan966x_fdb_flush_workqueue(struct lan966x *lan966x)
13286bac7f1SHoratiu Vultur {
13386bac7f1SHoratiu Vultur 	flush_workqueue(lan966x->fdb_work);
13486bac7f1SHoratiu Vultur }
13586bac7f1SHoratiu Vultur 
lan966x_fdb_port_event_work(struct lan966x_fdb_event_work * fdb_work)1369b4ed7d2SHoratiu Vultur static void lan966x_fdb_port_event_work(struct lan966x_fdb_event_work *fdb_work)
137811ba277SHoratiu Vultur {
138811ba277SHoratiu Vultur 	struct switchdev_notifier_fdb_info *fdb_info;
139811ba277SHoratiu Vultur 	struct lan966x_port *port;
140811ba277SHoratiu Vultur 	struct lan966x *lan966x;
141811ba277SHoratiu Vultur 
142811ba277SHoratiu Vultur 	lan966x = fdb_work->lan966x;
1439b4ed7d2SHoratiu Vultur 	port = netdev_priv(fdb_work->orig_dev);
1449b4ed7d2SHoratiu Vultur 	fdb_info = &fdb_work->fdb_info;
145811ba277SHoratiu Vultur 
146811ba277SHoratiu Vultur 	switch (fdb_work->event) {
147811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
148811ba277SHoratiu Vultur 		if (!fdb_info->added_by_user)
149811ba277SHoratiu Vultur 			break;
150811ba277SHoratiu Vultur 		lan966x_mac_add_entry(lan966x, port, fdb_info->addr,
151811ba277SHoratiu Vultur 				      fdb_info->vid);
152811ba277SHoratiu Vultur 		break;
153811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
154811ba277SHoratiu Vultur 		if (!fdb_info->added_by_user)
155811ba277SHoratiu Vultur 			break;
156811ba277SHoratiu Vultur 		lan966x_mac_del_entry(lan966x, fdb_info->addr,
157811ba277SHoratiu Vultur 				      fdb_info->vid);
158811ba277SHoratiu Vultur 		break;
159811ba277SHoratiu Vultur 	}
1609b4ed7d2SHoratiu Vultur }
1619b4ed7d2SHoratiu Vultur 
lan966x_fdb_bridge_event_work(struct lan966x_fdb_event_work * fdb_work)1629b4ed7d2SHoratiu Vultur static void lan966x_fdb_bridge_event_work(struct lan966x_fdb_event_work *fdb_work)
1639b4ed7d2SHoratiu Vultur {
1649b4ed7d2SHoratiu Vultur 	struct switchdev_notifier_fdb_info *fdb_info;
1659b4ed7d2SHoratiu Vultur 	struct lan966x *lan966x;
1669b4ed7d2SHoratiu Vultur 	int ret;
1679b4ed7d2SHoratiu Vultur 
1689b4ed7d2SHoratiu Vultur 	lan966x = fdb_work->lan966x;
1699b4ed7d2SHoratiu Vultur 	fdb_info = &fdb_work->fdb_info;
170811ba277SHoratiu Vultur 
171811ba277SHoratiu Vultur 	/* In case the bridge is called */
172811ba277SHoratiu Vultur 	switch (fdb_work->event) {
173811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
174811ba277SHoratiu Vultur 		/* If there is no front port in this vlan, there is no
175811ba277SHoratiu Vultur 		 * point to copy the frame to CPU because it would be
176811ba277SHoratiu Vultur 		 * just dropped at later point. So add it only if
177811ba277SHoratiu Vultur 		 * there is a port but it is required to store the fdb
178811ba277SHoratiu Vultur 		 * entry for later point when a port actually gets in
179811ba277SHoratiu Vultur 		 * the vlan.
180811ba277SHoratiu Vultur 		 */
181811ba277SHoratiu Vultur 		lan966x_fdb_add_entry(lan966x, fdb_info);
182811ba277SHoratiu Vultur 		if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
183811ba277SHoratiu Vultur 							   fdb_info->vid))
184811ba277SHoratiu Vultur 			break;
185811ba277SHoratiu Vultur 
186811ba277SHoratiu Vultur 		lan966x_mac_cpu_learn(lan966x, fdb_info->addr,
187811ba277SHoratiu Vultur 				      fdb_info->vid);
188811ba277SHoratiu Vultur 		break;
189811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
190811ba277SHoratiu Vultur 		ret = lan966x_fdb_del_entry(lan966x, fdb_info);
191811ba277SHoratiu Vultur 		if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
192811ba277SHoratiu Vultur 							   fdb_info->vid))
193811ba277SHoratiu Vultur 			break;
194811ba277SHoratiu Vultur 
195811ba277SHoratiu Vultur 		if (ret)
196811ba277SHoratiu Vultur 			lan966x_mac_cpu_forget(lan966x, fdb_info->addr,
197811ba277SHoratiu Vultur 					       fdb_info->vid);
198811ba277SHoratiu Vultur 		break;
199811ba277SHoratiu Vultur 	}
200811ba277SHoratiu Vultur }
201811ba277SHoratiu Vultur 
lan966x_fdb_lag_event_work(struct lan966x_fdb_event_work * fdb_work)202*9be99f2dSHoratiu Vultur static void lan966x_fdb_lag_event_work(struct lan966x_fdb_event_work *fdb_work)
203*9be99f2dSHoratiu Vultur {
204*9be99f2dSHoratiu Vultur 	struct switchdev_notifier_fdb_info *fdb_info;
205*9be99f2dSHoratiu Vultur 	struct lan966x_port *port;
206*9be99f2dSHoratiu Vultur 	struct lan966x *lan966x;
207*9be99f2dSHoratiu Vultur 
208*9be99f2dSHoratiu Vultur 	if (!lan966x_lag_first_port(fdb_work->orig_dev, fdb_work->dev))
209*9be99f2dSHoratiu Vultur 		return;
210*9be99f2dSHoratiu Vultur 
211*9be99f2dSHoratiu Vultur 	lan966x = fdb_work->lan966x;
212*9be99f2dSHoratiu Vultur 	port = netdev_priv(fdb_work->dev);
213*9be99f2dSHoratiu Vultur 	fdb_info = &fdb_work->fdb_info;
214*9be99f2dSHoratiu Vultur 
215*9be99f2dSHoratiu Vultur 	switch (fdb_work->event) {
216*9be99f2dSHoratiu Vultur 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
217*9be99f2dSHoratiu Vultur 		if (!fdb_info->added_by_user)
218*9be99f2dSHoratiu Vultur 			break;
219*9be99f2dSHoratiu Vultur 		lan966x_mac_add_entry(lan966x, port, fdb_info->addr,
220*9be99f2dSHoratiu Vultur 				      fdb_info->vid);
221*9be99f2dSHoratiu Vultur 		break;
222*9be99f2dSHoratiu Vultur 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
223*9be99f2dSHoratiu Vultur 		if (!fdb_info->added_by_user)
224*9be99f2dSHoratiu Vultur 			break;
225*9be99f2dSHoratiu Vultur 		lan966x_mac_del_entry(lan966x, fdb_info->addr, fdb_info->vid);
226*9be99f2dSHoratiu Vultur 		break;
227*9be99f2dSHoratiu Vultur 	}
228*9be99f2dSHoratiu Vultur }
229*9be99f2dSHoratiu Vultur 
lan966x_fdb_event_work(struct work_struct * work)2309b4ed7d2SHoratiu Vultur static void lan966x_fdb_event_work(struct work_struct *work)
2319b4ed7d2SHoratiu Vultur {
2329b4ed7d2SHoratiu Vultur 	struct lan966x_fdb_event_work *fdb_work =
2339b4ed7d2SHoratiu Vultur 		container_of(work, struct lan966x_fdb_event_work, work);
2349b4ed7d2SHoratiu Vultur 
2359b4ed7d2SHoratiu Vultur 	if (lan966x_netdevice_check(fdb_work->orig_dev))
2369b4ed7d2SHoratiu Vultur 		lan966x_fdb_port_event_work(fdb_work);
2379b4ed7d2SHoratiu Vultur 	else if (netif_is_bridge_master(fdb_work->orig_dev))
2389b4ed7d2SHoratiu Vultur 		lan966x_fdb_bridge_event_work(fdb_work);
239*9be99f2dSHoratiu Vultur 	else if (netif_is_lag_master(fdb_work->orig_dev))
240*9be99f2dSHoratiu Vultur 		lan966x_fdb_lag_event_work(fdb_work);
2419b4ed7d2SHoratiu Vultur 
242811ba277SHoratiu Vultur 	kfree(fdb_work->fdb_info.addr);
243811ba277SHoratiu Vultur 	kfree(fdb_work);
244811ba277SHoratiu Vultur }
245811ba277SHoratiu Vultur 
lan966x_handle_fdb(struct net_device * dev,struct net_device * orig_dev,unsigned long event,const void * ctx,const struct switchdev_notifier_fdb_info * fdb_info)246811ba277SHoratiu Vultur int lan966x_handle_fdb(struct net_device *dev,
247811ba277SHoratiu Vultur 		       struct net_device *orig_dev,
248811ba277SHoratiu Vultur 		       unsigned long event, const void *ctx,
249811ba277SHoratiu Vultur 		       const struct switchdev_notifier_fdb_info *fdb_info)
250811ba277SHoratiu Vultur {
251811ba277SHoratiu Vultur 	struct lan966x_port *port = netdev_priv(dev);
252811ba277SHoratiu Vultur 	struct lan966x *lan966x = port->lan966x;
253811ba277SHoratiu Vultur 	struct lan966x_fdb_event_work *fdb_work;
254811ba277SHoratiu Vultur 
255811ba277SHoratiu Vultur 	if (ctx && ctx != port)
256811ba277SHoratiu Vultur 		return 0;
257811ba277SHoratiu Vultur 
258811ba277SHoratiu Vultur 	switch (event) {
259811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
260811ba277SHoratiu Vultur 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
261811ba277SHoratiu Vultur 		if (lan966x_netdevice_check(orig_dev) &&
262811ba277SHoratiu Vultur 		    !fdb_info->added_by_user)
263811ba277SHoratiu Vultur 			break;
264811ba277SHoratiu Vultur 
265811ba277SHoratiu Vultur 		fdb_work = kzalloc(sizeof(*fdb_work), GFP_ATOMIC);
266811ba277SHoratiu Vultur 		if (!fdb_work)
267811ba277SHoratiu Vultur 			return -ENOMEM;
268811ba277SHoratiu Vultur 
2699b4ed7d2SHoratiu Vultur 		fdb_work->dev = dev;
2709b4ed7d2SHoratiu Vultur 		fdb_work->orig_dev = orig_dev;
271811ba277SHoratiu Vultur 		fdb_work->lan966x = lan966x;
272811ba277SHoratiu Vultur 		fdb_work->event = event;
273811ba277SHoratiu Vultur 		INIT_WORK(&fdb_work->work, lan966x_fdb_event_work);
274811ba277SHoratiu Vultur 		memcpy(&fdb_work->fdb_info, fdb_info, sizeof(fdb_work->fdb_info));
275811ba277SHoratiu Vultur 		fdb_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
276811ba277SHoratiu Vultur 		if (!fdb_work->fdb_info.addr)
277811ba277SHoratiu Vultur 			goto err_addr_alloc;
278811ba277SHoratiu Vultur 
279811ba277SHoratiu Vultur 		ether_addr_copy((u8 *)fdb_work->fdb_info.addr, fdb_info->addr);
280811ba277SHoratiu Vultur 
281811ba277SHoratiu Vultur 		queue_work(lan966x->fdb_work, &fdb_work->work);
282811ba277SHoratiu Vultur 		break;
283811ba277SHoratiu Vultur 	}
284811ba277SHoratiu Vultur 
285811ba277SHoratiu Vultur 	return 0;
286811ba277SHoratiu Vultur err_addr_alloc:
287811ba277SHoratiu Vultur 	kfree(fdb_work);
288811ba277SHoratiu Vultur 	return -ENOMEM;
289811ba277SHoratiu Vultur }
290