Skip to content

Commit d0dc12e

Browse files
Pavel Tatashintorvalds
Pavel Tatashin
authored andcommitted
mm/memory_hotplug: optimize memory hotplug
During memory hotplugging we traverse struct pages three times: 1. memset(0) in sparse_add_one_section() 2. loop in __add_section() to set do: set_page_node(page, nid); and SetPageReserved(page); 3. loop in memmap_init_zone() to call __init_single_pfn() This patch removes the first two loops, and leaves only loop 3. All struct pages are initialized in one place, the same as it is done during boot. The benefits: - We improve memory hotplug performance because we are not evicting the cache several times and also reduce loop branching overhead. - Remove condition from hotpath in __init_single_pfn(), that was added in order to fix the problem that was reported by Bharata in the above email thread, thus also improve performance during normal boot. - Make memory hotplug more similar to the boot memory initialization path because we zero and initialize struct pages only in one function. - Simplifies memory hotplug struct page initialization code, and thus enables future improvements, such as multi-threading the initialization of struct pages in order to improve hotplug performance even further on larger machines. [[email protected]: v5] Link: http://lkml.kernel.org/r/[email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Pavel Tatashin <[email protected]> Reviewed-by: Ingo Molnar <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Baoquan He <[email protected]> Cc: Bharata B Rao <[email protected]> Cc: Daniel Jordan <[email protected]> Cc: Dan Williams <[email protected]> Cc: Greg Kroah-Hartman <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Mel Gorman <[email protected]> Cc: Steven Sistare <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Vlastimil Babka <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent fc44f7f commit d0dc12e

File tree

5 files changed

+28
-38
lines changed

5 files changed

+28
-38
lines changed

drivers/base/node.c

