HWPOISON: Convert pr_debugs to pr_info
[deliverable/linux.git] / mm / memory-failure.c
index 6b44e52cacaa19857de7ba08af0469492dbeb2db..0a2ed9a17e8c6b435ec192a90a42b4048b1b5444 100644 (file)
@@ -7,21 +7,26 @@
  * Free Software Foundation.
  *
  * High level machine check handler. Handles pages reported by the
- * hardware as being corrupted usually due to a 2bit ECC memory or cache
+ * hardware as being corrupted usually due to a multi-bit ECC memory or cache
  * failure.
+ * 
+ * In addition there is a "soft offline" entry point that allows stop using
+ * not-yet-corrupted-by-suspicious pages without killing anything.
  *
  * Handles page cache pages in various states. The tricky part
- * here is that we can access any page asynchronous to other VM
- * users, because memory failures could happen anytime and anywhere,
- * possibly violating some of their assumptions. This is why this code
- * has to be extremely careful. Generally it tries to use normal locking
- * rules, as in get the standard locks, even if that means the
- * error handling takes potentially a long time.
- *
- * The operation to map back from RMAP chains to processes has to walk
- * the complete process list and has non linear complexity with the number
- * mappings. In short it can be quite slow. But since memory corruptions
- * are rare we hope to get away with this.
+ * here is that we can access any page asynchronously in respect to 
+ * other VM users, because memory failures could happen anytime and 
+ * anywhere. This could violate some of their assumptions. This is why 
+ * this code has to be extremely careful. Generally it tries to use 
+ * normal locking rules, as in get the standard locks, even if that means 
+ * the error handling takes potentially a long time.
+ * 
+ * There are several operations here with exponential complexity because
+ * of unsuitable VM data structures. For example the operation to map back 
+ * from RMAP chains to processes has to walk the complete process list and 
+ * has non linear complexity with the number. But since memory corruptions
+ * are rare we hope to get away with this. This avoids impacting the core 
+ * VM.
  */
 
 /*
@@ -46,6 +51,7 @@
 #include <linux/suspend.h>
 #include <linux/slab.h>
 #include <linux/swapops.h>
+#include <linux/hugetlb.h>
 #include "internal.h"
 
 int sysctl_memory_failure_early_kill __read_mostly = 0;
@@ -77,7 +83,7 @@ static int hwpoison_filter_dev(struct page *p)
                return 0;
 
        /*
-        * page_mapping() does not accept slab page
+        * page_mapping() does not accept slab pages.
         */
        if (PageSlab(p))
                return -EINVAL;
