Linux內(nèi)核高-低端內(nèi)存設置代碼跟蹤(ARM構(gòu)架)
- void __init bootmem_init(void)
- {
unsigned long min, max_low, max_high; max_low = max_high = 0; find_limits(&min, &max_low, &max_high); arm_bootmem_init(min, max_low); /* * Sparsemem tries to allocate bootmem in memory_present(), * so must be done after the fixed reservations */ arm_memory_present(); /* * sparse_init() needs the bootmem allocator up and running. */ sparse_init(); /* * Now free the memory - free_area_init_node needs * the sparse mem_map arrays initialized by sparse_init() * for memmap_init_zone(), otherwise all PFNs are invalid. */ arm_bootmem_free(min, max_low, max_high); high_memory = __va(((phys_addr_t)max_low << PAGE_SHIFT) - 1) + 1; /* * This doesnt seem to be used by the Linux memory manager any * more, but is used by ll_rw_block. If we can get rid of it, we * also get rid of some of the stuff above as well. * * Note: max_low_pfn and max_pfn reflect the number of _pages_ in * the system, not the maximum PFN. */ max_low_pfn = max_low - PHYS_PFN_OFFSET; max_pfn = max_high - PHYS_PFN_OFFSET; - }
- static void __init find_limits(unsigned long
*min, unsigned long *max_low, unsigned long *max_high) - {
struct meminfo *mi = &meminfo; int i; *min = -1UL; *max_low = *max_high = 0; for_each_bank (i, mi) { struct membank *bank = &mi->bank[i]; unsigned long start, end; start = bank_pfn_start(bank); end = bank_pfn_end(bank); if (*min > start) *min = start; if (*max_high < end) *max_high = end; if (bank->highmem) continue; if (*max_low < end) *max_low = end; } - }
- min :內(nèi)存物理地址起始
- max_low :低端內(nèi)存區(qū)物理地址末端
- max_high :高端內(nèi)存區(qū)物理地址末端
- “如果這個內(nèi)存bank是高端內(nèi)存(bank->highmem != 0),跳過max_low = end;語句,max_low和max_high將不同(結(jié)果實際上是max_high >
max_low); - 否則假設沒有一個內(nèi)存bank是高端內(nèi)存(所有bank->highmem == 0)max_low和max_high必然一致(高端內(nèi)存大小為0)”
- struct meminfo meminfo;
- /*
* Memory map description */ - #define NR_BANKS 8
- struct membank {
phys_addr_t start; unsigned long size; unsigned int highmem; - };
- struct meminfo {
int nr_banks; struct membank bank[NR_BANKS]; - };
- extern struct meminfo meminfo;
- #define for_each_bank(iter,mi)
for (iter = 0; iter < (mi)->nr_banks; iter++)
- void __init setup_arch(char **cmdline_p)
- {
struct machine_desc *mdesc; unwind_init(); setup_processor(); mdesc = setup_machine_fdt(__atags_pointer); if (!mdesc) mdesc = setup_machine_tags(machine_arch_type); machine_desc = mdesc; machine_name = mdesc->name; if (mdesc->soft_reboot) reboot_setup("s"); init_mm.start_code = (unsigned long) _text; init_mm.end_code = (unsigned long) _etext; init_mm.end_data = (unsigned long) _edata; init_mm.brk = (unsigned long) _end; strlcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE); *cmdline_p = cmd_line; parse_early_param(); sanity_check_meminfo(); arm_memblock_init(&meminfo, mdesc); paging_init(mdesc); request_standard_resources(mdesc); unflatten_device_tree(); - #ifdef CONFIG_SMP
if (is_smp()) smp_init_cpus(); - #endif
reserve_crashkernel(); cpu_init(); tcm_init(); - #ifdef CONFIG_MULTI_IRQ_HANDLER
handle_arch_irq = mdesc->handle_irq; - #endif
- #ifdef CONFIG_VT
- #if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con; - #elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con; - #endif
- #endif
early_trap_init(); if (mdesc->init_early) mdesc->init_early(); - }
在上面的注釋中,我已經(jīng)表明了重點和解析,下面我細化下:
(1)獲取參數(shù)部分
- int __init arm_add_memory(phys_addr_t start, unsigned long size)
- {
struct membank *bank = &meminfo.bank[meminfo.nr_banks]; if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_CRIT "NR_BANKS too low, " "ignoring memory at 0xllxn", (long long)start); return -EINVAL; } /* * Ensure that start/size are aligned to a page boundary. * Size is appropriately rounded down, start is rounded up. */ size -= start & ~PAGE_MASK; bank->start = PAGE_ALIGN(start); bank->size = size & PAGE_MASK; /* * Check whether this memory region has non-zero size or * invalid node number. */ if (bank->size == 0) return -EINVAL; meminfo.nr_banks++; return 0; - }
- /*
* Pick out the memory size. We look for mem=size@start, * where start and size are "size[KkMm]" */ - static int __init early_mem(char *p)
- {
static int usermem __initdata = 0; unsigned long size; phys_addr_t start; char *endp; /* * If the user specifies memory size, we * blow away any automatically generated * size. */ if (usermem == 0) { usermem = 1; meminfo.nr_banks = 0; } start = PHYS_OFFSET; size = memparse(p, &endp); if (*endp == @) start = memparse(endp + 1, NULL); arm_add_memory(start, size); return 0; - }
- early_param("mem", early_mem);
- static void * __initdata vmalloc_min = (void *)(VMALLOC_END - SZ_128M);
- /*
* vmalloc=size forces the vmalloc area to be exactly size * bytes. This can be used to increase (or decrease) the vmalloc * area - the default is 128m. */ - static int __init early_vmalloc(char *arg)
- {
unsigned long vmalloc_reserve = memparse(arg, NULL); if (vmalloc_reserve < SZ_16M) { vmalloc_reserve = SZ_16M; printk(KERN_WARNING "vmalloc area too small, limiting to %luMBn", vmalloc_reserve >> 20); } if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) { vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M); printk(KERN_WARNING "vmalloc area is too big, limiting to %luMBn", vmalloc_reserve >> 20); } vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve); return 0; - }
- early_param("vmalloc", early_vmalloc);
(2)在獲得了必要的信息(初始化好struct meminfo meminfo和vmalloc_min)后,內(nèi)核通過sanity_check_meminfo函數(shù)自動去通過vmalloc_min信息來初始化每個meminfo.bank[?]中的highmem成員。此過程中如果有必要,將可能會改變meminfo中的bank數(shù)組。處理函數(shù)位于mmu.c (archarmmm):
- static phys_addr_t lowmem_limit __initdata = 0;
- void __init sanity_check_meminfo(void)
- {
int i, j, highmem = 0; for (i = 0, j = 0; i < meminfo.nr_banks; i++) { struct membank *bank = &meminfo.bank[j]; *bank = meminfo.bank[i]; - #ifdef CONFIG_HIGHMEM
if (__va(bank->start) >= vmalloc_min || __va(bank->start) < (void *)PAGE_OFFSET) highmem = 1; bank->highmem = highmem; /* * Split those memory banks which are partially overlapping * the vmalloc area greatly simplifying things later. */ if (__va(bank->start) < vmalloc_min && bank->size > vmalloc_min - __va(bank->start)) { if (meminfo.nr_banks >= NR_BANKS) { printk(KERN_CRIT "NR_BANKS too low, " "ignoring high memoryn"); } else { memmove(bank + 1, bank, (meminfo.nr_banks - i) * sizeof(*bank)); meminfo.nr_banks++; i++; bank[1].size -= vmalloc_min - __va(bank->start); bank[1].start = __pa(vmalloc_min - 1) + 1; bank[1].highmem = highmem = 1; j++; } bank->size = vmalloc_min - __va(bank->start); } - #else
bank->highmem = highmem; /* * Check whether this memory bank would entirely overlap * the vmalloc area. */ if (__va(bank->start) >= vmalloc_min || __va(bank->start) < (void *)PAGE_OFFSET) { printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx " "(vmalloc region overlap).n", (unsigned long long)bank->start, (unsigned long long)bank->start + bank->size - 1); continue; } /* * Check whether this memory bank would partially overlap * the vmalloc area. */ if (__va(bank->start + bank->size) > vmalloc_min || __va(bank->start + bank->size) < __va(bank->start)) { unsigned long newsize = vmalloc_min - __va(bank->start); printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx " "to -%.8llx (vmalloc region overlap).n", (unsigned long long)bank->start, (unsigned long long)bank->start + bank->size - 1, (unsigned long long)bank->start + newsize - 1); bank->size = newsize; } - #endif
if (!bank->highmem && bank->start + bank->size > lowmem_limit) lowmem_limit = bank->start + bank->size; j++; } - #ifdef CONFIG_HIGHMEM
if (highmem) { const char *reason = NULL; if (cache_is_vipt_aliasing()) { /* * Interactions between kmap and other mappings * make highmem support with aliasing VIPT caches * rather difficult. */ reason = "with VIPT aliasing cache"; } if (reason) { printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memoryn", reason); while (j > 0 && meminfo.bank[j - 1].highmem) j--; } } - #endif
meminfo.nr_banks = j; memblock_set_current_limit(lowmem_limit); - }
(3)最后必須做的就是排序了,完成了這個工作就可以完全被我們上面提到的find_limits函數(shù)使用了,而這個工作就放在了接下來的arm_memblock_init(&meminfo, mdesc);中的一開頭:
- static int __init meminfo_cmp(const void *_a, const void *_b)
- {
const struct membank *a = _a, *b = _b; long cmp = bank_pfn_start(a) - bank_pfn_start(b); return cmp < 0 ? -1 : cmp > 0 ? 1 : 0; - }
- void __init arm_memblock_init(struct meminfo *mi, struct machine_desc *mdesc)
- {
int i; sort(&meminfo.bank, meminfo.nr_banks, sizeof(meminfo.bank[0]), meminfo_cmp, NULL); memblock_init(); for (i = 0; i < mi->nr_banks; i++) memblock_add(mi->bank[i].start, mi->bank[i].size); /* Register the kernel text, kernel data and initrd with memblock. */ - #ifdef CONFIG_XIP_KERNEL
memblock_reserve(__pa(_sdata), _end - _sdata); - #else
memblock_reserve(__pa(_stext), _end - _stext); - #endif
- #ifdef CONFIG_BLK_DEV_INITRD
if (phys_initrd_size && !memblock_is_region_memory(phys_initrd_start, phys_initrd_size)) { pr_err("INITRD: 0xlx+0xlx is not a memory region - disabling initrdn", phys_initrd_start, phys_initrd_size); phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size && memblock_is_region_reserved(phys_initrd_start, phys_initrd_size)) { pr_err("INITRD: 0xlx+0xlx overlaps in-use memory region - disabling initrdn", phys_initrd_start, phys_initrd_size); phys_initrd_start = phys_initrd_size = 0; } if (phys_initrd_size) { memblock_reserve(phys_initrd_start, phys_initrd_size); /* Now convert initrd to virtual addresses */ initrd_start = __phys_to_virt(phys_initrd_start); initrd_end = initrd_start + phys_initrd_size; } - #endif
arm_mm_memblock_reserve(); arm_dt_memblock_reserve(); /* reserve any platform specific memblock areas */ if (mdesc->reserve) mdesc->reserve(); memblock_analyze(); memblock_dump_all(); - }
通過上面的分析,整個高低端內(nèi)存是如何確定的基本就清晰了,這里總結(jié)一下:
ARM構(gòu)架中,高-低段內(nèi)存是內(nèi)核通過內(nèi)核啟動參數(shù)( mem=size@start和vmalloc=size)來自動配置的,如果沒有特殊去配置他,那么在普通的ARM系統(tǒng)中是不會有高端內(nèi)存存在的。除非你系統(tǒng)的RAM很大或vmalloc配置得很大,就很可能出現(xiàn)高端內(nèi)存。
評論