RE: [PATCH v6 net-next 10/15] net: dsa: netc: introduce NXP NETC switch driver for i.MX94

Wei Fang <[email protected]> Mon, 11 May 2026 07:17:14 +0000
Newsgroups dev.linux.lists.sashiko,dev.linux.lists.imx,org.kernel.vger.linux-devicetree,org.kernel.vger.netdev
Message-ID <PA4PR04MB750282C2004F177F08BF602A88382@PA4PR04MB7502.eurprd04.prod.outlook.com>
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to
> consider:
> - [High] devres-managed functions are incorrectly used inside the DSA `.setup`
> callback, leading to memory leaks and MDIO bus registration failures if the DSA
> tree is unbound and rebound.
> - [High] The driver's PCI shutdown callback fails to stop hardware DMA rings and
> leaves PCI bus mastering enabled.
> - [Low] Clearing the PCI driver data via pci_set_drvdata(pdev, NULL) in the
> shutdown callback breaks potential subsequent unbind operations.
> --
> 
> commit bee0164d1f3476eab41d58efbe3baf540bb03cda
> Author: Wei Fang <[email protected]>
> 
> net: dsa: netc: introduce NXP NETC switch driver for i.MX94
> 
> This commit introduces the initial NXP NETC Ethernet switch driver for
> the i.MX94 series. It provides basic probe and remove functionality, as
> well as port capabilities and basic network setup.
> 
> > diff --git a/drivers/net/dsa/netc/netc_main.c
> b/drivers/net/dsa/netc/netc_main.c
> > index 0000000000000..8e3a3230226c3 100644
> > --- /dev/null
> > +++ b/drivers/net/dsa/netc/netc_main.c
> 
> [ ... ]
> 
> > +static int netc_port_create_emdio_bus(struct netc_port *np,
> > +				      struct device_node *node)
> > +{
> > +	struct netc_switch *priv = np->switch_priv;
> > +	struct enetc_mdio_priv *mdio_priv;
> > +	struct device *dev = priv->dev;
> > +	struct enetc_hw *hw;
> > +	struct mii_bus *bus;
> > +	int err;
> > +
> > +	hw = enetc_hw_alloc(dev, np->iobase);
> > +	if (IS_ERR(hw))
> > +		return dev_err_probe(dev, PTR_ERR(hw),
> > +				     "Failed to allocate enetc_hw\n");
> > +
> > +	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
> 
> [ ... ]
> 
> > +	err = devm_of_mdiobus_register(dev, bus, node);
> 
> [ ... ]
> 
> > +static int netc_init_all_ports(struct netc_switch *priv)
> > +{
> > +	struct device *dev = priv->dev;
> > +	struct netc_port *np;
> > +	struct dsa_port *dp;
> > +	int err;
> > +
> > +	priv->ports = devm_kcalloc(dev, priv->info->num_ports,
> > +				   sizeof(struct netc_port *),
> > +				   GFP_KERNEL);
> 
> [ ... ]
> 
> > +	for (int i = 0; i < priv->info->num_ports; i++) {
> > +		np = devm_kzalloc(dev, sizeof(*np), GFP_KERNEL);
> 
> [ ... ]
> 
> > +static int netc_init_all_cbdrs(struct netc_switch *priv)
> > +{
> > +	struct netc_switch_regs *regs = &priv->regs;
> > +	struct ntmp_user *ntmp = &priv->ntmp;
> > +	int i, err;
> > +
> > +	ntmp->cbdr_num = NETC_CBDR_NUM;
> > +	ntmp->dev = priv->dev;
> > +	ntmp->ring = devm_kcalloc(ntmp->dev, ntmp->cbdr_num,
> > +				  sizeof(struct netc_cbdr),
> > +				  GFP_KERNEL);
> 
> [ ... ]
> 
> > +static int netc_setup(struct dsa_switch *ds)
> > +{
> > +	struct netc_switch *priv = ds->priv;
> > +	struct dsa_port *dp;
> > +	int err;
> > +
> > +	err = netc_init_switch_id(priv);
> > +	if (err)
> > +		return err;
> > +
> > +	err = netc_init_all_ports(priv);
> > +	if (err)
> > +		return err;
> > +
> > +	err = netc_init_ntmp_user(priv);
> 
> Can these devres-managed allocations and registrations cause issues when the
> DSA tree unbinds and rebounds?
> 
> In the DSA subsystem, the .setup callback is invoked when the conduit
> interface becomes available. If the conduit interface is unbound and rebound,
> the DSA tree goes through a teardown and setup cycle, meaning netc_setup()
> can be called multiple times during the lifetime of the underlying PCI device.

This analysis is wrong, the switch driver will be removed if the conduit interface
is unbound. A device link is created between the switch and the conduit interface.
See dsa_conduit_setup():

if (!netif_is_lag_master(dev)) {
		consumer_link = device_link_add(ds->dev, dev->dev.parent,
						DL_FLAG_AUTOREMOVE_CONSUMER);
		if (!consumer_link)
			netdev_err(dev,
				   "Failed to create a device link to DSA switch %s\n",
				   dev_name(ds->dev));
}

For NETC switch it does not support LAG, so the consumer_link is created
when dsa_conduit_setup() is called. netc_setup() is called only once during
the lifetime of the switch device. The call stack is show below.
netc_switch_probe()
	└──dsa_register_switch()
		 └── dsa_switch_probe()
			   └── dsa_tree_setup()
				     └── dsa_tree_setup_switches()
						   └── dsa_switch_setup()

This is a false positive, please ignore it.

> 
> Because netc_init_all_ports() and netc_init_ntmp_user() use devres functions
> (devm_kzalloc(), devm_kcalloc(), and devm_of_mdiobus_register()) tied to the
> PCI device, their resources will not be freed when .teardown is called. When
> the tree binds again, netc_setup() will execute anew, leaking the previously
> allocated memory. Also, devm_of_mdiobus_register() will attempt to register an
> MDIO bus with an ID that is already in use by the previous unfreed
> registration, potentially causing .setup to fail.
> 
> Would it be better to either move these allocations to probe(), or manage them
> without devm_ helpers and explicitly clean them up in .teardown()?
> 
> [ ... ]
> 
> > +static void netc_switch_shutdown(struct pci_dev *pdev)
> > +{
> > +	struct netc_switch *priv = pci_get_drvdata(pdev);
> > +
> > +	if (!priv)
> > +		return;
> > +
> > +	dsa_switch_shutdown(priv->ds);
> 
> Does the hardware continue to perform DMA after this shutdown callback runs?
> 
> The netc_switch_pci_init() function enables PCI bus mastering and sets up a
> 64-bit DMA mask, and netc_init_all_cbdrs() initializes the Control BD rings.
> However, dsa_switch_shutdown() only closes the logical network ports without
> invoking the DSA teardown callback, meaning the hardware BD rings are never
> disabled.
> 
> Leaving the hardware rings active with bus mastering enabled during a system
> reboot or a transition to a kexec/kdump kernel might allow the device to
> continue performing DMA into memory that may have been reassigned,
> potentially
> leading to silent memory corruption or IOMMU faults. Should the shutdown
> callback explicitly disable the rings or call pci_clear_master() here?

NETC switch is PCIe device, what I learned from pci_device_shutdown() is that
it is unnecessary to call pci_clear_master() in netc_switch_shutdown().

See the comment from pci_device_shutdown():
    /*
	 * If this is a kexec reboot, turn off Bus Master bit on the
	 * device to tell it to not continue to do DMA. Don't touch
	 * devices in D3cold or unknown states.
	 * If it is not a kexec reboot, firmware will hit the PCI
	 * devices with big hammer and stop their DMA any way.
	 */

> 
> > +	pci_set_drvdata(pdev, NULL);
> 
> Could clearing the driver data here break subsequent unbind operations?
> 
> If a user manually unbinds the driver via sysfs shortly after a shutdown
> event, netc_switch_remove() will read a NULL priv pointer and return early,

I believe this is a technically possible but meaningless edge case, and there
is no need to make any changes to this meaningless situation.

> skipping dsa_unregister_switch() and leaking the DSA state. Is it necessary to
> clear the driver data in the shutdown path?
> > +}