Skip to content

Day93 - vmalloc() Internals

Goal

Understand how vmalloc() provides virtually contiguous memory by mapping multiple independent physical pages allocated from the Buddy Allocator.

Build a userspace simulator that models the core concepts behind the Linux kernel vmalloc() implementation, including virtual address allocation, page mapping, address translation, and memory release.


What I Learned

Virtual vs Physical Memory

Unlike kmalloc(), which requires physically contiguous memory, vmalloc() allocates individual pages from the Buddy Allocator and maps them into a contiguous virtual address range.

This allows large memory allocations without requiring contiguous physical memory.

Virtual Address Space

0x10000000
+-----------+
|  Page 0   | ───────────────► PFN 1
+-----------+
|  Page 1   | ───────────────► PFN 3
+-----------+
|  Page 2   | ───────────────► PFN 8
+-----------+
|  Page 3   | ───────────────► PFN 2
+-----------+

Virtual Address
    Contiguous

Physical Pages
    Non-contiguous

vmalloc Architecture

The simulator is built on top of the existing memory subsystem:

          vmalloc()
     Buddy Allocator
         Physical Pages

Each vmalloc() allocation creates a virtual memory area that records:

  • Virtual address
  • Requested size
  • Number of mapped pages
  • Backing struct page array

Address Translation

The simulator implements vmalloc_to_page() to translate any virtual address inside a vmalloc area into its corresponding backing page.

Translation consists of:

  1. Locate the containing vmalloc area.
  2. Calculate the page offset.
  3. Convert the offset into a page index.
  4. Return the mapped struct page.
Virtual Address
Find vmalloc area
Calculate page index
struct page
PFN

Memory Lifecycle

Each vmalloc area owns its backing pages.

vmalloc()


alloc_pages(order = 0)
Create vmalloc_area
Return virtual address

...

vfree()

free_pages()
Remove vmalloc_area

Labs

Lab 1 - Basic Allocation and Free

Verified:

  • vmalloc()
  • vfree()
  • Virtual address allocation
  • Page count calculation
  • Area removal
  • Memory cleanup

Lab 2 - Virtual Address Translation

Implemented and verified:

  • vmalloc_to_page()
  • Page index calculation
  • Address boundary handling
  • Invalid address detection

Lab 3 - Physically Non-contiguous Pages

Created fragmented physical memory by reserving Buddy pages before calling vmalloc().

Verified that:

  • Virtual addresses remain contiguous.
  • Physical pages are not contiguous.
  • Address translation still returns the correct backing pages.

Lab 4 - Linux Kernel Verification

Verified the simulator against the real Linux kernel using:

  • vmalloc()
  • vmalloc_to_page()
  • page_to_pfn()
  • vfree()

Observed that:

  • Virtual addresses increase by one PAGE_SIZE.
  • Physical page frame numbers (PFNs) are not necessarily contiguous.
  • The Raspberry Pi 5 kernel uses a 16 KiB page size, while the simulator uses 4 KiB pages. Although the page size differs, the address translation principle remains identical.

APIs

Memory Management

  • vmalloc()
  • vfree()
  • vmalloc_to_page()

Buddy Allocator

  • alloc_pages()
  • free_pages()

Learning Summary

Today I implemented a simplified version of the Linux kernel vmalloc() subsystem.

Unlike kmalloc(), which depends on physically contiguous memory, vmalloc() builds a contiguous virtual address space by mapping independently allocated physical pages.

The simulator demonstrates the complete lifecycle of a vmalloc allocation, including virtual address management, page mapping, address translation, and memory release. The behavior was further verified using a real Linux kernel module, confirming that contiguous virtual addresses can be backed by non-contiguous physical pages.