aboutsummaryrefslogtreecommitdiffstats
path: root/mm/memory-failure.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r--mm/memory-failure.c149
1 files changed, 27 insertions, 122 deletions
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
index 51901b1..809823d 100644
--- a/mm/memory-failure.c
+++ b/mm/memory-failure.c
@@ -42,7 +42,6 @@
#include <linux/sched.h>
#include <linux/ksm.h>
#include <linux/rmap.h>
-#include <linux/export.h>
#include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/backing-dev.h>
@@ -54,7 +53,6 @@
#include <linux/hugetlb.h>
#include <linux/memory_hotplug.h>
#include <linux/mm_inline.h>
-#include <linux/kfifo.h>
#include "internal.h"
int sysctl_memory_failure_early_kill __read_mostly = 0;
@@ -1033,16 +1031,15 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
return 0;
} else if (PageHuge(hpage)) {
/*
- * Check "filter hit" and "race with other subpage."
+ * Check "just unpoisoned", "filter hit", and
+ * "race with other subpage."
*/
lock_page(hpage);
- if (PageHWPoison(hpage)) {
- if ((hwpoison_filter(p) && TestClearPageHWPoison(p))
- || (p != hpage && TestSetPageHWPoison(hpage))) {
- atomic_long_sub(nr_pages, &mce_bad_pages);
- unlock_page(hpage);
- return 0;
- }
+ if (!PageHWPoison(hpage)
+ || (hwpoison_filter(p) && TestClearPageHWPoison(p))
+ || (p != hpage && TestSetPageHWPoison(hpage))) {
+ atomic_long_sub(nr_pages, &mce_bad_pages);
+ return 0;
}
set_page_hwpoison_huge_page(hpage);
res = dequeue_hwpoisoned_huge_page(hpage);
@@ -1094,8 +1091,6 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
*/
if (!PageHWPoison(p)) {
printk(KERN_ERR "MCE %#lx: just unpoisoned\n", pfn);
- atomic_long_sub(nr_pages, &mce_bad_pages);
- put_page(hpage);
res = 0;
goto out;
}
@@ -1183,97 +1178,6 @@ void memory_failure(unsigned long pfn, int trapno)
__memory_failure(pfn, trapno, 0);
}
-#define MEMORY_FAILURE_FIFO_ORDER 4
-#define MEMORY_FAILURE_FIFO_SIZE (1 << MEMORY_FAILURE_FIFO_ORDER)
-
-struct memory_failure_entry {
- unsigned long pfn;
- int trapno;
- int flags;
-};
-
-struct memory_failure_cpu {
- DECLARE_KFIFO(fifo, struct memory_failure_entry,
- MEMORY_FAILURE_FIFO_SIZE);
- spinlock_t lock;
- struct work_struct work;
-};
-
-static DEFINE_PER_CPU(struct memory_failure_cpu, memory_failure_cpu);
-
-/**
- * memory_failure_queue - Schedule handling memory failure of a page.
- * @pfn: Page Number of the corrupted page
- * @trapno: Trap number reported in the signal to user space.
- * @flags: Flags for memory failure handling
- *
- * This function is called by the low level hardware error handler
- * when it detects hardware memory corruption of a page. It schedules
- * the recovering of error page, including dropping pages, killing
- * processes etc.
- *
- * The function is primarily of use for corruptions that
- * happen outside the current execution context (e.g. when
- * detected by a background scrubber)
- *
- * Can run in IRQ context.
- */
-void memory_failure_queue(unsigned long pfn, int trapno, int flags)
-{
- struct memory_failure_cpu *mf_cpu;
- unsigned long proc_flags;
- struct memory_failure_entry entry = {
- .pfn = pfn,
- .trapno = trapno,
- .flags = flags,
- };
-
- mf_cpu = &get_cpu_var(memory_failure_cpu);
- spin_lock_irqsave(&mf_cpu->lock, proc_flags);
- if (kfifo_put(&mf_cpu->fifo, &entry))
- schedule_work_on(smp_processor_id(), &mf_cpu->work);
- else
- pr_err("Memory failure: buffer overflow when queuing memory failure at 0x%#lx\n",
- pfn);
- spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
- put_cpu_var(memory_failure_cpu);
-}
-EXPORT_SYMBOL_GPL(memory_failure_queue);
-
-static void memory_failure_work_func(struct work_struct *work)
-{
- struct memory_failure_cpu *mf_cpu;
- struct memory_failure_entry entry = { 0, };
- unsigned long proc_flags;
- int gotten;
-
- mf_cpu = &__get_cpu_var(memory_failure_cpu);
- for (;;) {
- spin_lock_irqsave(&mf_cpu->lock, proc_flags);
- gotten = kfifo_get(&mf_cpu->fifo, &entry);
- spin_unlock_irqrestore(&mf_cpu->lock, proc_flags);
- if (!gotten)
- break;
- __memory_failure(entry.pfn, entry.trapno, entry.flags);
- }
-}
-
-static int __init memory_failure_init(void)
-{
- struct memory_failure_cpu *mf_cpu;
- int cpu;
-
- for_each_possible_cpu(cpu) {
- mf_cpu = &per_cpu(memory_failure_cpu, cpu);
- spin_lock_init(&mf_cpu->lock);
- INIT_KFIFO(mf_cpu->fifo);
- INIT_WORK(&mf_cpu->work, memory_failure_work_func);
- }
-
- return 0;
-}
-core_initcall(memory_failure_init);
-
/**
* unpoison_memory - Unpoison a previously poisoned page
* @pfn: Page number of the to be unpoisoned page
@@ -1314,7 +1218,7 @@ int unpoison_memory(unsigned long pfn)
* to the end.
*/
if (PageHuge(page)) {
- pr_info("MCE: Memory failure is now running on free hugepage %#lx\n", pfn);
+ pr_debug("MCE: Memory failure is now running on free hugepage %#lx\n", pfn);
return 0;
}
if (TestClearPageHWPoison(p))
@@ -1403,7 +1307,11 @@ static int get_any_page(struct page *p, unsigned long pfn, int flags)
/* Not a free page */
ret = 1;
}
+#ifndef CONFIG_DMA_CMA
unset_migratetype_isolate(p);
+#else
+ unset_migratetype_isolate(p, MIGRATE_MOVABLE);
+#endif
unlock_memory_hotplug();
return ret;
}
@@ -1423,7 +1331,7 @@ static int soft_offline_huge_page(struct page *page, int flags)
if (PageHWPoison(hpage)) {
put_page(hpage);
- pr_info("soft offline: %#lx hugepage already poisoned\n", pfn);
+ pr_debug("soft offline: %#lx hugepage already poisoned\n", pfn);
return -EBUSY;
}
@@ -1437,25 +1345,17 @@ static int soft_offline_huge_page(struct page *page, int flags)
list_for_each_entry_safe(page1, page2, &pagelist, lru)
put_page(page1);
- pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
- pfn, ret, page->flags);
+ pr_debug("soft offline: %#lx: migration failed %d, type %lx\n",
+ pfn, ret, page->flags);
if (ret > 0)
ret = -EIO;
return ret;
}
done:
- /* overcommit hugetlb page will be freed to buddy */
- if (PageHuge(hpage)) {
- if (!PageHWPoison(hpage))
- atomic_long_add(1 << compound_trans_order(hpage),
- &mce_bad_pages);
- set_page_hwpoison_huge_page(hpage);
- dequeue_hwpoisoned_huge_page(hpage);
- } else {
- SetPageHWPoison(page);
- atomic_long_inc(&mce_bad_pages);
- }
-
+ if (!PageHWPoison(hpage))
+ atomic_long_add(1 << compound_trans_order(hpage), &mce_bad_pages);
+ set_page_hwpoison_huge_page(hpage);
+ dequeue_hwpoisoned_huge_page(hpage);
/* keep elevated page count for bad page */
return ret;
}
@@ -1525,7 +1425,7 @@ int soft_offline_page(struct page *page, int flags)
}
if (!PageLRU(page)) {
pr_info("soft_offline: %#lx: unknown non LRU page type %lx\n",
- pfn, page->flags);
+ pfn, page->flags);
return -EIO;
}
@@ -1575,8 +1475,13 @@ int soft_offline_page(struct page *page, int flags)
inc_zone_page_state(page, NR_ISOLATED_ANON +
page_is_file_cache(page));
list_add(&page->lru, &pagelist);
+#ifndef CONFIG_DMA_CMA
ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
- false, MIGRATE_SYNC);
+ false, MIGRATE_SYNC);
+#else
+ ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL,
+ false, MIGRATE_SYNC, 0);
+#endif
if (ret) {
putback_lru_pages(&pagelist);
pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
@@ -1586,7 +1491,7 @@ int soft_offline_page(struct page *page, int flags)
}
} else {
pr_info("soft offline: %#lx: isolation failed: %d, page count %d, type %lx\n",
- pfn, ret, page_count(page), page->flags);
+ pfn, ret, page_count(page), page->flags);
}
if (ret)
return ret;