diff options
Diffstat (limited to 'mm/memory-failure.c')
-rw-r--r-- | mm/memory-failure.c | 149 |
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; |