Thread (1 message) 1 message, 1 author, 2d ago

Re: [PATCH net-next v15 4/6] net: dsa: mxl862xx: add devlink flash_update and info_get

From: netdev-bot+sashiko@kernel.org
Date: 2026-09-14 18:34:56
Also in: driver-core, linux-doc, lkml

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:

Critical: 0 · High: 1 · Medium: 0 · Low: 1

- [High] AB-BA deadlock between the devlink instance lock and the driver-
  core device lock.
- [Low] drivers/net/dsa/mxl862xx/Kconfig does not `select CRC32` although
  the new mxl862xx-fw.c calls crc32_le() twice in…

--

net: dsa: mxl862xx: add devlink flash_update and info_get

This adds devlink dev info version reporting (asic.id, asic.rev and the
running/stored firmware version) and devlink dev flash support for the
MxL862xx switch, transferring a validated image over the SB PDI protocol
after rebooting the switch into its MCUboot loader and then scheduling a
deferred re-probe.
quoted hunk
diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-fw.c b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c
new file mode 100644
index 0000000000000..119792eb7bd58
--- /dev/null
+++ b/drivers/net/dsa/mxl862xx/mxl862xx-fw.c
@@ -0,0 +1,648 @@
[ ... ]
+#include <linux/crc32.h>
[ ... ]
+	if (size1) {
+		crc = ~crc32_le(~0U, payload, size1);
+		if (crc != le32_to_cpu(hdr->image_checksum_1)) {
+			dev_err(&priv->mdiodev->dev,
+				"flash: image 1 CRC mismatch (got %08x, expected %08x)\n",
+				crc, le32_to_cpu(hdr->image_checksum_1));
+			return -EINVAL;
+		}
+	}
+
+	if (size2) {
+		crc = ~crc32_le(~0U, payload + size1, size2);
[Severity: Low]

Should drivers/net/dsa/mxl862xx/Kconfig also select CRC32 for these two
new crc32_le() users in mxl862xx_flash_validate()?

The driver's Kconfig currently has:

config NET_DSA_MXL862
	tristate "MaxLinear MxL862xx"
	depends on NET_DSA
	select CRC16
	select NET_DSA_TAG_MXL_862XX

CRC16 is selected for the crc16() user in mxl862xx-host.c, but nothing
selects CRC32. In lib/crc/Kconfig, CRC32 is a bare tristate with no prompt
and no default:

config CRC32
	tristate
	select BITREVERSE
	help
	  The CRC32 library functions.  Select this if your module uses any of
	  the functions from <linux/crc32.h> or <linux/crc32c.h>.

and lib/crc/Makefile builds crc32.o under obj-$(CONFIG_CRC32), so with
CONFIG_CRC32=n (or CRC32=m while NET_DSA_MXL862=y) does this fail to link
with an undefined crc32_le? The select is still missing at the end of the
series.

[ ... ]
+int mxl862xx_devlink_flash_update(struct dsa_switch *ds,
+				  struct devlink_flash_update_params *params,
+				  struct netlink_ext_ack *extack)
+{
[ ... ]
+	mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+	priv->flash_owner = NULL;
+	priv->block_host = false;
+	priv->skip_teardown = true;
+	mutex_unlock(&priv->mdiodev->bus->mdio_lock);
+
+	/* Reinitialise through a deferred re-probe: remove() runs with
+	 * skip_teardown set, then a fresh probe() starts against whatever
+	 * the switch now runs. The core skips the re-probe if the device
+	 * is unbound or shut down before it fires.
+	 */
+	err = device_schedule_reprobe(ds->dev, MXL862XX_FW_REPROBE_DELAY_MS);
[Severity: High]

Can this call deadlock against a concurrent unbind of the same mdio
device?

.devlink_flash_update runs with the devlink instance lock held by the
netlink core:

devlink_nl_pre_doit()
  __devlink_nl_pre_doit()
    devlink_get_from_attrs_lock()
      devl_dev_lock(devlink, false)   /* devl_lock() only */

and device_schedule_reprobe() then takes device_lock() synchronously in
that context:

drivers/base/dd.c:device_schedule_reprobe() {
	...
	device_lock(dev);
	parent = dev->parent;
	if (!dev->bus || !dev->p || dev->p->dead ||
	    !device_is_registered(dev) || !dev->driver) {
	...
}

So this path is devl_lock -> device_lock.

The opposite order exists in the unbind path, where the driver core holds
device_lock across ->remove():

device_release_driver_internal()   /* holds device_lock(&mdiodev->dev) */
  mxl862xx_remove()
    dsa_unregister_switch()
      dsa_switch_teardown()
        dsa_switch_devlink_unregister()
          devlink_unregister()
            devl_lock()

devlink itself documents the same order in net/devlink/devl_internal.h:

static inline void devl_dev_lock(struct devlink *devlink, bool dev_lock)
{
	if (dev_lock && devlink->dev)
		device_lock(devlink->dev);
	devl_lock(devlink);
}

For DSA, devlink->dev is ds->dev, i.e. &mdiodev->dev, so both locks in
the cycle are the same two objects. If a sysfs unbind or rmmod runs while
devlink dev flash is in progress, does one thread end up holding
device_lock and waiting on devl_lock while the flash thread holds
devl_lock for the whole transfer and then waits on device_lock?

Both are plain mutexes with no trylock or timeout here, and
device_initialize() marks dev->mutex with lockdep_set_novalidate_class(),
so would lockdep report this cycle at all?

Would scheduling the re-probe from a context that does not hold the
devlink instance lock, or a variant of the helper that does not take
device_lock in the caller's context, avoid the inversion?
+
+	return ret ? ret : err;
+}
Please double check this analysis, and thanks for your time reviewing it.

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1789175618.git.daniel%40makrotopia.org
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help