1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <net/switchdev.h>
4
5 #include "lan966x_main.h"
6
7 struct lan966x_pgid_entry {
8 struct list_head list;
9 int index;
10 refcount_t refcount;
11 u16 ports;
12 };
13
14 struct 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
lan966x_mdb_init(struct lan966x * lan966x)23 void lan966x_mdb_init(struct lan966x *lan966x)
24 {
25 INIT_LIST_HEAD(&lan966x->mdb_entries);
26 INIT_LIST_HEAD(&lan966x->pgid_entries);
27 }
28
lan966x_mdb_purge_mdb_entries(struct lan966x * lan966x)29 static 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(&mdb_entry->list);
35 kfree(mdb_entry);
36 }
37 }
38
lan966x_mdb_purge_pgid_entries(struct lan966x * lan966x)39 static 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(&pgid_entry->list);
45 kfree(pgid_entry);
46 }
47 }
48
lan966x_mdb_deinit(struct lan966x * lan966x)49 void lan966x_mdb_deinit(struct lan966x *lan966x)
50 {
51 lan966x_mdb_purge_mdb_entries(lan966x);
52 lan966x_mdb_purge_pgid_entries(lan966x);
53 }
54
55 static struct lan966x_mdb_entry *
lan966x_mdb_entry_get(struct lan966x * lan966x,const unsigned char * mac,u16 vid)56 lan966x_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(mdb_entry->mac, mac) &&
64 mdb_entry->vid == vid)
65 return mdb_entry;
66 }
67
68 return NULL;
69 }
70
71 static struct lan966x_mdb_entry *
lan966x_mdb_entry_add(struct lan966x * lan966x,const struct switchdev_obj_port_mdb * mdb)72 lan966x_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(sizeof(*mdb_entry), GFP_KERNEL);
78 if (!mdb_entry)
79 return ERR_PTR(-ENOMEM);
80
81 ether_addr_copy(mdb_entry->mac, mdb->addr);
82 mdb_entry->vid = mdb->vid;
83
84 list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
85
86 return mdb_entry;
87 }
88
lan966x_mdb_encode_mac(unsigned char * mac,struct lan966x_mdb_entry * mdb_entry,enum macaccess_entry_type type)89 static 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(mac, 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
lan966x_mdb_ip_add(struct lan966x_port * port,const struct switchdev_obj_port_mdb * mdb,enum macaccess_entry_type type)105 static 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(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, mdb->addr, mdb->vid);
116 if (!mdb_entry) {
117 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
118 if (IS_ERR(mdb_entry))
119 return PTR_ERR(mdb_entry);
120 } else {
121 lan966x_mdb_encode_mac(mac, mdb_entry, type);
122 lan966x_mac_forget(lan966x, mac, 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, 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, mdb_entry->vid, type);
138 }
139
lan966x_mdb_ip_del(struct lan966x_port * port,const struct switchdev_obj_port_mdb * mdb,enum macaccess_entry_type type)140 static 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(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, mdb->addr, 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, mdb_entry->vid, type);
168
169 mdb_entry->ports = ports;
170
171 if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
172 list_del(&mdb_entry->list);
173 kfree(mdb_entry);
174 return 0;
175 }
176
177 lan966x_mdb_encode_mac(mac, mdb_entry, type);
178 return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
179 mac, mdb_entry->vid, type);
180 }
181
182 static struct lan966x_pgid_entry *
lan966x_pgid_entry_add(struct lan966x * lan966x,int index,u16 ports)183 lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
184 {
185 struct lan966x_pgid_entry *pgid_entry;
186
187 pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
188 if (!pgid_entry)
189 return ERR_PTR(-ENOMEM);
190
191 pgid_entry->ports = ports;
192 pgid_entry->index = index;
193 refcount_set(&pgid_entry->refcount, 1);
194
195 list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
196
197 return pgid_entry;
198 }
199
200 static struct lan966x_pgid_entry *
lan966x_pgid_entry_get(struct lan966x * lan966x,struct lan966x_mdb_entry * mdb_entry)201 lan966x_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(&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 mdb_entry->ports);
233 }
234
235 return ERR_PTR(-ENOSPC);
236 }
237
lan966x_pgid_entry_del(struct lan966x * lan966x,struct lan966x_pgid_entry * pgid_entry)238 static void lan966x_pgid_entry_del(struct lan966x *lan966x,
239 struct lan966x_pgid_entry *pgid_entry)
240 {
241 if (!refcount_dec_and_test(&pgid_entry->refcount))
242 return;
243
244 list_del(&pgid_entry->list);
245 kfree(pgid_entry);
246 }
247
lan966x_mdb_l2_add(struct lan966x_port * port,const struct switchdev_obj_port_mdb * mdb,enum macaccess_entry_type type)248 static 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(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, mdb->addr, mdb->vid);
259 if (!mdb_entry) {
260 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
261 if (IS_ERR(mdb_entry))
262 return PTR_ERR(mdb_entry);
263 } else {
264 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
265 lan966x_mdb_encode_mac(mac, mdb_entry, type);
266 lan966x_mac_forget(lan966x, mac, 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(pgid_entry)) {
278 list_del(&mdb_entry->list);
279 kfree(mdb_entry);
280 return PTR_ERR(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, 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, pgid_entry->index, mdb_entry->mac,
294 mdb_entry->vid, type);
295 }
296
lan966x_mdb_l2_del(struct lan966x_port * port,const struct switchdev_obj_port_mdb * mdb,enum macaccess_entry_type type)297 static 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(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, mdb->addr, 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, mdb_entry->vid, type);
328 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
329
330 mdb_entry->ports = ports;
331
332 if (!mdb_entry->ports) {
333 list_del(&mdb_entry->list);
334 kfree(mdb_entry);
335 return 0;
336 }
337
338 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
339 if (IS_ERR(pgid_entry)) {
340 list_del(&mdb_entry->list);
341 kfree(mdb_entry);
342 return PTR_ERR(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, pgid_entry->index, mdb_entry->mac,
351 mdb_entry->vid, type);
352 }
353
354 static enum macaccess_entry_type
lan966x_mdb_classify(const unsigned char * mac)355 lan966x_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
lan966x_handle_port_mdb_add(struct lan966x_port * port,const struct switchdev_obj * obj)364 int 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(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
lan966x_handle_port_mdb_del(struct lan966x_port * port,const struct switchdev_obj * obj)381 int 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(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
lan966x_mdb_ip_cpu_copy(struct lan966x * lan966x,struct lan966x_mdb_entry * mdb_entry,enum macaccess_entry_type type)398 static 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, mdb_entry->vid, type);
406 lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
407 }
408
lan966x_mdb_l2_cpu_copy(struct lan966x * lan966x,struct lan966x_mdb_entry * mdb_entry,enum macaccess_entry_type type)409 static 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, mdb_entry->pgid);
417 lan966x_mdb_encode_mac(mac, mdb_entry, type);
418 lan966x_mac_forget(lan966x, mac, 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(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, pgid_entry->index, mdb_entry->mac,
433 mdb_entry->vid, type);
434 }
435
lan966x_mdb_write_entries(struct lan966x * lan966x,u16 vid)436 void 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(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
lan966x_mdb_ip_cpu_remove(struct lan966x * lan966x,struct lan966x_mdb_entry * mdb_entry,enum macaccess_entry_type type)453 static 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, mdb_entry->vid, type);
461 lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
462 }
463
lan966x_mdb_l2_cpu_remove(struct lan966x * lan966x,struct lan966x_mdb_entry * mdb_entry,enum macaccess_entry_type type)464 static 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, mdb_entry->pgid);
472 lan966x_mdb_encode_mac(mac, mdb_entry, type);
473 lan966x_mac_forget(lan966x, mac, 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(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, pgid_entry->index, mdb_entry->mac,
488 mdb_entry->vid, type);
489 }
490
lan966x_mdb_erase_entries(struct lan966x * lan966x,u16 vid)491 void 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(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
lan966x_mdb_clear_entries(struct lan966x * lan966x)508 void 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(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, mdb_entry->vid, type);
522 }
523 }
524
lan966x_mdb_restore_entries(struct lan966x * lan966x)525 void 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(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 mdb_entry->vid) &&
540 mdb_entry->cpu_copy)
541 cpu_copy = true;
542
543 lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
544 mdb_entry->vid, type);
545 } else {
546 lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
547 mdb_entry->mac,
548 mdb_entry->vid, type);
549 }
550 }
551 }
552