]> Git Repo - J-linux.git/blob - drivers/net/ethernet/microchip/lan966x/lan966x_mdb.c
Merge tag 'kbuild-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy...
[J-linux.git] / drivers / net / ethernet / microchip / lan966x / lan966x_mdb.c
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(&lan966x->mdb_entries);
26         INIT_LIST_HEAD(&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(&mdb_entry->list);
35                 kfree(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(&pgid_entry->list);
45                 kfree(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(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 *
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
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
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
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 *
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 *
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
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
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
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
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(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(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, mdb_entry->vid, type);
406         lan966x_mac_ip_learn(lan966x, true, mac, 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, 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
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
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
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
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
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
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 }
This page took 0.061279 seconds and 4 git commands to generate.