Commit 7d4f4d14 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'mscc-ocelot-all-ports-vlan-untagged-egress'

parents dfcb63ce d4004422
Loading
Loading
Loading
Loading
+195 −77
Original line number Diff line number Diff line
@@ -162,48 +162,117 @@ static int ocelot_vlant_set_mask(struct ocelot *ocelot, u16 vid, u32 mask)
	return ocelot_vlant_wait_for_completion(ocelot);
}

static void ocelot_port_set_native_vlan(struct ocelot *ocelot, int port,
					struct ocelot_vlan native_vlan)
static int ocelot_port_num_untagged_vlans(struct ocelot *ocelot, int port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	u32 val = 0;
	struct ocelot_bridge_vlan *vlan;
	int num_untagged = 0;

	ocelot_port->native_vlan = native_vlan;
	list_for_each_entry(vlan, &ocelot->vlans, list) {
		if (!(vlan->portmask & BIT(port)))
			continue;

	ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(native_vlan.vid),
		       REW_PORT_VLAN_CFG_PORT_VID_M,
		       REW_PORT_VLAN_CFG, port);
		if (vlan->untagged & BIT(port))
			num_untagged++;
	}

	return num_untagged;
}

static int ocelot_port_num_tagged_vlans(struct ocelot *ocelot, int port)
{
	struct ocelot_bridge_vlan *vlan;
	int num_tagged = 0;

	list_for_each_entry(vlan, &ocelot->vlans, list) {
		if (!(vlan->portmask & BIT(port)))
			continue;

		if (!(vlan->untagged & BIT(port)))
			num_tagged++;
	}

	return num_tagged;
}

/* We use native VLAN when we have to mix egress-tagged VLANs with exactly
 * _one_ egress-untagged VLAN (_the_ native VLAN)
 */
static bool ocelot_port_uses_native_vlan(struct ocelot *ocelot, int port)
{
	return ocelot_port_num_tagged_vlans(ocelot, port) &&
	       ocelot_port_num_untagged_vlans(ocelot, port) == 1;
}

static struct ocelot_bridge_vlan *
ocelot_port_find_native_vlan(struct ocelot *ocelot, int port)
{
	struct ocelot_bridge_vlan *vlan;

	list_for_each_entry(vlan, &ocelot->vlans, list)
		if (vlan->portmask & BIT(port) && vlan->untagged & BIT(port))
			return vlan;

	return NULL;
}

/* Keep in sync REW_TAG_CFG_TAG_CFG and, if applicable,
 * REW_PORT_VLAN_CFG_PORT_VID, with the bridge VLAN table and VLAN awareness
 * state of the port.
 */
static void ocelot_port_manage_port_tag(struct ocelot *ocelot, int port)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	enum ocelot_port_tag_config tag_cfg;
	bool uses_native_vlan = false;

	if (ocelot_port->vlan_aware) {
		if (native_vlan.valid)
			/* Tag all frames except when VID == DEFAULT_VLAN */
			val = REW_TAG_CFG_TAG_CFG(1);
		uses_native_vlan = ocelot_port_uses_native_vlan(ocelot, port);

		if (uses_native_vlan)
			tag_cfg = OCELOT_PORT_TAG_NATIVE;
		else if (ocelot_port_num_untagged_vlans(ocelot, port))
			tag_cfg = OCELOT_PORT_TAG_DISABLED;
		else
			/* Tag all frames */
			val = REW_TAG_CFG_TAG_CFG(3);
			tag_cfg = OCELOT_PORT_TAG_TRUNK;
	} else {
		/* Port tagging disabled. */
		val = REW_TAG_CFG_TAG_CFG(0);
		tag_cfg = OCELOT_PORT_TAG_DISABLED;
	}
	ocelot_rmw_gix(ocelot, val,

	ocelot_rmw_gix(ocelot, REW_TAG_CFG_TAG_CFG(tag_cfg),
		       REW_TAG_CFG_TAG_CFG_M,
		       REW_TAG_CFG, port);

	if (uses_native_vlan) {
		struct ocelot_bridge_vlan *native_vlan;

		/* Not having a native VLAN is impossible, because
		 * ocelot_port_num_untagged_vlans has returned 1.
		 * So there is no use in checking for NULL here.
		 */
		native_vlan = ocelot_port_find_native_vlan(ocelot, port);

		ocelot_rmw_gix(ocelot,
			       REW_PORT_VLAN_CFG_PORT_VID(native_vlan->vid),
			       REW_PORT_VLAN_CFG_PORT_VID_M,
			       REW_PORT_VLAN_CFG, port);
	}
}

