From: Marc Zyngier <maz@kernel.org>
To: Douglas Anderson <dianders@chromium.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Rafael J . Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>,
Alan Stern <stern@rowland.harvard.edu>,
Saravana Kannan <saravanak@kernel.org>,
Christoph Hellwig <hch@lst.de>,
Eric Dumazet <edumazet@google.com>,
Johan Hovold <johan@kernel.org>,
Leon Romanovsky <leon@kernel.org>,
Alexander Lobakin <aleksander.lobakin@intel.com>,
Alexey Kardashevskiy <aik@ozlabs.ru>,
Robin Murphy <robin.murphy@arm.com>,
stable@vger.kernel.org, driver-core@lists.linux.dev,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
Date: Mon, 06 Apr 2026 07:32:00 +0100 [thread overview]
Message-ID: <873418d2fz.wl-maz@kernel.org> (raw)
In-Reply-To: <20260403170432.v4.1.Id750b0fbcc94f23ed04b7aecabcead688d0d8c17@changeid>
Hi Doug,
On Sat, 04 Apr 2026 01:04:55 +0100,
Douglas Anderson <dianders@chromium.org> wrote:
>
> The moment we link a "struct device" into the list of devices for the
> bus, it's possible probe can happen. This is because another thread
> can load the driver at any time and that can cause the device to
> probe. This has been seen in practice with a stack crawl that looks
> like this [1]:
[...]
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 09b98f02f559..f07745659de3 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -3688,6 +3688,19 @@ int device_add(struct device *dev)
> fw_devlink_link_device(dev);
> }
>
> + /*
> + * The moment the device was linked into the bus's "klist_devices" in
> + * bus_add_device() then it's possible that probe could have been
> + * attempted in a different thread via userspace loading a driver
> + * matching the device. "ready_to_prove" being unset would have
nit; s/prove/probe/
> + * blocked those attempts. Now that all of the above initialization has
> + * happened, unblock probe. If probe happens through another thread
> + * after this point but before bus_probe_device() runs then it's fine.
> + * bus_probe_device() -> device_initial_probe() -> __device_attach()
> + * will notice (under device_lock) that the device is already bound.
> + */
> + dev_set_ready_to_probe(dev);
I think this lacks some ordering properties that we should be allowed
to rely on. In this case, the 'ready_to_probe' flag being set should
that all of the data structures are observable by another CPU.
Unfortunately, this doesn't seem to be the case, see below.
> +
> bus_probe_device(dev);
>
> /*
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index 37c7e54e0e4c..8ec93128ea98 100644
> --- a/drivers/base/dd.c
> +++ b/drivers/base/dd.c
> @@ -848,6 +848,18 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
> if (dev->driver)
> return -EBUSY;
>
> + /*
> + * In device_add(), the "struct device" gets linked into the subsystem's
> + * list of devices and broadcast to userspace (via uevent) before we're
> + * quite ready to probe. Those open pathways to driver probe before
> + * we've finished enough of device_add() to reliably support probe.
> + * Detect this and tell other pathways to try again later. device_add()
> + * itself will also try to probe immediately after setting
> + * "ready_to_probe".
> + */
> + if (!dev_ready_to_probe(dev))
> + return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n");
> +
> dev->can_match = true;
> dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
> drv->bus->name, __func__, drv->name);
> diff --git a/include/linux/device.h b/include/linux/device.h
> index e65d564f01cd..5eb0b22958e4 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -458,6 +458,21 @@ struct device_physical_location {
> bool lid;
> };
>
> +/**
> + * enum struct_device_flags - Flags in struct device
> + *
> + * Each flag should have a set of accessor functions created via
> + * __create_dev_flag_accessors() for each access.
> + *
> + * @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough
> + * initialization that probe could be called.
> + */
> +enum struct_device_flags {
> + DEV_FLAG_READY_TO_PROBE = 0,
> +
> + DEV_FLAG_COUNT
> +};
> +
> /**
> * struct device - The basic device structure
> * @parent: The device's "parent" device, the device to which it is attached.
> @@ -553,6 +568,7 @@ struct device_physical_location {
> * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers.
> * @dma_iommu: Device is using default IOMMU implementation for DMA and
> * doesn't rely on dma_ops structure.
> + * @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify.
> *
> * At the lowest level, every device in a Linux system is represented by an
> * instance of struct device. The device structure contains the information
> @@ -675,8 +691,34 @@ struct device {
> #ifdef CONFIG_IOMMU_DMA
> bool dma_iommu:1;
> #endif
> +
> + DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
> };
>
> +#define __create_dev_flag_accessors(accessor_name, flag_name) \
> +static inline bool dev_##accessor_name(const struct device *dev) \
> +{ \
> + return test_bit(flag_name, dev->flags); \
> +} \
> +static inline void dev_set_##accessor_name(struct device *dev) \
> +{ \
> + set_bit(flag_name, dev->flags); \
Atomic operations that are not RMW or that do not return a value are
unordered (see Documentation/atomic_bitops.txt). This implies that
observing the flag being set from another CPU does not guarantee that
the previous stores in program order are observed.
For that guarantee to hold, you'd need to have an
smp_mb__before_atomic() just before set_bit(), giving it release
semantics. This is equally valid for the test, clear and assign
variants.
I doubt this issue is visible on a busy system (which would be the
case at boot time), but I thought I'd mention it anyway.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
next prev parent reply other threads:[~2026-04-06 6:32 UTC|newest]
Thread overview: 29+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
2026-04-04 0:04 ` [PATCH v4 1/9] driver core: Don't let a device probe until it's ready Douglas Anderson
2026-04-04 17:35 ` Danilo Krummrich
2026-04-05 20:58 ` Danilo Krummrich
2026-04-05 22:39 ` Doug Anderson
2026-04-06 6:39 ` Greg Kroah-Hartman
2026-04-06 14:15 ` Danilo Krummrich
2026-04-06 6:32 ` Marc Zyngier [this message]
2026-04-06 14:41 ` Doug Anderson
2026-04-06 14:59 ` Danilo Krummrich
2026-04-06 16:34 ` Marc Zyngier
2026-04-06 16:43 ` Danilo Krummrich
2026-04-06 17:06 ` Marc Zyngier
2026-04-06 18:11 ` Danilo Krummrich
2026-04-06 18:59 ` Doug Anderson
2026-04-06 16:45 ` Doug Anderson
2026-04-04 0:04 ` [PATCH v4 2/9] driver core: Replace dev->can_match with dev_can_match() Douglas Anderson
2026-04-04 0:04 ` [PATCH v4 3/9] driver core: Replace dev->dma_iommu with dev_dma_iommu() Douglas Anderson
2026-04-04 0:04 ` [PATCH v4 4/9] driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync() Douglas Anderson
2026-04-04 0:04 ` [PATCH v4 5/9] driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass() Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 6/9] driver core: Replace dev->state_synced with dev_state_synced() Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 7/9] driver core: Replace dev->dma_coherent with dev_dma_coherent() Douglas Anderson
2026-04-06 5:49 ` Vinod Koul
2026-04-04 0:05 ` [PATCH v4 8/9] driver core: Replace dev->of_node_reused with dev_of_node_reused() Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 9/9] driver core: Replace dev->offline + ->offline_disabled with accessors Douglas Anderson
2026-04-04 17:11 ` [PATCH v4 0/9] driver core: Fix some race conditions Rafael J. Wysocki
2026-04-05 5:27 ` Greg Kroah-Hartman
2026-04-05 12:02 ` Danilo Krummrich
2026-04-05 22:43 ` Doug Anderson
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=873418d2fz.wl-maz@kernel.org \
--to=maz@kernel.org \
--cc=aik@ozlabs.ru \
--cc=aleksander.lobakin@intel.com \
--cc=dakr@kernel.org \
--cc=dianders@chromium.org \
--cc=driver-core@lists.linux.dev \
--cc=edumazet@google.com \
--cc=gregkh@linuxfoundation.org \
--cc=hch@lst.de \
--cc=johan@kernel.org \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=rafael@kernel.org \
--cc=robin.murphy@arm.com \
--cc=saravanak@kernel.org \
--cc=stable@vger.kernel.org \
--cc=stern@rowland.harvard.edu \
/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