diff options
Diffstat (limited to 'target/linux/generic/backport-5.15/767-v5.15-net-dsa-mt7530-use-independent-VLAN-learning-on-VLAN.patch')
-rw-r--r-- | target/linux/generic/backport-5.15/767-v5.15-net-dsa-mt7530-use-independent-VLAN-learning-on-VLAN.patch | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.15/767-v5.15-net-dsa-mt7530-use-independent-VLAN-learning-on-VLAN.patch b/target/linux/generic/backport-5.15/767-v5.15-net-dsa-mt7530-use-independent-VLAN-learning-on-VLAN.patch new file mode 100644 index 0000000000..f9fe0ef858 --- /dev/null +++ b/target/linux/generic/backport-5.15/767-v5.15-net-dsa-mt7530-use-independent-VLAN-learning-on-VLAN.patch @@ -0,0 +1,262 @@ +From e3a402764c5753698e7a9e45d4d21f093faa7852 Mon Sep 17 00:00:00 2001 +From: DENG Qingfang <dqfext@gmail.com> +Date: Wed, 4 Aug 2021 00:04:02 +0800 +Subject: [PATCH] net: dsa: mt7530: use independent VLAN learning on + VLAN-unaware bridges + +Consider the following bridge configuration, where bond0 is not +offloaded: + + +-- br0 --+ + / / | \ + / / | \ + / | | bond0 + / | | / \ + swp0 swp1 swp2 swp3 swp4 + . . . + . . . + A B C + +Ideally, when the switch receives a packet from swp3 or swp4, it should +forward the packet to the CPU, according to the port matrix and unknown +unicast flood settings. + +But packet loss will happen if the destination address is at one of the +offloaded ports (swp0~2). For example, when client C sends a packet to +A, the FDB lookup will indicate that it should be forwarded to swp0, but +the port matrix of swp3 and swp4 is configured to only allow the CPU to +be its destination, so it is dropped. + +However, this issue does not happen if the bridge is VLAN-aware. That is +because VLAN-aware bridges use independent VLAN learning, i.e. use VID +for FDB lookup, on offloaded ports. As swp3 and swp4 are not offloaded, +shared VLAN learning with default filter ID of 0 is used instead. So the +lookup for A with filter ID 0 never hits and the packet can be forwarded +to the CPU. + +In the current code, only two combinations were used to toggle user +ports' VLAN awareness: one is PCR.PORT_VLAN set to port matrix mode with +PVC.VLAN_ATTR set to transparent port, the other is PCR.PORT_VLAN set to +security mode with PVC.VLAN_ATTR set to user port. + +It turns out that only PVC.VLAN_ATTR contributes to VLAN awareness, and +port matrix mode just skips the VLAN table lookup. The reference manual +is somehow misleading when describing PORT_VLAN modes. It states that +PORT_MEM (VLAN port member) is used for destination if the VLAN table +lookup hits, but actually **PORT_MEM & PORT_MATRIX** (bitwise AND of +VLAN port member and port matrix) is used instead, which means we can +have two or more separate VLAN-aware bridges with the same PVID and +traffic won't leak between them. + +Therefore, to solve this, enable independent VLAN learning with PVID 0 +on VLAN-unaware bridges, by setting their PCR.PORT_VLAN to fallback +mode, while leaving standalone ports in port matrix mode. The CPU port +is always set to fallback mode to serve those bridges. + +During testing, it is found that FDB lookup with filter ID of 0 will +also hit entries with VID 0 even with independent VLAN learning. To +avoid that, install all VLANs with filter ID of 1. + +Signed-off-by: DENG Qingfang <dqfext@gmail.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +Signed-off-by: David S. Miller <davem@davemloft.net> +--- + drivers/net/dsa/mt7530.c | 72 +++++++++++++++++++++++++++++----------- + drivers/net/dsa/mt7530.h | 9 ++++- + 2 files changed, 60 insertions(+), 21 deletions(-) + +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -1011,6 +1011,10 @@ mt753x_cpu_port_enable(struct dsa_switch + mt7530_write(priv, MT7530_PCR_P(port), + PCR_MATRIX(dsa_user_ports(priv->ds))); + ++ /* Set to fallback mode for independent VLAN learning */ ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, ++ MT7530_PORT_FALLBACK_MODE); ++ + return 0; + } + +@@ -1165,6 +1169,10 @@ mt7530_port_bridge_join(struct dsa_switc + + mt7530_clear(priv, MT7530_PSC_P(port), SA_DIS); + ++ /* Set to fallback mode for independent VLAN learning */ ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, ++ MT7530_PORT_FALLBACK_MODE); ++ + mutex_unlock(&priv->reg_mutex); + + return 0; +@@ -1177,16 +1185,21 @@ mt7530_port_set_vlan_unaware(struct dsa_ + bool all_user_ports_removed = true; + int i; + +- /* When a port is removed from the bridge, the port would be set up +- * back to the default as is at initial boot which is a VLAN-unaware +- * port. ++ /* This is called after .port_bridge_leave when leaving a VLAN-aware ++ * bridge. Don't set standalone ports to fallback mode. + */ +- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, +- MT7530_PORT_MATRIX_MODE); ++ if (dsa_to_port(ds, port)->bridge_dev) ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, ++ MT7530_PORT_FALLBACK_MODE); ++ + mt7530_rmw(priv, MT7530_PVC_P(port), VLAN_ATTR_MASK | PVC_EG_TAG_MASK, + VLAN_ATTR(MT7530_VLAN_TRANSPARENT) | + PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); + ++ /* Set PVID to 0 */ ++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, ++ G0_PORT_VID_DEF); ++ + for (i = 0; i < MT7530_NUM_PORTS; i++) { + if (dsa_is_user_port(ds, i) && + dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) { +@@ -1212,15 +1225,14 @@ mt7530_port_set_vlan_aware(struct dsa_sw + struct mt7530_priv *priv = ds->priv; + + /* Trapped into security mode allows packet forwarding through VLAN +- * table lookup. CPU port is set to fallback mode to let untagged +- * frames pass through. ++ * table lookup. + */ +- if (dsa_is_cpu_port(ds, port)) +- mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, +- MT7530_PORT_FALLBACK_MODE); +- else ++ if (dsa_is_user_port(ds, port)) { + mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, + MT7530_PORT_SECURITY_MODE); ++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, ++ G0_PORT_VID(priv->ports[port].pvid)); ++ } + + /* Set the port as a user port which is to be able to recognize VID + * from incoming packets before fetching entry within the VLAN table. +@@ -1264,6 +1276,13 @@ mt7530_port_bridge_leave(struct dsa_swit + + mt7530_set(priv, MT7530_PSC_P(port), SA_DIS); + ++ /* When a port is removed from the bridge, the port would be set up ++ * back to the default as is at initial boot which is a VLAN-unaware ++ * port. ++ */ ++ mt7530_rmw(priv, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, ++ MT7530_PORT_MATRIX_MODE); ++ + mutex_unlock(&priv->reg_mutex); + } + +@@ -1406,7 +1425,8 @@ mt7530_hw_vlan_add(struct mt7530_priv *p + /* Validate the entry with independent learning, create egress tag per + * VLAN and joining the port as one of the port members. + */ +- val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | VLAN_VALID; ++ val = IVL_MAC | VTAG_EN | PORT_MEM(new_members) | FID(FID_BRIDGED) | ++ VLAN_VALID; + mt7530_write(priv, MT7530_VAWD1, val); + + /* Decide whether adding tag or not for those outgoing packets from the +@@ -1499,9 +1519,13 @@ mt7530_port_vlan_add(struct dsa_switch * + } + + if (pvid) { +- mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, +- G0_PORT_VID(vlan->vid_end)); + priv->ports[port].pvid = vlan->vid_end; ++ ++ /* Only configure PVID if VLAN filtering is enabled */ ++ if (dsa_port_is_vlan_filtering(dsa_to_port(ds, port))) ++ mt7530_rmw(priv, MT7530_PPBV1_P(port), ++ G0_PORT_VID_MASK, ++ G0_PORT_VID(vlan->vid_end)); + } + + mutex_unlock(&priv->reg_mutex); +@@ -1513,11 +1537,10 @@ mt7530_port_vlan_del(struct dsa_switch * + { + struct mt7530_hw_vlan_entry target_entry; + struct mt7530_priv *priv = ds->priv; +- u16 vid, pvid; ++ u16 vid; + + mutex_lock(&priv->reg_mutex); + +- pvid = priv->ports[port].pvid; + for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { + mt7530_hw_vlan_entry_init(&target_entry, port, 0); + mt7530_hw_vlan_update(priv, vid, &target_entry, +@@ -1526,12 +1549,13 @@ mt7530_port_vlan_del(struct dsa_switch * + /* PVID is being restored to the default whenever the PVID port + * is being removed from the VLAN. + */ +- if (pvid == vid) +- pvid = G0_PORT_VID_DEF; ++ if (priv->ports[port].pvid == vid) { ++ priv->ports[port].pvid = G0_PORT_VID_DEF; ++ mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, ++ G0_PORT_VID_DEF); ++ } + } + +- mt7530_rmw(priv, MT7530_PPBV1_P(port), G0_PORT_VID_MASK, pvid); +- priv->ports[port].pvid = pvid; + + mutex_unlock(&priv->reg_mutex); + +@@ -1827,6 +1851,10 @@ mt7530_setup(struct dsa_switch *ds) + return ret; + } else { + mt7530_port_disable(ds, i); ++ ++ /* Set default PVID to 0 on all user ports */ ++ mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK, ++ G0_PORT_VID_DEF); + } + + /* Enable consistent egress tag */ +@@ -1993,6 +2021,10 @@ mt7531_setup(struct dsa_switch *ds) + return ret; + } else { + mt7530_port_disable(ds, i); ++ ++ /* Set default PVID to 0 on all user ports */ ++ mt7530_rmw(priv, MT7530_PPBV1_P(i), G0_PORT_VID_MASK, ++ G0_PORT_VID_DEF); + } + + /* Enable consistent egress tag */ +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -145,11 +145,18 @@ enum mt7530_vlan_cmd { + #define VTAG_EN BIT(28) + /* VLAN Member Control */ + #define PORT_MEM(x) (((x) & 0xff) << 16) ++/* Filter ID */ ++#define FID(x) (((x) & 0x7) << 1) + /* VLAN Entry Valid */ + #define VLAN_VALID BIT(0) + #define PORT_MEM_SHFT 16 + #define PORT_MEM_MASK 0xff + ++enum mt7530_fid { ++ FID_STANDALONE = 0, ++ FID_BRIDGED = 1, ++}; ++ + #define MT7530_VAWD2 0x98 + /* Egress Tag Control */ + #define ETAG_CTRL_P(p, x) (((x) & 0x3) << ((p) << 1)) +@@ -244,7 +251,7 @@ enum mt7530_vlan_port_attr { + #define MT7530_PPBV1_P(x) (0x2014 + ((x) * 0x100)) + #define G0_PORT_VID(x) (((x) & 0xfff) << 0) + #define G0_PORT_VID_MASK G0_PORT_VID(0xfff) +-#define G0_PORT_VID_DEF G0_PORT_VID(1) ++#define G0_PORT_VID_DEF G0_PORT_VID(0) + + /* Register for port MAC control register */ + #define MT7530_PMCR_P(x) (0x3000 + ((x) * 0x100)) |