diff options
author | David S. Miller <davem@davemloft.net> | 2021-07-29 15:35:01 +0100 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2021-07-29 15:35:01 +0100 |
commit | 3bdf4d6196eb5432e7884491456b158d9b670e64 (patch) | |
tree | bc41dd175f8c5834a58783bfbfb993b939dfcf4c | |
parent | e5fe3a5fe333b9967eeb99966bcf3ec710318554 (diff) | |
parent | 04a1758348a87eb73b8a4554d0c227831e2bb33e (diff) | |
download | linux-3bdf4d6196eb5432e7884491456b158d9b670e64.tar.bz2 |
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: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/sja1105/sja1105_main.c | 134 | ||||
-rw-r--r-- | net/dsa/tag_sja1105.c | 27 |
2 files changed, 98 insertions, 63 deletions
diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 3047704c24d3..5ab1676a7448 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -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 @@ out: 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, diff --git a/net/dsa/tag_sja1105.c b/net/dsa/tag_sja1105.c index cddee4b499d8..c1f993d592ef 100644 --- a/net/dsa/tag_sja1105.c +++ b/net/dsa/tag_sja1105.c @@ -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); |