Linux vmalloc and Page Faults
Free Linux Kernel Development Course — Kernel Memory Management, Lecture 11
The linux vmalloc function is one of the two big memory allocation tools every kernel module writer needs to understand, the other one being kmalloc(), which we covered in the previous lecture. While kmalloc() hands you a small, physically contiguous chunk of RAM in a hurry, vmalloc() takes a slower path and gives you a large block of memory that is contiguous only in the kernel’s virtual address space. In this free linux kernel development course lecture, we will build a small driver that uses vmalloc(), watch it in /proc/vmallocinfo, and then go one level deeper to understand the page fault mechanism that makes lazy memory allocation possible in the first place.
free linux development course
free linux device drivers course
free linux kernel development course
linux vmalloc function
page fault handling linux
What You Will Learn
vmalloc / vzalloc / vfree API
Lazy memory allocation
Hard, soft, and unresolved page faults
/proc/vmallocinfo
Writing an original vmalloc driver
Prerequisites
Basic idea of virtual vs physical memory
A Linux VM or board to build/insmod modules
GCC and kernel headers installed
What Is vmalloc() in the Linux Kernel
vmalloc() allocates memory that is virtually contiguous but may be scattered across many non-contiguous physical pages. The kernel allocates individual pages from the page allocator, then builds new page table entries so that, from the CPU’s point of view, the whole block looks like one continuous range of addresses. This is exactly the same trick your operating system uses for user-space malloc() — it is just applied inside kernel space this time.
Because a fresh set of page table entries has to be created (and sometimes the kernel has to remap pages into a dedicated virtual range), vmalloc() is noticeably slower than kmalloc(). It is also not safe to use for DMA buffers, since DMA hardware needs a single contiguous physical address, and a vmalloc() region generally does not have one.
kmalloc()
Virtual address range maps 1:1 to physical RAM (shifted by PAGE_OFFSET). Physically contiguous. Fast. Good for DMA.
vmalloc()
Virtual address range is a separate reserved zone. Physical pages can be scattered anywhere. Slower. Not safe for DMA.
kmalloc() vs vmalloc(): Quick Comparison
| Property | kmalloc() | vmalloc() |
|---|---|---|
| Physical contiguity | Yes | No |
| Virtual contiguity | Yes | Yes |
| Speed | Fast | Slower (page table setup) |
| Max size | Bounded by KMALLOC_MAX_SIZE | Bounded by vmalloc address space |
| Safe for DMA | Yes | No |
| Typical use case | Small structs, buffers | Large software-only buffers (e.g. network/log buffers) |
vmalloc Function Prototypes
All three functions live in <linux/vmalloc.h> and are still the standard interface on current mainline kernels:
#include <linux/vmalloc.h>
void *vmalloc(unsigned long size);
void *vzalloc(unsigned long size); /* vmalloc + zero-fill */
void vfree(void *addr); /* frees a vmalloc region */
vmalloc() returns a pointer to the first byte of the block on success, or NULL on failure. vzalloc() does the same thing but also zeroes the memory before handing it back, which saves you an explicit memset(). Every block obtained with either function must eventually be released with vfree() — never with kfree().
Original Driver Example: ep_vmalloc_demo
The following original example allocates a 2 MB software buffer with vzalloc(), writes a pattern into it, and frees it on module removal. A buffer this size would very likely fail with kmalloc(), but is routine for vmalloc().
#include <linux/init.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#define EP_VBUF_SIZE (2 * 1024 * 1024) /* 2 MB */
static void *ep_vbuf;
static int __init ep_vmalloc_demo_init(void)
{
ep_vbuf = vzalloc(EP_VBUF_SIZE);
if (!ep_vbuf) {
pr_err("ep_vmalloc_demo: vzalloc failed\n");
return -ENOMEM;
}
memset(ep_vbuf, 0xAA, 64); /* touch first 64 bytes */
pr_info("ep_vmalloc_demo: allocated %d bytes at %px\n",
EP_VBUF_SIZE, ep_vbuf);
return 0;
}
static void __exit ep_vmalloc_demo_exit(void)
{
vfree(ep_vbuf);
pr_info("ep_vmalloc_demo: buffer freed\n");
}
module_init(ep_vmalloc_demo_init);
module_exit(ep_vmalloc_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala vmalloc demo");
Build and Run
$ make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
$ sudo insmod ep_vmalloc_demo.ko
$ dmesg | tail -3
$ sudo rmmod ep_vmalloc_demo
$ dmesg | tail -3
Expected Output
[ 1234.567] ep_vmalloc_demo: allocated 2097152 bytes at 000000006e2b1a90
[ 1240.112] ep_vmalloc_demo: buffer freed
Note: on current kernels, %px prints the raw pointer only when you are root and have permission; on production systems the kernel hashes pointers printed with plain %p for security. Use %px for debugging only.
Inspecting Allocations With /proc/vmallocinfo
Every active vmalloc() region in the system is listed in /proc/vmallocinfo, along with its size and the function that allocated it. While ep_vmalloc_demo is loaded, run:
$ sudo cat /proc/vmallocinfo | grep ep_vmalloc_demo
0xffffc90000abc000-0xffffc90000cbd000 2097152 ep_vmalloc_demo_init+0x1a/0x30 [ep_vmalloc_demo] pages=512 vmalloc
This confirms the 2 MB region (512 pages of 4 KB each) and shows which caller created it, which is extremely useful when debugging vmalloc leaks in real drivers.
Why Physical Memory Allocation Is Lazy
Here is something that surprises a lot of students: when you call vmalloc(), mmap(), or even plain brk(), the kernel does not necessarily hand you real, physical RAM right away. In most cases it only updates the page table to reserve a virtual address range. The actual physical page is attached later, the first time you really touch that memory. This strategy is called lazy allocation, and it exists so the kernel never wastes physical RAM on pages that a process declares but never actually uses.
The mechanism that makes lazy allocation work is the page fault. Whenever the CPU tries to access a virtual address whose page table entry does not yet have the right permission bits (or does not point to a physical page at all), the memory management unit raises a page fault interrupt, and control passes to the kernel’s page fault handler.
The Three Outcomes of a Page Fault
A page fault can be triggered by a read, a write, or an execute attempt. Once the CPU raises it, the page fault handler resolves it in one of three ways:
Soft fault
The page already exists somewhere in memory (e.g. another process’s working set). The handler simply attaches it and adjusts the page table entry. Fast, no I/O needed.
Hard fault
The page is not resident anywhere (e.g. it must be read from a memory-mapped file or swap). The handler performs I/O, possibly suspending the process while data is fetched.
Unresolved fault
The access was simply illegal (out-of-bounds, wrong permissions). The kernel sends SIGSEGV to the offending process, which is killed unless it has installed its own handler.
| Fault type | Cause | Handler action | Cost |
|---|---|---|---|
| Soft fault | Page resident elsewhere in memory | Attach existing page, fix up PTE | Low |
| Hard fault | Page must be fetched from disk/swap | Perform I/O, may reschedule process | High |
| Unresolved | Illegal access / permission violation | Deliver SIGSEGV | Process may be killed |
Important caution for driver writers: a page fault raised while the CPU is already inside an interrupt context leads to a double fault, and the kernel normally calls panic() in response. This is exactly why memory used inside interrupt handlers must be pre-allocated (typically with GFP_ATOMIC) rather than touched for the first time inside the interrupt itself — there must be no page fault possible at that point. If a second fault happens while the double fault is being handled, the CPU triggers a triple fault and the whole system reboots. This behavior is architecture-dependent, but the general rule — never let interrupt context sleep or fault — holds everywhere.
Real-World Use Cases
- Large software buffers with no DMA requirement, such as ring buffers for logging subsystems.
- Kernel module loading itself uses vmalloc-style mappings for code and data sections.
mmap()-backed device drivers rely on the same lazy-allocation and page-fault machinery to avoid pinning memory that user space has not touched yet.- File-backed memory mappings depend on hard faults to pull pages in from disk on demand.
Common Mistakes
| Mistake | Why It’s a Problem | Fix |
|---|---|---|
| Using vmalloc() memory for DMA | Not physically contiguous | Use dma_alloc_coherent() or kmalloc() |
| Calling vfree() from interrupt context on older kernels | May sleep internally in some paths | Free from process context, or schedule work |
| Forgetting vfree() on every error path | Memory leak | Use a single cleanup label / goto pattern |
| Touching large memory inside an IRQ handler | Risk of double fault / panic | Pre-allocate before entering interrupt context |
Best Practices
- Prefer
kmalloc()/kzalloc()for anything under a few KB; reach forvmalloc()only for large, software-only buffers. - Always check the return value of
vmalloc()/vzalloc()forNULL. - Match every
vmalloc()/vzalloc()with exactly onevfree(). - When size is not known in advance and could be large or small, consider
kvmalloc()/kvfree(), which automatically picks the right allocator.
Performance Considerations
vmalloc() pays a one-time cost building page tables at allocation time, so it should not be called in hot paths that run frequently. Allocate once at driver init/probe time and reuse the buffer for the driver’s lifetime whenever possible.
Security Considerations
Kernel pointers, including vmalloc addresses, are hashed when printed with %p to prevent leaking layout information to user space. Always zero sensitive buffers before vfree() if they held confidential data, since the underlying pages can be reused elsewhere.
Summary / Key Takeaways
vmalloc()gives virtually contiguous, physically scattered memory;kmalloc()gives both.- Memory allocation in Linux is lazy — physical pages are attached on first access, not at allocation time.
- Page faults are resolved as soft faults, hard faults, or delivered as SIGSEGV.
- Faulting inside interrupt context can panic the kernel — pre-allocate before you need it.
Conclusion
Understanding vmalloc() alongside the page fault mechanism gives you the full picture of how the Linux kernel manages memory: reserve a virtual range first, attach physical pages only when actually touched, and rely on the MMU and page fault handler to keep everything consistent. This foundation will make the next lecture on Copy-on-Write much easier to follow, since CoW is really just a clever, deliberate use of the same page fault trap.
Frequently Asked Questions
Is vmalloc() memory contiguous in physical RAM?
No. It is contiguous only in virtual address space. The underlying physical pages can be scattered anywhere in RAM.
Can I use vmalloc() memory for DMA transfers?
No, not directly. DMA hardware generally needs a single contiguous physical address range, so use kmalloc() or dma_alloc_coherent() instead.
What is the difference between vmalloc() and vzalloc()?
vzalloc() behaves exactly like vmalloc() but also zero-fills the returned memory before handing it back.
Why is vmalloc() slower than kmalloc()?
vmalloc() must build new page table entries and sometimes remap pages into a dedicated virtual range, while kmalloc() reuses an existing 1:1 mapping.
What triggers a page fault in the Linux kernel?
Any access (read, write, or execute) to a virtual address whose page table entry lacks the correct permission bits or has no physical page attached.
What is the difference between a hard fault and a soft fault?
A soft fault is resolved instantly because the page already exists in memory somewhere. A hard fault requires the kernel to perform I/O, such as reading from disk or swap.
What happens if a page fault occurs inside interrupt context?
It causes a double fault, which usually makes the kernel call panic(). This is why memory touched inside interrupt handlers must be pre-allocated.
How can I see how much vmalloc memory a driver has allocated?
Read /proc/vmallocinfo, which lists every active vmalloc region along with its size and allocating function.
Continue the Free Linux Kernel Development Course
Next up: how the kernel avoids copying memory using Copy-on-Write.
