Fundamentals 21 min read

Understanding the Linux Kernel MemBlock Allocator: Architecture, Initialization, and Usage

An in‑depth overview of the Linux kernel MemBlock allocator explains its purpose during early boot, its core data structures (memblock, memblock_type, memblock_region), initialization process, allocation strategies, key APIs such as memblock_add, memblock_remove, memblock_alloc, and how it hands off to the buddy system.

Deepin Linux
Deepin Linux
Deepin Linux
Understanding the Linux Kernel MemBlock Allocator: Architecture, Initialization, and Usage

When a Linux system boots, the kernel must allocate memory before the regular buddy allocator is ready. The MemBlock allocator is introduced to manage physical memory in this early stage, providing a simple framework that divides memory into memory (available) and reserved (already used) regions.

1. Overview of MemBlock – MemBlock replaced the older bootmem allocator because bootmem required many kernel services to be initialized first. MemBlock organizes memory into two main collections, each described by struct memblock_type , which contain arrays of struct memblock_region entries. The top‑level struct memblock holds the memory and reserved types, a current_limit address, and a bottom_up flag that controls allocation direction.

2. Core Data Structures

struct memblock : contains current_limit , memory , and reserved .

struct memblock_type : tracks the number of regions ( cnt ), the allocated array size ( max ), total size of all regions ( total_size ), and a pointer to the regions array.

struct memblock_region : defines a single physical block with base , size , optional nid (NUMA node), and flags such as MEMBLOCK_NOMAP .

// file: include/linux/memblock.h
struct memblock {
    phys_addr_t current_limit;
    struct memblock_type memory;
    struct memblock_type reserved;
};
// file: include/linux/memblock.h
struct memblock_type {
    unsigned long cnt;      /* number of regions */
    unsigned long max;      /* size of the allocated array */
    phys_addr_t total_size; /* size of all regions */
    struct memblock_region *regions;
};
// file: include/linux/memblock.h
struct memblock_region {
    phys_addr_t base;
    phys_addr_t size;
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
    int nid;
#endif
};

3. Initialization Process – During early boot (e.g., on arm64), the kernel obtains the physical memory map from the device‑tree blob (DTB) or U‑Boot. It then populates the global struct memblock memblock variable, initializing memory.regions and reserved.regions with static arrays of 128 entries ( INIT_MEMBLOCK_REGIONS ) and setting current_limit to MEMBLOCK_ALLOC_ANYWHERE . Configuration options such as CONFIG_NO_BOOTMEM and CONFIG_HAVE_MEMBLOCK control whether bootmem or memblock is used.

// file: mm/memblock.c
struct memblock memblock __initdata_memblock = {
    .memory.regions = memblock_memory_init_regions,
    .memory.cnt = 1,    /* empty dummy entry */
    .memory.max = INIT_MEMBLOCK_REGIONS,

    .reserved.regions = memblock_reserved_init_regions,
    .reserved.cnt = 1,    /* empty dummy entry */
    .reserved.max = INIT_MEMBLOCK_REGIONS,

    .current_limit = MEMBLOCK_ALLOC_ANYWHERE,
};

After the memory map is parsed, memblock_add() inserts each contiguous physical region into the memory list, keeping the list ordered for fast lookup. This phase supplies memory for early kernel data structures, device‑driver initialization, and module loading.

4. Key APIs

memblock_add(base, size, nid) – adds a new usable region.

memblock_remove(base, size, nid) – removes a region that should no longer be used.

memblock_alloc(size, align, nid) – allocates a contiguous block respecting alignment; the block is moved from memory to reserved .

memblock_free(base, size, nid) – releases a previously allocated block back to memory .

phys_addr_t new_mem_base = 0x10000000;
phys_addr_t new_mem_size = 0x1000000;
int node_id = 0;
memblock_add(new_mem_base, new_mem_size, node_id);
phys_addr_t allocated_mem;
size_t required_size = 0x1000;
size_t alignment = 0x100;
int node_id = 0;
allocated_mem = memblock_alloc(required_size, alignment, node_id);
if (allocated_mem) {
    // use the memory
}

5. Allocation Strategy – MemBlock uses a “first match” algorithm. The direction of the search is controlled by bottom_up : when true, allocation scans from low to high addresses; when false, it scans from high to low. This flexibility helps reduce fragmentation on different architectures.

6. Transition to the Buddy System – Once the buddy allocator is initialized, the kernel transfers the reserved regions managed by MemBlock to the buddy system, which then takes over all regular memory management tasks. MemBlock’s data structures are discarded (or kept in a special .meminit.data section) based on configuration options such as CONFIG_ARCH_DISCARD_MEMBLOCK and CONFIG_MEMORY_HOTPLUG .

Overall, the MemBlock allocator is a crucial early‑boot component that provides deterministic, low‑overhead memory handling before the full-fledged page‑based allocator becomes available.

memory managementKernellinuxboot processMemBlock
Deepin Linux
Written by

Deepin Linux

Research areas: Windows & Linux platforms, C/C++ backend development, embedded systems and Linux kernel, etc.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.