内存管理-7-memblock子系统-1-初探

基于msm-5.4

一、memblock简介

现实的物理内存是被截的一段一段的了,并不是一马平川的。伙伴系统要进行初始化,就需要知道哪些区域是可用的,哪些区域已经被占用了、被保留了,剩余的才可以用于伙伴系统的初始化。因此需要一个早期的内存管理机制,即 memblock 机制.

全局变量: struct memblock memblock;
可用物理内存:memblock.memory 数组
reserve的物理内存:memblock.reserved 数组
内核镜像(.init段除外)、dtb、u-boot、页表。
GPU、Camera、音视频编解码的保留物理内存。

保留的区域有很多,驱动需要占用内存,内核启动自己的镜像也会占用一部分预留内存,u-boot启动完后也一直驻留在内存中,reboot时还会跳转到u-boot中去执行。

.init只在内核初始化时运行一次,运行完后可以清理掉,可以节省一部分内存。


二、memblock接口

1. 文件接口

/sys/kernel/debug/memblock/memory
/sys/kernel/debug/memblock/reserved
/proc/kmsg: memblock=debug

分别对应memory区和reserved区每个区块的物理起始和结束地址,第三个是启动参数,用于打开memblock的调试信息。


2. 函数接口

int memblock_add(phys_addr_t base, phys_addr_t size);
int memblock_remove(phys_addr_t base, phys_addr_t size);
for_each_mem_range
int memblock_reserve(phys_addr_t base, phys_addr_t size);
int memblock_free(phys_addr_t base, phys_addr_t size);

这些regions添加后的状态:


三、memblock初始化

1. 执行路径

start_kernel
    setup_arch
        setup_machine_fdt
            early_init_dt_scan
                early_init_dt_scan_memory
        arm64_memblock_init
            early_init_fdt_scan_reserved_mem

/* 添加/删除就是初始化这两个全局数组 */
static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS]; //128
static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS]; //161

early_init_dt_scan_memory() 中会检索 device_type="memory" 设备树节点。
early_init_fdt_scan_reserved_mem() 中会检索根节点下的名为 "reserved-memory" 设备树一级子节点。

除了这两个函数外,还有其它路径会调用memblock的接口函数进行添加。

下一步会将memory这个空闲的内存块信息释放给伙伴系统。


四、memblock内存释放

对于reserved区域的内存块,在释放到伙伴系统时,会设置reserved属性,这样这些页面在伙伴系统中就保留下来了。

1. 释放调用路径

start_kernel
    setup_arch
        setup_machine_fdt
            early_init_dt_scan
                early_init_dt_scan_memory
        arm64_memblock_init
            early_init_fdt_scan_reserved_mem
    mm_init
        mem_init
            memblock_free_all //这里

memblock_free_all() 会将所有没有使用的内存放到伙伴系统的freelist中。

for_each_reserved_mem_region(i, p_start, p_end)    
for_each_free_mem_range(i, nid, flags, p_start, p_end, p_nid)

将内存释放到伙伴系统后,就等于伙伴系统接管了物理内存的管理。


2. 释放代码走读

unsigned long __init memblock_free_all(void) //memblock.c
{
    unsigned long pages;
    /* 释放所有低端内存到伙伴系统 */
    pages = free_low_memory_core_early();
    /* 计算释放的总的page数(这里不包含被预留占用的,会比实际的物理内存小一些的) */
    totalram_pages_add(pages); //计算释放的总的page数

    return pages;
}

static unsigned long __init free_low_memory_core_early(void) //memblock.c
{
    phys_addr_t start, end; /* 遍历保存每个region的起始和结束地址 */

    /* 遍历memblock.reserved中的regions, 将region中的页面属性设置为reserved*/
    for_each_reserved_mem_region(i, &start, &end)
        reserve_bootmem_region(start, end); //但是没有释放到伙伴系统####

    /* 遍历memblock.memory中的regions, 以region为单位,释放内存到伙伴系统 */
    for_each_free_mem_range(i, NUMA_NO_NODE, MEMBLOCK_NONE, &start, &end, NULL)
        count += __free_memory_core(start, end);

    return count;
}

/*
 * __free_memory_core --> __free_pages_memory --> memblock_free_pages --> __free_pages_core --> __free_pages -->
 * free_the_page --> __free_pages_ok --> free_one_page --> __free_one_page --> add_to_free_area_tail
*/

/* 将page根据order、zone、migrate_type 添加到指定的链表中 */
static inline void add_to_free_area_tail(struct page *page, struct free_area *area, int migratetype)
{
    list_add_tail(&page->lru, &area->free_list[migratetype]);
    area->nr_free++;
}


五、.init内存释放

1. memblock中reserved的区域

内核的代码段(.text/.data/.bss,.init除外).
initrd
dtb
设备树中的reversed-momery区域(CMA除外)
临时页表
驱动reserved momery的初始化


2. .init段释放路径

//include/linux/init.h
#define __init __section(.init.text) __cold  __latent_entropy __noinitretpoline __nocfi

__init修饰的代码只在内核初始化过程中调用一次,之后就再也不会使用了。释放掉可以节省一部分内存。

start_kernel
    setup_arch
        setup_machine_fdt
            early_init_dt_scan
                early_init_dt_scan_memory //memblock.memory初始化
        arm64_memblock_init
            early_init_fdt_scan_reserved_mem //memblock.reserved初始化
    mm_init
        mem_init
            memblock_free_all //释放到伙伴系统
    arch_call_rest_init
        rest_init
            kernel_init
                free_initmem //这里释放.init段内存


3. 释放到伙伴系统中

/* 将 .init.text 段占据的内存释放到伙伴系统 */
void free_initmem(void)
{
    free_reserved_area(lm_alias(__init_begin), lm_alias(__init_end), 0, "unused kernel");
    unmap_kernel_range((u64)__init_begin, (u64)(__init_end - __init_begin));
}

/*
 * free_reserved_area --> free_reserved_page --> __free_reserved_page --> __free_page --> __free_pages -->
 * free_the_page --> __free_pages_ok --> free_one_page
 */

 

posted on 2024-07-03 10:20  Hello-World3  阅读(134)  评论(0编辑  收藏  举报

导航