From: Daniel Golle <daniel@makrotopia.org>
To: "Chester A. Unal" <chester.a.unal@arinc9.com>,
"Daniel Golle" <daniel@makrotopia.org>,
"Andrew Lunn" <andrew@lunn.ch>,
"Vladimir Oltean" <olteanv@gmail.com>,
"David S. Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Matthias Brugger" <matthias.bgg@gmail.com>,
"AngeloGioacchino Del Regno"
<angelogioacchino.delregno@collabora.com>,
"DENG Qingfang" <dqfext@gmail.com>,
"Florian Fainelli" <f.fainelli@gmail.com>,
"Arınç ÜNAL" <arinc.unal@arinc9.com>,
"Sean Wang" <sean.wang@mediatek.com>,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org
Subject: [PATCH net 3/5] net: dsa: mt7530: fix CPU port VLAN not being reset to unaware
Date: Tue, 5 May 2026 15:16:35 +0100 [thread overview]
Message-ID: <a59cada36aa99021cebcf050cf3bc18b150cdfd8.1777986341.git.daniel@makrotopia.org> (raw)
In-Reply-To: <cover.1777986341.git.daniel@makrotopia.org>
After a VLAN-aware bridge is destroyed, creating any VLAN-unaware
bridge loses all connectivity. The VID 0 VLAN table entry used by
VLAN-unaware ports in FALLBACK mode gets corrupted during VLAN-aware
operation: mt7530_hw_vlan_add() overwrites its EG_CON flag with
VTAG_EN and bridge teardown removes ports from its PORT_MEM.
The cleanup code that should restore it never runs because the current
port's dp->vlan_filtering flag is still true when checked (DSA updates
it only after the driver callback returns). Even when restored, the
deferred VLAN deletion events from the switchdev workqueue can corrupt
VID 0 again after the restoration.
Skip the current port in the all_user_ports_removed check, call
mt7530_setup_vlan0() to restore the VID 0 entry, and protect VID 0
from being modified by bridge VLAN operations in port_vlan_add and
port_vlan_del since it is managed exclusively by mt7530_setup_vlan0().
Remove the CPU port PCR and PVC register writes which were clobbering
PORT_VLAN mode and VLAN_ATTR with wrong values.
Fixes: 83163f7dca56 ("net: dsa: mediatek: add VLAN support for MT7530")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
drivers/net/dsa/mt7530.c | 111 ++++++++++++++++++++++-----------------
1 file changed, 62 insertions(+), 49 deletions(-)
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
index a696c8736982..7cd8566a31e6 100644
--- a/drivers/net/dsa/mt7530.c
+++ b/drivers/net/dsa/mt7530.c
@@ -1623,6 +1623,49 @@ mt7530_port_bridge_join(struct dsa_switch *ds, int port,
return 0;
}
+static int
+mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
+{
+ struct mt7530_dummy_poll p;
+ u32 val;
+ int ret;
+
+ val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
+ mt7530_write(priv, MT7530_VTCR, val);
+
+ INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
+ ret = readx_poll_timeout(_mt7530_read, &p, val,
+ !(val & VTCR_BUSY), 20, 20000);
+ if (ret < 0) {
+ dev_err(priv->dev, "poll timeout\n");
+ return ret;
+ }
+
+ val = mt7530_read(priv, MT7530_VTCR);
+ if (val & VTCR_INVALID) {
+ dev_err(priv->dev, "read VTCR invalid\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int
+mt7530_setup_vlan0(struct mt7530_priv *priv)
+{
+ u32 val;
+
+ /* Validate the entry with independent learning, keep the original
+ * ingress tag attribute.
+ */
+ val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
+ VLAN_VALID;
+ mt7530_write(priv, MT7530_VAWD1, val);
+ mt7530_write(priv, MT7530_VAWD2, 0);
+
+ return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
+}
+
static void
mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
{
@@ -1648,6 +1691,8 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
G0_PORT_VID_DEF);
for (i = 0; i < priv->ds->num_ports; i++) {
+ if (i == port)
+ continue;
if (dsa_is_user_port(ds, i) &&
dsa_port_is_vlan_filtering(dsa_to_port(ds, i))) {
all_user_ports_removed = false;
@@ -1659,13 +1704,9 @@ mt7530_port_set_vlan_unaware(struct dsa_switch *ds, int port)
* the CPU port get out of VLAN filtering mode.
*/
if (all_user_ports_removed) {
- struct dsa_port *dp = dsa_to_port(ds, port);
- struct dsa_port *cpu_dp = dp->cpu_dp;
-
- mt7530_write(priv, MT7530_PCR_P(cpu_dp->index),
- PCR_MATRIX(dsa_user_ports(priv->ds)));
- mt7530_write(priv, MT7530_PVC_P(cpu_dp->index), PORT_SPEC_TAG
- | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
+ mutex_lock(&priv->reg_mutex);
+ mt7530_setup_vlan0(priv);
+ mutex_unlock(&priv->reg_mutex);
}
}
@@ -1853,33 +1894,6 @@ mt7530_port_mdb_del(struct dsa_switch *ds, int port,
return ret;
}
-static int
-mt7530_vlan_cmd(struct mt7530_priv *priv, enum mt7530_vlan_cmd cmd, u16 vid)
-{
- struct mt7530_dummy_poll p;
- u32 val;
- int ret;
-
- val = VTCR_BUSY | VTCR_FUNC(cmd) | vid;
- mt7530_write(priv, MT7530_VTCR, val);
-
- INIT_MT7530_DUMMY_POLL(&p, priv, MT7530_VTCR);
- ret = readx_poll_timeout(_mt7530_read, &p, val,
- !(val & VTCR_BUSY), 20, 20000);
- if (ret < 0) {
- dev_err(priv->dev, "poll timeout\n");
- return ret;
- }
-
- val = mt7530_read(priv, MT7530_VTCR);
- if (val & VTCR_INVALID) {
- dev_err(priv->dev, "read VTCR invalid\n");
- return -EINVAL;
- }
-
- return 0;
-}
-
static int
mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering,
struct netlink_ext_ack *extack)
@@ -1984,21 +1998,6 @@ mt7530_hw_vlan_update(struct mt7530_priv *priv, u16 vid,
mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, vid);
}
-static int
-mt7530_setup_vlan0(struct mt7530_priv *priv)
-{
- u32 val;
-
- /* Validate the entry with independent learning, keep the original
- * ingress tag attribute.
- */
- val = IVL_MAC | EG_CON | PORT_MEM(MT7530_ALL_MEMBERS) | FID(FID_BRIDGED) |
- VLAN_VALID;
- mt7530_write(priv, MT7530_VAWD1, val);
-
- return mt7530_vlan_cmd(priv, MT7530_VTCR_WR_VID, 0);
-}
-
static int
mt7530_port_vlan_add(struct dsa_switch *ds, int port,
const struct switchdev_obj_port_vlan *vlan,
@@ -2011,9 +2010,18 @@ mt7530_port_vlan_add(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
+ /* VID 0 is managed exclusively by mt7530_setup_vlan0() for
+ * VLAN-unaware bridge operation. Don't let the bridge overwrite
+ * its EG_CON flag with VTAG_EN and corrupt PORT_MEM.
+ */
+ if (vlan->vid == 0)
+ goto skip_vlan_table;
+
mt7530_hw_vlan_entry_init(&new_entry, port, untagged);
mt7530_hw_vlan_update(priv, vlan->vid, &new_entry, mt7530_hw_vlan_add);
+skip_vlan_table:
+
if (pvid) {
priv->ports[port].pvid = vlan->vid;
@@ -2053,10 +2061,15 @@ mt7530_port_vlan_del(struct dsa_switch *ds, int port,
mutex_lock(&priv->reg_mutex);
+ /* VID 0 is managed exclusively by mt7530_setup_vlan0(). */
+ if (vlan->vid == 0)
+ goto skip_vlan_table;
+
mt7530_hw_vlan_entry_init(&target_entry, port, 0);
mt7530_hw_vlan_update(priv, vlan->vid, &target_entry,
mt7530_hw_vlan_del);
+skip_vlan_table:
/* PVID is being restored to the default whenever the PVID port
* is being removed from the VLAN.
*/
--
2.54.0
next prev parent reply other threads:[~2026-05-05 14:16 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-05 14:15 [PATCH net 0/5] net: dsa: mt7530: assorted fixes Daniel Golle
2026-05-05 14:16 ` [PATCH net 1/5] net: dsa: mt7530: fix FDB entries not aging out with short timeout Daniel Golle
2026-05-05 14:16 ` [PATCH net 2/5] net: dsa: mt7530: preserve VLAN tags on trapped link-local frames Daniel Golle
2026-05-05 15:37 ` Chester A. Unal
2026-05-05 16:03 ` Daniel Golle
2026-05-05 16:12 ` Chester A. Unal
2026-05-05 14:16 ` Daniel Golle [this message]
2026-05-05 14:16 ` [PATCH net 4/5] net: dsa: mt7530: clear flood flags on bridge leave Daniel Golle
2026-05-05 14:16 ` [PATCH net 5/5] net: dsa: mt7530: untag VLAN-aware bridge PVID Daniel Golle
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=a59cada36aa99021cebcf050cf3bc18b150cdfd8.1777986341.git.daniel@makrotopia.org \
--to=daniel@makrotopia.org \
--cc=andrew@lunn.ch \
--cc=angelogioacchino.delregno@collabora.com \
--cc=arinc.unal@arinc9.com \
--cc=chester.a.unal@arinc9.com \
--cc=davem@davemloft.net \
--cc=dqfext@gmail.com \
--cc=edumazet@google.com \
--cc=f.fainelli@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=matthias.bgg@gmail.com \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
--cc=sean.wang@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox