Commit 3bdf4d61 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'sja110-vlan-fixes'

Vladimir Oltean says:

====================
NXP SJA1105 VLAN regressions

These are 3 patches to fix issues seen with some more varied testing
done after the changes in the "Traffic termination for sja1105 ports
under VLAN-aware bridge" series were made:
https://patchwork.kernel.org/project/netdevbpf/cover/20210726165536.1338471-1-vladimir.oltean@nxp.com/



Issue 1: traffic no longer works on a port after leaving a VLAN-aware bridge
Issue 2: untagged traffic not dropped if pvid is absent from a VLAN-aware port
Issue 3: PTP and STP broken on ports under a VLAN-aware bridge
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents e5fe3a5f 04a17583
Loading
Loading
Loading
Loading
+88 −46
Original line number Diff line number Diff line
@@ -57,6 +57,81 @@ static bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd,
	return !!(l2_fwd[from].reach_port & BIT(to));
}

static int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid)
{
	struct sja1105_vlan_lookup_entry *vlan;
	int count, i;

	vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;
	count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count;

	for (i = 0; i < count; i++)
		if (vlan[i].vlanid == vid)
			return i;

	/* Return an invalid entry index if not found */
	return -1;
}

static int sja1105_drop_untagged(struct dsa_switch *ds, int port, bool drop)
{
	struct sja1105_private *priv = ds->priv;
	struct sja1105_mac_config_entry *mac;

	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;

	if (mac[port].drpuntag == drop)
		return 0;

	mac[port].drpuntag = drop;

	return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
					    &mac[port], true);
}

static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid)
{
	struct sja1105_mac_config_entry *mac;

	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;

	if (mac[port].vlanid == pvid)
		return 0;

	mac[port].vlanid = pvid;

	return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
					    &mac[port], true);
}

static int sja1105_commit_pvid(struct dsa_switch *ds, int port)
{
	struct dsa_port *dp = dsa_to_port(ds, port);
	struct sja1105_private *priv = ds->priv;
	struct sja1105_vlan_lookup_entry *vlan;
	bool drop_untagged = false;
	int match, rc;
	u16 pvid;

	if (dp->bridge_dev && br_vlan_enabled(dp->bridge_dev))
		pvid = priv->bridge_pvid[port];
	else
		pvid = priv->tag_8021q_pvid[port];

	rc = sja1105_pvid_apply(priv, port, pvid);
	if (rc)
		return rc;

	vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;

	match = sja1105_is_vlan_configured(priv, pvid);

	if (match < 0 || !(vlan[match].vmemb_port & BIT(port)))
		drop_untagged = true;

	return sja1105_drop_untagged(ds, port, drop_untagged);
}

static int sja1105_init_mac_settings(struct sja1105_private *priv)
{
	struct sja1105_mac_config_entry default_mac = {
@@ -1656,6 +1731,10 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port,
	if (rc)
		return rc;

	rc = sja1105_commit_pvid(ds, port);
	if (rc)
		return rc;

	return sja1105_manage_flood_domains(priv);
}

@@ -1955,35 +2034,6 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
	return rc;
}

static int sja1105_pvid_apply(struct sja1105_private *priv, int port, u16 pvid)
{
	struct sja1105_mac_config_entry *mac;

	mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries;

	if (mac[port].vlanid == pvid)
		return 0;

	mac[port].vlanid = pvid;

	return sja1105_dynamic_config_write(priv, BLK_IDX_MAC_CONFIG, port,
					   &mac[port], true);
}

static int sja1105_commit_pvid(struct dsa_switch *ds, int port)
{
	struct dsa_port *dp = dsa_to_port(ds, port);
	struct sja1105_private *priv = ds->priv;
	u16 pvid;

	if (dp->bridge_dev && br_vlan_enabled(dp->bridge_dev))
		pvid = priv->bridge_pvid[port];
	else
		pvid = priv->tag_8021q_pvid[port];

	return sja1105_pvid_apply(priv, port, pvid);
}

