From: netdev-bot+sashiko@kernel.org
To: daniel@makrotopia.org
Cc: jiri@resnulli.us, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, horms@kernel.org,
corbet@lwn.net, skhan@linuxfoundation.org, rdunlap@infradead.org,
gregkh@linuxfoundation.org, rafael@kernel.org, dakr@kernel.org,
andrew@lunn.ch, olteanv@gmail.com, linux@armlinux.org.uk,
netdev@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, driver-core@lists.linux.dev
Subject: Re: [PATCH net-next v18 3/6] driver core: add device_schedule_reprobe()
Date: Wed, 30 Sep 2026 03:48:49 +0000 [thread overview]
Message-ID: <179074012933.434549.4644127327520055010@kernel.org> (raw)
In-Reply-To: =?utf-8?q?=3Ce70f147857d21b792de81c7d39c2f74c6e0ec3f5=2E1790426?= =?utf-8?q?599=2Egit=2Edaniel=40makrotopia=2Eorg=3E?=
Thank you for your contribution! Sashiko AI review found 4 potential
issue(s) to consider.
Critical: 0 · High: 0 · Medium: 4 · Low: 0
If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).
- [Medium] In device_reprobe_work_fn() (drivers/base/dd.c), the check `if
(ret <= 0 && ret != -EPROBE_DEFER) dev_err(dev, "re-probe left the…
- [Medium] When probing is blocked, device_reprobe_work_fn() re-queues
itself with `queue_delayed_work(system_freezable_wq, &rp->work,…
- [Medium] The shutdown/suspend gate in device_reprobe_work_fn() is a
check-then-act.
- [Medium] device_schedule_reprobe() pins @dev with get_device() in a
delayed work that nothing can cancel or flush: the rp handle is never…
--
driver core: add device_schedule_reprobe()
This adds device_schedule_reprobe(). It detaches and re-probes a device
from a driver-core delayed work after a delay the caller chooses. Under
the device lock, the work checks that the device is still bound to the
same driver, and it re-arms itself while suspend or shutdown blocks
probing.
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index f6525a7ee8c54..823a25f9c0895 100644
> --- a/drivers/base/dd.c
> +++ b/drivers/base/dd.c
> @@ -1436,3 +1436,129 @@ void driver_detach(const struct device_driver *drv)
[ ... ]
> +static void device_reprobe_work_fn(struct work_struct *work)
> +{
> + struct device_reprobe *rp = container_of(work, struct device_reprobe,
> + work.work);
> + struct device *dev = rp->dev;
> + bool detached = false;
> + int ret;
> +
> + device_lock(dev);
> + /*
> + * rp->drv is only compared, never dereferenced: the driver it points
> + * to may have been unregistered and freed. The saved name rejects a
> + * freed address the allocator has since handed to another driver.
> + */
> + if (!dev->p->dead && dev->driver == rp->drv &&
> + !strcmp(dev->driver->name, rp->drv_name)) {
> + if (defer_all_probes) {
> + device_unlock(dev);
> + queue_delayed_work(system_freezable_wq, &rp->work,
> + rp->delay);
> + return;
> + }
[Severity: Medium]
What stops this re-arm once shutdown has started? device_shutdown() does:
wait_for_device_probe();
device_block_probing();
device_unblock_probing() is only called from dpm_complete(). Once a reboot
starts, defer_all_probes stays true, so a pending request keeps
re-queueing itself until power-off.
device_schedule_reprobe() also accepts delay_ms == 0, which makes
rp->delay 0:
rp->delay = msecs_to_jiffies(delay_ms);
With a zero delay, queue_delayed_work() queues the work at once. The work
then runs back to back and takes and drops dev->mutex on every pass,
while device_shutdown() walks the device list and takes the same
per-device locks.
The same applies to the dpm_prepare()..dpm_complete() window when
system_freezable_wq is not frozen, such as the kexec jump case mentioned
in the v18 changelog. The in-series mxl862xx caller uses 500 ms, so it
only gets a periodic re-arm. The exported API still allows the zero-delay
case.
Should there be a minimum re-arm interval? Or could the shutdown case drop
the request instead of re-arming? The kernel-doc only says the work
"re-arms itself after @delay_ms".
> + __device_release_driver(dev, NULL);
> + detached = true;
> + }
[Severity: Medium]
Can defer_all_probes become true right after the check above?
device_block_probing() sets it with a plain store and takes no device
lock:
void device_block_probing(void)
{
defer_all_probes = true;
/* sync with probes to avoid races. */
wait_for_device_probe();
}
wait_for_device_probe() flushes deferred_probe_work and waits for
probe_count and async probes. It does not wait for this work item on
system_freezable_wq.
Suppose the work reads false and calls __device_release_driver(), and dev
has managed consumers. The device lock is then dropped inside the
device_links_busy() loop:
while (device_links_busy(dev)) {
__device_driver_unlock(dev, parent);
device_links_unbind_consumers(dev);
__device_driver_lock(dev, parent);
...
if (dev->driver != drv) {
In that gap, device_shutdown() can take device_lock(dev) and call
dev->driver->shutdown(dev). When the work gets the lock back, dev->driver
is unchanged, so it continues to device_remove(). That runs ->remove()
after ->shutdown() on hardware that is already shut down. Consumers that
the backward walk has already shut down could see the same order.
The kernel-doc says a request "that fires during a shutdown detaches
nothing" and that the overlap "may run ->remove() in place of
->shutdown()". Isn't the actual result that both callbacks run, with
->remove() second?
The lock-dropping loop predates this patch. This helper adds a
timer-driven unbind that is likely to fire around a reboot that follows a
flash.
> + device_unlock(dev);
> +
> + if (detached) {
> + ret = device_attach(dev);
> + /* 0 is unbound too: a failed probe is folded into "no match" */
> + if (ret <= 0 && ret != -EPROBE_DEFER)
> + dev_err(dev, "re-probe left the device unbound\n");
> + }
[Severity: Medium]
Will this ever see -EPROBE_DEFER coming from the driver's own ->probe()?
really_probe() makes ->probe() errors positive:
drivers/base/dd.c:really_probe() {
...
ret = -ret;
goto probe_failed;
...
}
driver_probe_device() adds the device to the deferred list when it sees
EPROBE_DEFER, and it returns the positive value. __device_attach_driver()
then folds any positive value into 0:
ret = driver_probe_device(drv, dev);
if (ret < 0)
return ret;
return ret == 0;
So device_attach() returns 0 for a probe-time deferral, and this
dev_err() fires even though the deferred-probe machinery will retry the
probe. The ret != -EPROBE_DEFER test seems to cover only the negative
deferrals, such as bus match deferral, device_links_check_suppliers() and
the defer_all_probes path in really_probe().
The commit message says:
so the message is a plain dev_err() that skips only -EPROBE_DEFER, which
the deferred-probe machinery retries on its own.
Does the code match this for the common case?
The same message is also printed if dev is unregistered between the
detach and device_attach(). In that case __device_attach() returns 0 early
because dev->p->dead is set.
> +
> + put_device(dev);
> + kfree(rp->drv_name);
> + kfree(rp);
> +}
[Severity: Medium]
Could this put_device() drop the last reference and call into module
text that has already been unloaded?
rp is never returned to the caller, and there is no way to cancel or
flush it. The reference taken by get_device() in device_schedule_reprobe()
is therefore held for the whole delay.
For example, the mdio bus under the in-series mxl862xx user could be torn
down within that window through mdiobus_unregister() -> device_unregister().
If the module that provides dev->release, dev->type->release or
dev->class->dev_release is then unloaded, the work sees dev->p->dead and
skips the detach. This put_device() then drops the last reference:
device_reprobe_work_fn()
put_device()
kobject_put()
device_release()
dev->release(dev) <- lives in the unloaded module
The commit message gives "a concurrent rmmod can free the module text the
epilogue is still executing" as the motivation, and says "The work
function is built-in text, so a caller needs no module reference". Does
that still hold for the module that owns the device's release callback?
[ ... ]
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1790426599.git.daniel%40makrotopia.org
next reply other threads:[~2026-09-30 3:48 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 3:48 netdev-bot+sashiko [this message]
-- strict thread matches above, loose matches on Subject: below --
2026-09-26 13:48 [PATCH net-next v18 0/6] net: dsa: mxl862xx: devlink flash and rescue Daniel Golle
2026-09-26 13:49 ` [PATCH net-next v18 3/6] driver core: add device_schedule_reprobe() Daniel Golle
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=179074012933.434549.4644127327520055010@kernel.org \
--to=netdev-bot+sashiko@kernel.org \
--cc=andrew@lunn.ch \
--cc=corbet@lwn.net \
--cc=dakr@kernel.org \
--cc=daniel@makrotopia.org \
--cc=davem@davemloft.net \
--cc=driver-core@lists.linux.dev \
--cc=edumazet@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=horms@kernel.org \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
--cc=rafael@kernel.org \
--cc=rdunlap@infradead.org \
--cc=skhan@linuxfoundation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®