内核杂谈——揭开linux内核连续物理内存CMA的面纱

之前追CMA代码的时候,写了一篇文章基于ARM32的 linux内存调节之CMA 。今天就从使用的角度来看看CMA和普通内存的区别。默认CONFIG_CMA

尝试思考几个问题,来揭开面纱吧。

CMA在未加入内存的时候,是否是存放在memblock.reserved region中?

众所周知CMA是由伙伴系统管理的,何以见得?

伙伴系统如何对这部分页做分配处理的?

当CMA要被使用的时候,如何真正变成CMA?

目录

第一个问题

第二个问题

第三个问题

第四个问题


简要回顾下上一篇博文

在设备树中 /reserved-memory 节点下定义一块reserved内存后,开机启动内核在 early_init_fdt_scan_reserved_mem 中会去获取这段reserved 内存;或者在cmaline中定义了CMA(见early_cma),会在dma_contiguous_reserve初始这段CMA。在ARM64中,这段reserved空间不会像ARM32一样,单独去做映射。

 

第一个问题

在 early_init_fdt_scan_reserved_mem 中 通过函数__fdt_scan_reserved_mem中的__reserved_mem_reserve_reg会将设备数中定义的reserved mem添加到 memblock.reserved region中。后面会一步步将这段内存添加到reserved-mem[],其次cma_areas[].

在dma_contiguous_reserve中会解析cmdline参数,通过函数dma_contiguous_reserve将解析到的内存添加到memblock.reserved region中。

 

第二个问题

1 在内存初始化的时候第一次看见CMA的影子是在 zone_init_free_lists 中,free_area每个order中初始了一块 MIGRATE_CMA 链表,用于存放迁移类型为CMA的页。既然内核都做了这个打算,肯定是对CMA有想法的。

获取到的CMA内存块会存放在数组 cam_areas 中。函数 cma_init_reserved_areas 使用 core_initcall 加载,会将cma_areas里的每个CMA块以page的方式释放到free_area,并且会清除page的reserved标记,并设置迁移类型为MIGRATE_CMA,最终CMA的内存存放在了伙伴系统中,因为看到了__free_pages。注意:释放memblock到伙伴系统的时候,是不会将reserved region中的page添加到free_area的

cma_init_reserved_areas:
    cma_activate_area:
        init_cma_reserved_pageblock:
            __free_page

2 cma 内存释放,同样可以找到free_page的影子。

cma_release:
    free_contig_range:
    {
	unsigned int count = 0;
	for (; nr_pages--; pfn++) {
		struct page *page = pfn_to_page(pfn);

		count += page_count(page) != 1;
		__free_page(page);
	}
    }

 

第三个问题

从分配使用角度:我们从__alloc_pages_nodemask开始看起,找到 get_page_from_freelist。一层层拨开洋葱皮一样找到分配page的函数__rmqueue。

static __always_inline struct page *
__rmqueue(struct zone *zone, unsigned int order, int migratetype,
						unsigned int alloc_flags)
{
	struct page *page;

retry:
//A计划:从free_area中找到目标order的目标migratetype链表获取page
	page = __rmqueue_smallest(zone, order, migratetype);
	if (unlikely(!page)) {
//B计划:A计划失败拿不到页,如果migratetype == MIGRATE_MOVABLE,那可以从CMA中获取page
    		if (migratetype == MIGRATE_MOVABLE)
			page = __rmqueue_cma_fallback(zone, order);
//C计划:B计划失败,就和CMA没啥关系了,CMA不参与备用选择。
		if (!page && __rmqueue_fallback(zone, order, migratetype,
								alloc_flags))
			goto retry;
	}

	return page;
}

我们可以找到第一次页分配失败会尝试去从__rmqueue_cma_fallback获取页,只不过是以MIGRATE_CMA来分配页面,注意分配到的页属性不再是MIGRATE_MOVABLE。

虽然CMA交给了伙伴系统管理,但是使用它的资源还要遵守它的规定,毕竟生来不是服务于伙伴系统的,而是为大内存设备准备的。

从分配水位角度:get_page_from_freelist在分配页前先会对水位做处理。在zone_watermark_fast中会找到cma_pages,分配页的时候默认不会算上CMA页第四个问题:

static inline bool zone_watermark_fast(struct zone *z, unsigned int order,
		unsigned long mark, int classzone_idx, unsigned int alloc_flags)
{
	long free_pages = zone_page_state(z, NR_FREE_PAGES);
	long cma_pages = 0;

#ifdef CONFIG_CMA
	if (!(alloc_flags & ALLOC_CMA))
		cma_pages = zone_page_state(z, NR_FREE_CMA_PAGES);
#endif
//order为0的情况,去除cma_pages
	if (!order && (free_pages - cma_pages) > mark + z->lowmem_reserve[classzone_idx])
		return true;

	return __zone_watermark_ok(z, order, mark, classzone_idx, alloc_flags,
					free_pages);
}

空闲页数是会剔除cma_pages的。order不为0也是同样的判断。所以有时候需要注意,一些log中打印的free_pages会大于min水位,但还是分配页失败,这时需要看看cma_pages是多少了。

 

第四个问题

看一下CMA分配函数 cma_alloc,具体的分配动作在 alloc_contig_range 中。

struct page *cma_alloc(struct cma *cma, size_t count, unsigned int align,
		       bool no_warn)
{
                pfn = cma->base_pfn + (bitmap_no << cma->order_per_bit); //首先获取目标CMA的区域
		mutex_lock(&cma_mutex);
//隔离这部分页,将被使用的页面迁移出去,腾出CMA的空间
    		ret = alloc_contig_range(pfn, pfn + count, MIGRATE_CMA,
				     GFP_KERNEL | (no_warn ? __GFP_NOWARN : 0));
}

alloc_contig_range 中 会有 migrate_pages 将旧页面的内容复制到新页面,然后使用旧页面作为cma使用。这部分涉及内存迁移。

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值