Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 24

Choosing the Right Allocator: kmalloc, vmalloc, and Direct Page Allocation

Recommended resources

Introduction

Welcome back. In our previous lesson, we explored the SLUB allocator, the kernel's high-performance mechanism for managing small, frequently used objects. We saw that it sits on top of the buddy allocator and serves as the backend for the kmalloc() API, providing fast, physically contiguous memory chunks and solving the problem of internal fragmentation for small allocations.

This lesson builds directly on that foundation. We now have two allocators in our toolkit: the buddy system for pages and SLUB for objects. However, the kernel provides a third major allocation mechanism. Your goal for this lesson is to compare kmalloc, vmalloc, and direct page allocation, and select the appropriate allocator for a given scenario.

Mastering this is crucial. Choosing the right memory allocator is not just about performance; it's about correctness, stability, and scalability. In an interview, being able to articulate the trade-offs between these functions demonstrates a deep and practical understanding of the kernel's memory management subsystem.


1. A Tale of Three Allocators

At a high level, the kernel offers three primary ways to obtain memory, each with a different promise regarding memory contiguity.

  1. kmalloc(): This is your general-purpose workhorse. As we learned, it's typically backed by the SLUB allocator. It returns a pointer to a memory block that is both physically and virtually contiguous.
  2. Direct Page Allocation (__get_free_pages(), alloc_pages()): This is the interface to the buddy system allocator. It allocates one or more full pages and also returns a block that is both physically and virtually contiguous.
  3. vmalloc(): This allocator returns a block of memory that is virtually contiguous but not physically contiguous. It works by allocating potentially scattered physical pages and mapping them into a contiguous block in the kernel's virtual address space.

The choice between them boils down to your specific requirements for size, performance, and, most importantly, physical contiguity.

This diagram provides an excellent visual summary of the core differences between kmalloc and vmalloc, the two most distinct allocators.

Linux Kernel Memory Allocation: kmalloc() vs vmalloc() - Internal Architecture
A comparison of the internal workings of kmalloc() and vmalloc(). Notice how kmalloc() relies on the buddy system to provide physically contiguous pages to the slab allocator, resulting in a fast allocation. In contrast, vmalloc() pieces together non-contiguous physical pages by manipulating page tables, which is a slower process.

Let's dissect the scenarios where you would choose one over the others.


2. Physically Contiguous Memory: kmalloc vs. Direct Page Allocation

Both kmalloc and the direct page allocation functions provide physically contiguous memory, which is essential for any hardware interaction involving Direct Memory Access (DMA). A peripheral device using DMA needs a single physical starting address and a length; it doesn't understand the CPU's virtual memory mappings.

So, when would you choose one over the other?

  • Use kmalloc() for object-sized allocations. For most tasks, like allocating space for a device-specific structure or a small network packet buffer (typically less than 128KB), kmalloc() is the correct choice. The SLUB allocator is highly optimized for this, minimizing wasted space (internal fragmentation) and providing excellent performance.

  • Use Direct Page Allocation for page-sized allocations. When you need a memory block that is one or more full pages (e.g., 4KB, 8KB, 16KB), using alloc_pages() or __get_free_pages() is more direct and efficient. There's no slab overhead. The most common use case is allocating buffers that will be mapped into a user-space process via the mmap system call, as mmap operates on full pages.

To explore these APIs in more detail, let's turn to a classic resource.

Allocating Memory (Chapter 8, LDD3)

The book "Linux Device Drivers, 3rd Edition" (LDD3) provides a clear and authoritative explanation of the kernel's memory allocation interfaces. We'll read the sections covering kmalloc and page-oriented allocation.

Please read two sections from this chapter: Start at the beginning of the chapter and read through "The Real Story of kmalloc". Pay close attention to the GFP_ flags (GFP_KERNEL, GFP_ATOMIC), which are critical for determining if the allocator can sleep. This is a very common source of bugs. Then, skip to the section "get_free_page and Friends". This covers the direct page allocation API. Focus on the order parameter, which specifies the number of pages to allocate as a power of two.

After this reading, you should understand that both kmalloc and alloc_pages are competing for the same resource: contiguous physical pages from the buddy system. kmalloc just adds the SLUB layer on top for efficiency with smaller sizes.


3. The Big Trade-off: kmalloc vs. vmalloc

The most fundamental decision is whether you truly need physical contiguity. This is where vmalloc enters the picture.

Let's watch a video that explains this trade-off using a fantastic real-world example.

vmalloc() and Why ZFS Doesn't Work On 32-bit Linux (yet)

This video from Mark Furneaux gives an exceptionally clear conceptual breakdown of kmalloc versus vmalloc and explains why the distinction is so critical.

Watch from 04:27 to 10:22. The first part explains kmalloc (physically contiguous, good for DMA). The second part introduces vmalloc (virtually contiguous, slower, bad for DMA) and discusses the performance implications (TLB pressure).

Let's summarize the key points from the video and expand on them:

Aspect kmalloc vmalloc
Physical Contiguity Yes. The allocated pages are physically adjacent in RAM. No. The allocated pages can be scattered anywhere in physical RAM.
Virtual Contiguity Yes. The memory appears as a single block to the kernel's code. Yes. The memory also appears as a single block to the kernel's code.
Use for DMA Yes. This is the primary reason for its existence. No. Hardware cannot use it because the physical pages are not sequential.
Allocation Success Brittle for large sizes. Finding a large (e.g., 1MB) block of physically contiguous free memory on a running system is very difficult due to fragmentation. Allocations may fail. Robust for large sizes. As long as there are enough free pages anywhere, vmalloc can succeed by mapping them together virtually.
Performance High. Address translation is often a simple offset calculation, which is very fast. Lower. It requires building custom page table entries for the mapping. Accessing this memory can lead to more Translation Lookaside Buffer (TLB) misses, slowing down the system.

