|
@@ -18,7 +18,6 @@
|
|
#include <linux/mm.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/swap.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/interrupt.h>
|
|
-#include <linux/rwsem.h>
|
|
|
|
#include <linux/pagemap.h>
|
|
#include <linux/pagemap.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/jiffies.h>
|
|
#include <linux/bootmem.h>
|
|
#include <linux/bootmem.h>
|
|
@@ -981,21 +980,21 @@ static void __init __free_pages_boot_core(struct page *page,
|
|
|
|
|
|
#if defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID) || \
|
|
#if defined(CONFIG_HAVE_ARCH_EARLY_PFN_TO_NID) || \
|
|
defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
|
|
defined(CONFIG_HAVE_MEMBLOCK_NODE_MAP)
|
|
-/* Only safe to use early in boot when initialisation is single-threaded */
|
|
|
|
|
|
+
|
|
static struct mminit_pfnnid_cache early_pfnnid_cache __meminitdata;
|
|
static struct mminit_pfnnid_cache early_pfnnid_cache __meminitdata;
|
|
|
|
|
|
int __meminit early_pfn_to_nid(unsigned long pfn)
|
|
int __meminit early_pfn_to_nid(unsigned long pfn)
|
|
{
|
|
{
|
|
|
|
+ static DEFINE_SPINLOCK(early_pfn_lock);
|
|
int nid;
|
|
int nid;
|
|
|
|
|
|
- /* The system will behave unpredictably otherwise */
|
|
|
|
- BUG_ON(system_state != SYSTEM_BOOTING);
|
|
|
|
-
|
|
|
|
|
|
+ spin_lock(&early_pfn_lock);
|
|
nid = __early_pfn_to_nid(pfn, &early_pfnnid_cache);
|
|
nid = __early_pfn_to_nid(pfn, &early_pfnnid_cache);
|
|
- if (nid >= 0)
|
|
|
|
- return nid;
|
|
|
|
- /* just returns 0 */
|
|
|
|
- return 0;
|
|
|
|
|
|
+ if (nid < 0)
|
|
|
|
+ nid = 0;
|
|
|
|
+ spin_unlock(&early_pfn_lock);
|
|
|
|
+
|
|
|
|
+ return nid;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -1060,7 +1059,15 @@ static void __init deferred_free_range(struct page *page,
|
|
__free_pages_boot_core(page, pfn, 0);
|
|
__free_pages_boot_core(page, pfn, 0);
|
|
}
|
|
}
|
|
|
|
|
|
-static __initdata DECLARE_RWSEM(pgdat_init_rwsem);
|
|
|
|
|
|
+/* Completion tracking for deferred_init_memmap() threads */
|
|
|
|
+static atomic_t pgdat_init_n_undone __initdata;
|
|
|
|
+static __initdata DECLARE_COMPLETION(pgdat_init_all_done_comp);
|
|
|
|
+
|
|
|
|
+static inline void __init pgdat_init_report_one_done(void)
|
|
|
|
+{
|
|
|
|
+ if (atomic_dec_and_test(&pgdat_init_n_undone))
|
|
|
|
+ complete(&pgdat_init_all_done_comp);
|
|
|
|
+}
|
|
|
|
|
|
/* Initialise remaining memory on a node */
|
|
/* Initialise remaining memory on a node */
|
|
static int __init deferred_init_memmap(void *data)
|
|
static int __init deferred_init_memmap(void *data)
|
|
@@ -1077,7 +1084,7 @@ static int __init deferred_init_memmap(void *data)
|
|
const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id);
|
|
const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id);
|
|
|
|
|
|
if (first_init_pfn == ULONG_MAX) {
|
|
if (first_init_pfn == ULONG_MAX) {
|
|
- up_read(&pgdat_init_rwsem);
|
|
|
|
|
|
+ pgdat_init_report_one_done();
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1177,7 +1184,8 @@ free_range:
|
|
|
|
|
|
pr_info("node %d initialised, %lu pages in %ums\n", nid, nr_pages,
|
|
pr_info("node %d initialised, %lu pages in %ums\n", nid, nr_pages,
|
|
jiffies_to_msecs(jiffies - start));
|
|
jiffies_to_msecs(jiffies - start));
|
|
- up_read(&pgdat_init_rwsem);
|
|
|
|
|
|
+
|
|
|
|
+ pgdat_init_report_one_done();
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1185,14 +1193,17 @@ void __init page_alloc_init_late(void)
|
|
{
|
|
{
|
|
int nid;
|
|
int nid;
|
|
|
|
|
|
|
|
+ /* There will be num_node_state(N_MEMORY) threads */
|
|
|
|
+ atomic_set(&pgdat_init_n_undone, num_node_state(N_MEMORY));
|
|
for_each_node_state(nid, N_MEMORY) {
|
|
for_each_node_state(nid, N_MEMORY) {
|
|
- down_read(&pgdat_init_rwsem);
|
|
|
|
kthread_run(deferred_init_memmap, NODE_DATA(nid), "pgdatinit%d", nid);
|
|
kthread_run(deferred_init_memmap, NODE_DATA(nid), "pgdatinit%d", nid);
|
|
}
|
|
}
|
|
|
|
|
|
/* Block until all are initialised */
|
|
/* Block until all are initialised */
|
|
- down_write(&pgdat_init_rwsem);
|
|
|
|
- up_write(&pgdat_init_rwsem);
|
|
|
|
|
|
+ wait_for_completion(&pgdat_init_all_done_comp);
|
|
|
|
+
|
|
|
|
+ /* Reinit limits that are based on free pages after the kernel is up */
|
|
|
|
+ files_maxfiles_init();
|
|
}
|
|
}
|
|
#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
|
|
#endif /* CONFIG_DEFERRED_STRUCT_PAGE_INIT */
|
|
|
|
|
|
@@ -1285,6 +1296,10 @@ static inline int check_new_page(struct page *page)
|
|
bad_reason = "non-NULL mapping";
|
|
bad_reason = "non-NULL mapping";
|
|
if (unlikely(atomic_read(&page->_count) != 0))
|
|
if (unlikely(atomic_read(&page->_count) != 0))
|
|
bad_reason = "nonzero _count";
|
|
bad_reason = "nonzero _count";
|
|
|
|
+ if (unlikely(page->flags & __PG_HWPOISON)) {
|
|
|
|
+ bad_reason = "HWPoisoned (hardware-corrupted)";
|
|
|
|
+ bad_flags = __PG_HWPOISON;
|
|
|
|
+ }
|
|
if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
|
|
if (unlikely(page->flags & PAGE_FLAGS_CHECK_AT_PREP)) {
|
|
bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
|
|
bad_reason = "PAGE_FLAGS_CHECK_AT_PREP flag set";
|
|
bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
|
|
bad_flags = PAGE_FLAGS_CHECK_AT_PREP;
|