/* Default vlan to clasify for untagged frames (may be zero) */
static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
				 struct ocelot_vlan pvid_vlan)
				 const struct ocelot_bridge_vlan *pvid_vlan)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	u16 pvid = OCELOT_VLAN_UNAWARE_PVID;
	u32 val = 0;

	ocelot_port->pvid_vlan = pvid_vlan;

	if (!ocelot_port->vlan_aware)
		pvid_vlan.vid = 0;
	if (ocelot_port->vlan_aware && pvid_vlan)
		pvid = pvid_vlan->vid;

	ocelot_rmw_gix(ocelot,
		       ANA_PORT_VLAN_CFG_VLAN_VID(pvid_vlan.vid),
		       ANA_PORT_VLAN_CFG_VLAN_VID(pvid),
		       ANA_PORT_VLAN_CFG_VLAN_VID_M,
		       ANA_PORT_VLAN_CFG, port);

@@ -212,7 +281,7 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
	 * classified to VLAN 0, but that is always in our RX filter, so it
	 * would get accepted were it not for this setting.
	 */
	if (!pvid_vlan.valid && ocelot_port->vlan_aware)
	if (!pvid_vlan && ocelot_port->vlan_aware)
		val = ANA_PORT_DROP_CFG_DROP_PRIO_S_TAGGED_ENA |
		      ANA_PORT_DROP_CFG_DROP_PRIO_C_TAGGED_ENA;

@@ -222,31 +291,90 @@ static void ocelot_port_set_pvid(struct ocelot *ocelot, int port,
		       ANA_PORT_DROP_CFG, port);
}

static int ocelot_vlan_member_set(struct ocelot *ocelot, u32 vlan_mask, u16 vid)
static struct ocelot_bridge_vlan *ocelot_bridge_vlan_find(struct ocelot *ocelot,
							  u16 vid)
{
	struct ocelot_bridge_vlan *vlan;

	list_for_each_entry(vlan, &ocelot->vlans, list)
		if (vlan->vid == vid)
			return vlan;

	return NULL;
}

