* [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
@ 2026-04-03 0:49 ` Douglas Anderson
2026-04-03 0:49 ` [PATCH v3 9/9] driver core: Replace dev->offline + ->offline_disabled with DEV_FLAGs Douglas Anderson
1 sibling, 0 replies; 4+ 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] 4+ 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
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 11:56 ` Mark Brown
1 sibling, 1 reply; 4+ 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] 4+ messages in thread