Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Virtual Allocator — vmalloc

Status: Not yet implemented. This chapter documents the design and what needs to be built.

How vmalloc Differs from kmalloc

kmallocvmalloc
ReturnsPhysical address (identity mapped)Virtual address
Backed byHeap region in identity-mapped spaceAny physical frames, mapped on demand
Contiguous?Physically contiguousVirtually contiguous, physically scattered
Use caseSmall kernel allocationsLarge allocations, kernel buffers
LocationLow memory (~0x00200000+)High virtual memory (~0xC0000000+)

How vmalloc Works

vmalloc(size)
    │
    ├─ Round up size to page boundary
    ├─ Find N free virtual pages in the vmalloc region
    ├─ For each of the N pages:
    │   ├─ allocate_frame()         ← get a physical frame
    │   └─ map_page(frame, virt, flags) ← map it into virtual space
    └─ Return first virtual address

The physical frames don’t need to be contiguous — each is independently mapped to a contiguous virtual range.

The vmalloc Region

0xFFFFFFFF ─────────────  4GB
           (end of vmalloc region)
           
           vmalloc allocates here:
           virtual pages mapped to
           scattered physical frames
           
0xC0000000 ─────────────  3GB
           (start of vmalloc region)

0x00400000 ─────────────  ← End of identity mapping
           (kmalloc lives here)

Tracking Virtual Allocations

Unlike kmalloc, where the header is adjacent to the data, vmalloc needs a separate metadata structure. We need to know, for each vmalloc allocation:

  • Which virtual address range is in use
  • How many pages it spans (so vfree knows how many to unmap)

One approach: a small array of VmallocRegion structs in kernel memory:

#![allow(unused)]
fn main() {
struct VmallocRegion {
    virt_start: u32,   // Virtual start address
    page_count: u32,   // Number of pages allocated
    in_use: bool,      // Is this slot taken?
}

static mut VMALLOC_TABLE: [VmallocRegion; MAX_VMALLOC_REGIONS] = ...;
}

Functions to Implement

#![allow(unused)]
fn main() {
// memory/virtual_mem.rs

pub unsafe fn vmalloc(size: usize) -> *mut u8;
// Allocate `size` bytes of virtual address space
// Backed by physical frames via map_page()
// Returns null on failure

pub unsafe fn vfree(ptr: *mut u8);
// Unmap and free all pages for this allocation
// Calls unmap_page() + free_frame() for each page

pub unsafe fn vsize(ptr: *mut u8) -> usize;
// Return the size of the vmalloc allocation at ptr
}

The vmalloc Address Allocator

We need a way to find free virtual address ranges in the vmalloc region. The simplest approach: start at 0xC0000000 and walk forward, skipping ranges that are in use. Maintain a VMALLOC_NEXT pointer similar to the frame allocator’s bump pointer.

Key Differences in vfree

vfree must do more work than kfree:

#![allow(unused)]
fn main() {
pub unsafe fn vfree(ptr: *mut u8) {
    // Find the allocation record for ptr
    let region = find_vmalloc_region(ptr as u32);

    // For each page in the allocation:
    for i in 0..region.page_count {
        let virt = region.virt_start + i * PAGE_SIZE;

        // Get the physical frame address before unmapping
        let phys = get_physaddr(virt as *const u8) as u32;

        // Unmap the virtual page
        unmap_page(virt);

        // Return the physical frame to the frame allocator
        free_frame(phys & !0xFFF);
    }

    // Mark the allocation record as free
    region.in_use = false;
}
}

When to Use vmalloc vs kmalloc

Use kmalloc for:

  • Small structs and buffers (< a few KB)
  • Allocations that need physically contiguous memory
  • Performance-critical allocations (vmalloc has overhead from page mapping)

Use vmalloc for:

  • Large buffers (several MB)
  • Allocations where physical contiguity doesn’t matter
  • When you need more memory than fits in the kmalloc heap