1// SPDX-License-Identifier: GPL-2.0+
2
3#include <net/switchdev.h>
4
5#include "lan966x_main.h"
6
7struct lan966x_pgid_entry {
8 struct list_head list;
9 int index;
10 refcount_t refcount;
11 u16 ports;
12};
13
14struct lan966x_mdb_entry {
15 struct list_head list;
16 unsigned char mac[ETH_ALEN];
17 u16 vid;
18 u16 ports;
19 struct lan966x_pgid_entry *pgid;
20 u8 cpu_copy;
21};
22
23void lan966x_mdb_init(struct lan966x *lan966x)
24{
25 INIT_LIST_HEAD(list: &lan966x->mdb_entries);
26 INIT_LIST_HEAD(list: &lan966x->pgid_entries);
27}
28
29static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
30{
31 struct lan966x_mdb_entry *mdb_entry, *tmp;
32
33 list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
34 list_del(entry: &mdb_entry->list);
35 kfree(objp: mdb_entry);
36 }
37}
38
39static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
40{
41 struct lan966x_pgid_entry *pgid_entry, *tmp;
42
43 list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
44 list_del(entry: &pgid_entry->list);
45 kfree(objp: pgid_entry);
46 }
47}
48
49void lan966x_mdb_deinit(struct lan966x *lan966x)
50{
51 lan966x_mdb_purge_mdb_entries(lan966x);
52 lan966x_mdb_purge_pgid_entries(lan966x);
53}
54
55static struct lan966x_mdb_entry *
56lan966x_mdb_entry_get(struct lan966x *lan966x,
57 const unsigned char *mac,
58 u16 vid)
59{
60 struct lan966x_mdb_entry *mdb_entry;
61
62 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
63 if (ether_addr_equal(addr1: mdb_entry->mac, addr2: mac) &&
64 mdb_entry->vid == vid)
65 return mdb_entry;
66 }
67
68 return NULL;
69}
70
71static struct lan966x_mdb_entry *
72lan966x_mdb_entry_add(struct lan966x *lan966x,
73 const struct switchdev_obj_port_mdb *mdb)
74{
75 struct lan966x_mdb_entry *mdb_entry;
76
77 mdb_entry = kzalloc(size: sizeof(*mdb_entry), GFP_KERNEL);
78 if (!mdb_entry)
79 return ERR_PTR(error: -ENOMEM);
80
81 ether_addr_copy(dst: mdb_entry->mac, src: mdb->addr);
82 mdb_entry->vid = mdb->vid;
83
84 list_add_tail(new: &mdb_entry->list, head: &lan966x->mdb_entries);
85
86 return mdb_entry;
87}
88
89static void lan966x_mdb_encode_mac(unsigned char *mac,
90 struct lan966x_mdb_entry *mdb_entry,
91 enum macaccess_entry_type type)
92{
93 ether_addr_copy(dst: mac, src: mdb_entry->mac);
94
95 if (type == ENTRYTYPE_MACV4) {
96 mac[0] = 0;
97 mac[1] = mdb_entry->ports >> 8;
98 mac[2] = mdb_entry->ports & 0xff;
99 } else if (type == ENTRYTYPE_MACV6) {
100 mac[0] = mdb_entry->ports >> 8;
101 mac[1] = mdb_entry->ports & 0xff;
102 }
103}
104
105static int lan966x_mdb_ip_add(struct lan966x_port *port,
106 const struct switchdev_obj_port_mdb *mdb,
107 enum macaccess_entry_type type)
108{
109 bool cpu_port = netif_is_bridge_master(dev: mdb->obj.orig_dev);
110 struct lan966x *lan966x = port->lan966x;
111 struct lan966x_mdb_entry *mdb_entry;
112 unsigned char mac[ETH_ALEN];
113 bool cpu_copy = false;
114
115 mdb_entry = lan966x_mdb_entry_get(lan966x, mac: mdb->addr, vid: mdb->vid);
116 if (!mdb_entry) {
117 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
118 if (IS_ERR(ptr: mdb_entry))
119 return PTR_ERR(ptr: mdb_entry);
120 } else {
121 lan966x_mdb_encode_mac(mac, mdb_entry, type);
122 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
123 }
124
125 if (cpu_port)
126 mdb_entry->cpu_copy++;
127 else
128 mdb_entry->ports |= BIT(port->chip_port);
129
130 /* Copy the frame to CPU only if the CPU is in the VLAN */
131 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid: mdb_entry->vid) &&
132 mdb_entry->cpu_copy)
133 cpu_copy = true;
134
135 lan966x_mdb_encode_mac(mac, mdb_entry, type);
136 return lan966x_mac_ip_learn(lan966x, cpu_copy,
137 mac, vid: mdb_entry->vid, type);
138}
139
140static int lan966x_mdb_ip_del(struct lan966x_port *port,
141 const struct switchdev_obj_port_mdb *mdb,
142 enum macaccess_entry_type type)
143{
144 bool cpu_port = netif_is_bridge_master(dev: mdb->obj.orig_dev);
145 struct lan966x *lan966x = port->lan966x;
146 struct lan966x_mdb_entry *mdb_entry;
147 unsigned char mac[ETH_ALEN];
148 u16 ports;
149
150 mdb_entry = lan966x_mdb_entry_get(lan966x, mac: mdb->addr, vid: mdb->vid);
151 if (!mdb_entry)
152 return -ENOENT;
153
154 ports = mdb_entry->ports;
155 if (cpu_port) {
156 /* If there are still other references to the CPU port then
157 * there is no point to delete and add again the same entry
158 */
159 mdb_entry->cpu_copy--;
160 if (mdb_entry->cpu_copy)
161 return 0;
162 } else {
163 ports &= ~BIT(port->chip_port);
164 }
165
166 lan966x_mdb_encode_mac(mac, mdb_entry, type);
167 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
168
169 mdb_entry->ports = ports;
170
171 if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
172 list_del(entry: &mdb_entry->list);
173 kfree(objp: mdb_entry);
174 return 0;
175 }
176
177 lan966x_mdb_encode_mac(mac, mdb_entry, type);
178 return lan966x_mac_ip_learn(lan966x, cpu_copy: mdb_entry->cpu_copy,
179 mac, vid: mdb_entry->vid, type);
180}
181
182static struct lan966x_pgid_entry *
183lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
184{
185 struct lan966x_pgid_entry *pgid_entry;
186
187 pgid_entry = kzalloc(size: sizeof(*pgid_entry), GFP_KERNEL);
188 if (!pgid_entry)
189 return ERR_PTR(error: -ENOMEM);
190
191 pgid_entry->ports = ports;
192 pgid_entry->index = index;
193 refcount_set(r: &pgid_entry->refcount, n: 1);
194
195 list_add_tail(new: &pgid_entry->list, head: &lan966x->pgid_entries);
196
197 return pgid_entry;
198}
199
200static struct lan966x_pgid_entry *
201lan966x_pgid_entry_get(struct lan966x *lan966x,
202 struct lan966x_mdb_entry *mdb_entry)
203{
204 struct lan966x_pgid_entry *pgid_entry;
205 int index;
206
207 /* Try to find an existing pgid that uses the same ports as the
208 * mdb_entry
209 */
210 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
211 if (pgid_entry->ports == mdb_entry->ports) {
212 refcount_inc(r: &pgid_entry->refcount);
213 return pgid_entry;
214 }
215 }
216
217 /* Try to find an empty pgid entry and allocate one in case it finds it,
218 * otherwise it means that there are no more resources
219 */
220 for (index = PGID_GP_START; index < PGID_GP_END; index++) {
221 bool used = false;
222
223 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
224 if (pgid_entry->index == index) {
225 used = true;
226 break;
227 }
228 }
229
230 if (!used)
231 return lan966x_pgid_entry_add(lan966x, index,
232 ports: mdb_entry->ports);
233 }
234
235 return ERR_PTR(error: -ENOSPC);
236}
237
238static void lan966x_pgid_entry_del(struct lan966x *lan966x,
239 struct lan966x_pgid_entry *pgid_entry)
240{
241 if (!refcount_dec_and_test(r: &pgid_entry->refcount))
242 return;
243
244 list_del(entry: &pgid_entry->list);
245 kfree(objp: pgid_entry);
246}
247
248static int lan966x_mdb_l2_add(struct lan966x_port *port,
249 const struct switchdev_obj_port_mdb *mdb,
250 enum macaccess_entry_type type)
251{
252 bool cpu_port = netif_is_bridge_master(dev: mdb->obj.orig_dev);
253 struct lan966x *lan966x = port->lan966x;
254 struct lan966x_pgid_entry *pgid_entry;
255 struct lan966x_mdb_entry *mdb_entry;
256 unsigned char mac[ETH_ALEN];
257
258 mdb_entry = lan966x_mdb_entry_get(lan966x, mac: mdb->addr, vid: mdb->vid);
259 if (!mdb_entry) {
260 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
261 if (IS_ERR(ptr: mdb_entry))
262 return PTR_ERR(ptr: mdb_entry);
263 } else {
264 lan966x_pgid_entry_del(lan966x, pgid_entry: mdb_entry->pgid);
265 lan966x_mdb_encode_mac(mac, mdb_entry, type);
266 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
267 }
268
269 if (cpu_port) {
270 mdb_entry->ports |= BIT(CPU_PORT);
271 mdb_entry->cpu_copy++;
272 } else {
273 mdb_entry->ports |= BIT(port->chip_port);
274 }
275
276 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
277 if (IS_ERR(ptr: pgid_entry)) {
278 list_del(entry: &mdb_entry->list);
279 kfree(objp: mdb_entry);
280 return PTR_ERR(ptr: pgid_entry);
281 }
282 mdb_entry->pgid = pgid_entry;
283
284 /* Copy the frame to CPU only if the CPU is in the VLAN */
285 if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid: mdb_entry->vid) &&
286 mdb_entry->cpu_copy)
287 mdb_entry->ports &= BIT(CPU_PORT);
288
289 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
290 ANA_PGID_PGID,
291 lan966x, ANA_PGID(pgid_entry->index));
292
293 return lan966x_mac_learn(lan966x, port: pgid_entry->index, mac: mdb_entry->mac,
294 vid: mdb_entry->vid, type);
295}
296
297static int lan966x_mdb_l2_del(struct lan966x_port *port,
298 const struct switchdev_obj_port_mdb *mdb,
299 enum macaccess_entry_type type)
300{
301 bool cpu_port = netif_is_bridge_master(dev: mdb->obj.orig_dev);
302 struct lan966x *lan966x = port->lan966x;
303 struct lan966x_pgid_entry *pgid_entry;
304 struct lan966x_mdb_entry *mdb_entry;
305 unsigned char mac[ETH_ALEN];
306 u16 ports;
307
308 mdb_entry = lan966x_mdb_entry_get(lan966x, mac: mdb->addr, vid: mdb->vid);
309 if (!mdb_entry)
310 return -ENOENT;
311
312 ports = mdb_entry->ports;
313 if (cpu_port) {
314 /* If there are still other references to the CPU port then
315 * there is no point to delete and add again the same entry
316 */
317 mdb_entry->cpu_copy--;
318 if (mdb_entry->cpu_copy)
319 return 0;
320
321 ports &= ~BIT(CPU_PORT);
322 } else {
323 ports &= ~BIT(port->chip_port);
324 }
325
326 lan966x_mdb_encode_mac(mac, mdb_entry, type);
327 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
328 lan966x_pgid_entry_del(lan966x, pgid_entry: mdb_entry->pgid);
329
330 mdb_entry->ports = ports;
331
332 if (!mdb_entry->ports) {
333 list_del(entry: &mdb_entry->list);
334 kfree(objp: mdb_entry);
335 return 0;
336 }
337
338 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
339 if (IS_ERR(ptr: pgid_entry)) {
340 list_del(entry: &mdb_entry->list);
341 kfree(objp: mdb_entry);
342 return PTR_ERR(ptr: pgid_entry);
343 }
344 mdb_entry->pgid = pgid_entry;
345
346 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
347 ANA_PGID_PGID,
348 lan966x, ANA_PGID(pgid_entry->index));
349
350 return lan966x_mac_learn(lan966x, port: pgid_entry->index, mac: mdb_entry->mac,
351 vid: mdb_entry->vid, type);
352}
353
354static enum macaccess_entry_type
355lan966x_mdb_classify(const unsigned char *mac)
356{
357 if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
358 return ENTRYTYPE_MACV4;
359 if (mac[0] == 0x33 && mac[1] == 0x33)
360 return ENTRYTYPE_MACV6;
361 return ENTRYTYPE_LOCKED;
362}
363
364int lan966x_handle_port_mdb_add(struct lan966x_port *port,
365 const struct switchdev_obj *obj)
366{
367 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
368 enum macaccess_entry_type type;
369
370 /* Split the way the entries are added for ipv4/ipv6 and for l2. The
371 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
372 * entry, while for l2 is required to use pgid entries
373 */
374 type = lan966x_mdb_classify(mac: mdb->addr);
375 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
376 return lan966x_mdb_ip_add(port, mdb, type);
377
378 return lan966x_mdb_l2_add(port, mdb, type);
379}
380
381int lan966x_handle_port_mdb_del(struct lan966x_port *port,
382 const struct switchdev_obj *obj)
383{
384 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
385 enum macaccess_entry_type type;
386
387 /* Split the way the entries are removed for ipv4/ipv6 and for l2. The
388 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
389 * entry, while for l2 is required to use pgid entries
390 */
391 type = lan966x_mdb_classify(mac: mdb->addr);
392 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
393 return lan966x_mdb_ip_del(port, mdb, type);
394
395 return lan966x_mdb_l2_del(port, mdb, type);
396}
397
398static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
399 struct lan966x_mdb_entry *mdb_entry,
400 enum macaccess_entry_type type)
401{
402 unsigned char mac[ETH_ALEN];
403
404 lan966x_mdb_encode_mac(mac, mdb_entry, type);
405 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
406 lan966x_mac_ip_learn(lan966x, cpu_copy: true, mac, vid: mdb_entry->vid, type);
407}
408
409static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
410 struct lan966x_mdb_entry *mdb_entry,
411 enum macaccess_entry_type type)
412{
413 struct lan966x_pgid_entry *pgid_entry;
414 unsigned char mac[ETH_ALEN];
415
416 lan966x_pgid_entry_del(lan966x, pgid_entry: mdb_entry->pgid);
417 lan966x_mdb_encode_mac(mac, mdb_entry, type);
418 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
419
420 mdb_entry->ports |= BIT(CPU_PORT);
421
422 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
423 if (IS_ERR(ptr: pgid_entry))
424 return;
425
426 mdb_entry->pgid = pgid_entry;
427
428 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
429 ANA_PGID_PGID,
430 lan966x, ANA_PGID(pgid_entry->index));
431
432 lan966x_mac_learn(lan966x, port: pgid_entry->index, mac: mdb_entry->mac,
433 vid: mdb_entry->vid, type);
434}
435
436void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
437{
438 struct lan966x_mdb_entry *mdb_entry;
439 enum macaccess_entry_type type;
440
441 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
442 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
443 continue;
444
445 type = lan966x_mdb_classify(mac: mdb_entry->mac);
446 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
447 lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
448 else
449 lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
450 }
451}
452
453static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
454 struct lan966x_mdb_entry *mdb_entry,
455 enum macaccess_entry_type type)
456{
457 unsigned char mac[ETH_ALEN];
458
459 lan966x_mdb_encode_mac(mac, mdb_entry, type);
460 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
461 lan966x_mac_ip_learn(lan966x, cpu_copy: false, mac, vid: mdb_entry->vid, type);
462}
463
464static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
465 struct lan966x_mdb_entry *mdb_entry,
466 enum macaccess_entry_type type)
467{
468 struct lan966x_pgid_entry *pgid_entry;
469 unsigned char mac[ETH_ALEN];
470
471 lan966x_pgid_entry_del(lan966x, pgid_entry: mdb_entry->pgid);
472 lan966x_mdb_encode_mac(mac, mdb_entry, type);
473 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
474
475 mdb_entry->ports &= ~BIT(CPU_PORT);
476
477 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
478 if (IS_ERR(ptr: pgid_entry))
479 return;
480
481 mdb_entry->pgid = pgid_entry;
482
483 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
484 ANA_PGID_PGID,
485 lan966x, ANA_PGID(pgid_entry->index));
486
487 lan966x_mac_learn(lan966x, port: pgid_entry->index, mac: mdb_entry->mac,
488 vid: mdb_entry->vid, type);
489}
490
491void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
492{
493 struct lan966x_mdb_entry *mdb_entry;
494 enum macaccess_entry_type type;
495
496 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
497 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
498 continue;
499
500 type = lan966x_mdb_classify(mac: mdb_entry->mac);
501 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
502 lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
503 else
504 lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
505 }
506}
507
508void lan966x_mdb_clear_entries(struct lan966x *lan966x)
509{
510 struct lan966x_mdb_entry *mdb_entry;
511 enum macaccess_entry_type type;
512 unsigned char mac[ETH_ALEN];
513
514 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
515 type = lan966x_mdb_classify(mac: mdb_entry->mac);
516
517 lan966x_mdb_encode_mac(mac, mdb_entry, type);
518 /* Remove just the MAC entry, still keep the PGID in case of L2
519 * entries because this can be restored at later point
520 */
521 lan966x_mac_forget(lan966x, mac, vid: mdb_entry->vid, type);
522 }
523}
524
525void lan966x_mdb_restore_entries(struct lan966x *lan966x)
526{
527 struct lan966x_mdb_entry *mdb_entry;
528 enum macaccess_entry_type type;
529 unsigned char mac[ETH_ALEN];
530 bool cpu_copy = false;
531
532 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
533 type = lan966x_mdb_classify(mac: mdb_entry->mac);
534
535 lan966x_mdb_encode_mac(mac, mdb_entry, type);
536 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
537 /* Copy the frame to CPU only if the CPU is in the VLAN */
538 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
539 vid: mdb_entry->vid) &&
540 mdb_entry->cpu_copy)
541 cpu_copy = true;
542
543 lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
544 vid: mdb_entry->vid, type);
545 } else {
546 lan966x_mac_learn(lan966x, port: mdb_entry->pgid->index,
547 mac: mdb_entry->mac,
548 vid: mdb_entry->vid, type);
549 }
550 }
551}
552

source code of linux/drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c