Linux Kernel Memory Allocators Explained-free Linux device drivers tutorial

Linux Kernel Memory Allocators Explained
Free Linux Kernel Development Course • Kernel Memory Management • Lecture 11.6
Kernel 6.x Verified
Original Demo Driver
Beginner Friendly

free embedded systems course
free linux kernel development course
free linux device drivers course
linux kernel memory allocators
free linux development course

Every driver you write in this free Linux kernel development course eventually needs memory, and the kernel gives you several different ways to get it. Understanding linux kernel memory allocators — kmalloc, vmalloc, the SLAB/SLUB allocator, and the underlying page allocator — is essential before you write real drivers, because picking the wrong allocator can hurt performance or even break your DMA transfers. In this lecture of our free embedded Linux course, you will see how these allocators stack on top of each other, when to use each one, and you will build an original kernel module that exercises kmalloc and vmalloc side by side.

What You Will Learn

  • How the page allocator, SLAB allocator, kmalloc, and vmalloc relate to each other
  • The difference between physically contiguous and virtually contiguous memory
  • When to choose kmalloc vs vmalloc vs the SLAB/SLUB allocator directly
  • Modern GFP flags used with these allocators on kernel 6.x
  • Writing an original driver that allocates memory with both kmalloc and vmalloc

Prerequisites

  • lddch11_1 through lddch11_5 (virtual memory, zones, VMAs, page tables, TLB)
  • Comfort building and loading a kernel module on a kernel 6.x system

The Kernel Memory Allocator Stack

Rather than one single allocator, Linux layers several allocators on top of each other, each solving a different problem. At the very bottom sits the page allocator, which hands out physical memory in fixed chunks — 4 KB pages on most systems, though 8 KB, 16 KB, or 64 KB pages are possible depending on architecture and configuration. On top of that sits the SLAB allocator (SLUB is the default implementation in modern kernels), which carves pages into small, reusable objects so the kernel is not forced to allocate a whole page for a tiny structure. kmalloc is built on top of SLAB/SLUB and is the allocator most drivers reach for by default. vmalloc, on the other hand, talks to the page allocator directly but maps the resulting pages into a separate virtually contiguous region, even when the underlying physical pages are scattered.

Linux Kernel Memory Allocator Stack
Kernel Module / Driver
kmalloc()
vmalloc()
SLAB / SLUB Allocator
Page Allocator (buddy system, 4K chunks and up)
Main Physical Memory

Modernization note: the classic SLAB allocator described in older textbooks has effectively been replaced by SLUB as the default kmalloc backend on current mainline kernels, with the older SLAB implementation removed from modern kernel source trees. The public API — kmalloc(), kfree(), kmem_cache_create() — stays the same either way, so your driver code does not need to change, but it is worth knowing SLUB is what is actually running under the hood today.

kmalloc vs vmalloc: The Core Difference

Aspect kmalloc() vmalloc()
Physical memory Physically contiguous Usually physically scattered
Virtual memory Virtually contiguous (maps directly) Virtually contiguous (mapped via page tables)
Typical use case DMA buffers, small structures, hot paths Large buffers where physical contiguity is not required
Speed Fast, no extra page table setup Slower, requires building new page table mappings
Maximum practical size Limited (a few MB at most, backend dependent) Can be much larger, limited mainly by vmalloc address space
Safe for DMA? Yes, when physically contiguous memory is required No, not safe for hardware DMA without extra mapping work

The rule of thumb used throughout this free Linux device drivers course: reach for kmalloc() first for anything small or DMA-related, and only reach for vmalloc() when you genuinely need a large buffer and do not care whether the underlying physical pages are contiguous.

Common GFP Flags You Will See

Flag Meaning
GFP_KERNEL Normal allocation, allowed to sleep; the default choice in process context
GFP_ATOMIC Allocation must not sleep; required in interrupt or atomic context
GFP_DMA Memory suitable for legacy DMA controllers restricted to low physical addresses
GFP_NOWAIT Similar to GFP_ATOMIC style behavior, will not sleep, may fail more easily

