linux中memblock的使用

来源:互联网 发布:沙盘软件多开器 编辑:程序博客网 时间:2024/06/14 23:44

memblock是linux用于管理内存的一种机制。

接下来说一些这个机制的初始化,以及其操作方法。

先看一下memblock中使用的结构体:

//memblock中的flagenum {MEMBLOCK_NONE= 0x0,/* No special request */MEMBLOCK_HOTPLUG= 0x1,/* hotpluggable region */MEMBLOCK_MIRROR= 0x2,/* mirrored region */};

struct memblock_region {phys_addr_t base;     //region的基地址phys_addr_t size;     //region的大小unsigned long flags;  //region的标志#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAPint nid;             //属于哪个noid#endif};

struct memblock_type {unsigned long cnt;/*当前regions的数量 */unsigned long max;/*当前集合中可记录的n内存区域的最大大小*/phys_addr_t total_size;/*region的总大小*/struct memblock_region *regions; //指向内存区域};
struct memblock {bool bottom_up;  /*表示分配器的分配方式,true从低地址向高地址分配,false则相反*/phys_addr_t current_limit; //指出n内存块的大小限制,struct memblock_type memory;//可用内存struct memblock_type reserved;//已分配内存};

memblock的初始化:
#define INIT_MEMBLOCK_REGIONS128/*可分配内存区域*/static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;/*已分配内存区域*/static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_REGIONS] __initdata_memblock;struct memblock memblock __initdata_memblock = {.memory.regions= memblock_memory_init_regions,.memory.cnt= 1,/* empty dummy entry */.memory.max= INIT_MEMBLOCK_REGIONS,.reserved.regions= memblock_reserved_init_regions,.reserved.cnt= 1,/* empty dummy entry */.reserved.max= INIT_MEMBLOCK_REGIONS,.bottom_up= false,.current_limit= MEMBLOCK_ALLOC_ANYWHERE,};

memblock的操作函数:

加入操作

//把基址为base,大小为size的内存区域加入memblock中,并标记node号为nidint __init_memblock memblock_add_node(phys_addr_t base, phys_addr_t size,int nid){return memblock_add_range(&memblock.memory, base, size, nid, 0);}
移除操作

//从memblock中去除基址为base,大小为size的区域int __init_memblock memblock_remove(phys_addr_t base, phys_addr_t size){return memblock_remove_range(&memblock.memory, base, size);}

释放操作:

//释放一段基址为base,大小为size的区域int __init_memblock memblock_free(phys_addr_t base, phys_addr_t size){//调用memblock_remove_range把这个区域删除return memblock_remove_range(&memblock.reserved, base, size);}

标记已经使用方法:

//把基址为base,大小为size的区域设置为已分配int __init_memblock memblock_reserve(phys_addr_t base, phys_addr_t size){return memblock_reserve_region(base, size, MAX_NUMNODES, 0);}

内存分配函数

phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align){return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);}

一: add函数的实现

/** 把一块内存区域放入到memblock中(memory类型)*这个函数执行分为两步:*第一步:判断原来的记录数加上要插入的区域数后是否大于最大可记录数,如果大于就把type翻倍*第二步:type翻倍后,再进行插入操作*/int __init_memblock memblock_add_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size,int nid, unsigned long flags){bool insert = false;phys_addr_t obase = base;phys_addr_t end = base + memblock_cap_size(base, &size);int i, nr_new;//如果大小为0,则直接返回if (!size)return 0;//type为空时,就直接插入到regions[0]中。也就是插入的第一块if (type->regions[0].size == 0) {WARN_ON(type->cnt != 1 || type->total_size);type->regions[0].base = base;type->regions[0].size = size;type->regions[0].flags = flags;memblock_set_region_node(&type->regions[0], nid);type->total_size = size;return 0;}repeat:base = obase;nr_new = 0;//对type中所有的region进行查询,看看有没有和要插入区域重叠的部分for (i = 0; i < type->cnt; i++) {struct memblock_region *rgn = &type->regions[i];phys_addr_t rbase = rgn->base;phys_addr_t rend = rbase + rgn->size;if (rbase >= end)break;if (rend <= base)continue;//运行到这里,说明rbase<end,而rbase>base,有重叠部分//nr_new记录要插入的区域的数if (rbase > base) {nr_new++;if (insert)//把这个区域插入memblock_insert_region(type, i++, base,       rbase - base, nid,       flags);}//更新base为新插入区域的末端base = min(rend, end);}/*如果base<end,则说明还有部分没有插入*/if (base < end) {nr_new++;if (insert)memblock_insert_region(type, i, base, end - base,       nid, flags);}//插入操作分为两步,这里是第一部分/**这里是当要插入的额区域加上原有的区域大于最大可记录数时,则把type翻倍,然后再插入*/if (!insert) {while (type->cnt + nr_new > type->max)if (memblock_double_array(type, obase, size) < 0)return -ENOMEM;insert = true;goto repeat;} else {memblock_merge_regions(type);return 0;}}

下面这个函数就是为参数中的type设置为原来的两倍大小,并在memblock重新为其分配了一块内存
static int __init_memblock memblock_double_array(struct memblock_type *type,phys_addr_t new_area_start,phys_addr_t new_area_size){struct memblock_region *new_array, *old_array;phys_addr_t old_alloc_size, new_alloc_size;phys_addr_t old_size, new_size, addr;int use_slab = slab_is_available();int *in_slab;//判断memblock是否可以resizeif (!memblock_can_resize)return -1;/*计算新的大小,使得新大小为原来的两倍*/old_size = type->max * sizeof(struct memblock_region);new_size = old_size << 1;//设置新大小的对齐格式old_alloc_size = PAGE_ALIGN(old_size);new_alloc_size = PAGE_ALIGN(new_size);//设置在哪个内存中memory,还是reserveif (type == &memblock.memory)in_slab = &memblock_memory_in_slab;elsein_slab = &memblock_reserved_in_slab;//目前还没有使用slab,是走else分支if (use_slab) {new_array = kmalloc(new_size, GFP_KERNEL);addr = new_array ? __pa(new_array) : 0;} else {//如果不是reserved类型的memblock,就设置new_area_start和new_area_size为0if (type != &memblock.reserved)new_area_start = new_area_size = 0;//在memblock中为查找一个大小为new_alloc_size的内存区域addr = memblock_find_in_range(new_area_start + new_area_size,memblock.current_limit,new_alloc_size, PAGE_SIZE);if (!addr && new_area_size)addr = memblock_find_in_range(0,min(new_area_start, memblock.current_limit),new_alloc_size, PAGE_SIZE);//用new_array记录新块的物理地址new_array = addr ? __va(addr) : NULL;}//把参数中的type->regions拷贝到new_array中memcpy(new_array, type->regions, old_size);//把new_array中从type->max地址后old_size长度的内存清零memset(new_array + type->max, 0, old_size);//使用old_array记录原来的type->regionsold_array = type->regions;//把new_array赋值给type->regionstype->regions = new_array;//把最大能记录的数值乘以2type->max <<= 1;if (*in_slab)kfree(old_array);//把原来的old_array释放掉else if  (old_array != memblock_memory_init_regions && old_array != memblock_reserved_init_regions)memblock_free(__pa(old_array), old_alloc_size);return 0;}

具体插入操作的函数

static void __init_memblock memblock_insert_region(struct memblock_type *type,int idx, phys_addr_t base,phys_addr_t size,int nid, unsigned long flags){//获取要插入的内存区域struct memblock_region *rgn = &type->regions[idx];//验证是否大于最大可记录的值BUG_ON(type->cnt >= type->max);//把rgn拷贝到rgn+1的地方memmove(rgn + 1, rgn, (type->cnt - idx) * sizeof(*rgn));//重新设置rgn的基址,大小,flag信息rgn->base = base;rgn->size = size;rgn->flags = flags;//设置rgn的对应的node为nidmemblock_set_region_node(rgn, nid);//记录的内存区域数量加1type->cnt++;//记录的内存区域的总大小更新type->total_size += size;}

合并邻居区域的操作:

//合并相邻的内存区域static void __init_memblock memblock_merge_regions(struct memblock_type *type){int i = 0;//循环遍历这个类型的内存区域(memory,reserve两种)中的所有regionwhile (i < type->cnt - 1) {struct memblock_region *this = &type->regions[i];struct memblock_region *next = &type->regions[i + 1];//如果不是相邻的,或者不是同一个node上的,或者flag不相同,则i+1,进行下一个if (this->base + this->size != next->base ||    memblock_get_region_node(this) !=    memblock_get_region_node(next) ||    this->flags != next->flags) {BUG_ON(this->base + this->size > next->base);i++;continue;}//运行到这里,说明可以合并//把this的大小扩展为this->size+next->size//并把next+1和区域赋值到next上,然后把记录的区域数减1this->size += next->size; memmove(next, next + 1, (type->cnt - (i + 2)) * sizeof(*next));type->cnt--;}}

二, remove操作:

int __init_memblock memblock_remove_range(struct memblock_type *type,phys_addr_t base, phys_addr_t size){int start_rgn, end_rgn;int i, ret;ret = memblock_isolate_range(type,base,size,&start_rgn, &end_rgn);if (ret)return ret;//调用memblock_remove_region进行删除操作for (i = end_rgn - 1; i >= start_rgn; i--)memblock_remove_region(type, i);return 0;}

static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r){/**把type管理的大小减少,并把后一个区域的内容拷贝到当前的区域*  然后把cnt减1*/type->total_size -= type->regions[r].size;memmove(&type->regions[r], &type->regions[r + 1],(type->cnt - (r + 1)) * sizeof(type->regions[r]));type->cnt--;//如果type为空,则把regions的基址,大小,flags清零/* Special case for empty arrays */if (type->cnt == 0) {WARN_ON(type->total_size != 0);type->cnt = 1;type->regions[0].base = 0;type->regions[0].size = 0;type->regions[0].flags = 0;memblock_set_region_node(&type->regions[0], MAX_NUMNODES);}}

三,free操作

free就是调用memblock_remove_region把区域进行删除


四, reserve操作

调用memblock_add_range把这个区域加入到reserve区域的内存中。


五,alloc操作

phys_addr_t __init memblock_alloc(phys_addr_t size, phys_addr_t align){return memblock_alloc_base(size, align, MEMBLOCK_ALLOC_ACCESSIBLE);}phys_addr_t __init memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr){phys_addr_t alloc;alloc = __memblock_alloc_base(size, align, max_addr);return alloc;}phys_addr_t __init __memblock_alloc_base(phys_addr_t size, phys_addr_t align, phys_addr_t max_addr){return memblock_alloc_base_nid(size, align, max_addr, NUMA_NO_NODE,       MEMBLOCK_NONE);}

static phys_addr_t __init memblock_alloc_base_nid(phys_addr_t size,phys_addr_t align, phys_addr_t max_addr,int nid, ulong flags){phys_addr_t found;if (WARN_ON(!align))align = __alignof__(long long);//设置对齐方式size = round_up(size, align);//在memblock中查找一个指定大小的区域found = memblock_find_in_range_node(size, align, 0, max_addr, nid,    flags);//如果找到了,把这个区域放入到reserve内存中。if (found && !memblock_reserve(found, size))return found;return 0;}

phys_addr_t __init_memblock 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 kernel_end, ret;//如果end是MEMBLOCK_ALLOC_ACCESSIBLE,表示在所有区域中查找if (end == MEMBLOCK_ALLOC_ACCESSIBLE)end = memblock.current_limit;start = max_t(phys_addr_t, start, PAGE_SIZE);end = max(start, end);//记录kernel加载的末地址kernel_end = __pa_symbol(_end);//如果botton_upe设置为true,则从上往下进行分配//在这里是从下往上分配if (memblock_bottom_up() && end > kernel_end) {phys_addr_t bottom_up_start;/* make sure we will allocate above the kernel */bottom_up_start = max(start, kernel_end);/* ok, try bottom-up allocation first */ret = __memblock_find_range_bottom_up(bottom_up_start, end,      size, align, nid, flags);if (ret)return ret;WARN_ONCE(1, "memblock: bottom-up allocation failed, "     "memory hotunplug may be affected\n");}return __memblock_find_range_top_down(start, end, size, align, nid,      flags);


static phys_addr_t __init_memblock__memblock_find_range_top_down(phys_addr_t start, phys_addr_t end,       phys_addr_t size, phys_addr_t align, int nid,       ulong flags){phys_addr_t this_start, this_end, cand;u64 i;//从memblock中倒着往前查找for_each_free_mem_range_reverse(i, nid, flags, &this_start, &this_end,NULL) {//clamp判断this_start是否在start到end之间this_start = clamp(this_start, start, end);this_end = clamp(this_end, start, end);if (this_end < size)continue;//page对齐cand = round_down(this_end - size, align);if (cand >= this_start)return cand;}return 0;}

从上往下分配static phys_addr_t __init_memblock__memblock_find_range_bottom_up(phys_addr_t start, phys_addr_t end,phys_addr_t size, phys_addr_t align, int nid,ulong flags){phys_addr_t this_start, this_end, cand;u64 i;//正着从memblock中查找for_each_free_mem_range(i, nid, flags, &this_start, &this_end, NULL) {this_start = clamp(this_start, start, end);this_end = clamp(this_end, start, end);cand = round_up(this_start, align);if (cand < this_end && this_end - cand >= size)return cand;}return 0;}

memblock的插入是按照地址从大到小进行插入的,所以从上往下查找就是正着查找memblock,而从下往上分配,则是从memblock尾端开始查找。



原创粉丝点击