* [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
@ 2026-04-04 0:04 ` Douglas Anderson
2026-04-04 17:35 ` Danilo Krummrich
` (2 more replies)
2026-04-04 0:04 ` [PATCH v4 2/9] driver core: Replace dev->can_match with dev_can_match() Douglas Anderson
` (9 subsequent siblings)
10 siblings, 3 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:04 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, stable, driver-core, linux-kernel
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]:
really_probe()
__driver_probe_device()
driver_probe_device()
__driver_attach()
bus_for_each_dev()
driver_attach()
bus_add_driver()
driver_register()
__platform_driver_register()
init_module() [some module]
do_one_initcall()
do_init_module()
load_module()
__arm64_sys_finit_module()
invoke_syscall()
As a result of the above, it was seen that device_links_driver_bound()
could be called for the device before "dev->fwnode->dev" was
assigned. This prevented __fw_devlink_pickup_dangling_consumers() from
being called which meant that other devices waiting on our driver's
sub-nodes were stuck deferring forever.
It's believed that this problem is showing up suddenly for two
reasons:
1. Android has recently (last ~1 year) implemented an optimization to
the order it loads modules [2]. When devices opt-in to this faster
loading, modules are loaded one-after-the-other very quickly. This
is unlike how other distributions do it. The reproduction of this
problem has only been seen on devices that opt-in to Android's
"parallel module loading".
2. Android devices typically opt-in to fw_devlink, and the most
noticeable issue is the NULL "dev->fwnode->dev" in
device_links_driver_bound(). fw_devlink is somewhat new code and
also not in use by all Linux devices.
Even though the specific symptom where "dev->fwnode->dev" wasn't
assigned could be fixed by moving that assignment higher in
device_add(), other parts of device_add() (like the call to
device_pm_add()) are also important to run before probe. Only moving
the "dev->fwnode->dev" assignment would likely fix the current
symptoms but lead to difficult-to-debug problems in the future.
Fix the problem by preventing probe until device_add() has run far
enough that the device is ready to probe. If somehow we end up trying
to probe before we're allowed, __driver_probe_device() will return
-EPROBE_DEFER which will make certain the device is noticed.
In the race condition that was seen with Android's faster module
loading, we will temporarily add the device to the deferred list and
then take it off immediately when device_add() probes the device.
Instead of adding another flag to the bitfields already in "struct
device", instead add a new "flags" field and use that. This allows us
to freely change the bit from different thread without holding the
device lock and without worrying about corrupting nearby bits.
[1] Captured on a machine running a downstream 6.6 kernel
[2] https://cs.android.com/android/platform/superproject/main/+/main:system/core/libmodprobe/libmodprobe.cpp?q=LoadModulesParallel
Cc: stable@vger.kernel.org
Fixes: 2023c610dc54 ("Driver core: add new device to bus's list before probing")
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
v1: https://lore.kernel.org/r/20260320200656.RFC.1.Id750b0fbcc94f23ed04b7aecabcead688d0d8c17@changeid
v2: https://lore.kernel.org/r/20260330072839.v2.1.Id750b0fbcc94f23ed04b7aecabcead688d0d8c17@changeid
v3: https://lore.kernel.org/r/20260403005005.30424-1-dianders@chromium.org/
As of v2 this feels like a very safe change. It doesn't change the
ordering of any steps of probe and it _just_ prevents the early probe
from happening.
I ran tests where I turned the printout "Device not ready_to_probe" on
and I could see the printout happening, evidence of the race occurring
from other printouts, and things successfully being resolved.
I kept Alan and Rafael's Reviewed-by tags in v3 even though I changed
the implementation to use "flags" as per Danilo's request. This didn't
seem like a major enough change to remove tags, but please shout if
you'd like your tag removed.
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- Use a new "flags" bitfield
- Add missing \n in probe error message
Changes in v2:
- Instead of adjusting the ordering, use "ready_to_probe" flag
drivers/base/core.c | 13 +++++++++++++
drivers/base/dd.c | 12 ++++++++++++
include/linux/device.h | 42 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
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
+ * 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);
+
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); \
+} \
+static inline void dev_clear_##accessor_name(struct device *dev) \
+{ \
+ clear_bit(flag_name, dev->flags); \
+} \
+static inline void dev_assign_##accessor_name(struct device *dev, bool value) \
+{ \
+ assign_bit(flag_name, dev->flags, value); \
+} \
+static inline bool dev_test_and_set_##accessor_name(struct device *dev) \
+{ \
+ return test_and_set_bit(flag_name, dev->flags); \
+}
+
+__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
+
/**
* struct device_link - Device link representation.
* @supplier: The device on the supplier end of the link.
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
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-06 6:32 ` Marc Zyngier
2 siblings, 0 replies; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-04 17:35 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Sat Apr 4, 2026 at 2:04 AM CEST, Douglas Anderson wrote:
> +#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); \
> +} \
> +static inline void dev_clear_##accessor_name(struct device *dev) \
> +{ \
> + clear_bit(flag_name, dev->flags); \
> +} \
> +static inline void dev_assign_##accessor_name(struct device *dev, bool value) \
> +{ \
> + assign_bit(flag_name, dev->flags, value); \
> +} \
> +static inline bool dev_test_and_set_##accessor_name(struct device *dev) \
> +{ \
> + return test_and_set_bit(flag_name, dev->flags); \
> +}
> +
> +__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
Since this is a public header included in a lot of places, it might be worth to
#undef the macro once done defining all accessors.
> +
> /**
> * struct device_link - Device link representation.
> * @supplier: The device on the supplier end of the link.
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
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:32 ` Marc Zyngier
2 siblings, 1 reply; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-05 20:58 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Sat Apr 4, 2026 at 2:04 AM CEST, Douglas Anderson wrote:
> Instead of adding another flag to the bitfields already in "struct
> device", instead add a new "flags" field and use that. This allows us
> to freely change the bit from different thread without holding the
> device lock and without worrying about corrupting nearby bits.
I was just about to pick up this patch series (Greg mentioned to pick it up next
week, but we agreed offlist that I will pick it now, so it gets a few more
cycles in linux-next).
Due to this, taking a second glance at the code, I noticed the below issue.
> 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
> + * 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);
By converting this to a bitop, we now avoid races with other bitfields (such as
dev->can_match), but I think we still need to take the device lock for this one
specifically:
Task 0 (device_add): Task 1 (__driver_probe_device):
dev->fwnode->dev = dev;
device_lock(dev);
device_lock(dev); if (dev_ready_to_probe())
dev_set_ready_to_probe() access(fwnode->dev);
device_unlock(dev); device_unlock(dev);
Otherwise, nothing prevents the above dev->fwnode->dev = dev assignment to be
re-ordered with dev_set_ready_to_probe() and we are back to the problem the
commit attempts to solve in the first place.
(Technically, this could also be solved with explicit memory barriers - here and
below -, but __driver_probe_device() is always called with the device lock held,
so just taking the device lock seems *much* simpler. Also, in the absolute
majority of cases the lock should be uncontended in device_add() anyways.)
> +
> 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;
Focused on ordering from the above, I also noticed that this ordering of
dev_ready_to_probe() and dev->can_match = true is actually pretty subtle and we
should add the following comment.
/*
* Set can_match = true after calling dev_ready_to_probe(), so
* driver_deferred_probe_add() won't actually add the device to the
* deferred probe list when dev_ready_to_probe() returns false.
*
* When dev_ready_to_probe() returns false, it means that device_add()
* will do another probe() attempt for us.
*/
As it would be nice to land this for v7.1-rc1, I can apply both changes on
apply, i.e. not need to resend AFAIC.
Greg, Rafael: does that work for you?
Thanks,
Danilo
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-05 20:58 ` Danilo Krummrich
@ 2026-04-05 22:39 ` Doug Anderson
2026-04-06 6:39 ` Greg Kroah-Hartman
0 siblings, 1 reply; 29+ messages in thread
From: Doug Anderson @ 2026-04-05 22:39 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
Hi,
On Sun, Apr 5, 2026 at 1:58 PM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Sat Apr 4, 2026 at 2:04 AM CEST, Douglas Anderson wrote:
> > Instead of adding another flag to the bitfields already in "struct
> > device", instead add a new "flags" field and use that. This allows us
> > to freely change the bit from different thread without holding the
> > device lock and without worrying about corrupting nearby bits.
>
> I was just about to pick up this patch series (Greg mentioned to pick it up next
> week, but we agreed offlist that I will pick it now, so it gets a few more
> cycles in linux-next).
>
> Due to this, taking a second glance at the code, I noticed the below issue.
>
> > 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
> > + * 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);
>
> By converting this to a bitop, we now avoid races with other bitfields (such as
> dev->can_match), but I think we still need to take the device lock for this one
> specifically:
>
> Task 0 (device_add): Task 1 (__driver_probe_device):
>
> dev->fwnode->dev = dev;
> device_lock(dev);
> device_lock(dev); if (dev_ready_to_probe())
> dev_set_ready_to_probe() access(fwnode->dev);
> device_unlock(dev); device_unlock(dev);
>
> Otherwise, nothing prevents the above dev->fwnode->dev = dev assignment to be
> re-ordered with dev_set_ready_to_probe() and we are back to the problem the
> commit attempts to solve in the first place.
Ah, that sounds like a reasonable concern, and I agree that taking the
device_lock() here seems like the cleanest solution.
> > @@ -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;
>
> Focused on ordering from the above, I also noticed that this ordering of
> dev_ready_to_probe() and dev->can_match = true is actually pretty subtle and we
> should add the following comment.
>
> /*
> * Set can_match = true after calling dev_ready_to_probe(), so
> * driver_deferred_probe_add() won't actually add the device to the
> * deferred probe list when dev_ready_to_probe() returns false.
> *
> * When dev_ready_to_probe() returns false, it means that device_add()
> * will do another probe() attempt for us.
> */
Sure. That seems useful for future readers.
> As it would be nice to land this for v7.1-rc1, I can apply both changes on
> apply, i.e. not need to resend AFAIC.
Thanks! I'm happy to resend a new version if need be, but I'm also
happy if you want to make changes when applying.
-Doug
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-05 22:39 ` Doug Anderson
@ 2026-04-06 6:39 ` Greg Kroah-Hartman
2026-04-06 14:15 ` Danilo Krummrich
0 siblings, 1 reply; 29+ messages in thread
From: Greg Kroah-Hartman @ 2026-04-06 6:39 UTC (permalink / raw)
To: Doug Anderson
Cc: Danilo Krummrich, Rafael J . Wysocki, Alan Stern, Saravana Kannan,
Christoph Hellwig, Eric Dumazet, Johan Hovold, Leon Romanovsky,
Alexander Lobakin, Alexey Kardashevskiy, Robin Murphy, stable,
driver-core, linux-kernel
On Sun, Apr 05, 2026 at 03:39:26PM -0700, Doug Anderson wrote:
> Hi,
>
> On Sun, Apr 5, 2026 at 1:58 PM Danilo Krummrich <dakr@kernel.org> wrote:
> >
> > On Sat Apr 4, 2026 at 2:04 AM CEST, Douglas Anderson wrote:
> > > Instead of adding another flag to the bitfields already in "struct
> > > device", instead add a new "flags" field and use that. This allows us
> > > to freely change the bit from different thread without holding the
> > > device lock and without worrying about corrupting nearby bits.
> >
> > I was just about to pick up this patch series (Greg mentioned to pick it up next
> > week, but we agreed offlist that I will pick it now, so it gets a few more
> > cycles in linux-next).
> >
> > Due to this, taking a second glance at the code, I noticed the below issue.
> >
> > > 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
> > > + * 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);
> >
> > By converting this to a bitop, we now avoid races with other bitfields (such as
> > dev->can_match), but I think we still need to take the device lock for this one
> > specifically:
> >
> > Task 0 (device_add): Task 1 (__driver_probe_device):
> >
> > dev->fwnode->dev = dev;
> > device_lock(dev);
> > device_lock(dev); if (dev_ready_to_probe())
> > dev_set_ready_to_probe() access(fwnode->dev);
> > device_unlock(dev); device_unlock(dev);
> >
> > Otherwise, nothing prevents the above dev->fwnode->dev = dev assignment to be
> > re-ordered with dev_set_ready_to_probe() and we are back to the problem the
> > commit attempts to solve in the first place.
>
> Ah, that sounds like a reasonable concern, and I agree that taking the
> device_lock() here seems like the cleanest solution.
>
>
> > > @@ -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;
> >
> > Focused on ordering from the above, I also noticed that this ordering of
> > dev_ready_to_probe() and dev->can_match = true is actually pretty subtle and we
> > should add the following comment.
> >
> > /*
> > * Set can_match = true after calling dev_ready_to_probe(), so
> > * driver_deferred_probe_add() won't actually add the device to the
> > * deferred probe list when dev_ready_to_probe() returns false.
> > *
> > * When dev_ready_to_probe() returns false, it means that device_add()
> > * will do another probe() attempt for us.
> > */
>
> Sure. That seems useful for future readers.
>
>
> > As it would be nice to land this for v7.1-rc1, I can apply both changes on
> > apply, i.e. not need to resend AFAIC.
>
> Thanks! I'm happy to resend a new version if need be, but I'm also
> happy if you want to make changes when applying.
New version is always best :)
thanks,
greg k-h
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 6:39 ` Greg Kroah-Hartman
@ 2026-04-06 14:15 ` Danilo Krummrich
0 siblings, 0 replies; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-06 14:15 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Doug Anderson, Rafael J . Wysocki, Alan Stern, Saravana Kannan,
Christoph Hellwig, Eric Dumazet, Johan Hovold, Leon Romanovsky,
Alexander Lobakin, Alexey Kardashevskiy, Robin Murphy, stable,
driver-core, linux-kernel
On Mon Apr 6, 2026 at 8:39 AM CEST, Greg Kroah-Hartman wrote:
> On Sun, Apr 05, 2026 at 03:39:26PM -0700, Doug Anderson wrote:
>> Thanks! I'm happy to resend a new version if need be, but I'm also
>> happy if you want to make changes when applying.
>
> New version is always best :)
Yeah, a new version sounds good -- thanks Doug!
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
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-06 6:32 ` Marc Zyngier
2026-04-06 14:41 ` Doug Anderson
2 siblings, 1 reply; 29+ messages in thread
From: Marc Zyngier @ 2026-04-06 6:32 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, stable, driver-core,
linux-kernel
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.
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 6:32 ` Marc Zyngier
@ 2026-04-06 14:41 ` Doug Anderson
2026-04-06 14:59 ` Danilo Krummrich
2026-04-06 16:34 ` Marc Zyngier
0 siblings, 2 replies; 29+ messages in thread
From: Doug Anderson @ 2026-04-06 14:41 UTC (permalink / raw)
To: Marc Zyngier
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, stable, driver-core,
linux-kernel
Hi,
On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
>
> > + * 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.
I agree. I think Danilo was proposing fixing this by just doing:
device_lock(dev);
dev_set_ready_to_probe(dev);
device_unlock(dev);
While that's a bit of an overkill, it also works I think. Do folks
have a preference for what they'd like to see in v5?
> > @@ -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.
Are you suggesting I add smp memory barriers directly in all the
accessors? ...or just that clients of these functions should use
memory barriers as appropriate?
In other words, would I do:
smp_mb__before_atomic();
dev_set_ready_to_probe(dev);
...or add the barrier into all of the accessor?
My thought was to not add the barrier into the accessors since at
least one of the accessors talks about being run from a hot path
(dma_reset_need_sync()). ...but I just want to make sure.
-Doug
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 14:41 ` Doug Anderson
@ 2026-04-06 14:59 ` Danilo Krummrich
2026-04-06 16:34 ` Marc Zyngier
1 sibling, 0 replies; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-06 14:59 UTC (permalink / raw)
To: Doug Anderson
Cc: Marc Zyngier, Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Mon Apr 6, 2026 at 4:41 PM CEST, Doug Anderson wrote:
> Hi,
>
> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
>>
>> > + * 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.
>
> I agree. I think Danilo was proposing fixing this by just doing:
>
> device_lock(dev);
> dev_set_ready_to_probe(dev);
> device_unlock(dev);
>
> While that's a bit of an overkill, it also works I think. Do folks
> have a preference for what they'd like to see in v5?
Except for the rare case where device_add() races with driver_attach(), which is
exactly the race that should be fixed by this, the device lock will be
uncontended in device_add(), so I don't consider this overkill.
>> > @@ -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.
>
> Are you suggesting I add smp memory barriers directly in all the
> accessors? ...or just that clients of these functions should use
> memory barriers as appropriate?
>
> In other words, would I do:
>
> smp_mb__before_atomic();
> dev_set_ready_to_probe(dev);
>
> ...or add the barrier into all of the accessor?
I think this would be a bit overkill; all (other) fields are either already
protected by a lock, or are not prone to reordering races otherwise.
> My thought was to not add the barrier into the accessors since at
> least one of the accessors talks about being run from a hot path
> (dma_reset_need_sync()). ...but I just want to make sure.
>
> -Doug
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
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 16:45 ` Doug Anderson
1 sibling, 2 replies; 29+ messages in thread
From: Marc Zyngier @ 2026-04-06 16:34 UTC (permalink / raw)
To: Doug Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, stable, driver-core,
linux-kernel
On Mon, 06 Apr 2026 15:41:08 +0100,
Doug Anderson <dianders@chromium.org> wrote:
>
> Hi,
>
> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
> >
> > > + * 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.
>
> I agree. I think Danilo was proposing fixing this by just doing:
>
> device_lock(dev);
> dev_set_ready_to_probe(dev);
> device_unlock(dev);
>
> While that's a bit of an overkill, it also works I think. Do folks
> have a preference for what they'd like to see in v5?
It would work, but I find the construct rather obscure, and it implies
that there is a similar lock taken on the read path. Looking at the
code for a couple of minutes doesn't lead to an immediate clue that
such lock is indeed taken on all read paths.
>
>
> > > @@ -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.
>
> Are you suggesting I add smp memory barriers directly in all the
> accessors? ...or just that clients of these functions should use
> memory barriers as appropriate?
>
> In other words, would I do:
>
> smp_mb__before_atomic();
> dev_set_ready_to_probe(dev);
>
> ...or add the barrier into all of the accessor?
>
> My thought was to not add the barrier into the accessors since at
> least one of the accessors talks about being run from a hot path
> (dma_reset_need_sync()). ...but I just want to make sure.
I don't think this needs to be inflicted on all flags, specially the
ones you are simply moving into the bitmap and that didn't have any
particular ordering requirements. 'ready_to_probe' is a bit different,
as it is new and tries to offer ordering semantics.
So an open-coded barrier on both sides would do the trick, unless you
go for the lock and can convince yourself that it is indeed always
acquired on all the read paths.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 16:34 ` Marc Zyngier
@ 2026-04-06 16:43 ` Danilo Krummrich
2026-04-06 17:06 ` Marc Zyngier
2026-04-06 16:45 ` Doug Anderson
1 sibling, 1 reply; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-06 16:43 UTC (permalink / raw)
To: Marc Zyngier
Cc: Doug Anderson, Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Mon Apr 6, 2026 at 6:34 PM CEST, Marc Zyngier wrote:
> On Mon, 06 Apr 2026 15:41:08 +0100,
> Doug Anderson <dianders@chromium.org> wrote:
>>
>> Hi,
>>
>> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
>> >
>> > > + * 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.
>>
>> I agree. I think Danilo was proposing fixing this by just doing:
>>
>> device_lock(dev);
>> dev_set_ready_to_probe(dev);
>> device_unlock(dev);
>>
>> While that's a bit of an overkill, it also works I think. Do folks
>> have a preference for what they'd like to see in v5?
>
> It would work, but I find the construct rather obscure, and it implies
> that there is a similar lock taken on the read path. Looking at the
> code for a couple of minutes doesn't lead to an immediate clue that
> such lock is indeed taken on all read paths.
Why do you think this is obscure? As I mentioned in [1], the whole purpose of
dev_set_ready_to_probe() is to protect against a concurrent probe() attempt of
driver_attach() in __driver_probe_device(), while __driver_probe_device() is
protected by the device lock is by design.
[1] https://lore.kernel.org/driver-core/DHM5TCBT6GDE.EFG3IPRP99G7@kernel.org/
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 16:43 ` Danilo Krummrich
@ 2026-04-06 17:06 ` Marc Zyngier
2026-04-06 18:11 ` Danilo Krummrich
0 siblings, 1 reply; 29+ messages in thread
From: Marc Zyngier @ 2026-04-06 17:06 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Doug Anderson, Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Mon, 06 Apr 2026 17:43:22 +0100,
"Danilo Krummrich" <dakr@kernel.org> wrote:
>
> On Mon Apr 6, 2026 at 6:34 PM CEST, Marc Zyngier wrote:
> > On Mon, 06 Apr 2026 15:41:08 +0100,
> > Doug Anderson <dianders@chromium.org> wrote:
> >>
> >> Hi,
> >>
> >> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
> >> >
> >> > > + * 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.
> >>
> >> I agree. I think Danilo was proposing fixing this by just doing:
> >>
> >> device_lock(dev);
> >> dev_set_ready_to_probe(dev);
> >> device_unlock(dev);
> >>
> >> While that's a bit of an overkill, it also works I think. Do folks
> >> have a preference for what they'd like to see in v5?
> >
> > It would work, but I find the construct rather obscure, and it implies
> > that there is a similar lock taken on the read path. Looking at the
> > code for a couple of minutes doesn't lead to an immediate clue that
> > such lock is indeed taken on all read paths.
>
> Why do you think this is obscure?
Because you're not using the lock to protect any data. You're using
the lock for its release effect. Yes, it works. But the combination of
atomics *and* locking is just odd. You normally pick one model or the
other, not a combination of both.
> As I mentioned in [1], the whole purpose of
> dev_set_ready_to_probe() is to protect against a concurrent probe() attempt of
> driver_attach() in __driver_probe_device(), while __driver_probe_device() is
> protected by the device lock is by design.
>
> [1] https://lore.kernel.org/driver-core/DHM5TCBT6GDE.EFG3IPRP99G7@kernel.org/
I don't have much skin in this game, and you seem to have strong
opinions about how these things are supposed to work. So whatever
floats your boat, as long as it is correct.
Thanks,
M.
--
Jazz isn't dead. It just smells funny.
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 17:06 ` Marc Zyngier
@ 2026-04-06 18:11 ` Danilo Krummrich
2026-04-06 18:59 ` Doug Anderson
0 siblings, 1 reply; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-06 18:11 UTC (permalink / raw)
To: Marc Zyngier
Cc: Doug Anderson, Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
On Mon Apr 6, 2026 at 7:06 PM CEST, Marc Zyngier wrote:
> On Mon, 06 Apr 2026 17:43:22 +0100,
> "Danilo Krummrich" <dakr@kernel.org> wrote:
>>
>> On Mon Apr 6, 2026 at 6:34 PM CEST, Marc Zyngier wrote:
>> > On Mon, 06 Apr 2026 15:41:08 +0100,
>> > Doug Anderson <dianders@chromium.org> wrote:
>> >>
>> >> Hi,
>> >>
>> >> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
>> >> >
>> >> > > + * 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.
>> >>
>> >> I agree. I think Danilo was proposing fixing this by just doing:
>> >>
>> >> device_lock(dev);
>> >> dev_set_ready_to_probe(dev);
>> >> device_unlock(dev);
>> >>
>> >> While that's a bit of an overkill, it also works I think. Do folks
>> >> have a preference for what they'd like to see in v5?
>> >
>> > It would work, but I find the construct rather obscure, and it implies
>> > that there is a similar lock taken on the read path. Looking at the
>> > code for a couple of minutes doesn't lead to an immediate clue that
>> > such lock is indeed taken on all read paths.
>>
>> Why do you think this is obscure?
>
> Because you're not using the lock to protect any data. You're using
> the lock for its release effect. Yes, it works. But the combination of
> atomics *and* locking is just odd. You normally pick one model or the
> other, not a combination of both.
Yeah, the choice of bitops was purely because previously (in v2) this was a C
bitfield member in struct device protected with the device lock. But, not all of
the bitfield members were protected by the same lock or protected by a lock at
all, which would have made this racy with the other bitfield members. I.e. the
choice of bitops was independent; see also [2] for context.
[2] https://lore.kernel.org/driver-core/DHH1PD0ASG8H.1K3KG9L658DYN@kernel.org/
>> As I mentioned in [1], the whole purpose of
>> dev_set_ready_to_probe() is to protect against a concurrent probe() attempt of
>> driver_attach() in __driver_probe_device(), while __driver_probe_device() is
>> protected by the device lock is by design.
>>
>> [1] https://lore.kernel.org/driver-core/DHM5TCBT6GDE.EFG3IPRP99G7@kernel.org/
>
> I don't have much skin in this game, and you seem to have strong
> opinions about how these things are supposed to work. So whatever
> floats your boat, as long as it is correct.
Not overly, it's more about calling out the fact that probe() paths are
serialized through the device lock by design, so it seems natural to protect
dev_set_ready_to_probe() with the device lock.
The fact that dev_set_ready_to_probe() uses a bitop under the hood is an
implementation detail, i.e. it could also be an independent boolean.
That said, as I caught the issue in [3], I also mentioned the option of an
explicit memory barrier in device_add() and __driver_probe_device(). I.e. I'm
not entirely against it, but I think the device lock is a bit cleaner.
[3] https://lore.kernel.org/driver-core/DHLITCTY913U.J59JSQOVL0NH@kernel.org/
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 18:11 ` Danilo Krummrich
@ 2026-04-06 18:59 ` Doug Anderson
0 siblings, 0 replies; 29+ messages in thread
From: Doug Anderson @ 2026-04-06 18:59 UTC (permalink / raw)
To: Danilo Krummrich
Cc: Marc Zyngier, Greg Kroah-Hartman, Rafael J . Wysocki, Alan Stern,
Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, stable, driver-core, linux-kernel
Hi,
On Mon, Apr 6, 2026 at 11:11 AM Danilo Krummrich <dakr@kernel.org> wrote:
>
> On Mon Apr 6, 2026 at 7:06 PM CEST, Marc Zyngier wrote:
> > On Mon, 06 Apr 2026 17:43:22 +0100,
> > "Danilo Krummrich" <dakr@kernel.org> wrote:
> >>
> >> On Mon Apr 6, 2026 at 6:34 PM CEST, Marc Zyngier wrote:
> >> > On Mon, 06 Apr 2026 15:41:08 +0100,
> >> > Doug Anderson <dianders@chromium.org> wrote:
> >> >>
> >> >> Hi,
> >> >>
> >> >> On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
> >> >> >
> >> >> > > + * 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.
> >> >>
> >> >> I agree. I think Danilo was proposing fixing this by just doing:
> >> >>
> >> >> device_lock(dev);
> >> >> dev_set_ready_to_probe(dev);
> >> >> device_unlock(dev);
> >> >>
> >> >> While that's a bit of an overkill, it also works I think. Do folks
> >> >> have a preference for what they'd like to see in v5?
> >> >
> >> > It would work, but I find the construct rather obscure, and it implies
> >> > that there is a similar lock taken on the read path. Looking at the
> >> > code for a couple of minutes doesn't lead to an immediate clue that
> >> > such lock is indeed taken on all read paths.
> >>
> >> Why do you think this is obscure?
> >
> > Because you're not using the lock to protect any data. You're using
> > the lock for its release effect. Yes, it works. But the combination of
> > atomics *and* locking is just odd. You normally pick one model or the
> > other, not a combination of both.
>
> Yeah, the choice of bitops was purely because previously (in v2) this was a C
> bitfield member in struct device protected with the device lock. But, not all of
> the bitfield members were protected by the same lock or protected by a lock at
> all, which would have made this racy with the other bitfield members. I.e. the
> choice of bitops was independent; see also [2] for context.
>
> [2] https://lore.kernel.org/driver-core/DHH1PD0ASG8H.1K3KG9L658DYN@kernel.org/
I've changed the snippet in the commit description to now justify the
use of bitops like this:
Instead of adding another flag to the bitfields already in "struct
device", instead add a new "flags" field and use that. This allows us
to freely change the bit from different thread without worrying about
corrupting nearby bits (and means threads changing other bit won't
corrupt us).
> >> As I mentioned in [1], the whole purpose of
> >> dev_set_ready_to_probe() is to protect against a concurrent probe() attempt of
> >> driver_attach() in __driver_probe_device(), while __driver_probe_device() is
> >> protected by the device lock is by design.
> >>
> >> [1] https://lore.kernel.org/driver-core/DHM5TCBT6GDE.EFG3IPRP99G7@kernel.org/
> >
> > I don't have much skin in this game, and you seem to have strong
> > opinions about how these things are supposed to work. So whatever
> > floats your boat, as long as it is correct.
>
> Not overly, it's more about calling out the fact that probe() paths are
> serialized through the device lock by design, so it seems natural to protect
> dev_set_ready_to_probe() with the device lock.
>
> The fact that dev_set_ready_to_probe() uses a bitop under the hood is an
> implementation detail, i.e. it could also be an independent boolean.
>
> That said, as I caught the issue in [3], I also mentioned the option of an
> explicit memory barrier in device_add() and __driver_probe_device(). I.e. I'm
> not entirely against it, but I think the device lock is a bit cleaner.
>
> [3] https://lore.kernel.org/driver-core/DHLITCTY913U.J59JSQOVL0NH@kernel.org/
I've got the series all prepped and it sounds as if the alignment is
on using device_lock(). I'll give it a few more hours in case there
are additional responses, then send a v5. ;-)
-Doug
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 1/9] driver core: Don't let a device probe until it's ready
2026-04-06 16:34 ` Marc Zyngier
2026-04-06 16:43 ` Danilo Krummrich
@ 2026-04-06 16:45 ` Doug Anderson
1 sibling, 0 replies; 29+ messages in thread
From: Doug Anderson @ 2026-04-06 16:45 UTC (permalink / raw)
To: Marc Zyngier
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, stable, driver-core,
linux-kernel
Hi,
On Mon, Apr 6, 2026 at 9:35 AM Marc Zyngier <maz@kernel.org> wrote:
>
> On Mon, 06 Apr 2026 15:41:08 +0100,
> Doug Anderson <dianders@chromium.org> wrote:
> >
> > Hi,
> >
> > On Sun, Apr 5, 2026 at 11:32 PM Marc Zyngier <maz@kernel.org> wrote:
> > >
> > > > + * 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.
> >
> > I agree. I think Danilo was proposing fixing this by just doing:
> >
> > device_lock(dev);
> > dev_set_ready_to_probe(dev);
> > device_unlock(dev);
> >
> > While that's a bit of an overkill, it also works I think. Do folks
> > have a preference for what they'd like to see in v5?
>
> It would work, but I find the construct rather obscure, and it implies
> that there is a similar lock taken on the read path. Looking at the
> code for a couple of minutes doesn't lead to an immediate clue that
> such lock is indeed taken on all read paths.
Yeah, it's definitely taken on all read paths. It is only accessed in
__driver_probe_device(). __driver_probe_device() is called in two
places.
1. From device_driver_attach(), it's called with
"__device_driver_lock()" held, which includes the device lock.
2. From driver_probe_device().
...then we look at driver_probe_device(). That's called from three places.
1. From __driver_attach_async_helper, it's called with
"__device_driver_lock()" held, which includes the device lock.
2. From __driver_attach(), it's called with "__device_driver_lock()"
held, which includes the device lock.
3. From __device_attach_driver()
...then we look at __device_attach_driver(). That's called from two places:
1. From __device_attach_async_helper(), which holds the device lock.
2. From __device_attach(), which holds the device lock.
...assuming I didn't mess up, that covers them all.
> > Are you suggesting I add smp memory barriers directly in all the
> > accessors? ...or just that clients of these functions should use
> > memory barriers as appropriate?
> >
> > In other words, would I do:
> >
> > smp_mb__before_atomic();
> > dev_set_ready_to_probe(dev);
> >
> > ...or add the barrier into all of the accessor?
> >
> > My thought was to not add the barrier into the accessors since at
> > least one of the accessors talks about being run from a hot path
> > (dma_reset_need_sync()). ...but I just want to make sure.
>
> I don't think this needs to be inflicted on all flags, specially the
> ones you are simply moving into the bitmap and that didn't have any
> particular ordering requirements. 'ready_to_probe' is a bit different,
> as it is new and tries to offer ordering semantics.
OK, cool. Just wanted to make sure I was understanding!
> So an open-coded barrier on both sides would do the trick, unless you
> go for the lock and can convince yourself that it is indeed always
> acquired on all the read paths.
I'm pretty convinced it's acquired on all read paths. :-)
-Doug
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 2/9] driver core: Replace dev->can_match with dev_can_match()
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 0:04 ` Douglas Anderson
2026-04-04 0:04 ` [PATCH v4 3/9] driver core: Replace dev->dma_iommu with dev_dma_iommu() Douglas Anderson
` (8 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:04 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, driver-core, linux-kernel
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "can_match" over to the "flags" field
so modifications are safe.
Cc: Saravana Kannan <saravanak@kernel.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
drivers/base/core.c | 10 +++++-----
drivers/base/dd.c | 8 ++++----
include/linux/device.h | 9 +++++----
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f07745659de3..8acf7f684417 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1011,7 +1011,7 @@ static void device_links_missing_supplier(struct device *dev)
static bool dev_is_best_effort(struct device *dev)
{
- return (fw_devlink_best_effort && dev->can_match) ||
+ return (fw_devlink_best_effort && dev_can_match(dev)) ||
(dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT));
}
@@ -1079,7 +1079,7 @@ int device_links_check_suppliers(struct device *dev)
if (dev_is_best_effort(dev) &&
device_link_test(link, DL_FLAG_INFERRED) &&
- !link->supplier->can_match) {
+ !dev_can_match(link->supplier)) {
ret = -EAGAIN;
continue;
}
@@ -1370,7 +1370,7 @@ void device_links_driver_bound(struct device *dev)
} else if (dev_is_best_effort(dev) &&
device_link_test(link, DL_FLAG_INFERRED) &&
link->status != DL_STATE_CONSUMER_PROBE &&
- !link->supplier->can_match) {
+ !dev_can_match(link->supplier)) {
/*
* When dev_is_best_effort() is true, we ignore device
* links to suppliers that don't have a driver. If the
@@ -1758,7 +1758,7 @@ static int fw_devlink_no_driver(struct device *dev, void *data)
{
struct device_link *link = to_devlink(dev);
- if (!link->supplier->can_match)
+ if (!dev_can_match(link->supplier))
fw_devlink_relax_link(link);
return 0;
@@ -3708,7 +3708,7 @@ int device_add(struct device *dev)
* match with any driver, don't block its consumers from probing in
* case the consumer device is able to operate without this supplier.
*/
- if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match)
+ if (dev->fwnode && fw_devlink_drv_reg_done && !dev_can_match(dev))
fw_devlink_unblock_consumers(dev);
if (parent)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 8ec93128ea98..c7c0851ae073 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -132,7 +132,7 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func);
void driver_deferred_probe_add(struct device *dev)
{
- if (!dev->can_match)
+ if (!dev_can_match(dev))
return;
mutex_lock(&deferred_probe_mutex);
@@ -860,7 +860,7 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
if (!dev_ready_to_probe(dev))
return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n");
- dev->can_match = true;
+ dev_set_can_match(dev);
dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
drv->bus->name, __func__, drv->name);
@@ -1006,7 +1006,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
- dev->can_match = true;
+ dev_set_can_match(dev);
driver_deferred_probe_add(dev);
/*
* Device can't match with a driver right now, so don't attempt
@@ -1258,7 +1258,7 @@ static int __driver_attach(struct device *dev, void *data)
return 0;
} else if (ret == -EPROBE_DEFER) {
dev_dbg(dev, "Device match requests probe deferral\n");
- dev->can_match = true;
+ dev_set_can_match(dev);
driver_deferred_probe_add(dev);
/*
* Driver could not match with device, but may match with
diff --git a/include/linux/device.h b/include/linux/device.h
index 5eb0b22958e4..5e42261ba3aa 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -466,9 +466,13 @@ struct device_physical_location {
*
* @DEV_FLAG_READY_TO_PROBE: If set then device_add() has finished enough
* initialization that probe could be called.
+ * @DEV_FLAG_CAN_MATCH: The device has matched with a driver at least once or it
+ * is in a bus (like AMBA) which can't check for matching drivers
+ * until other devices probe successfully.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
+ DEV_FLAG_CAN_MATCH = 1,
DEV_FLAG_COUNT
};
@@ -555,9 +559,6 @@ enum struct_device_flags {
* @state_synced: The hardware state of this device has been synced to match
* the software state of this device by calling the driver/bus
* sync_state() callback.
- * @can_match: The device has matched with a driver at least once or it is in
- * a bus (like AMBA) which can't check for matching drivers until
- * other devices probe successfully.
* @dma_coherent: this particular device is dma coherent, even if the
* architecture supports non-coherent devices.
* @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
@@ -676,7 +677,6 @@ struct device {
bool offline:1;
bool of_node_reused:1;
bool state_synced:1;
- bool can_match:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
@@ -718,6 +718,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \
}
__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
+__create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH);
/**
* struct device_link - Device link representation.
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 3/9] driver core: Replace dev->dma_iommu with dev_dma_iommu()
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 0:04 ` [PATCH v4 2/9] driver core: Replace dev->can_match with dev_can_match() Douglas Anderson
@ 2026-04-04 0:04 ` 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
` (7 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:04 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, driver-core, iommu, joro,
linux-kernel, will
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "dma_iommu" over to the "flags" field
so modifications are safe.
Cc: Leon Romanovsky <leon@kernel.org>
Cc: Robin Murphy <robin.murphy@arm.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
NOTE: even though previously we only took up a bit if
CONFIG_IOMMU_DMA, in this change I reserve the bit unconditionally.
While we could get the "dynamic" behavior by changing the flags
definition to be an "enum", it doesn't seem worth it at this
point. This also allows us to move one "#ifdef" to an "if", getting
better compile-time testing of both sides of the "if".
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
drivers/iommu/dma-iommu.c | 9 ++++++---
drivers/iommu/iommu.c | 5 ++---
include/linux/device.h | 9 ++++-----
include/linux/iommu-dma.h | 3 ++-
4 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 94d514169642..036994069b80 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2112,18 +2112,21 @@ EXPORT_SYMBOL_GPL(dma_iova_destroy);
void iommu_setup_dma_ops(struct device *dev, struct iommu_domain *domain)
{
+ bool dma_iommu;
+
if (dev_is_pci(dev))
dev->iommu->pci_32bit_workaround = !iommu_dma_forcedac;
- dev->dma_iommu = iommu_is_dma_domain(domain);
- if (dev->dma_iommu && iommu_dma_init_domain(domain, dev))
+ dma_iommu = iommu_is_dma_domain(domain);
+ dev_assign_dma_iommu(dev, dma_iommu);
+ if (dma_iommu && iommu_dma_init_domain(domain, dev))
goto out_err;
return;
out_err:
pr_warn("Failed to set up IOMMU for device %s; retaining platform DMA ops\n",
dev_name(dev));
- dev->dma_iommu = false;
+ dev_clear_dma_iommu(dev);
}
static bool has_msi_cookie(const struct iommu_domain *domain)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 50718ab810a4..8dc50a0eee85 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -589,9 +589,8 @@ static void iommu_deinit_device(struct device *dev)
dev->iommu_group = NULL;
module_put(ops->owner);
dev_iommu_free(dev);
-#ifdef CONFIG_IOMMU_DMA
- dev->dma_iommu = false;
-#endif
+ if (IS_ENABLED(CONFIG_IOMMU_DMA))
+ dev_clear_dma_iommu(dev);
}
static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
diff --git a/include/linux/device.h b/include/linux/device.h
index 5e42261ba3aa..feb11ba1ba71 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -469,10 +469,13 @@ struct device_physical_location {
* @DEV_FLAG_CAN_MATCH: The device has matched with a driver at least once or it
* is in a bus (like AMBA) which can't check for matching drivers
* until other devices probe successfully.
+ * @DEV_FLAG_DMA_IOMMU: Device is using default IOMMU implementation for DMA and
+ * doesn't rely on dma_ops structure.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
DEV_FLAG_CAN_MATCH = 1,
+ DEV_FLAG_DMA_IOMMU = 2,
DEV_FLAG_COUNT
};
@@ -567,8 +570,6 @@ enum struct_device_flags {
* for dma allocations. This flag is managed by the dma ops
* instance from ->dma_supported.
* @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
@@ -688,9 +689,6 @@ struct device {
#ifdef CONFIG_DMA_NEED_SYNC
bool dma_skip_sync:1;
#endif
-#ifdef CONFIG_IOMMU_DMA
- bool dma_iommu:1;
-#endif
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -719,6 +717,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \
__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
__create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH);
+__create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU);
/**
* struct device_link - Device link representation.
diff --git a/include/linux/iommu-dma.h b/include/linux/iommu-dma.h
index a92b3ff9b934..060f6e23ab3c 100644
--- a/include/linux/iommu-dma.h
+++ b/include/linux/iommu-dma.h
@@ -7,12 +7,13 @@
#ifndef _LINUX_IOMMU_DMA_H
#define _LINUX_IOMMU_DMA_H
+#include <linux/device.h>
#include <linux/dma-direction.h>
#ifdef CONFIG_IOMMU_DMA
static inline bool use_dma_iommu(struct device *dev)
{
- return dev->dma_iommu;
+ return dev_dma_iommu(dev);
}
#else
static inline bool use_dma_iommu(struct device *dev)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 4/9] driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync()
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (2 preceding siblings ...)
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 ` 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
` (6 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:04 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, Andrew Morton, Jason Gunthorpe,
driver-core, iommu, linux-kernel, linux-mm, m.szyprowski
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "dma_skip_sync" over to the "flags"
field so modifications are safe.
Cc: Alexander Lobakin <aleksander.lobakin@intel.com>
Cc: Eric Dumazet <edumazet@google.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
NOTE: even though previously we only took up a bit if
CONFIG_DMA_NEED_SYNC, in this change I reserve the bit
unconditionally. While we could get the "dynamic" behavior by changing
the flags definition to be an "enum", it doesn't seem worth it at this
point.
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
include/linux/device.h | 8 ++++----
include/linux/dma-map-ops.h | 4 ++--
include/linux/dma-mapping.h | 2 +-
kernel/dma/mapping.c | 8 ++++----
mm/hmm.c | 2 +-
5 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/include/linux/device.h b/include/linux/device.h
index feb11ba1ba71..fde0c72c3159 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -471,11 +471,14 @@ struct device_physical_location {
* until other devices probe successfully.
* @DEV_FLAG_DMA_IOMMU: Device is using default IOMMU implementation for DMA and
* doesn't rely on dma_ops structure.
+ * @DEV_FLAG_DMA_SKIP_SYNC: DMA sync operations can be skipped for coherent
+ * buffers.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
DEV_FLAG_CAN_MATCH = 1,
DEV_FLAG_DMA_IOMMU = 2,
+ DEV_FLAG_DMA_SKIP_SYNC = 3,
DEV_FLAG_COUNT
};
@@ -569,7 +572,6 @@ enum struct_device_flags {
* and optionall (if the coherent mask is large enough) also
* for dma allocations. This flag is managed by the dma ops
* instance from ->dma_supported.
- * @dma_skip_sync: DMA sync operations can be skipped for coherent buffers.
* @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
@@ -686,9 +688,6 @@ struct device {
#ifdef CONFIG_DMA_OPS_BYPASS
bool dma_ops_bypass : 1;
#endif
-#ifdef CONFIG_DMA_NEED_SYNC
- bool dma_skip_sync:1;
-#endif
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -718,6 +717,7 @@ static inline bool dev_test_and_set_##accessor_name(struct device *dev) \
__create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
__create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH);
__create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU);
+__create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC);
/**
* struct device_link - Device link representation.
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 60b63756df82..edd7de60a957 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -245,8 +245,8 @@ static inline void dma_reset_need_sync(struct device *dev)
{
#ifdef CONFIG_DMA_NEED_SYNC
/* Reset it only once so that the function can be called on hotpath */
- if (unlikely(dev->dma_skip_sync))
- dev->dma_skip_sync = false;
+ if (unlikely(dev_dma_skip_sync(dev)))
+ dev_clear_dma_skip_sync(dev);
#endif
}
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 99ef042ecdb4..ef4feb2b37d8 100644
--- a/include/linux/dma-mapping.h
+++ b/include/linux/dma-mapping.h
@@ -419,7 +419,7 @@ bool __dma_need_sync(struct device *dev, dma_addr_t dma_addr);
static inline bool dma_dev_need_sync(const struct device *dev)
{
/* Always call DMA sync operations when debugging is enabled */
- return !dev->dma_skip_sync || IS_ENABLED(CONFIG_DMA_API_DEBUG);
+ return !dev_dma_skip_sync(dev) || IS_ENABLED(CONFIG_DMA_API_DEBUG);
}
static inline void dma_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 6d3dd0bd3a88..76fc65d1a8a4 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -467,7 +467,7 @@ bool dma_need_unmap(struct device *dev)
{
if (!dma_map_direct(dev, get_dma_ops(dev)))
return true;
- if (!dev->dma_skip_sync)
+ if (!dev_dma_skip_sync(dev))
return true;
return IS_ENABLED(CONFIG_DMA_API_DEBUG);
}
@@ -483,16 +483,16 @@ static void dma_setup_need_sync(struct device *dev)
* mapping, if any. During the device initialization, it's
* enough to check only for the DMA coherence.
*/
- dev->dma_skip_sync = dev_is_dma_coherent(dev);
+ dev_assign_dma_skip_sync(dev, dev_is_dma_coherent(dev));
else if (!ops->sync_single_for_device && !ops->sync_single_for_cpu &&
!ops->sync_sg_for_device && !ops->sync_sg_for_cpu)
/*
* Synchronization is not possible when none of DMA sync ops
* is set.
*/
- dev->dma_skip_sync = true;
+ dev_set_dma_skip_sync(dev);
else
- dev->dma_skip_sync = false;
+ dev_clear_dma_skip_sync(dev);
}
#else /* !CONFIG_DMA_NEED_SYNC */
static inline void dma_setup_need_sync(struct device *dev) { }
diff --git a/mm/hmm.c b/mm/hmm.c
index 5955f2f0c83d..c72c9ddfdb2f 100644
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -709,7 +709,7 @@ int hmm_dma_map_alloc(struct device *dev, struct hmm_dma_map *map,
* best approximation to ensure no swiotlb buffering happens.
*/
#ifdef CONFIG_DMA_NEED_SYNC
- dma_need_sync = !dev->dma_skip_sync;
+ dma_need_sync = !dev_dma_skip_sync(dev);
#endif /* CONFIG_DMA_NEED_SYNC */
if (dma_need_sync || dma_addressing_limited(dev))
return -EOPNOTSUPP;
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 5/9] driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass()
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (3 preceding siblings ...)
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 ` Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 6/9] driver core: Replace dev->state_synced with dev_state_synced() Douglas Anderson
` (5 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:04 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, chleroy, driver-core, gbatra,
iommu, jgg, linux-kernel, linuxppc-dev, m.szyprowski, maddy, mpe,
npiggin
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "dma_ops_bypass" over to the "flags"
field so modifications are safe.
Cc: Christoph Hellwig <hch@lst.de>
Cc: Alexey Kardashevskiy <aik@ozlabs.ru>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
NOTE: even though previously we only took up a bit if
CONFIG_DMA_OPS_BYPASS, in this change I reserve the bit
unconditionally. While we could get the "dynamic" behavior by
changing the flags definition to be an "enum", it doesn't seem worth
it at this point. This also allows us to move one "#ifdef" to an "if",
getting better compile-time testing of both sides of the "if".
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
arch/powerpc/kernel/dma-iommu.c | 8 ++++----
include/linux/device.h | 15 +++++++--------
kernel/dma/mapping.c | 4 +---
3 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c
index 73e10bd4d56d..6d1899b38c3d 100644
--- a/arch/powerpc/kernel/dma-iommu.c
+++ b/arch/powerpc/kernel/dma-iommu.c
@@ -67,7 +67,7 @@ bool arch_dma_unmap_sg_direct(struct device *dev, struct scatterlist *sg,
}
bool arch_dma_alloc_direct(struct device *dev)
{
- if (dev->dma_ops_bypass)
+ if (dev_dma_ops_bypass(dev))
return true;
return false;
@@ -75,7 +75,7 @@ bool arch_dma_alloc_direct(struct device *dev)
bool arch_dma_free_direct(struct device *dev, dma_addr_t dma_handle)
{
- if (!dev->dma_ops_bypass)
+ if (!dev_dma_ops_bypass(dev))
return false;
return is_direct_handle(dev, dma_handle);
@@ -164,7 +164,7 @@ int dma_iommu_dma_supported(struct device *dev, u64 mask)
* fixed ops will be used for RAM. This is limited by
* bus_dma_limit which is set when RAM is pre-mapped.
*/
- dev->dma_ops_bypass = true;
+ dev_set_dma_ops_bypass(dev);
dev_info(dev, "iommu: 64-bit OK but direct DMA is limited by %llx\n",
dev->bus_dma_limit);
return 1;
@@ -185,7 +185,7 @@ int dma_iommu_dma_supported(struct device *dev, u64 mask)
}
dev_dbg(dev, "iommu: not 64-bit, using default ops\n");
- dev->dma_ops_bypass = false;
+ dev_clear_dma_ops_bypass(dev);
return 1;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index fde0c72c3159..2ce89e3cfab9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -473,12 +473,18 @@ struct device_physical_location {
* doesn't rely on dma_ops structure.
* @DEV_FLAG_DMA_SKIP_SYNC: DMA sync operations can be skipped for coherent
* buffers.
+ * @DEV_FLAG_DMA_OPS_BYPASS: If set then the dma_ops are bypassed for the
+ * streaming DMA operations (->map_* / ->unmap_* / ->sync_*), and
+ * optional (if the coherent mask is large enough) also for dma
+ * allocations. This flag is managed by the dma ops instance from
+ * ->dma_supported.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
DEV_FLAG_CAN_MATCH = 1,
DEV_FLAG_DMA_IOMMU = 2,
DEV_FLAG_DMA_SKIP_SYNC = 3,
+ DEV_FLAG_DMA_OPS_BYPASS = 4,
DEV_FLAG_COUNT
};
@@ -567,11 +573,6 @@ enum struct_device_flags {
* sync_state() callback.
* @dma_coherent: this particular device is dma coherent, even if the
* architecture supports non-coherent devices.
- * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the
- * streaming DMA operations (->map_* / ->unmap_* / ->sync_*),
- * and optionall (if the coherent mask is large enough) also
- * for dma allocations. This flag is managed by the dma ops
- * instance from ->dma_supported.
* @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
@@ -685,9 +686,6 @@ struct device {
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
bool dma_coherent:1;
#endif
-#ifdef CONFIG_DMA_OPS_BYPASS
- bool dma_ops_bypass : 1;
-#endif
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -718,6 +716,7 @@ __create_dev_flag_accessors(ready_to_probe, DEV_FLAG_READY_TO_PROBE);
__create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH);
__create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU);
__create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC);
+__create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS);
/**
* struct device_link - Device link representation.
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 76fc65d1a8a4..30bf8455730a 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -126,11 +126,9 @@ static bool dma_go_direct(struct device *dev, dma_addr_t mask,
if (likely(!ops))
return true;
-#ifdef CONFIG_DMA_OPS_BYPASS
- if (dev->dma_ops_bypass)
+ if (IS_ENABLED(CONFIG_DMA_OPS_BYPASS) && dev_dma_ops_bypass(dev))
return min_not_zero(mask, dev->bus_dma_limit) >=
dma_direct_get_required_mask(dev);
-#endif
return false;
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 6/9] driver core: Replace dev->state_synced with dev_state_synced()
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (4 preceding siblings ...)
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 ` Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 7/9] driver core: Replace dev->dma_coherent with dev_dma_coherent() Douglas Anderson
` (4 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, driver-core, linux-kernel
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "state_synced" over to the "flags"
field so modifications are safe.
Cc: Saravana Kannan <saravanak@kernel.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
drivers/base/core.c | 8 ++++----
drivers/base/dd.c | 8 +++-----
include/linux/device.h | 9 +++++----
3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 8acf7f684417..0986051a6f14 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1123,7 +1123,7 @@ static void __device_links_queue_sync_state(struct device *dev,
if (!dev_has_sync_state(dev))
return;
- if (dev->state_synced)
+ if (dev_state_synced(dev))
return;
list_for_each_entry(link, &dev->links.consumers, s_node) {
@@ -1138,7 +1138,7 @@ static void __device_links_queue_sync_state(struct device *dev,
* than once. This can happen if new consumers get added to the device
* and probed before the list is flushed.
*/
- dev->state_synced = true;
+ dev_set_state_synced(dev);
if (WARN_ON(!list_empty(&dev->links.defer_sync)))
return;
@@ -1779,7 +1779,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data)
struct device *sup = link->supplier;
if (!device_link_test(link, DL_FLAG_MANAGED) ||
- link->status == DL_STATE_ACTIVE || sup->state_synced ||
+ link->status == DL_STATE_ACTIVE || dev_state_synced(sup) ||
!dev_has_sync_state(sup))
return 0;
@@ -1793,7 +1793,7 @@ static int fw_devlink_dev_sync_state(struct device *dev, void *data)
return 0;
dev_warn(sup, "Timed out. Forcing sync_state()\n");
- sup->state_synced = true;
+ dev_set_state_synced(sup);
get_device(sup);
list_add_tail(&sup->links.defer_sync, data);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index c7c0851ae073..e1132690652f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -581,12 +581,10 @@ static ssize_t state_synced_store(struct device *dev,
return -EINVAL;
device_lock(dev);
- if (!dev->state_synced) {
- dev->state_synced = true;
+ if (!dev_test_and_set_state_synced(dev))
dev_sync_state(dev);
- } else {
+ else
ret = -EINVAL;
- }
device_unlock(dev);
return ret ? ret : count;
@@ -598,7 +596,7 @@ static ssize_t state_synced_show(struct device *dev,
bool val;
device_lock(dev);
- val = dev->state_synced;
+ val = dev_state_synced(dev);
device_unlock(dev);
return sysfs_emit(buf, "%u\n", val);
diff --git a/include/linux/device.h b/include/linux/device.h
index 2ce89e3cfab9..a1d59ff9702c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -478,6 +478,9 @@ struct device_physical_location {
* optional (if the coherent mask is large enough) also for dma
* allocations. This flag is managed by the dma ops instance from
* ->dma_supported.
+ * @DEV_FLAG_STATE_SYNCED: The hardware state of this device has been synced to
+ * match the software state of this device by calling the
+ * driver/bus sync_state() callback.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
@@ -485,6 +488,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_IOMMU = 2,
DEV_FLAG_DMA_SKIP_SYNC = 3,
DEV_FLAG_DMA_OPS_BYPASS = 4,
+ DEV_FLAG_STATE_SYNCED = 5,
DEV_FLAG_COUNT
};
@@ -568,9 +572,6 @@ enum struct_device_flags {
* @offline: Set after successful invocation of bus type's .offline().
* @of_node_reused: Set if the device-tree node is shared with an ancestor
* device.
- * @state_synced: The hardware state of this device has been synced to match
- * the software state of this device by calling the driver/bus
- * sync_state() callback.
* @dma_coherent: this particular device is dma coherent, even if the
* architecture supports non-coherent devices.
* @flags: DEV_FLAG_XXX flags. Use atomic bitfield operations to modify.
@@ -680,7 +681,6 @@ struct device {
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
- bool state_synced:1;
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
@@ -717,6 +717,7 @@ __create_dev_flag_accessors(can_match, DEV_FLAG_CAN_MATCH);
__create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU);
__create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC);
__create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS);
+__create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED);
/**
* struct device_link - Device link representation.
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 7/9] driver core: Replace dev->dma_coherent with dev_dma_coherent()
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (5 preceding siblings ...)
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 ` 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
` (3 subsequent siblings)
10 siblings, 1 reply; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, Frank.Li, alex, andre.przywara,
andrew, aou, catalin.marinas, dmaengine, driver-core,
gregory.clement, iommu, jgg, kees, linux-arm-kernel, linux-kernel,
linux-mips, linux-riscv, linux-snps-arc, linux, m.szyprowski,
palmer, peter.ujfalusi, pjw, sebastian.hesselbarth, tsbogend,
vgupta, vkoul, will, willy
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "dma_coherent" over to the "flags"
field so modifications are safe.
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
NOTE: even though previously we only took up a bit if
CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE, CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU,
or CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL, in this change I reserve the
bit unconditionally. While we could get the "dynamic" behavior by
changing the flags definition to be an "enum", it doesn't seem worth
it at this point.
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
arch/arc/mm/dma.c | 4 ++--
arch/arm/mach-highbank/highbank.c | 2 +-
arch/arm/mach-mvebu/coherency.c | 2 +-
arch/arm/mm/dma-mapping-nommu.c | 4 ++--
arch/arm/mm/dma-mapping.c | 28 ++++++++++++++--------------
arch/arm64/mm/dma-mapping.c | 2 +-
arch/mips/mm/dma-noncoherent.c | 2 +-
arch/riscv/mm/dma-noncoherent.c | 2 +-
drivers/base/core.c | 2 +-
drivers/dma/ti/k3-udma-glue.c | 6 +++---
drivers/dma/ti/k3-udma.c | 6 +++---
include/linux/device.h | 11 ++++-------
include/linux/dma-map-ops.h | 2 +-
13 files changed, 35 insertions(+), 38 deletions(-)
diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 6b85e94f3275..9b9adb02b4c5 100644
--- a/arch/arc/mm/dma.c
+++ b/arch/arc/mm/dma.c
@@ -98,8 +98,8 @@ void arch_setup_dma_ops(struct device *dev, bool coherent)
* DMA buffers.
*/
if (is_isa_arcv2() && ioc_enable && coherent)
- dev->dma_coherent = true;
+ dev_set_dma_coherent(dev);
dev_info(dev, "use %scoherent DMA ops\n",
- dev->dma_coherent ? "" : "non");
+ dev_dma_coherent(dev) ? "" : "non");
}
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
index 47335c7dadf8..8b7d0929dac4 100644
--- a/arch/arm/mach-highbank/highbank.c
+++ b/arch/arm/mach-highbank/highbank.c
@@ -98,7 +98,7 @@ static int highbank_platform_notifier(struct notifier_block *nb,
if (of_property_read_bool(dev->of_node, "dma-coherent")) {
val = readl(sregs_base + reg);
writel(val | 0xff01, sregs_base + reg);
- dev->dma_coherent = true;
+ dev_set_dma_coherent(dev);
}
return NOTIFY_OK;
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c
index fa2c1e1aeb96..7234d487ff39 100644
--- a/arch/arm/mach-mvebu/coherency.c
+++ b/arch/arm/mach-mvebu/coherency.c
@@ -95,7 +95,7 @@ static int mvebu_hwcc_notifier(struct notifier_block *nb,
if (event != BUS_NOTIFY_ADD_DEVICE)
return NOTIFY_DONE;
- dev->dma_coherent = true;
+ dev_set_dma_coherent(dev);
return NOTIFY_OK;
}
diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
index fecac107fd0d..c6a70686507b 100644
--- a/arch/arm/mm/dma-mapping-nommu.c
+++ b/arch/arm/mm/dma-mapping-nommu.c
@@ -42,11 +42,11 @@ void arch_setup_dma_ops(struct device *dev, bool coherent)
* enough to check if MPU is in use or not since in absence of
* MPU system memory map is used.
*/
- dev->dma_coherent = cacheid ? coherent : true;
+ dev_assign_dma_coherent(dev, cacheid ? coherent : true);
} else {
/*
* Assume coherent DMA in case MMU/MPU has not been set up.
*/
- dev->dma_coherent = (get_cr() & CR_M) ? coherent : true;
+ dev_assign_dma_coherent(dev, (get_cr() & CR_M) ? coherent : true);
}
}
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f304037d1c34..f9bc53b60f99 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -1076,7 +1076,7 @@ static void *arm_iommu_alloc_attrs(struct device *dev, size_t size,
pgprot_t prot = __get_dma_pgprot(attrs, PAGE_KERNEL);
struct page **pages;
void *addr = NULL;
- int coherent_flag = dev->dma_coherent ? COHERENT : NORMAL;
+ int coherent_flag = dev_dma_coherent(dev) ? COHERENT : NORMAL;
*handle = DMA_MAPPING_ERROR;
size = PAGE_ALIGN(size);
@@ -1124,7 +1124,7 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
if (vma->vm_pgoff >= nr_pages)
return -ENXIO;
- if (!dev->dma_coherent)
+ if (!dev_dma_coherent(dev))
vma->vm_page_prot = __get_dma_pgprot(attrs, vma->vm_page_prot);
err = vm_map_pages(vma, pages, nr_pages);
@@ -1141,7 +1141,7 @@ static int arm_iommu_mmap_attrs(struct device *dev, struct vm_area_struct *vma,
static void arm_iommu_free_attrs(struct device *dev, size_t size, void *cpu_addr,
dma_addr_t handle, unsigned long attrs)
{
- int coherent_flag = dev->dma_coherent ? COHERENT : NORMAL;
+ int coherent_flag = dev_dma_coherent(dev) ? COHERENT : NORMAL;
struct page **pages;
size = PAGE_ALIGN(size);
@@ -1202,7 +1202,7 @@ static int __map_sg_chunk(struct device *dev, struct scatterlist *sg,
phys_addr_t phys = page_to_phys(sg_page(s));
unsigned int len = PAGE_ALIGN(s->offset + s->length);
- if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+ if (!dev_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_device(sg_phys(s), s->length, dir);
prot = __dma_info_to_prot(dir, attrs);
@@ -1304,7 +1304,7 @@ static void arm_iommu_unmap_sg(struct device *dev,
if (sg_dma_len(s))
__iommu_remove_mapping(dev, sg_dma_address(s),
sg_dma_len(s));
- if (!dev->dma_coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
+ if (!dev_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_cpu(sg_phys(s), s->length, dir);
}
}
@@ -1323,7 +1323,7 @@ static void arm_iommu_sync_sg_for_cpu(struct device *dev,
struct scatterlist *s;
int i;
- if (dev->dma_coherent)
+ if (dev_dma_coherent(dev))
return;
for_each_sg(sg, s, nents, i)
@@ -1345,7 +1345,7 @@ static void arm_iommu_sync_sg_for_device(struct device *dev,
struct scatterlist *s;
int i;
- if (dev->dma_coherent)
+ if (dev_dma_coherent(dev))
return;
for_each_sg(sg, s, nents, i)
@@ -1371,7 +1371,7 @@ static dma_addr_t arm_iommu_map_phys(struct device *dev, phys_addr_t phys,
dma_addr_t dma_addr;
int ret, prot;
- if (!dev->dma_coherent &&
+ if (!dev_dma_coherent(dev) &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
arch_sync_dma_for_device(phys, size, dir);
@@ -1412,7 +1412,7 @@ static void arm_iommu_unmap_phys(struct device *dev, dma_addr_t handle,
if (!iova)
return;
- if (!dev->dma_coherent &&
+ if (!dev_dma_coherent(dev) &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
phys_addr_t phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1431,7 +1431,7 @@ static void arm_iommu_sync_single_for_cpu(struct device *dev,
unsigned int offset = handle & ~PAGE_MASK;
phys_addr_t phys;
- if (dev->dma_coherent || !iova)
+ if (dev_dma_coherent(dev) || !iova)
return;
phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1446,7 +1446,7 @@ static void arm_iommu_sync_single_for_device(struct device *dev,
unsigned int offset = handle & ~PAGE_MASK;
phys_addr_t phys;
- if (dev->dma_coherent || !iova)
+ if (dev_dma_coherent(dev) || !iova)
return;
phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1701,13 +1701,13 @@ static void arm_teardown_iommu_dma_ops(struct device *dev) { }
void arch_setup_dma_ops(struct device *dev, bool coherent)
{
/*
- * Due to legacy code that sets the ->dma_coherent flag from a bus
- * notifier we can't just assign coherent to the ->dma_coherent flag
+ * Due to legacy code that sets the dma_coherent flag from a bus
+ * notifier we can't just assign coherent to the dma_coherent flag
* here, but instead have to make sure we only set but never clear it
* for now.
*/
if (coherent)
- dev->dma_coherent = true;
+ dev_set_dma_coherent(dev);
/*
* Don't override the dma_ops if they have already been set. Ideally
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
index b2b5792b2caa..dc1fce939451 100644
--- a/arch/arm64/mm/dma-mapping.c
+++ b/arch/arm64/mm/dma-mapping.c
@@ -48,7 +48,7 @@ void arch_setup_dma_ops(struct device *dev, bool coherent)
dev_driver_string(dev), dev_name(dev),
ARCH_DMA_MINALIGN, cls);
- dev->dma_coherent = coherent;
+ dev_assign_dma_coherent(dev, coherent);
xen_setup_dma_ops(dev);
}
diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c
index ab4f2a75a7d0..30ef3e247eb7 100644
--- a/arch/mips/mm/dma-noncoherent.c
+++ b/arch/mips/mm/dma-noncoherent.c
@@ -139,6 +139,6 @@ void arch_sync_dma_for_cpu(phys_addr_t paddr, size_t size,
#ifdef CONFIG_ARCH_HAS_SETUP_DMA_OPS
void arch_setup_dma_ops(struct device *dev, bool coherent)
{
- dev->dma_coherent = coherent;
+ dev_assign_dma_coherent(dev, coherent);
}
#endif
diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c
index cb89d7e0ba88..a1ec2d71d1c9 100644
--- a/arch/riscv/mm/dma-noncoherent.c
+++ b/arch/riscv/mm/dma-noncoherent.c
@@ -140,7 +140,7 @@ void arch_setup_dma_ops(struct device *dev, bool coherent)
"%s %s: device non-coherent but no non-coherent operations supported",
dev_driver_string(dev), dev_name(dev));
- dev->dma_coherent = coherent;
+ dev_assign_dma_coherent(dev, coherent);
}
void riscv_noncoherent_supported(void)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 0986051a6f14..531f02a5469a 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3173,7 +3173,7 @@ void device_initialize(struct device *dev)
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
- dev->dma_coherent = dma_default_coherent;
+ dev_assign_dma_coherent(dev, dma_default_coherent);
#endif
swiotlb_dev_init(dev);
}
diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c
index f87d244cc2d6..686dc140293e 100644
--- a/drivers/dma/ti/k3-udma-glue.c
+++ b/drivers/dma/ti/k3-udma-glue.c
@@ -312,7 +312,7 @@ k3_udma_glue_request_tx_chn_common(struct device *dev,
if (xudma_is_pktdma(tx_chn->common.udmax)) {
/* prepare the channel device as coherent */
- tx_chn->common.chan_dev.dma_coherent = true;
+ dev_set_dma_coherent(&tx_chn->common.chan_dev);
dma_coerce_mask_and_coherent(&tx_chn->common.chan_dev,
DMA_BIT_MASK(48));
}
@@ -1003,7 +1003,7 @@ k3_udma_glue_request_rx_chn_priv(struct device *dev, const char *name,
if (xudma_is_pktdma(rx_chn->common.udmax)) {
/* prepare the channel device as coherent */
- rx_chn->common.chan_dev.dma_coherent = true;
+ dev_set_dma_coherent(&rx_chn->common.chan_dev);
dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev,
DMA_BIT_MASK(48));
}
@@ -1104,7 +1104,7 @@ k3_udma_glue_request_remote_rx_chn_common(struct k3_udma_glue_rx_channel *rx_chn
if (xudma_is_pktdma(rx_chn->common.udmax)) {
/* prepare the channel device as coherent */
- rx_chn->common.chan_dev.dma_coherent = true;
+ dev_set_dma_coherent(&rx_chn->common.chan_dev);
dma_coerce_mask_and_coherent(&rx_chn->common.chan_dev,
DMA_BIT_MASK(48));
rx_chn->single_fdq = false;
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index c964ebfcf3b6..1cf158eb7bdb 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -428,18 +428,18 @@ static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel)
/* No special handling for the channel */
chan->dev->chan_dma_dev = false;
- chan_dev->dma_coherent = false;
+ dev_clear_dma_coherent(chan_dev);
chan_dev->dma_parms = NULL;
} else if (asel == 14 || asel == 15) {
chan->dev->chan_dma_dev = true;
- chan_dev->dma_coherent = true;
+ dev_set_dma_coherent(chan_dev);
dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48));
chan_dev->dma_parms = chan_dev->parent->dma_parms;
} else {
dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel);
- chan_dev->dma_coherent = false;
+ dev_clear_dma_coherent(chan_dev);
chan_dev->dma_parms = NULL;
}
}
diff --git a/include/linux/device.h b/include/linux/device.h
index a1d59ff9702c..fca986cef2ed 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -481,6 +481,8 @@ struct device_physical_location {
* @DEV_FLAG_STATE_SYNCED: The hardware state of this device has been synced to
* match the software state of this device by calling the
* driver/bus sync_state() callback.
+ * @DEV_FLAG_DMA_COHERENT: This particular device is dma coherent, even if the
+ * architecture supports non-coherent devices.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
@@ -489,6 +491,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_SKIP_SYNC = 3,
DEV_FLAG_DMA_OPS_BYPASS = 4,
DEV_FLAG_STATE_SYNCED = 5,
+ DEV_FLAG_DMA_COHERENT = 6,
DEV_FLAG_COUNT
};
@@ -572,8 +575,6 @@ enum struct_device_flags {
* @offline: Set after successful invocation of bus type's .offline().
* @of_node_reused: Set if the device-tree node is shared with an ancestor
* device.
- * @dma_coherent: this particular device is dma coherent, even if the
- * architecture supports non-coherent devices.
* @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
@@ -681,11 +682,6 @@ struct device {
bool offline_disabled:1;
bool offline:1;
bool of_node_reused:1;
-#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
- defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
- defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
- bool dma_coherent:1;
-#endif
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -718,6 +714,7 @@ __create_dev_flag_accessors(dma_iommu, DEV_FLAG_DMA_IOMMU);
__create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC);
__create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS);
__create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED);
+__create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT);
/**
* struct device_link - Device link representation.
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index edd7de60a957..44dd9035b4fe 100644
--- a/include/linux/dma-map-ops.h
+++ b/include/linux/dma-map-ops.h
@@ -230,7 +230,7 @@ int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
extern bool dma_default_coherent;
static inline bool dev_is_dma_coherent(struct device *dev)
{
- return dev->dma_coherent;
+ return dev_dma_coherent(dev);
}
#else
#define dma_default_coherent true
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 7/9] driver core: Replace dev->dma_coherent with dev_dma_coherent()
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
0 siblings, 0 replies; 29+ messages in thread
From: Vinod Koul @ 2026-04-06 5:49 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, Frank.Li, alex,
andre.przywara, andrew, aou, catalin.marinas, dmaengine,
driver-core, gregory.clement, iommu, jgg, kees, linux-arm-kernel,
linux-kernel, linux-mips, linux-riscv, linux-snps-arc, linux,
m.szyprowski, palmer, peter.ujfalusi, pjw, sebastian.hesselbarth,
tsbogend, vgupta, will, willy
On 03-04-26, 17:05, Douglas Anderson wrote:
> In C, bitfields are not necessarily safe to modify from multiple
> threads without locking. Switch "dma_coherent" over to the "flags"
> field so modifications are safe.
Acked-by: Vinod Koul <vkoul@kernel.org>
--
~Vinod
^ permalink raw reply [flat|nested] 29+ messages in thread
* [PATCH v4 8/9] driver core: Replace dev->of_node_reused with dev_of_node_reused()
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (6 preceding siblings ...)
2026-04-04 0:05 ` [PATCH v4 7/9] driver core: Replace dev->dma_coherent with dev_dma_coherent() Douglas Anderson
@ 2026-04-04 0:05 ` Douglas Anderson
2026-04-04 0:05 ` [PATCH v4 9/9] driver core: Replace dev->offline + ->offline_disabled with accessors Douglas Anderson
` (2 subsequent siblings)
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, Mark Brown, alexander.stein,
andrew, andrew, andriy.shevchenko, bhelgaas, brgl, davem,
devicetree, driver-core, hkallweit1, jirislaby, joel, kees, kuba,
lgirdwood, linux-arm-kernel, linux-aspeed, linux-kernel,
linux-pci, linux-serial, linux-usb, linux, mani, netdev, pabeni,
robh
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "of_node_reused" over to the "flags"
field so modifications are safe.
Cc: Johan Hovold <johan@kernel.org>
Acked-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
drivers/base/core.c | 2 +-
drivers/base/pinctrl.c | 2 +-
drivers/base/platform.c | 2 +-
drivers/net/pcs/pcs-xpcs-plat.c | 2 +-
drivers/of/device.c | 6 +++---
drivers/pci/of.c | 2 +-
drivers/pci/pwrctrl/core.c | 2 +-
drivers/regulator/bq257xx-regulator.c | 2 +-
drivers/regulator/rk808-regulator.c | 2 +-
drivers/tty/serial/serial_base_bus.c | 2 +-
drivers/usb/gadget/udc/aspeed-vhub/dev.c | 2 +-
include/linux/device.h | 7 ++++---
12 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 531f02a5469a..f12f3b53b4d0 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -5281,7 +5281,7 @@ void device_set_of_node_from_dev(struct device *dev, const struct device *dev2)
{
of_node_put(dev->of_node);
dev->of_node = of_node_get(dev2->of_node);
- dev->of_node_reused = true;
+ dev_set_of_node_reused(dev);
}
EXPORT_SYMBOL_GPL(device_set_of_node_from_dev);
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
index 6e250272c843..0bbc83231234 100644
--- a/drivers/base/pinctrl.c
+++ b/drivers/base/pinctrl.c
@@ -24,7 +24,7 @@ int pinctrl_bind_pins(struct device *dev)
{
int ret;
- if (dev->of_node_reused)
+ if (dev_of_node_reused(dev))
return 0;
dev->pins = devm_kzalloc(dev, sizeof(*(dev->pins)), GFP_KERNEL);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index d44591d52e36..199e6fb25770 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -856,7 +856,7 @@ struct platform_device *platform_device_register_full(
pdev->dev.parent = pdevinfo->parent;
pdev->dev.fwnode = pdevinfo->fwnode;
pdev->dev.of_node = of_node_get(to_of_node(pdev->dev.fwnode));
- pdev->dev.of_node_reused = pdevinfo->of_node_reused;
+ dev_assign_of_node_reused(&pdev->dev, pdevinfo->of_node_reused);
if (pdevinfo->dma_mask) {
pdev->platform_dma_mask = pdevinfo->dma_mask;
diff --git a/drivers/net/pcs/pcs-xpcs-plat.c b/drivers/net/pcs/pcs-xpcs-plat.c
index b8c48f9effbf..f4b1b8246ce9 100644
--- a/drivers/net/pcs/pcs-xpcs-plat.c
+++ b/drivers/net/pcs/pcs-xpcs-plat.c
@@ -349,7 +349,7 @@ static int xpcs_plat_init_dev(struct dw_xpcs_plat *pxpcs)
* up later. Make sure DD-core is aware of the OF-node being re-used.
*/
device_set_node(&mdiodev->dev, fwnode_handle_get(dev_fwnode(dev)));
- mdiodev->dev.of_node_reused = true;
+ dev_set_of_node_reused(&mdiodev->dev);
/* Pass the data further so the DW XPCS driver core could use it */
mdiodev->dev.platform_data = (void *)device_get_match_data(dev);
diff --git a/drivers/of/device.c b/drivers/of/device.c
index f7e75e527667..be4e1584e0af 100644
--- a/drivers/of/device.c
+++ b/drivers/of/device.c
@@ -26,7 +26,7 @@
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct device *dev)
{
- if (!matches || !dev->of_node || dev->of_node_reused)
+ if (!matches || !dev->of_node || dev_of_node_reused(dev))
return NULL;
return of_match_node(matches, dev->of_node);
}
@@ -192,7 +192,7 @@ ssize_t of_device_modalias(struct device *dev, char *str, ssize_t len)
{
ssize_t sl;
- if (!dev || !dev->of_node || dev->of_node_reused)
+ if (!dev || !dev->of_node || dev_of_node_reused(dev))
return -ENODEV;
sl = of_modalias(dev->of_node, str, len - 2);
@@ -254,7 +254,7 @@ int of_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env *
{
int sl;
- if ((!dev) || (!dev->of_node) || dev->of_node_reused)
+ if ((!dev) || (!dev->of_node) || dev_of_node_reused(dev))
return -ENODEV;
/* Devicetree modalias is tricky, we add it in 2 steps */
diff --git a/drivers/pci/of.c b/drivers/pci/of.c
index 9f8eb5df279e..1f9b669abdb0 100644
--- a/drivers/pci/of.c
+++ b/drivers/pci/of.c
@@ -38,7 +38,7 @@ int pci_set_of_node(struct pci_dev *dev)
struct device *pdev __free(put_device) =
bus_find_device_by_of_node(&platform_bus_type, node);
if (pdev)
- dev->bus->dev.of_node_reused = true;
+ dev_set_of_node_reused(&dev->bus->dev);
device_set_node(&dev->dev, of_fwnode_handle(no_free_ptr(node)));
return 0;
diff --git a/drivers/pci/pwrctrl/core.c b/drivers/pci/pwrctrl/core.c
index 7754baed67f2..72963a92362a 100644
--- a/drivers/pci/pwrctrl/core.c
+++ b/drivers/pci/pwrctrl/core.c
@@ -39,7 +39,7 @@ static int pci_pwrctrl_notify(struct notifier_block *nb, unsigned long action,
* If we got here then the PCI device is the second after the
* power control platform device. Mark its OF node as reused.
*/
- dev->of_node_reused = true;
+ dev_set_of_node_reused(dev);
break;
}
diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
index dab8f1ab4450..40e0f1a7ae81 100644
--- a/drivers/regulator/bq257xx-regulator.c
+++ b/drivers/regulator/bq257xx-regulator.c
@@ -143,7 +143,7 @@ static int bq257xx_regulator_probe(struct platform_device *pdev)
struct regulator_config cfg = {};
pdev->dev.of_node = pdev->dev.parent->of_node;
- pdev->dev.of_node_reused = true;
+ dev_set_of_node_reused(&pdev->dev);
pdata = devm_kzalloc(&pdev->dev, sizeof(struct bq257xx_reg_data), GFP_KERNEL);
if (!pdata)
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index e66408f23bb6..8297d31cde9f 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -2115,7 +2115,7 @@ static int rk808_regulator_probe(struct platform_device *pdev)
int ret, i, nregulators;
pdev->dev.of_node = pdev->dev.parent->of_node;
- pdev->dev.of_node_reused = true;
+ dev_set_of_node_reused(&pdev->dev);
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap)
diff --git a/drivers/tty/serial/serial_base_bus.c b/drivers/tty/serial/serial_base_bus.c
index a12935f6b992..5f23284a8778 100644
--- a/drivers/tty/serial/serial_base_bus.c
+++ b/drivers/tty/serial/serial_base_bus.c
@@ -74,7 +74,7 @@ static int serial_base_device_init(struct uart_port *port,
dev->parent = parent_dev;
dev->bus = &serial_base_bus_type;
dev->release = release;
- dev->of_node_reused = true;
+ dev_set_of_node_reused(dev);
device_set_node(dev, fwnode_handle_get(dev_fwnode(parent_dev)));
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
index 2ecd049dacc2..8b9449d16324 100644
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -593,7 +593,7 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
d->gadget.max_speed = USB_SPEED_HIGH;
d->gadget.speed = USB_SPEED_UNKNOWN;
d->gadget.dev.of_node = vhub->pdev->dev.of_node;
- d->gadget.dev.of_node_reused = true;
+ dev_set_of_node_reused(&d->gadget.dev);
rc = usb_add_gadget_udc(d->port_dev, &d->gadget);
if (rc != 0)
diff --git a/include/linux/device.h b/include/linux/device.h
index fca986cef2ed..8132aab17e04 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -483,6 +483,8 @@ struct device_physical_location {
* driver/bus sync_state() callback.
* @DEV_FLAG_DMA_COHERENT: This particular device is dma coherent, even if the
* architecture supports non-coherent devices.
+ * @DEV_FLAG_OF_NODE_REUSED: Set if the device-tree node is shared with an
+ * ancestor device.
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
@@ -492,6 +494,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_OPS_BYPASS = 4,
DEV_FLAG_STATE_SYNCED = 5,
DEV_FLAG_DMA_COHERENT = 6,
+ DEV_FLAG_OF_NODE_REUSED = 7,
DEV_FLAG_COUNT
};
@@ -573,8 +576,6 @@ enum struct_device_flags {
*
* @offline_disabled: If set, the device is permanently online.
* @offline: Set after successful invocation of bus type's .offline().
- * @of_node_reused: Set if the device-tree node is shared with an ancestor
- * device.
* @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
@@ -681,7 +682,6 @@ struct device {
bool offline_disabled:1;
bool offline:1;
- bool of_node_reused:1;
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -715,6 +715,7 @@ __create_dev_flag_accessors(dma_skip_sync, DEV_FLAG_DMA_SKIP_SYNC);
__create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS);
__create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED);
__create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT);
+__create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED);
/**
* struct device_link - Device link representation.
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* [PATCH v4 9/9] driver core: Replace dev->offline + ->offline_disabled with accessors
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (7 preceding siblings ...)
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 ` 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
10 siblings, 0 replies; 29+ messages in thread
From: Douglas Anderson @ 2026-04-04 0:05 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Saravana Kannan, Christoph Hellwig, Eric Dumazet, Johan Hovold,
Leon Romanovsky, Alexander Lobakin, Alexey Kardashevskiy,
Robin Murphy, Douglas Anderson, Mark Brown, ardb, catalin.marinas,
chleroy, david, driver-core, kees, kevin.brodsky, lenb,
linux-acpi, linux-arm-kernel, linux-cxl, linux-kernel, linux-mm,
linuxppc-dev, maddy, maz, miko.lenczewski, mpe, npiggin,
osalvador, oupton, peterz, tglx, will, yangyicong, yeoreum.yun
In C, bitfields are not necessarily safe to modify from multiple
threads without locking. Switch "offline" and "offline_disabled" over
to the "flags" field so modifications are safe.
Cc: Rafael J. Wysocki <rafael@kernel.org>
Acked-by: Mark Brown <broonie@kernel.org>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
---
Not fixing any known bugs; problem is theoretical and found by code
inspection. Change is done somewhat manually and only lightly tested
(mostly compile-time tested).
Changes in v4:
- Use accessor functions for flags
Changes in v3:
- New
arch/arm64/kernel/cpufeature.c | 2 +-
.../powerpc/platforms/pseries/hotplug-memory.c | 4 ++--
drivers/acpi/scan.c | 2 +-
drivers/base/core.c | 18 +++++++++---------
drivers/base/cpu.c | 4 ++--
drivers/base/memory.c | 2 +-
include/linux/device.h | 12 ++++++------
kernel/cpu.c | 4 ++--
8 files changed, 24 insertions(+), 24 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32c2dbcc0c64..c832aa565dc2 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -4042,7 +4042,7 @@ static int enable_mismatched_32bit_el0(unsigned int cpu)
*/
lucky_winner = cpu_32bit ? cpu : cpumask_any_and(cpu_32bit_el0_mask,
cpu_active_mask);
- get_cpu_device(lucky_winner)->offline_disabled = true;
+ dev_set_offline_disabled(get_cpu_device(lucky_winner));
setup_elf_hwcaps(compat_elf_hwcaps);
elf_hwcap_fixup();
pr_info("Asymmetric 32-bit EL0 support detected on CPU %u; CPU hot-unplug disabled on CPU %u\n",
diff --git a/arch/powerpc/platforms/pseries/hotplug-memory.c b/arch/powerpc/platforms/pseries/hotplug-memory.c
index b2f14db59034..75f85a5da981 100644
--- a/arch/powerpc/platforms/pseries/hotplug-memory.c
+++ b/arch/powerpc/platforms/pseries/hotplug-memory.c
@@ -213,9 +213,9 @@ static int dlpar_change_lmb_state(struct drmem_lmb *lmb, bool online)
return -EINVAL;
}
- if (online && mem_block->dev.offline)
+ if (online && dev_offline(&mem_block->dev))
rc = device_online(&mem_block->dev);
- else if (!online && !mem_block->dev.offline)
+ else if (!online && !dev_offline(&mem_block->dev))
rc = device_offline(&mem_block->dev);
else
rc = 0;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e8cdbdb46fdb..1353adbcc234 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -122,7 +122,7 @@ bool acpi_scan_is_offline(struct acpi_device *adev, bool uevent)
mutex_lock_nested(&adev->physical_node_lock, SINGLE_DEPTH_NESTING);
list_for_each_entry(pn, &adev->physical_node_list, node)
- if (device_supports_offline(pn->dev) && !pn->dev->offline) {
+ if (device_supports_offline(pn->dev) && !dev_offline(pn->dev)) {
if (uevent)
kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f12f3b53b4d0..79c85dbcffad 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2788,7 +2788,7 @@ static ssize_t online_show(struct device *dev, struct device_attribute *attr,
bool val;
device_lock(dev);
- val = !dev->offline;
+ val = !dev_offline(dev);
device_unlock(dev);
return sysfs_emit(buf, "%u\n", val);
}
@@ -2913,7 +2913,7 @@ static int device_add_attrs(struct device *dev)
if (error)
goto err_remove_type_groups;
- if (device_supports_offline(dev) && !dev->offline_disabled) {
+ if (device_supports_offline(dev) && !dev_offline_disabled(dev)) {
error = device_create_file(dev, &dev_attr_online);
if (error)
goto err_remove_dev_groups;
@@ -4178,7 +4178,7 @@ static int device_check_offline(struct device *dev, void *not_used)
if (ret)
return ret;
- return device_supports_offline(dev) && !dev->offline ? -EBUSY : 0;
+ return device_supports_offline(dev) && !dev_offline(dev) ? -EBUSY : 0;
}
/**
@@ -4196,7 +4196,7 @@ int device_offline(struct device *dev)
{
int ret;
- if (dev->offline_disabled)
+ if (dev_offline_disabled(dev))
return -EPERM;
ret = device_for_each_child(dev, NULL, device_check_offline);
@@ -4205,13 +4205,13 @@ int device_offline(struct device *dev)
device_lock(dev);
if (device_supports_offline(dev)) {
- if (dev->offline) {
+ if (dev_offline(dev)) {
ret = 1;
} else {
ret = dev->bus->offline(dev);
if (!ret) {
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
- dev->offline = true;
+ dev_set_offline(dev);
}
}
}
@@ -4236,11 +4236,11 @@ int device_online(struct device *dev)
device_lock(dev);
if (device_supports_offline(dev)) {
- if (dev->offline) {
+ if (dev_offline(dev)) {
ret = dev->bus->online(dev);
if (!ret) {
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
- dev->offline = false;
+ dev_clear_offline(dev);
}
} else {
ret = 1;
@@ -4714,7 +4714,7 @@ static int device_attrs_change_owner(struct device *dev, kuid_t kuid,
if (error)
return error;
- if (device_supports_offline(dev) && !dev->offline_disabled) {
+ if (device_supports_offline(dev) && !dev_offline_disabled(dev)) {
/* Change online device attributes of @dev to @kuid/@kgid. */
error = sysfs_file_change_owner(kobj, dev_attr_online.attr.name,
kuid, kgid);
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 875abdc9942e..19d288a3c80c 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -422,8 +422,8 @@ int register_cpu(struct cpu *cpu, int num)
cpu->dev.id = num;
cpu->dev.bus = &cpu_subsys;
cpu->dev.release = cpu_device_release;
- cpu->dev.offline_disabled = !cpu->hotpluggable;
- cpu->dev.offline = !cpu_online(num);
+ dev_assign_offline_disabled(&cpu->dev, !cpu->hotpluggable);
+ dev_assign_offline(&cpu->dev, !cpu_online(num));
cpu->dev.of_node = of_get_cpu_node(num, NULL);
cpu->dev.groups = common_cpu_attr_groups;
if (cpu->hotpluggable)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index a3091924918b..5005654f44fa 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -697,7 +697,7 @@ static int __add_memory_block(struct memory_block *memory)
memory->dev.id = memory->start_section_nr / sections_per_block;
memory->dev.release = memory_block_release;
memory->dev.groups = memory_memblk_attr_groups;
- memory->dev.offline = memory->state == MEM_OFFLINE;
+ dev_assign_offline(&memory->dev, memory->state == MEM_OFFLINE);
ret = device_register(&memory->dev);
if (ret) {
diff --git a/include/linux/device.h b/include/linux/device.h
index 8132aab17e04..65fc6c566cc6 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -485,6 +485,8 @@ struct device_physical_location {
* architecture supports non-coherent devices.
* @DEV_FLAG_OF_NODE_REUSED: Set if the device-tree node is shared with an
* ancestor device.
+ * @DEV_FLAG_OFFLINE_DISABLED: If set, the device is permanently online.
+ * @DEV_FLAG_OFFLINE: Set after successful invocation of bus type's .offline().
*/
enum struct_device_flags {
DEV_FLAG_READY_TO_PROBE = 0,
@@ -495,6 +497,8 @@ enum struct_device_flags {
DEV_FLAG_STATE_SYNCED = 5,
DEV_FLAG_DMA_COHERENT = 6,
DEV_FLAG_OF_NODE_REUSED = 7,
+ DEV_FLAG_OFFLINE_DISABLED = 8,
+ DEV_FLAG_OFFLINE = 9,
DEV_FLAG_COUNT
};
@@ -573,9 +577,6 @@ enum struct_device_flags {
* @removable: Whether the device can be removed from the system. This
* should be set by the subsystem / bus driver that discovered
* the device.
- *
- * @offline_disabled: If set, the device is permanently online.
- * @offline: Set after successful invocation of bus type's .offline().
* @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
@@ -680,9 +681,6 @@ struct device {
enum device_removable removable;
- bool offline_disabled:1;
- bool offline:1;
-
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
};
@@ -716,6 +714,8 @@ __create_dev_flag_accessors(dma_ops_bypass, DEV_FLAG_DMA_OPS_BYPASS);
__create_dev_flag_accessors(state_synced, DEV_FLAG_STATE_SYNCED);
__create_dev_flag_accessors(dma_coherent, DEV_FLAG_DMA_COHERENT);
__create_dev_flag_accessors(of_node_reused, DEV_FLAG_OF_NODE_REUSED);
+__create_dev_flag_accessors(offline_disabled, DEV_FLAG_OFFLINE_DISABLED);
+__create_dev_flag_accessors(offline, DEV_FLAG_OFFLINE);
/**
* struct device_link - Device link representation.
diff --git a/kernel/cpu.c b/kernel/cpu.c
index bc4f7a9ba64e..f975bb34915b 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -2639,7 +2639,7 @@ static void cpuhp_offline_cpu_device(unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
- dev->offline = true;
+ dev_set_offline(dev);
/* Tell user space about the state change */
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
}
@@ -2648,7 +2648,7 @@ static void cpuhp_online_cpu_device(unsigned int cpu)
{
struct device *dev = get_cpu_device(cpu);
- dev->offline = false;
+ dev_clear_offline(dev);
/* Tell user space about the state change */
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
}
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 29+ messages in thread* Re: [PATCH v4 0/9] driver core: Fix some race conditions
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (8 preceding siblings ...)
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 ` Rafael J. Wysocki
2026-04-05 5:27 ` Greg Kroah-Hartman
10 siblings, 0 replies; 29+ messages in thread
From: Rafael J. Wysocki @ 2026-04-04 17:11 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Saravana Kannan, Christoph Hellwig, Eric Dumazet,
Johan Hovold, Leon Romanovsky, Alexander Lobakin,
Alexey Kardashevskiy, Robin Murphy, Andrew Morton, Frank.Li,
Jason Gunthorpe, alex, alexander.stein, andre.przywara, andrew,
andrew, andriy.shevchenko, aou, ardb, bhelgaas, brgl, broonie,
catalin.marinas, chleroy, davem, david, devicetree, dmaengine,
driver-core, gbatra, gregory.clement, hkallweit1, iommu,
jirislaby, joel, joro, kees, kevin.brodsky, kuba, lenb, lgirdwood,
linux-acpi, linux-arm-kernel, linux-aspeed, linux-cxl,
linux-kernel, linux-mips, linux-mm, linux-pci, linux-riscv,
linux-serial, linux-snps-arc, linux-usb, linux, linuxppc-dev,
m.szyprowski, maddy, mani, maz, miko.lenczewski, mpe, netdev,
npiggin, osalvador, oupton, pabeni, palmer, peter.ujfalusi,
peterz, pjw, robh, sebastian.hesselbarth, tglx, tsbogend, vgupta,
vkoul, will, willy, yangyicong, yeoreum.yun
On Sat, Apr 4, 2026 at 2:07 AM Douglas Anderson <dianders@chromium.org> wrote:
>
> The main goal of this series is to fix the observed bug talked about
> in the first patch ("driver core: Don't let a device probe until it's
> ready"). That patch fixes a problem that has been observed in the real
> world and could land even if the rest of the patches are found
> unacceptable or need to be spun.
>
> That said, during patch review Danilo correctly pointed out that many
> of the bitfield accesses in "struct device" are unsafe. I added a
> bunch of patches in the series to address each one.
>
> Danilo said he's most worried about "can_match", so I put that one
> first. After that, I tried to transition bitfields to flags in reverse
> order to when the bitfield was added.
>
> Even if transitioning from bitfields to flags isn't truly needed for
> correctness, it seems silly (and wasteful of space in struct device)
> to have some in bitfields and some as flags. Thus I didn't spend time
> for each bitfield showing that it's truly needed for correctness.
>
> Transition was done semi manually. Presumably someone skilled at
> coccinelle could do a better job, but I just used sed in a heavy-
> handed manner and then reviewed/fixed the results, undoing anything my
> script got wrong. My terrible/ugly script was:
>
> var=can_match
> caps="${var^^}"
> for f in $(git grep -l "[>\.]${var}[^1-9_a-zA-Z\[]"); do
> echo $f
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)->${var} = true/set_bit(DEV_FLAG_${caps}, \&\\1->flags)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)\.${var} = true/dev_set_${caps}(\&\\1)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)->${var} = false/clear_bit(DEV_FLAG_${caps}, \&\\1->flags)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)\.${var} = false/dev_clear_${caps}(\&\\1)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)->${var} = \([^;]*\)/assign_bit(DEV_FLAG_${caps}, \&\\1->flags, \\2)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)\.${var} = \([^;]*\)/dev_assign_${caps}(\&\\1, \\2)/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)->${var}\([^1-9_a-zA-Z\[]\)/test_bit(DEV_FLAG_${caps}, \&\\1->flags)\\2/" "$f"
> sed -i~ -e "s/\([a-zA-Z_0-9\.>()-][a-zA-Z_0-9\.>()-]*\)\.${var}\([^1-9_a-zA-Z\[]\)/dev_${caps}(\&\\1)\\2/" "$f"
> done
>
> From v3 to v4, I transitioned to accessor functions with another ugly
> sed script. I had git format the old patches, then transformed them
> with:
>
> for f in *.patch; do
> echo $f
> sed -i~ -e "s/test_and_set_bit(DEV_FLAG_\([^,]*\), \&\(.*\)->flags)/dev_test_and_set_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/test_and_set_bit(DEV_FLAG_\([^,]*\), \(.*\)\.flags)/dev_test_and_set_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/test_bit(DEV_FLAG_\([^,]*\), \&\(.*\)->flags)/dev_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/test_bit(DEV_FLAG_\([^,]*\), \(.*\)\.flags)/dev_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/set_bit(DEV_FLAG_\([^,]*\), \&\(.*\)->flags)/dev_set_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/set_bit(DEV_FLAG_\([^,]*\), \(.*\)\.flags)/dev_set_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/clear_bit(DEV_FLAG_\([^,]*\), \&\(.*\)->flags)/dev_clear_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/clear_bit(DEV_FLAG_\([^,]*\), \(.*\)\.flags)/dev_clear_\\L\\1(\\2)/" "$f"
> sed -i~ -e "s/assign_bit(DEV_FLAG_\([^,]*\), \&\(.*\)->flags, \(.*\))/dev_assign_\\L\\1(\\2, \\3)/" "$f"
> sed -i~ -e "s/assign_bit(DEV_FLAG_\([^,]*\), \(.*\)\.flags, \(.*\))/dev_assign_\\L\\1(\\2, \\3)/" "$f"
> done
>
> ...and then did a few manual touchups for spacing.
>
> NOTE: one potentially "controversial" choice I made in some patches
> was to always reserve a flag ID even if a flag is only used under
> certain CONFIG_ settings. This is a change from how things were
> before. Keeping the numbering consistent and allowing easy
> compile-testing of both CONFIG settings seemed worth it, especially
> since it won't take up any extra space until we've added a lot more
> flags.
>
> I only marked the first patch as a "Fix" since it is the only one
> fixing observed problems. Other patches could be considered fixes too
> if folks want.
>
> I tested the first patch in the series backported to kernel 6.6 on the
> Pixel phone that was experiencing the race. I added extra printouts to
> make sure that the problem was hitting / addressed. The rest of the
> patches are tested with allmodconfig with arm32, arm64, ppc, and
> x86. I boot tested on an arm64 Chromebook running mainline.
>
> Changes in v4:
> - Use accessor functions for flags
>
> Changes in v3:
> - Use a new "flags" bitfield
> - Add missing \n in probe error message
>
> Changes in v2:
> - Instead of adjusting the ordering, use "ready_to_probe" flag
>
> Douglas Anderson (9):
> driver core: Don't let a device probe until it's ready
> driver core: Replace dev->can_match with dev_can_match()
> driver core: Replace dev->dma_iommu with dev_dma_iommu()
> driver core: Replace dev->dma_skip_sync with dev_dma_skip_sync()
> driver core: Replace dev->dma_ops_bypass with dev_dma_ops_bypass()
> driver core: Replace dev->state_synced with dev_state_synced()
> driver core: Replace dev->dma_coherent with dev_dma_coherent()
> driver core: Replace dev->of_node_reused with dev_of_node_reused()
> driver core: Replace dev->offline + ->offline_disabled with accessors
>
> arch/arc/mm/dma.c | 4 +-
> arch/arm/mach-highbank/highbank.c | 2 +-
> arch/arm/mach-mvebu/coherency.c | 2 +-
> arch/arm/mm/dma-mapping-nommu.c | 4 +-
> arch/arm/mm/dma-mapping.c | 28 ++--
> arch/arm64/kernel/cpufeature.c | 2 +-
> arch/arm64/mm/dma-mapping.c | 2 +-
> arch/mips/mm/dma-noncoherent.c | 2 +-
> arch/powerpc/kernel/dma-iommu.c | 8 +-
> .../platforms/pseries/hotplug-memory.c | 4 +-
> arch/riscv/mm/dma-noncoherent.c | 2 +-
> drivers/acpi/scan.c | 2 +-
> drivers/base/core.c | 53 +++++---
> drivers/base/cpu.c | 4 +-
> drivers/base/dd.c | 28 ++--
> drivers/base/memory.c | 2 +-
> drivers/base/pinctrl.c | 2 +-
> drivers/base/platform.c | 2 +-
> drivers/dma/ti/k3-udma-glue.c | 6 +-
> drivers/dma/ti/k3-udma.c | 6 +-
> drivers/iommu/dma-iommu.c | 9 +-
> drivers/iommu/iommu.c | 5 +-
> drivers/net/pcs/pcs-xpcs-plat.c | 2 +-
> drivers/of/device.c | 6 +-
> drivers/pci/of.c | 2 +-
> drivers/pci/pwrctrl/core.c | 2 +-
> drivers/regulator/bq257xx-regulator.c | 2 +-
> drivers/regulator/rk808-regulator.c | 2 +-
> drivers/tty/serial/serial_base_bus.c | 2 +-
> drivers/usb/gadget/udc/aspeed-vhub/dev.c | 2 +-
> include/linux/device.h | 120 ++++++++++++------
> include/linux/dma-map-ops.h | 6 +-
> include/linux/dma-mapping.h | 2 +-
> include/linux/iommu-dma.h | 3 +-
> kernel/cpu.c | 4 +-
> kernel/dma/mapping.c | 12 +-
> mm/hmm.c | 2 +-
> 37 files changed, 206 insertions(+), 142 deletions(-)
>
> --
For the whole set
Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 0/9] driver core: Fix some race conditions
2026-04-04 0:04 [PATCH v4 0/9] driver core: Fix some race conditions Douglas Anderson
` (9 preceding siblings ...)
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
10 siblings, 2 replies; 29+ messages in thread
From: Greg Kroah-Hartman @ 2026-04-05 5:27 UTC (permalink / raw)
To: Douglas Anderson
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Saravana Kannan,
Christoph Hellwig, Eric Dumazet, Johan Hovold, Leon Romanovsky,
Alexander Lobakin, Alexey Kardashevskiy, Robin Murphy,
Andrew Morton, Frank.Li, Jason Gunthorpe, alex, alexander.stein,
andre.przywara, andrew, andrew, andriy.shevchenko, aou, ardb,
bhelgaas, brgl, broonie, catalin.marinas, chleroy, davem, david,
devicetree, dmaengine, driver-core, gbatra, gregory.clement,
hkallweit1, iommu, jirislaby, joel, joro, kees, kevin.brodsky,
kuba, lenb, lgirdwood, linux-acpi, linux-arm-kernel, linux-aspeed,
linux-cxl, linux-kernel, linux-mips, linux-mm, linux-pci,
linux-riscv, linux-serial, linux-snps-arc, linux-usb, linux,
linuxppc-dev, m.szyprowski, maddy, mani, maz, miko.lenczewski,
mpe, netdev, npiggin, osalvador, oupton, pabeni, palmer,
peter.ujfalusi, peterz, pjw, robh, sebastian.hesselbarth, tglx,
tsbogend, vgupta, vkoul, will, willy, yangyicong, yeoreum.yun
On Fri, Apr 03, 2026 at 05:04:54PM -0700, Douglas Anderson wrote:
> NOTE: one potentially "controversial" choice I made in some patches
> was to always reserve a flag ID even if a flag is only used under
> certain CONFIG_ settings. This is a change from how things were
> before. Keeping the numbering consistent and allowing easy
> compile-testing of both CONFIG settings seemed worth it, especially
> since it won't take up any extra space until we've added a lot more
> flags.
Nah, this is fine, I don't see any problems with this as the original
code kind of was doing the same thing with the "hole" in the structure
if those options were not enabled.
> I only marked the first patch as a "Fix" since it is the only one
> fixing observed problems. Other patches could be considered fixes too
> if folks want.
>
> I tested the first patch in the series backported to kernel 6.6 on the
> Pixel phone that was experiencing the race. I added extra printouts to
> make sure that the problem was hitting / addressed. The rest of the
> patches are tested with allmodconfig with arm32, arm64, ppc, and
> x86. I boot tested on an arm64 Chromebook running mainline.
I'm guessing your tests passed? :)
Anyway, this looks great, unless there are any objections, other than
the "needs to be undefined", which a follow-on patch can handle, I'll
queue them up next week for 7.1-rc1.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 29+ messages in thread* Re: [PATCH v4 0/9] driver core: Fix some race conditions
2026-04-05 5:27 ` Greg Kroah-Hartman
@ 2026-04-05 12:02 ` Danilo Krummrich
2026-04-05 22:43 ` Doug Anderson
1 sibling, 0 replies; 29+ messages in thread
From: Danilo Krummrich @ 2026-04-05 12:02 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Douglas Anderson, Rafael J . Wysocki, Alan Stern, Saravana Kannan,
Christoph Hellwig, Eric Dumazet, Johan Hovold, Leon Romanovsky,
Alexander Lobakin, Alexey Kardashevskiy, Robin Murphy,
Andrew Morton, Frank.Li, Jason Gunthorpe, alex, alexander.stein,
andre.przywara, andrew, andrew, andriy.shevchenko, aou, ardb,
bhelgaas, brgl, broonie, catalin.marinas, chleroy, davem, david,
devicetree, dmaengine, driver-core, gbatra, gregory.clement,
hkallweit1, iommu, jirislaby, joel, joro, kees, kevin.brodsky,
kuba, lenb, lgirdwood, linux-acpi, linux-arm-kernel, linux-aspeed,
linux-cxl, linux-kernel, linux-mips, linux-mm, linux-pci,
linux-riscv, linux-serial, linux-snps-arc, linux-usb, linux,
linuxppc-dev, m.szyprowski, maddy, mani, maz, miko.lenczewski,
mpe, netdev, npiggin, osalvador, oupton, pabeni, palmer,
peter.ujfalusi, peterz, pjw, robh, sebastian.hesselbarth, tglx,
tsbogend, vgupta, vkoul, will, willy, yangyicong, yeoreum.yun
On Sun Apr 5, 2026 at 7:27 AM CEST, Greg Kroah-Hartman wrote:
> Anyway, this looks great, unless there are any objections, other than
> the "needs to be undefined", which a follow-on patch can handle, I'll
> queue them up next week for 7.1-rc1.
Sounds good, for the series:
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
^ permalink raw reply [flat|nested] 29+ messages in thread
* Re: [PATCH v4 0/9] driver core: Fix some race conditions
2026-04-05 5:27 ` Greg Kroah-Hartman
2026-04-05 12:02 ` Danilo Krummrich
@ 2026-04-05 22:43 ` Doug Anderson
1 sibling, 0 replies; 29+ messages in thread
From: Doug Anderson @ 2026-04-05 22:43 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Saravana Kannan,
Christoph Hellwig, Eric Dumazet, Johan Hovold, Leon Romanovsky,
Alexander Lobakin, Alexey Kardashevskiy, Robin Murphy,
Andrew Morton, Frank.Li, Jason Gunthorpe, alex, alexander.stein,
andre.przywara, andrew, andrew, andriy.shevchenko, aou, ardb,
bhelgaas, brgl, broonie, catalin.marinas, chleroy, davem, david,
devicetree, dmaengine, driver-core, gbatra, gregory.clement,
hkallweit1, iommu, jirislaby, joel, joro, kees, kevin.brodsky,
kuba, lenb, lgirdwood, linux-acpi, linux-arm-kernel, linux-aspeed,
linux-cxl, linux-kernel, linux-mips, linux-mm, linux-pci,
linux-riscv, linux-serial, linux-snps-arc, linux-usb, linux,
linuxppc-dev, m.szyprowski, maddy, mani, maz, miko.lenczewski,
mpe, netdev, npiggin, osalvador, oupton, pabeni, palmer,
peter.ujfalusi, peterz, pjw, robh, sebastian.hesselbarth, tglx,
tsbogend, vgupta, vkoul, will, willy, yangyicong, yeoreum.yun
Hi,
On Sat, Apr 4, 2026 at 10:28 PM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> On Fri, Apr 03, 2026 at 05:04:54PM -0700, Douglas Anderson wrote:
> > NOTE: one potentially "controversial" choice I made in some patches
> > was to always reserve a flag ID even if a flag is only used under
> > certain CONFIG_ settings. This is a change from how things were
> > before. Keeping the numbering consistent and allowing easy
> > compile-testing of both CONFIG settings seemed worth it, especially
> > since it won't take up any extra space until we've added a lot more
> > flags.
>
> Nah, this is fine, I don't see any problems with this as the original
> code kind of was doing the same thing with the "hole" in the structure
> if those options were not enabled.
>
> > I only marked the first patch as a "Fix" since it is the only one
> > fixing observed problems. Other patches could be considered fixes too
> > if folks want.
> >
> > I tested the first patch in the series backported to kernel 6.6 on the
> > Pixel phone that was experiencing the race. I added extra printouts to
> > make sure that the problem was hitting / addressed. The rest of the
> > patches are tested with allmodconfig with arm32, arm64, ppc, and
> > x86. I boot tested on an arm64 Chromebook running mainline.
>
> I'm guessing your tests passed? :)
Yup, all the tests that I've run have passed. I also threw in an
"allnoconfig" compile test just for good measure.
> Anyway, this looks great, unless there are any objections, other than
> the "needs to be undefined", which a follow-on patch can handle, I'll
> queue them up next week for 7.1-rc1.
Thanks. As per the other thread, I'm happy if you or Danilo want to
apply it, and I'm happy if you want to make minor fixups when
applying.
When I see the patches applied, I'll send a followup patch to address
the "needs to be undefined" comment, unless Danilo makes that change
himself when applying.
-Doug
^ permalink raw reply [flat|nested] 29+ messages in thread