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

  49struct memblock_region {
  50        phys_addr_t base;   //base address of the region
  51        phys_addr_t size;
  52        enum memblock_flags flags;
  53#ifdef CONFIG_NEED_MULTIPLE_NODES
  54        int nid;
  55#endif
  56};
 

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_type

  66struct memblock_type {
  67        unsigned long cnt;  //number of regions
  68        unsigned long max;  //size of the allocated array
  69        phys_addr_t total_size;  //size of all regions
  70        struct memblock_region *regions;  //array of regions
  71        char *name;
  72};
  73

memblock_type 用于描述在当前的memblock中,同类型的 memory region 的数量。参见 memblock.c 中静态数组定义。

 

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 增加才能改变。

current_limit = MEMBLOCK_ALLOC_ANYWHERE , 为  MEMBLOCK_ALLOC_ANYWHERE, membloc.h 中 定义 为    (~(phys_addr_t)0) ,很大的值。
 
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)
 
posted @ 2022-03-27 11:06  张志伟122  阅读(428)  评论(0编辑  收藏  举报