Original Demo: kmalloc and vmalloc Side by Side

The module below, ep_alloc_demo, allocates a small buffer with kmalloc() and a larger buffer with vmalloc(), then prints details about each so you can directly compare their behavior.

// ep_alloc_demo.c
// Original demo module for the free Linux kernel development course
// Compares kmalloc() and vmalloc() allocations side by side

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>

#define EP_KMALLOC_SIZE   256
#define EP_VMALLOC_SIZE   (2 * 1024 * 1024) /* 2 MB, large enough to favor vmalloc */

static void *ep_kbuf;
static void *ep_vbuf;

static int __init ep_alloc_demo_init(void)
{
    ep_kbuf = kmalloc(EP_KMALLOC_SIZE, GFP_KERNEL);
    if (!ep_kbuf) {
        pr_err("ep_alloc_demo: kmalloc failed\n");
        return -ENOMEM;
    }

    ep_vbuf = vmalloc(EP_VMALLOC_SIZE);
    if (!ep_vbuf) {
        pr_err("ep_alloc_demo: vmalloc failed\n");
        kfree(ep_kbuf);
        return -ENOMEM;
    }

    memset(ep_kbuf, 0xAB, EP_KMALLOC_SIZE);
    memset(ep_vbuf, 0xCD, EP_VMALLOC_SIZE);

    pr_info("ep_alloc_demo: loaded\n");
    pr_info("ep_alloc_demo: kmalloc buffer  virt=0x%px  size=%d bytes  phys=0x%llx\n",
            ep_kbuf, EP_KMALLOC_SIZE, (unsigned long long)virt_to_phys(ep_kbuf));
    pr_info("ep_alloc_demo: vmalloc buffer  virt=0x%px  size=%d bytes\n",
            ep_vbuf, EP_VMALLOC_SIZE);
    pr_info("ep_alloc_demo: note - vmalloc buffer has no single virt_to_phys() value,\n");
    pr_info("ep_alloc_demo: its physical pages are typically scattered\n");

    return 0;
}

static void __exit ep_alloc_demo_exit(void)
{
    kfree(ep_kbuf);
    vfree(ep_vbuf);
    pr_info("ep_alloc_demo: unloaded, both buffers freed\n");
}

