Commit 68bb4665 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'l2-multicast-forwarding-for-ocelot-switch'

Vladimir Oltean says:

====================
L2 multicast forwarding for Ocelot switch

This series enables the mscc_ocelot switch to forward raw L2 (non-IP)
mdb entries as configured by the bridge driver after this patch:

https://patchwork.ozlabs.org/project/netdev/patch/20201028233831.610076-1-vladimir.oltean@nxp.com/
====================

Link: https://lore.kernel.org/r/20201029022738.722794-1-vladimir.oltean@nxp.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 0e761ac0 e5d1f896
Loading
Loading
Loading
Loading
+101 −46
Original line number Diff line number Diff line
@@ -958,52 +958,88 @@ static enum macaccess_entry_type ocelot_classify_mdb(const unsigned char *addr)
		return ENTRYTYPE_MACv4;
	if (addr[0] == 0x33 && addr[1] == 0x33)
		return ENTRYTYPE_MACv6;
	return ENTRYTYPE_NORMAL;
	return ENTRYTYPE_LOCKED;
}

static int ocelot_mdb_get_pgid(struct ocelot *ocelot,
			       enum macaccess_entry_type entry_type)
static struct ocelot_pgid *ocelot_pgid_alloc(struct ocelot *ocelot, int index,
					     unsigned long ports)
{
	int pgid;
	struct ocelot_pgid *pgid;

	pgid = kzalloc(sizeof(*pgid), GFP_KERNEL);
	if (!pgid)
		return ERR_PTR(-ENOMEM);

	pgid->ports = ports;
	pgid->index = index;
	refcount_set(&pgid->refcount, 1);
	list_add_tail(&pgid->list, &ocelot->pgids);

	return pgid;
}

static void ocelot_pgid_free(struct ocelot *ocelot, struct ocelot_pgid *pgid)
{
	if (!refcount_dec_and_test(&pgid->refcount))
		return;

	list_del(&pgid->list);
	kfree(pgid);
}

static struct ocelot_pgid *ocelot_mdb_get_pgid(struct ocelot *ocelot,
					       const struct ocelot_multicast *mc)
{
	struct ocelot_pgid *pgid;
	int index;

	/* According to VSC7514 datasheet 3.9.1.5 IPv4 Multicast Entries and
	 * 3.9.1.6 IPv6 Multicast Entries, "Instead of a lookup in the
	 * destination mask table (PGID), the destination set is programmed as
	 * part of the entry MAC address.", and the DEST_IDX is set to 0.
	 */
	if (entry_type == ENTRYTYPE_MACv4 ||
	    entry_type == ENTRYTYPE_MACv6)
		return 0;
	if (mc->entry_type == ENTRYTYPE_MACv4 ||
	    mc->entry_type == ENTRYTYPE_MACv6)
		return ocelot_pgid_alloc(ocelot, 0, mc->ports);

	for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) {
		struct ocelot_multicast *mc;
	list_for_each_entry(pgid, &ocelot->pgids, list) {
		/* When searching for a nonreserved multicast PGID, ignore the
		 * dummy PGID of zero that we have for MACv4/MACv6 entries
		 */
		if (pgid->index && pgid->ports == mc->ports) {
			refcount_inc(&pgid->refcount);
			return pgid;
		}
	}

	/* Search for a free index in the nonreserved multicast PGID area */
	for_each_nonreserved_multicast_dest_pgid(ocelot, index) {
		bool used = false;

		list_for_each_entry(mc, &ocelot->multicast, list) {
			if (mc->pgid == pgid) {
		list_for_each_entry(pgid, &ocelot->pgids, list) {
			if (pgid->index == index) {
				used = true;
				break;
			}
		}

		if (!used)
			return pgid;
			return ocelot_pgid_alloc(ocelot, index, mc->ports);
	}

	return -1;
	return ERR_PTR(-ENOSPC);
}

static void ocelot_encode_ports_to_mdb(unsigned char *addr,
				       struct ocelot_multicast *mc,
				       enum macaccess_entry_type entry_type)
				       struct ocelot_multicast *mc)
{
	memcpy(addr, mc->addr, ETH_ALEN);
	ether_addr_copy(addr, mc->addr);

	if (entry_type == ENTRYTYPE_MACv4) {
	if (mc->entry_type == ENTRYTYPE_MACv4) {
		addr[0] = 0;
		addr[1] = mc->ports >> 8;
		addr[2] = mc->ports & 0xff;
	} else if (entry_type == ENTRYTYPE_MACv6) {
	} else if (mc->entry_type == ENTRYTYPE_MACv6) {
		addr[0] = mc->ports >> 8;
		addr[1] = mc->ports & 0xff;
	}
@@ -1013,11 +1049,10 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
			const struct switchdev_obj_port_mdb *mdb)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	enum macaccess_entry_type entry_type;
	unsigned char addr[ETH_ALEN];
	struct ocelot_multicast *mc;
	struct ocelot_pgid *pgid;
	u16 vid = mdb->vid;
	bool new = false;

	if (port == ocelot->npi)
		port = ocelot->num_phys_ports;
@@ -1025,40 +1060,48 @@ int ocelot_port_mdb_add(struct ocelot *ocelot, int port,
	if (!vid)
		vid = ocelot_port->pvid;

	entry_type = ocelot_classify_mdb(mdb->addr);

	mc = ocelot_multicast_get(ocelot, mdb->addr, vid);
	if (!mc) {
		int pgid = ocelot_mdb_get_pgid(ocelot, entry_type);

		if (pgid < 0) {
			dev_err(ocelot->dev,
				"No more PGIDs available for mdb %pM vid %d\n",
				mdb->addr, vid);
			return -ENOSPC;
		}

		/* New entry */
		mc = devm_kzalloc(ocelot->dev, sizeof(*mc), GFP_KERNEL);
		if (!mc)
			return -ENOMEM;

		memcpy(mc->addr, mdb->addr, ETH_ALEN);
		mc->entry_type = ocelot_classify_mdb(mdb->addr);
		ether_addr_copy(mc->addr, mdb->addr);
		mc->vid = vid;
		mc->pgid = pgid;

		list_add_tail(&mc->list, &ocelot->multicast);
		new = true;
	}

	if (!new) {
		ocelot_encode_ports_to_mdb(addr, mc, entry_type);
	} else {
		/* Existing entry. Clean up the current port mask from
		 * hardware now, because we'll be modifying it.
		 */
		ocelot_pgid_free(ocelot, mc->pgid);
		ocelot_encode_ports_to_mdb(addr, mc);
		ocelot_mact_forget(ocelot, addr, vid);
	}

	mc->ports |= BIT(port);
	ocelot_encode_ports_to_mdb(addr, mc, entry_type);

	return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
	pgid = ocelot_mdb_get_pgid(ocelot, mc);
	if (IS_ERR(pgid)) {
		dev_err(ocelot->dev,
			"Cannot allocate PGID for mdb %pM vid %d\n",
			mc->addr, mc->vid);
		devm_kfree(ocelot->dev, mc);
		return PTR_ERR(pgid);
	}
	mc->pgid = pgid;

	ocelot_encode_ports_to_mdb(addr, mc);

	if (mc->entry_type != ENTRYTYPE_MACv4 &&
	    mc->entry_type != ENTRYTYPE_MACv6)
		ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
				 pgid->index);

	return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
				 mc->entry_type);
}
EXPORT_SYMBOL(ocelot_port_mdb_add);

