linux中memblock的使用

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

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

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

//memblock中的flag
enum {
	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_MAP
	int 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_REGIONS	128
/*可分配内存区域*/
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号为nid
int __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);
}

标记已经使用方法:

扫描二维码关注公众号,回复: 1810823 查看本文章
//把基址为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是否可以resize
	if (!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,还是reserve
	if (type == &memblock.memory)
		in_slab = &memblock_memory_in_slab;
	else
		in_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为0
		if (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->regions
	old_array = type->regions;
//把new_array赋值给type->regions
	type->regions = new_array;
//把最大能记录的数值乘以2
	type->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为nid
	memblock_set_region_node(rgn, nid);
//记录的内存区域数量加1
	type->cnt++;
//记录的内存区域的总大小更新
	type->total_size += size;
}

合并邻居区域的操作:

//合并相邻的内存区域
static void __init_memblock memblock_merge_regions(struct memblock_type *type)
{
	int i = 0;
//循环遍历这个类型的内存区域(memory,reserve两种)中的所有region
	while (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上,然后把记录的区域数减1
		this->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尾端开始查找。



猜你喜欢

转载自blog.csdn.net/u010383937/article/details/78595874