Linux Page Allocator API Guide
A free Linux kernel development course lecture: understand the low-level page allocator, alloc_pages(), GFP flags, and how every other kernel allocator builds on top of it.
The linux page allocator is the lowest-level memory manager in the Linux kernel, and every higher allocator — kmalloc, vmalloc, the SLUB allocator — ultimately asks the page allocator for raw pages. If you are following our free linux kernel development course, this lecture is where the “memory allocation mechanism” overview from the previous lecture becomes concrete: you will learn the exact functions the kernel uses to hand out physical memory, in orders that are always a power of two. This is core reading for anyone building a free linux device drivers course portfolio, because DMA buffers, large driver buffers, and page-cache-style allocations all go through this API directly.
Keywords covered in this lecture
alloc_pages()
__get_free_pages()
GFP flags
free linux kernel development course
free linux device drivers course
What You Will Learn
- Why the page allocator exists and how it sits under kmalloc/vmalloc/SLUB
- The complete page allocation API: alloc_pages(), alloc_page(), __get_free_pages(), get_zeroed_page()
- How to free memory correctly with __free_pages() and free_pages()
- The GFP flags that control zone and allocation behaviour
- How to convert between struct page, kernel virtual addresses, and PFNs
- How the modern kernel’s MAX_ORDER/NR_PAGE_ORDERS limit really works
- How to write, build, and test an original page-allocator demo driver
Prerequisites
- Completion of the earlier lectures in this free linux kernel development course, especially the memory zones and memory-allocation-mechanism lectures
- Comfort building and loading kernel modules (insmod/rmmod, dmesg)
- A Linux VM or board running a recent 6.x kernel, with kernel headers installed
Where the Page Allocator Sits
In the previous lecture we introduced the allocator stack at a high level. The linux page allocator is the layer that physically owns memory: it hands out page frames in blocks whose size is always a power of two, using the buddy algorithm (covered in full detail in the next lecture). Everything above it — the SLUB allocator behind kmalloc(), and vmalloc() for large virtually-contiguous ranges — eventually calls down into this same API.
Allocator Stack
Page Order: Why Everything Is a Power of Two
The page allocator never hands out an arbitrary byte count. It hands out 2^order contiguous pages, where order is a small integer you pass in. Order 0 means a single page; order 3 means eight pages. This restriction is what makes the buddy algorithm possible, and it is the first thing to understand before touching the API.
| Order | Pages | Size (4 KB pages) |
|---|---|---|
| 0 | 1 | 4 KB |
| 1 | 2 | 8 KB |
| 2 | 4 | 16 KB |
| 3 | 8 | 32 KB |
| … | … | … |
| 10 (highest valid order) | 1024 | 4 MB |
Modernization Note
On current mainline kernels the highest usable order is exposed as MAX_ORDER, and the total number of order buckets (0 through MAX_ORDER) is NR_PAGE_ORDERS. Older kernel documentation described MAX_ORDER as “one past the highest order” (so MAX_ORDER=11 meant orders 0-10 were valid); current kernels define MAX_ORDER as the highest valid order itself (10), with NR_PAGE_ORDERS=11 counting the buckets. The practical ceiling is unchanged: a default kernel still tops out at order 10, i.e. 1024 pages, or 4 MB on a 4 KB-page system. This can still be raised with CONFIG_ARCH_FORCE_MAX_ORDER at build time.
Allocating Pages: alloc_pages() and alloc_page()
alloc_pages() is the core allocation call. It returns a pointer to a struct page representing the first page of the block.
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
/* order 0 shortcut for a single page */
#define alloc_page(gfp_mask) alloc_pages(gfp_mask, 0)
Call it with the number of pages you need expressed as an order, and a GFP mask describing where and how the memory should come from.
struct page *pg;
/* Get a single page for kernel use */
pg = alloc_pages(GFP_KERNEL, 0);
if (!pg)
return -ENOMEM;
Getting a Kernel Virtual Address: __get_free_pages()
Most drivers do not want a struct page — they want a usable kernel virtual address. __get_free_pages() and its variants do the alloc_pages() call for you and hand back the address directly.
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
unsigned long __get_free_page(gfp_t gfp_mask); /* order 0 shortcut */
unsigned long get_zeroed_page(gfp_t gfp_mask); /* order 0, zero-filled */
get_zeroed_page() is worth remembering separately: it is the only one of the three that guarantees the returned page is zero-filled, which matters whenever you are handing memory back to user space.
Freeing Pages Correctly
Whichever allocation function you used decides which free function you must use — the pairing matters.
| Allocated with | Free with | Parameter type |
|---|---|---|
| alloc_pages() / alloc_page() | __free_pages(page, order) | struct page * |
| __get_free_pages() / __get_free_page() / get_zeroed_page() | free_pages(addr, order) | unsigned long (kernel virtual address) |
void __free_pages(struct page *page, unsigned int order);
void free_pages(unsigned long addr, unsigned int order);
Warning
The order you pass to a free function must exactly match the order used at allocation time. Passing the wrong order does not fail loudly — it corrupts the buddy free lists and can crash the system much later, far from the real bug.
GFP Flags for Page Allocation
The gfp_mask argument tells the allocator which memory zone to search and whether the caller can sleep while waiting for memory.
| Flag | Meaning |
|---|---|
| GFP_KERNEL | Standard kernel allocation; may sleep while memory is reclaimed |
| GFP_ATOMIC | Cannot sleep; safe to call from interrupt/atomic context, digs into emergency reserves |
| GFP_USER | Allocation on behalf of a user-space request |
| GFP_DMA | Restrict to the legacy ZONE_DMA range for older DMA-limited devices |
| GFP_NOWAIT | Like GFP_ATOMIC’s “don’t sleep” behaviour, but does not dip into emergency reserves |
| __GFP_ZERO | Modifier: zero the returned memory (combine with another flag, e.g. GFP_KERNEL | __GFP_ZERO) |
Modernization Note
GFP_HIGHMEM should never be passed to __get_free_pages()/__get_free_page(), because HIGHMEM pages are not guaranteed to have a permanent kernel virtual address and the function cannot return one reliably. On modern 64-bit kernels this is largely academic: ZONE_HIGHMEM only exists under CONFIG_HIGHMEM, which is a 32-bit-only configuration on current mainline Linux, as covered in the memory zones lecture.
Converting Between Pages, Addresses, and PFNs
Different kernel APIs speak different “languages” for the same memory — a struct page *, a kernel virtual address, or a page frame number (PFN). These helpers translate between them.
struct page *virt_to_page(const void *kaddr); /* virtual addr -> struct page */
void *page_to_virt(const struct page *pg); /* struct page -> virtual addr */
void *page_address(const struct page *page); /* struct page -> logical addr */
page_address() is what get_zeroed_page() effectively does internally: allocate a page, resolve its address, clear it, and hand the address back to the caller. page_to_pfn()/pfn_to_page(), covered in the virtual memory lecture, complete the picture whenever code needs to talk about physical frame numbers instead of addresses.
Original Driver: ep_page_alloc_demo
This driver allocates pages using both API styles at module load, prints their addresses, and frees everything correctly at unload — exactly the discipline every real driver needs.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/gfp.h>
#include <linux/mm.h>
static struct page *ep_page; /* via alloc_pages() */
static unsigned long ep_vaddr; /* via __get_free_pages() */
#define EP_ORDER 2 /* 2^2 = 4 pages = 16 KB */
static int __init ep_page_alloc_init(void)
{
void *page_addr;
/* Style 1: alloc_pages() returns a struct page */
ep_page = alloc_pages(GFP_KERNEL, EP_ORDER);
if (!ep_page) {
pr_err("ep_page_alloc_demo: alloc_pages failed\n");
return -ENOMEM;
}
page_addr = page_address(ep_page);
pr_info("ep_page_alloc_demo: alloc_pages() -> struct page=%p virt=%p\n",
ep_page, page_addr);
/* Style 2: __get_free_pages() returns a virtual address directly */
ep_vaddr = __get_free_pages(GFP_KERNEL | __GFP_ZERO, EP_ORDER);
if (!ep_vaddr) {
pr_err("ep_page_alloc_demo: __get_free_pages failed\n");
__free_pages(ep_page, EP_ORDER);
return -ENOMEM;
}
pr_info("ep_page_alloc_demo: __get_free_pages() -> virt=0x%lx (zeroed)\n",
ep_vaddr);
return 0;
}
static void __exit ep_page_alloc_exit(void)
{
__free_pages(ep_page, EP_ORDER);
free_pages(ep_vaddr, EP_ORDER);
pr_info("ep_page_alloc_demo: all pages freed\n");
}
module_init(ep_page_alloc_init);
module_exit(ep_page_alloc_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala page allocator demo");
Build and Run
make -C /lib/modules/$(uname -r)/build M=$PWD modules
sudo insmod ep_page_alloc_demo.ko
dmesg | tail -5
sudo rmmod ep_page_alloc_demo
dmesg | tail -3
Expected Output
[ 812.221001] ep_page_alloc_demo: alloc_pages() -> struct page=ffffea0001a4c000 virt=ffff9a1c8c530000
[ 812.221010] ep_page_alloc_demo: __get_free_pages() -> virt=0x9a1c8c534000 (zeroed)
[ 830.552330] ep_page_alloc_demo: all pages freed
Real-World Use Cases
- DMA buffers: network and storage drivers often request physically contiguous pages directly from the page allocator before wrapping them with the DMA mapping API
- Large driver buffers: frame buffers or bulk transfer buffers that don’t fit efficiently in a SLUB cache
- Page cache internals: the kernel’s own file page cache is built directly on struct page and the page allocator
- Custom memory pools: drivers that pre-allocate a pool of pages at init time to avoid allocation failures later under memory pressure
Common Mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Mismatched free order | Silent buddy-list corruption | Store the order alongside the allocation; always free with the same value |
| Mixing struct page and virtual-address free calls | Undefined behaviour / crash | __free_pages() for alloc_pages(), free_pages() for __get_free_pages() |
| Using GFP_KERNEL in interrupt context | “scheduling while atomic” bug | Use GFP_ATOMIC or GFP_NOWAIT in atomic context |
| Requesting very high orders casually | Allocation failure under fragmentation | Prefer vmalloc() for large, non-DMA buffers |
Best Practices
- Keep the order as low as possible — order-0 and order-1 requests are far more likely to succeed than order-6+
- Always check the return value; alloc_pages()/__get_free_pages() can and do fail
- Use __GFP_ZERO instead of manually memset()-ing sensitive buffers you plan to expose to user space
- Prefer the devm_ managed variants where available in your driver class, to guarantee cleanup on error paths
Performance Considerations
Lower-order allocations are serviced from per-CPU free lists and are extremely fast; higher-order allocations must walk the buddy free lists and may trigger reclaim or compaction, which is comparatively expensive. If your driver allocates the same size repeatedly, consider a SLUB-based kmem_cache instead of calling the page allocator directly on every request.
Security Considerations
Freshly allocated pages are not guaranteed to be zeroed unless you ask for it — stale kernel data can otherwise leak to user space through an uninitialized buffer. Always use __GFP_ZERO or get_zeroed_page() for any buffer that will be copied out to a user-space process.
Summary / Key Takeaways
- The page allocator hands out memory in power-of-two page blocks, sized by “order”
- alloc_pages()/alloc_page() return a struct page; __get_free_pages() family returns a virtual address
- Free with the function matching how you allocated, and always pass the same order
- GFP flags choose the zone and whether the call can sleep
- The default ceiling is order 10 (4 MB on 4 KB-page systems), tunable via CONFIG_ARCH_FORCE_MAX_ORDER
Conclusion
The page allocator is the foundation every other Linux memory API stands on, and understanding its API directly makes the SLUB allocator and vmalloc() far easier to reason about later in this free linux kernel development course. In the next lecture we go one level deeper into the buddy algorithm itself — how blocks split and merge — which explains why order matters so much and why fragmentation happens at all.
FAQ
What is the linux page allocator?
It is the lowest-level memory manager in the Linux kernel (mm/page_alloc.c), responsible for handing out physical memory in power-of-two page blocks using the buddy algorithm.
What is the difference between alloc_pages() and __get_free_pages()?
alloc_pages() returns a struct page pointer; __get_free_pages() returns a usable kernel virtual address directly, which is more convenient for most drivers.
How many pages can I allocate at once?
By default up to order 10, i.e. 1024 pages (4 MB on a 4 KB-page system), controlled by NR_PAGE_ORDERS/MAX_ORDER and adjustable via CONFIG_ARCH_FORCE_MAX_ORDER.
Which GFP flag should I use in an interrupt handler?
Use GFP_ATOMIC (or GFP_NOWAIT if you don’t want to dip into emergency reserves) — never GFP_KERNEL, which can sleep.
Why did my __free_pages() call crash the kernel later?
Almost always a mismatched order between allocation and free, which silently corrupts the buddy free lists until an unrelated allocation later crashes.
Is GFP_HIGHMEM safe with __get_free_pages()?
No. HIGHMEM pages aren’t guaranteed a permanent kernel virtual address, so __get_free_pages()/__get_free_page() must not be used with GFP_HIGHMEM; use alloc_pages() plus kmap_local_page() instead.
What is this course?
This lecture is part of EmbeddedPathashala’s free linux kernel development course, a free embedded linux course covering kernel internals, device drivers, and memory management on current 6.x kernels.
Continue the Free Linux Kernel Development Course
Next up: how the buddy algorithm actually splits and merges blocks.
