[openwrt/openwrt] realtek: 6.6: fix VLAN handling

LEDE Commits lede-commits at lists.infradead.org
Sat Sep 14 12:30:39 PDT 2024


svanheule pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/a22d359fa56fe06ebd74f87eccacf56b3752da58

commit a22d359fa56fe06ebd74f87eccacf56b3752da58
Author: Markus Stockhausen <markus.stockhausen at gmx.de>
AuthorDate: Sat Sep 7 03:23:03 2024 -0400

    realtek: 6.6: fix VLAN handling
    
    The CPU port of realtek switches needs some proper PVID set to handle
    untagged packets. Because the ethernet driver does no special VLAN
    handling (see CPU tag RVID/RVID_SEL) as of now we can only steer
    untagged packets by setting PVID for the CPU port. VLAN handling has
    never been perfect but 3 events made things worse.
    
    - Commit a37650821644 ("rtl83xx: dsa: Do nothing when vid 0")
    - Commit e691e2b302d9 ("rtl83xx: dsa: reset PVID to 1 instead of 0")
    - Upgrade to kernel 6.6
    
    Reasons are:
    
    - Rejecting VID 0 disabled Linux initialization routines
    - Initialization for PVID forgot to set priv->ports[port].pvid
    - Kernel 6.6 does no longer clarify CPU port as untagged
    
    To fix this prepare the VID 0 setup inside the driver. Join all ports
    to VID 0 and let no one from outsinde interfere with this setup.
    Especially ignore PVID settings for the CPU port for all further
    VLAN commands.
    
    Signed-off-by: Markus Stockhausen <markus.stockhausen at gmx.de>
    Suggested-by: Bjørn Mork <bjorn at mork.no>
---
 .../files-6.6/drivers/net/dsa/rtl83xx/dsa.c        | 71 ++++++++++++++--------
 1 file changed, 45 insertions(+), 26 deletions(-)

diff --git a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
index 7ec3f4af1b..2f9b3ba8c1 100644
--- a/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
+++ b/target/linux/realtek/files-6.6/drivers/net/dsa/rtl83xx/dsa.c
@@ -109,6 +109,20 @@ static enum dsa_tag_protocol rtl83xx_get_tag_protocol(struct dsa_switch *ds,
 	return DSA_TAG_PROTO_TRAILER;
 }
 
+static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
+				  int port, int pvid)
+{
+	/* Set both inner and outer PVID of the port */
+	priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
+	priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
+	priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
+					PBVLAN_MODE_UNTAG_AND_PRITAG);
+	priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
+					PBVLAN_MODE_UNTAG_AND_PRITAG);
+
+	priv->ports[port].pvid = pvid;
+}
+
 /* Initialize all VLANS */
 static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
 {
@@ -132,17 +146,22 @@ static void rtl83xx_vlan_setup(struct rtl838x_switch_priv *priv)
 		info.l2_tunnel_list_id = -1;
 	}
 
-	/* Initialize all vlans 0-4095 */
-	for (int i = 0; i < MAX_VLANS; i ++)
+	/* Initialize normal VLANs 1-4095 */
+	for (int i = 1; i < MAX_VLANS; i ++)
 		priv->r->vlan_set_tagged(i, &info);
 
-	/* reset PVIDs; defaults to 1 on reset */
+	/*
+	 * Initialize the special VLAN 0 and reset PVIDs. The CPU port PVID
+	 * is applied to packets from the CPU for untagged destinations,
+	 * regardless if the actual ingress VID. Any port with untagged
+	 * egress VLAN(s) must therefore be a member of VLAN 0 to support
+	 * CPU port as ingress when VLAN filtering is enabled.
+	 */
 	for (int i = 0; i <= priv->cpu_port; i++) {
-		priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_INNER, 1);
-		priv->r->vlan_port_pvid_set(i, PBVLAN_TYPE_OUTER, 1);
-		priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_INNER, PBVLAN_MODE_UNTAG_AND_PRITAG);
-		priv->r->vlan_port_pvidmode_set(i, PBVLAN_TYPE_OUTER, PBVLAN_MODE_UNTAG_AND_PRITAG);
+		rtl83xx_vlan_set_pvid(priv, i, 0);
+		info.tagged_ports |= BIT_ULL(i);
 	}
+	priv->r->vlan_set_tagged(0, &info);
 
 	/* Set forwarding action based on inner VLAN tag */
 	for (int i = 0; i < priv->cpu_port; i++)
@@ -1418,20 +1437,6 @@ static int rtl83xx_vlan_prepare(struct dsa_switch *ds, int port,
 	return 0;
 }
 
-static void rtl83xx_vlan_set_pvid(struct rtl838x_switch_priv *priv,
-				  int port, int pvid)
-{
-	/* Set both inner and outer PVID of the port */
-	priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_INNER, pvid);
-	priv->r->vlan_port_pvid_set(port, PBVLAN_TYPE_OUTER, pvid);
-	priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_INNER,
-					PBVLAN_MODE_UNTAG_AND_PRITAG);
-	priv->r->vlan_port_pvidmode_set(port, PBVLAN_TYPE_OUTER,
-					PBVLAN_MODE_UNTAG_AND_PRITAG);
-
-	priv->ports[port].pvid = pvid;
-}
-
 static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
 			    const struct switchdev_obj_port_vlan *vlan,
 			    struct netlink_ext_ack *extack)
@@ -1443,7 +1448,8 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
 	pr_debug("%s port %d, vid %d, flags %x\n",
 		__func__, port, vlan->vid, vlan->flags);
 
-	if(!vlan->vid) return 0;
+	/* Let no one mess with our special VLAN 0 */
+	if (!vlan->vid) return 0;
 
 	if (vlan->vid > 4095) {
 		dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
@@ -1456,10 +1462,20 @@ static int rtl83xx_vlan_add(struct dsa_switch *ds, int port,
 
 	mutex_lock(&priv->reg_mutex);
 
-	if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
-		rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
-	else if (priv->ports[port].pvid == vlan->vid)
-		rtl83xx_vlan_set_pvid(priv, port, 0);
+	/*
+	 * Realtek switches copy frames as-is to/from the CPU. For a proper
+	 * VLAN handling the 12 bit RVID field (= VLAN id) for incoming traffic
+	 * and the 1 bit RVID_SEL field (0 = use inner tag, 1 = use outer tag)
+	 * for outgoing traffic of the CPU tag structure need to be handled. As
+	 * of now no such logic is in place. So for the CPU port keep the fixed
+	 * PVID=0 from initial setup in place and ignore all subsequent settings.
+	 */
+	if (port != priv->cpu_port) {
+		if (vlan->flags & BRIDGE_VLAN_INFO_PVID)
+			rtl83xx_vlan_set_pvid(priv, port, vlan->vid);
+		else if (priv->ports[port].pvid == vlan->vid)
+			rtl83xx_vlan_set_pvid(priv, port, 0);
+	}
 
 	/* Get port memberships of this vlan */
 	priv->r->vlan_tables_read(vlan->vid, &info);
@@ -1503,6 +1519,9 @@ static int rtl83xx_vlan_del(struct dsa_switch *ds, int port,
 	pr_debug("%s: port %d, vid %d, flags %x\n",
 		__func__, port, vlan->vid, vlan->flags);
 
+	/* Let no one mess with our special VLAN 0 */
+	if (!vlan->vid) return 0;
+
 	if (vlan->vid > 4095) {
 		dev_err(priv->dev, "VLAN out of range: %d", vlan->vid);
 		return -ENOTSUPP;




More information about the lede-commits mailing list