net: sparx5: add list for mdb entries in driver
authorCasper Andersson <casper.casan@gmail.com>
Thu, 25 Aug 2022 09:28:36 +0000 (11:28 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 29 Aug 2022 11:57:38 +0000 (12:57 +0100)
Keep track of all mdb entries in software for easy access.

Signed-off-by: Casper Andersson <casper.casan@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/sparx5/sparx5_main.c
drivers/net/ethernet/microchip/sparx5/sparx5_main.h
drivers/net/ethernet/microchip/sparx5/sparx5_switchdev.c

index 01be7bd..ad598cf 100644 (file)
@@ -661,6 +661,9 @@ static int sparx5_start(struct sparx5 *sparx5)
        queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
                           SPX5_MACT_PULL_DELAY);
 
+       mutex_init(&sparx5->mdb_lock);
+       INIT_LIST_HEAD(&sparx5->mdb_entries);
+
        err = sparx5_register_netdevs(sparx5);
        if (err)
                return err;
index b197129..3d9e358 100644 (file)
@@ -215,6 +215,15 @@ struct sparx5_skb_cb {
        unsigned long jiffies;
 };
 
+struct sparx5_mdb_entry {
+       struct list_head list;
+       DECLARE_BITMAP(port_mask, SPX5_PORTS);
+       unsigned char addr[ETH_ALEN];
+       bool cpu_copy;
+       u16 vid;
+       u16 pgid_idx;
+};
+
 #define SPARX5_PTP_TIMEOUT             msecs_to_jiffies(10)
 #define SPARX5_SKB_CB(skb) \
        ((struct sparx5_skb_cb *)((skb)->cb))
@@ -256,6 +265,10 @@ struct sparx5 {
        struct list_head mact_entries;
        /* mac table list (mact_entries) mutex */
        struct mutex mact_lock;
+       /* SW MDB table */
+       struct list_head mdb_entries;
+       /* mdb list mutex */
+       struct mutex mdb_lock;
        struct delayed_work mact_work;
        struct workqueue_struct *mact_queue;
        /* Board specifics */
index ec07f7d..8ac71de 100644 (file)
@@ -386,16 +386,95 @@ static int sparx5_handle_port_vlan_add(struct net_device *dev,
                                  v->flags & BRIDGE_VLAN_INFO_UNTAGGED);
 }
 