static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid,
				  bool untagged)
{
	struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
	unsigned long portmask;
	int err;

	err = ocelot_vlant_set_mask(ocelot, vid, vlan_mask);
	if (vlan) {
		portmask = vlan->portmask | BIT(port);

		err = ocelot_vlant_set_mask(ocelot, vid, portmask);
		if (err)
			return err;

	ocelot->vlan_mask[vid] = vlan_mask;
		vlan->portmask = portmask;
		/* Bridge VLANs can be overwritten with a different
		 * egress-tagging setting, so make sure to override an untagged
		 * with a tagged VID if that's going on.
		 */
		if (untagged)
			vlan->untagged |= BIT(port);
		else
			vlan->untagged &= ~BIT(port);

		return 0;
	}

static int ocelot_vlan_member_add(struct ocelot *ocelot, int port, u16 vid)
{
	return ocelot_vlan_member_set(ocelot,
				      ocelot->vlan_mask[vid] | BIT(port),
				      vid);
	vlan = kzalloc(sizeof(*vlan), GFP_KERNEL);
	if (!vlan)
		return -ENOMEM;

	portmask = BIT(port);

	err = ocelot_vlant_set_mask(ocelot, vid, portmask);
	if (err) {
		kfree(vlan);
		return err;
	}

	vlan->vid = vid;
	vlan->portmask = portmask;
	if (untagged)
		vlan->untagged = BIT(port);
	INIT_LIST_HEAD(&vlan->list);
	list_add_tail(&vlan->list, &ocelot->vlans);

	return 0;
}

static int ocelot_vlan_member_del(struct ocelot *ocelot, int port, u16 vid)
{
	return ocelot_vlan_member_set(ocelot,
				      ocelot->vlan_mask[vid] & ~BIT(port),
				      vid);
	struct ocelot_bridge_vlan *vlan = ocelot_bridge_vlan_find(ocelot, vid);
	unsigned long portmask;
	int err;

	if (!vlan)
		return 0;

	portmask = vlan->portmask & ~BIT(port);

	err = ocelot_vlant_set_mask(ocelot, vid, portmask);
	if (err)
		return err;

	vlan->portmask = portmask;
	if (vlan->portmask)
		return 0;

	list_del(&vlan->list);
	kfree(vlan);

	return 0;
}

int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
@@ -279,7 +407,7 @@ int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port,
		       ANA_PORT_VLAN_CFG, port);

	ocelot_port_set_pvid(ocelot, port, ocelot_port->pvid_vlan);
	ocelot_port_set_native_vlan(ocelot, port, ocelot_port->native_vlan);
	ocelot_port_manage_port_tag(ocelot, port);

	return 0;
}
@@ -288,15 +416,21 @@ EXPORT_SYMBOL(ocelot_port_vlan_filtering);
int ocelot_vlan_prepare(struct ocelot *ocelot, int port, u16 vid, bool pvid,
			bool untagged, struct netlink_ext_ack *extack)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];

	/* Deny changing the native VLAN, but always permit deleting it */
	if (untagged && ocelot_port->native_vlan.vid != vid &&
	    ocelot_port->native_vlan.valid) {
	if (untagged) {
		/* We are adding an egress-tagged VLAN */
		if (ocelot_port_uses_native_vlan(ocelot, port)) {
			NL_SET_ERR_MSG_MOD(extack,
					   "Port with egress-tagged VLANs cannot have more than one egress-untagged (native) VLAN");
			return -EBUSY;
		}
	} else {
		/* We are adding an egress-tagged VLAN */
		if (ocelot_port_num_untagged_vlans(ocelot, port) > 1) {
			NL_SET_ERR_MSG_MOD(extack,
				   "Port already has a native VLAN");
					   "Port with more than one egress-untagged VLAN cannot have egress-tagged VLANs");
			return -EBUSY;
		}
	}

	return 0;
}
@@ -307,27 +441,17 @@ int ocelot_vlan_add(struct ocelot *ocelot, int port, u16 vid, bool pvid,
{
	int err;

	err = ocelot_vlan_member_add(ocelot, port, vid);
	err = ocelot_vlan_member_add(ocelot, port, vid, untagged);
	if (err)
		return err;

	/* Default ingress vlan classification */
	if (pvid) {
		struct ocelot_vlan pvid_vlan;

		pvid_vlan.vid = vid;
		pvid_vlan.valid = true;
		ocelot_port_set_pvid(ocelot, port, pvid_vlan);
	}
	if (pvid)
		ocelot_port_set_pvid(ocelot, port,
				     ocelot_bridge_vlan_find(ocelot, vid));

	/* Untagged egress vlan clasification */
	if (untagged) {
		struct ocelot_vlan native_vlan;

		native_vlan.vid = vid;
		native_vlan.valid = true;
		ocelot_port_set_native_vlan(ocelot, port, native_vlan);
	}
	ocelot_port_manage_port_tag(ocelot, port);

	return 0;
}
@@ -343,18 +467,11 @@ int ocelot_vlan_del(struct ocelot *ocelot, int port, u16 vid)
		return err;

	/* Ingress */
	if (ocelot_port->pvid_vlan.vid == vid) {
		struct ocelot_vlan pvid_vlan = {0};

		ocelot_port_set_pvid(ocelot, port, pvid_vlan);
	}
	if (ocelot_port->pvid_vlan && ocelot_port->pvid_vlan->vid == vid)
		ocelot_port_set_pvid(ocelot, port, NULL);

	/* Egress */
	if (ocelot_port->native_vlan.vid == vid) {
		struct ocelot_vlan native_vlan = {0};

		ocelot_port_set_native_vlan(ocelot, port, native_vlan);
	}
	ocelot_port_manage_port_tag(ocelot, port);

	return 0;
}
@@ -372,13 +489,13 @@ static void ocelot_vlan_init(struct ocelot *ocelot)

	/* Configure the port VLAN memberships */
	for (vid = 1; vid < VLAN_N_VID; vid++)
		ocelot_vlan_member_set(ocelot, 0, vid);
		ocelot_vlant_set_mask(ocelot, vid, 0);

	/* Because VLAN filtering is enabled, we need VID 0 to get untagged
	 * traffic.  It is added automatically if 8021q module is loaded, but
	 * we can't rely on it since module may be not loaded.
	 */
	ocelot_vlan_member_set(ocelot, all_ports, 0);
	ocelot_vlant_set_mask(ocelot, OCELOT_VLAN_UNAWARE_PVID, all_ports);

	/* Set vlan ingress filter mask to all ports but the CPU port by
	 * default.
@@ -1680,12 +1797,11 @@ void ocelot_port_bridge_leave(struct ocelot *ocelot, int port,
			      struct net_device *bridge)
{
	struct ocelot_port *ocelot_port = ocelot->ports[port];
	struct ocelot_vlan pvid = {0}, native_vlan = {0};

	ocelot_port->bridge = NULL;

	ocelot_port_set_pvid(ocelot, port, pvid);
	ocelot_port_set_native_vlan(ocelot, port, native_vlan);
	ocelot_port_set_pvid(ocelot, port, NULL);
	ocelot_port_manage_port_tag(ocelot, port);
	ocelot_apply_bridge_fwd_mask(ocelot);
}
EXPORT_SYMBOL(ocelot_port_bridge_leave);
@@ -2071,7 +2187,8 @@ static void ocelot_cpu_port_init(struct ocelot *ocelot)
			    OCELOT_TAG_PREFIX_NONE);

	/* Configure the CPU port to be VLAN aware */
	ocelot_write_gix(ocelot, ANA_PORT_VLAN_CFG_VLAN_VID(0) |
	ocelot_write_gix(ocelot,
			 ANA_PORT_VLAN_CFG_VLAN_VID(OCELOT_VLAN_UNAWARE_PVID) |
			 ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA |
			 ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1),
			 ANA_PORT_VLAN_CFG, cpu);
