arm64内存-memblock介绍 API 实现
建议先看:https://www.cnblogs.com/zhangzhiwei122/p/16085238.html
memblock 属于 “六、内存布局” 这一部分。收集物理内存信息
memblock 介绍
memblock 内存管理机制主要用于 Kernel 启动阶段的内存管理,可以认为从kernel 启动到 free_initmem 。
在启动阶段, 内存分配并不需要很复杂,也不必它多考虑回收再利用,所以使用 memblock 来进行内存分配管理。
memblock 基于静态数组来进行管理。
memblock 数据结构
include/linux/memblock.h
memblock_region
memblock_region 用于描述此一段内存(base 和 size 确定)的使用情况(flags确定)。
35enum memblock_flags { 36 MEMBLOCK_NONE = 0x0, /* No special request */ 37 MEMBLOCK_HOTPLUG = 0x1, /* hotpluggable region */ 38 MEMBLOCK_MIRROR = 0x2, /* mirrored region */ 39 MEMBLOCK_NOMAP = 0x4, /* don't add to kernel direct mapping */ 40}; 41
memblock
81struct memblock { 82 bool bottom_up; /* is bottom up direction? */ 83 phys_addr_t current_limit; //physical address of the current allocation limit 84 struct memblock_type memory; 85 struct memblock_type reserved; 86};
memblock API
memblock.h
在指定的node和范围内寻找可用大小的内存
phys_addr_t memblock_find_in_range_node(phys_addr_t size, phys_addr_t align,
phys_addr_t start, phys_addr_t end,
int nid, ulong flags);
phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end,
phys_addr_t size, phys_addr_t align);
内存添加和删除type为memory的memblock region
int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid);
int memblock_add(phys_addr_t base, phys_addr_t size);
int memblock_remove(phys_addr_t base, phys_addr_t size);
memblock的内存分配和释放
phys_addr_t memblock_alloc_nid(phys_addr_t size, phys_addr_t align, int nid);
phys_addr_t memblock_alloc_try_nid(phys_addr_t size, phys_addr_t align, int nid);
phys_addr_t memblock_alloc(phys_addr_t size, phys_addr_t align);
int memblock_free(phys_addr_t base, phys_addr_t size);
void memblock_allow_resize(void);
int memblock_reserve(phys_addr_t base, phys_addr_t size);
void memblock_trim_memory(phys_addr_t align);
bool memblock_overlaps_region(struct memblock_type *type,
phys_addr_t base, phys_addr_t size);
int memblock_mark_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_clear_hotplug(phys_addr_t base, phys_addr_t size);
int memblock_mark_mirror(phys_addr_t base, phys_addr_t size);
int memblock_mark_nomap(phys_addr_t base, phys_addr_t size);
int memblock_clear_nomap(phys_addr_t base, phys_addr_t size);
ulong choose_memblock_flags(void);
memblock 实现
memblock.c
memblock 静态数组定义
105static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock; 106static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock; 107#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAP 108static struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS]; 109#endif 110 111struct memblock memblock __initdata_memblock = { 112 .memory.regions = memblock_memory_init_regions, 113 .memory.cnt = 1, /* empty dummy entry */ 114 .memory.max = INIT_MEMBLOCK_REGIONS, 115 .memory.name = "memory", 116 117 .reserved.regions = memblock_reserved_init_regions, 118 .reserved.cnt = 1, /* empty dummy entry */ 119 .reserved.max = INIT_MEMBLOCK_RESERVED_REGIONS, 120 .reserved.name = "reserved", 121 122 .bottom_up = false, 123 .current_limit = MEMBLOCK_ALLOC_ANYWHERE, 124};
105 ~ 106 行 - INIT_MEMBLOCK_REGIONS为 128,INIT_MEMBLOCK_RESERVED_REGIONS 也为 128 , 两个数组定义,memblock_memory_init_regions, memblock_reserved_init_regions。
region 结构体内 各成员都为 0,base, size , flags 都为 0, flags 为 0,表示 MEMBLOCK_NONE.
112 ~ 115 memblock 中,memory 这个type 的 初始化。 regions 指针指向上面定义的数组,max 为 128 ,表示数组最大128 个
(假设,调用了 130次add region,超出数组大小的,无法加入进数组)。
cnt = 1,表示里面有 1 个region 。
total_size = 0. 表示这个 type 里面的 空间 为0. 需要通过 memblock_add 增加才能改变。
bottom_up = false
,默认的分配方式是从上到下
memblock_add
706/** 707 * memblock_add - add new memblock region 708 * @base: base address of the new region 709 * @size: size of the new region 710 * 711 * Add new memblock region [@base, @base + @size) to the "memory" 712 * type. See memblock_add_range() description for mode details 713 * 714 * Return: 715 * 0 on success, -errno on failure. 716 */ 717int __init_memblock memblock_add(phys_addr_t base, phys_addr_t size) 718{ 719 phys_addr_t end = base + size - 1; 720 721 memblock_dbg("%s: [%pa-%pa] %pS\n", __func__, 722 &base, &end, (void *)_RET_IP_); 723 724 return memblock_add_range(&memblock.memory, base, size, MAX_NUMNODES, 0); 725}
724 行,调用 memblock_add_range , 加入到 memblock.memory 里面。 MAX_NUMNODES ,在UMA 系统里面,为0.
memblock_insert_region
556/** 557 * memblock_insert_region - insert new memblock region 558 * @type: memblock type to insert into 559 * @idx: index for the insertion point 560 * @base: base address of the new region 561 * @size: size of the new region 562 * @nid: node id of the new region 563 * @flags: flags of the new region 564 * 565 * Insert new memblock region [@base, @base + @size) into @type at @idx. 566 * @type must already have extra room to accommodate the new region. 567 */ 568static void __init_memblock memblock_insert_region(struct memblock_type *type, 569 int idx, phys_addr_t base, 570 phys_addr_t size, 571 int nid, 572 enum memblock_flags flags) 573{ 574 struct memblock_region *rgn = &type->regions[idx]; 575 576 BUG_ON(type->cnt >= type->max); 577 memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn)); 578 rgn->base = base; 579 rgn->size = size; 580 rgn->flags = flags; 581 memblock_set_region_node(rgn, nid); 582 type->cnt++; 583 type->total_size += size; 584}
577 行,使用 memmove 进行整体向后移动。然后将 base , size 赋值到 idx 指向的 数组元素。
memblock_add_range
基本原则: ranges[0].base + ranges[0].size <= ranges[1].base .
602static int __init_memblock memblock_add_range(struct memblock_type *type, 603 phys_addr_t base, phys_addr_t size, 604 int nid, enum memblock_flags flags) 605{ 606 bool insert = false; 607 phys_addr_t obase = base; 608 phys_addr_t end = base + memblock_cap_size(base, &size); 609 int idx, nr_new; 610 struct memblock_region *rgn; 611 612 if (!size) 613 return 0; 614 615 /* special case for empty array */ 616 if (type->regions[0].size == 0) { 617 WARN_ON(type->cnt != 1 || type->total_size); 618 type->regions[0].base = base; 619 type->regions[0].size = size; 620 type->regions[0].flags = flags; 621 memblock_set_region_node(&type->regions[0], nid); 622 type->total_size = size; 623 return 0; 624 } 625repeat: 626 /* 627 * The following is executed twice. Once with %false @insert and 628 * then with %true. The first counts the number of regions needed 629 * to accommodate the new area. The second actually inserts them. 630 */ 631 base = obase; 632 nr_new = 0; 633 634 for_each_memblock_type(idx, type, rgn) { 635 phys_addr_t rbase = rgn->base; 636 phys_addr_t rend = rbase + rgn->size; 637 638 if (rbase >= end) 639 break; 640 if (rend <= base) 641 continue; 642 /* 643 * @rgn overlaps. If it separates the lower part of new 644 * area, insert that portion. 645 */ 646 if (rbase > base) { 650 WARN_ON(flags != rgn->flags); 651 nr_new++; 652 if (insert) 653 memblock_insert_region(type, idx++, base, 654 rbase - base, nid, 655 flags); 656 } 657 /* area below @rend is dealt with, forget about it */ 658 base = min(rend, end); 659 } 660 661 /* insert the remaining portion */ 662 if (base < end) { 663 nr_new++; 664 if (insert) 665 memblock_insert_region(type, idx, base, end - base, 666 nid, flags); 667 } 668 669 if (!nr_new) 670 return 0; 671 672 /* 673 * If this was the first round, resize array and repeat for actual 674 * insertions; otherwise, merge and return. 675 */ 676 if (!insert) { 677 while (type->cnt + nr_new > type->max) 678 if (memblock_double_array(type, obase, size) < 0) 679 return -ENOMEM; 680 insert = true; 681 goto repeat; 682 } else { 683 memblock_merge_regions(type); 684 return 0; 685 } 686} 687
608 行,保证 base+size 不会超过 phys max addr,即不会超过 64 bits 表示最大无符号数值。 612 ~ 613 对size 进行检查。
616 ~ 624 行,数组为空的情形的处理。判断条件是 type->ranges[0].size == 0.
625 行 repeat 标签和 676 ~ 681行进行配合。第一次进入, insert = false , 执行 676 ~ 681 段代码,设置insert 为true, 然后goto repeat 再执行一边。
第一遍,得到 nr_new ,如果 type->cnt + nr_new 超过 type->max ,则 使用函数 memblock_double_array 将 ranges 数组容量 翻倍。
第二遍,nr_new 和第一遍一样,仅仅是需要 insert 的地方,调用 memblock_insert_region,最后调用 memblock_merge_regions。
634 ~ 659 行逻辑, for 循环 idx < type->cnt 时进行:
因为是从小地址开始寻找的。
638 ~ 639 , 带add 地址块 的当前idx所指地址块下方,需要停止循环。
640 ~ 641,带add 地址块,在当前idx 所指地址块上方,继续检查下一个。
上面两个条件都不满足,则说明带插入块的 end > rbase && base < rend 。
如果 rbase > base , 则将 部分 拼接到 idx 指向地址块的下方。653 idx ++ ,先传递进入 idx, 完成插入,然后 ++ 跳过刚才插入的内存块,到range 内存块。
658 行,保持 end 的值不变,不断更新(提高) base 的值。
如果 end 值大于 当前 range end, base 指向 range end , range end 和 end 组成的新 块和下一个 idx 进行比较。
如果当前 end 小于 当前 rend, 则baes == end, 662 行的 if 不会进入。
memblock_alloc
分配出来的是 虚拟地址,但是 什么时间 为 这个虚拟地址 建立的页表呢 ?
memblock_alloc 调用必须要在 paging_init 之后。
paging_init 调用在 arm64_memblock_init 之后。
顺序:arm64_memblock_init -> paging_init -> memblock_alloc
arm64_memblock_init 收集物理内存的分布和预留情况 -> paging_init 根据 memblock 中的信息,对所有 内核 线性区域 VA 地址段 映射到的 物理地址段 【如果存在】就建立页表。
memblock_alloc 分配得到的 VA 地址 其实 已经在 paging_init 里面建立过页表了,所以可以直接使用 memset 对齐进行 访问 填充。
407static inline void * __init memblock_alloc(phys_addr_t size, phys_addr_t align) 408{ 409 return memblock_alloc_try_nid(size, align, MEMBLOCK_LOW_LIMIT, 410 MEMBLOCK_ALLOC_ACCESSIBLE, NUMA_NO_NODE); 411}
max_addr MEMBLOCK_ALLOC_ACCESSIBLE to allocate only from memory limited by memblock.current_limit value
memblock_alloc_try_nid
1589void * __init memblock_alloc_try_nid( 1590 phys_addr_t size, phys_addr_t align, 1591 phys_addr_t min_addr, phys_addr_t max_addr, 1592 int nid) 1593{ 1594 void *ptr; 1595 1596 memblock_dbg("%s: %llu bytes align=0x%llx nid=%d from=%pa max_addr=%pa %pS\n", 1597 __func__, (u64)size, (u64)align, nid, &min_addr, 1598 &max_addr, (void *)_RET_IP_); 1599 ptr = memblock_alloc_internal(size, align, 1600 min_addr, max_addr, nid, false); 1601 if (ptr) 1602 memset(ptr, 0, size); 1603 1604 return ptr; 1605}
memblock_alloc_internal
找到 起始地址, alloc,然后 使用 phys_to_virt 转换到 虚拟地址,返回这个虚拟地址。
1464 */ 1465static void * __init memblock_alloc_internal( 1466 phys_addr_t size, phys_addr_t align, 1467 phys_addr_t min_addr, phys_addr_t max_addr, 1468 int nid, bool exact_nid) 1469{ 1470 phys_addr_t alloc; 1480 if (max_addr > memblock.current_limit) 1481 max_addr = memblock.current_limit; 1482 1483 alloc = memblock_alloc_range_nid(size, align, min_addr, max_addr, nid, 1484 exact_nid); 1485 1486 /* retry allocation without lower limit */ 1487 if (!alloc && min_addr) 1488 alloc = memblock_alloc_range_nid(size, align, 0, max_addr, nid, 1489 exact_nid); 1490 1491 if (!alloc) 1492 return NULL; 1493 1494 return phys_to_virt(alloc); 1495}
memblock_alloc_range_nid
返回 0 或者 找到的起始地址 start address
1351phys_addr_t __init memblock_alloc_range_nid(phys_addr_t size, 1352 phys_addr_t align, phys_addr_t start, 1353 phys_addr_t end, int nid, 1354 bool exact_nid) 1355{ 1356 enum memblock_flags flags = choose_memblock_flags(); 1357 phys_addr_t found; 1358 1359 if (WARN_ONCE(nid == MAX_NUMNODES, "Usage of MAX_NUMNODES is deprecated. Use NUMA_NO_NODE instead\n")) 1360 nid = NUMA_NO_NODE; 1361 1362 if (!align) { 1363 /* Can't use WARNs this early in boot on powerpc */ 1364 dump_stack(); 1365 align = SMP_CACHE_BYTES; 1366 } 1368again: 1369 found = memblock_find_in_range_node(size, align, start, end, nid, 1370 flags); 1371 if (found && !memblock_reserve(found, size)) 1372 goto done; 1373 1374 if (nid != NUMA_NO_NODE && !exact_nid) { 1375 found = memblock_find_in_range_node(size, align, start, 1376 end, NUMA_NO_NODE, 1377 flags); 1378 if (found && !memblock_reserve(found, size)) 1379 goto done; 1380 } 1382 if (flags & MEMBLOCK_MIRROR) { 1383 flags &= ~MEMBLOCK_MIRROR; 1384 pr_warn("Could not allocate %pap bytes of mirrored memory\n", 1385 &size); 1386 goto again; 1387 } 1389 return 0; 1390 1391done: 1392 /* Skip kmemleak for kasan_init() due to high volume. */ 1393 if (end != MEMBLOCK_ALLOC_KASAN) 1394 /* 1395 * The min_count is set to 0 so that memblock allocated 1396 * blocks are never reported as leaks. This is because many 1397 * of these blocks are only referred via the physical 1398 * address which is not looked up by kmemleak. 1399 */ 1400 kmemleak_alloc_phys(found, size, 0, 0); 1401 1402 return found; 1403} 1404
1369 行,调用 memblock_find_in_range_node找 合适的 range
1370行,找到合适的,found 不为0,立即调用 memblock_reserve 函数,将这块 地址区域 放到 reserve 数组当中。
choose_memblock_flags
155static bool system_has_some_mirror __initdata_memblock = false; 160static enum memblock_flags __init_memblock choose_memblock_flags(void) 161{ 162 return system_has_some_mirror ? MEMBLOCK_MIRROR : MEMBLOCK_NONE; 163}
memblock_find_in_range_node
返回0 或者 找到的 start address
289static phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size, 290 phys_addr_t align, phys_addr_t start, 291 phys_addr_t end, int nid, 292 enum memblock_flags flags) 293{ 294 phys_addr_t kernel_end, ret; 295 296 /* pump up @end */ 297 if (end == MEMBLOCK_ALLOC_ACCESSIBLE || 298 end == MEMBLOCK_ALLOC_KASAN) 299 end = memblock.current_limit; 300 301 /* avoid allocating the first page */ 302 start = max_t(phys_addr_t, start, PAGE_SIZE); 303 end = max(start, end); 304 kernel_end = __pa_symbol(_end); 336 return __memblock_find_range_top_down(start, end, size, align, nid,flags); 338}
memblock_find_range_top_down
返回 0 ,或者 cand
242static phys_addr_t __init_memblock 243__memblock_find_range_top_down(phys_addr_t start, phys_addr_t end, 244 phys_addr_t size, phys_addr_t align, int nid, 245 enum memblock_flags flags) 246{ 247 phys_addr_t this_start, this_end, cand; 248 u64 i; 249 250 for_each_free_mem_range_reverse(i, nid, flags, &this_start, &this_end, 251 NULL) { 252 this_start = clamp(this_start, start, end); 253 this_end = clamp(this_end, start, end); 254 255 if (this_end < size) 256 continue; 257 258 cand = round_down(this_end - size, align); 259 if (cand >= this_start) 260 return cand; 261 } 262 263 return 0; 264}
250 行,for each free mem range reverse , 遍历 memblock.memory 中,找到 不在 memblock.reserved 里面的 。从最后面开始向下找。
252 行, clamp(value, low, high) 先 temp = max ( value , low ) ; 然后 min ( temp , high ) 。
255 ~ 256 行,this_end < size时,continue。 因为 this_end < size 时, ths_end - this_start 肯定 小于 size 。 所以只能继续 找。
258行,candinate start 计算:this_end - size 得到起始地址, 然后 按照 align 对齐到 cand 。
259 ~ 260 cand >= this_start 后,这个地址合适,返回cand 。
330/** 331 * for_each_free_mem_range_reverse - rev-iterate through free memblock areas 332 * @i: u64 used as loop variable 333 * @nid: node selector, %NUMA_NO_NODE for all nodes 334 * @flags: pick from blocks based on memory attributes 335 * @p_start: ptr to phys_addr_t for start address of the range, can be %NULL 336 * @p_end: ptr to phys_addr_t for end address of the range, can be %NULL 337 * @p_nid: ptr to int for nid of the range, can be %NULL 338 * 339 * Walks over free (memory && !reserved) areas of memblock in reverse 340 * order. Available as soon as memblock is initialized. 341 */ 342#define for_each_free_mem_range_reverse(i, nid, flags, p_start, p_end, \ 343 p_nid) \ 344 __for_each_mem_range_rev(i, &memblock.memory, &memblock.reserved, \ 345 nid, flags, p_start, p_end, p_nid)