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>
This commit is contained in:
commit
3bdf4d6196
@ -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,
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user