Re: [PATCH v3 3/7] net: dsa: mediatek: add support for passthrough mode
flat view
From: netdev-bot+sashiko@kernel.org
Date: 2026-10-29 02:02:41
Also in:
linux-devicetree, linux-mediatek, lkml, netdev
Thank you for your contribution! Sashiko AI review found 10 potential issue(s) to consider. Critical: 0 · High: 2 · Medium: 6 · Low: 2 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [High] mt753x_check_passthrough() uses one counter for every available port that has an `ethernet` (CPU) phandle or a `link` (DSA) phandle. - [High] net: dsa: mt7530: NULL pointer dereference in mt7531_mac_config() on DSA port - [Medium] In passthrough mode, mt753x_cpu_port_enable() calls mt7530_port_change_mtu(ds, port, MT7530_MAX_MTU) to raise the global… - [Medium] The patch adds support for this switch being the downstream switch, with a DSA port as its upstream link. - [Medium] Passthrough receive works only if the downstream switch is DSA switch index 0. - [Medium] In the user-port branch, mt7530_port_enable() replaces the per-port `dp->cpu_dp->index` with the switch-wide… - [Medium] In passthrough mode, mt753x_cpu_port_enable() writes MT7530_PVC_P(port) = VLAN_ATTR(MT7530_VLAN_TRANSPARENT) |… - [Medium] In passthrough mode the upstream switch's CPU port no longer has PORT_SPEC_TAG; mt753x_cpu_port_enable() writes… - [Low] In passthrough mode, mt753x_cpu_port_enable() unconditionally sets MT7530_LOOP_DET_DISABLE (BIT(14)) in MT753X_MTRAP (0x7804) with a… - [Low] When the downstream DSA link port is enabled, mt7530_port_enable() does a full `regmap_write(priv->regmap,… -- net: dsa: mediatek: add support for passthrough mode This adds a passthrough mode to the mt7530 driver. In this mode a switch forwards all traffic between its CPU port and a link to a second, downstream MT7530 without changing the MediaTek tag, and most switchdev ops become no-ops. Passthrough is detected at probe time from the device tree, and the switch can also act as the downstream switch, with a DSA link as its upstream port.
quoted hunk ↗ jump to hunk
diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index d10194488bdee..3eb2e11d69856 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c
[ ... ]
quoted hunk ↗ jump to hunk
@@ -1286,13 +1292,41 @@ mt753x_trap_frames(struct mt7530_priv *priv) TO_CPU_FW_CPU_ONLY); } +static int +mt7530_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu); + +/* If this switch is downstream of another switch that is in passthrough mode, + * the "CPU" port is actually a DSA port. + */ static void mt753x_cpu_port_enable(struct dsa_switch *ds, int port) { struct mt7530_priv *priv = ds->priv; - /* Enable Mediatek header mode on the cpu port */ - regmap_write(priv->regmap, MT7530_PVC_P(port), PORT_SPEC_TAG); + if (priv->is_passthrough) { + /* Disable parsing of the DSA tag, it will be forwarded blindly + * to the downstream switch. + */ + regmap_write(priv->regmap, MT7530_PVC_P(port), + VLAN_ATTR(MT7530_VLAN_TRANSPARENT) | + PVC_EG_TAG(MT7530_VLAN_EG_DISABLED));
[Severity: Medium]
Does this EG_DISABLED setting survive setup? As soon as
mt753x_cpu_port_enable() returns, the same loop iteration in mt7530_setup()
runs this:
if (dsa_is_upstream_port(ds, i)) {
mt753x_cpu_port_enable(ds, i);
} else {
...
}
/* Enable consistent egress tag */
regmap_update_bits(priv->regmap, MT7530_PVC_P(i),
PVC_EG_TAG_MASK,
PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT));
mt7531_setup_common() does the same update. So the upstream port ends up
with EG_TAG set to consistent. The downstream DSA port is programmed later
from mt7530_port_enable() and keeps EG_DISABLED.
That doesn't match the commit message, which says both ports are
"configured as MT7530_VLAN_EG_DISABLED and MT7530_VLAN_TRANSPARENT".
Should the egress tag update be skipped for the upstream port in
passthrough mode?
[Severity: Medium]
Without PORT_SPEC_TAG on the CPU port, frames from the host go through the
normal ARL and trap logic. mt753x_trap_frames() still installs the BPC,
RGAC1 and RGAC2 rules unconditionally. Those rules use TO_CPU_FW_CPU_ONLY
for PAE, BPDU and 01:80:C2:00:00:01/02/03/0E.
Say the host sends an STP BPDU, LACPDU, EAPOL or LLDP frame on a downstream
user port. The real DA is still at the front of the frame, because the MTK
tag sits after the SA.
Would that frame match the trap rules and be sent to the CPU port? That is
its own ingress port, so the frame would never reach the downstream switch.
Control frames from the downstream switch to the host would still work, so
the failure would be one-way. This depends on the switch applying trap
rules to untagged frames that enter the CPU port, which I could not confirm
without the datasheet.
quoted hunk ↗ jump to hunk
+ + /* The port is not configured to parse DSA tags, so they are + * liable to be confused for length fields, so length check is + * disabled. + */ + regmap_clear_bits(priv->regmap, MT753X_AGC, AGC_L2LEN_CHK); + + /* In passthrough mode, MTU is only enforced downstream */ + mt7530_port_change_mtu(ds, port, MT7530_MAX_MTU);
[Severity: Medium] Does this call do anything? This branch runs only when priv->is_passthrough is true. This patch also adds the following as the first check in mt7530_port_change_mtu(): if (priv->is_passthrough) return 0; So MT7530_GMACCR is never written, and MAX_RX_PKT_LEN on the passthrough switch keeps its reset default. Later DSA .port_change_mtu calls for this switch return early in the same way. The upstream switch no longer parses the MTK tag, so it counts those 4 bytes as payload. Meanwhile .port_max_mtu still reports MT7530_MAX_MTU and .port_change_mtu reports success. Can the downstream switch and the DSA core then accept MTUs whose frames the upstream switch drops? Clearing AGC_L2LEN_CHK turns off the length field check, but it does not change the MAC's maximum frame length.
quoted hunk ↗ jump to hunk
+ + /* Loop detection has no value in passthrough mode */ + regmap_set_bits(priv->regmap, MT753X_MTRAP, + MT7530_LOOP_DET_DISABLE);
[Severity: Low] Is BIT(14) of MT753X_MTRAP valid on every chip that can reach this code? mt753x_cpu_port_enable() is also called from mt7531_setup_common(), which covers MT7531, MT7988, EN7581 and AN7583. The MTRAP layout differs between chip families. For example, BIT(8) is MT7530_P6_DIS on MT7530 but MT7531_CHG_STRAP on MT7531. Every other MTRAP access in the driver is limited to MT7530-family code paths. On those other chips, could this set an undocumented strap bit instead of disabling loop detection? Should the write depend on priv->id?
quoted hunk ↗ jump to hunk
+ } else { + /* Not passthrough, enable DSA tag handling on CPU port. */ + regmap_write(priv->regmap, MT7530_PVC_P(port), PORT_SPEC_TAG); + }
[ ... ]
quoted hunk ↗ jump to hunk
@@ -1322,6 +1356,7 @@ static int mt7530_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { + int upstream_pt = dsa_switch_upstream_port(ds); struct dsa_port *dp = dsa_to_port(ds, port); struct mt7530_priv *priv = ds->priv;@@ -1332,13 +1367,38 @@ mt7530_port_enable(struct dsa_switch *ds, int port, * bridge. */ if (dsa_port_is_user(dp)) { - struct dsa_port *cpu_dp = dp->cpu_dp; + priv->ports[port].pm |= PCR_MATRIX(BIT(upstream_pt)); - priv->ports[port].pm |= PCR_MATRIX(BIT(cpu_dp->index));
[Severity: Medium]
Does this change behaviour on switches with more than one CPU port?
dsa_switch_upstream_port() returns the upstream port of the first available
port on the switch, not of the port being enabled:
include/net/dsa.h:dsa_switch_upstream_port() {
dsa_switch_for_each_available_port(dp, ds) {
return dsa_upstream_port(ds, dp->index);
}
...
}
mt753x_port_change_conduit() and mt7530_update_port_member() still base
priv->ports[port].pm on the port's own dp->cpu_dp.
Take an MT7531 with CPU ports 5 and 6. lan0, the first available port, is
moved to CPU port 6 with .port_change_conduit, and lan1 stays on CPU
port 5. If lan1 is then taken down and up, this code ORs BIT(6) into lan1's
pm, which already has BIT(5). mt753x_port_change_conduit() clears only the
old cpu_dp bit, so the extra bit is never removed.
Would lan1 then forward to both CPU ports?
Right now this is hidden, because the new probe check rejects dual-CPU
device trees that have user ports (see below). It becomes reachable once
that check is fixed.
quoted hunk ↗ jump to hunk
+ } else if (dsa_port_is_dsa(dp) && dp->index != upstream_pt) { + priv->ports[port].pm |= PCR_MATRIX(BIT(upstream_pt)); + + /* Should not happen */ + WARN_ON_ONCE(!priv->is_passthrough); +
[Severity: High] Can a DSA link port now reach mt7531_mac_config() and oops there? This patch makes DSA links a supported setup on MT7531 as well. That happens through this branch, where the passthrough switch programs its downstream link, and through the dsa_is_upstream_port() change in mt7531_setup_common(), where a downstream switch has a DSA link as its upstream port. However, mt7531_mac_config() still assumes that every RGMII port has a user netdev: if (phy_interface_mode_is_rgmii(interface)) { dp = dsa_to_port(ds, port); phydev = dp->user->phydev; mt7531_rgmii_setup(priv, interface, phydev); } In struct dsa_port, user and conduit share a union. CPU ports set conduit and user ports set user. DSA ports set neither, so dp->user is NULL for them. Take an MT7531BE without SGMII on port 5 (!priv->p5_sgmii), where port 5 is a DSA link with an RGMII phy-mode. This can be the downstream link of a passthrough MT7531BE or the uplink of a downstream one. The call chain is dsa_port_setup() -> dsa_port_enable_rt() -> phylink_start() -> phylink_major_config() -> mt753x_phylink_mac_config() -> mt7531_mac_config(). Would that dereference NULL during switch setup? Should mt7531_mac_config() look up the phydev only for user ports and pass NULL otherwise? mt7531_rgmii_setup() already handles !phydev by applying the delays implied by the phy-mode, which seems to be what a DSA link needs. [ ... ]
quoted hunk ↗ jump to hunk
+ /* Make the upstream port able to connect to the DSA port. + * This must be explicit because PORT_SPEC_TAG is unset. + */ + regmap_write(priv->regmap, MT7530_PCR_P(upstream_pt), + PCR_MATRIX(BIT(port)));
[Severity: Low] Is a full register write intended here? It clears every other field in the upstream port's PCR. That includes the fallback mode that mt753x_cpu_port_enable() set just before: regmap_update_bits(priv->regmap, MT7530_PCR_P(port), PCR_PORT_VLAN_MASK, MT7530_PORT_FALLBACK_MODE); It also clears PORT_PRI, the PCR EG_TAG field and the mirror bits. The write also bypasses priv->ports[upstream_pt].pm, which stays 0. Every other PCR writer uses regmap_update_bits(..., PCR_MATRIX_MASK, pm). As a result, the forwarding bit is protected only by the is_passthrough early returns and the check below. mt7530_port_disable() also has nothing to undo this PCR write or the MFC flood bits set for the DSA port. Would regmap_update_bits() with PCR_MATRIX_MASK, plus an update of the pm cache, be safer?
quoted hunk ↗ jump to hunk
} priv->ports[port].enable = true; - regmap_update_bits(priv->regmap, MT7530_PCR_P(port), PCR_MATRIX_MASK, - priv->ports[port].pm); + + /* In passthrough mode, CPU port mask is set above. */ + if (!(priv->is_passthrough && dp->index == upstream_pt)) + regmap_update_bits(priv->regmap, MT7530_PCR_P(port), + PCR_MATRIX_MASK, priv->ports[port].pm);
[ ... ]
quoted hunk ↗ jump to hunk
@@ -1862,6 +1949,10 @@ mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, { struct dsa_port *dp = dsa_to_port(ds, port); struct dsa_port *cpu_dp = dp->cpu_dp; + struct mt7530_priv *priv = ds->priv; + + if (priv->is_passthrough) + return 0;
[Severity: Medium] When this switch is the downstream one, is cpu_dp->index still a local port number? dsa_tree_setup_default_cpu() sets dp->cpu_dp for the downstream switch's ports to the CPU port on the upstream switch, so the index belongs to another switch. is_passthrough is false on the downstream switch, so the new early returns don't apply. The patch moves mt7530_port_enable(), the setup loops and mt7530_port_change_mtu() to the local upstream port. Three places still treat cpu_dp->index as a local port: mt7530_port_vlan_filtering(): mt7530_port_set_vlan_aware(ds, port); mt7530_port_set_vlan_aware(ds, cpu_dp->index); mt7530_update_port_member(): struct dsa_port *cpu_dp = dp->cpu_dp; u32 port_bitmap = BIT(cpu_dp->index); mt753x_conduit_state_change(): mask = BIT(cpu_dp->index); Suppose the downstream uplink port number differs from the upstream CPU port number. Would a bridge join, a bridge leave or a BR_ISOLATED change then overwrite p->pm and drop the uplink bit that mt7530_port_enable() set? If that index happens to be a local user port, frames could also be forwarded to it without the isolation check. In the same setup, would VLAN filtering make an unrelated local port VLAN-aware instead of the uplink? Would trapped frames go to the wrong local CPU_PORT in MT753X_MFC? The patch only updates the comment in mt753x_conduit_state_change(). The EN751221 hardware in this series seems to work only because both indices are 6. [ ... ]
quoted hunk ↗ jump to hunk
@@ -3399,6 +3505,55 @@ static int mt7988_setup(struct dsa_switch *ds) return mt7531_setup_common(ds); } +/* 1 if passthrough, negative if error. */ +static int mt753x_check_passthrough(struct device *dev) +{
[ ... ]
quoted hunk ↗ jump to hunk
+ for_each_available_child_of_node(ports, port) { + struct device_node *link; + + enabled_ports++; + + link = of_parse_phandle(port, "ethernet", 0); + if (!link) + link = of_parse_phandle(port, "link", 0); + + if (!link) + continue; + + of_node_put(link); + + passthrough_ports++; + } + + of_node_put(ports); + + /* + * A switch is considered passthrough if exactly two available + * ports have an "ethernet" or "link" phandle. + */ + if (passthrough_ports > 2 || + (passthrough_ports == 2 && enabled_ports != 2) + ) { + dev_err(dev, "Only two ports allowed in passthrough mode\n"); + return -EINVAL; + }
[Severity: High] Will this break probe on existing boards that have two CPU ports? CPU ports (ethernet) and DSA links (link) share one counter. Two of them plus any user port returns -EINVAL, and mt7530_probe_common() then fails the whole probe. Several in-tree device trees have user ports plus port@5 with ethernet = <&gmac1> and port@6 with ethernet = <&gmac0>. Examples are mt7622-bananapi-bpi-r64.dts, mt7623n-bananapi-bpi-r2.dts, mt7986a/b-rfb.dts and the MT7621 boards based on mt7621.dtsi. On BPI-R64 this gives passthrough_ports == 2 and enabled_ports == 7, since of_parse_phandle() returns the gmac node whatever its status. Would all of these boards lose switch networking with "Only two ports allowed in passthrough mode"? The check is also wrong in the other direction. mt7621.dtsi with swp0-4 disabled has two CPU ports and nothing else, so it would be treated as passthrough without any DSA link. A normal cascade with a CPU port, a DSA link and user ports would also be rejected. Later patches in the series don't seem to change this function. Should the check require exactly one ethernet port and one link port, and leave other layouts to the existing code paths? [Severity: Medium] Does passthrough receive work only if the downstream switch has DSA switch index 0? The MTK tag has no switch ID, and mtk_tag_rcv() hardcodes index 0: net/dsa/tag_mtk.c:mtk_tag_rcv() { ... skb->dev = dsa_conduit_find_user(dev, 0, port); if (!skb->dev) { kfree_skb(skb); return NULL; } ... } dsa_conduit_find_user() requires an exact match on dp->ds->index. The natural numbering gives the CPU-attached upstream switch dsa,member = <0 0>. With that numbering, every frame from the downstream switch is looked up on the upstream switch, finds no user port, and is freed without any message. Could this requirement be checked here, or at least documented in the binding?
quoted hunk ↗ jump to hunk
+ + return passthrough_ports == 2; +}
-- Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261028011317.1557238-1-cjd%40cjdns.fr