* Re: [PATCH net-next v16 3/6] driver core: add device_schedule_reprobe()
@ 2026-09-17 16:11 netdev-bot+sashiko
0 siblings, 0 replies; 2+ messages in thread
From: netdev-bot+sashiko @ 2026-09-17 16:11 UTC (permalink / raw)
To: daniel
Cc: jiri, davem, edumazet, kuba, pabeni, horms, corbet, skhan,
rdunlap, gregkh, rafael, dakr, andrew, olteanv, netdev,
linux-doc, linux-kernel, driver-core
Thank you for your contribution! Sashiko AI review found 8 potential
issue(s) to consider:
Critical: 0 · High: 1 · Medium: 3 · Low: 4
- [High] The new `abort_if_blocked` protection stops at @dev.
- [Medium] `__device_release_driver()` (drivers/base/dd.c:1341-1345) can
now abandon a release *inside* the `while (device_links_busy(dev))`…
- [Medium] Only the detach half of the re-probe is gated on
`defer_all_probes`; the attach half is not.
- [Medium] The detach and the re-attach are not one serialized operation,
so an administrative unbind can be acknowledged and then undone.
- [Low] `device_schedule_reprobe()` stores only a raw `const struct
device_driver *` (`drv = READ_ONCE(dev->driver)`, no reference, no…
- [Low] The kernel-doc of `device_schedule_reprobe()` says "Nothing is
locked in the caller's context, so this may be called from any process…
- [Low] Reported for completeness; my baseline decision is that this is
NOT a defect to act on.
- [Low] Reported for completeness; my baseline decision is FALSE
POSITIVE.
--
driver core: add device_schedule_reprobe()
This adds a driver-core helper that schedules a delayed detach and
re-probe of a device from built-in work, replacing the open-coded work
items drivers use today. __device_release_driver() gains a return value
and an abort_if_blocked flag so a re-probe can abandon the unbind when
probing has been blocked meanwhile.
A few questions about the new helper and the new flag.
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index f6525a7ee8c54..f521ddefc4a88 100644
> --- a/drivers/base/dd.c
> +++ b/drivers/base/dd.c
> @@ -1314,8 +1314,12 @@ EXPORT_SYMBOL_GPL(driver_attach);
> /*
> * __device_release_driver() must be called with @dev lock held.
> * When called for a USB interface, @dev->parent lock must be held as well.
> + * @abort_if_blocked gives up the release instead when probing has been
> + * blocked while the locks were dropped; only a caller that may abandon
> + * the unbind altogether can ask for it.
> */
> -static void __device_release_driver(struct device *dev, struct device *parent)
> +static bool __device_release_driver(struct device *dev, struct device *parent,
> + bool abort_if_blocked)
> {
> struct device_driver *drv;
>
> @@ -1334,9 +1338,10 @@ static void __device_release_driver(struct device *dev, struct device *parent)
> * have released the driver successfully while this one
> * was waiting, so check for that.
> */
> - if (dev->driver != drv) {
> + if (dev->driver != drv ||
> + (abort_if_blocked && defer_all_probes)) {
> pm_runtime_put(dev);
> - return;
> + return false;
> }
> }
>
[Severity: Medium]
When the new abort_if_blocked && defer_all_probes arm fires, is the
device-links state left half torn down?
By the time this check runs, device_links_busy() has already done:
drivers/base/core.c:device_links_busy() {
...
WRITE_ONCE(link->status, DL_STATE_SUPPLIER_UNBIND);
}
dev->links.status = DL_DEV_UNBINDING;
...
}
and device_links_unbind_consumers() has already force-released the drivers
of the active consumers.
Returning false here leaves @dev bound, so device_links_driver_cleanup() -
the only code that moves those links back to DL_STATE_DORMANT and clears
DL_DEV_UNBINDING - never runs, and device_links_driver_bound() cannot run
either because the supplier never rebinds.
Can the already-detached consumers then re-probe?
device_links_check_suppliers() looks like it would defer them forever:
drivers/base/core.c:device_links_check_suppliers() {
...
if (link->status != DL_STATE_AVAILABLE &&
!device_link_test(link, DL_FLAG_SYNC_STATE_ONLY)) {
...
ret = dev_err_probe(dev, -EPROBE_DEFER, ...);
...
}
and any new device_link_add() against this still-bound supplier starts at
DL_STATE_SUPPLIER_UNBIND because supplier->links.status is DL_DEV_UNBINDING.
The pre-existing dev->driver != drv arm is different in that a concurrent
releaser completes the state machine. Here the only caller passing true,
device_reprobe_work_fn(), treats false as "nothing happened" and does no
compensating cleanup. Should the kernel-doc and commit message mention this,
or should the abort undo the links state it inherits?
[ ... ]
> @@ -1369,7 +1377,7 @@ void device_release_driver_internal(struct device *dev,
> __device_driver_lock(dev, parent);
>
> if (!drv || drv == dev->driver)
> - __device_release_driver(dev, parent);
> + __device_release_driver(dev, parent, false);
>
> __device_driver_unlock(dev, parent);
> }
[Severity: High]
Does the new flag actually close the ->shutdown()/->remove() interleaving
for the consumers, or only for @dev?
The commit message says:
"__device_release_driver() drops the device lock while busy consumer
links are unbound, which would let a ->shutdown() interleave in the
middle of a release already under way, so it now re-checks on
re-acquisition and abandons the release."
But the consumer unbinds happen inside that dropped-lock window, before the
new check is evaluated:
drivers/base/dd.c:__device_release_driver() {
...
while (device_links_busy(dev)) {
__device_driver_unlock(dev, parent);
device_links_unbind_consumers(dev);
__device_driver_lock(dev, parent);
...
}
and each consumer goes through this wrapper, which hardcodes the flag off:
drivers/base/core.c:device_links_unbind_consumers() {
...
device_release_driver_internal(consumer, NULL,
consumer->parent);
...
}
So the sequence looks reachable:
device_reprobe_work_fn()
device_lock(dev) /* defer_all_probes == false */
__device_release_driver(dev, NULL, true)
device_links_busy(dev) == true
device_unlock(dev)
device_links_unbind_consumers(dev)
meanwhile:
device_shutdown()
device_block_probing() /* defer_all_probes = true */
device_lock(consumer)
consumer->driver->shutdown(consumer)
device_unlock(consumer)
back in device_links_unbind_consumers():
device_release_driver_internal(consumer, ...)
__device_release_driver(consumer, ..., false)
device_remove(consumer) /* consumer ->remove() */
Is the consumer's ->remove() then run after its ->shutdown() has already
quiesced it? Nothing else serialises the two beyond the consumer's own
device lock, and wait_for_device_probe() cannot see the new delayed work:
drivers/base/dd.c:wait_for_device_probe() {
flush_work(&deferred_probe_work);
wait_event(probe_waitqueue, atomic_read(&probe_count) == 0);
async_synchronize_full();
}
Should the abort decision be propagated into the consumer releases before
their removal callbacks run, or should shutdown be synchronised against the
whole in-flight re-probe?
> @@ -1436,3 +1444,98 @@ void driver_detach(const struct device_driver *drv)
> put_device(dev);
> }
> }
> +
> +struct device_reprobe {
> + struct delayed_work work;
> + const struct device_driver *drv;
> + struct device *dev;
> +};
> +
> +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 ever compared, never dereferenced: the driver it
> + * points to may have been unregistered and freed by now.
> + * device_shutdown() blocks probing before its walk reaches @dev.
> + */
> + if (!defer_all_probes && !dev->p->dead && dev->driver == rp->drv)
> + detached = __device_release_driver(dev, NULL, true);
> + device_unlock(dev);
> +
> + if (detached) {
> + ret = device_attach(dev);
> + if (ret < 0 && ret != -EPROBE_DEFER)
> + dev_err_probe(dev, ret,
> + "re-probe failed, device left unbound\n");
> + }
[Severity: Medium]
Only the detach half is gated on defer_all_probes here; is the attach half
also meant to be?
device_attach() runs after device_unlock(dev) with no re-check, and
__device_attach() resumes the parent before anything tests defer_all_probes:
drivers/base/dd.c:__device_attach() {
...
if (dev->parent)
pm_runtime_get_sync(dev->parent);
ret = bus_for_each_drv(dev->bus, NULL, &data,
__device_attach_driver);
...
}
really_probe() only rejects the probe later. device_shutdown() itself only
does pm_runtime_get_noresume() plus a barrier, so a parent that is
RPM_SUSPENDED stays suspended - can this work then runtime-resume a parent
whose ->shutdown() has already run, and add @dev to the deferred-probe list
while probing is blocked?
Nothing cancels or flushes this work either, and wait_for_device_probe()
(which device_block_probing() relies on) only flushes deferred_probe_work,
waits for probe_count and calls async_synchronize_full(), none of which
observe a system_freezable_wq delayed work.
[Severity: Medium]
Can an administrative unbind be acknowledged and then undone here, since the
detach and the re-attach are not one serialised operation?
drivers/base/bus.c:unbind_store() {
...
if (dev && dev->driver == drv) {
device_driver_detach(dev);
err = count;
...
}
That dev->driver == drv test is unlocked, and device_driver_detach() ends up
in device_release_driver_internal(), which blocks on the device lock this
work holds. When it gets the lock, dev->driver is NULL, so it does nothing,
and unbind_store() still returns count. Then this work calls
device_attach(dev) and the device is bound again. The same applies to an
unbind arriving in the window between device_unlock(dev) and
device_attach(dev).
The commit message lists "a stale re-probe undoes an administrative unbind"
among the things the new helper addresses, while the new return value only
covers an unbind that wins inside the busy-consumer-links loop. Could that
wording be narrowed, or the gap between detach and re-attach closed?
> +
> + put_device(dev);
> + kfree(rp);
> +}
> +
> +/**
> + * device_schedule_reprobe - schedule a deferred detach and re-probe
> + * @dev: device to detach and re-probe
> + * @delay_ms: delay in milliseconds before the re-probe runs
> + *
> + * Schedule a detach and re-probe of @dev after @delay_ms milliseconds.
> + * The re-probe is skipped if, by the time the scheduled work runs, the
> + * device has been removed, probing has been blocked for a system
> + * shutdown, or @dev is no longer bound to the driver that was bound at
> + * scheduling time.
> + *
> + * The work function is built-in text, so the bound driver may call this
> + * from its own code without holding a module reference. If the driver
> + * module is unloaded before the work runs, driver unregistration unbinds
> + * @dev first and the scheduled work does nothing.
[ ... ]
> + *
> + * Nothing is locked in the caller's context, so this may be called from
> + * any process context, @dev's own device lock held included, but not
> + * from @dev's ->probe(), which the scheduled work would detach.
[ ... ]
> + */
> +int device_schedule_reprobe(struct device *dev, unsigned int delay_ms)
> +{
> + const struct device_driver *drv;
> + struct device_reprobe *rp;
> +
> + drv = READ_ONCE(dev->driver);
[Severity: Low]
The recorded identity of the binding is a bare struct device_driver pointer,
with no reference, no module pin and no generation counter, and
device_reprobe_work_fn() gates the destructive detach only on
dev->driver == rp->drv
Is pointer identity enough here? An unbind followed by a rebind of the same
driver within @delay_ms makes the test pass for a binding the caller never
scheduled against. After a module unload, can the same address be reused by
another module's struct device_driver, in which case the kernel-doc promise
"If the driver module is unloaded before the work runs, driver
unregistration unbinds @dev first and the scheduled work does nothing."
no longer holds, and the work would drive __device_release_driver() -
including device_links_unbind_consumers() and an unrelated driver's
->remove() - followed by device_attach()?
There is also no cancel or flush counterpart, so a caller cannot gate this
itself. Would a bind generation counter, a per-binding token, or at least
comparing the driver name as well, match the documented semantics?
> + /*
> + * A bus taking the parent lock would need @dev's parent pinned until
> + * the work runs, which device_move() can invalidate.
> + */
> + if (!drv || !dev->bus || dev->bus->need_parent_lock || !dev->p ||
> + dev->p->dead || !device_is_registered(dev))
> + return -EINVAL;
> +
> + rp = kzalloc_obj(*rp);
> + if (!rp)
> + return -ENOMEM;
[Severity: Low]
The kernel-doc says this "may be called from any process context", but
kzalloc_obj() with no gfp argument resolves to GFP_KERNEL and may sleep:
include/linux/gfp.h:
#define __default_gfp(a,b,...) b
#define default_gfp(...) __default_gfp(,##__VA_ARGS__,GFP_KERNEL)
The commit message invites callers from the PM callbacks, whose _late and
_noirq phases run with interrupts disabled. Could a Context: line stating
that this helper may sleep be added, or the gfp flags be passed explicitly?
> +
> + rp->dev = get_device(dev);
> + rp->drv = drv;
> +
> + INIT_DELAYED_WORK(&rp->work, device_reprobe_work_fn);
> + queue_delayed_work(system_freezable_wq, &rp->work,
> + msecs_to_jiffies(delay_ms));
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(device_schedule_reprobe);
[Severity: Low]
This isn't a bug, just a wording note. The commit message opens by
describing live defects in iwlwifi's iwl_trans_reprobe_wk() and hci_h5's
h5_btrtl_reprobe_worker():
"both end that work function with put_device(); kfree();
module_put(THIS_MODULE);, where a concurrent rmmod can free the module
text the epilogue is still executing."
Neither driver is converted here and there is no Fixes: tag, while the newly
exported symbol has no in-tree caller until the mxl862xx patches later in
the series. Splitting the core helper from its first user is normal, so
nothing needs changing in the code; would leading with the motivation rather
than with those two drivers read more clearly?
--
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/cover.1789477568.git.daniel%40makrotopia.org
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH net-next v16 3/6] driver core: add device_schedule_reprobe()
2026-09-15 13:09 [PATCH net-next v16 0/6] net: dsa: mxl862xx: devlink flash and rescue Daniel Golle
@ 2026-09-15 13:09 ` Daniel Golle
0 siblings, 0 replies; 2+ messages in thread
From: Daniel Golle @ 2026-09-15 13:09 UTC (permalink / raw)
To: Jiri Pirko, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan,
Randy Dunlap, Daniel Golle, Greg Kroah-Hartman,
Rafael J. Wysocki, Danilo Krummrich, Andrew Lunn,
Vladimir Oltean, netdev, linux-doc, linux-kernel, driver-core
Drivers that need a deferred re-probe of their own device open-code a
work item in module text. iwlwifi (iwl_trans_schedule_reprobe(), for a
firmware crash a lighter restart cannot fix) and hci_h5
(h5_btrtl_resume(), RTL devices lose their firmware state over suspend)
both end that work function with put_device(); kfree();
module_put(THIS_MODULE);, where a concurrent rmmod can free the module
text the epilogue is still executing. Neither checks any bound state
before calling device_reprobe(), and a driver cannot make that check
stick because device_reprobe() takes the device lock internally, so a
stale re-probe undoes an administrative unbind and the detach half runs
against a device whose ->shutdown() has already run.
Add device_schedule_reprobe(), which detaches and re-probes a device
after a caller-specified delay. The work function is built-in text and
the recorded driver pointer is only ever compared, never dereferenced,
so a caller needs no module reference. The bound-state check and
__device_release_driver() run under one hold of the device lock, and
__device_release_driver() now reports whether it was this call that
released the driver, so an unbind winning the race while busy consumer
links are unbound is not followed by a re-attach. The first user is the
mxl862xx devlink flash path added later in this series.
Nothing is locked or validated in the caller's context, so the helper
may be called with the device lock held, as the PM callbacks, ->remove()
and ->shutdown() hold it. Buses that take the parent lock to bind are
refused with -EINVAL: that lock has to be taken before @dev's own, so
the parent would have to be recorded before either is held, where
device_move() can replace it without taking any device lock.
usb_bus_type is the only such bus and no caller needs it today;
supporting one means deriving the parent inside the work and re-checking
it once both locks are held.
The detach is skipped once probing is blocked, which device_shutdown()
and dpm_prepare() both do before they touch any device. dpm_prepare()
leaves no window, its callers having frozen the freezable workqueues
first; device_shutdown() blocks probing only once
wait_for_device_probe() has returned, so a re-probe already past the
test completes its detach and the device runs ->remove() in place of
->shutdown(). __device_release_driver()
drops the device lock while busy consumer links are unbound, which would
let a ->shutdown() interleave in the middle of a release already under
way, so it now re-checks on re-acquisition and abandons the release. That
is offered as a flag, because only a caller free to leave the device
bound can take it: an administrative unbind has to complete, and
driver_detach() would spin on a device it never released. Past that loop
the lock is held until the driver is gone, so ->remove() cannot follow a
->shutdown() on the same device.
Assisted-by: LLM
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v16:
- commit message: device_shutdown() blocks probing only once
wait_for_device_probe() has returned, so a re-probe already past the
test detaches the device instead of leaving it bound for its
->shutdown()
- take no lock in the caller's context and drop the parent snapshot,
refusing buses that need the parent lock instead: the caller-context
device lock inverted against the devlink instance lock on the flash
path and against a synchronous work cancel on the rescue path, and a
pinned parent can be freed by device_move() (found by Sashiko AI
review)
- abandon the release when probing is blocked while the device links
loop has the locks dropped, rather than calling that window
pre-existing: a deferred re-probe is the one unbind that may be
abandoned, so it is the one that can close it (found by Sashiko AI
review)
- commit message: describe what this patch changes rather than bugs in
drivers it does not convert, and name the first user (found by
Sashiko AI review)
- kernel-doc: drop the promise that an administrative unbind always
wins, which unbind_store() does not guarantee (found by Sashiko AI
review)
v15:
- skip the detach while probing is blocked instead of adding a
per-device shutdown_done flag: device_shutdown() blocks probing
before its walk starts, so the flag left a window where the work
detached a device that then neither re-attached nor got its
->shutdown() call (found by Sashiko AI review)
- validate the device and snapshot the parent, its locking requirement
and the bound driver under the device lock, so an unregister racing
the allocation can neither leave a freed parent pinned nor pair a
NULL parent with a request to lock it (found by Sashiko AI review)
- keep -EPROBE_DEFER out of the re-probe error path, where
dev_err_probe() would record the message as the device's deferred
probe reason (found by Sashiko AI review)
- kernel-doc: a stale re-probe leaves an unbound device unbound, which
an unbind followed by a rebind within the delay does not (found by
Sashiko AI review)
v14: no changes
v13:
- queue the work on system_freezable_wq, so a re-probe pending across
system suspend can neither detach a device the PM core has suspended
nor race its late suspend callbacks; it runs after resume instead
(found by Sashiko AI review)
- record at scheduling time whether the parent needs locking, instead
of reading dev->bus in the work, which may be gone with its module
once the device has been unregistered (found by Sashiko AI review)
- let __device_release_driver() report whether it released the driver,
so an administrative unbind that wins the race inside the device
links loop is not undone by the re-attach (found by Sashiko AI
review)
- use dev_err_probe() for the re-probe error path, so a re-probe
deferred at resume no longer logs a spurious error (Hans de Goede,
on the standalone posting of this helper)
- describe the parent pinning and locking in the commit message, as in
the standalone posting
v12:
- pin the parent device across the deferred work; a reference on the
child alone left device_reprobe_work_fn() dereferencing a freed
dev->parent under __device_driver_lock() when the device was
unregistered before the work ran (found by Sashiko AI review)
- take the parent lock across device_attach() on buses that require
it, matching bus_rescan_devices_helper() (found by Sashiko AI review)
v11: new patch: add device_schedule_reprobe() to the driver core (posted
earlier as an RFC) so mxl862xx can schedule its post-flash and
post-drain re-probe through the core instead of open-coding a work
item
drivers/base/dd.c | 111 +++++++++++++++++++++++++++++++++++++++--
include/linux/device.h | 2 +
2 files changed, 109 insertions(+), 4 deletions(-)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index f6525a7ee8c5..f521ddefc4a8 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1314,8 +1314,12 @@ EXPORT_SYMBOL_GPL(driver_attach);
/*
* __device_release_driver() must be called with @dev lock held.
* When called for a USB interface, @dev->parent lock must be held as well.
+ * @abort_if_blocked gives up the release instead when probing has been
+ * blocked while the locks were dropped; only a caller that may abandon
+ * the unbind altogether can ask for it.
*/
-static void __device_release_driver(struct device *dev, struct device *parent)
+static bool __device_release_driver(struct device *dev, struct device *parent,
+ bool abort_if_blocked)
{
struct device_driver *drv;
@@ -1334,9 +1338,10 @@ static void __device_release_driver(struct device *dev, struct device *parent)
* have released the driver successfully while this one
* was waiting, so check for that.
*/
- if (dev->driver != drv) {
+ if (dev->driver != drv ||
+ (abort_if_blocked && defer_all_probes)) {
pm_runtime_put(dev);
- return;
+ return false;
}
}
@@ -1359,7 +1364,10 @@ static void __device_release_driver(struct device *dev, struct device *parent)
bus_notify(dev, BUS_NOTIFY_UNBOUND_DRIVER);
kobject_uevent(&dev->kobj, KOBJ_UNBIND);
+ return true;
}
+
+ return false;
}
void device_release_driver_internal(struct device *dev,
@@ -1369,7 +1377,7 @@ void device_release_driver_internal(struct device *dev,
__device_driver_lock(dev, parent);
if (!drv || drv == dev->driver)
- __device_release_driver(dev, parent);
+ __device_release_driver(dev, parent, false);
__device_driver_unlock(dev, parent);
}
@@ -1436,3 +1444,98 @@ void driver_detach(const struct device_driver *drv)
put_device(dev);
}
}
+
+struct device_reprobe {
+ struct delayed_work work;
+ const struct device_driver *drv;
+ struct device *dev;
+};
+
+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 ever compared, never dereferenced: the driver it
+ * points to may have been unregistered and freed by now.
+ * device_shutdown() blocks probing before its walk reaches @dev.
+ */
+ if (!defer_all_probes && !dev->p->dead && dev->driver == rp->drv)
+ detached = __device_release_driver(dev, NULL, true);
+ device_unlock(dev);
+
+ if (detached) {
+ ret = device_attach(dev);
+ if (ret < 0 && ret != -EPROBE_DEFER)
+ dev_err_probe(dev, ret,
+ "re-probe failed, device left unbound\n");
+ }
+
+ put_device(dev);
+ kfree(rp);
+}
+
+/**
+ * device_schedule_reprobe - schedule a deferred detach and re-probe
+ * @dev: device to detach and re-probe
+ * @delay_ms: delay in milliseconds before the re-probe runs
+ *
+ * Schedule a detach and re-probe of @dev after @delay_ms milliseconds.
+ * The re-probe is skipped if, by the time the scheduled work runs, the
+ * device has been removed, probing has been blocked for a system
+ * shutdown, or @dev is no longer bound to the driver that was bound at
+ * scheduling time.
+ *
+ * The work function is built-in text, so the bound driver may call this
+ * from its own code without holding a module reference. If the driver
+ * module is unloaded before the work runs, driver unregistration unbinds
+ * @dev first and the scheduled work does nothing.
+ *
+ * Multiple pending re-probes for the same device are individually safe;
+ * a caller that wants at most one pending re-probe must gate scheduling
+ * itself.
+ *
+ * The work is freezable: a re-probe pending across system suspend runs
+ * once the system has resumed.
+ *
+ * Nothing is locked in the caller's context, so this may be called from
+ * any process context, @dev's own device lock held included, but not
+ * from @dev's ->probe(), which the scheduled work would detach.
+ *
+ * Returns: 0 on success, -EINVAL if @dev is not a registered device
+ * bound to a driver or sits on a bus which takes the parent lock to
+ * bind, -ENOMEM on allocation failure.
+ */
+int device_schedule_reprobe(struct device *dev, unsigned int delay_ms)
+{
+ const struct device_driver *drv;
+ struct device_reprobe *rp;
+
+ drv = READ_ONCE(dev->driver);
+ /*
+ * A bus taking the parent lock would need @dev's parent pinned until
+ * the work runs, which device_move() can invalidate.
+ */
+ if (!drv || !dev->bus || dev->bus->need_parent_lock || !dev->p ||
+ dev->p->dead || !device_is_registered(dev))
+ return -EINVAL;
+
+ rp = kzalloc_obj(*rp);
+ if (!rp)
+ return -ENOMEM;
+
+ rp->dev = get_device(dev);
+ rp->drv = drv;
+
+ INIT_DELAYED_WORK(&rp->work, device_reprobe_work_fn);
+ queue_delayed_work(system_freezable_wq, &rp->work,
+ msecs_to_jiffies(delay_ms));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(device_schedule_reprobe);
diff --git a/include/linux/device.h b/include/linux/device.h
index aee79fd6b32b..7a9916950577 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1314,6 +1314,8 @@ int __must_check device_attach(struct device *dev);
int __must_check driver_attach(const struct device_driver *drv);
void device_initial_probe(struct device *dev);
int __must_check device_reprobe(struct device *dev);
+int __must_check device_schedule_reprobe(struct device *dev,
+ unsigned int delay_ms);
bool device_is_bound(struct device *dev);
--
2.55.0
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-17 16:11 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-17 16:11 [PATCH net-next v16 3/6] driver core: add device_schedule_reprobe() netdev-bot+sashiko
-- strict thread matches above, loose matches on Subject: below --
2026-09-15 13:09 [PATCH net-next v16 0/6] net: dsa: mxl862xx: devlink flash and rescue Daniel Golle
2026-09-15 13:09 ` [PATCH net-next v16 3/6] driver core: add device_schedule_reprobe() Daniel Golle
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®