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

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
...@@ -57,6 +57,81 @@ static bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd, ...@@ -57,6 +57,81 @@ static bool sja1105_can_forward(struct sja1105_l2_forwarding_entry *l2_fwd,
return !!(l2_fwd[from].reach_port & BIT(to)); 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) static int sja1105_init_mac_settings(struct sja1105_private *priv)
{ {
struct sja1105_mac_config_entry default_mac = { struct sja1105_mac_config_entry default_mac = {
...@@ -1656,6 +1731,10 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port, ...@@ -1656,6 +1731,10 @@ static int sja1105_bridge_member(struct dsa_switch *ds, int port,
if (rc) if (rc)
return rc; return rc;
rc = sja1105_commit_pvid(ds, port);
if (rc)
return rc;
return sja1105_manage_flood_domains(priv); return sja1105_manage_flood_domains(priv);
} }
...@@ -1955,35 +2034,6 @@ int sja1105_static_config_reload(struct sja1105_private *priv, ...@@ -1955,35 +2034,6 @@ int sja1105_static_config_reload(struct sja1105_private *priv,
return rc; 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 static enum dsa_tag_protocol
sja1105_get_tag_protocol(struct dsa_switch *ds, int port, sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
enum dsa_tag_protocol mp) enum dsa_tag_protocol mp)
...@@ -1993,22 +2043,6 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port, ...@@ -1993,22 +2043,6 @@ sja1105_get_tag_protocol(struct dsa_switch *ds, int port,
return priv->info->tag_proto; 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, /* The TPID setting belongs to the General Parameters table,
* which can only be partially reconfigured at runtime (and not the TPID). * which can only be partially reconfigured at runtime (and not the TPID).
* So a switch reset is required. * So a switch reset is required.
...@@ -2215,8 +2249,16 @@ static int sja1105_bridge_vlan_del(struct dsa_switch *ds, int port, ...@@ -2215,8 +2249,16 @@ static int sja1105_bridge_vlan_del(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan) const struct switchdev_obj_port_vlan *vlan)
{ {
struct sja1105_private *priv = ds->priv; 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, static int sja1105_dsa_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
......
...@@ -368,10 +368,11 @@ static bool sja1110_skb_has_inband_control_extension(const struct sk_buff *skb) ...@@ -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; return ntohs(eth_hdr(skb)->h_proto) == ETH_P_SJA1110;
} }
/* Returns true for imprecise RX and sets the @vid. /* If the VLAN in the packet is a tag_8021q one, set @source_port and
* Returns false for precise RX and sets @source_port and @switch_id. * @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) int *switch_id, u16 *vid)
{ {
struct vlan_ethhdr *hdr = (struct vlan_ethhdr *)skb_mac_header(skb); 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, ...@@ -382,15 +383,11 @@ static bool sja1105_vlan_rcv(struct sk_buff *skb, int *source_port,
else else
vlan_tci = ntohs(hdr->h_vlan_TCI); vlan_tci = ntohs(hdr->h_vlan_TCI);
if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK)) { if (vid_is_dsa_8021q_rxvlan(vlan_tci & VLAN_VID_MASK))
dsa_8021q_rcv(skb, source_port, switch_id); return dsa_8021q_rcv(skb, source_port, switch_id);
return false;
}
/* Try our best with imprecise RX */ /* Try our best with imprecise RX */
*vid = vlan_tci & VLAN_VID_MASK; *vid = vlan_tci & VLAN_VID_MASK;
return true;
} }
static struct sk_buff *sja1105_rcv(struct sk_buff *skb, static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
...@@ -399,7 +396,6 @@ 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; int source_port = -1, switch_id = -1;
struct sja1105_meta meta = {0}; struct sja1105_meta meta = {0};
bool imprecise_rx = false;
struct ethhdr *hdr; struct ethhdr *hdr;
bool is_link_local; bool is_link_local;
bool is_meta; bool is_meta;
...@@ -413,8 +409,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -413,8 +409,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
if (sja1105_skb_has_tag_8021q(skb)) { if (sja1105_skb_has_tag_8021q(skb)) {
/* Normal traffic path. */ /* Normal traffic path. */
imprecise_rx = sja1105_vlan_rcv(skb, &source_port, &switch_id, sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
&vid);
} else if (is_link_local) { } else if (is_link_local) {
/* Management traffic path. Switch embeds the switch ID and /* Management traffic path. Switch embeds the switch ID and
* port ID into bytes of the destination MAC, courtesy of * port ID into bytes of the destination MAC, courtesy of
...@@ -433,7 +428,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb, ...@@ -433,7 +428,7 @@ static struct sk_buff *sja1105_rcv(struct sk_buff *skb,
return NULL; return NULL;
} }
if (imprecise_rx) if (source_port == -1 || switch_id == -1)
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
else else
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); 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, ...@@ -550,7 +545,6 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb,
struct packet_type *pt) struct packet_type *pt)
{ {
int source_port = -1, switch_id = -1; int source_port = -1, switch_id = -1;
bool imprecise_rx = false;
u16 vid; u16 vid;
skb->offload_fwd_mark = 1; skb->offload_fwd_mark = 1;
...@@ -564,10 +558,9 @@ static struct sk_buff *sja1110_rcv(struct sk_buff *skb, ...@@ -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 */ /* Packets with in-band control extensions might still have RX VLANs */
if (likely(sja1105_skb_has_tag_8021q(skb))) if (likely(sja1105_skb_has_tag_8021q(skb)))
imprecise_rx = sja1105_vlan_rcv(skb, &source_port, &switch_id, sja1105_vlan_rcv(skb, &source_port, &switch_id, &vid);
&vid);
if (imprecise_rx) if (source_port == -1 || switch_id == -1)
skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid); skb->dev = dsa_find_designated_bridge_port_by_vid(netdev, vid);
else else
skb->dev = dsa_master_find_slave(netdev, switch_id, source_port); skb->dev = dsa_master_find_slave(netdev, switch_id, source_port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment