* [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 7:04 ` Greg Kroah-Hartman
2026-04-03 0:49 ` [PATCH v3 2/9] driver core: Replace dev->can_match with DEV_FLAG_CAN_MATCH Douglas Anderson
` (7 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, 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
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 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 | 15 +++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 09b98f02f559..8d4028a2165f 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. "DEV_FLAG_READY_TO_PROBE" 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.
+ */
+ set_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags);
+
bus_probe_device(dev);
/*
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 37c7e54e0e4c..3aead51173f8 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
+ * "DEV_FLAG_READY_TO_PROBE".
+ */
+ if (!test_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags))
+ 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..5b7ace5921aa 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -458,6 +458,18 @@ struct device_physical_location {
bool lid;
};
+/**
+ * enum struct_device_flags - Flags in struct device
+ *
+ * Should be accessed with thread-safe bitops.
+ *
+ * @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,
+};
+
/**
* struct device - The basic device structure
* @parent: The device's "parent" device, the device to which it is attached.
@@ -553,6 +565,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,6 +688,8 @@ struct device {
#ifdef CONFIG_IOMMU_DMA
bool dma_iommu:1;
#endif
+
+ unsigned long flags;
};
/**
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 0:49 ` [PATCH v3 1/9] driver core: Don't let a device probe until it's ready Douglas Anderson
@ 2026-04-03 7:04 ` Greg Kroah-Hartman
2026-04-03 15:34 ` Doug Anderson
0 siblings, 1 reply; 18+ messages in thread
From: Greg Kroah-Hartman @ 2026-04-03 7:04 UTC (permalink / raw)
To: Douglas Anderson
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Robin Murphy,
Leon Romanovsky, Paul Burton, Saravana Kannan, Alexander Lobakin,
Eric Dumazet, Toshi Kani, Christoph Hellwig, Alexey Kardashevskiy,
Johan Hovold, stable, driver-core, linux-kernel
On Thu, Apr 02, 2026 at 05:49:47PM -0700, Douglas Anderson 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]:
>
> 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
>
> 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 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 | 15 +++++++++++++++
> 3 files changed, 40 insertions(+)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 09b98f02f559..8d4028a2165f 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. "DEV_FLAG_READY_TO_PROBE" 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.
> + */
> + set_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags);
> +
> bus_probe_device(dev);
>
> /*
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index 37c7e54e0e4c..3aead51173f8 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
> + * "DEV_FLAG_READY_TO_PROBE".
> + */
> + if (!test_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags))
> + 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..5b7ace5921aa 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -458,6 +458,18 @@ struct device_physical_location {
> bool lid;
> };
>
> +/**
> + * enum struct_device_flags - Flags in struct device
> + *
> + * Should be accessed with thread-safe bitops.
> + *
> + * @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,
> +};
If you are going to want this to be a bit value, please use BIT(X) and
not an enum, as that's going to just get confusing over time.
Also, none of this manual test_bit()/set_bit() stuff, please give us
real "accessors" for this like:
bool device_ready_to_probe(struct device *dev);
so that it's obvious what is happening.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 7:04 ` Greg Kroah-Hartman
@ 2026-04-03 15:34 ` Doug Anderson
2026-04-03 15:44 ` Greg Kroah-Hartman
0 siblings, 1 reply; 18+ messages in thread
From: Doug Anderson @ 2026-04-03 15:34 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki
Cc: Danilo Krummrich, Alan Stern, Robin Murphy, Leon Romanovsky,
Saravana Kannan, Alexander Lobakin, Eric Dumazet,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold, stable,
driver-core, linux-kernel
Hi,
On Fri, Apr 3, 2026 at 12:04 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> > @@ -458,6 +458,18 @@ struct device_physical_location {
> > bool lid;
> > };
> >
> > +/**
> > + * enum struct_device_flags - Flags in struct device
> > + *
> > + * Should be accessed with thread-safe bitops.
> > + *
> > + * @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,
> > +};
>
> If you are going to want this to be a bit value, please use BIT(X) and
> not an enum, as that's going to just get confusing over time.
I don't believe I can do that. BIT(x) is not compatible with the
atomic bitops API. BIT(x) will turn the bit number (x) into a hex
value, but the atomic bitops API needs the bit number.
If you wish, I can turn this into something like:
#define DEV_FLAG_READY_TO_PROBE 0
#define DEV_FLAG_CAN_MATCH 1
#define DEV_FLAG_DMA_IOMMU 2
...
...but that seemed worse (to me) than the enum. Please shout if I
misunderstood or you disagree.
> Also, none of this manual test_bit()/set_bit() stuff, please give us
> real "accessors" for this like:
> bool device_ready_to_probe(struct device *dev);
>
> so that it's obvious what is happening.
Sure, that matches Rafael's request and is a nice improvement. To keep
from having to replicate a bunch of boilerplate code, I'll have macros
define the accessors:
#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);
__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);
__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);
Happy to tweak the above if desired.
-Doug
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 15:34 ` Doug Anderson
@ 2026-04-03 15:44 ` Greg Kroah-Hartman
2026-04-03 16:26 ` Doug Anderson
0 siblings, 1 reply; 18+ messages in thread
From: Greg Kroah-Hartman @ 2026-04-03 15:44 UTC (permalink / raw)
To: Doug Anderson
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Robin Murphy,
Leon Romanovsky, Saravana Kannan, Alexander Lobakin, Eric Dumazet,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold, stable,
driver-core, linux-kernel
On Fri, Apr 03, 2026 at 08:34:20AM -0700, Doug Anderson wrote:
> Hi,
>
> On Fri, Apr 3, 2026 at 12:04 AM Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> >
> > > @@ -458,6 +458,18 @@ struct device_physical_location {
> > > bool lid;
> > > };
> > >
> > > +/**
> > > + * enum struct_device_flags - Flags in struct device
> > > + *
> > > + * Should be accessed with thread-safe bitops.
> > > + *
> > > + * @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,
> > > +};
> >
> > If you are going to want this to be a bit value, please use BIT(X) and
> > not an enum, as that's going to just get confusing over time.
>
> I don't believe I can do that. BIT(x) is not compatible with the
> atomic bitops API. BIT(x) will turn the bit number (x) into a hex
> value, but the atomic bitops API needs the bit number.
Ah, yeah, sorry, missed that.
> If you wish, I can turn this into something like:
>
> #define DEV_FLAG_READY_TO_PROBE 0
> #define DEV_FLAG_CAN_MATCH 1
> #define DEV_FLAG_DMA_IOMMU 2
> ...
>
> ...but that seemed worse (to me) than the enum. Please shout if I
> misunderstood or you disagree.
If you make it a numbered enum, that's fine (I hate unnumbered ones),
and a comment somewhere saying we will "max out" at 63, or have a
DEV_FLAG_MAX_BIT entry in there.
> > Also, none of this manual test_bit()/set_bit() stuff, please give us
> > real "accessors" for this like:
> > bool device_ready_to_probe(struct device *dev);
> >
> > so that it's obvious what is happening.
>
> Sure, that matches Rafael's request and is a nice improvement. To keep
> from having to replicate a bunch of boilerplate code, I'll have macros
> define the accessors:
>
> #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);
> __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);
> __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);
>
> Happy to tweak the above if desired.
Sure, that works, thanks!
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 15:44 ` Greg Kroah-Hartman
@ 2026-04-03 16:26 ` Doug Anderson
2026-04-03 17:23 ` Greg Kroah-Hartman
0 siblings, 1 reply; 18+ messages in thread
From: Doug Anderson @ 2026-04-03 16:26 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Robin Murphy,
Leon Romanovsky, Saravana Kannan, Alexander Lobakin, Eric Dumazet,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold, stable,
driver-core, linux-kernel
Hi,
On Fri, Apr 3, 2026 at 8:44 AM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> > If you wish, I can turn this into something like:
> >
> > #define DEV_FLAG_READY_TO_PROBE 0
> > #define DEV_FLAG_CAN_MATCH 1
> > #define DEV_FLAG_DMA_IOMMU 2
> > ...
> >
> > ...but that seemed worse (to me) than the enum. Please shout if I
> > misunderstood or you disagree.
>
> If you make it a numbered enum, that's fine (I hate unnumbered ones),
Sounds like a plan.
> and a comment somewhere saying we will "max out" at 63, or have a
> DEV_FLAG_MAX_BIT entry in there.
Sure. To be compatible across all architectures, it should probably
max at 31, right? Atomic bitops works with an "unsigned long" which
might be only 32-bits. Oh, actually I should just add "DEV_FLAG_COUNT"
at the end of the enum and declare flags with DECLARE_BITMAP(). Then
there is no max.
In total, I've now got this:
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_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
};
...and "flags" is now:
DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
Yell if you want anything changed. I'll restart my allmodconfig
compile tests now.
-Doug
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v3 1/9] driver core: Don't let a device probe until it's ready
2026-04-03 16:26 ` Doug Anderson
@ 2026-04-03 17:23 ` Greg Kroah-Hartman
0 siblings, 0 replies; 18+ messages in thread
From: Greg Kroah-Hartman @ 2026-04-03 17:23 UTC (permalink / raw)
To: Doug Anderson
Cc: Rafael J . Wysocki, Danilo Krummrich, Alan Stern, Robin Murphy,
Leon Romanovsky, Saravana Kannan, Alexander Lobakin, Eric Dumazet,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold, stable,
driver-core, linux-kernel
On Fri, Apr 03, 2026 at 09:26:58AM -0700, Doug Anderson wrote:
> Hi,
>
> On Fri, Apr 3, 2026 at 8:44 AM Greg Kroah-Hartman
> <gregkh@linuxfoundation.org> wrote:
> >
> > > If you wish, I can turn this into something like:
> > >
> > > #define DEV_FLAG_READY_TO_PROBE 0
> > > #define DEV_FLAG_CAN_MATCH 1
> > > #define DEV_FLAG_DMA_IOMMU 2
> > > ...
> > >
> > > ...but that seemed worse (to me) than the enum. Please shout if I
> > > misunderstood or you disagree.
> >
> > If you make it a numbered enum, that's fine (I hate unnumbered ones),
>
> Sounds like a plan.
>
>
> > and a comment somewhere saying we will "max out" at 63, or have a
> > DEV_FLAG_MAX_BIT entry in there.
>
> Sure. To be compatible across all architectures, it should probably
> max at 31, right? Atomic bitops works with an "unsigned long" which
> might be only 32-bits. Oh, actually I should just add "DEV_FLAG_COUNT"
> at the end of the enum and declare flags with DECLARE_BITMAP(). Then
> there is no max.
>
> In total, I've now got this:
>
> 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_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
> };
>
> ...and "flags" is now:
>
> DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
>
> Yell if you want anything changed. I'll restart my allmodconfig
> compile tests now.
That looks much better, thanks!
greg k-h
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 2/9] driver core: Replace dev->can_match with DEV_FLAG_CAN_MATCH
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 1/9] driver core: Don't let a device probe until it's ready Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 10:52 ` Rafael J. Wysocki
2026-04-03 0:49 ` [PATCH v3 3/9] driver core: Replace dev->dma_iommu with DEV_FLAG_DMA_IOMMU Douglas Anderson
` (6 subsequent siblings)
8 siblings, 1 reply; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, Douglas Anderson,
Saravana Kannan, 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@google.com>
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 v3:
- New
drivers/base/core.c | 10 +++++-----
drivers/base/dd.c | 8 ++++----
include/linux/device.h | 8 ++++----
3 files changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 8d4028a2165f..cbdfee887833 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 && test_bit(DEV_FLAG_CAN_MATCH, &dev->flags)) ||
(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) {
+ !test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags)) {
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) {
+ !test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags)) {
/*
* 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 (!test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags))
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 && !test_bit(DEV_FLAG_CAN_MATCH, &dev->flags))
fw_devlink_unblock_consumers(dev);
if (parent)
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 3aead51173f8..66df31696349 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 (!test_bit(DEV_FLAG_CAN_MATCH, &dev->flags))
return;
mutex_lock(&deferred_probe_mutex);
@@ -860,7 +860,7 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
if (!test_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags))
return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n");
- dev->can_match = true;
+ set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
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;
+ set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
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;
+ set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
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 5b7ace5921aa..7e5737c6d7d1 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -465,9 +465,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,
+ DEV_FLAG_CAN_MATCH,
};
/**
@@ -552,9 +556,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
@@ -673,7 +674,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)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 2/9] driver core: Replace dev->can_match with DEV_FLAG_CAN_MATCH
2026-04-03 0:49 ` [PATCH v3 2/9] driver core: Replace dev->can_match with DEV_FLAG_CAN_MATCH Douglas Anderson
@ 2026-04-03 10:52 ` Rafael J. Wysocki
0 siblings, 0 replies; 18+ messages in thread
From: Rafael J. Wysocki @ 2026-04-03 10:52 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Robin Murphy, Leon Romanovsky, Paul Burton,
Saravana Kannan, Alexander Lobakin, Eric Dumazet, Toshi Kani,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold,
Saravana Kannan, driver-core, linux-kernel
On Fri, Apr 3, 2026 at 2:51 AM Douglas Anderson <dianders@chromium.org> wrote:
>
> 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@google.com>
> 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 v3:
> - New
>
> drivers/base/core.c | 10 +++++-----
> drivers/base/dd.c | 8 ++++----
> include/linux/device.h | 8 ++++----
> 3 files changed, 13 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index 8d4028a2165f..cbdfee887833 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 && test_bit(DEV_FLAG_CAN_MATCH, &dev->flags)) ||
I'm wondering if these test_bit(DEV_FLAG_..., &dev->flags) can be put
behind a set of static inline functions, like for example in this case
dev_can_match() or device_can_mach(). The above would become
return (fw_devlink_best_effort && dev_can_match(dev)) ||
(and analogously below).
That would at least make the code somewhat easier to follow.
> (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) {
> + !test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags)) {
> 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) {
> + !test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags)) {
> /*
> * 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 (!test_bit(DEV_FLAG_CAN_MATCH, &link->supplier->flags))
> 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 && !test_bit(DEV_FLAG_CAN_MATCH, &dev->flags))
> fw_devlink_unblock_consumers(dev);
>
> if (parent)
> diff --git a/drivers/base/dd.c b/drivers/base/dd.c
> index 3aead51173f8..66df31696349 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 (!test_bit(DEV_FLAG_CAN_MATCH, &dev->flags))
> return;
>
> mutex_lock(&deferred_probe_mutex);
> @@ -860,7 +860,7 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
> if (!test_bit(DEV_FLAG_READY_TO_PROBE, &dev->flags))
> return dev_err_probe(dev, -EPROBE_DEFER, "Device not ready to probe\n");
>
> - dev->can_match = true;
> + set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
> 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;
> + set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
> 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;
> + set_bit(DEV_FLAG_CAN_MATCH, &dev->flags);
> 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 5b7ace5921aa..7e5737c6d7d1 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -465,9 +465,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,
> + DEV_FLAG_CAN_MATCH,
> };
>
> /**
> @@ -552,9 +556,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
> @@ -673,7 +674,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)
> --
> 2.53.0.1213.gd9a14994de-goog
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 3/9] driver core: Replace dev->dma_iommu with DEV_FLAG_DMA_IOMMU
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 1/9] driver core: Don't let a device probe until it's ready Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 2/9] driver core: Replace dev->can_match with DEV_FLAG_CAN_MATCH Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 4/9] driver core: Replace dev->dma_skip_sync with DEV_FLAG_DMA_SKIP_SYNC Douglas Anderson
` (5 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, 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 v3:
- New
drivers/iommu/dma-iommu.c | 9 ++++++---
drivers/iommu/iommu.c | 5 ++---
include/linux/device.h | 8 +++-----
include/linux/iommu-dma.h | 4 +++-
4 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 94d514169642..a3423ece9578 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);
+ assign_bit(DEV_FLAG_DMA_IOMMU, &dev->flags, 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;
+ clear_bit(DEV_FLAG_DMA_IOMMU, &dev->flags);
}
static bool has_msi_cookie(const struct iommu_domain *domain)
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 50718ab810a4..7722ac086981 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))
+ clear_bit(DEV_FLAG_DMA_IOMMU, &dev->flags);
}
static struct iommu_domain *pasid_array_entry_to_domain(void *entry)
diff --git a/include/linux/device.h b/include/linux/device.h
index 7e5737c6d7d1..e74c7d0813ce 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -468,10 +468,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,
DEV_FLAG_CAN_MATCH,
+ DEV_FLAG_DMA_IOMMU,
};
/**
@@ -564,8 +567,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
@@ -685,9 +686,6 @@ struct device {
#ifdef CONFIG_DMA_NEED_SYNC
bool dma_skip_sync:1;
#endif
-#ifdef CONFIG_IOMMU_DMA
- bool dma_iommu:1;
-#endif
unsigned long flags;
};
diff --git a/include/linux/iommu-dma.h b/include/linux/iommu-dma.h
index a92b3ff9b934..b6177672539d 100644
--- a/include/linux/iommu-dma.h
+++ b/include/linux/iommu-dma.h
@@ -7,12 +7,14 @@
#ifndef _LINUX_IOMMU_DMA_H
#define _LINUX_IOMMU_DMA_H
+#include <linux/bitops.h>
#include <linux/dma-direction.h>
+#include <linux/device.h>
#ifdef CONFIG_IOMMU_DMA
static inline bool use_dma_iommu(struct device *dev)
{
- return dev->dma_iommu;
+ return test_bit(DEV_FLAG_DMA_IOMMU, &dev->flags);
}
#else
static inline bool use_dma_iommu(struct device *dev)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 4/9] driver core: Replace dev->dma_skip_sync with DEV_FLAG_DMA_SKIP_SYNC
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (2 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 3/9] driver core: Replace dev->dma_iommu with DEV_FLAG_DMA_IOMMU Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 5/9] driver core: Replace dev->dma_ops_bypass with DEV_FLAG_DMA_OPS_BYPASS Douglas Anderson
` (4 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, 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 v3:
- New
include/linux/device.h | 7 +++----
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, 11 insertions(+), 12 deletions(-)
diff --git a/include/linux/device.h b/include/linux/device.h
index e74c7d0813ce..e900748d3038 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -470,11 +470,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,
DEV_FLAG_CAN_MATCH,
DEV_FLAG_DMA_IOMMU,
+ DEV_FLAG_DMA_SKIP_SYNC,
};
/**
@@ -566,7 +569,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
@@ -683,9 +685,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
unsigned long flags;
};
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 60b63756df82..4d9d1fe3277c 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(test_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags)))
+ clear_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags);
#endif
}
diff --git a/include/linux/dma-mapping.h b/include/linux/dma-mapping.h
index 99ef042ecdb4..23273b0fe84e 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 !test_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags) || 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..f50b648ed460 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 (!test_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags))
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);
+ assign_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags, 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;
+ set_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags);
else
- dev->dma_skip_sync = false;
+ clear_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags);
}
#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..137450e096bc 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 = !test_bit(DEV_FLAG_DMA_SKIP_SYNC, &dev->flags);
#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] 18+ messages in thread* [PATCH v3 5/9] driver core: Replace dev->dma_ops_bypass with DEV_FLAG_DMA_OPS_BYPASS
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (3 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 4/9] driver core: Replace dev->dma_skip_sync with DEV_FLAG_DMA_SKIP_SYNC Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 6/9] driver core: Replace dev->state_synced with DEV_FLAG_STATE_SYNCED Douglas Anderson
` (3 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, Douglas Anderson, chleroy,
driver-core, gbatra, iommu, 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 v3:
- New
arch/powerpc/kernel/dma-iommu.c | 8 ++++----
include/linux/device.h | 14 ++++++--------
kernel/dma/mapping.c | 8 +++-----
3 files changed, 13 insertions(+), 17 deletions(-)
diff --git a/arch/powerpc/kernel/dma-iommu.c b/arch/powerpc/kernel/dma-iommu.c
index 73e10bd4d56d..6486417c8acb 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 (test_bit(DEV_FLAG_DMA_OPS_BYPASS, &dev->flags))
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 (!test_bit(DEV_FLAG_DMA_OPS_BYPASS, &dev->flags))
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;
+ set_bit(DEV_FLAG_DMA_OPS_BYPASS, &dev->flags);
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;
+ clear_bit(DEV_FLAG_DMA_OPS_BYPASS, &dev->flags);
return 1;
}
diff --git a/include/linux/device.h b/include/linux/device.h
index e900748d3038..f5845bd7c3e6 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -472,12 +472,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,
DEV_FLAG_CAN_MATCH,
DEV_FLAG_DMA_IOMMU,
DEV_FLAG_DMA_SKIP_SYNC,
+ DEV_FLAG_DMA_OPS_BYPASS,
};
/**
@@ -564,11 +570,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
@@ -682,9 +683,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
unsigned long flags;
};
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index f50b648ed460..44d4c319ffad 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) && test_bit(DEV_FLAG_DMA_OPS_BYPASS, &dev->flags))
return min_not_zero(mask, dev->bus_dma_limit) >=
dma_direct_get_required_mask(dev);
-#endif
return false;
}
@@ -895,8 +893,8 @@ bool dma_pci_p2pdma_supported(struct device *dev)
const struct dma_map_ops *ops = get_dma_ops(dev);
/*
- * Note: dma_ops_bypass is not checked here because P2PDMA should
- * not be used with dma mapping ops that do not have support even
+ * Note: DEV_FLAG_DMA_OPS_BYPASS is not checked here because P2PDMA
+ * should not be used with dma mapping ops that do not have support even
* if the specific device is bypassing them.
*/
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 6/9] driver core: Replace dev->state_synced with DEV_FLAG_STATE_SYNCED
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (4 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 5/9] driver core: Replace dev->dma_ops_bypass with DEV_FLAG_DMA_OPS_BYPASS Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 7/9] driver core: Replace dev->dma_coherent with DEV_FLAG_DMA_COHERENT Douglas Anderson
` (2 subsequent siblings)
8 siblings, 0 replies; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, Douglas Anderson,
Saravana Kannan, 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@google.com>
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 v3:
- New
drivers/base/core.c | 9 +++++----
drivers/base/dd.c | 8 +++-----
include/linux/device.h | 8 ++++----
3 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index cbdfee887833..8dbb7a9c7aab 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 (test_bit(DEV_FLAG_STATE_SYNCED, &dev->flags))
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;
+ set_bit(DEV_FLAG_STATE_SYNCED, &dev->flags);
if (WARN_ON(!list_empty(&dev->links.defer_sync)))
return;
@@ -1779,7 +1779,8 @@ 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 ||
+ test_bit(DEV_FLAG_STATE_SYNCED, &sup->flags) ||
!dev_has_sync_state(sup))
return 0;
@@ -1793,7 +1794,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;
+ set_bit(DEV_FLAG_STATE_SYNCED, &sup->flags);
get_device(sup);
list_add_tail(&sup->links.defer_sync, data);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 66df31696349..f5d004f919ae 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 (!test_and_set_bit(DEV_FLAG_STATE_SYNCED, &dev->flags))
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 = test_bit(DEV_FLAG_STATE_SYNCED, &dev->flags);
device_unlock(dev);
return sysfs_emit(buf, "%u\n", val);
diff --git a/include/linux/device.h b/include/linux/device.h
index f5845bd7c3e6..6c961dac9fdb 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -477,6 +477,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,
@@ -484,6 +487,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_IOMMU,
DEV_FLAG_DMA_SKIP_SYNC,
DEV_FLAG_DMA_OPS_BYPASS,
+ DEV_FLAG_STATE_SYNCED,
};
/**
@@ -565,9 +569,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.
@@ -677,7 +678,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)
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 7/9] driver core: Replace dev->dma_coherent with DEV_FLAG_DMA_COHERENT
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (5 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 6/9] driver core: Replace dev->state_synced with DEV_FLAG_STATE_SYNCED Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 8/9] driver core: Replace dev->of_node_reused with DEV_FLAG_OF_NODE_REUSED Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs Douglas Anderson
8 siblings, 0 replies; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, 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>
Cc: Paul Burton <paul.burton@mips.com>
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 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 | 30 ++++++++++++++++--------------
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 | 10 +++-------
include/linux/dma-map-ops.h | 2 +-
13 files changed, 36 insertions(+), 38 deletions(-)
diff --git a/arch/arc/mm/dma.c b/arch/arc/mm/dma.c
index 6b85e94f3275..3d56878cb6a2 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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &dev->flags);
dev_info(dev, "use %scoherent DMA ops\n",
- dev->dma_coherent ? "" : "non");
+ test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) ? "" : "non");
}
diff --git a/arch/arm/mach-highbank/highbank.c b/arch/arm/mach-highbank/highbank.c
index 47335c7dadf8..ffa3f591f57a 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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &dev->flags);
}
return NOTIFY_OK;
diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c
index fa2c1e1aeb96..8391303a6a17 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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &dev->flags);
return NOTIFY_OK;
}
diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
index fecac107fd0d..ac0a976e30a0 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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, 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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, (get_cr() & CR_M) ? coherent : true);
}
}
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index f304037d1c34..9c2c635d7ac0 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 = test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) ? 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 (!test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags))
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 = test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) ? COHERENT : NORMAL;
struct page **pages;
size = PAGE_ALIGN(size);
@@ -1202,7 +1202,8 @@ 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 (!test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) &&
+ !(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 +1305,8 @@ 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 (!test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) &&
+ !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
arch_sync_dma_for_cpu(sg_phys(s), s->length, dir);
}
}
@@ -1323,7 +1325,7 @@ static void arm_iommu_sync_sg_for_cpu(struct device *dev,
struct scatterlist *s;
int i;
- if (dev->dma_coherent)
+ if (test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags))
return;
for_each_sg(sg, s, nents, i)
@@ -1345,7 +1347,7 @@ static void arm_iommu_sync_sg_for_device(struct device *dev,
struct scatterlist *s;
int i;
- if (dev->dma_coherent)
+ if (test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags))
return;
for_each_sg(sg, s, nents, i)
@@ -1371,7 +1373,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 (!test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO)))
arch_sync_dma_for_device(phys, size, dir);
@@ -1412,7 +1414,7 @@ static void arm_iommu_unmap_phys(struct device *dev, dma_addr_t handle,
if (!iova)
return;
- if (!dev->dma_coherent &&
+ if (!test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) &&
!(attrs & (DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_MMIO))) {
phys_addr_t phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1431,7 +1433,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 (test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) || !iova)
return;
phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1446,7 +1448,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 (test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags) || !iova)
return;
phys = iommu_iova_to_phys(mapping->domain, iova);
@@ -1701,13 +1703,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 DEV_FLAG_DMA_COHERENT from a bus
+ * notifier we can't just assign coherent to DEV_FLAG_DMA_COHERENT
* here, but instead have to make sure we only set but never clear it
* for now.
*/
if (coherent)
- dev->dma_coherent = true;
+ set_bit(DEV_FLAG_DMA_COHERENT, &dev->flags);
/*
* 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..256c7631aff5 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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, coherent);
xen_setup_dma_ops(dev);
}
diff --git a/arch/mips/mm/dma-noncoherent.c b/arch/mips/mm/dma-noncoherent.c
index ab4f2a75a7d0..496bf5f4999c 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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, coherent);
}
#endif
diff --git a/arch/riscv/mm/dma-noncoherent.c b/arch/riscv/mm/dma-noncoherent.c
index cb89d7e0ba88..3b793a1cc607 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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, coherent);
}
void riscv_noncoherent_supported(void)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 8dbb7a9c7aab..00005777c21f 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3174,7 +3174,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;
+ assign_bit(DEV_FLAG_DMA_COHERENT, &dev->flags, 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..cda8f4a8f440 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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &tx_chn->common.chan_dev.flags);
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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &rx_chn->common.chan_dev.flags);
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;
+ set_bit(DEV_FLAG_DMA_COHERENT, &rx_chn->common.chan_dev.flags);
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..770aae467fc5 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;
+ clear_bit(DEV_FLAG_DMA_COHERENT, &chan_dev->flags);
chan_dev->dma_parms = NULL;
} else if (asel == 14 || asel == 15) {
chan->dev->chan_dma_dev = true;
- chan_dev->dma_coherent = true;
+ set_bit(DEV_FLAG_DMA_COHERENT, &chan_dev->flags);
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;
+ clear_bit(DEV_FLAG_DMA_COHERENT, &chan_dev->flags);
chan_dev->dma_parms = NULL;
}
}
diff --git a/include/linux/device.h b/include/linux/device.h
index 6c961dac9fdb..c2a6dba7a036 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -480,6 +480,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,
@@ -488,6 +490,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_SKIP_SYNC,
DEV_FLAG_DMA_OPS_BYPASS,
DEV_FLAG_STATE_SYNCED,
+ DEV_FLAG_DMA_COHERENT,
};
/**
@@ -569,8 +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.
- * @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
@@ -678,11 +679,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
unsigned long flags;
};
diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h
index 4d9d1fe3277c..91d34678657c 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 test_bit(DEV_FLAG_DMA_COHERENT, &dev->flags);
}
#else
#define dma_default_coherent true
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v3 8/9] driver core: Replace dev->of_node_reused with DEV_FLAG_OF_NODE_REUSED
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (6 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 7/9] driver core: Replace dev->dma_coherent with DEV_FLAG_DMA_COHERENT Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 11:56 ` Mark Brown
2026-04-03 0:49 ` [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs Douglas Anderson
8 siblings, 1 reply; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, Douglas Anderson,
alexander.stein, andrew, andrew, andriy.shevchenko, astewart,
bhelgaas, brgl, broonie, 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>
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 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 | 6 +++---
12 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 00005777c21f..a87bd40499b6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -5282,7 +5282,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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags);
}
EXPORT_SYMBOL_GPL(device_set_of_node_from_dev);
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
index 6e250272c843..62c228c75d50 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 (test_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags))
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..5128ff7e5e78 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;
+ assign_bit(DEV_FLAG_OF_NODE_REUSED, &pdev->dev.flags, 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..c2722d8bd98a 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &mdiodev->dev.flags);
/* 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..fd77295a8c0f 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 || test_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags))
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 || test_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags))
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) || test_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags))
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..197b60c5a660 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &dev->bus->dev.flags);
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..cfbe9b615b88 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags);
break;
}
diff --git a/drivers/regulator/bq257xx-regulator.c b/drivers/regulator/bq257xx-regulator.c
index dab8f1ab4450..01d3139e1d87 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &pdev->dev.flags);
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..375ea7861134 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &pdev->dev.flags);
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..86c6003bbebb 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &dev->flags);
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..57048e3aa6bb 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;
+ set_bit(DEV_FLAG_OF_NODE_REUSED, &d->gadget.dev.flags);
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 c2a6dba7a036..f6ca067bacca 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -482,6 +482,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,
@@ -491,6 +493,7 @@ enum struct_device_flags {
DEV_FLAG_DMA_OPS_BYPASS,
DEV_FLAG_STATE_SYNCED,
DEV_FLAG_DMA_COHERENT,
+ DEV_FLAG_OF_NODE_REUSED,
};
/**
@@ -570,8 +573,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
@@ -678,7 +679,6 @@ struct device {
bool offline_disabled:1;
bool offline:1;
- bool of_node_reused:1;
unsigned long flags;
};
--
2.53.0.1213.gd9a14994de-goog
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v3 8/9] driver core: Replace dev->of_node_reused with DEV_FLAG_OF_NODE_REUSED
2026-04-03 0:49 ` [PATCH v3 8/9] driver core: Replace dev->of_node_reused with DEV_FLAG_OF_NODE_REUSED Douglas Anderson
@ 2026-04-03 11:56 ` Mark Brown
0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2026-04-03 11:56 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Robin Murphy, Leon Romanovsky, Paul Burton,
Saravana Kannan, Alexander Lobakin, Eric Dumazet, Toshi Kani,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold,
alexander.stein, andrew, andrew, andriy.shevchenko, astewart,
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
[-- Attachment #1: Type: text/plain, Size: 282 bytes --]
On Thu, Apr 02, 2026 at 05:49:54PM -0700, Douglas Anderson wrote:
> 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.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs
2026-04-03 0:49 [PATCH v3 0/9] driver core: Fix some race conditions Douglas Anderson
` (7 preceding siblings ...)
2026-04-03 0:49 ` [PATCH v3 8/9] driver core: Replace dev->of_node_reused with DEV_FLAG_OF_NODE_REUSED Douglas Anderson
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 11:56 ` Mark Brown
8 siblings, 1 reply; 18+ messages in thread
From: Douglas Anderson @ 2026-04-03 0:49 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern
Cc: Robin Murphy, Leon Romanovsky, Paul Burton, Saravana Kannan,
Alexander Lobakin, Eric Dumazet, Toshi Kani, Christoph Hellwig,
Alexey Kardashevskiy, Johan Hovold, Douglas Anderson, ardb,
broonie, 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>
Cc: Toshi Kani <toshi.kani@hp.com>
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 v3:
- New
arch/arm64/kernel/cpufeature.c | 2 +-
.../platforms/pseries/hotplug-memory.c | 4 ++--
drivers/acpi/scan.c | 3 ++-
drivers/base/core.c | 19 ++++++++++---------
drivers/base/cpu.c | 4 ++--
drivers/base/memory.c | 2 +-
include/linux/device.h | 9 ++++-----
kernel/cpu.c | 4 ++--
8 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 32c2dbcc0c64..f6f7c35b7a93 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;
+ set_bit(DEV_FLAG_OFFLINE_DISABLED, &get_cpu_device(lucky_winner)->flags);
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..d9a0a75ada46 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 && test_bit(DEV_FLAG_OFFLINE, &mem_block->dev.flags))
rc = device_online(&mem_block->dev);
- else if (!online && !mem_block->dev.offline)
+ else if (!online && !test_bit(DEV_FLAG_OFFLINE, &mem_block->dev.flags))
rc = device_offline(&mem_block->dev);
else
rc = 0;
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index e8cdbdb46fdb..f2707b704468 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -122,7 +122,8 @@ 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) &&
+ !test_bit(DEV_FLAG_OFFLINE, &pn->dev->flags)) {
if (uevent)
kobject_uevent_env(&pn->dev->kobj, KOBJ_CHANGE, envp);
diff --git a/drivers/base/core.c b/drivers/base/core.c
index a87bd40499b6..63d724ece384 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2789,7 +2789,7 @@ static ssize_t online_show(struct device *dev, struct device_attribute *attr,
bool val;
device_lock(dev);
- val = !dev->offline;
+ val = !test_bit(DEV_FLAG_OFFLINE, &dev->flags);
device_unlock(dev);
return sysfs_emit(buf, "%u\n", val);
}
@@ -2914,7 +2914,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) && !test_bit(DEV_FLAG_OFFLINE_DISABLED, &dev->flags)) {
error = device_create_file(dev, &dev_attr_online);
if (error)
goto err_remove_dev_groups;
@@ -4179,7 +4179,8 @@ 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) &&
+ !test_bit(DEV_FLAG_OFFLINE, &dev->flags) ? -EBUSY : 0;
}
/**
@@ -4197,7 +4198,7 @@ int device_offline(struct device *dev)
{
int ret;
- if (dev->offline_disabled)
+ if (test_bit(DEV_FLAG_OFFLINE_DISABLED, &dev->flags))
return -EPERM;
ret = device_for_each_child(dev, NULL, device_check_offline);
@@ -4206,13 +4207,13 @@ int device_offline(struct device *dev)
device_lock(dev);
if (device_supports_offline(dev)) {
- if (dev->offline) {
+ if (test_bit(DEV_FLAG_OFFLINE, &dev->flags)) {
ret = 1;
} else {
ret = dev->bus->offline(dev);
if (!ret) {
kobject_uevent(&dev->kobj, KOBJ_OFFLINE);
- dev->offline = true;
+ set_bit(DEV_FLAG_OFFLINE, &dev->flags);
}
}
}
@@ -4237,11 +4238,11 @@ int device_online(struct device *dev)
device_lock(dev);
if (device_supports_offline(dev)) {
- if (dev->offline) {
+ if (test_bit(DEV_FLAG_OFFLINE, &dev->flags)) {
ret = dev->bus->online(dev);
if (!ret) {
kobject_uevent(&dev->kobj, KOBJ_ONLINE);
- dev->offline = false;
+ clear_bit(DEV_FLAG_OFFLINE, &dev->flags);
}
} else {
ret = 1;
@@ -4715,7 +4716,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) && !test_bit(DEV_FLAG_OFFLINE_DISABLED, &dev->flags)) {
/* 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..e4e6a399def4 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);
+ assign_bit(DEV_FLAG_OFFLINE_DISABLED, &cpu->dev.flags, !cpu->hotpluggable);
+ assign_bit(DEV_FLAG_OFFLINE, &cpu->dev.flags, !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..7f42727dde81 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;
+ assign_bit(DEV_FLAG_OFFLINE, &memory->dev.flags, memory->state == MEM_OFFLINE);
ret = device_register(&memory->dev);
if (ret) {
diff --git a/include/linux/device.h b/include/linux/device.h
index f6ca067bacca..fd53aa04cad9 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -484,6 +484,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,
@@ -494,6 +496,8 @@ enum struct_device_flags {
DEV_FLAG_STATE_SYNCED,
DEV_FLAG_DMA_COHERENT,
DEV_FLAG_OF_NODE_REUSED,
+ DEV_FLAG_OFFLINE_DISABLED,
+ DEV_FLAG_OFFLINE,
};
/**
@@ -571,8 +575,6 @@ enum struct_device_flags {
* 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
@@ -677,9 +679,6 @@ struct device {
enum device_removable removable;
- bool offline_disabled:1;
- bool offline:1;
-
unsigned long flags;
};
diff --git a/kernel/cpu.c b/kernel/cpu.c
index bc4f7a9ba64e..15a873ad8025 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;
+ set_bit(DEV_FLAG_OFFLINE, &dev->flags);
/* 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;
+ clear_bit(DEV_FLAG_OFFLINE, &dev->flags);
/* 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] 18+ messages in thread* Re: [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs
2026-04-03 0:49 ` [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs Douglas Anderson
@ 2026-04-03 11:56 ` Mark Brown
0 siblings, 0 replies; 18+ messages in thread
From: Mark Brown @ 2026-04-03 11:56 UTC (permalink / raw)
To: Douglas Anderson
Cc: Greg Kroah-Hartman, Rafael J . Wysocki, Danilo Krummrich,
Alan Stern, Robin Murphy, Leon Romanovsky, Paul Burton,
Saravana Kannan, Alexander Lobakin, Eric Dumazet, Toshi Kani,
Christoph Hellwig, Alexey Kardashevskiy, Johan Hovold, 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
[-- Attachment #1: Type: text/plain, Size: 298 bytes --]
On Thu, Apr 02, 2026 at 05:49:55PM -0700, Douglas Anderson wrote:
> 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.
Acked-by: Mark Brown <broonie@kernel.org>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread