| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551 |
- // SPDX-License-Identifier: GPL-2.0+
- #include <net/switchdev.h>
- #include "lan966x_main.h"
- struct lan966x_pgid_entry {
- struct list_head list;
- int index;
- refcount_t refcount;
- u16 ports;
- };
- struct lan966x_mdb_entry {
- struct list_head list;
- unsigned char mac[ETH_ALEN];
- u16 vid;
- u16 ports;
- struct lan966x_pgid_entry *pgid;
- u8 cpu_copy;
- };
- void lan966x_mdb_init(struct lan966x *lan966x)
- {
- INIT_LIST_HEAD(&lan966x->mdb_entries);
- INIT_LIST_HEAD(&lan966x->pgid_entries);
- }
- static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
- {
- struct lan966x_mdb_entry *mdb_entry, *tmp;
- list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
- list_del(&mdb_entry->list);
- kfree(mdb_entry);
- }
- }
- static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
- {
- struct lan966x_pgid_entry *pgid_entry, *tmp;
- list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
- list_del(&pgid_entry->list);
- kfree(pgid_entry);
- }
- }
- void lan966x_mdb_deinit(struct lan966x *lan966x)
- {
- lan966x_mdb_purge_mdb_entries(lan966x);
- lan966x_mdb_purge_pgid_entries(lan966x);
- }
- static struct lan966x_mdb_entry *
- lan966x_mdb_entry_get(struct lan966x *lan966x,
- const unsigned char *mac,
- u16 vid)
- {
- struct lan966x_mdb_entry *mdb_entry;
- list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
- if (ether_addr_equal(mdb_entry->mac, mac) &&
- mdb_entry->vid == vid)
- return mdb_entry;
- }
- return NULL;
- }
- static struct lan966x_mdb_entry *
- lan966x_mdb_entry_add(struct lan966x *lan966x,
- const struct switchdev_obj_port_mdb *mdb)
- {
- struct lan966x_mdb_entry *mdb_entry;
- mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
- if (!mdb_entry)
- return ERR_PTR(-ENOMEM);
- ether_addr_copy(mdb_entry->mac, mdb->addr);
- mdb_entry->vid = mdb->vid;
- list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
- return mdb_entry;
- }
- static void lan966x_mdb_encode_mac(unsigned char *mac,
- struct lan966x_mdb_entry *mdb_entry,
- enum macaccess_entry_type type)
- {
- ether_addr_copy(mac, mdb_entry->mac);
- if (type == ENTRYTYPE_MACV4) {
- mac[0] = 0;
- mac[1] = mdb_entry->ports >> 8;
- mac[2] = mdb_entry->ports & 0xff;
- } else if (type == ENTRYTYPE_MACV6) {
- mac[0] = mdb_entry->ports >> 8;
- mac[1] = mdb_entry->ports & 0xff;
- }
- }
- static int lan966x_mdb_ip_add(struct lan966x_port *port,
- const struct switchdev_obj_port_mdb *mdb,
- enum macaccess_entry_type type)
- {
- bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
- struct lan966x *lan966x = port->lan966x;
- struct lan966x_mdb_entry *mdb_entry;
- unsigned char mac[ETH_ALEN];
- bool cpu_copy = false;
- mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
- if (!mdb_entry) {
- mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
- if (IS_ERR(mdb_entry))
- return PTR_ERR(mdb_entry);
- } else {
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- }
- if (cpu_port)
- mdb_entry->cpu_copy++;
- else
- mdb_entry->ports |= BIT(port->chip_port);
- /* Copy the frame to CPU only if the CPU is in the VLAN */
- if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
- mdb_entry->cpu_copy)
- cpu_copy = true;
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- return lan966x_mac_ip_learn(lan966x, cpu_copy,
- mac, mdb_entry->vid, type);
- }
- static int lan966x_mdb_ip_del(struct lan966x_port *port,
- const struct switchdev_obj_port_mdb *mdb,
- enum macaccess_entry_type type)
- {
- bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
- struct lan966x *lan966x = port->lan966x;
- struct lan966x_mdb_entry *mdb_entry;
- unsigned char mac[ETH_ALEN];
- u16 ports;
- mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
- if (!mdb_entry)
- return -ENOENT;
- ports = mdb_entry->ports;
- if (cpu_port) {
- /* If there are still other references to the CPU port then
- * there is no point to delete and add again the same entry
- */
- mdb_entry->cpu_copy--;
- if (mdb_entry->cpu_copy)
- return 0;
- } else {
- ports &= ~BIT(port->chip_port);
- }
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- mdb_entry->ports = ports;
- if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
- list_del(&mdb_entry->list);
- kfree(mdb_entry);
- return 0;
- }
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
- mac, mdb_entry->vid, type);
- }
- static struct lan966x_pgid_entry *
- lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
- {
- struct lan966x_pgid_entry *pgid_entry;
- pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
- if (!pgid_entry)
- return ERR_PTR(-ENOMEM);
- pgid_entry->ports = ports;
- pgid_entry->index = index;
- refcount_set(&pgid_entry->refcount, 1);
- list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
- return pgid_entry;
- }
- static struct lan966x_pgid_entry *
- lan966x_pgid_entry_get(struct lan966x *lan966x,
- struct lan966x_mdb_entry *mdb_entry)
- {
- struct lan966x_pgid_entry *pgid_entry;
- int index;
- /* Try to find an existing pgid that uses the same ports as the
- * mdb_entry
- */
- list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
- if (pgid_entry->ports == mdb_entry->ports) {
- refcount_inc(&pgid_entry->refcount);
- return pgid_entry;
- }
- }
- /* Try to find an empty pgid entry and allocate one in case it finds it,
- * otherwise it means that there are no more resources
- */
- for (index = PGID_GP_START; index < PGID_GP_END; index++) {
- bool used = false;
- list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
- if (pgid_entry->index == index) {
- used = true;
- break;
- }
- }
- if (!used)
- return lan966x_pgid_entry_add(lan966x, index,
- mdb_entry->ports);
- }
- return ERR_PTR(-ENOSPC);
- }
- static void lan966x_pgid_entry_del(struct lan966x *lan966x,
- struct lan966x_pgid_entry *pgid_entry)
- {
- if (!refcount_dec_and_test(&pgid_entry->refcount))
- return;
- list_del(&pgid_entry->list);
- kfree(pgid_entry);
- }
- static int lan966x_mdb_l2_add(struct lan966x_port *port,
- const struct switchdev_obj_port_mdb *mdb,
- enum macaccess_entry_type type)
- {
- bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
- struct lan966x *lan966x = port->lan966x;
- struct lan966x_pgid_entry *pgid_entry;
- struct lan966x_mdb_entry *mdb_entry;
- unsigned char mac[ETH_ALEN];
- mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
- if (!mdb_entry) {
- mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
- if (IS_ERR(mdb_entry))
- return PTR_ERR(mdb_entry);
- } else {
- lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- }
- if (cpu_port) {
- mdb_entry->ports |= BIT(CPU_PORT);
- mdb_entry->cpu_copy++;
- } else {
- mdb_entry->ports |= BIT(port->chip_port);
- }
- pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
- if (IS_ERR(pgid_entry)) {
- list_del(&mdb_entry->list);
- kfree(mdb_entry);
- return PTR_ERR(pgid_entry);
- }
- mdb_entry->pgid = pgid_entry;
- /* Copy the frame to CPU only if the CPU is in the VLAN */
- if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
- mdb_entry->cpu_copy)
- mdb_entry->ports &= BIT(CPU_PORT);
- lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
- ANA_PGID_PGID,
- lan966x, ANA_PGID(pgid_entry->index));
- return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
- mdb_entry->vid, type);
- }
- static int lan966x_mdb_l2_del(struct lan966x_port *port,
- const struct switchdev_obj_port_mdb *mdb,
- enum macaccess_entry_type type)
- {
- bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
- struct lan966x *lan966x = port->lan966x;
- struct lan966x_pgid_entry *pgid_entry;
- struct lan966x_mdb_entry *mdb_entry;
- unsigned char mac[ETH_ALEN];
- u16 ports;
- mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
- if (!mdb_entry)
- return -ENOENT;
- ports = mdb_entry->ports;
- if (cpu_port) {
- /* If there are still other references to the CPU port then
- * there is no point to delete and add again the same entry
- */
- mdb_entry->cpu_copy--;
- if (mdb_entry->cpu_copy)
- return 0;
- ports &= ~BIT(CPU_PORT);
- } else {
- ports &= ~BIT(port->chip_port);
- }
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
- mdb_entry->ports = ports;
- if (!mdb_entry->ports) {
- list_del(&mdb_entry->list);
- kfree(mdb_entry);
- return 0;
- }
- pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
- if (IS_ERR(pgid_entry)) {
- list_del(&mdb_entry->list);
- kfree(mdb_entry);
- return PTR_ERR(pgid_entry);
- }
- mdb_entry->pgid = pgid_entry;
- lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
- ANA_PGID_PGID,
- lan966x, ANA_PGID(pgid_entry->index));
- return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
- mdb_entry->vid, type);
- }
- static enum macaccess_entry_type
- lan966x_mdb_classify(const unsigned char *mac)
- {
- if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
- return ENTRYTYPE_MACV4;
- if (mac[0] == 0x33 && mac[1] == 0x33)
- return ENTRYTYPE_MACV6;
- return ENTRYTYPE_LOCKED;
- }
- int lan966x_handle_port_mdb_add(struct lan966x_port *port,
- const struct switchdev_obj *obj)
- {
- const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
- enum macaccess_entry_type type;
- /* Split the way the entries are added for ipv4/ipv6 and for l2. The
- * reason is that for ipv4/ipv6 it doesn't require to use any pgid
- * entry, while for l2 is required to use pgid entries
- */
- type = lan966x_mdb_classify(mdb->addr);
- if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
- return lan966x_mdb_ip_add(port, mdb, type);
- return lan966x_mdb_l2_add(port, mdb, type);
- }
- int lan966x_handle_port_mdb_del(struct lan966x_port *port,
- const struct switchdev_obj *obj)
- {
- const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
- enum macaccess_entry_type type;
- /* Split the way the entries are removed for ipv4/ipv6 and for l2. The
- * reason is that for ipv4/ipv6 it doesn't require to use any pgid
- * entry, while for l2 is required to use pgid entries
- */
- type = lan966x_mdb_classify(mdb->addr);
- if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
- return lan966x_mdb_ip_del(port, mdb, type);
- return lan966x_mdb_l2_del(port, mdb, type);
- }
- static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
- struct lan966x_mdb_entry *mdb_entry,
- enum macaccess_entry_type type)
- {
- unsigned char mac[ETH_ALEN];
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
- }
- static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
- struct lan966x_mdb_entry *mdb_entry,
- enum macaccess_entry_type type)
- {
- struct lan966x_pgid_entry *pgid_entry;
- unsigned char mac[ETH_ALEN];
- lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- mdb_entry->ports |= BIT(CPU_PORT);
- pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
- if (IS_ERR(pgid_entry))
- return;
- mdb_entry->pgid = pgid_entry;
- lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
- ANA_PGID_PGID,
- lan966x, ANA_PGID(pgid_entry->index));
- lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
- mdb_entry->vid, type);
- }
- void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
- {
- struct lan966x_mdb_entry *mdb_entry;
- enum macaccess_entry_type type;
- list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
- if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
- continue;
- type = lan966x_mdb_classify(mdb_entry->mac);
- if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
- lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
- else
- lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
- }
- }
- static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
- struct lan966x_mdb_entry *mdb_entry,
- enum macaccess_entry_type type)
- {
- unsigned char mac[ETH_ALEN];
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
- }
- static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
- struct lan966x_mdb_entry *mdb_entry,
- enum macaccess_entry_type type)
- {
- struct lan966x_pgid_entry *pgid_entry;
- unsigned char mac[ETH_ALEN];
- lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- mdb_entry->ports &= ~BIT(CPU_PORT);
- pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
- if (IS_ERR(pgid_entry))
- return;
- mdb_entry->pgid = pgid_entry;
- lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
- ANA_PGID_PGID,
- lan966x, ANA_PGID(pgid_entry->index));
- lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
- mdb_entry->vid, type);
- }
- void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
- {
- struct lan966x_mdb_entry *mdb_entry;
- enum macaccess_entry_type type;
- list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
- if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
- continue;
- type = lan966x_mdb_classify(mdb_entry->mac);
- if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
- lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
- else
- lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
- }
- }
- void lan966x_mdb_clear_entries(struct lan966x *lan966x)
- {
- struct lan966x_mdb_entry *mdb_entry;
- enum macaccess_entry_type type;
- unsigned char mac[ETH_ALEN];
- list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
- type = lan966x_mdb_classify(mdb_entry->mac);
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- /* Remove just the MAC entry, still keep the PGID in case of L2
- * entries because this can be restored at later point
- */
- lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
- }
- }
- void lan966x_mdb_restore_entries(struct lan966x *lan966x)
- {
- struct lan966x_mdb_entry *mdb_entry;
- enum macaccess_entry_type type;
- unsigned char mac[ETH_ALEN];
- bool cpu_copy = false;
- list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
- type = lan966x_mdb_classify(mdb_entry->mac);
- lan966x_mdb_encode_mac(mac, mdb_entry, type);
- if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
- /* Copy the frame to CPU only if the CPU is in the VLAN */
- if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
- mdb_entry->vid) &&
- mdb_entry->cpu_copy)
- cpu_copy = true;
- lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
- mdb_entry->vid, type);
- } else {
- lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
- mdb_entry->mac,
- mdb_entry->vid, type);
- }
- }
- }
|