diff options
author | Mel Gorman <mgorman@suse.de> | 2015-06-30 17:56:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-30 22:44:56 -0400 |
commit | 8a942fdea560d4ac0e9d9fabcd5201ad20e0c382 (patch) | |
tree | 452c0f5acccf96ffc54748a44ab5bc788f7dbffc /mm/page_alloc.c | |
parent | d70ddd7a5d9aa335f9b4b0c3d879e1e70ee1e4e3 (diff) |
mm: meminit: make __early_pfn_to_nid SMP-safe and introduce meminit_pfn_in_nid
__early_pfn_to_nid() use static variables to cache recent lookups as
memblock lookups are very expensive but it assumes that memory
initialisation is single-threaded. Parallel initialisation of struct
pages will break that assumption so this patch makes __early_pfn_to_nid()
SMP-safe by requiring the caller to cache recent search information.
early_pfn_to_nid() keeps the same interface but is only safe to use early
in boot due to the use of a global static variable. meminit_pfn_in_nid()
is an SMP-safe version that callers must maintain their own state for.
Signed-off-by: Mel Gorman <mgorman@suse.de>
Tested-by: Nate Zimmer <nzimmer@sgi.com>
Tested-by: Waiman Long <waiman.long@hp.com>
Tested-by: Daniel J Blueman <daniel@numascale.com>
Acked-by: Pekka Enberg <penberg@kernel.org>
Cc: Robin Holt <robinmholt@gmail.com>
Cc: Nate Zimmer <nzimmer@sgi.com>
Cc: Dave Hansen <dave.hansen@intel.com>
Cc: Waiman Long <waiman.long@hp.com>
Cc: Scott Norton <scott.norton@hp.com>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r-- | mm/page_alloc.c | 40 |
1 files changed, 25 insertions, 15 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c index c2ee4ecad083..ffdb2308848d 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c | |||
@@ -4551,39 +4551,41 @@ int __meminit init_currently_empty_zone(struct zone *zone, | |||
4551 | 4551 | ||
4552 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP | 4552 | #ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP |
4553 | #ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID | 4553 | #ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID |
4554 | |||
4554 | /* | 4555 | /* |
4555 | * Required by SPARSEMEM. Given a PFN, return what node the PFN is on. | 4556 | * Required by SPARSEMEM. Given a PFN, return what node the PFN is on. |
4556 | */ | 4557 | */ |
4557 | int __meminit __early_pfn_to_nid(unsigned long pfn) | 4558 | int __meminit __early_pfn_to_nid(unsigned long pfn, |
4559 | struct mminit_pfnnid_cache *state) | ||
4558 | { | 4560 | { |
4559 | unsigned long start_pfn, end_pfn; | 4561 | unsigned long start_pfn, end_pfn; |
4560 | int nid; | 4562 | int nid; |
4561 | /* | ||
4562 | * NOTE: The following SMP-unsafe globals are only used early in boot | ||
4563 | * when the kernel is running single-threaded. | ||
4564 | */ | ||
4565 | static unsigned long __meminitdata last_start_pfn, last_end_pfn; | ||
4566 | static int __meminitdata last_nid; | ||
4567 | 4563 | ||
4568 | if (last_start_pfn <= pfn && pfn < last_end_pfn) | 4564 | if (state->last_start <= pfn && pfn < state->last_end) |
4569 | return last_nid; | 4565 | return state->last_nid; |
4570 | 4566 | ||
4571 | nid = memblock_search_pfn_nid(pfn, &start_pfn, &end_pfn); | 4567 | nid = memblock_search_pfn_nid(pfn, &start_pfn, &end_pfn); |
4572 | if (nid != -1) { | 4568 | if (nid != -1) { |
4573 | last_start_pfn = start_pfn; | 4569 | state->last_start = start_pfn; |
4574 | last_end_pfn = end_pfn; | 4570 | state->last_end = end_pfn; |
4575 | last_nid = nid; | 4571 | state->last_nid = nid; |
4576 | } | 4572 | } |
4577 | 4573 | ||
4578 | return nid; | 4574 | return nid; |
4579 | } | 4575 | } |
4580 | #endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */ | 4576 | #endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */ |
4581 | 4577 | ||
4578 | static struct mminit_pfnnid_cache early_pfnnid_cache __meminitdata; | ||
4579 | |||
4580 | /* Only safe to use early in boot when initialisation is single-threaded */ | ||
4582 | int __meminit early_pfn_to_nid(unsigned long pfn) | 4581 | int __meminit early_pfn_to_nid(unsigned long pfn) |
4583 | { | 4582 | { |
4584 | int nid; | 4583 | int nid; |
4585 | 4584 | ||
4586 | nid = __early_pfn_to_nid(pfn); | 4585 | /* The system will behave unpredictably otherwise */ |
4586 | BUG_ON(system_state != SYSTEM_BOOTING); | ||
4587 | |||
4588 | nid = __early_pfn_to_nid(pfn, &early_pfnnid_cache); | ||
4587 | if (nid >= 0) | 4589 | if (nid >= 0) |
4588 | return nid; | 4590 | return nid; |
4589 | /* just returns 0 */ | 4591 | /* just returns 0 */ |
@@ -4591,15 +4593,23 @@ int __meminit early_pfn_to_nid(unsigned long pfn) | |||
4591 | } | 4593 | } |
4592 | 4594 | ||
4593 | #ifdef CONFIG_NODES_SPAN_OTHER_NODES | 4595 | #ifdef CONFIG_NODES_SPAN_OTHER_NODES |
4594 | bool __meminit early_pfn_in_nid(unsigned long pfn, int node) | 4596 | bool __meminit meminit_pfn_in_nid(unsigned long pfn, int node, |
4597 | struct mminit_pfnnid_cache *state) | ||
4595 | { | 4598 | { |
4596 | int nid; | 4599 | int nid; |
4597 | 4600 | ||
4598 | nid = __early_pfn_to_nid(pfn); | 4601 | nid = __early_pfn_to_nid(pfn, state); |
4599 | if (nid >= 0 && nid != node) | 4602 | if (nid >= 0 && nid != node) |
4600 | return false; | 4603 | return false; |
4601 | return true; | 4604 | return true; |
4602 | } | 4605 | } |
4606 | |||
4607 | /* Only safe to use early in boot when initialisation is single-threaded */ | ||
4608 | bool __meminit early_pfn_in_nid(unsigned long pfn, int node) | ||
4609 | { | ||
4610 | return meminit_pfn_in_nid(pfn, node, &early_pfnnid_cache); | ||
4611 | } | ||
4612 | |||
4603 | #endif | 4613 | #endif |
4604 | 4614 | ||
4605 | /** | 4615 | /** |