@@ -1066,9 +1109,9 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
			const struct switchdev_obj_port_mdb *mdb)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	enum macaccess_entry_type entry_type;
	unsigned char addr[ETH_ALEN];
	struct ocelot_multicast *mc;
	struct ocelot_pgid *pgid;
	u16 vid = mdb->vid;

	if (port == ocelot->npi)
@@ -1081,11 +1124,10 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
	if (!mc)
		return -ENOENT;

	entry_type = ocelot_classify_mdb(mdb->addr);

	ocelot_encode_ports_to_mdb(addr, mc, entry_type);
	ocelot_encode_ports_to_mdb(addr, mc);
	ocelot_mact_forget(ocelot, addr, vid);

	ocelot_pgid_free(ocelot, mc->pgid);
	mc->ports &= ~BIT(port);
	if (!mc->ports) {
		list_del(&mc->list);
@@ -1093,9 +1135,21 @@ int ocelot_port_mdb_del(struct ocelot *ocelot, int port,
		return 0;
	}

	ocelot_encode_ports_to_mdb(addr, mc, entry_type);
	/* We have a PGID with fewer ports now */
	pgid = ocelot_mdb_get_pgid(ocelot, mc);
	if (IS_ERR(pgid))
		return PTR_ERR(pgid);
	mc->pgid = pgid;

	ocelot_encode_ports_to_mdb(addr, mc);

	if (mc->entry_type != ENTRYTYPE_MACv4 &&
	    mc->entry_type != ENTRYTYPE_MACv6)
		ocelot_write_rix(ocelot, pgid->ports, ANA_PGID_PGID,
				 pgid->index);

	return ocelot_mact_learn(ocelot, mc->pgid, addr, vid, entry_type);
	return ocelot_mact_learn(ocelot, pgid->index, addr, vid,
				 mc->entry_type);
}
EXPORT_SYMBOL(ocelot_port_mdb_del);

@@ -1453,6 +1507,7 @@ int ocelot_init(struct ocelot *ocelot)
		return -ENOMEM;

	INIT_LIST_HEAD(&ocelot->multicast);
	INIT_LIST_HEAD(&ocelot->pgids);
	ocelot_mact_init(ocelot);
	ocelot_vlan_init(ocelot);
	ocelot_vcap_init(ocelot);
+23 −8
Original line number Diff line number Diff line
@@ -41,14 +41,6 @@ struct frame_info {
	u32 timestamp;	/* rew_val */
};

struct ocelot_multicast {
	struct list_head list;
	unsigned char addr[ETH_ALEN];
	u16 vid;
	u16 ports;
	int pgid;
};

struct ocelot_port_tc {
	bool block_shared;
	unsigned long offload_cnt;
@@ -87,6 +79,29 @@ enum macaccess_entry_type {
	ENTRYTYPE_MACv6,
};

/* A (PGID) port mask structure, encoding the 2^ocelot->num_phys_ports
 * possibilities of egress port masks for L2 multicast traffic.
 * For a switch with 9 user ports, there are 512 possible port masks, but the
 * hardware only has 46 individual PGIDs that it can forward multicast traffic
 * to. So we need a structure that maps the limited PGID indices to the port
 * destinations requested by the user for L2 multicast.
 */
struct ocelot_pgid {
	unsigned long ports;
	int index;
	refcount_t refcount;
	struct list_head list;
};

struct ocelot_multicast {
	struct list_head list;
	enum macaccess_entry_type entry_type;
	unsigned char addr[ETH_ALEN];
	u16 vid;
	u16 ports;
	struct ocelot_pgid *pgid;
};

int ocelot_port_fdb_do_dump(const unsigned char *addr, u16 vid,
			    bool is_static, void *data);
int ocelot_mact_learn(struct ocelot *ocelot, int port,
+1 −0
Original line number Diff line number Diff line
@@ -632,6 +632,7 @@ struct ocelot {
	u32				*lags;

	struct list_head		multicast;
	struct list_head		pgids;

	struct list_head		dummy_rules;
	struct ocelot_vcap_block	block[3];