@@ -182,7 +188,7 @@ EXPORT_SYMBOL_GPL(hwpoison_filter);
  * signal.
  */
 static int kill_proc_ao(struct task_struct *t, unsigned long addr, int trapno,
-                       unsigned long pfn)
+                       unsigned long pfn, struct page *page)
 {
        struct siginfo si;
        int ret;
@@ -197,7 +203,7 @@ static int kill_proc_ao(struct task_struct *t, unsigned long addr, int trapno,
 #ifdef __ARCH_SI_TRAPNO
        si.si_trapno = trapno;
 #endif
-       si.si_addr_lsb = PAGE_SHIFT;
+       si.si_addr_lsb = compound_order(compound_head(page)) + PAGE_SHIFT;
        /*
         * Don't use force here, it's convenient if the signal
         * can be temporarily blocked.
@@ -234,7 +240,7 @@ void shake_page(struct page *p, int access)
                int nr;
                do {
                        nr = shrink_slab(1000, GFP_KERNEL, 1000);
-                       if (page_count(p) == 0)
+                       if (page_count(p) == 1)
                                break;
                } while (nr > 10);
        }
@@ -308,7 +314,7 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
         * a SIGKILL because the error is not contained anymore.
         */
        if (tk->addr == -EFAULT) {
-               pr_debug("MCE: Unable to find user space address %lx in %s\n",
+               pr_info("MCE: Unable to find user space address %lx in %s\n",
                        page_to_pfn(p), tsk->comm);
                tk->addr_valid = 0;
        }
@@ -326,7 +332,7 @@ static void add_to_kill(struct task_struct *tsk, struct page *p,
  * wrong earlier.
  */
 static void kill_procs_ao(struct list_head *to_kill, int doit, int trapno,
-                         int fail, unsigned long pfn)
+                         int fail, struct page *page, unsigned long pfn)
 {
        struct to_kill *tk, *next;
 
@@ -351,7 +357,7 @@ static void kill_procs_ao(struct list_head *to_kill, int doit, int trapno,
                         * process anyways.
                         */
                        else if (kill_proc_ao(tk->tsk, tk->addr, trapno,
-                                             pfn) < 0)
+                                             pfn, page) < 0)
                                printk(KERN_ERR
                "MCE %#lx: Cannot send advisory machine check signal to %s:%d\n",
                                        pfn, tk->tsk->comm, tk->tsk->pid);
@@ -576,7 +582,7 @@ static int me_pagecache_clean(struct page *p, unsigned long pfn)
                                        pfn, err);
                } else if (page_has_private(p) &&
                                !try_to_release_page(p, GFP_NOIO)) {
-                       pr_debug("MCE %#lx: failed to release buffers\n", pfn);
+                       pr_info("MCE %#lx: failed to release buffers\n", pfn);
                } else {
                        ret = RECOVERED;
                }
@@ -690,17 +696,29 @@ static int me_swapcache_clean(struct page *p, unsigned long pfn)
 /*
  * Huge pages. Needs work.
  * Issues:
- * No rmap support so we cannot find the original mapper. In theory could walk
- * all MMs and look for the mappings, but that would be non atomic and racy.
- * Need rmap for hugepages for this. Alternatively we could employ a heuristic,
- * like just walking the current process and hoping it has it mapped (that
- * should be usually true for the common "shared database cache" case)
- * Should handle free huge pages and dequeue them too, but this needs to
- * handle huge page accounting correctly.
+ * - Error on hugepage is contained in hugepage unit (not in raw page unit.)
+ *   To narrow down kill region to one page, we need to break up pmd.
+ * - To support soft-offlining for hugepage, we need to support hugepage
+ *   migration.
  */
 static int me_huge_page(struct page *p, unsigned long pfn)
 {
-       return FAILED;
+       struct page *hpage = compound_head(p);
+       /*
+        * We can safely recover from error on free or reserved (i.e.
+        * not in-use) hugepage by dequeuing it from freelist.
+        * To check whether a hugepage is in-use or not, we can't use
+        * page->lru because it can be used in other hugepage operations,
+        * such as __unmap_hugepage_range() and gather_surplus_pages().
+        * So instead we use page_mapping() and PageAnon().
+        * We assume that this function is called with page lock held,
+        * so there is no race between isolation and mapping/unmapping.
+        */
+       if (!(page_mapping(hpage) || PageAnon(hpage))) {
+               __isolate_hwpoisoned_huge_page(hpage);
+               return RECOVERED;
+       }
+       return DELAYED;
 }
 
 /*
@@ -838,6 +856,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
        int ret;
        int i;
        int kill = 1;
+       struct page *hpage = compound_head(p);
 
        if (PageReserved(p) || PageSlab(p))
                return SWAP_SUCCESS;
@@ -846,10 +865,10 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
         * This check implies we don't kill processes if their pages
         * are in the swap cache early. Those are always late kills.
         */
-       if (!page_mapped(p))
+       if (!page_mapped(hpage))
                return SWAP_SUCCESS;
 
-       if (PageCompound(p) || PageKsm(p))
+       if (PageKsm(p))
                return SWAP_FAIL;
 
        if (PageSwapCache(p)) {
@@ -864,10 +883,11 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
         * XXX: the dirty test could be racy: set_page_dirty() may not always
         * be called inside page lock (it's recommended but not enforced).
         */
-       mapping = page_mapping(p);
-       if (!PageDirty(p) && mapping && mapping_cap_writeback_dirty(mapping)) {
-               if (page_mkclean(p)) {
-                       SetPageDirty(p);
+       mapping = page_mapping(hpage);
+       if (!PageDirty(hpage) && mapping &&
+           mapping_cap_writeback_dirty(mapping)) {
+               if (page_mkclean(hpage)) {
+                       SetPageDirty(hpage);
                } else {
                        kill = 0;
                        ttu |= TTU_IGNORE_HWPOISON;
@@ -886,14 +906,14 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
         * there's nothing that can be done.
         */
        if (kill)
-               collect_procs(p, &tokill);
+               collect_procs(hpage, &tokill);
 
        /*
         * try_to_unmap can fail temporarily due to races.
         * Try a few times (RED-PEN better strategy?)
         */
        for (i = 0; i < N_UNMAP_TRIES; i++) {
-               ret = try_to_unmap(p, ttu);
+               ret = try_to_unmap(hpage, ttu);
                if (ret == SWAP_SUCCESS)
                        break;
                pr_debug("MCE %#lx: try_to_unmap retry needed %d\n", pfn,  ret);
@@ -901,7 +921,7 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
 
        if (ret != SWAP_SUCCESS)
                printk(KERN_ERR "MCE %#lx: failed to unmap page (mapcount=%d)\n",
-                               pfn, page_mapcount(p));
+                               pfn, page_mapcount(hpage));
 
        /*
         * Now that the dirty bit has been propagated to the
@@ -912,17 +932,35 @@ static int hwpoison_user_mappings(struct page *p, unsigned long pfn,
         * use a more force-full uncatchable kill to prevent
         * any accesses to the poisoned memory.
         */
-       kill_procs_ao(&tokill, !!PageDirty(p), trapno,
-                     ret != SWAP_SUCCESS, pfn);
+       kill_procs_ao(&tokill, !!PageDirty(hpage), trapno,
+                     ret != SWAP_SUCCESS, p, pfn);
 
        return ret;
 }
 
+static void set_page_hwpoison_huge_page(struct page *hpage)
+{
+       int i;
+       int nr_pages = 1 << compound_order(hpage);
+       for (i = 0; i < nr_pages; i++)
+               SetPageHWPoison(hpage + i);
+}
+
+static void clear_page_hwpoison_huge_page(struct page *hpage)
+{
+       int i;
+       int nr_pages = 1 << compound_order(hpage);
+       for (i = 0; i < nr_pages; i++)
+               ClearPageHWPoison(hpage + i);
+}
+
 int __memory_failure(unsigned long pfn, int trapno, int flags)
 {
        struct page_state *ps;
        struct page *p;
+       struct page *hpage;
        int res;
+       unsigned int nr_pages;
 
        if (!sysctl_memory_failure_recovery)
                panic("Memory failure from trap %d on page %lx", trapno, pfn);
@@ -935,12 +973,14 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
        }
 
        p = pfn_to_page(pfn);
+       hpage = compound_head(p);
        if (TestSetPageHWPoison(p)) {
                printk(KERN_ERR "MCE %#lx: already hardware poisoned\n", pfn);
                return 0;
        }
 
-       atomic_long_add(1, &mce_bad_pages);
+       nr_pages = 1 << compound_order(hpage);
+       atomic_long_add(nr_pages, &mce_bad_pages);
 
        /*
         * We need/can do nothing about count=0 pages.
@@ -954,7 +994,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
         * that may make page_freeze_refs()/page_unfreeze_refs() mismatch.
         */
        if (!(flags & MF_COUNT_INCREASED) &&
-               !get_page_unless_zero(compound_head(p))) {
+               !get_page_unless_zero(hpage)) {
                if (is_free_buddy_page(p)) {
                        action_result(pfn, "free buddy", DELAYED);
                        return 0;
@@ -972,9 +1012,9 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
         * The check (unnecessarily) ignores LRU pages being isolated and
         * walked by the page reclaim code, however that's not a big loss.
         */
-       if (!PageLRU(p))
+       if (!PageLRU(p) && !PageHuge(p))
                shake_page(p, 0);
-       if (!PageLRU(p)) {
+       if (!PageLRU(p) && !PageHuge(p)) {
                /*
                 * shake_page could have turned it free.
                 */
@@ -992,7 +1032,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
         * It's very difficult to mess with pages currently under IO
         * and in many cases impossible, so we just avoid it here.
         */
-       lock_page_nosync(p);
+       lock_page_nosync(hpage);
 
        /*
         * unpoison always clear PG_hwpoison inside page lock
@@ -1004,12 +1044,32 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
        }
        if (hwpoison_filter(p)) {
                if (TestClearPageHWPoison(p))
-                       atomic_long_dec(&mce_bad_pages);
-               unlock_page(p);
-               put_page(p);
+                       atomic_long_sub(nr_pages, &mce_bad_pages);
+               unlock_page(hpage);
+               put_page(hpage);
                return 0;
        }
 
+       /*
+        * For error on the tail page, we should set PG_hwpoison
+        * on the head page to show that the hugepage is hwpoisoned
+        */
+       if (PageTail(p) && TestSetPageHWPoison(hpage)) {
+               action_result(pfn, "hugepage already hardware poisoned",
+                               IGNORED);
+               unlock_page(hpage);
+               put_page(hpage);
+               return 0;
+       }
+       /*
+        * Set PG_hwpoison on all pages in an error hugepage,
+        * because containment is done in hugepage unit for now.
+        * Since we have done TestSetPageHWPoison() for the head page with
+        * page lock held, we can safely set PG_hwpoison bits on tail pages.
+        */
+       if (PageHuge(p))
+               set_page_hwpoison_huge_page(hpage);
+
        wait_on_page_writeback(p);
 
        /*
@@ -1039,7 +1099,7 @@ int __memory_failure(unsigned long pfn, int trapno, int flags)
                }
        }
 out:
-       unlock_page(p);
+       unlock_page(hpage);
        return res;
 }
 EXPORT_SYMBOL_GPL(__memory_failure);
@@ -1083,6 +1143,7 @@ int unpoison_memory(unsigned long pfn)
        struct page *page;
        struct page *p;
        int freeit = 0;
+       unsigned int nr_pages;
 
        if (!pfn_valid(pfn))
                return -ENXIO;
@@ -1091,14 +1152,16 @@ int unpoison_memory(unsigned long pfn)
        page = compound_head(p);
 
        if (!PageHWPoison(p)) {
-               pr_debug("MCE: Page was already unpoisoned %#lx\n", pfn);
+               pr_info("MCE: Page was already unpoisoned %#lx\n", pfn);
                return 0;
        }
 
+       nr_pages = 1 << compound_order(page);
+
        if (!get_page_unless_zero(page)) {
                if (TestClearPageHWPoison(p))
-                       atomic_long_dec(&mce_bad_pages);
-               pr_debug("MCE: Software-unpoisoned free page %#lx\n", pfn);
+                       atomic_long_sub(nr_pages, &mce_bad_pages);
+               pr_info("MCE: Software-unpoisoned free page %#lx\n", pfn);
                return 0;
        }
 
@@ -1109,11 +1172,13 @@ int unpoison_memory(unsigned long pfn)
         * the PG_hwpoison page will be caught and isolated on the entrance to
         * the free buddy page pool.
         */
-       if (TestClearPageHWPoison(p)) {
-               pr_debug("MCE: Software-unpoisoned page %#lx\n", pfn);
-               atomic_long_dec(&mce_bad_pages);
+       if (TestClearPageHWPoison(page)) {
+               pr_info("MCE: Software-unpoisoned page %#lx\n", pfn);
+               atomic_long_sub(nr_pages, &mce_bad_pages);
                freeit = 1;
        }
+       if (PageHuge(p))
+               clear_page_hwpoison_huge_page(page);
        unlock_page(page);
 
        put_page(page);
@@ -1157,12 +1222,12 @@ static int get_any_page(struct page *p, unsigned long pfn, int flags)
        set_migratetype_isolate(p);
        if (!get_page_unless_zero(compound_head(p))) {
                if (is_free_buddy_page(p)) {
-                       pr_debug("get_any_page: %#lx free buddy page\n", pfn);
+                       pr_info("get_any_page: %#lx free buddy page\n", pfn);
                        /* Set hwpoison bit while page is still isolated */
                        SetPageHWPoison(p);
                        ret = 0;
                } else {
-                       pr_debug("get_any_page: %#lx: unknown zero refcount page type %lx\n",
+                       pr_info("get_any_page: %#lx: unknown zero refcount page type %lx\n",
                                pfn, p->flags);
                        ret = -EIO;
                }
@@ -1228,7 +1293,7 @@ int soft_offline_page(struct page *page, int flags)
                        goto done;
        }
        if (!PageLRU(page)) {
-               pr_debug("soft_offline: %#lx: unknown non LRU page type %lx\n",
+               pr_info("soft_offline: %#lx: unknown non LRU page type %lx\n",
                                pfn, page->flags);
                return -EIO;
        }
@@ -1242,7 +1307,7 @@ int soft_offline_page(struct page *page, int flags)
        if (PageHWPoison(page)) {
                unlock_page(page);
                put_page(page);
-               pr_debug("soft offline: %#lx page already poisoned\n", pfn);
+               pr_info("soft offline: %#lx page already poisoned\n", pfn);
                return -EBUSY;
        }
 
@@ -1263,7 +1328,7 @@ int soft_offline_page(struct page *page, int flags)
        put_page(page);
        if (ret == 1) {
                ret = 0;
-               pr_debug("soft_offline: %#lx: invalidated\n", pfn);
+               pr_info("soft_offline: %#lx: invalidated\n", pfn);
                goto done;
        }
 
@@ -1279,13 +1344,13 @@ int soft_offline_page(struct page *page, int flags)
                list_add(&page->lru, &pagelist);
                ret = migrate_pages(&pagelist, new_page, MPOL_MF_MOVE_ALL, 0);
                if (ret) {
-                       pr_debug("soft offline: %#lx: migration failed %d, type %lx\n",
+                       pr_info("soft offline: %#lx: migration failed %d, type %lx\n",
                                pfn, ret, page->flags);
                        if (ret > 0)
                                ret = -EIO;
                }
        } else {
-               pr_debug("soft offline: %#lx: isolation failed: %d, page count %d, type %lx\n",
+               pr_info("soft offline: %#lx: isolation failed: %d, page count %d, type %lx\n",
                                pfn, ret, page_count(page), page->flags);
        }
        if (ret)
This page took 0.034724 seconds and 5 git commands to generate.