How Does alloc_pages_exact() Work in Linux? – Best Linux Device Drivers Training in Hyderabad

alloc_pages_exact() in Linux Kernel: The Exact Page Allocator API Explained
A free Linux kernel development course lesson from EmbeddedPathashala — Memory Management Series
Level: Intermediate
Reading time: 12 min
Kernel version: 6.x (current mainline)

« Previous Lecture  |  Next Lecture »

If you have ever written a Linux device driver that needs a large, physically contiguous buffer — say for a video capture card, a DMA ring, or a firmware image — you have probably bumped into a strange side effect of the kernel’s default page allocator: it can waste a huge amount of RAM just to satisfy your request. This lesson explains exactly why that happens and how the kernel’s alloc_pages_exact() linux kernel API solves the problem cleanly, without touching the slab allocator at all.

What You Will Learn
Why the buddy allocator wastes memory
alloc_pages_exact() internals
free_pages_exact() usage rules
Order and power-of-two allocation
GFP_KERNEL vs GFP_ATOMIC in this API
Real driver use cases
Common bugs and how to avoid them

Prerequisites

Before this lesson, you should already be comfortable with the basics of kernel module programming, and ideally have gone through our earlier lessons on the buddy allocator and __get_free_pages(). You don’t need any prior GFP flag knowledge — we cover that here too.

Why the Default Page Allocator Wastes Memory

The Linux kernel’s core physical memory allocator is a binary buddy allocator. It only hands out memory in power-of-two chunks called “orders.” Order 0 is one page (4 KB on most architectures), order 1 is two pages (8 KB), order 2 is four pages (16 KB), and so on, up to a maximum order that the kernel currently caps around 4 MB on typical configurations.

This design is fast and avoids fragmentation, but it has one annoying consequence: if you ask for a size that doesn’t land exactly on a power of two, the allocator rounds your request up to the next order and gives you the whole block. Nothing forces it to give the leftover pages back.

Buddy Allocator Rounding: 132 KB Request
You asked for
132 KB
→
Buddy allocator gives you
256 KB (next power of two)
→
Wasted
124 KB unusable

For small allocations this rounding is harmless. But once you start allocating multiple pages for large-ish buffers, the wasted memory becomes significant — and this waste happens every single time you allocate, and it cannot be reclaimed by anyone else while your driver holds the block.

The Exact Page Allocator APIs

To fix this, the kernel provides a small, purpose-built pair of functions that sit on top of the buddy allocator: alloc_pages_exact() and free_pages_exact(). They still allocate physically contiguous memory (a hard requirement for DMA and many hardware buffers), but they trim away the unused tail pages before returning to the caller.

#include <linux/gfp.h>

void *alloc_pages_exact(size_t size, gfp_t gfp_mask);
void  free_pages_exact(void *virt, size_t size);
Parameter Meaning
size Exact number of bytes you need (not rounded by you — the function handles rounding internally).
gfp_mask Standard GFP flags — GFP_KERNEL if you can sleep, GFP_ATOMIC if you cannot.
virt (free function) The pointer returned by the matching alloc_pages_exact() call.

How It Works Internally

The implementation is a good example of pragmatic kernel engineering. Conceptually, it does three things:

  1. It rounds your requested size up to the nearest power-of-two order and allocates that full block using the normal page allocator, exactly like the buddy allocator would.
  2. It walks from the end of the memory you actually asked for to the end of the memory that was actually allocated.
  3. It frees every page in that leftover range back to the buddy system, one page at a time.
Internal Workflow of alloc_pages_exact()
1. Round size up to nearest power-of-two order
2. Allocate the full block from the buddy allocator
3. Identify the unused tail pages beyond your requested size
4. Free the tail pages back to the system, one page at a time
5. Return a pointer to your exact-sized, still-contiguous buffer

The net result: the memory you get back is still guaranteed to be physically contiguous (important for hardware that does DMA without a scatter-gather list), but the kernel is not sitting on pages it will never hand you.

A Minimal Usage Example

Here is a simple pattern you’ll typically see inside a driver’s probe function or buffer setup routine.

#include <linux/module.h>
#include <linux/gfp.h>

#define DRIVER_BUF_SIZE (132 * 1024)  /* 132 KB, not a power of two */

static void *drv_buf;

static int __init exact_alloc_demo_init(void)
{
    drv_buf = alloc_pages_exact(DRIVER_BUF_SIZE, GFP_KERNEL);
    if (!drv_buf)
        return -ENOMEM;

    pr_info("exact_alloc_demo: got %d bytes at %p\n",
            DRIVER_BUF_SIZE, drv_buf);
    return 0;
}

static void __exit exact_alloc_demo_exit(void)
{
    free_pages_exact(drv_buf, DRIVER_BUF_SIZE);
}

module_init(exact_alloc_demo_init);
module_exit(exact_alloc_demo_exit);
MODULE_LICENSE("GPL");
Note

