Commit c8a3ea43 authored by Casper Andersson's avatar Casper Andersson Committed by David S. Miller
Browse files

net: sparx5: add list for mdb entries in driver



Keep track of all mdb entries in software for easy access.

Signed-off-by: default avatarCasper Andersson <casper.casan@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e8013f8e
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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;
+13 −0
Original line number Diff line number Diff line
@@ -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 */
+116 −82
Original line number Diff line number Diff line
@@ -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");
			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);
	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;
	}
	}

	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;
}