Re: [PATCH net-next v3 10/12] net: airoha: Do not stop GDM port if it is shared

Jakub Kicinski
2026-04-10T02:57:39Z

This is an AI-generated review of your patch. The human sending this
email says "For your consideration."
---
net: airoha: Do not stop GDM port if it is shared

Theoretically, in the current codebase, two independent net_devices can
be connected to the same GDM port so we need to check the GDM port is not
used by any other running net_device before setting the forward
configuration to FE_PSE_PORT_DROP.

Tested-by: Xuegang Lu <xuegang.lu@airoha.com>

> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index fd27ba13029bd..5b0cd37b155e4 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c

[ ... ]

> @@ -1688,18 +1693,18 @@ static int airoha_dev_stop(struct net_device *netdev)
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> struct airoha_gdm_port *port = dev->port;
> struct airoha_qdma *qdma = dev->qdma;
> - int i, err;
> + int i;
>
> netif_tx_disable(netdev);
> - err = airoha_set_vip_for_gdm_port(dev, false);
> - if (err)
> - return err;
> -
> for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++)
> netdev_tx_reset_subqueue(netdev, i);
>
> - airoha_set_gdm_port_fwd_cfg(qdma->eth, REG_GDM_FWD_CFG(port->id),
> - FE_PSE_PORT_DROP);
> + if (atomic_dec_and_test(&port->users)) {
> + airoha_set_vip_for_gdm_port(dev, false);
> + airoha_set_gdm_port_fwd_cfg(qdma->eth,
> + REG_GDM_FWD_CFG(port->id),
> + FE_PSE_PORT_DROP);
> + }
>
> if (atomic_dec_and_test(&qdma->users)) {
> airoha_qdma_clear(qdma, REG_QDMA_GLOBAL_CFG,
> GLOBAL_CFG_TX_DMA_EN_MASK |
> GLOBAL_CFG_RX_DMA_EN_MASK);
>
> for (i = 0; i < ARRAY_SIZE(qdma->q_tx); i++) {
> if (!qdma->q_tx[i].ndesc)
> continue;
>
> airoha_qdma_cleanup_tx_queue(&qdma->q_tx[i]);
> }
> }

This problem wasn't introduced by this patch, but does the QDMA teardown
path have a DMA corruption vulnerability here?

When GLOBAL_CFG_TX_DMA_EN_MASK is cleared, it fails to poll
GLOBAL_CFG_TX_DMA_BUSY_MASK, proceeding to unmap memory while the hardware
DMA engine may still be processing in-flight descriptors.

Furthermore, airoha_qdma_cleanup_tx_queue() unmaps the DMA addresses and
frees the SKBs, but it does not appear to clear the physical hardware
descriptors or reset the hardware ring pointers (REG_TX_CPU_IDX /
REG_TX_DMA_IDX).

When the device is later reopened in airoha_dev_open(),
GLOBAL_CFG_TX_DMA_EN_MASK is set again without re-initializing the rings.
Will the hardware DMA engine resume fetching from its previous index,
processing stale descriptors whose backing memory was already unmapped
and freed?

[ ... ]

> @@ -1851,10 +1856,22 @@ static void airoha_dev_get_stats64(struct net_device *netdev,
> static int airoha_dev_change_mtu(struct net_device *netdev, int mtu)
> {
> struct airoha_gdm_dev *dev = netdev_priv(netdev);
> + u32 cur_len, len = ETH_HLEN + mtu + ETH_FCS_LEN;
> struct airoha_gdm_port *port = dev->port;
> - u32 len = ETH_HLEN + mtu + ETH_FCS_LEN;
> struct airoha_eth *eth = dev->eth;
>
> + cur_len = FIELD_GET(GDM_LONG_LEN_MASK,
> + airoha_fe_rr(eth, REG_GDM_LEN_CFG(port->id)));
> + if (len < cur_len) {
> + u8 port_refcnt = atomic_read(&port->users);
> +
> + /* We can decrease the device MTU just if the GDM port is
> + * not shared or if the other device is not running.
> + */
> + if (port_refcnt > 1 || (port_refcnt && !netif_running(netdev)))
> + return -EBUSY;
> + }
> +

Does this MTU decrease validation create a state trap on shared ports?

If both eth0 and eth1 (sharing a port) are up, and a user increases eth0 MTU
to 9000, the hardware MTU updates to 9000. If the user then tries to lower
eth0 MTU back to 1500, len < cur_len evaluates to true. Because eth1 is also
up (port_refcnt > 1), this returns -EBUSY.

This would prevent either interface from decreasing its MTU without fully
bringing down the sibling interface.

Should the software netdev->mtu be decoupled from the hardware validation,
allowing valid MTU changes in software while dynamically programming the
hardware MTU to the maximum of all currently up interfaces on the shared
port?