static enum dsa_tag_protocol
sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
			 enum dsa_tag_protocol mp)
@@ -1993,22 +2043,6 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
	return priv->info->tag_proto;
}

static int sja1105_is_vlan_configured(struct sja1105_private *priv, u16 vid)
{
	struct sja1105_vlan_lookup_entry *vlan;
	int count, i;

	vlan = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entries;
	count = priv->static_config.tables[BLK_IDX_VLAN_LOOKUP].entry_count;

	for (i = 0; i < count; i++)
		if (vlan[i].vlanid == vid)
			return i;

	/* Return an invalid entry index if not found */
	return -1;
}

/* The TPID setting belongs to the General Parameters table,
 * which can only be partially reconfigured at runtime (and not the TPID).
 * So a switch reset is required.
@@ -2215,8 +2249,16 @@ static int sja1105_bridge_vlan_del(struct dsa_switch *ds, int port,
				   const struct switchdev_obj_port_vlan *vlan)
{
	struct sja1105_private *priv = ds->priv;
	int rc;

	return sja1105_vlan_del(priv, port, vlan->vid);
	rc = sja1105_vlan_del(priv, port, vlan->vid);
	if (rc)
		return rc;

	/* In case the pvid was deleted, make sure that untagged packets will
	 * be dropped.
	 */
	return sja1105_commit_pvid(ds, port);
}

static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
+10 −17
Original line number Diff line number Diff line
@@ -368,10 +368,11 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb)
	return ntohs(eth_hdr(skb)->h_proto) == ETH_P_SJA1110;
}

/* Returns true for imprecise RX and sets the @vid.
 * Returns false for precise RX and sets @source_port and @switch_id.
/* If the VLAN in the packet is a tag_8021q one, set @source_port and
 * @switch_id and strip the header. Otherwise set @vid and keep it in the
 * packet.
 */
static bool sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
static void sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
			     int *switch_id, u16 *vid)
{
	struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb);
@@ -382,15 +383,11 @@ static bool sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
	else
		vlan_tci = ntohs(hdr->h_vlan_TCI);

	if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK)) {
		dsa_8021q_rcv(skb, source_port, switch_id);
		return false;
	}
	if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK))
		return dsa_8021q_rcv(skb, source_port, switch_id);

	/* Try our best with imprecise RX */
	*vid = vlan_tci & VLAN_VID_MASK;

	return true;
}

static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
@@ -399,7 +396,6 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
{
	int source_port = -1, switch_id = -1;
	struct sja1105_meta meta = {0};
	bool imprecise_rx = false;
	struct ethhdr *hdr;
	bool is_link_local;
	bool is_meta;
@@ -413,8 +409,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,

	if (sja1105_skb_has_tag_8021q(skb)) {
		/* Normal traffic path. */
		imprecise_rx = sja1105_vlan_rcv(skb, &source_port, &switch_id,
						&vid);
		sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
	} else if (is_link_local) {
		/* Management traffic path. Switch embeds the switch ID and
		 * port ID into bytes of the destination MAC, courtesy of
@@ -433,7 +428,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
		return NULL;
	}

	if (imprecise_rx)
	if (source_port == -1 || switch_id == -1)
		skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
	else
		skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
@@ -550,7 +545,6 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
				   struct packet_type *pt)
{
	int source_port = -1, switch_id = -1;
	bool imprecise_rx = false;
	u16 vid;

	skb->offload_fwd_mark = 1;
@@ -564,10 +558,9 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,

	/* Packets with in-band control extensions might still have RX VLANs */
	if (likely(sja1105_skb_has_tag_8021q(skb)))
		imprecise_rx = sja1105_vlan_rcv(skb, &source_port, &switch_id,
						&vid);
		sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);

	if (imprecise_rx)
	if (source_port == -1 || switch_id == -1)
		skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
	else
		skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);