← Previous Lecture | Next Lecture →
This lecture continues EmbeddedPathashala’s free Linux kernel development course and
free Linux device drivers course. In the previous lecture we covered GFP flags. Now we go
one level lower and look at the page allocator itself — the kernel subsystem responsible for handing out
raw physical pages, and how to correctly free them again.
__get_free_page() and __get_free_pages()
free_page() and free_pages()
Power-of-two allocation sizing
Common memory bugs in kernel code
Writing a demo kernel module
Prerequisites
- Completion of Lecture 1 of this series on GFP flags.
- Basic familiarity with writing and loading a Linux kernel module (insmod / rmmod).
- A Linux system with kernel headers installed, for building modules locally.
What Is the Page Allocator?
The Linux kernel manages physical memory in fixed-size units called pages (4 KB on most common
architectures). Almost every other memory allocator in the kernel — the slab allocator behind
kmalloc(), the vmalloc subsystem, filesystem buffer caches — ultimately gets its memory from
the page allocator, also known as the buddy allocator because of the buddy-system algorithm
it uses internally to split and merge blocks of pages efficiently.
Most driver authors never call the page allocator directly; kmalloc()/kzalloc()
cover the common case. But understanding the page allocator is valuable because it explains where all other
kernel memory ultimately comes from, and it is the right tool when you genuinely need page-aligned, whole-page
chunks of memory.
Allocating Pages: The Core Functions
The page allocator exposes a small family of functions for grabbing pages. The two you will meet most
often are __get_free_page() for a single page, and __get_free_pages() for a block
of 2order contiguous pages. Both take a GFP flag (see Lecture 1) describing the calling context.
/* Original illustrative example for this course. Allocate one page,
* use it, and free it correctly. */
#include <linux/module.h>
#include <linux/gfp.h>
static unsigned long demo_page;
static int __init pagealloc_demo_init(void)
{
demo_page = __get_free_page(GFP_KERNEL);
if (!demo_page) {
pr_err("pagealloc_demo: page allocation failed\n");
return -ENOMEM;
}
pr_info("pagealloc_demo: allocated one page at kernel virtual addr 0x%lx\n",
demo_page);
return 0;
}
static void __exit pagealloc_demo_exit(void)
{
free_page(demo_page);
pr_info("pagealloc_demo: page freed, module unloaded\n");
}
module_init(pagealloc_demo_init);
module_exit(pagealloc_demo_exit);
MODULE_LICENSE("GPL");
Notice the pattern: allocate, check for failure, use the memory, and always free exactly what you
allocated, exactly once. This pattern is the same whether you are working with a single page or a multi-page
block.
Allocating Multiple Pages with an Order
When you need more than one contiguous page, you specify an order rather than a raw byte
count. The actual number of pages allocated is always 2order — this is a direct
consequence of how the buddy allocator manages free blocks internally, splitting and merging pairs of
“buddy” blocks of matching size.
| Order | Pages Allocated | Size (4 KB pages) |
|---|---|---|
| 0 | 1 | 4 KB |
| 1 | 2 | 8 KB |
| 2 | 4 | 16 KB |
| 3 | 8 | 32 KB |
/* Original illustrative example: allocate 4 contiguous pages (order = 2) */
unsigned long block;
block = __get_free_pages(GFP_KERNEL, 2); /* order 2 -> 4 pages -> 16 KB */
if (!block)
return -ENOMEM;
/* ... use the 16 KB block ... */
free_pages(block, 2); /* must pass the SAME order used to allocate */
__get_free_page()
free_page()
__get_free_pages(gfp, order)
free_pages(addr, same order)
Common Mistakes and Troubleshooting
| Mistake | Why It’s a Problem | Fix |
|---|---|---|
| Freeing with the wrong order | Corrupts the buddy allocator’s bookkeeping | Store the order alongside the pointer, free with the same value |
| Not checking the return value | NULL pointer dereference, possible crash | Always check for 0 / NULL before use |
| Double-freeing the same pages | Memory corruption, security risk | Clear the pointer to NULL after freeing |
| Memory leak on error paths | Slowly exhausts system memory | Use clear goto-based cleanup labels in every function |
Best Practices
- Prefer
kmalloc()/kzalloc()for general-purpose small allocations; reach for
the page allocator only when you specifically need page-aligned or whole-page memory. - Keep a single source of truth for the order used, ideally a named constant, so allocate and free calls
cannot drift apart. - Favor zeroed allocations where the data is security-sensitive or where uninitialized memory could leak
information. - Use kernel static analysis tools such as Coccinelle, sparse, and smatch, and dynamic tools such as
KASAN, to catch allocator misuse during development rather than in production.
Performance Considerations
Higher-order allocations (order 3 and above) become progressively harder to satisfy as the system runs and
physical memory fragments, because the buddy allocator needs that many physically contiguous pages.
Where possible, prefer multiple smaller allocations or vmalloc() (which only needs virtually
contiguous memory) over a single large physically-contiguous request, unless your hardware genuinely requires
physical contiguity, such as for DMA buffers.
Security Considerations
Freshly allocated, non-zeroed pages may contain residual data from whatever previously used those physical
frames. If that memory is ever exposed to user space — copied out via an ioctl, mmap’d, or similar — failing
to zero it first can leak sensitive kernel data. Use zeroed allocation paths whenever the memory will be
visible outside the kernel.
Summary / Key Takeaways
- The page allocator is the foundation underneath nearly all other kernel memory allocators.
__get_free_page()/free_page()handle single pages;__get_free_pages()/free_pages()handle power-of-two blocks specified by an order.- Always free with the exact same order used at allocation time.
- Higher orders are harder to satisfy as memory fragments — keep allocation sizes as small as practical.
- Use static and dynamic analysis tools during development to catch allocator bugs early.
Conclusion
Together, GFP flags and the page allocator form the foundation of kernel memory management. Once you
understand that GFP flags describe context (can this allocation sleep?) and the page allocator
describes granularity (how many physically contiguous pages do I need?), the rest of the kernel’s
memory APIs — kmalloc, vmalloc, and beyond — become much easier to reason about. This concludes this two-part
module of EmbeddedPathashala’s free Linux kernel development course; keep following the series for more free
Linux device drivers and embedded systems lessons.
FAQ
Q1. What is the difference between kmalloc() and the page allocator?
kmalloc() uses the slab allocator for arbitrary-sized small allocations and is itself built on
top of the page allocator; the page allocator deals only in whole pages or power-of-two blocks of pages.
Q2. Why must I free pages with the same order I used to allocate them?
The buddy allocator tracks free blocks by size; passing a mismatched order corrupts its internal bookkeeping
and can corrupt unrelated memory.
Q3. When should I use the page allocator instead of kmalloc()?
When you specifically need page-aligned memory or large physically contiguous regions, such as certain DMA
buffer scenarios.
Q4. What happens if a high-order allocation fails?
You get a NULL/0 return; your code should handle this gracefully, and consider falling back to
vmalloc() if physical contiguity is not strictly required.
Q5. Is the page allocator API the same across recent kernel versions?
The core functions covered here have remained stable for a long time; always cross-check against your
specific kernel source tree for the most current signatures and any newer helper wrappers.
Q6. What tools can help me find page allocator bugs?
Static analyzers such as sparse, Coccinelle, and smatch, plus dynamic tools like KASAN, are commonly used
within the kernel build system to catch these issues.
Q7. Can I allocate zeroed pages directly from the page allocator?
Yes, by combining the appropriate GFP flag with zeroing behavior or by using a zeroing wrapper where
available; this avoids exposing stale memory contents.
Explore more lectures in EmbeddedPathashala’s free Linux kernel development, Linux device drivers, and
embedded systems courses.
