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