Skip to content

Commit

Permalink
mm, memory_hotplug: display allowed zones in the preferred ordering
Browse files Browse the repository at this point in the history
Prior to commit f1dd2cd ("mm, memory_hotplug: do not associate
hotadded memory to zones until online") we used to allow to change the
valid zone types of a memory block if it is adjacent to a different zone
type.

This fact was reflected in memoryNN/valid_zones by the ordering of
printed zones.  The first one was default (echo online > memoryNN/state)
and the other one could be onlined explicitly by online_{movable,kernel}.

This behavior was removed by the said patch and as such the ordering was
not all that important.  In most cases a kernel zone would be default
anyway.  The only exception is movable_node handled by "mm,
memory_hotplug: support movable_node for hotpluggable nodes".

Let's reintroduce this behavior again because later patch will remove
the zone overlap restriction and so user will be allowed to online
kernel resp.  movable block regardless of its placement.  Original
behavior will then become significant again because it would be
non-trivial for users to see what is the default zone to online into.

Implementation is really simple.  Pull out zone selection out of
move_pfn_range into zone_for_pfn_range helper and use it in
show_valid_zones to display the zone for default onlining and then both
kernel and movable if they are allowed.  Default online zone is not
duplicated.

Link: http://lkml.kernel.org/r/20170714121233.16861-2-mhocko@kernel.org
Signed-off-by: Michal Hocko <mhocko@suse.com>
Acked-by: Joonsoo Kim <iamjoonsoo.kim@lge.com>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Andrea Arcangeli <aarcange@redhat.com>
Cc: Reza Arbab <arbab@linux.vnet.ibm.com>
Cc: Yasuaki Ishimatsu <yasu.isimatu@gmail.com>
Cc: Xishi Qiu <qiuxishi@huawei.com>
Cc: Kani Toshimitsu <toshi.kani@hpe.com>
Cc: <slaoub@gmail.com>
Cc: Daniel Kiper <daniel.kiper@oracle.com>
Cc: Igor Mammedov <imammedo@redhat.com>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: Wei Yang <richard.weiyang@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Michal Hocko authored and torvalds committed Sep 7, 2017
1 parent c115258 commit e5e6893
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 43 deletions.
33 changes: 23 additions & 10 deletions drivers/base/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -388,14 +388,30 @@ static ssize_t show_phys_device(struct device *dev,
}

#ifdef CONFIG_MEMORY_HOTREMOVE
static void print_allowed_zone(char *buf, int nid, unsigned long start_pfn,
unsigned long nr_pages, int online_type,
struct zone *default_zone)
{
struct zone *zone;

if (!allow_online_pfn_range(nid, start_pfn, nr_pages, online_type))
return;

zone = zone_for_pfn_range(online_type, nid, start_pfn, nr_pages);
if (zone != default_zone) {
strcat(buf, " ");
strcat(buf, zone->name);
}
}

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 = section_nr_to_pfn(mem->start_section_nr);
unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block;
unsigned long valid_start_pfn, valid_end_pfn;
bool append = false;
struct zone *default_zone;
int nid;

/*
Expand All @@ -418,16 +434,13 @@ static ssize_t show_valid_zones(struct device *dev,
}

nid = pfn_to_nid(start_pfn);
if (allow_online_pfn_range(nid, start_pfn, nr_pages, MMOP_ONLINE_KERNEL)) {
strcat(buf, default_zone_for_pfn(nid, start_pfn, nr_pages)->name);
append = true;
}
default_zone = zone_for_pfn_range(MMOP_ONLINE_KEEP, nid, start_pfn, nr_pages);
strcat(buf, default_zone->name);

if (allow_online_pfn_range(nid, start_pfn, nr_pages, MMOP_ONLINE_MOVABLE)) {
if (append)
strcat(buf, " ");
strcat(buf, NODE_DATA(nid)->node_zones[ZONE_MOVABLE].name);
}
print_allowed_zone(buf, nid, start_pfn, nr_pages, MMOP_ONLINE_KERNEL,
default_zone);
print_allowed_zone(buf, nid, start_pfn, nr_pages, MMOP_ONLINE_MOVABLE,
default_zone);
out:
strcat(buf, "\n");

Expand Down
2 changes: 1 addition & 1 deletion include/linux/memory_hotplug.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,6 @@ extern struct page *sparse_decode_mem_map(unsigned long coded_mem_map,
unsigned long pnum);
extern bool allow_online_pfn_range(int nid, unsigned long pfn, unsigned long nr_pages,
int online_type);
extern struct zone *default_zone_for_pfn(int nid, unsigned long pfn,
extern struct zone *zone_for_pfn_range(int online_type, int nid, unsigned start_pfn,
unsigned long nr_pages);
#endif /* __LINUX_MEMORY_HOTPLUG_H */
73 changes: 41 additions & 32 deletions mm/memory_hotplug.c
Original file line number Diff line number Diff line change
Expand Up @@ -773,31 +773,6 @@ static void node_states_set_node(int node, struct memory_notify *arg)
node_set_state(node, N_MEMORY);
}

