summaryrefslogtreecommitdiff
path: root/kernel/dma
diff options
context:
space:
mode:
authorThomas Gleixner <tglx@kernel.org>2026-04-12 22:33:39 +0200
committerThomas Gleixner <tglx@kernel.org>2026-04-12 22:33:39 +0200
commit1655f6895a896eb632ca8a019259bc5d358a9712 (patch)
treed34ad546baeed14bede485773b1be28ab8e6a289 /kernel/dma
parent7eaf8e32de5f4ed4defda6fff81749041bb9d23f (diff)
parent68ed094971b09ba530baf6f75cf1902df880a8d1 (diff)
Merge tag 'timers-v7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux into timers/clocksource
- Added the DT bindings for the compatible string 'fsl,imx25-epit' (Frank Li) - Made the rttm_cs variable static for the rtl otto timer driver (Krzysztof Kozlowski) - Fixed error return code handling in the sun5i timer driver (Chen Ni) - Made the timer-of and the mmio code compatible with modules (Daniel Lezcano) Link: https://lore.kernel.org/151feae1-39ba-4abd-a9f9-9bff377a2cd8@oss.qualcomm.com
Diffstat (limited to 'kernel/dma')
-rw-r--r--kernel/dma/Kconfig6
-rw-r--r--kernel/dma/coherent.c2
-rw-r--r--kernel/dma/contiguous.c26
-rw-r--r--kernel/dma/debug.c30
-rw-r--r--kernel/dma/direct.c5
-rw-r--r--kernel/dma/direct.h5
-rw-r--r--kernel/dma/map_benchmark.c2
-rw-r--r--kernel/dma/mapping.c6
-rw-r--r--kernel/dma/pool.c34
-rw-r--r--kernel/dma/remap.c2
-rw-r--r--kernel/dma/swiotlb.c7
11 files changed, 75 insertions, 50 deletions
diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig
index 31cfdb6b4bc3..159900736f25 100644
--- a/kernel/dma/Kconfig
+++ b/kernel/dma/Kconfig
@@ -47,12 +47,6 @@ config ARCH_HAS_DMA_SET_MASK
config ARCH_HAS_DMA_WRITE_COMBINE
bool
-#
-# Select if the architectures provides the arch_dma_mark_clean hook
-#
-config ARCH_HAS_DMA_MARK_CLEAN
- bool
-
config DMA_DECLARE_COHERENT
bool
diff --git a/kernel/dma/coherent.c b/kernel/dma/coherent.c
index 77c8d9487a9a..1147497bc512 100644
--- a/kernel/dma/coherent.c
+++ b/kernel/dma/coherent.c
@@ -49,7 +49,7 @@ static struct dma_coherent_mem *dma_init_coherent_memory(phys_addr_t phys_addr,
if (!mem_base)
return ERR_PTR(-EINVAL);
- dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL);
+ dma_mem = kzalloc_obj(struct dma_coherent_mem);
if (!dma_mem)
goto out_unmap_membase;
dma_mem->bitmap = bitmap_zalloc(pages, GFP_KERNEL);
diff --git a/kernel/dma/contiguous.c b/kernel/dma/contiguous.c
index d8fd6f779f79..c56004d314dc 100644
--- a/kernel/dma/contiguous.c
+++ b/kernel/dma/contiguous.c
@@ -91,6 +91,16 @@ static int __init early_cma(char *p)
}
early_param("cma", early_cma);
+/*
+ * cma_skip_dt_default_reserved_mem - This is called from the
+ * reserved_mem framework to detect if the default cma region is being
+ * set by the "cma=" kernel parameter.
+ */
+bool __init cma_skip_dt_default_reserved_mem(void)
+{
+ return size_cmdline != -1;
+}
+
#ifdef CONFIG_DMA_NUMA_CMA
static struct cma *dma_contiguous_numa_area[MAX_NUMNODES];
@@ -247,10 +257,12 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
pr_debug("%s: reserving %ld MiB for global area\n", __func__,
(unsigned long)selected_size / SZ_1M);
- dma_contiguous_reserve_area(selected_size, selected_base,
- selected_limit,
- &dma_contiguous_default_area,
- fixed);
+ ret = dma_contiguous_reserve_area(selected_size, selected_base,
+ selected_limit,
+ &dma_contiguous_default_area,
+ fixed);
+ if (ret)
+ return;
ret = dma_heap_cma_register_heap(dma_contiguous_default_area);
if (ret)
@@ -470,12 +482,6 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
struct cma *cma;
int err;
- if (size_cmdline != -1 && default_cma) {
- pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n",
- rmem->name);
- return -EBUSY;
- }
-
if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
of_get_flat_dt_prop(node, "no-map", NULL))
return -EINVAL;
diff --git a/kernel/dma/debug.c b/kernel/dma/debug.c
index 138ede653de4..86f87e43438c 100644
--- a/kernel/dma/debug.c
+++ b/kernel/dma/debug.c
@@ -63,6 +63,7 @@ enum map_err_types {
* @sg_mapped_ents: 'mapped_ents' from dma_map_sg
* @paddr: physical start address of the mapping
* @map_err_type: track whether dma_mapping_error() was checked
+ * @is_cache_clean: driver promises not to write to buffer while mapped
* @stack_len: number of backtrace entries in @stack_entries
* @stack_entries: stack of backtrace history
*/
@@ -76,7 +77,8 @@ struct dma_debug_entry {
int sg_call_ents;
int sg_mapped_ents;
phys_addr_t paddr;
- enum map_err_types map_err_type;
+ enum map_err_types map_err_type;
+ bool is_cache_clean;
#ifdef CONFIG_STACKTRACE
unsigned int stack_len;
unsigned long stack_entries[DMA_DEBUG_STACKTRACE_ENTRIES];
@@ -472,12 +474,15 @@ static int active_cacheline_dec_overlap(phys_addr_t cln)
return active_cacheline_set_overlap(cln, --overlap);
}
-static int active_cacheline_insert(struct dma_debug_entry *entry)
+static int active_cacheline_insert(struct dma_debug_entry *entry,
+ bool *overlap_cache_clean)
{
phys_addr_t cln = to_cacheline_number(entry);
unsigned long flags;
int rc;
+ *overlap_cache_clean = false;
+
/* If the device is not writing memory then we don't have any
* concerns about the cpu consuming stale data. This mitigates
* legitimate usages of overlapping mappings.
@@ -487,8 +492,16 @@ static int active_cacheline_insert(struct dma_debug_entry *entry)
spin_lock_irqsave(&radix_lock, flags);
rc = radix_tree_insert(&dma_active_cacheline, cln, entry);
- if (rc == -EEXIST)
+ if (rc == -EEXIST) {
+ struct dma_debug_entry *existing;
+
active_cacheline_inc_overlap(cln);
+ existing = radix_tree_lookup(&dma_active_cacheline, cln);
+ /* A lookup failure here after we got -EEXIST is unexpected. */
+ WARN_ON(!existing);
+ if (existing)
+ *overlap_cache_clean = existing->is_cache_clean;
+ }
spin_unlock_irqrestore(&radix_lock, flags);
return rc;
@@ -583,19 +596,24 @@ DEFINE_SHOW_ATTRIBUTE(dump);
*/
static void add_dma_entry(struct dma_debug_entry *entry, unsigned long attrs)
{
+ bool overlap_cache_clean;
struct hash_bucket *bucket;
unsigned long flags;
int rc;
+ entry->is_cache_clean = !!(attrs & DMA_ATTR_CPU_CACHE_CLEAN);
+
bucket = get_hash_bucket(entry, &flags);
hash_bucket_add(bucket, entry);
put_hash_bucket(bucket, flags);
- rc = active_cacheline_insert(entry);
+ rc = active_cacheline_insert(entry, &overlap_cache_clean);
if (rc == -ENOMEM) {
pr_err_once("cacheline tracking ENOMEM, dma-debug disabled\n");
global_disable = true;
- } else if (rc == -EEXIST && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+ } else if (rc == -EEXIST &&
+ !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+ !(entry->is_cache_clean && overlap_cache_clean) &&
!(IS_ENABLED(CONFIG_DMA_BOUNCE_UNALIGNED_KMALLOC) &&
is_swiotlb_active(entry->dev))) {
err_printk(entry->dev, entry,
@@ -882,7 +900,7 @@ void dma_debug_add_bus(const struct bus_type *bus)
if (dma_debug_disabled())
return;
- nb = kzalloc(sizeof(struct notifier_block), GFP_KERNEL);
+ nb = kzalloc_obj(struct notifier_block);
if (nb == NULL) {
pr_err("dma_debug_add_bus: out of memory\n");
return;
diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c
index 50c3fe2a1d55..8f43a930716d 100644
--- a/kernel/dma/direct.c
+++ b/kernel/dma/direct.c
@@ -425,9 +425,6 @@ void dma_direct_sync_sg_for_cpu(struct device *dev,
arch_sync_dma_for_cpu(paddr, sg->length, dir);
swiotlb_sync_single_for_cpu(dev, paddr, sg->length, dir);
-
- if (dir == DMA_FROM_DEVICE)
- arch_dma_mark_clean(paddr, sg->length);
}
if (!dev_is_dma_coherent(dev))
@@ -657,7 +654,7 @@ int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
if (!offset)
return 0;
- map = kcalloc(2, sizeof(*map), GFP_KERNEL);
+ map = kzalloc_objs(*map, 2);
if (!map)
return -ENOMEM;
map[0].cpu_start = cpu_start;
diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h
index da2fadf45bcd..e89f175e9c2d 100644
--- a/kernel/dma/direct.h
+++ b/kernel/dma/direct.h
@@ -75,9 +75,6 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev,
}
swiotlb_sync_single_for_cpu(dev, paddr, size, dir);
-
- if (dir == DMA_FROM_DEVICE)
- arch_dma_mark_clean(paddr, size);
}
static inline dma_addr_t dma_direct_map_phys(struct device *dev,
@@ -88,7 +85,7 @@ static inline dma_addr_t dma_direct_map_phys(struct device *dev,
if (is_swiotlb_force_bounce(dev)) {
if (attrs & DMA_ATTR_MMIO)
- goto err_overflow;
+ return DMA_MAPPING_ERROR;
return swiotlb_map(dev, phys, size, dir, attrs);
}
diff --git a/kernel/dma/map_benchmark.c b/kernel/dma/map_benchmark.c
index 794041a39e65..0f33b3ea7daf 100644
--- a/kernel/dma/map_benchmark.c
+++ b/kernel/dma/map_benchmark.c
@@ -121,7 +121,7 @@ static int do_map_benchmark(struct map_benchmark_data *map)
int ret = 0;
int i;
- tsk = kmalloc_array(threads, sizeof(*tsk), GFP_KERNEL);
+ tsk = kmalloc_objs(*tsk, threads);
if (!tsk)
return -ENOMEM;
diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c
index 37163eb49f9f..3928a509c44c 100644
--- a/kernel/dma/mapping.c
+++ b/kernel/dma/mapping.c
@@ -638,7 +638,7 @@ void *dma_alloc_attrs(struct device *dev, size_t size, dma_addr_t *dma_handle,
/* let the implementation decide on the zone to allocate from: */
flag &= ~(__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM);
- if (dma_alloc_direct(dev, ops)) {
+ if (dma_alloc_direct(dev, ops) || arch_dma_alloc_direct(dev)) {
cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs);
} else if (use_dma_iommu(dev)) {
cpu_addr = iommu_dma_alloc(dev, size, dma_handle, flag, attrs);
@@ -679,7 +679,7 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
return;
debug_dma_free_coherent(dev, size, cpu_addr, dma_handle);
- if (dma_alloc_direct(dev, ops))
+ if (dma_alloc_direct(dev, ops) || arch_dma_free_direct(dev, dma_handle))
dma_direct_free(dev, size, cpu_addr, dma_handle, attrs);
else if (use_dma_iommu(dev))
iommu_dma_free(dev, size, cpu_addr, dma_handle, attrs);
@@ -768,7 +768,7 @@ static struct sg_table *alloc_single_sgt(struct device *dev, size_t size,
struct sg_table *sgt;
struct page *page;
- sgt = kmalloc(sizeof(*sgt), gfp);
+ sgt = kmalloc_obj(*sgt, gfp);
if (!sgt)
return NULL;
if (sg_alloc_table(sgt, 1, gfp))
diff --git a/kernel/dma/pool.c b/kernel/dma/pool.c
index 26392badc36b..2b2fbb709242 100644
--- a/kernel/dma/pool.c
+++ b/kernel/dma/pool.c
@@ -184,6 +184,12 @@ static __init struct gen_pool *__dma_atomic_pool_init(size_t pool_size,
return pool;
}
+#ifdef CONFIG_ZONE_DMA32
+#define has_managed_dma32 has_managed_zone(ZONE_DMA32)
+#else
+#define has_managed_dma32 false
+#endif
+
static int __init dma_atomic_pool_init(void)
{
int ret = 0;
@@ -199,17 +205,20 @@ static int __init dma_atomic_pool_init(void)
}
INIT_WORK(&atomic_pool_work, atomic_pool_work_fn);
- atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
+ /* All memory might be in the DMA zone(s) to begin with */
+ if (has_managed_zone(ZONE_NORMAL)) {
+ atomic_pool_kernel = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL);
- if (!atomic_pool_kernel)
- ret = -ENOMEM;
+ if (!atomic_pool_kernel)
+ ret = -ENOMEM;
+ }
if (has_managed_dma()) {
atomic_pool_dma = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL | GFP_DMA);
if (!atomic_pool_dma)
ret = -ENOMEM;
}
- if (IS_ENABLED(CONFIG_ZONE_DMA32)) {
+ if (has_managed_dma32) {
atomic_pool_dma32 = __dma_atomic_pool_init(atomic_pool_size,
GFP_KERNEL | GFP_DMA32);
if (!atomic_pool_dma32)
@@ -224,11 +233,11 @@ postcore_initcall(dma_atomic_pool_init);
static inline struct gen_pool *dma_guess_pool(struct gen_pool *prev, gfp_t gfp)
{
if (prev == NULL) {
- if (IS_ENABLED(CONFIG_ZONE_DMA32) && (gfp & GFP_DMA32))
- return atomic_pool_dma32;
- if (atomic_pool_dma && (gfp & GFP_DMA))
- return atomic_pool_dma;
- return atomic_pool_kernel;
+ if (gfp & GFP_DMA)
+ return atomic_pool_dma ?: atomic_pool_dma32 ?: atomic_pool_kernel;
+ if (gfp & GFP_DMA32)
+ return atomic_pool_dma32 ?: atomic_pool_dma ?: atomic_pool_kernel;
+ return atomic_pool_kernel ?: atomic_pool_dma32 ?: atomic_pool_dma;
}
if (prev == atomic_pool_kernel)
return atomic_pool_dma32 ? atomic_pool_dma32 : atomic_pool_dma;
@@ -268,15 +277,20 @@ struct page *dma_alloc_from_pool(struct device *dev, size_t size,
{
struct gen_pool *pool = NULL;
struct page *page;
+ bool pool_found = false;
while ((pool = dma_guess_pool(pool, gfp))) {
+ pool_found = true;
page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
phys_addr_ok);
if (page)
return page;
}
- WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
+ if (pool_found)
+ WARN(!(gfp & __GFP_NOWARN), "DMA pool exhausted for %s\n", dev_name(dev));
+ else
+ WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
return NULL;
}
diff --git a/kernel/dma/remap.c b/kernel/dma/remap.c
index b7c1c0c92d0c..205c0c0ba2fe 100644
--- a/kernel/dma/remap.c
+++ b/kernel/dma/remap.c
@@ -45,7 +45,7 @@ void *dma_common_contiguous_remap(struct page *page, size_t size,
void *vaddr;
int i;
- pages = kvmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
+ pages = kvmalloc_objs(struct page *, count);
if (!pages)
return NULL;
for (i = 0; i < count; i++)
diff --git a/kernel/dma/swiotlb.c b/kernel/dma/swiotlb.c
index a547c7693135..d8e6f1d889d5 100644
--- a/kernel/dma/swiotlb.c
+++ b/kernel/dma/swiotlb.c
@@ -1809,19 +1809,18 @@ static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
if (!mem) {
struct io_tlb_pool *pool;
- mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+ mem = kzalloc_obj(*mem);
if (!mem)
return -ENOMEM;
pool = &mem->defpool;
- pool->slots = kcalloc(nslabs, sizeof(*pool->slots), GFP_KERNEL);
+ pool->slots = kzalloc_objs(*pool->slots, nslabs);
if (!pool->slots) {
kfree(mem);
return -ENOMEM;
}
- pool->areas = kcalloc(nareas, sizeof(*pool->areas),
- GFP_KERNEL);
+ pool->areas = kzalloc_objs(*pool->areas, nareas);
if (!pool->areas) {
kfree(pool->slots);
kfree(mem);