+static int sparx5_alloc_mdb_entry(struct sparx5 *sparx5,
+                                 const unsigned char *addr,
+                                 u16 vid,
+                                 struct sparx5_mdb_entry **entry_out)
+{
+       struct sparx5_mdb_entry *entry;
+       u16 pgid_idx;
+       int err;
+
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry)
+               return -ENOMEM;
+
+       err = sparx5_pgid_alloc_mcast(sparx5, &pgid_idx);
+       if (err) {
+               kfree(entry);
+               return err;
+       }
+
+       memcpy(entry->addr, addr, ETH_ALEN);
+       entry->vid = vid;
+       entry->pgid_idx = pgid_idx;
+
+       mutex_lock(&sparx5->mdb_lock);
+       list_add_tail(&entry->list, &sparx5->mdb_entries);
+       mutex_unlock(&sparx5->mdb_lock);
+
+       *entry_out = entry;
+       return 0;
+}
+
+static void sparx5_free_mdb_entry(struct sparx5 *sparx5,
+                                 const unsigned char *addr,
+                                 u16 vid)
+{
+       struct sparx5_mdb_entry *entry, *tmp;
+
+       mutex_lock(&sparx5->mdb_lock);
+       list_for_each_entry_safe(entry, tmp, &sparx5->mdb_entries, list) {
+               if ((vid == 0 || entry->vid == vid) &&
+                   ether_addr_equal(addr, entry->addr)) {
+                       list_del(&entry->list);
+
+                       sparx5_pgid_free(sparx5, entry->pgid_idx);
+                       kfree(entry);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&sparx5->mdb_lock);
+}
+
+static struct sparx5_mdb_entry *sparx5_mdb_get_entry(struct sparx5 *sparx5,
+                                                    const unsigned char *addr,
+                                                    u16 vid)
+{
+       struct sparx5_mdb_entry *e, *found = NULL;
+
+       mutex_lock(&sparx5->mdb_lock);
+       list_for_each_entry(e, &sparx5->mdb_entries, list) {
+               if (ether_addr_equal(e->addr, addr) && e->vid == vid) {
+                       found = e;
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&sparx5->mdb_lock);
+       return found;
+}
+
+static void sparx5_cpu_copy_ena(struct sparx5 *spx5, u16 pgid, bool enable)
+{
+       spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(enable),
+                ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,
+                ANA_AC_PGID_MISC_CFG(pgid));
+}
+
 static int sparx5_handle_port_mdb_add(struct net_device *dev,
                                      struct notifier_block *nb,
                                      const struct switchdev_obj_port_mdb *v)
 {
        struct sparx5_port *port = netdev_priv(dev);
        struct sparx5 *spx5 = port->sparx5;
-       u16 pgid_idx, vid;
-       u32 mact_entry;
+       struct sparx5_mdb_entry *entry;
        bool is_host;
-       int res, err;
+       int err;
+       u16 vid;
 
        if (!sparx5_netdevice_check(dev))
                return -EOPNOTSUPP;
@@ -410,66 +489,25 @@ static int sparx5_handle_port_mdb_add(struct net_device *dev,
        else
                vid = v->vid;
 
-       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
-
-       if (res == 0) {
-               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
-
-               /* MC_IDX starts after the port masks in the PGID table */
-               pgid_idx += SPX5_PORTS;
-
-               if (is_host)
-                       spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
-                                ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,
-                                ANA_AC_PGID_MISC_CFG(pgid_idx));
-               else
-                       sparx5_pgid_update_mask(port, pgid_idx, true);
-
-       } else {
-               err = sparx5_pgid_alloc_mcast(spx5, &pgid_idx);
-               if (err) {
-                       netdev_warn(dev, "multicast pgid table full\n");
+       entry = sparx5_mdb_get_entry(spx5, v->addr, vid);
+       if (!entry) {
+               err = sparx5_alloc_mdb_entry(spx5, v->addr, vid, &entry);
+               if (err)
                        return err;
-               }
-
-               if (is_host)
-                       spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(1),
-                                ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,
-                                ANA_AC_PGID_MISC_CFG(pgid_idx));
-               else
-                       sparx5_pgid_update_mask(port, pgid_idx, true);
-
-               err = sparx5_mact_learn(spx5, pgid_idx, v->addr, vid);
-
-               if (err) {
-                       netdev_warn(dev, "could not learn mac address %pM\n", v->addr);
-                       sparx5_pgid_free(spx5, pgid_idx);
-                       sparx5_pgid_update_mask(port, pgid_idx, false);
-                       return err;
-               }
        }
 
-       return 0;
-}
+       mutex_lock(&spx5->mdb_lock);
+       if (is_host && !entry->cpu_copy) {
+               sparx5_cpu_copy_ena(spx5, entry->pgid_idx, true);
+               entry->cpu_copy = true;
+       } else if (!is_host) {
+               sparx5_pgid_update_mask(port, entry->pgid_idx, true);
+               set_bit(port->portno, entry->port_mask);
+       }
+       mutex_unlock(&spx5->mdb_lock);
 
-static int sparx5_mdb_del_entry(struct net_device *dev,
-                               struct sparx5 *spx5,
-                               const unsigned char mac[ETH_ALEN],
-                               const u16 vid,
-                               u16 pgid_idx)
-{
-       int err;
+       sparx5_mact_learn(spx5, entry->pgid_idx, entry->addr, entry->vid);
 
-       err = sparx5_mact_forget(spx5, mac, vid);
-       if (err) {
-               netdev_warn(dev, "could not forget mac address %pM", mac);
-               return err;
-       }
-       err = sparx5_pgid_free(spx5, pgid_idx);
-       if (err) {
-               netdev_err(dev, "attempted to free already freed pgid\n");
-               return err;
-       }
        return 0;
 }
 
@@ -479,42 +517,38 @@ static int sparx5_handle_port_mdb_del(struct net_device *dev,
 {
        struct sparx5_port *port = netdev_priv(dev);
        struct sparx5 *spx5 = port->sparx5;
-       u16 pgid_idx, vid;
-       u32 mact_entry, res, pgid_entry[3], misc_cfg;
-       bool host_ena;
+       struct sparx5_mdb_entry *entry;
+       bool is_host;
+       u16 vid;
 
        if (!sparx5_netdevice_check(dev))
                return -EOPNOTSUPP;
 
+       is_host = netif_is_bridge_master(v->obj.orig_dev);
+
        if (!br_vlan_enabled(spx5->hw_bridge_dev))
                vid = 1;
        else
                vid = v->vid;
 
-       res = sparx5_mact_find(spx5, v->addr, vid, &mact_entry);
-
-       if (res == 0) {
-               pgid_idx = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(mact_entry);
-
-               /* MC_IDX starts after the port masks in the PGID table */
-               pgid_idx += SPX5_PORTS;
-
-               if (netif_is_bridge_master(v->obj.orig_dev))
-                       spx5_rmw(ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_SET(0),
-                                ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA, spx5,
-                                ANA_AC_PGID_MISC_CFG(pgid_idx));
-               else
-                       sparx5_pgid_update_mask(port, pgid_idx, false);
-
-               misc_cfg = spx5_rd(spx5, ANA_AC_PGID_MISC_CFG(pgid_idx));
-               host_ena = ANA_AC_PGID_MISC_CFG_PGID_CPU_COPY_ENA_GET(misc_cfg);
+       entry = sparx5_mdb_get_entry(spx5, v->addr, vid);
+       if (!entry)
+               return 0;
 
-               sparx5_pgid_read_mask(spx5, pgid_idx, pgid_entry);
-               if (bitmap_empty((unsigned long *)pgid_entry, SPX5_PORTS) && !host_ena)
-                       /* No ports or CPU are in MC group. Remove entry */
-                       return sparx5_mdb_del_entry(dev, spx5, v->addr, vid, pgid_idx);
+       mutex_lock(&spx5->mdb_lock);
+       if (is_host && entry->cpu_copy) {
+               sparx5_cpu_copy_ena(spx5, entry->pgid_idx, false);
+               entry->cpu_copy = false;
+       } else if (!is_host) {
+               clear_bit(port->portno, entry->port_mask);
+               sparx5_pgid_update_mask(port, entry->pgid_idx, false);
        }
+       mutex_unlock(&spx5->mdb_lock);
 
+       if (bitmap_empty(entry->port_mask, SPX5_PORTS) && !entry->cpu_copy) {
+               sparx5_mact_forget(spx5, entry->addr, entry->vid);
+               sparx5_free_mdb_entry(spx5, entry->addr, entry->vid);
+       }
        return 0;
 }