« 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.
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.
132 KB
256 KB (next power of two)
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:
- 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.
- It walks from the end of the memory you actually asked for to the end of the memory that was actually allocated.
- It frees every page in that leftover range back to the buddy system, one page at a time.
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");
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
sizeyou passed toalloc_pages_exact()and reuse it infree_pages_exact(). - Using it for huge buffers: If your size approaches the
MAX_PAGE_ORDERceiling, 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_ATOMICif 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
Yes. It remains part of the core page allocator API in current mainline Linux and is exported for use by kernel modules and drivers.
No, not by default. If you need zeroed memory, combine your GFP flags with __GFP_ZERO.
Yes, as long as you pass GFP_ATOMIC instead of GFP_KERNEL, since it must never sleep in that context.
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.
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.
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.
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.
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.
