Re: [PATCH net-next 10/12] net: stmmac: tc956x: add TC956x/QPS615 support

Alex Elder <[email protected]> Fri, 29 May 2026 21:04:45 -0500
Newsgroups dev.linux.lists.sashiko,org.kernel.vger.bpf
Message-ID <[email protected]>
On 5/2/26 10:56 AM, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
> - [High] The RX queue IRQ mapping loop incorrectly checks `res->tx_irq[i]` instead of `res->rx_irq[i]` for failures.
> - [High] DMA address truncation on 32-bit architectures leads to corrupted hardware DMA programming.
> - [High] Mapped IRQs are never disposed, leaking descriptors and causing a WARN on device removal.
> - [Medium] Hardware clocks and resets are leaked on `probe()` and `mac_enable()` error paths.
> - [Medium] OF node reference leak for `mdio_node` on probe error paths and device removal.

All of the reports here are valid, and all will be addressed
in the next version of the patch (as described further below).

> --
> 
> commit e138cc3787ee995063c093b9ca94f6ca453bbcda
> Author: Daniel Thompson <[email protected]>
> Subject: net: stmmac: tc956x: add TC956x/QPS615 support
> 
> This commit adds support for the Toshiba TC956x eMAC/MSIGEN combination,
> implemented as a driver for an auxiliary bus device that bridges TC956x
> level interrupts to PCIe MSIs.
> 
>> diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-tc956x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-tc956x.c
>> new file mode 100644
>> index 000000000000..27ff83dd3bd9
>> --- /dev/null
>> +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-tc956x.c
> 
> [ ... ]
> 
>> +static int tc956x_mac_enable(struct tc956x_data *td)
>> +{
>> +	const struct tc956x_chip *chip = td->auxbus_data->chip;
>> +	struct plat_stmmacenet_data *plat = td->plat;
>> +	u32 id = td->auxbus_data->mac_id;
>> +	int ret;
>> +
>> +	tc956x_clock_enable(chip, id, MAC_CLOCK_TX);
>> +	tc956x_clock_enable(chip, id, MAC_CLOCK_RX);
>> +	tc956x_clock_enable(chip, id, MAC_CLOCK_ALL);
>> +	if (id)
>> +		tc956x_clock_enable(chip, id, MAC_CLOCK_RMII);
>> +
>> +	/* Set the speed related registers */
>> +	ret = tc956x_mac_configure(td, plat->max_speed);
>> +	if (ret)
>> +		return ret;
> 
> If tc956x_mac_configure() fails, will this leak the clocks that were just
> enabled since tc956x_clock_disable() is not called?

This is a legitimate issue.  However in the next version
of this patch, the call to tc956x_mac_configure() is no
longer necessary.  As as a result, the issue will no
longer occur, and in fact tc956x_mac_enable() becomes a
void function, simplifying callers as well.

> 
> [ ... ]
> 
>> +static void tc956x_dma_init_rx_chan(struct stmmac_priv *priv,
>> +				    void __iomem *ioaddr,
>> +				    struct stmmac_dma_cfg *dma_cfg,
>> +				    dma_addr_t phy, u32 chan)
>> +{
>> +	dma_addr_t translated = phy + TC956X_SLV00_SRC_ADDR;
> 
> If dma_addr_t is 32-bit on the target architecture, will adding
> TC956X_SLV00_SRC_ADDR (0x1000000000ULL) to phy truncate the result and cause
> the upper 32 bits to be 0?
> 
> Should translated be explicitly typed as u64? The same pattern appears
> in tc956x_dma_init_tx_chan(), tc956x_desc_set_addr(), and
> tc956x_desc_set_sec_addr().

Here, the translated variable (in all four functions) will be
converted to a u64, which avoids the problem even if the
size of a dma_addr_t is 32 bits.

> 
> [ ... ]
> 
>> +static int tc956x_dwmac_parse_dt(struct tc956x_data *td)
>> +{
>> +	struct device_node *mdio_node;
>> +	struct device *dev = td->dev;
>> +	struct device_node *np;
>> +
>> +	np = dev_of_node(dev);
>> +	if (!np)
>> +		return dev_err_probe(dev, -EINVAL, "no devicetree node\n");
>> +
>> +	/* Find the MDIO bus */
>> +	for_each_child_of_node(np, mdio_node) {
>> +		if (of_device_is_compatible(mdio_node,
>> +					    "snps,dwmac-mdio"))
>> +			break;
>> +	}
>> +
>> +	/* Pass the MDIO bus (if there is one) to the core driver */
>> +	if (mdio_node) {
>> +		td->plat->mdio_node = mdio_node;
>> +		td->plat->mdio_bus_data->needs_reset = true;
>> +	}
>> +
>> +	return 0;
>> +}
> 
> Since the loop breaks early when a compatible node is found, does this leak
> the reference to mdio_node?
> 
> Is a call to of_node_put() needed in tc956x_dwmac_remove() and on probe
> error paths?

Yes, this did leak the reference to the mdio_node.  The
next version of this patch addresses this in the caller,
dropping the reference on any error, and dropping it also
in the driver remove function.

> 
> [ ... ]
> 
>> +static int tc956x_stmmac_resources_init(struct tc956x_data *td,
>> +					struct stmmac_resources *res)
>> +{
>> +	struct irq_domain *irq_domain = td->irq_domain;
>> +	u32 i;
>> +
>> +	res->irq = irq_create_mapping(irq_domain, HWIRQ_EVENT);
>> +	if (!res->irq)
>> +		return -EINVAL;
>> +
>> +	for (i = 0; i < td->plat->tx_queues_to_use; i++) {
>> +		res->tx_irq[i] = irq_create_mapping(irq_domain, HWIRQ_TX0 + i);
>> +		if (!res->tx_irq[i])
>> +			return -EINVAL;
>> +	}
>> +
>> +	for (i = 0; i < td->plat->rx_queues_to_use; i++) {
>> +		res->rx_irq[i] = irq_create_mapping(irq_domain, HWIRQ_RX0 + i);
>> +		if (!res->tx_irq[i])
>> +			return -EINVAL;
>> +	}
> 
> Should this be checking res->rx_irq[i] instead of res->tx_irq[i]?

Yes, Ayaan mentioned this as well and it has been fixed.

> 
> If tx_irq[i] was previously set during the TX mapping loop, this check won't
> catch RX mapping failures and uninitialized IRQs could be left in the
> resource structure.
> 
> [ ... ]
> 
>> +static int tc956x_dwmac_probe(struct auxiliary_device *adev,
>> +			      const struct auxiliary_device_id *id)
>> +{
> 
> [ ... ]
> 
>> +	ret = tc956x_mac_enable(td);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "failed to enable MAC\n");
>> +
>> +	ret = stmmac_dvr_probe(dev, td->plat, &res);
>> +	if (ret)
>> +		return dev_err_probe(dev, ret, "failed stmmac probe\n");
>> +
>> +	return 0;
>> +}
> 
> Does this error path need to call tc956x_mac_disable(td) to unwind the
> hardware state if stmmac_dvr_probe() fails?

Yes, and this will be addressed in the next version of this patch.

> 
> [ ... ]
> 
>> +static void tc956x_dwmac_remove(struct auxiliary_device *adev)
>> +{
>> +	struct device *dev = &adev->dev;
>> +	struct net_device *ndev = dev_get_drvdata(dev);
>> +	struct stmmac_priv *priv = netdev_priv(ndev);
>> +	struct tc956x_data *td = priv->plat->bsp_priv;
>> +
>> +	stmmac_dvr_remove(dev);
>> +	tc956x_mac_disable(td);
>> +}
> 
> Are the IRQs created by irq_create_mapping() leaking here?

A new function tc956x_stmmac_resources_exit() will be created to
release the mappings created by tc956x_stmmac_resources_init().

					-Alex

> 
> There appear to be no calls to irq_dispose_mapping() on device removal or
> probe error paths. While IRQ_DOMAIN_FLAG_DESTROY_GC frees the generic
> chip data, the descriptors might leak and trigger a warning in
> irq_domain_remove().
>