vmalloc API
Intermediate
Kernel 6.x
Every free Linux kernel development course eventually reaches a question every beginner asks: “kmalloc() gave me an error for a large allocation, what do I use instead?” The answer, most of the time, is vmalloc(). This tutorial explains exactly what vmalloc() does, how it is different from kmalloc(), and the situations where a real device driver should or should not use it.
What You Will Learn
Prerequisites: a basic understanding of kmalloc() and the page allocator, and familiarity with the concept of virtual addresses. If these are new to you, start with our free Linux device drivers course memory management module first.
Physical Contiguity vs Virtual Contiguity
To understand vmalloc(), you first need to understand the difference between two kinds of “contiguous” memory:
- Physically contiguous memory occupies one unbroken run of physical RAM addresses. kmalloc() always returns this kind of memory.
- Virtually contiguous memory looks like one unbroken block to your code (you can index it as one array), but underneath, the kernel has stitched together several unrelated physical pages using the page tables. vmalloc() returns this kind of memory.
Physically, pages A-D can be anywhere in RAM. The page table maps them into one continuous virtual range so your code can treat them as a single buffer.
This is exactly why vmalloc() can succeed for large allocations even when the physical RAM is heavily fragmented: it does not need to find one giant free physical run, only enough individual free pages, however scattered they are.
The vmalloc() API
The core function signature is simple and does not need a GFP flag argument for basic use, because vmalloc() always allocates in a context where sleeping is allowed:
#include <linux/vmalloc.h>
void *buf;
size_t size = 4 * 1024 * 1024; /* 4 MB, unlikely to succeed via kmalloc */
buf = vmalloc(size);
if (!buf)
return -ENOMEM;
memset(buf, 0, size);
/* ... use buf ... */
vfree(buf);
If you want the memory pre-zeroed in one step, use vzalloc() instead of calling vmalloc() followed by memset():
buf = vzalloc(size); /* same as vmalloc() + memset(0) */
Why vmalloc() Is Slower Than kmalloc()
vmalloc() has three costs that kmalloc() does not have:
| Cost | Explanation |
|---|---|
| Page table setup | The kernel must build new page table entries to map the scattered pages into one virtual range, which kmalloc() never needs to do. |
| TLB pressure | Because the underlying pages are not physically contiguous, the CPU’s translation lookaside buffer cannot cover the whole range with one large-page entry, which slightly hurts access performance. |
| Allocation overhead | Locating and mapping many individual pages simply takes more CPU cycles than the fast paths used by kmalloc() or a slab cache. |
A Critical Limitation: DMA and Hardware Access
If you genuinely need to inspect the physical page behind a specific offset inside a vmalloc() buffer (for example, to build a scatter-gather list manually), the kernel provides a helper for that:
struct page *pg = vmalloc_to_page(buf + offset);
unsigned long phys_addr = page_to_phys(pg);
vmalloc() vs kmalloc(): A Quick Decision Table
| Situation | Recommended API |
|---|---|
| Small allocation (a few KB or less) | kmalloc() |
| Large allocation (hundreds of KB to several MB) | vmalloc() |
| Memory that a device will access via DMA | kmalloc() or dma_alloc_coherent(), never vmalloc() |
| Called from an interrupt handler | kmalloc() with GFP_ATOMIC; vmalloc() cannot be used here |
| Same fixed-size object allocated repeatedly | A custom slab cache |
Real-World Use Cases
vmalloc() is commonly used for module loading itself (the kernel maps the executable code and data sections of a loadable module using vmalloc space), for large buffers inside filesystem and networking subsystems, and for kernel-side allocations that mirror large user-space mappings. If you have ever wondered how the kernel loads a multi-megabyte .ko file into a non-contiguous chunk of RAM without needing one giant free physical block, vmalloc() is the mechanism behind it.
Common Mistakes to Avoid
- Passing vmalloc() memory directly to a DMA-capable device — this is a serious and hard-to-debug data corruption bug.
- Calling vmalloc() from interrupt context or with a spinlock held — vmalloc() can sleep and is only safe in process context.
- Using vmalloc() for small, frequent allocations, where the mapping overhead outweighs any benefit.
- Forgetting that vfree() must be called from process context as well, for the same reason as vmalloc().
Best Practices
- Reach for vmalloc() only after confirming kmalloc() cannot satisfy the size you need.
- Prefer vzalloc() over vmalloc() plus a manual memset() for cleaner, less error-prone code.
- Document clearly in your driver why a particular buffer uses vmalloc(), since it is the less common choice and future maintainers will want to know.
Frequently Asked Questions
No. vmalloc() can sleep while it sets up page table mappings, so it must only be called from process context, never from interrupt context or with a spinlock held.
Because vmalloc() memory is not physically contiguous, the device controller (which only understands physical addresses) ends up reading or writing pages that are not actually next to each other in RAM. Use kmalloc() or dma_alloc_coherent() for any buffer a device will access directly.
Yes, vmalloc() draws from a dedicated, limited region of the kernel’s virtual address space, so extremely large allocations can still fail if that region is exhausted, even though physical RAM is available.
vzalloc() is functionally identical to calling vmalloc() and then zeroing the entire buffer yourself, just in a single, more convenient call.
No. kmalloc() should always be the first choice for anything reasonably small, since it is faster and physically contiguous. Move to vmalloc() only when you specifically need a large buffer and do not need DMA access to it.
This lesson is part of EmbeddedPathashala’s free Linux kernel development course and free Linux device drivers course covering memory management from first principles.