module_init(ep_alloc_demo_init);
module_exit(ep_alloc_demo_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Original kmalloc vs vmalloc demo for free Linux kernel development course");

Build and Run

# Makefile
obj-m += ep_alloc_demo.o

all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_alloc_demo.ko
$ dmesg | tail -6

Expected Output

[ 2001.100200] ep_alloc_demo: loaded
[ 2001.100202] ep_alloc_demo: kmalloc buffer  virt=0xffff9c1a2b3c4d00  size=256 bytes  phys=0x1a2b3c400
[ 2001.100204] ep_alloc_demo: vmalloc buffer  virt=0xffffb1e2c3d4e000  size=2097152 bytes
[ 2001.100205] ep_alloc_demo: note - vmalloc buffer has no single virt_to_phys() value,
[ 2001.100206] ep_alloc_demo: its physical pages are typically scattered

$ sudo rmmod ep_alloc_demo
$ dmesg | tail -1
[ 2010.334455] ep_alloc_demo: unloaded, both buffers freed

Notice that virt_to_phys() gives a single, meaningful physical address for the kmalloc buffer, because it is physically contiguous. Calling the same function on a vmalloc pointer would only give you the physical address of the first page — the rest of the buffer’s physical pages are not guaranteed to be adjacent at all.

Real-World Use Cases

  • DMA ring buffers for network and storage drivers: kmalloc (or dma_alloc_coherent) for guaranteed physical contiguity
  • Loading a large kernel module or firmware image into memory: vmalloc, where physical contiguity does not matter
  • Frequently allocated fixed-size objects, like inodes or task structures: dedicated SLAB/SLUB caches via kmem_cache_create()

Common Mistakes

Mistake Why It’s a Problem Fix
Using vmalloc() for DMA buffers DMA hardware usually needs physically contiguous memory Use kmalloc() or dma_alloc_coherent() instead
Requesting a very large kmalloc() allocation kmalloc has a practical upper size limit tied to SLUB size classes Switch to vmalloc() for large, non-DMA buffers
Calling kmalloc(…, GFP_KERNEL) inside an interrupt handler GFP_KERNEL may sleep, which is not allowed in atomic context Use GFP_ATOMIC in interrupt or atomic contexts

Best Practices

  • Always check the return value of kmalloc()/vmalloc() for NULL before use
  • Match every allocation with the correct free function: kfree() for kmalloc, vfree() for vmalloc
  • Prefer devm_kzalloc() in real driver probe() functions so cleanup is automatic

Performance Considerations

kmalloc() is generally faster than vmalloc() because it does not need to build new page table mappings — it simply hands back memory from the existing linear-mapped region. vmalloc() has to allocate scattered physical pages and then construct fresh virtual mappings for them, which costs more CPU time and, from our previous lecture, potentially more TLB pressure once that new mapping is actually used.

Security Considerations

Always zero out sensitive buffers before freeing them, and never expose raw kernel pointers to user space, since predictable kernel memory addresses can help an attacker defeat KASLR and other kernel hardening mechanisms.

Summary and Key Takeaways

  • The page allocator hands out physical pages; SLAB/SLUB carves them into reusable objects; kmalloc and vmalloc sit on top
  • kmalloc() gives physically contiguous memory and is DMA-safe; vmalloc() gives virtually contiguous memory only
  • SLUB is the modern default kmalloc backend, replacing the classic SLAB implementation in current kernels
  • Choose the allocator based on size and whether physical contiguity is actually required

Conclusion

You now understand how the Linux kernel memory allocators fit together, from the low-level page allocator all the way up to the kmalloc() and vmalloc() calls you will use in almost every driver you write. This closes out our tour of address translation and allocation fundamentals in this free Linux kernel development course. Keep practicing with the ep_alloc_demo module above, experiment with different sizes, and watch how the kernel behaves as you approach kmalloc’s practical size limits.

FAQ

What is the difference between kmalloc and vmalloc in Linux?

kmalloc() returns memory that is both physically and virtually contiguous, making it DMA-safe. vmalloc() returns memory that is only virtually contiguous; the underlying physical pages are usually scattered.

Is SLAB still used in modern Linux kernels?

The classic SLAB implementation has been removed from current mainline kernels in favor of SLUB, which is now the default kmalloc backend. The public kmalloc/kmem_cache API is unchanged.

Which allocator should I use for a DMA buffer?

Use kmalloc() for small buffers, or dma_alloc_coherent() for buffers that need to be shared safely with DMA-capable hardware. Avoid vmalloc() for DMA.

Why does vmalloc() take longer than kmalloc()?

vmalloc() must build new page table mappings for scattered physical pages, while kmalloc() reuses the kernel’s existing linear mapping, avoiding that extra setup cost.

What does GFP_KERNEL mean?

It is a memory allocation flag meaning “normal allocation, allowed to sleep if memory is not immediately available.” It is the default choice for allocations made in process context.

Can I use kmalloc inside an interrupt handler?

Yes, but you must use GFP_ATOMIC instead of GFP_KERNEL, since interrupt context cannot sleep waiting for memory to become available.

Is this a free Linux device drivers course?

Yes, this lecture is part of a completely free Linux kernel development course and free Linux device drivers course available on EmbeddedPathashala.

 

Continue the Free Linux Kernel Development Course

Next up: a deep dive into kmalloc size classes and the SLUB allocator internals.

Leave a Reply

Your email address will not be published. Required fields are marked *