Always pass the same size value to free_pages_exact() that you passed to alloc_pages_exact(). The free routine relies on that exact value to compute which pages to release. Passing a mismatched size is a real bug and can corrupt kernel memory.

Size Limits You Must Respect

Because alloc_pages_exact() is built on top of the ordinary page allocator, it inherits the same ceiling: the maximum order the buddy allocator can hand out in one call. On most current kernels this maximum is defined by MAX_PAGE_ORDER (older kernel sources, and this PDF’s original text, refer to the same limit as MAX_ORDER — the macro was renamed and clarified starting with Linux 6.8, but the underlying concept, and the practical ceiling of a few megabytes per single allocation, has not changed). If you need more memory than that in one physically contiguous chunk, you need a different tool entirely, such as CMA (Contiguous Memory Allocator) reserved regions or vmalloc() for large buffers that don’t need to be physically contiguous.

Real-World Use Cases

Use Case Why alloc_pages_exact() Fits
Video/frame buffers Odd-sized resolutions rarely land on power-of-two byte counts.
DMA ring buffers Needs physical contiguity but a driver-specific, non-power-of-two size.
Firmware image staging Firmware blobs come in arbitrary sizes decided by the vendor.

Common Mistakes and Troubleshooting

  • Mismatched free size: Always store the original size you passed to alloc_pages_exact() and reuse it in free_pages_exact().
  • Using it for huge buffers: If your size approaches the MAX_PAGE_ORDER ceiling, the allocation will fail under memory pressure even though it “should” fit. Consider CMA instead.
  • Calling with GFP_KERNEL from interrupt context: This can trigger a sleep in atomic context. Use GFP_ATOMIC if you’re not in process context (see our companion lesson on GFP flags).
  • Forgetting to check the return value: Like every kernel allocator, it can return NULL. Always check before use.

Best Practices

  • Prefer alloc_pages_exact() over raw __get_free_pages() whenever your buffer size is not naturally a power of two and you want to be a good citizen of system memory.
  • Prefer the slab/kmalloc layer instead for small, frequently allocated objects — this API is meant for larger, page-granularity buffers.
  • Document the exact size passed at the allocation site so the matching free call is unambiguous during code review.

Performance and Security Considerations

Performance-wise, the internal “allocate big, then free the tail” approach costs a handful of extra function calls at allocation time compared to a raw buddy allocation, but this is a one-time cost paid once per allocation, not per byte, so it’s negligible in practice. From a security standpoint, treat any buffer obtained this way like any other kernel allocation: zero it if it will ever be exposed to user space or hardware you don’t fully trust, and never leak the raw kernel pointer to user space.

Summary / Key Takeaways

  • The buddy allocator only serves power-of-two sized chunks, which wastes memory for odd-sized requests.
  • alloc_pages_exact() allocates the necessary block and then frees back the unused tail pages.
  • Memory returned is still physically contiguous, making it safe for DMA use.
  • Always free with the exact same size using free_pages_exact().
  • Allocation size is still bounded by the kernel’s maximum page order.

Conclusion

The alloc_pages_exact() and free_pages_exact() pair is a small but genuinely useful corner of the Linux memory management subsystem. It gives driver authors a simple way to avoid wasting physical memory on non-power-of-two, page-granularity buffers, without giving up the physical contiguity guarantee that DMA-capable hardware often demands. Once you understand the underlying buddy allocator behavior, this API is easy to reason about and safe to use correctly. In the next lesson, we go one level deeper into the GFP flags that control every one of these allocation calls.

FAQ

Is alloc_pages_exact() still available in the latest Linux kernel?

Yes. It remains part of the core page allocator API in current mainline Linux and is exported for use by kernel modules and drivers.

Does alloc_pages_exact() zero the memory it returns?

No, not by default. If you need zeroed memory, combine your GFP flags with __GFP_ZERO.

Can I use alloc_pages_exact() in interrupt context?

Yes, as long as you pass GFP_ATOMIC instead of GFP_KERNEL, since it must never sleep in that context.

What happens if I pass the wrong size to free_pages_exact()?

The function computes which pages to release based on the size you give it. A mismatched size can free the wrong pages or leak memory, so it must exactly match the original allocation.

Is there a node-aware version of this API?

Yes, the kernel also provides an equivalent that lets you prefer a specific NUMA node before falling back to others, for systems where memory locality matters.

How is this different from kmalloc()?

kmalloc() goes through the slab/SLUB allocator and is meant for smaller general-purpose objects. alloc_pages_exact() works directly with the page allocator and is meant for larger, page-granularity, physically contiguous buffers.

Where can I practice this as part of a free Linux kernel development course?

EmbeddedPathashala’s free Linux kernel development course and free Linux device drivers course both include hands-on labs covering the page allocator, GFP flags, and memory management internals.

Continue Learning for Free

This lesson is part of EmbeddedPathashala’s free Linux kernel development course and free Linux device drivers course — covering memory management, synchronization, and driver internals from the ground up.

« Previous Lecture  |  Next Lecture »

Leave a Reply

Your email address will not be published. Required fields are marked *