A Deeper Look into vmalloc

For someone with your background aiming for deep expertise, it's valuable to see exactly why vmalloc is slower. It's not magic; it's work. The kernel has to manually build the page table mappings to create the illusion of contiguity.

vmalloc - call path
This flowchart details the internal steps of `vmalloc`. Notice that it must first allocate a `vm_struct` (often using `kmalloc`!) to track the virtual memory area, then loop to allocate each physical page (`alloc_page`) and populate the page tables to map it. This is significantly more work than a `kmalloc` call.

This is why the general rule is: Always use kmalloc unless you have a specific reason not to. The most common reason is needing a very large block of memory (>128KB) that is for software use only and not for DMA. A classic example is for loading a kernel module's code and data sections into memory.


4. A Decision Framework for Kernel Allocation

Let's synthesize this into a practical decision framework. When you need to allocate memory in a driver or kernel subsystem, ask yourself these questions in order:

  1. Is this memory for a hardware device (DMA)?

    • Yes: You must use an allocator that provides physically contiguous memory. For most cases, this is kmalloc. (Note: For advanced DMA, you might use the streaming DMA API, but that also uses physically contiguous pages).
    • No: Proceed to question 2.
  2. How large is the allocation?

    • Small to medium-sized (e.g., from a few bytes up to a few pages, typically < 128KB): kmalloc is the best choice. It's fast, efficient, and the allocation is likely to succeed.
    • Large (e.g., hundreds of KB or several MB): Proceed to question 3.
  3. Do I have a compelling reason to use vmalloc?

    • The primary reason is that you need a very large contiguous block of virtual memory for software purposes, and you are concerned that a kmalloc of that size will fail due to physical memory fragmentation.
    • If yes, vmalloc is the right tool.
    • If you just need a collection of pages and they don't need to be virtually contiguous, you might be better off allocating them individually with alloc_page and managing them with your own data structure (e.g., a linked list).

This can be summarized in a table:

Scenario Recommended Allocator Why?
Allocating a struct my_device (256 bytes) for a driver. kmalloc Small, object-like allocation. Fast and efficient via SLUB.
Allocating a 4KB buffer for a DMA transfer. kmalloc Requires physical contiguity. Size is reasonable for kmalloc.
Allocating an 8KB buffer to be mapped into user-space via mmap. alloc_pages mmap works with full pages. Direct page allocation is most efficient for this.
Allocating a 2MB buffer for a software-based video decompression codec. vmalloc Large allocation not for DMA. kmalloc(2MB) would likely fail. vmalloc is designed for this.

5. Interview Scenario

Your ability to apply this framework is what top-tier companies look for. Consider this problem:

Question: "You are reviewing code for a new network driver. To handle jumbo frames, the original developer has a function that calls kmalloc(9000) to get a buffer for each incoming packet. During stress testing, the system occasionally panics due to an allocation failure, especially after running for a long time. What is the likely problem, and how would you advise the developer to fix it?"

Click to see the analysis

A strong answer would methodically apply our decision framework:

  1. Identify the Core Need: A network driver buffer is for DMA. The network card needs to write packet data into physically contiguous memory. This immediately rules out vmalloc.

  2. Diagnose the Problem: The allocation is kmalloc(9000). This falls into a gray area—it's larger than a single page (4KB) but not enormous. kmalloc will round this up to the next available slab size (likely 16KB) and request 4 contiguous physical pages from the buddy allocator. While this often works, on a system that has been running for a long time, the physical memory becomes fragmented. Finding 4 free pages "in a row" becomes increasingly difficult, leading to the occasional kmalloc failure and kernel panic. The developer has created a brittle design.

  3. Propose Solutions:

    • Solution A (The "Smarter" kmalloc): The problem is not kmalloc itself, but the request for a single large block. Modern network hardware supports Scatter-Gather DMA. Instead of one large buffer, you can provide the hardware with a list of smaller, physically separate buffers. The fix would be to allocate packet buffers in smaller, page-sized chunks (e.g., using alloc_page or kmalloc(PAGE_SIZE)) and use the networking stack's APIs (skb_frag_list) to chain them together. This is much more robust because allocating single pages is almost always successful.

    • Solution B (Discussing vmalloc's Unsuitability): You should explicitly state why vmalloc is the wrong fix. A junior developer might see "large allocation fails" and think "use vmalloc". You would explain that vmalloc provides virtually contiguous memory that is physically scattered, which is useless for the DMA engine of the network card. This demonstrates you understand the root cause related to hardware constraints.

  4. Conclusion: The best practice is to design drivers to work with page-sized, physically contiguous chunks whenever possible, as these are the fundamental units the memory manager provides reliably. Avoid requesting large, single kmalloc blocks in performance-critical or long-running paths.


Conclusion

You have now completed a comprehensive tour of the kernel's physical and kernel-virtual memory allocators. You have a framework to decide which one to use in any given situation.

Key Takeaways:

  • The choice between kmalloc, alloc_pages, and vmalloc hinges on requirements for physical contiguity, allocation size, and performance.
  • kmalloc is the default for small-to-medium, physically contiguous allocations needed for DMA or general objects.
  • alloc_pages is the direct, efficient way to get one or more full, physically contiguous pages, often for mmap.
  • vmalloc is the solution for large, software-only buffers where physical contiguity is not needed and a large kmalloc would be unreliable.

This deep understanding of how the kernel manages its own memory is a prerequisite for the next topic. So far, we've dealt with memory that is allocated and mapped immediately. In our next lesson, we will explore demand paging and page fault handling, the mechanism that allows the kernel to defer allocating physical memory for a process until the very moment it's accessed.