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

Commit ed2f2400 authored by Zhang Zhen's avatar Zhang Zhen Committed by Linus Torvalds
Browse files

memory-hotplug: add sysfs valid_zones attribute



Currently memory-hotplug has two limits:

1. If the memory block is in ZONE_NORMAL, you can change it to
   ZONE_MOVABLE, but this memory block must be adjacent to ZONE_MOVABLE.

2. If the memory block is in ZONE_MOVABLE, you can change it to
   ZONE_NORMAL, but this memory block must be adjacent to ZONE_NORMAL.

With this patch, we can easy to know a memory block can be onlined to
which zone, and don't need to know the above two limits.

Updated the related Documentation.

[akpm@linux-foundation.org: use conventional comment layout]
[akpm@linux-foundation.org: fix build with CONFIG_MEMORY_HOTREMOVE=n]
[akpm@linux-foundation.org: remove unused local zone_prev]
Signed-off-by: default avatarZhang Zhen <zhenzhang.zhang@huawei.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: David Rientjes <rientjes@google.com>
Cc: Toshi Kani <toshi.kani@hp.com>
Cc: Yasuaki Ishimatsu <isimatu.yasuaki@jp.fujitsu.com>
Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com>
Cc: Wang Nan <wangnan0@huawei.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent cc71aba3
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -61,6 +61,14 @@ Users: hotplug memory remove tools
		http://www.ibm.com/developerworks/wikis/display/LinuxP/powerpc-utils


What:           /sys/devices/system/memory/memoryX/valid_zones
Date:           July 2014
Contact:	Zhang Zhen <zhenzhang.zhang@huawei.com>
Description:
		The file /sys/devices/system/memory/memoryX/valid_zones	is
		read-only and is designed to show which zone this memory
		block can be onlined to.

What:		/sys/devices/system/memoryX/nodeY
Date:		October 2009
Contact:	Linux Memory Management list <linux-mm@kvack.org>
+10 −1
Original line number Diff line number Diff line
@@ -155,6 +155,7 @@ Under each memory block, you can see 4 files:
/sys/devices/system/memory/memoryXXX/phys_device
/sys/devices/system/memory/memoryXXX/state
/sys/devices/system/memory/memoryXXX/removable
/sys/devices/system/memory/memoryXXX/valid_zones

'phys_index'      : read-only and contains memory block id, same as XXX.
'state'           : read-write
@@ -170,6 +171,15 @@ Under each memory block, you can see 4 files:
                    block is removable and a value of 0 indicates that
                    it is not removable. A memory block is removable only if
                    every section in the block is removable.
'valid_zones'     : read-only: designed to show which zones this memory block
		    can be onlined to.
		    The first column shows it's default zone.
		    "memory6/valid_zones: Normal Movable" shows this memoryblock
		    can be onlined to ZONE_NORMAL by default and to ZONE_MOVABLE
		    by online_movable.
		    "memory7/valid_zones: Movable Normal" shows this memoryblock
		    can be onlined to ZONE_MOVABLE by default and to ZONE_NORMAL
		    by online_kernel.

NOTE:
  These directories/files appear after physical memory hotplug phase.
@@ -408,7 +418,6 @@ node if necessary.
  - allowing memory hot-add to ZONE_MOVABLE. maybe we need some switch like
    sysctl or new control file.
  - showing memory block and physical device relationship.
  - showing memory block is under ZONE_MOVABLE or not
  - test and make it better memory offlining.
  - support HugeTLB page migration and offlining.
  - memmap removing at memory offline.
+42 −0
Original line number Diff line number Diff line
@@ -373,6 +373,45 @@ static ssize_t show_phys_device(struct device *dev,
	return sprintf(buf, "%d\n", mem->phys_device);
}

#ifdef CONFIG_MEMORY_HOTREMOVE
static ssize_t show_valid_zones(struct device *dev,
				struct device_attribute *attr, char *buf)
{
	struct memory_block *mem = to_memory_block(dev);
	unsigned long start_pfn, end_pfn;
	unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
	struct page *first_page;
	struct zone *zone;

	start_pfn = section_nr_to_pfn(mem->start_section_nr);
	end_pfn = start_pfn + nr_pages;
	first_page = pfn_to_page(start_pfn);

	/* The block contains more than one zone can not be offlined. */
	if (!test_pages_in_a_zone(start_pfn, end_pfn))
		return sprintf(buf, "none\n");

	zone = page_zone(first_page);

	if (zone_idx(zone) == ZONE_MOVABLE - 1) {
		/*The mem block is the last memoryblock of this zone.*/
		if (end_pfn == zone_end_pfn(zone))
			return sprintf(buf, "%s %s\n",
					zone->name, (zone + 1)->name);
	}

	if (zone_idx(zone) == ZONE_MOVABLE) {
		/*The mem block is the first memoryblock of ZONE_MOVABLE.*/
		if (start_pfn == zone->zone_start_pfn)
			return sprintf(buf, "%s %s\n",
					zone->name, (zone - 1)->name);
	}

	return sprintf(buf, "%s\n", zone->name);
}
static DEVICE_ATTR(valid_zones, 0444, show_valid_zones, NULL);
#endif

static DEVICE_ATTR(phys_index, 0444, show_mem_start_phys_index, NULL);
static DEVICE_ATTR(state, 0644, show_mem_state, store_mem_state);
static DEVICE_ATTR(phys_device, 0444, show_phys_device, NULL);
@@ -523,6 +562,9 @@ static struct attribute *memory_memblk_attrs[] = {
	&dev_attr_state.attr,
	&dev_attr_phys_device.attr,
	&dev_attr_removable.attr,
#ifdef CONFIG_MEMORY_HOTREMOVE
	&dev_attr_valid_zones.attr,
#endif
	NULL
};

+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ extern int zone_grow_waitqueues(struct zone *zone, unsigned long nr_pages);
extern int add_one_highpage(struct page *page, int pfn, int bad_ppro);
/* VM interface that may be used by firmware interface */
extern int online_pages(unsigned long, unsigned long, int);
extern int test_pages_in_a_zone(unsigned long, unsigned long);
extern void __offline_isolated_pages(unsigned long, unsigned long);

typedef void (*online_page_callback_t)(struct page *page);
+1 −1
Original line number Diff line number Diff line
@@ -1307,7 +1307,7 @@ int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
/*
 * Confirm all pages in a range [start, end) is belongs to the same zone.
 */
static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
{
	unsigned long pfn;
	struct zone *zone = NULL;