Linux Kernel vmalloc() API Tutorial
If you are following our free Linux kernel development course, this lecture is your complete Linux kernel vmalloc() API tutorial. You will learn what the kernel’s vmalloc region actually is, why a driver author would reach for vmalloc() instead of kmalloc(), and how to safely allocate and release virtually contiguous memory from kernel code. This tutorial is part of our broader free Linux device drivers course and free embedded systems course, and every example here is written and tested against a modern, currently maintained Linux kernel — not an outdated textbook snapshot.
What You Will Learn
- What the kernel vmalloc address space is and where it sits inside the kernel segment
- The difference between virtually contiguous and physically contiguous memory
- How to use
vmalloc(),vzalloc(),vfree(), and the modernkvmalloc()hybrid API - When to pick vmalloc over the slab allocator (kmalloc family)
- Huge-page backed vmalloc allocations on recent kernels
- Common bugs, debugging tips, and production best practices
Linux kernel memory management
kernel vmalloc region
free Linux kernel development course
free Linux device drivers course
free embedded systems course
kmalloc vs vmalloc
Prerequisites
- Basic C programming and pointer arithmetic
- A working Linux kernel module build setup (kernel headers + a recent distro kernel, 6.x or 7.x)
- Familiarity with our earlier lecture on the page allocator and the slab/SLUB allocator
- Comfort compiling and loading a kernel module with
insmod/rmmod
What Is the Kernel vmalloc Region?
Every kernel virtual address space is split into fixed zones. One of those zones is reserved purely for on-demand virtual allocations, separate from the direct-mapped physical memory area that the page and slab allocators use. This reserved zone is what we call the vmalloc region, and it stretches across a dedicated range of kernel virtual addresses.
When the region is created at boot, none of its pages point to real physical memory yet. It is pure address space. Only when you call the vmalloc() API does the kernel walk this region, find a free virtual range large enough for your request, allocate individual physical pages (through the underlying page allocator), and then wire up page-table entries to map those physical pages into that virtual range.
The key takeaway: memory returned by vmalloc() is guaranteed to be contiguous in virtual address terms only. The physical pages backing it can be scattered anywhere across RAM. For a driver author, that distinction matters a lot — it is exactly why vmalloc() cannot be used for DMA buffers, which need physically contiguous memory.
Why Not Just Use kmalloc()?
The slab-based kmalloc() family is fast and gives you physically contiguous memory, but it has a practical ceiling. On most architectures a single kmalloc() request tops out in the low single-digit megabytes because it depends on the page allocator’s buddy system, which can only satisfy requests up to a limited allocation order. If your driver needs a large virtually contiguous buffer — think large ring buffers, firmware staging areas, or big lookup tables — kmalloc() will simply fail once you cross that ceiling.
This is precisely the gap vmalloc() fills. It trades a small performance cost (page-table setup and less TLB friendliness) for the ability to hand you a much larger virtually contiguous buffer, assembled from individually allocated physical pages.
kmalloc() vs vmalloc() at a Glance
| Aspect | kmalloc() / slab | vmalloc() |
|---|---|---|
| Physical contiguity | Yes | Not guaranteed |
| Virtual contiguity | Yes | Yes |
| Practical size limit | Small (buddy-order limited) | Much larger, limited by free virtual space |
| Speed | Faster, no page-table walk | Slower, builds page tables per call |
| Safe for DMA? | Yes (with proper flags) | No |
| Can sleep? | Depends on GFP flags | Yes — process context only |
The vmalloc() Family of APIs
All of these declarations live in <linux/vmalloc.h>:
void *vmalloc(unsigned long size);
void *vzalloc(unsigned long size);
void vfree(const void *addr);
vmalloc() reserves a range in the vmalloc region, backs it with freshly allocated physical pages, and returns a kernel virtual address on success or NULL on failure. The content of that memory is not guaranteed to be zeroed, so if your logic depends on a clean buffer, use vzalloc() instead, which zeroes the memory for you at a small extra cost.
Two behavioral rules are worth memorizing:
- Process context only. vmalloc() may sleep while it sets up page tables, so it must never be called from interrupt context or with a spinlock held.
- Page-aligned start. The returned address always begins on a page boundary, and the kernel may internally round your request up to the next full page, so the usable memory can be slightly larger than what you asked for.
When you are done, release the buffer with vfree(), passing the same pointer vmalloc()/vzalloc() returned. Calling vfree(NULL) is safe and simply does nothing.
A Simple, Original Driver Example
Below is a minimal character-driver-style module that allocates a scratch buffer with vzalloc(), writes a small pattern into it, and frees it cleanly on module exit. This example is written independently for this course, so treat it as a learning template rather than production code.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#define EP_BUF_SIZE (2 * 1024 * 1024) /* 2 MB scratch buffer */
static void *ep_scratch_buf;
static int __init ep_vmalloc_demo_init(void)
{
ep_scratch_buf = vzalloc(EP_BUF_SIZE);
if (!ep_scratch_buf) {
pr_err("ep_vmalloc_demo: allocation of %d bytes failed\n",
EP_BUF_SIZE);
return -ENOMEM;
}
memset(ep_scratch_buf, 0xAB, 256); /* touch first 256 bytes */
pr_info("ep_vmalloc_demo: allocated %d bytes at %p\n",
EP_BUF_SIZE, ep_scratch_buf);
return 0;
}
static void __exit ep_vmalloc_demo_exit(void)
{
vfree(ep_scratch_buf);
pr_info("ep_vmalloc_demo: buffer released\n");
}
module_init(ep_vmalloc_demo_init);
module_exit(ep_vmalloc_demo_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("EmbeddedPathashala vmalloc() teaching example");
Build and load it like any other out-of-tree module:
make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
sudo insmod ep_vmalloc_demo.ko
dmesg | tail
sudo rmmod ep_vmalloc_demo
Modern Additions: Huge Pages and the kvmalloc() Hybrid
Two developments are worth knowing on current kernels:
- Huge-page backed vmalloc. Recent kernels can back sufficiently large vmalloc() allocations with huge pages instead of thousands of individual base pages, cutting down page-table overhead and improving TLB behavior for very large buffers. This kicks in automatically for eligible large allocations on supporting architectures.
- kvmalloc() / kvfree(). Many subsystems don’t know in advance whether a request will be small enough for the slab allocator or large enough to need vmalloc().
kvmalloc()solves this by first trying kmalloc() and transparently falling back to vmalloc() only if the slab allocation fails. Always release memory from this API withkvfree(), which correctly detects which allocator actually served the request.
void *kvmalloc(size_t size, gfp_t flags);
void kvfree(const void *addr);
For most driver code written today, kvmalloc() is the pragmatic default when the required size is variable or potentially large, while plain vmalloc()/vzalloc() remain the right tool when you deliberately want a large, virtually contiguous allocation every time.
Real-World Use Cases
| Use Case | Why vmalloc() Fits |
|---|---|
| Loading kernel module code/data segments | Module size varies and can exceed slab limits |
| Kernel-mode thread stacks (CONFIG_VMAP_STACK) | Guard pages around each stack catch overflows |
| Large in-memory lookup tables or ring buffers | Needs a big virtually contiguous block, not DMA-capable |
| ioremap()-style MMIO mapping internals | Uses the same vmalloc address space machinery |
Common Mistakes and Troubleshooting
- Calling vmalloc() from an interrupt handler. It can sleep, so this will trigger kernel warnings or crashes. Move the allocation to process context, or pre-allocate before entering the interrupt path.
- Assuming vmalloc() memory is physically contiguous. Never hand a vmalloc() pointer to a DMA API expecting a physical address.
- Leaking buffers on error paths. Every successful vmalloc()/vzalloc() needs a matching vfree() on both the normal exit path and every error path.
- Mixing allocator families. Never free a kmalloc() pointer with vfree(), or a vmalloc() pointer with kfree(). If you used kvmalloc(), always release with kvfree().
- Forgetting the actual size may be rounded up. Don’t assume the buffer is exactly the byte count you requested; treat it as at least that size.
Best Practices
- Prefer kmalloc()/kzalloc() for small, fixed-size, performance-sensitive allocations.
- Reach for vmalloc()/vzalloc() only when you genuinely need a large virtually contiguous buffer that the slab allocator cannot satisfy.
- Use kvmalloc()/kvfree() when the size is unpredictable at compile time.
- Always check the return value for NULL before touching the buffer.
- Zero sensitive buffers with vzalloc() rather than manually memset()-ing after the fact.
Performance and Security Considerations
Performance: vmalloc() allocations are slower to set up than kmalloc() because the kernel must build page-table entries for the new mapping, and access patterns can suffer more TLB misses since the backing pages are physically scattered. Reserve vmalloc() for allocations where its size flexibility genuinely outweighs this cost.
Security: Because vmalloc() memory content isn’t guaranteed to start zeroed, always use vzalloc() (or explicitly zero the buffer) before exposing it to user space or other subsystems, to avoid leaking stale kernel memory contents. Kernels built with KASAN’s vmalloc support can also help catch out-of-bounds accesses on these allocations during development and testing.
Summary / Key Takeaways
- The vmalloc region is a dedicated slice of kernel virtual address space for on-demand allocations.
- vmalloc() memory is virtually contiguous but not necessarily physically contiguous.
- Use vmalloc()/vzalloc() for large buffers the slab allocator can’t provide; use vfree() to release them.
- kvmalloc()/kvfree() is the modern, flexible choice when allocation size varies.
- Never call vmalloc() outside process context, and never mix allocator families when freeing.
Conclusion
The vmalloc() API is one of the three core memory allocation tools every Linux kernel and driver developer should understand, alongside the page allocator and the slab/SLUB allocator. Once you know where the vmalloc region sits in the kernel’s address space, and when virtual-only contiguity is enough for your use case, choosing between kmalloc(), vmalloc(), and kvmalloc() becomes a straightforward engineering decision rather than guesswork. In the next lecture of this free Linux kernel development course, we continue building on this memory management foundation.
Frequently Asked Questions
No. It is guaranteed to be contiguous only in kernel virtual address terms. The underlying physical pages are typically scattered across RAM.
No. vmalloc() can sleep while building page-table mappings, so it must only be called from process context.
vzalloc() behaves exactly like vmalloc() but additionally zeroes the returned memory before handing it back to the caller.
DMA hardware typically needs physically contiguous memory. Since vmalloc() only guarantees virtual contiguity, it is unsuitable for DMA buffers.
kvmalloc() first attempts a kmalloc()-style slab allocation and falls back to vmalloc() only if that fails. Use it when the required size is variable or unknown at compile time, and always free with kvfree().
Not guaranteed. Use vzalloc() if you need zeroed memory, especially before exposing a buffer to user space.
Call vfree() with the exact pointer returned by vmalloc()/vzalloc(). Passing NULL to vfree() is safe.
Yes, generally. vmalloc() has to set up page-table entries for a fresh virtual mapping, and access patterns can incur more TLB misses than the physically contiguous memory kmalloc() provides.
Continue Your Free Linux Kernel Development Journey
This lecture is part of EmbeddedPathashala’s free Linux kernel development course, free Linux device drivers course, and free embedded systems course.