@@ -2130,6 +2247,7 @@ int ocelot_init(struct ocelot *ocelot)

	INIT_LIST_HEAD(&ocelot->multicast);
	INIT_LIST_HEAD(&ocelot->pgids);
	INIT_LIST_HEAD(&ocelot->vlans);
	ocelot_detect_features(ocelot);
	ocelot_mact_init(ocelot);
	ocelot_vlan_init(ocelot);
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include "ocelot_rew.h"
#include "ocelot_qs.h"

#define OCELOT_VLAN_UNAWARE_PVID 0
#define OCELOT_BUFFER_CELL_SZ 60

#define OCELOT_STATS_CHECK_DELAY (2 * HZ)
+4 −4
Original line number Diff line number Diff line
@@ -116,16 +116,16 @@ static void ocelot_mrp_save_mac(struct ocelot *ocelot,
				struct ocelot_port *port)
{
	ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_test_dmac,
			  port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
			  OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
	ocelot_mact_learn(ocelot, PGID_BLACKHOLE, mrp_control_dmac,
			  port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
			  OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
}

static void ocelot_mrp_del_mac(struct ocelot *ocelot,
			       struct ocelot_port *port)
{
	ocelot_mact_forget(ocelot, mrp_test_dmac, port->pvid_vlan.vid);
	ocelot_mact_forget(ocelot, mrp_control_dmac, port->pvid_vlan.vid);
	ocelot_mact_forget(ocelot, mrp_test_dmac, OCELOT_VLAN_UNAWARE_PVID);
	ocelot_mact_forget(ocelot, mrp_control_dmac, OCELOT_VLAN_UNAWARE_PVID);
}

int ocelot_mrp_add(struct ocelot *ocelot, int port,
+6 −6
Original line number Diff line number Diff line
@@ -418,7 +418,7 @@ static int ocelot_vlan_vid_del(struct net_device *dev, u16 vid)
	 * with VLAN filtering feature. We need to keep it to receive
	 * untagged traffic.
	 */
	if (vid == 0)
	if (vid == OCELOT_VLAN_UNAWARE_PVID)
		return 0;

	ret = ocelot_vlan_del(ocelot, port, vid);
@@ -553,7 +553,7 @@ static int ocelot_mc_unsync(struct net_device *dev, const unsigned char *addr)
	struct ocelot_mact_work_ctx w;

	ether_addr_copy(w.forget.addr, addr);
	w.forget.vid = ocelot_port->pvid_vlan.vid;
	w.forget.vid = OCELOT_VLAN_UNAWARE_PVID;
	w.type = OCELOT_MACT_FORGET;

	return ocelot_enqueue_mact_action(ocelot, &w);
@@ -567,7 +567,7 @@ static int ocelot_mc_sync(struct net_device *dev, const unsigned char *addr)
	struct ocelot_mact_work_ctx w;

	ether_addr_copy(w.learn.addr, addr);
	w.learn.vid = ocelot_port->pvid_vlan.vid;
	w.learn.vid = OCELOT_VLAN_UNAWARE_PVID;
	w.learn.pgid = PGID_CPU;
	w.learn.entry_type = ENTRYTYPE_LOCKED;
	w.type = OCELOT_MACT_LEARN;
@@ -602,9 +602,9 @@ static int ocelot_port_set_mac_address(struct net_device *dev, void *p)

	/* Learn the new net device MAC address in the mac table. */
	ocelot_mact_learn(ocelot, PGID_CPU, addr->sa_data,
			  ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
			  OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);
	/* Then forget the previous one. */
	ocelot_mact_forget(ocelot, dev->dev_addr, ocelot_port->pvid_vlan.vid);
	ocelot_mact_forget(ocelot, dev->dev_addr, OCELOT_VLAN_UNAWARE_PVID);

	eth_hw_addr_set(dev, addr->sa_data);
	return 0;
@@ -1707,7 +1707,7 @@ int ocelot_probe_port(struct ocelot *ocelot, int port, struct regmap *target,

	eth_hw_addr_gen(dev, ocelot->base_mac, port);
	ocelot_mact_learn(ocelot, PGID_CPU, dev->dev_addr,
			  ocelot_port->pvid_vlan.vid, ENTRYTYPE_LOCKED);
			  OCELOT_VLAN_UNAWARE_PVID, ENTRYTYPE_LOCKED);

	ocelot_init_port(ocelot, port);

+17 −7
Original line number Diff line number Diff line
@@ -563,9 +563,22 @@ struct ocelot_vcap_block {
	int pol_lpr;
};

struct ocelot_vlan {
	bool valid;
struct ocelot_bridge_vlan {
	u16 vid;
	unsigned long portmask;
	unsigned long untagged;
	struct list_head list;
};

enum ocelot_port_tag_config {
	/* all VLANs are egress-untagged */
	OCELOT_PORT_TAG_DISABLED = 0,
	/* all VLANs except the native VLAN and VID 0 are egress-tagged */
	OCELOT_PORT_TAG_NATIVE = 1,
	/* all VLANs except VID 0 are egress-tagged */
	OCELOT_PORT_TAG_TRUNK_NO_VID0 = 2,
	/* all VLANs are egress-tagged */
	OCELOT_PORT_TAG_TRUNK = 3,
};

enum ocelot_sb {
@@ -590,9 +603,7 @@ struct ocelot_port {

	bool				vlan_aware;
	/* VLAN that untagged frames are classified to, on ingress */
	struct ocelot_vlan		pvid_vlan;
	/* The VLAN ID that will be transmitted as untagged, on egress */
	struct ocelot_vlan		native_vlan;
	const struct ocelot_bridge_vlan	*pvid_vlan;

	unsigned int			ptp_skbs_in_flight;
	u8				ptp_cmd;
@@ -635,8 +646,7 @@ struct ocelot {

	u8				base_mac[ETH_ALEN];

	/* Keep track of the vlan port masks */
	u32				vlan_mask[VLAN_N_VID];
	struct list_head		vlans;

	/* Switches like VSC9959 have flooding per traffic class */
	int				num_flooding_pgids;