Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bbc76be6 authored by Wen Congyang's avatar Wen Congyang Committed by Linus Torvalds
Browse files

memory-hotplug: remove redundant codes



offlining memory blocks and checking whether memory blocks are offlined
are very similar.  This patch introduces a new function to remove
redundant codes.

Signed-off-by: default avatarWen Congyang <wency@cn.fujitsu.com>
Signed-off-by: default avatarTang Chen <tangchen@cn.fujitsu.com>
Reviewed-by: default avatarKamezawa Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Jiang Liu <jiang.liu@huawei.com>
Cc: Jianguo Wu <wujianguo@huawei.com>
Cc: Lai Jiangshan <laijs@cn.fujitsu.com>
Cc: Wu Jianguo <wujianguo@huawei.com>
Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 6677e3ea
Loading
Loading
Loading
Loading
+82 −47
Original line number Diff line number Diff line
@@ -1380,20 +1380,26 @@ int offline_pages(unsigned long start_pfn, unsigned long nr_pages)
	return __offline_pages(start_pfn, start_pfn + nr_pages, 120 * HZ);
}

int remove_memory(u64 start, u64 size)
/**
 * walk_memory_range - walks through all mem sections in [start_pfn, end_pfn)
 * @start_pfn: start pfn of the memory range
 * @end_pfn: end pft of the memory range
 * @arg: argument passed to func
 * @func: callback for each memory section walked
 *
 * This function walks through all present mem sections in range
 * [start_pfn, end_pfn) and call func on each mem section.
 *
 * Returns the return value of func.
 */
static int walk_memory_range(unsigned long start_pfn, unsigned long end_pfn,
		void *arg, int (*func)(struct memory_block *, void *))
{
	struct memory_block *mem = NULL;
	struct mem_section *section;
	unsigned long start_pfn, end_pfn;
	unsigned long pfn, section_nr;
	int ret;
	int return_on_error = 0;
	int retry = 0;

	start_pfn = PFN_DOWN(start);
	end_pfn = start_pfn + PFN_DOWN(size);

repeat:
	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
		section_nr = pfn_to_section_nr(pfn);
		if (!present_section_nr(section_nr))
@@ -1410,22 +1416,76 @@ int remove_memory(u64 start, u64 size)
		if (!mem)
			continue;

		ret = offline_memory_block(mem);
		ret = func(mem, arg);
		if (ret) {
			if (return_on_error) {
			kobject_put(&mem->dev.kobj);
			return ret;
			} else {
				retry = 1;
			}
		}
	}

	if (mem)
		kobject_put(&mem->dev.kobj);

	if (retry) {
		return_on_error = 1;
	return 0;
}

/**
 * offline_memory_block_cb - callback function for offlining memory block
 * @mem: the memory block to be offlined
 * @arg: buffer to hold error msg
 *
 * Always return 0, and put the error msg in arg if any.
 */
static int offline_memory_block_cb(struct memory_block *mem, void *arg)
{
	int *ret = arg;
	int error = offline_memory_block(mem);

	if (error != 0 && *ret == 0)
		*ret = error;

	return 0;
}

static int is_memblock_offlined_cb(struct memory_block *mem, void *arg)
{
	int ret = !is_memblock_offlined(mem);

	if (unlikely(ret))
		pr_warn("removing memory fails, because memory "
			"[%#010llx-%#010llx] is onlined\n",
			PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)),
			PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1))-1);

	return ret;
}

int remove_memory(u64 start, u64 size)
{
	unsigned long start_pfn, end_pfn;
	int ret = 0;
	int retry = 1;

	start_pfn = PFN_DOWN(start);
	end_pfn = start_pfn + PFN_DOWN(size);

	/*
	 * When CONFIG_MEMCG is on, one memory block may be used by other
	 * blocks to store page cgroup when onlining pages. But we don't know
	 * in what order pages are onlined. So we iterate twice to offline
	 * memory:
	 * 1st iterate: offline every non primary memory block.
	 * 2nd iterate: offline primary (i.e. first added) memory block.
	 */
repeat:
	walk_memory_range(start_pfn, end_pfn, &ret,
			  offline_memory_block_cb);
	if (ret) {
		if (!retry)
			return ret;

		retry = 0;
		ret = 0;
		goto repeat;
	}

@@ -1443,38 +1503,13 @@ int remove_memory(u64 start, u64 size)
	 * memory blocks are offlined.
	 */

	mem = NULL;
	for (pfn = start_pfn; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
		section_nr = pfn_to_section_nr(pfn);
		if (!present_section_nr(section_nr))
			continue;

		section = __nr_to_section(section_nr);
		/* same memblock? */
		if (mem)
			if ((section_nr >= mem->start_section_nr) &&
			    (section_nr <= mem->end_section_nr))
				continue;

		mem = find_memory_block_hinted(section, mem);
		if (!mem)
			continue;

		ret = is_memblock_offlined(mem);
		if (!ret) {
			pr_warn("removing memory fails, because memory "
				"[%#010llx-%#010llx] is onlined\n",
				PFN_PHYS(section_nr_to_pfn(mem->start_section_nr)),
				PFN_PHYS(section_nr_to_pfn(mem->end_section_nr + 1)) - 1);

			kobject_put(&mem->dev.kobj);
	ret = walk_memory_range(start_pfn, end_pfn, NULL,
				is_memblock_offlined_cb);
	if (ret) {
		unlock_memory_hotplug();
		return ret;
	}
	}

	if (mem)
		kobject_put(&mem->dev.kobj);
	unlock_memory_hotplug();

	return 0;