MIGRATE_ISOLATE浅析

在linux内核的migratetype中,有MIGRATE_ISOLATE这样一个内存迁移类型,注释里写着can’t allocate from here,这个是怎么做到的呢?他又有什么作用呢?

本文分析基于linux4.19.195
可以看到内核里,是函数set_migratetype_isolate将一个内存块设置成了MIGRATE_ISOLATE这个类型。

static int set_migratetype_isolate(struct page *page, int migratetype,
				bool skip_hwpoisoned_pages)
{
	struct zone *zone;
	unsigned long flags, pfn;
	struct memory_isolate_notify arg;
	int notifier_ret;
	int ret = -EBUSY;

	zone = page_zone(page);

	spin_lock_irqsave(&zone->lock, flags);

	/*
	 * We assume the caller intended to SET migrate type to isolate.
	 * If it is already set, then someone else must have raced and
	 * set it before us.  Return -EBUSY
	 */
	if (is_migrate_isolate_page(page))
		goto out;

	pfn = page_to_pfn(page);
	arg.start_pfn = pfn;
	arg.nr_pages = pageblock_nr_pages;
	arg.pages_found = 0;

	/*
	 * It may be possible to isolate a pageblock even if the
	 * migratetype is not MIGRATE_MOVABLE. The memory isolation
	 * notifier chain is used by balloon drivers to return the
	 * number of pages in a range that are held by the balloon
	 * driver to shrink memory. If all the pages are accounted for
	 * by balloons, are free, or on the LRU, isolation can continue.
	 * Later, for example, when memory hotplug notifier runs, these
	 * pages reported as "can be isolated" should be isolated(freed)
	 * by the balloon driver through the memory notifier chain.
	 */
	notifier_ret = memory_isolate_notify(MEM_ISOLATE_COUNT, &arg);
	notifier_ret = notifier_to_errno(notifier_ret);
	if (notifier_ret)
		goto out;
	/*
	 * FIXME: Now, memory hotplug doesn't call shrink_slab() by itself.
	 * We just check MOVABLE pages.
	 */
	if (!has_unmovable_pages(zone, page, arg.pages_found, migratetype,
				 skip_hwpoisoned_pages))
		ret = 0;

	/*
	 * immobile means "not-on-lru" pages. If immobile is larger than
	 * removable-by-driver pages reported by notifier, we'll fail.
	 */

out:
	if (!ret) {
		unsigned long nr_pages;
		int mt = get_pageblock_migratetype(page);

		set_pageblock_migratetype(page, MIGRATE_ISOLATE);
		zone->nr_isolate_pageblock++;
		nr_pages = move_freepages_block(zone, page, MIGRATE_ISOLATE,
									NULL);

		__mod_zone_freepage_state(zone, -nr_pages, mt);
	}

	spin_unlock_irqrestore(&zone->lock, flags);
	if (!ret)
		drain_all_pages(zone);
	return ret;
}

函数首先判断这个内存块是不是MIGRATE_ISOLATE类型的话,如果是的话,直接退出即可。
然后,调用has_unmovable_pages,查看是否有unmovable的页,如果有的话,那可能无法通过migration等方法把这个页隔离出来,从而实现“can’t allocate from here”的效果。
如果前面检查都通过了的话,那么就会调用move_freepages_block()函数,把这个内存块在buddy中的所有空闲页,都挪到zone->free_area[order].free_list[MIGRATE_ISOLATE]链表上去。
那么,这个链表上的page,是不会被buddy分配出去的吗?
答案是yes,具体怎么做到的,可以看函数prepare_alloc_pages()的实现。

