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 | |
23 | void 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 | |
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(entry: &mdb_entry->list); |
35 | kfree(objp: mdb_entry); |
36 | } |
37 | } |
38 | |
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(entry: &pgid_entry->list); |
45 | kfree(objp: pgid_entry); |
46 | } |
47 | } |
48 | |
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 * |
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(addr1: mdb_entry->mac, addr2: mac) && |
64 | mdb_entry->vid == vid) |
65 | return mdb_entry; |
66 | } |
67 | |
68 | return NULL; |
69 | } |
70 | |
71 | static struct lan966x_mdb_entry * |
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(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 | |
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(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 | |
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(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 | |
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(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 | |
182 | static struct lan966x_pgid_entry * |
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(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 | |
200 | static struct lan966x_pgid_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(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 | |
238 | static 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 | |
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(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 | |
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(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 | |
354 | static enum macaccess_entry_type |
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 | |
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(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 | |
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(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 | |
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, vid: mdb_entry->vid, type); |
406 | lan966x_mac_ip_learn(lan966x, cpu_copy: true, mac, vid: mdb_entry->vid, type); |
407 | } |
408 | |
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, 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 | |
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(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 | |
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, vid: mdb_entry->vid, type); |
461 | lan966x_mac_ip_learn(lan966x, cpu_copy: false, mac, vid: mdb_entry->vid, type); |
462 | } |
463 | |
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, 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 | |
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(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 | |
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(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 | |
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(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 | |