Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 
0003 #include <net/switchdev.h>
0004 
0005 #include "lan966x_main.h"
0006 
0007 struct lan966x_pgid_entry {
0008     struct list_head list;
0009     int index;
0010     refcount_t refcount;
0011     u16 ports;
0012 };
0013 
0014 struct lan966x_mdb_entry {
0015     struct list_head list;
0016     unsigned char mac[ETH_ALEN];
0017     u16 vid;
0018     u16 ports;
0019     struct lan966x_pgid_entry *pgid;
0020     u8 cpu_copy;
0021 };
0022 
0023 void lan966x_mdb_init(struct lan966x *lan966x)
0024 {
0025     INIT_LIST_HEAD(&lan966x->mdb_entries);
0026     INIT_LIST_HEAD(&lan966x->pgid_entries);
0027 }
0028 
0029 static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
0030 {
0031     struct lan966x_mdb_entry *mdb_entry, *tmp;
0032 
0033     list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
0034         list_del(&mdb_entry->list);
0035         kfree(mdb_entry);
0036     }
0037 }
0038 
0039 static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
0040 {
0041     struct lan966x_pgid_entry *pgid_entry, *tmp;
0042 
0043     list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
0044         list_del(&pgid_entry->list);
0045         kfree(pgid_entry);
0046     }
0047 }
0048 
0049 void lan966x_mdb_deinit(struct lan966x *lan966x)
0050 {
0051     lan966x_mdb_purge_mdb_entries(lan966x);
0052     lan966x_mdb_purge_pgid_entries(lan966x);
0053 }
0054 
0055 static struct lan966x_mdb_entry *
0056 lan966x_mdb_entry_get(struct lan966x *lan966x,
0057               const unsigned char *mac,
0058               u16 vid)
0059 {
0060     struct lan966x_mdb_entry *mdb_entry;
0061 
0062     list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0063         if (ether_addr_equal(mdb_entry->mac, mac) &&
0064             mdb_entry->vid == vid)
0065             return mdb_entry;
0066     }
0067 
0068     return NULL;
0069 }
0070 
0071 static struct lan966x_mdb_entry *
0072 lan966x_mdb_entry_add(struct lan966x *lan966x,
0073               const struct switchdev_obj_port_mdb *mdb)
0074 {
0075     struct lan966x_mdb_entry *mdb_entry;
0076 
0077     mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
0078     if (!mdb_entry)
0079         return ERR_PTR(-ENOMEM);
0080 
0081     ether_addr_copy(mdb_entry->mac, mdb->addr);
0082     mdb_entry->vid = mdb->vid;
0083 
0084     list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
0085 
0086     return mdb_entry;
0087 }
0088 
0089 static void lan966x_mdb_encode_mac(unsigned char *mac,
0090                    struct lan966x_mdb_entry *mdb_entry,
0091                    enum macaccess_entry_type type)
0092 {
0093     ether_addr_copy(mac, mdb_entry->mac);
0094 
0095     if (type == ENTRYTYPE_MACV4) {
0096         mac[0] = 0;
0097         mac[1] = mdb_entry->ports >> 8;
0098         mac[2] = mdb_entry->ports & 0xff;
0099     } else if (type == ENTRYTYPE_MACV6) {
0100         mac[0] = mdb_entry->ports >> 8;
0101         mac[1] = mdb_entry->ports & 0xff;
0102     }
0103 }
0104 
0105 static int lan966x_mdb_ip_add(struct lan966x_port *port,
0106                   const struct switchdev_obj_port_mdb *mdb,
0107                   enum macaccess_entry_type type)
0108 {
0109     bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0110     struct lan966x *lan966x = port->lan966x;
0111     struct lan966x_mdb_entry *mdb_entry;
0112     unsigned char mac[ETH_ALEN];
0113     bool cpu_copy = false;
0114 
0115     mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0116     if (!mdb_entry) {
0117         mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
0118         if (IS_ERR(mdb_entry))
0119             return PTR_ERR(mdb_entry);
0120     } else {
0121         lan966x_mdb_encode_mac(mac, mdb_entry, type);
0122         lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0123     }
0124 
0125     if (cpu_port)
0126         mdb_entry->cpu_copy++;
0127     else
0128         mdb_entry->ports |= BIT(port->chip_port);
0129 
0130     /* Copy the frame to CPU only if the CPU is in the VLAN */
0131     if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
0132         mdb_entry->cpu_copy)
0133         cpu_copy = true;
0134 
0135     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0136     return lan966x_mac_ip_learn(lan966x, cpu_copy,
0137                     mac, mdb_entry->vid, type);
0138 }
0139 
0140 static int lan966x_mdb_ip_del(struct lan966x_port *port,
0141                   const struct switchdev_obj_port_mdb *mdb,
0142                   enum macaccess_entry_type type)
0143 {
0144     bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0145     struct lan966x *lan966x = port->lan966x;
0146     struct lan966x_mdb_entry *mdb_entry;
0147     unsigned char mac[ETH_ALEN];
0148     u16 ports;
0149 
0150     mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0151     if (!mdb_entry)
0152         return -ENOENT;
0153 
0154     ports = mdb_entry->ports;
0155     if (cpu_port) {
0156         /* If there are still other references to the CPU port then
0157          * there is no point to delete and add again the same entry
0158          */
0159         mdb_entry->cpu_copy--;
0160         if (mdb_entry->cpu_copy)
0161             return 0;
0162     } else {
0163         ports &= ~BIT(port->chip_port);
0164     }
0165 
0166     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0167     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0168 
0169     mdb_entry->ports = ports;
0170 
0171     if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
0172         list_del(&mdb_entry->list);
0173         kfree(mdb_entry);
0174         return 0;
0175     }
0176 
0177     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0178     return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
0179                     mac, mdb_entry->vid, type);
0180 }
0181 
0182 static struct lan966x_pgid_entry *
0183 lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
0184 {
0185     struct lan966x_pgid_entry *pgid_entry;
0186 
0187     pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
0188     if (!pgid_entry)
0189         return ERR_PTR(-ENOMEM);
0190 
0191     pgid_entry->ports = ports;
0192     pgid_entry->index = index;
0193     refcount_set(&pgid_entry->refcount, 1);
0194 
0195     list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
0196 
0197     return pgid_entry;
0198 }
0199 
0200 static struct lan966x_pgid_entry *
0201 lan966x_pgid_entry_get(struct lan966x *lan966x,
0202                struct lan966x_mdb_entry *mdb_entry)
0203 {
0204     struct lan966x_pgid_entry *pgid_entry;
0205     int index;
0206 
0207     /* Try to find an existing pgid that uses the same ports as the
0208      * mdb_entry
0209      */
0210     list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
0211         if (pgid_entry->ports == mdb_entry->ports) {
0212             refcount_inc(&pgid_entry->refcount);
0213             return pgid_entry;
0214         }
0215     }
0216 
0217     /* Try to find an empty pgid entry and allocate one in case it finds it,
0218      * otherwise it means that there are no more resources
0219      */
0220     for (index = PGID_GP_START; index < PGID_GP_END; index++) {
0221         bool used = false;
0222 
0223         list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
0224             if (pgid_entry->index == index) {
0225                 used = true;
0226                 break;
0227             }
0228         }
0229 
0230         if (!used)
0231             return lan966x_pgid_entry_add(lan966x, index,
0232                               mdb_entry->ports);
0233     }
0234 
0235     return ERR_PTR(-ENOSPC);
0236 }
0237 
0238 static void lan966x_pgid_entry_del(struct lan966x *lan966x,
0239                    struct lan966x_pgid_entry *pgid_entry)
0240 {
0241     if (!refcount_dec_and_test(&pgid_entry->refcount))
0242         return;
0243 
0244     list_del(&pgid_entry->list);
0245     kfree(pgid_entry);
0246 }
0247 
0248 static int lan966x_mdb_l2_add(struct lan966x_port *port,
0249                   const struct switchdev_obj_port_mdb *mdb,
0250                   enum macaccess_entry_type type)
0251 {
0252     bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0253     struct lan966x *lan966x = port->lan966x;
0254     struct lan966x_pgid_entry *pgid_entry;
0255     struct lan966x_mdb_entry *mdb_entry;
0256     unsigned char mac[ETH_ALEN];
0257 
0258     mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0259     if (!mdb_entry) {
0260         mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
0261         if (IS_ERR(mdb_entry))
0262             return PTR_ERR(mdb_entry);
0263     } else {
0264         lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0265         lan966x_mdb_encode_mac(mac, mdb_entry, type);
0266         lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0267     }
0268 
0269     if (cpu_port) {
0270         mdb_entry->ports |= BIT(CPU_PORT);
0271         mdb_entry->cpu_copy++;
0272     } else {
0273         mdb_entry->ports |= BIT(port->chip_port);
0274     }
0275 
0276     pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0277     if (IS_ERR(pgid_entry)) {
0278         list_del(&mdb_entry->list);
0279         kfree(mdb_entry);
0280         return PTR_ERR(pgid_entry);
0281     }
0282     mdb_entry->pgid = pgid_entry;
0283 
0284     /* Copy the frame to CPU only if the CPU is in the VLAN */
0285     if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
0286         mdb_entry->cpu_copy)
0287         mdb_entry->ports &= BIT(CPU_PORT);
0288 
0289     lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0290         ANA_PGID_PGID,
0291         lan966x, ANA_PGID(pgid_entry->index));
0292 
0293     return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0294                  mdb_entry->vid, type);
0295 }
0296 
0297 static int lan966x_mdb_l2_del(struct lan966x_port *port,
0298                   const struct switchdev_obj_port_mdb *mdb,
0299                   enum macaccess_entry_type type)
0300 {
0301     bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
0302     struct lan966x *lan966x = port->lan966x;
0303     struct lan966x_pgid_entry *pgid_entry;
0304     struct lan966x_mdb_entry *mdb_entry;
0305     unsigned char mac[ETH_ALEN];
0306     u16 ports;
0307 
0308     mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
0309     if (!mdb_entry)
0310         return -ENOENT;
0311 
0312     ports = mdb_entry->ports;
0313     if (cpu_port) {
0314         /* If there are still other references to the CPU port then
0315          * there is no point to delete and add again the same entry
0316          */
0317         mdb_entry->cpu_copy--;
0318         if (mdb_entry->cpu_copy)
0319             return 0;
0320 
0321         ports &= ~BIT(CPU_PORT);
0322     } else {
0323         ports &= ~BIT(port->chip_port);
0324     }
0325 
0326     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0327     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0328     lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0329 
0330     mdb_entry->ports = ports;
0331 
0332     if (!mdb_entry->ports) {
0333         list_del(&mdb_entry->list);
0334         kfree(mdb_entry);
0335         return 0;
0336     }
0337 
0338     pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0339     if (IS_ERR(pgid_entry)) {
0340         list_del(&mdb_entry->list);
0341         kfree(mdb_entry);
0342         return PTR_ERR(pgid_entry);
0343     }
0344     mdb_entry->pgid = pgid_entry;
0345 
0346     lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0347         ANA_PGID_PGID,
0348         lan966x, ANA_PGID(pgid_entry->index));
0349 
0350     return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0351                  mdb_entry->vid, type);
0352 }
0353 
0354 static enum macaccess_entry_type
0355 lan966x_mdb_classify(const unsigned char *mac)
0356 {
0357     if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
0358         return ENTRYTYPE_MACV4;
0359     if (mac[0] == 0x33 && mac[1] == 0x33)
0360         return ENTRYTYPE_MACV6;
0361     return ENTRYTYPE_LOCKED;
0362 }
0363 
0364 int lan966x_handle_port_mdb_add(struct lan966x_port *port,
0365                 const struct switchdev_obj *obj)
0366 {
0367     const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
0368     enum macaccess_entry_type type;
0369 
0370     /* Split the way the entries are added for ipv4/ipv6 and for l2. The
0371      * reason is that for ipv4/ipv6 it doesn't require to use any pgid
0372      * entry, while for l2 is required to use pgid entries
0373      */
0374     type = lan966x_mdb_classify(mdb->addr);
0375     if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0376         return lan966x_mdb_ip_add(port, mdb, type);
0377 
0378     return lan966x_mdb_l2_add(port, mdb, type);
0379 }
0380 
0381 int lan966x_handle_port_mdb_del(struct lan966x_port *port,
0382                 const struct switchdev_obj *obj)
0383 {
0384     const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
0385     enum macaccess_entry_type type;
0386 
0387     /* Split the way the entries are removed for ipv4/ipv6 and for l2. The
0388      * reason is that for ipv4/ipv6 it doesn't require to use any pgid
0389      * entry, while for l2 is required to use pgid entries
0390      */
0391     type = lan966x_mdb_classify(mdb->addr);
0392     if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0393         return lan966x_mdb_ip_del(port, mdb, type);
0394 
0395     return lan966x_mdb_l2_del(port, mdb, type);
0396 }
0397 
0398 static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
0399                     struct lan966x_mdb_entry *mdb_entry,
0400                     enum macaccess_entry_type type)
0401 {
0402     unsigned char mac[ETH_ALEN];
0403 
0404     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0405     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0406     lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
0407 }
0408 
0409 static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
0410                     struct lan966x_mdb_entry *mdb_entry,
0411                     enum macaccess_entry_type type)
0412 {
0413     struct lan966x_pgid_entry *pgid_entry;
0414     unsigned char mac[ETH_ALEN];
0415 
0416     lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0417     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0418     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0419 
0420     mdb_entry->ports |= BIT(CPU_PORT);
0421 
0422     pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0423     if (IS_ERR(pgid_entry))
0424         return;
0425 
0426     mdb_entry->pgid = pgid_entry;
0427 
0428     lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0429         ANA_PGID_PGID,
0430         lan966x, ANA_PGID(pgid_entry->index));
0431 
0432     lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0433               mdb_entry->vid, type);
0434 }
0435 
0436 void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
0437 {
0438     struct lan966x_mdb_entry *mdb_entry;
0439     enum macaccess_entry_type type;
0440 
0441     list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0442         if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
0443             continue;
0444 
0445         type = lan966x_mdb_classify(mdb_entry->mac);
0446         if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0447             lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
0448         else
0449             lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
0450     }
0451 }
0452 
0453 static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
0454                       struct lan966x_mdb_entry *mdb_entry,
0455                       enum macaccess_entry_type type)
0456 {
0457     unsigned char mac[ETH_ALEN];
0458 
0459     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0460     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0461     lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
0462 }
0463 
0464 static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
0465                       struct lan966x_mdb_entry *mdb_entry,
0466                       enum macaccess_entry_type type)
0467 {
0468     struct lan966x_pgid_entry *pgid_entry;
0469     unsigned char mac[ETH_ALEN];
0470 
0471     lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
0472     lan966x_mdb_encode_mac(mac, mdb_entry, type);
0473     lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0474 
0475     mdb_entry->ports &= ~BIT(CPU_PORT);
0476 
0477     pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
0478     if (IS_ERR(pgid_entry))
0479         return;
0480 
0481     mdb_entry->pgid = pgid_entry;
0482 
0483     lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
0484         ANA_PGID_PGID,
0485         lan966x, ANA_PGID(pgid_entry->index));
0486 
0487     lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
0488               mdb_entry->vid, type);
0489 }
0490 
0491 void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
0492 {
0493     struct lan966x_mdb_entry *mdb_entry;
0494     enum macaccess_entry_type type;
0495 
0496     list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0497         if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
0498             continue;
0499 
0500         type = lan966x_mdb_classify(mdb_entry->mac);
0501         if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
0502             lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
0503         else
0504             lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
0505     }
0506 }
0507 
0508 void lan966x_mdb_clear_entries(struct lan966x *lan966x)
0509 {
0510     struct lan966x_mdb_entry *mdb_entry;
0511     enum macaccess_entry_type type;
0512     unsigned char mac[ETH_ALEN];
0513 
0514     list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0515         type = lan966x_mdb_classify(mdb_entry->mac);
0516 
0517         lan966x_mdb_encode_mac(mac, mdb_entry, type);
0518         /* Remove just the MAC entry, still keep the PGID in case of L2
0519          * entries because this can be restored at later point
0520          */
0521         lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
0522     }
0523 }
0524 
0525 void lan966x_mdb_restore_entries(struct lan966x *lan966x)
0526 {
0527     struct lan966x_mdb_entry *mdb_entry;
0528     enum macaccess_entry_type type;
0529     unsigned char mac[ETH_ALEN];
0530     bool cpu_copy = false;
0531 
0532     list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
0533         type = lan966x_mdb_classify(mdb_entry->mac);
0534 
0535         lan966x_mdb_encode_mac(mac, mdb_entry, type);
0536         if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
0537             /* Copy the frame to CPU only if the CPU is in the VLAN */
0538             if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
0539                                   mdb_entry->vid) &&
0540                 mdb_entry->cpu_copy)
0541                 cpu_copy = true;
0542 
0543             lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
0544                          mdb_entry->vid, type);
0545         } else {
0546             lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
0547                       mdb_entry->mac,
0548                       mdb_entry->vid, type);
0549         }
0550     }
0551 }