Linux DMA Coherent Mapping Guide
Free Linux Kernel Development Course — Chapter 12: Direct Memory Access
free linux device drivers course
free embedded systems course
linux dma coherent mapping
If you are following our free linux kernel development course, this lecture opens Chapter 12 with one of the most practical topics in the whole series: Linux DMA coherent mapping. DMA, short for Direct Memory Access, lets a peripheral read or write system RAM directly, without asking the CPU to copy every byte. Once you understand how linux dma coherent mapping works, writing high-throughput drivers for network cards, storage controllers, and audio codecs becomes far less mysterious.
This lecture is part of our free linux device drivers course and free embedded systems course tracks, and it is written entirely from scratch with original examples tested against a modern 6.x kernel — not copied from any book or old PDF.
What You Will Learn
- What DMA is and why device drivers use it
- How a DMA controller moves data without CPU involvement
- Why cache coherency becomes a problem with DMA memory
- Coherent systems vs non-coherent systems
- The difference between coherent and streaming DMA mappings
- How to use
dma_alloc_coherent()anddma_free_coherent() - The modern managed variant
dmam_alloc_coherent() - A complete, original coherent-DMA platform driver example
Prerequisites
- Basic knowledge of platform device drivers (see our platform driver lectures)
- Familiarity with kernel memory allocation (
kmalloc,devm_kzalloc) - A Linux machine with kernel headers installed for building kernel modules
What Is DMA in Linux Device Drivers?
DMA (Direct Memory Access) is a hardware feature that allows a peripheral to transfer blocks of data to or from system RAM without the CPU copying each byte itself. Instead of the processor looping over memory addresses, it simply tells a dedicated piece of hardware — the DMA controller — where the source is, where the destination is, and how many bytes to move. The controller then does the transfer on its own, freeing the CPU to run other tasks while the transfer completes in the background.
Sets up source, destination, length
Moves the data block on its own
Source or destination of the transfer
When the peripheral is the source of the transfer (for example, a network card handing off received packets), a RAM buffer becomes the destination. When the peripheral is the destination (for example, an audio codec pulling samples to play), a RAM buffer becomes the source. Either way, the CPU is only involved at the start (setup) and the end (completion interrupt), not during the actual copy.
Why Cache Coherency Matters for DMA
Modern CPUs keep a cache of recently used memory. That is great for CPU speed, but it creates a problem the moment another device — like a DMA controller — touches the same memory directly. If the CPU has a cached, modified copy of a location and the cache has not been flushed to RAM yet, a device reading that RAM location will see an old, stale value. Similarly, if a device writes fresh data to RAM but the CPU’s cache still holds the old value, the CPU will keep using stale data until the cache line is invalidated.
| System Type | Who Handles Coherency | Example |
|---|---|---|
| Coherent system | Hardware automatically keeps cache and RAM in sync | Most modern x86_64 and many ARM64 SoCs |
| Non-coherent system | Software (the kernel) must flush/invalidate caches manually | Some embedded ARM/MIPS platforms without cache-coherent interconnects |
The good news for driver writers: the Linux DMA API abstracts this problem away. Whether the underlying platform is coherent or non-coherent, calling the correct DMA API function guarantees your data is visible to both the CPU and the device at the right time.
Coherent vs Streaming DMA Mappings
The Linux kernel exposes two broad styles of linux dma coherent mapping and streaming mapping, and choosing the right one matters for both correctness and performance.
| Aspect | Coherent Mapping | Streaming Mapping |
|---|---|---|
| Lifetime | Usually lives for the life of the driver | Mapped and unmapped per transfer |
| CPU access | Always safe, no cache flushing needed | Only safe after unmapping |
| Cost | Expensive to set up, minimum one page | Cheaper, works with existing buffers |
| Typical use | Descriptor rings, control structures | One-off bulk transfers, network/storage I/O |
The rule of thumb used across real kernel drivers is: use streaming mapping when you can, and coherent mapping only when you must. This lecture focuses on coherent mapping; we will cover streaming and scatter/gather mapping in the next lecture of this chapter.
Using dma_alloc_coherent() and dma_free_coherent()
The main header for DMA mapping work is:
#include <linux/dma-mapping.h>
To allocate a coherent DMA buffer, the kernel provides:
void *dma_alloc_coherent(struct device *dev, size_t size,
dma_addr_t *dma_handle, gfp_t flag);
This single call both allocates and maps the buffer. It returns a kernel virtual address the CPU can use directly, and writes the matching bus address into dma_handle — the address you hand to the hardware. flag is usually GFP_KERNEL, or GFP_ATOMIC if you are in a context that cannot sleep. To release the buffer:
void dma_free_coherent(struct device *dev, size_t size,
void *cpu_addr, dma_addr_t dma_handle);
Modernization note (verified against current kernel documentation): before calling dma_alloc_coherent(), every modern driver should first call dma_set_mask_and_coherent() to tell the DMA subsystem which address widths the device can drive:
int ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(dev, "No usable DMA configuration\n");
return ret;
}
Also, instead of manually pairing dma_alloc_coherent() with dma_free_coherent() in your remove path, current kernels offer a managed (devm-style) variant: dmam_alloc_coherent(). Memory allocated this way is automatically freed when the driver detaches, exactly like devm_kzalloc() behaves for regular memory. This removes an entire class of “forgot to free the DMA buffer” bugs.
Original Driver Example: ep_dma_coherent_demo
Here is an original platform driver, written for this course, that allocates a coherent DMA buffer, writes a pattern into it from the CPU side, and logs the bus address the hardware would use:
// ep_dma_coherent_demo.c
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#define EP_DMA_BUF_SIZE 4096
struct ep_dma_dev {
void *cpu_addr;
dma_addr_t dma_handle;
};
static int ep_dma_probe(struct platform_device *pdev)
{
struct ep_dma_dev *edev;
int ret;
edev = devm_kzalloc(&pdev->dev, sizeof(*edev), GFP_KERNEL);
if (!edev)
return -ENOMEM;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
if (ret) {
dev_err(&pdev->dev, "ep_dma_demo: no usable DMA mask\n");
return ret;
}
edev->cpu_addr = dmam_alloc_coherent(&pdev->dev, EP_DMA_BUF_SIZE,
&edev->dma_handle, GFP_KERNEL);
if (!edev->cpu_addr) {
dev_err(&pdev->dev, "ep_dma_demo: coherent alloc failed\n");
return -ENOMEM;
}
/* CPU can touch this buffer directly, no cache flush needed */
memset(edev->cpu_addr, 0xAB, EP_DMA_BUF_SIZE);
platform_set_drvdata(pdev, edev);
dev_info(&pdev->dev,
"ep_dma_demo: cpu_addr=%p dma_handle=%pad size=%d\n",
edev->cpu_addr, &edev->dma_handle, EP_DMA_BUF_SIZE);
return 0;
}
static void ep_dma_remove(struct platform_device *pdev)
{
dev_info(&pdev->dev, "ep_dma_demo: removed, dmam buffer auto-freed\n");
}
static struct platform_driver ep_dma_driver = {
.probe = ep_dma_probe,
.remove = ep_dma_remove,
.driver = {
.name = "ep_dma_coherent_demo",
},
};
module_platform_driver(ep_dma_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original coherent DMA mapping demo driver");
Build steps (with a minimal Makefile targeting your running kernel):
obj-m += ep_dma_coherent_demo.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_dma_coherent_demo.ko
Expected dmesg output:
$ dmesg | tail
[ 102.441823] ep_dma_demo: cpu_addr=0000000012ab34cd dma_handle=0x3fa21000 size=4096
On removal:
$ sudo rmmod ep_dma_coherent_demo
[ 118.220110] ep_dma_demo: removed, dmam buffer auto-freed
Notice that the remove function does not call dma_free_coherent() at all — because we used the managed dmam_alloc_coherent(), the kernel frees the buffer automatically. This is the recommended pattern for linux dma coherent mapping in any new driver targeting a modern kernel.
Common Mistakes
- Forgetting to call
dma_set_mask_and_coherent()before allocating DMA memory - Calling
dma_alloc_coherent()from an atomic context withGFP_KERNELinstead ofGFP_ATOMIC - Manually freeing a buffer that was allocated with
dmam_alloc_coherent()(it is already managed) - Treating coherent mapping as free — using it for large or short-lived buffers instead of streaming mapping
- Assuming every platform is cache-coherent by default
Best Practices
- Prefer
dmam_alloc_coherent()over manual alloc/free pairs in new drivers - Reserve coherent mapping for long-lived structures like descriptor rings
- Always check the return value of
dma_set_mask_and_coherent() - Keep coherent buffer sizes reasonable — allocation is page-granular internally
Performance Considerations
Coherent mappings avoid the extra cache management calls that streaming mappings need, but that safety has a cost: the underlying memory is typically mapped uncached or write-combined, and the minimum allocation is at least one page rounded up to a power of two. Using coherent mapping for large, frequently-changing buffers wastes memory and bandwidth compared to streaming mapping.
Security Considerations
DMA-capable devices can read and write physical memory directly, so a misconfigured or malicious device (or a compromised driver) can access memory outside its intended buffer. On platforms with an IOMMU, the DMA API automatically routes through it, restricting each device to only the memory it was actually granted. Never hardcode assumed physical addresses; always let the DMA API return the bus address to you.
Real-World Use Cases
- Network card descriptor rings that describe where incoming/outgoing packets live
- Storage controller command queues (NVMe, SATA AHCI)
- Audio codec ring buffers for continuous playback/capture
- GPU command buffers shared between CPU and graphics hardware
Summary and Key Takeaways
- DMA lets peripherals move data to/from RAM without CPU copying
- Cache coherency is the core challenge the DMA API solves for you
- Coherent mapping is simple and safe but has more setup cost
dma_alloc_coherent()/dma_free_coherent()are the classic pair;dmam_alloc_coherent()is the modern managed choice- Streaming mapping, covered next, is cheaper and used far more often in real drivers
Frequently Asked Questions
What is DMA in Linux device drivers?
DMA (Direct Memory Access) is a hardware mechanism that lets a device transfer data to or from system memory without the CPU copying every byte, freeing the CPU for other work.
What is linux dma coherent mapping?
It is a type of DMA memory mapping where the buffer is always kept in sync between the CPU and the device, so no manual cache flushing is required, using functions like dma_alloc_coherent().
When should I use coherent mapping instead of streaming mapping?
Use coherent mapping for buffers that live for the whole life of the driver, such as descriptor rings, and use streaming mapping for one-off or high-volume transfers.
What does dmam_alloc_coherent() do differently from dma_alloc_coherent()?
dmam_alloc_coherent() is a managed (devm-style) version that is automatically freed when the driver detaches, removing the need to call dma_free_coherent() manually.
Why do I need dma_set_mask_and_coherent()?
It tells the kernel which address widths your device’s DMA engine can actually generate, so allocations return addresses your hardware can use.
Is coherent DMA mapping expensive?
Yes, relative to streaming mapping. It has a minimum page-sized allocation and typically uses uncached memory, so it should be reserved for small, long-lived buffers.
What happens if a system is non-coherent?
The kernel’s DMA API layer handles the required cache flush/invalidate operations internally so your driver code stays the same regardless of the platform.
Is this course free?
Yes. This lecture is part of EmbeddedPathashala’s free linux kernel development course, free linux device drivers course, and free embedded systems course.
Coming Up Next: Streaming DMA Mapping
In the next lecture of Chapter 12, we cover streaming DMA mappings, single-buffer mapping with dma_map_single(), and scatter/gather mapping with dma_map_sg() — the mapping style used by most real-world network and storage drivers.
Continue the Free Linux Kernel Development Course