bool allow_online_pfn_range(int nid, unsigned long pfn, unsigned long nr_pages, int online_type)
{
struct pglist_data *pgdat = NODE_DATA(nid);
struct zone *movable_zone = &pgdat->node_zones[ZONE_MOVABLE];
struct zone *default_zone = default_zone_for_pfn(nid, pfn, nr_pages);

/*
* TODO there shouldn't be any inherent reason to have ZONE_NORMAL
* physically before ZONE_MOVABLE. All we need is they do not
* overlap. Historically we didn't allow ZONE_NORMAL after ZONE_MOVABLE
* though so let's stick with it for simplicity for now.
* TODO make sure we do not overlap with ZONE_DEVICE
*/
if (online_type == MMOP_ONLINE_KERNEL) {
if (zone_is_empty(movable_zone))
return true;
return movable_zone->zone_start_pfn >= pfn + nr_pages;
} else if (online_type == MMOP_ONLINE_MOVABLE) {
return zone_end_pfn(default_zone) <= pfn;
}

/* MMOP_ONLINE_KEEP will always succeed and inherits the current zone */
return online_type == MMOP_ONLINE_KEEP;
}

static void __meminit resize_zone_range(struct zone *zone, unsigned long start_pfn,
unsigned long nr_pages)
{
Expand Down Expand Up @@ -856,7 +831,7 @@ void __ref move_pfn_range_to_zone(struct zone *zone,
* If no kernel zone covers this pfn range it will automatically go
* to the ZONE_NORMAL.
*/
struct zone *default_zone_for_pfn(int nid, unsigned long start_pfn,
static struct zone *default_zone_for_pfn(int nid, unsigned long start_pfn,
unsigned long nr_pages)
{
struct pglist_data *pgdat = NODE_DATA(nid);
Expand All @@ -872,6 +847,31 @@ struct zone *default_zone_for_pfn(int nid, unsigned long start_pfn,
return &pgdat->node_zones[ZONE_NORMAL];
}

bool allow_online_pfn_range(int nid, unsigned long pfn, unsigned long nr_pages, int online_type)
{
struct pglist_data *pgdat = NODE_DATA(nid);
struct zone *movable_zone = &pgdat->node_zones[ZONE_MOVABLE];
struct zone *default_zone = default_zone_for_pfn(nid, pfn, nr_pages);

/*
* TODO there shouldn't be any inherent reason to have ZONE_NORMAL
* physically before ZONE_MOVABLE. All we need is they do not
* overlap. Historically we didn't allow ZONE_NORMAL after ZONE_MOVABLE
* though so let's stick with it for simplicity for now.
* TODO make sure we do not overlap with ZONE_DEVICE
*/
if (online_type == MMOP_ONLINE_KERNEL) {
if (zone_is_empty(movable_zone))
return true;
return movable_zone->zone_start_pfn >= pfn + nr_pages;
} else if (online_type == MMOP_ONLINE_MOVABLE) {
return zone_end_pfn(default_zone) <= pfn;
}

/* MMOP_ONLINE_KEEP will always succeed and inherits the current zone */
return online_type == MMOP_ONLINE_KEEP;
}

static inline bool movable_pfn_range(int nid, struct zone *default_zone,
unsigned long start_pfn, unsigned long nr_pages)
{
Expand All @@ -885,12 +885,8 @@ static inline bool movable_pfn_range(int nid, struct zone *default_zone,
return !zone_intersects(default_zone, start_pfn, nr_pages);
}

/*
* Associates the given pfn range with the given node and the zone appropriate
* for the given online type.
*/
static struct zone * __meminit move_pfn_range(int online_type, int nid,
unsigned long start_pfn, unsigned long nr_pages)
struct zone * zone_for_pfn_range(int online_type, int nid, unsigned start_pfn,
unsigned long nr_pages)
{
struct pglist_data *pgdat = NODE_DATA(nid);
struct zone *zone = default_zone_for_pfn(nid, start_pfn, nr_pages);
Expand All @@ -909,6 +905,19 @@ static struct zone * __meminit move_pfn_range(int online_type, int nid,
zone = &pgdat->node_zones[ZONE_MOVABLE];
}

return zone;
}

/*
* Associates the given pfn range with the given node and the zone appropriate
* for the given online type.
*/
static struct zone * __meminit move_pfn_range(int online_type, int nid,
unsigned long start_pfn, unsigned long nr_pages)
{
struct zone *zone;

zone = zone_for_pfn_range(online_type, nid, start_pfn, nr_pages);
move_pfn_range_to_zone(zone, start_pfn, nr_pages);
return zone;
}
Expand Down

0 comments on commit e5e6893

Please sign in to comment.