static inline bool prepare_alloc_pages(gfp_t gfp_mask, unsigned int order,
		int preferred_nid, nodemask_t *nodemask,
		struct alloc_context *ac, gfp_t *alloc_mask,
		unsigned int *alloc_flags)
{
	ac->high_zoneidx = gfp_zone(gfp_mask);
	ac->zonelist = node_zonelist(preferred_nid, gfp_mask);
	ac->nodemask = nodemask;
	ac->migratetype = gfpflags_to_migratetype(gfp_mask);

	if (cpusets_enabled()) {
		*alloc_mask |= __GFP_HARDWALL;
		if (!ac->nodemask)
			ac->nodemask = &cpuset_current_mems_allowed; //cpuset.mem相关
		else
			*alloc_flags |= ALLOC_CPUSET;
	}

	fs_reclaim_acquire(gfp_mask);
	fs_reclaim_release(gfp_mask);

	might_sleep_if(gfp_mask & __GFP_DIRECT_RECLAIM);

	if (should_fail_alloc_page(gfp_mask, order))
		return false;

	if (IS_ENABLED(CONFIG_CMA) && ac->migratetype == MIGRATE_MOVABLE)
		*alloc_flags |= ALLOC_CMA;

	return true;
}

结构体alloc_context中的migratetype决定了初始分配的migratetype,这个成员是由函数gfpflags_to_migratetype()赋值的,从函数的实现可以很清晰的看到,只有可能返回__GFP_RECLAIMABLE、__GFP_MOVABLE或者0,0也就代表了MIGRATE_UNMOVABLE,对,没有MIGRATE_ISOLATE。通过函数gfpflags_to_migratetype()的实现,我们也能清楚的看到,enum migratetype里迁移类型的顺序,是和__GFP_RECLAIMABLE、__GFP_MOVABLE的值对应的上的。
那么,如果初始分配的migratetype无法分配出内存,fallback的话会怎么样呢?
__rmqueue_fallback()函数的实现给了答案,该函数会调用find_suitable_fallback函数做fallback migrate type的赋值。函数很简单,就是查fallbacks[][]这个表格。

static int fallbacks[MIGRATE_TYPES][4] = {
	[MIGRATE_UNMOVABLE]   = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,   MIGRATE_TYPES },
	[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,   MIGRATE_MOVABLE,   MIGRATE_TYPES },
	[MIGRATE_MOVABLE]     = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_TYPES },
#ifdef CONFIG_CMA
	[MIGRATE_CMA]         = { MIGRATE_TYPES }, /* Never used */
#endif
#ifdef CONFIG_MEMORY_ISOLATION
	[MIGRATE_ISOLATE]     = { MIGRATE_TYPES }, /* Never used */
#endif
};

无需做任何解释,显然是无法从MIGRATE_ISOLATE这个类型里分配出页了。
那MIGRATE_ISOLATE的作用是什么?我们看看谁会用set_migratetype_isolate()这个函数。
挑个最常见的例子,alloc_contig_range()这个函数就会调用set_migratetype_isolate(),那么系统会在什么时候调用alloc_contig_range()呢?一个很常见的场景,就是我们往/sys/kernel/mm/hugepages/hugepages-xxx/nr_hugepages写入数值申请预留大页的时候。alloc_contig_range()这个函数的作用是:tries to allocate given range of pages。那他怎么实现的呢?

int alloc_contig_range(unsigned long start, unsigned long end,
		       unsigned migratetype, gfp_t gfp_mask)
{
	****
	start_isolate_page_range()
	__alloc_contig_migrate_range()
	****
}

这个函数要实现的事分配一段连续的物理内存,但是系统运行起来后,内存碎片的很常见的,那怎么办呢?函数alloc_contig_range()首先调用start_isolate_page_range,将相关内存块配置成MIGRATE_ISOLATE,保证待申请范围里的内存,不会被buddy分配出去。然后,通过__alloc_contig_migrate_range函数,将那些待申请范围里的已经被分配出去的内存,尽可能的通过迁移(页迁移可以参考博客)的方法回收回来。可以看到,如果没有MIGRATE_ISOLATE,那么在__alloc_contig_migrate_range做迁移的过程中,还在buddy中的内存如果被分配出去了,那__alloc_contig_migrate_range()函数做的事情也是无用功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值