[RFC PATCH net-next v2 3/5] net: dsa: qca8k: serialize CPU MAC pause during MTU changes
From: Yongzhao Chen <hidden>
Date: 2026-09-22 20:27:14
Subsystem:
networking drivers, networking [dsa], qualcomm atheros qca8k dsa switch driver, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Andrew Lunn, Vladimir Oltean, Christian Marangi, Linus Torvalds
The global maximum frame size must be changed with CPU MACs disabled. The old sequence only handled ports 0 and 6, leaving an internal PHY CPU port enabled while the register was updated. Include enabled internal CPU ports in the pause. Serialize the whole MTU update with port enable/disable and phylink link-up/down callbacks, so a link-up cannot re-enable a CPU MAC during the frame-size write. Read and restore each port's original TXMAC/RXMAC bits instead of enabling both unconditionally; a link-down port remains down and LINK_AUTO is preserved. Abort before updating the frame size if a status read or MAC pause fails, attempt to restore any port already touched, and report a restore error when the frame-size write itself succeeded. Keep the existing treatment of ports 0 and 6, including when either is a user port. Assisted-by: LLM Signed-off-by: Yongzhao Chen <redacted> --- drivers/net/dsa/qca/qca8k-8xxx.c | 3 ++ drivers/net/dsa/qca/qca8k-common.c | 79 ++++++++++++++++++++++++------ drivers/net/dsa/qca/qca8k.h | 2 + 3 files changed, 69 insertions(+), 15 deletions(-)
diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c
index 17704cb53..787f30495 100644
--- a/drivers/net/dsa/qca/qca8k-8xxx.c
+++ b/drivers/net/dsa/qca/qca8k-8xxx.c@@ -1486,7 +1486,9 @@ qca8k_phylink_mac_link_up(struct phylink_config *config, reg |= QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + mutex_lock(&priv->port_status_mutex); qca8k_write(priv, QCA8K_REG_PORT_STATUS(port), reg); + mutex_unlock(&priv->port_status_mutex); } static struct qca8k_pcs *pcs_to_qca8k_pcs(struct phylink_pcs *pcs)
@@ -2119,6 +2121,7 @@ qca8k_sw_probe(struct mdio_device *mdiodev) priv->ds->ops = &qca8k_switch_ops; priv->ds->phylink_mac_ops = &qca8k_phylink_mac_ops; mutex_init(&priv->reg_mutex); + mutex_init(&priv->port_status_mutex); dev_set_drvdata(&mdiodev->dev, priv); return dsa_register_switch(priv->ds);
diff --git a/drivers/net/dsa/qca/qca8k-common.c b/drivers/net/dsa/qca/qca8k-common.c
index 13005f10e..a584ddf72 100644
--- a/drivers/net/dsa/qca/qca8k-common.c
+++ b/drivers/net/dsa/qca/qca8k-common.c@@ -463,7 +463,8 @@ int qca8k_mib_init(struct qca8k_priv *priv) return ret; } -void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +static void qca8k_port_set_status_locked(struct qca8k_priv *priv, int port, + int enable) { u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC;
@@ -477,6 +478,13 @@ void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(port), mask); } +void qca8k_port_set_status(struct qca8k_priv *priv, int port, int enable) +{ + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, enable); + mutex_unlock(&priv->port_status_mutex); +} + void qca8k_get_strings(struct dsa_switch *ds, int port, u32 stringset, uint8_t *data) {
@@ -751,8 +759,10 @@ int qca8k_port_enable(struct dsa_switch *ds, int port, { struct qca8k_priv *priv = ds->priv; - qca8k_port_set_status(priv, port, 1); + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, 1); priv->port_enabled_map |= BIT(port); + mutex_unlock(&priv->port_status_mutex); if (dsa_is_user_port(ds, port)) phy_support_asym_pause(phy);
@@ -764,14 +774,20 @@ void qca8k_port_disable(struct dsa_switch *ds, int port) { struct qca8k_priv *priv = ds->priv; - qca8k_port_set_status(priv, port, 0); + mutex_lock(&priv->port_status_mutex); + qca8k_port_set_status_locked(priv, port, 0); priv->port_enabled_map &= ~BIT(port); + mutex_unlock(&priv->port_status_mutex); } int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { struct qca8k_priv *priv = ds->priv; - int ret; + u32 mask = QCA8K_PORT_STATUS_TXMAC | QCA8K_PORT_STATUS_RXMAC; + u32 status[QCA8K_NUM_PORTS] = { 0 }; + u32 ports; + u32 stopped = 0; + int ret, restore_ret, i; /* We have only have a general MTU setting. * DSA always set the CPU port's MTU to the largest MTU of the user
@@ -784,25 +800,58 @@ int qca8k_port_change_mtu(struct dsa_switch *ds, int port, int new_mtu) /* To change the MAX_FRAME_SIZE the cpu ports must be off or * the switch panics. - * Turn off both cpu ports before applying the new value to prevent - * this. + * Include internal PHY CPU ports as well as the two MAC-only ports. + * Toggle only MAC enables, preserving the phylink link-control mode. */ - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 0); + ports = BIT(0) | BIT(6); + for (i = 1; i < 6; i++) + if (dsa_is_cpu_port(ds, i)) + ports |= BIT(i); - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 0); + mutex_lock(&priv->port_status_mutex); + ports &= priv->port_enabled_map; + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + if (!(ports & BIT(i))) + continue; + + ret = regmap_read(priv->regmap, QCA8K_REG_PORT_STATUS(i), + &status[i]); + if (ret) + goto unlock; + } + + for (i = 0; i < QCA8K_NUM_PORTS; i++) { + if (!(ports & BIT(i)) || !(status[i] & mask)) + continue; + + stopped |= BIT(i); + ret = regmap_clear_bits(priv->regmap, QCA8K_REG_PORT_STATUS(i), + mask); + if (ret) + goto restore; + } /* Include L2 header / FCS length */ ret = qca8k_write(priv, QCA8K_MAX_FRAME_SIZE, new_mtu + ETH_HLEN + ETH_FCS_LEN); - if (priv->port_enabled_map & BIT(0)) - qca8k_port_set_status(priv, 0, 1); - - if (priv->port_enabled_map & BIT(6)) - qca8k_port_set_status(priv, 6, 1); +restore: + for (i = 0; i < QCA8K_NUM_PORTS; i++) + if (stopped & BIT(i)) { + restore_ret = regmap_update_bits(priv->regmap, + QCA8K_REG_PORT_STATUS(i), + mask, status[i] & mask); + if (restore_ret) { + dev_err(priv->dev, "failed to restore MAC state on port %d: %d\n", + i, restore_ret); + if (!ret) + ret = restore_ret; + } + } +unlock: + mutex_unlock(&priv->port_status_mutex); return ret; }
diff --git a/drivers/net/dsa/qca/qca8k.h b/drivers/net/dsa/qca/qca8k.h
index 956338893..6cb060204 100644
--- a/drivers/net/dsa/qca/qca8k.h
+++ b/drivers/net/dsa/qca/qca8k.h@@ -459,6 +459,8 @@ struct qca8k_priv { struct mii_bus *internal_mdio_bus; struct dsa_switch *ds; struct mutex reg_mutex; + /* Serializes port MAC state changes with global MTU updates. */ + struct mutex port_status_mutex; struct device *dev; struct gpio_desc *reset_gpio; struct net_device *mgmt_conduit; /* Track if mdio/mib Ethernet is available */
--
2.43.0