Skip to content

Commit 8a942fd

Browse files
Mel Gormantorvalds
authored andcommitted
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 <[email protected]> Tested-by: Nate Zimmer <[email protected]> Tested-by: Waiman Long <[email protected]> Tested-by: Daniel J Blueman <[email protected]> Acked-by: Pekka Enberg <[email protected]> Cc: Robin Holt <[email protected]> Cc: Nate Zimmer <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Waiman Long <[email protected]> Cc: Scott Norton <[email protected]> Cc: "Luck, Tony" <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: "H. Peter Anvin" <[email protected]> Cc: Thomas Gleixner <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent d70ddd7 commit 8a942fd

File tree

4 files changed

+51
-30
lines changed

4 files changed

+51
-30
lines changed

arch/ia64/mm/numa.c

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,27 +58,22 @@ paddr_to_nid(unsigned long paddr)
5858
* SPARSEMEM to allocate the SPARSEMEM sectionmap on the NUMA node where
5959
* the section resides.
6060
*/
61-
int __meminit __early_pfn_to_nid(unsigned long pfn)
61+
int __meminit __early_pfn_to_nid(unsigned long pfn,
62+
struct mminit_pfnnid_cache *state)
6263
{
6364
int i, section = pfn >> PFN_SECTION_SHIFT, ssec, esec;
64-
/*
65-
* NOTE: The following SMP-unsafe globals are only used early in boot
66-
* when the kernel is running single-threaded.
67-
*/
68-
static int __meminitdata last_ssec, last_esec;
69-
static int __meminitdata last_nid;
7065

71-
if (section >= last_ssec && section < last_esec)
72-
return last_nid;
66+
if (section >= state->last_start && section < state->last_end)
67+
return state->last_nid;
7368

7469
for (i = 0; i < num_node_memblks; i++) {
7570
ssec = node_memblk[i].start_paddr >> PA_SECTION_SHIFT;
7671
esec = (node_memblk[i].start_paddr + node_memblk[i].size +
7772
((1L << PA_SECTION_SHIFT) - 1)) >> PA_SECTION_SHIFT;
7873
if (section >= ssec && section < esec) {
79-
last_ssec = ssec;
80-
last_esec = esec;
81-
last_nid = node_memblk[i].nid;
74+
state->last_start = ssec;
75+
state->last_end = esec;
76+
state->last_nid = node_memblk[i].nid;
8277
return node_memblk[i].nid;
8378
}
8479
}

include/linux/mm.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,15 +1726,17 @@ extern void sparse_memory_present_with_active_regions(int nid);
17261726

17271727
#if !defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP) && \
17281728
!defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID)
1729-
static inline int __early_pfn_to_nid(unsigned long pfn)
1729+
static inline int __early_pfn_to_nid(unsigned long pfn,
1730+
struct mminit_pfnnid_cache *state)
17301731
{
17311732
return 0;
17321733
}
17331734
#else
17341735
/* please see mm/page_alloc.c */
17351736
extern int __meminit early_pfn_to_nid(unsigned long pfn);
17361737
/* there is a per-arch backend function. */
1737-
extern int __meminit __early_pfn_to_nid(unsigned long pfn);
1738+
extern int __meminit __early_pfn_to_nid(unsigned long pfn,
1739+
struct mminit_pfnnid_cache *state);
17381740
#endif
17391741

17401742
extern void set_dma_reserve(unsigned long new_dma_reserve);

include/linux/mmzone.h

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,10 +1216,24 @@ void sparse_init(void);
12161216
#define sparse_index_init(_sec, _nid) do {} while (0)
12171217
#endif /* CONFIG_SPARSEMEM */
12181218

1219+
/*
1220+
* During memory init memblocks map pfns to nids. The search is expensive and
1221+
* this caches recent lookups. The implementation of __early_pfn_to_nid
1222+
* may treat start/end as pfns or sections.
1223+
*/
1224+
struct mminit_pfnnid_cache {
1225+
unsigned long last_start;
1226+
unsigned long last_end;
1227+
int last_nid;
1228+
};
1229+
12191230
#ifdef CONFIG_NODES_SPAN_OTHER_NODES
12201231
bool early_pfn_in_nid(unsigned long pfn, int nid);
1232+
bool meminit_pfn_in_nid(unsigned long pfn, int node,
1233+
struct mminit_pfnnid_cache *state);
12211234
#else
1222-
#define early_pfn_in_nid(pfn, nid) (1)
1235+
#define early_pfn_in_nid(pfn, nid) (1)
1236+
#define meminit_pfn_in_nid(pfn, nid, state) (1)
12231237
#endif
12241238

12251239
#ifndef early_pfn_valid

mm/page_alloc.c

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4551,55 +4551,65 @@ int __meminit init_currently_empty_zone(struct zone *zone,
45514551

45524552
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
45534553
#ifndef CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID
4554+
45544555
/*
45554556
* Required by SPARSEMEM. Given a PFN, return what node the PFN is on.
45564557
*/
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)
45584560
{
45594561
unsigned long start_pfn, end_pfn;
45604562
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;
45674563

4568-
if (last_start_pfn <= pfn && pfn < last_end_pfn)
4569-
return last_nid;
4564+
if (state->last_start <= pfn && pfn < state->last_end)
4565+
return state->last_nid;
45704566

45714567
nid = memblock_search_pfn_nid(pfn, &start_pfn, &end_pfn);
45724568
if (nid != -1) {
4573-
last_start_pfn = start_pfn;
4574-
last_end_pfn = end_pfn;
4575-
last_nid = nid;
4569+
state->last_start = start_pfn;
4570+
state->last_end = end_pfn;
4571+
state->last_nid = nid;
45764572
}
45774573

45784574
return nid;
45794575
}
45804576
#endif /* CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID */
45814577

4578+
static struct mminit_pfnnid_cache early_pfnnid_cache __meminitdata;
4579+
4580+
/* Only safe to use early in boot when initialisation is single-threaded */
45824581
int __meminit early_pfn_to_nid(unsigned long pfn)
45834582
{
45844583
int nid;
45854584

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);
45874589
if (nid >= 0)
45884590
return nid;
45894591
/* just returns 0 */
45904592
return 0;
45914593
}
45924594

45934595
#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)
45954598
{
45964599
int nid;
45974600

4598-
nid = __early_pfn_to_nid(pfn);
4601+
nid = __early_pfn_to_nid(pfn, state);
45994602
if (nid >= 0 && nid != node)
46004603
return false;
46014604
return true;
46024605
}
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+
46034613
#endif
46044614

46054615
/**

0 commit comments

Comments
 (0)