+2
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,8 @@ int register_mem_sect_under_node(struct memory_block *mem_blk, int nid,
407407

408408
if (!mem_blk)
409409
return -EFAULT;
410+
411+
mem_blk->nid = nid;
410412
if (!node_online(nid))
411413
return 0;
412414

include/linux/memory.h

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct memory_block {
3333
void *hw; /* optional pointer to fw/hw data */
3434
int (*phys_callback)(struct memory_block *);
3535
struct device dev;
36+
int nid; /* NID for this memory block */
3637
};
3738

3839
int arch_get_memory_phys_device(unsigned long start_pfn);

mm/memory_hotplug.c

+8-19
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,6 @@ static int __meminit __add_section(int nid, unsigned long phys_start_pfn,
250250
struct vmem_altmap *altmap, bool want_memblock)
251251
{
252252
int ret;
253-
int i;
254253

255254
if (pfn_valid(phys_start_pfn))
256255
return -EEXIST;
@@ -259,23 +258,6 @@ static int __meminit __add_section(int nid, unsigned long phys_start_pfn,
259258
if (ret < 0)
260259
return ret;
261260

262-
/*
263-
* Make all the pages reserved so that nobody will stumble over half
264-
* initialized state.
265-
* FIXME: We also have to associate it with a node because page_to_nid
266-
* relies on having page with the proper node.
267-
*/
268-
for (i = 0; i < PAGES_PER_SECTION; i++) {
269-
unsigned long pfn = phys_start_pfn + i;
270-
struct page *page;
271-
if (!pfn_valid(pfn))
272-
continue;
273-
274-
page = pfn_to_page(pfn);
275-
set_page_node(page, nid);
276-
SetPageReserved(page);
277-
}
278-
279261
if (!want_memblock)
280262
return 0;
281263

@@ -908,8 +890,15 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages, int online_typ
908890
int nid;
909891
int ret;
910892
struct memory_notify arg;
893+
struct memory_block *mem;
894+
895+
/*
896+
* We can't use pfn_to_nid() because nid might be stored in struct page
897+
* which is not yet initialized. Instead, we find nid from memory block.
898+
*/
899+
mem = find_memory_block(__pfn_to_section(pfn));
900+
nid = mem->nid;
911901

912-
nid = pfn_to_nid(pfn);
913902
/* associate pfn range with the zone */
914903
zone = move_pfn_range(online_type, nid, pfn, nr_pages);
915904

mm/page_alloc.c

+10-18
Original file line numberDiff line numberDiff line change
@@ -1143,10 +1143,9 @@ static void free_one_page(struct zone *zone,
11431143
}
11441144

11451145
static void __meminit __init_single_page(struct page *page, unsigned long pfn,
1146-
unsigned long zone, int nid, bool zero)
1146+
unsigned long zone, int nid)
11471147
{
1148-
if (zero)
1149-
mm_zero_struct_page(page);
1148+
mm_zero_struct_page(page);
11501149
set_page_links(page, zone, nid, pfn);
11511150
init_page_count(page);
11521151
page_mapcount_reset(page);
@@ -1160,12 +1159,6 @@ static void __meminit __init_single_page(struct page *page, unsigned long pfn,
11601159
#endif
11611160
}
11621161

1163-
static void __meminit __init_single_pfn(unsigned long pfn, unsigned long zone,
1164-
int nid, bool zero)
1165-
{
1166-
return __init_single_page(pfn_to_page(pfn), pfn, zone, nid, zero);
1167-
}
1168-
11691162
#ifdef CONFIG_DEFERRED_STRUCT_PAGE_INIT
11701163
static void __meminit init_reserved_page(unsigned long pfn)
11711164
{
@@ -1184,7 +1177,7 @@ static void __meminit init_reserved_page(unsigned long pfn)
11841177
if (pfn >= zone->zone_start_pfn && pfn < zone_end_pfn(zone))
11851178
break;
11861179
}
1187-
__init_single_pfn(pfn, zid, nid, true);
1180+
__init_single_page(pfn_to_page(pfn), pfn, zid, nid);
11881181
}
11891182
#else
11901183
static inline void init_reserved_page(unsigned long pfn)
@@ -1501,7 +1494,7 @@ static unsigned long __init deferred_init_pages(int nid, int zid,
15011494
} else {
15021495
page++;
15031496
}
1504-
__init_single_page(page, pfn, zid, nid, true);
1497+
__init_single_page(page, pfn, zid, nid);
15051498
nr_pages++;
15061499
}
15071500
return (nr_pages);
@@ -5434,6 +5427,7 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
54345427
pg_data_t *pgdat = NODE_DATA(nid);
54355428
unsigned long pfn;
54365429
unsigned long nr_initialised = 0;
5430+
struct page *page;
54375431
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
54385432
struct memblock_region *r = NULL, *tmp;
54395433
#endif
@@ -5486,6 +5480,11 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
54865480
#endif
54875481

54885482
not_early:
5483+
page = pfn_to_page(pfn);
5484+
__init_single_page(page, pfn, zone, nid);
5485+
if (context == MEMMAP_HOTPLUG)
5486+
SetPageReserved(page);
5487+
54895488
/*
54905489
* Mark the block movable so that blocks are reserved for
54915490
* movable at startup. This will force kernel allocations
@@ -5502,15 +5501,8 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
55025501
* because this is done early in sparse_add_one_section
55035502
*/
55045503
if (!(pfn & (pageblock_nr_pages - 1))) {
5505-
struct page *page = pfn_to_page(pfn);
5506-
5507-
__init_single_page(page, pfn, zone, nid,
5508-
context != MEMMAP_HOTPLUG);
55095504
set_pageblock_migratetype(page, MIGRATE_MOVABLE);
55105505
cond_resched();
5511-
} else {
5512-
__init_single_pfn(pfn, zone, nid,
5513-
context != MEMMAP_HOTPLUG);
55145506
}
55155507
}
55165508
}

mm/sparse.c

+7-1
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,13 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat,
779779
goto out;
780780
}
781781

782-
memset(memmap, 0, sizeof(struct page) * PAGES_PER_SECTION);
782+
#ifdef CONFIG_DEBUG_VM
783+
/*
784+
* Poison uninitialized struct pages in order to catch invalid flags
785+
* combinations.
786+
*/
787+
memset(memmap, PAGE_POISON_PATTERN, sizeof(struct page) * PAGES_PER_SECTION);
788+
#endif
783789

784790
section_mark_present(ms);
785791

0 commit comments

Comments
 (0)