From: Markus Stockhausen Date: Sat, 7 Sep 2024 07:23:03 +0000 (-0400) Subject: realtek: 6.6: fix VLAN handling X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=a22d359fa56fe06ebd74f87eccacf56b3752da58;p=openwrt%2Fstaging%2Fxback.git 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 Suggested-by: Bjørn Mork --- 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;