Understanding Linux Reserved Memory Mechanism

As the kernel runs, physical memory becomes increasingly fragmented. Certain devices, such as those using DMA, require large contiguous physical memory blocks. When such memory is unavailable at runtime, the device may fail to operate. To address this, Linux provides a mechanism to reserve a dedicated region of physical memory for specific devices. This reserved memory is not managed by the system's buddy allocator and is bound exclusively to the device that needs it.

While reserving memory is straightforward, it leads to waste when the device is idle — the reserved area cannot be reused. A more flexible approach is the Contiguous Memory Allocator (CMA), which allows the system to use reserved memory when the device is not active and returns it to the device when needed. Additionally, for drivers that use DMA on such reserved regions, a shared-dma-pool can be declared in the device tree to create a dedicated DMA memory pool accessible via the DMA API.

Memory Node in Device Tree

/ {
    #address-cells = <1>;
    #size-cells = <1>;

    memory {
        reg = <0x80000000 0x20000000>;
    };
};

The #address-cells and #size-cells properties define the number of 32‑bit cells needed to represant an address or size in the child nodes. In the memory node, the reg property consists of two cells: start address (0x80000000) and size (0x20000000). The kernel parses this during boot to learn about available physical memory. Note that the final memory layout may be modified by the bootloader.

Reserved Memory

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    display_reserved: framebuffer@8e000000 {
        reg = <0x8e000000 0x02000000>;   /* static reservation */
    };
};

fb0: video@12300000 {
    memory-region = <&display_reserved>;
};

A static reserved region is specified by the reg property (address and size). Alternatively, a dynamic reservation can be used by providing only a size and an optional alignment value, letting the kernel choose the physical location:

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    display_reserved: framebuffer@8e000000 {
        size = <0x02000000>;
        alignment = <0x2000>;            /* optional alignment */
    };
};

The memory-region property in a device node references the reserved region. This makes it convenient for the driver to retrieve the physical address using DT APIs. Even without this reference, the device can still use the region as long as it knows the address — the memory is simply never given to the system.

Reserved memory (without no-map) appears under /proc/meminfo as Reserved and is excluded from MemTotal.

If the no-map property is present, the region is not mapped into the kernel’s virtual address space and is entirely invisible to the system. The driver must create its own mapping, for example:

struct device_node *np;
struct resource r;
phys_addr_t paddr;
void *vaddr;

np = of_parse_phandle(dev->of_node, "memory-region", 0);
if (!np) {
    dev_err(dev, "No memory-region property\n");
    return -ENODEV;
}
if (of_address_to_resource(np, 0, &r)) {
    dev_err(dev, "Failed to get region address\n");
    return -EINVAL;
}

paddr = r.start;
vaddr = memremap(paddr, resource_size(&r), MEMREMAP_WB);
dev_info(dev, "Reserved memory mapped: vaddr %p, paddr %pap\n",
         vaddr, &paddr);

CMA Memory Pool

CMA is a special type of reserved memory that can be used by the buddy system when the owning device is idle. It is declared with the compatible = "shared-dma-pool" and the reusable flag. The kernel initialises CMA pools during early boot (code in kernel/dma/contiguous.c and mm/cma.c).

reserved-memory {
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;

    linux_cma_region: linux-cma-buffers@931000000 {
        compatible = "shared-dma-pool";
        reusable;
        reg = <0x09 0x40000000 0x00 0x20000000>;
        linux,cma-default;
    };
};

Key properties:

  • reusable — mandatory for CMA. Allows the OS to use this memory when the device does not need it, and to reclaim it on demand.
  • linux,cma-default — if present, this region becomes the default CMA pool for the system.
  • no-map must not be used together with reusable.

Drivers typically do not call cma_alloc() directly. Instead, the DMA API uses dma_alloc_from_contiguous(), which internally calls cma_alloc():

struct page *dma_alloc_from_contiguous(struct device *dev, int count,
                                       unsigned int align)
{
    return cma_alloc(dev_get_cma_area(dev), count, align);
}

Memory allocated from a CMA pool is accounted in MemTotal and appears in CmaReserved / CmaFree in /proc/meminfo.

DMA Memory Pool (Coherent)

For devices that require a dedicated DMA memory pool (not reclaimable by the system), use compatible = "shared-dma-pool" together with the no-map property. This creates a pool managed by the coherent DMA allocator (kernel/dma/coherent.c).

reserved-memory {
    #address-cells = <1>;
    #size-cells = <1>;
    ranges;

    dma_memory_region: dma-memory@86000000 {
        compatible = "shared-dma-pool";
        reg = <0x86000000 0x02000000>;
        no-map;
    };
};

The no-map property prevents the kernel from creating a page table mapping. The driver must use ioremap() to access it, much like memory‑mapped I/O.

In the driver, the DMA API can be used to allocate from this pool:

int rc;
struct device *dev;
void *vaddr;
dma_addr_t paddr;

/* Bind device to its reserved memory region */
rc = of_reserved_mem_device_init(dev);
if (rc) {
    dev_err(dev, "Failed to init reserved memory\n");
    return rc;
}

/* Set the DMA coherent mask (must match the region size) */
dma_set_coherent_mask(dev, DMA_BIT_MASK(32));

/* Allocate coherent memory from the dedicated pool */
vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &paddr, GFP_KERNEL);
if (!vaddr) {
    dev_err(dev, "dma_alloc_coherent failed\n");
    return -ENOMEM;
}
dev_info(dev, "Coherent memory allocated: vaddr %p, paddr %pad\n",
         vaddr, &paddr);

Memory reserved with no-map does not appear in MemTotal and is not tracked in Reserved.

Summary of Key Differences

Type Properties Visibility Reclaimable by OS?
Static reserved reg Visible in Reserved, not in MemTotal No
Static reserved + no‑map reg, no-map Completely hidden No
CMA pool compatible="shared-dma-pool", reusable Part of MemTotal, shown in CmaReserved Yes (when device inactvie)
DMA coherent pool compatible="shared-dma-pool", no-map Hidden No

Tags: Linux kernel Device Tree CMA DMA Reserved Memory

Posted on Sun, 06 Sep 2026 16:43:19 +0000 by raker7