1. An overview of the
Then the last article “Linux kernel source analysis of setup_arch (A)” to continue the analysis, this article first analyzes the arm_memblock_init function, and then analyzes the kernel startup stage is how to carry out memory management.
2. arm_memblock_init
This function adds the memory information recorded in meminfo to memblock.memory, and then adds the memory area where the kernel image resides to memblock.reserved. Arm_mm_memblock_reserve adds the memory area where the page table resides to memblock.reserved; If a device tree is used, the memory occupied is reserved with arm_dt_memblock_reserve, and finally the cpu-specific mDESC ->reserve is called, which corresponds to cpu_mem_reserve, defined in cpu.c.
/* arch/arm/mm/init.c */
void __init arm_memblock_init(...). {
for (i = 0; i < mi->nr_banks; i++)
memblock_add(mi->bank[i].start, mi->bank[i].size);
memblock_reserve(__pa(_stext), _end - _stext);
arm_mm_memblock_reserve();
arm_dt_memblock_reserve();
if (mdesc->reserve)
mdesc->reserve();
arm_memblock_steal_permitted = false;
memblock_allow_resize();
memblock_dump_all();
}
/* include/kernel/memblock.h */
struct memblock {
phys_addr_t current_limit;
struct memblock_type memory;
struct memblock_type reserved;
};
Copy the code
3. memblock_alloc
Now it’s time to execute the paging_init function. Before we look at paging_init, let’s talk a little bit about memory management during kernel startup. The memblock data structure is introduced from arm_memblock_init, which is used to implement the memory management function at the initial stage of kernel startup. Strictly speaking, its life cycle is up to paging_init::bootmem_init, memblock_alloc call flow is as follows.
The function that actually looks for free memory is memblock_find_in_range_node, where the macro definition for_each_free_mem_range_reverse actually looks for free memory.
/* mm/memblock.c */
phys_addr_t memblock_find_in_range_node(...).
{... for_each_free_mem_range_reverse(i, nid, &this_start, &this_end,NULL) {...if (cand >= this_start)
return cand;
}
return 0;
}
Copy the code
The macro is defined as follows, but there is a nested function Orz…
/* include/linux/memblock.h */
#definefor_each_free_mem_range_reverse(i, nid, p_start, p_end, p_nid) \ for (i = (u64)ULLONG_MAX, \ __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid); \ i ! = (u64)ULLONG_MAX; \ __next_free_mem_range_rev(&i, nid, p_start, p_end, p_nid))
Copy the code
First of all, memblock.reserved indicates the occupied memory area. Memblock. memory records the memory module information. Now back to the __next_free_mem_range_rev function, the purpose of code segments (1) and (2) is to find the free area of memory between the two reserved areas on the memory stick. Find then pass code (3) the spare area starting address and ending address correction, because (1) (2) the code segment can only guarantee free area and the current memory chips overlap, does not guarantee the spare area completely under the current memory chips, mainly because there is no guarantee that both reserved area in the current memory chips.
/* mm/memblock.c */
void __init_memblock __next_free_mem_range_rev(...)
{
struct memblock_type *mem = &memblock.memory;
struct memblock_type *rsv =&memblock.reserved; .* / / * (1)
for(; mi >=0; mi--) {
struct memblock_region *m = &mem->regions[mi];
phys_addr_t m_start = m->base;
phys_addr_tm_end = m->base + m->size; ./ * * / (2)
for(; ri >=0; ri--) {
struct memblock_region *r = &rsv->regions[ri];
phys_addr_t r_start = ri ? r[- 1].base + r[- 1].size : 0;
phys_addr_tr_end = ri < rsv->cnt ? r->base : ULLONG_MAX; ./ * (3) * /
if (m_end > r_start) {
if (out_start)
*out_start = max(m_start, r_start);
if (out_end)
*out_end = min(m_end, r_end);
if(out_nid) *out_nid = memblock_get_region_node(m); .return;
}
}
}
*idx = ULLONG_MAX;
}
Copy the code
Return to memblock_find_in_range_node to check whether the start address and end address of the region are valid, and finally apply for the requested size of the memory region. All you need to do is mark this area as reserved and the whole process of allocating memory is over.
/* mm/memblock.c */
int memblock_reserve(phys_addr_t base, phys_addr_t size)
{
struct memblock_type* _rgn = &memblock.reserved;
return memblock_add_region(_rgn, base, size, MAX_NUMNODES);
}
Copy the code
4. To summarize
- The arm_memblock_init function first transfers the memory information recorded in meminfo to memblock.memory, and then records the used memory area to memblock.reserved, including the area occupied by the kernel image, the page table area and the device tree.
- Memblock_alloc manages the memory through the information recorded in memory and reserved in memblock. Each memory request is recorded in memblock.reserved.