From: sashiko-bot@kernel.org
To: "Caleb James DeLisle" <cjd@cjdns.fr>
Cc: devicetree@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v4 5/7] net: dsa: mediatek: support MDIO switch downstream of MMIO switch
Date: Fri, 30 Oct 2026 19:22:21 +0000 [thread overview]
Message-ID: <sashiko-outbox-191440@kernel.org> (raw)
In-Reply-To: <20261029184202.1859843-6-cjd@cjdns.fr>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] net: dsa: mt7530: broken PHY IRQs due to late mt7530_setup_mdio_irq()
- [High] net: dsa: mt7530: premature and re-entrant mt7530_setup_mdio() in probe
- [High] net: dsa: mt7530: unchecked mt7530_setup_mdio() error in mt7988_probe()
- [High] net: dsa: mt7530: reg_mutex lockdep recursion with downstream switch
--
commit 51caf2efd42e68935e207fbaf1614c12f27f5efa
Author: Caleb James DeLisle <cjd@cjdns.fr>
net: dsa: mediatek: support MDIO switch downstream of MMIO switch
This commit exports mt7530_setup_mdio(), makes it idempotent via a check on
priv->child_bus, moves mt7530_setup_mdio_irq() to mt753x_setup(), and calls
mt7530_setup_mdio() from mt7988_probe() after dsa_register_switch() to allow a
downstream MDIO switch to probe and complete the DSA switch tree.
> diff --git a/drivers/net/dsa/mt7530-mmio.c b/drivers/net/dsa/mt7530-mmio.c
> index cf3bdb23a2d54..746a0a4622497 100644
> --- a/drivers/net/dsa/mt7530-mmio.c
> +++ b/drivers/net/dsa/mt7530-mmio.c
> @@ -61,7 +61,21 @@ mt7988_probe(struct platform_device *pdev)
> if (IS_ERR(priv->regmap))
> return PTR_ERR(priv->regmap);
>
> - return dsa_register_switch(priv->ds);
> + ret = dsa_register_switch(priv->ds);
> +
> + if (ret)
> + return ret;
> +
> + /*
> + * If not all switches in the hierarchy have been probed,
> + * dsa_register_switch() returns 0 without calling mt753x_setup().
> + * Call mt7530_setup_mdio() because there's a chance that the missing
> + * switch is on the MDIO bus. Do not call this before
> + * dsa_register_switch() as it may exit with an error.
> + */
> + mt7530_setup_mdio(priv);
> +
> + return 0;
[Severity: High]
What happens if mt7530_setup_mdio(priv) fails here in mt7988_probe(), for
example with -ENOMEM from devm_mdiobus_alloc() or -EPROBE_DEFER from
devm_of_mdiobus_register()?
Because the return value of mt7530_setup_mdio(priv) is ignored and
mt7988_probe() returns 0, the driver core will treat probe as successful and
never retry deferred probing, leaving the downstream switch unprobed, the DSA
tree permanently incomplete, and priv->ds registered in the switch tree
holding allocated dsa_port instances and conduit net_device references without
unwinding via mt7530_remove_common(priv) / dsa_unregister_switch(priv->ds).
Should mt7988_probe() check the return value of mt7530_setup_mdio(priv) and
unregister the switch on error?
[Severity: High]
Also, when a downstream MDIO switch is registered on the MMIO switch's MDIO
bus (upstream_priv->child_bus), won't register accesses on the downstream
switch trigger a lockdep recursive locking warning on priv->reg_mutex?
Both the upstream MMIO switch and the downstream MDIO switch initialize
priv->reg_mutex at the same callsite in mt7530_probe_common():
drivers/net/dsa/mt7530.c:mt7530_probe_common() {
...
mutex_init(&priv->reg_mutex);
...
}
This assigns both mutexes to the same static lockdep class. When any operation
on the downstream switch holds mutex_lock(&downstream_priv->reg_mutex) at
subclass 0 and performs a regmap access, such as in mt7530_port_disable():
drivers/net/dsa/mt7530.c:mt7530_port_disable() {
mutex_lock(&priv->reg_mutex);
priv->ports[port].enable = false;
regmap_update_bits(priv->regmap, MT7530_PCR_P(port), PCR_MATRIX_MASK,
PCR_MATRIX_CLR);
mutex_unlock(&priv->reg_mutex);
}
the underlying MDIO transfer goes through mt7530_regmap_read() /
mt7530_regmap_write() on downstream_priv->bus (which is
upstream_priv->child_bus) and calls mt7531_ind_c22_phy_read() /
mt7531_ind_c22_phy_write(), which acquires mutex_lock(&upstream_priv->reg_mutex)
at subclass 0 of the same lockdep class:
drivers/net/dsa/mt7530.c:mt7531_ind_c22_phy_write() {
mutex_lock(&priv->reg_mutex);
...
}
Does priv->reg_mutex need a separate lockdep key or nesting annotation when
switches are stacked this way?
> diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c
> index d45388e2efc83..24e4485624d39 100644
> --- a/drivers/net/dsa/mt7530.c
> +++ b/drivers/net/dsa/mt7530.c
> @@ -2509,7 +2509,7 @@ mt7530_free_mdio_irq(struct mt7530_priv *priv)
> }
> }
>
> -static int
> +int
> mt7530_setup_mdio(struct mt7530_priv *priv)
> {
> struct device_node *mnp, *np = priv->dev->of_node;
> @@ -2519,6 +2519,10 @@ mt7530_setup_mdio(struct mt7530_priv *priv)
> static int idx;
> int ret = 0;
>
> + /* Already done */
> + if (priv->child_bus)
> + return 0;
> +
> mnp = of_get_child_by_name(np, "mdio");
>
> if (mnp && !of_device_is_available(mnp))
> @@ -2543,9 +2547,6 @@ mt7530_setup_mdio(struct mt7530_priv *priv)
> bus->parent = dev;
> bus->phy_mask = ~ds->phys_mii_mask;
>
> - if (priv->irq_domain && !mnp)
> - mt7530_setup_mdio_irq(priv);
> -
> priv->child_bus = bus;
[Severity: High]
When mt7988_probe() calls mt7530_setup_mdio(priv) after dsa_register_switch()
returns 0 on an incomplete DSA tree, neither dsa_switch_setup() nor
mt753x_setup() has executed yet. Can calling mt7530_setup_mdio() at that point
cause four ordering and re-entrancy regressions?
1. Hardware reset and PHY clock setup in mt7988_setup() have not run yet when
mt7988_probe() calls mt7530_setup_mdio(priv). In mt7988_setup():
drivers/net/dsa/mt7530.c:mt7988_setup() {
/* Reset the switch */
reset_control_assert(priv->rstc);
usleep_range(20, 50);
reset_control_deassert(priv->rstc);
usleep_range(20, 50);
/* AN7583 require additional tweak to CONN_CFG */
if (priv->id == ID_AN7583)
regmap_update_bits(priv->regmap, AN7583_GEPHY_CONN_CFG, ...);
/* Reset the switch PHYs */
regmap_write(priv->regmap, MT7530_SYS_CTRL, SYS_CTRL_PHY_RST);
}
MDIO accesses in mt7530_setup_mdio() happen before priv->rstc is deasserted
and before AN7583_GEPHY_CONN_CFG is configured. Then, when the downstream
switch registers and completes the DSA tree, dsa_tree_setup() invokes
mt753x_setup() -> mt7988_setup() on the upstream switch, asserting priv->rstc
and writing SYS_CTRL_PHY_RST after MDIO devices and PHYs on the bus have
already been probed.
2. mt7530_setup_irq(priv) is only called from mt753x_setup(), so
priv->irq_domain is still NULL when mt7988_probe() calls
mt7530_setup_mdio(priv). Any child PHY or switch node under the mdio DT node
whose interrupt-parent is the upstream switch will fail fwnode_irq_get() with
-EPROBE_DEFER during devm_of_mdiobus_register().
3. ds->phys_mii_mask is only populated by dsa_switch_setup():
net/dsa/dsa.c:dsa_switch_setup() {
...
ds->phys_mii_mask |= dsa_user_ports(ds);
...
}
When mt7988_probe() calls mt7530_setup_mdio(priv) on an incomplete tree,
ds->phys_mii_mask is still 0, so bus->phy_mask = ~ds->phys_mii_mask is
0xffffffff. If there is no mdio child DT node (mnp == NULL),
devm_of_mdiobus_register(dev, bus, NULL) scans no PHYs and sets
priv->child_bus = bus. When the tree later completes and mt753x_setup() calls
mt7530_setup_mdio(priv), it returns 0 immediately because priv->child_bus is
already set, leaving ds->user_mii_bus with no PHYs.
4. Because priv->child_bus = bus is set before devm_of_mdiobus_register(dev,
bus, mnp) finishes iterating all child nodes of mnp in
__of_mdiobus_parse_phys(), probing the downstream switch child node
synchronously calls:
mt7530_probe()
-> dsa_register_switch()
-> dsa_tree_setup()
-> dsa_tree_setup_switches()
-> mt753x_setup()
-> mt7530_setup_mdio()
That re-entrant mt7530_setup_mdio() call sees priv->child_bus != NULL and
returns 0 while the outer __of_mdiobus_parse_phys() loop is still mid-iteration
and has not yet registered any PHY child nodes listed after the downstream
switch node in mnp. dsa_tree_setup_ports() then runs immediately and fails
phylink_of_phy_connect() with -ENODEV for those upstream user ports.
[ ... ]
> @@ -3386,10 +3388,17 @@ mt753x_setup(struct dsa_switch *ds)
> if (ret)
> return ret;
>
> + /*
> + * This must be called before mt753x_setup() returns because the DSA
> + * core synchronously sets up ports and expects PHYs to be operational.
> + */
> ret = mt7530_setup_mdio(priv);
> if (ret)
> return ret;
>
> + if (priv->irq_domain && ds->user_mii_bus)
> + mt7530_setup_mdio_irq(priv);
[Severity: High]
Doesn't moving mt7530_setup_mdio_irq(priv) after mt7530_setup_mdio(priv) break
PHY interrupts for all switches using ds->user_mii_bus?
In mt7530_setup_mdio(), devm_mdiobus_alloc() initializes all bus->irq[i]
entries to PHY_POLL. When mnp is NULL, devm_of_mdiobus_register(dev, bus, NULL)
synchronously scans the bus and creates the phy_device instances via:
devm_of_mdiobus_register()
-> __of_mdiobus_register()
-> __mdiobus_register()
-> mdiobus_scan_bus_c22()
-> mdiobus_scan_c22()
-> get_phy_device()
-> phy_device_create()
During phy_device_create(), phylib copies bus->irq[addr] into each struct
phy_device:
drivers/net/phy/phy_device.c:phy_device_create() {
...
dev->irq = bus->irq[addr];
...
}
Because mt7530_setup_mdio_irq(priv) now runs after mt7530_setup_mdio(priv) has
already registered the bus and created the PHY devices, bus->irq[addr] is still
PHY_POLL at creation time, so every PHY is created with phydev->irq == PHY_POLL
and populating ds->user_mii_bus->irq[p] afterwards has no effect on the
already-created PHY devices (while leaking the mapped IRQ descriptors).
In addition, the error path in mt7530_setup_mdio() still calls
mt7530_free_mdio_irq(priv) when devm_of_mdiobus_register() fails:
drivers/net/dsa/mt7530.c:mt7530_setup_mdio() {
...
ret = devm_of_mdiobus_register(dev, bus, mnp);
if (ret) {
priv->child_bus = NULL;
dev_err(dev, "failed to register MDIO bus: %d\n", ret);
if (priv->irq_domain && !mnp)
mt7530_free_mdio_irq(priv);
}
}
even though mt7530_setup_mdio_irq(priv) was not called before
devm_of_mdiobus_register().
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029184202.1859843-1-cjd@cjdns.fr?part=5
next prev parent reply other threads:[~2026-10-30 19:22 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-29 18:41 [PATCH v4 0/7] net: dsa: mt7530: support EcoNet EN751221 Caleb James DeLisle
2026-10-29 18:41 ` [PATCH v4 1/7] net: dsa: mt7530: get ctrl phy addr using a function Caleb James DeLisle
2026-10-29 18:41 ` [PATCH v4 2/7] dt-bindings: net: dsa: mediatek,mt7530: describe 'passthrough' hierarchy Caleb James DeLisle
2026-10-29 18:41 ` [PATCH v4 3/7] net: dsa: mediatek: add support for passthrough mode Caleb James DeLisle
2026-10-30 19:22 ` sashiko-bot
2026-10-29 18:41 ` [PATCH v4 4/7] net: dsa: mediatek: support PLL setup on MMIO MT7530 Caleb James DeLisle
2026-10-30 19:22 ` sashiko-bot
2026-10-29 18:42 ` [PATCH v4 5/7] net: dsa: mediatek: support MDIO switch downstream of MMIO switch Caleb James DeLisle
2026-10-30 19:22 ` sashiko-bot [this message]
2026-10-29 18:42 ` [PATCH v4 6/7] dt-bindings: net: dsa: mediatek,mt7530: add EcoNet EN751221 switches Caleb James DeLisle
2026-10-29 18:42 ` [PATCH v4 7/7] net: dsa: mediatek: support EN751221 switch Caleb James DeLisle
2026-10-30 19:22 ` sashiko-bot
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=sashiko-outbox-191440@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=cjd@cjdns.fr \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
/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