]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - mm/memory-failure.c
mm: hwpoison: adjust for new thp refcounting
[karo-tx-linux.git] / mm / memory-failure.c
index 05e079bf9425b238aa0ef64dee6f2115ba6fcfa4..1b99403d76f2673b8e80231dc9a3fced81328a2b 100644 (file)
@@ -882,15 +882,7 @@ int get_hwpoison_page(struct page *page)
 {
        struct page *head = compound_head(page);
 
-       if (PageHuge(head))
-               return get_page_unless_zero(head);
-
-       /*
-        * Thp tail page has special refcounting rule (refcount of tail pages
-        * is stored in ->_mapcount,) so we can't call get_page_unless_zero()
-        * directly for tail pages.
-        */
-       if (PageTransHuge(head)) {
+       if (!PageHuge(head) && PageTransHuge(head)) {
                /*
                 * Non anonymous thp exists only in allocation/free time. We
                 * can't handle such a case correctly, so let's give it up.
@@ -902,41 +894,12 @@ int get_hwpoison_page(struct page *page)
                                page_to_pfn(page));
                        return 0;
                }
-
-               if (get_page_unless_zero(head)) {
-                       if (PageTail(page))
-                               get_page(page);
-                       return 1;
-               } else {
-                       return 0;
-               }
        }
 
-       return get_page_unless_zero(page);
+       return get_page_unless_zero(head);
 }
 EXPORT_SYMBOL_GPL(get_hwpoison_page);
 
-/**
- * put_hwpoison_page() - Put refcount for memory error handling:
- * @page:      raw error page (hit by memory error)
- */
-void put_hwpoison_page(struct page *page)
-{
-       struct page *head = compound_head(page);
-
-       if (PageHuge(head)) {
-               put_page(head);
-               return;
-       }
-
-       if (PageTransHuge(head))
-               if (page != head)
-                       put_page(head);
-
-       put_page(page);
-}
-EXPORT_SYMBOL_GPL(put_hwpoison_page);
-
 /*
  * Do all that is necessary to remove user space mappings. Unmap
  * the pages and send SIGBUS to the processes if the data was dirty.
@@ -1162,6 +1125,8 @@ int memory_failure(unsigned long pfn, int trapno, int flags)
                        return -EBUSY;
                }
                unlock_page(hpage);
+               get_hwpoison_page(p);
+               put_hwpoison_page(hpage);
                VM_BUG_ON_PAGE(!page_count(p), p);
                hpage = compound_head(p);
        }
@@ -1753,24 +1718,28 @@ int soft_offline_page(struct page *page, int flags)
                        put_hwpoison_page(page);
                return -EBUSY;
        }
-       if (!PageHuge(page) && PageTransHuge(hpage)) {
-               lock_page(page);
-               ret = split_huge_page(hpage);
-               unlock_page(page);
-               if (unlikely(ret)) {
-                       pr_info("soft offline: %#lx: failed to split THP\n",
-                               pfn);
-                       if (flags & MF_COUNT_INCREASED)
-                               put_hwpoison_page(page);
-                       return -EBUSY;
-               }
-       }
 
        get_online_mems();
-
        ret = get_any_page(page, pfn, flags);
        put_online_mems();
+
        if (ret > 0) { /* for in-use pages */
+               if (!PageHuge(page) && PageTransHuge(hpage)) {
+                       lock_page(hpage);
+                       ret = split_huge_page(hpage);
+                       unlock_page(hpage);
+                       if (unlikely(ret || PageTransCompound(page) ||
+                                       !PageAnon(page))) {
+                               pr_info("soft offline: %#lx: failed to split THP\n",
+                                       pfn);
+                               if (flags & MF_COUNT_INCREASED)
+                                       put_hwpoison_page(hpage);
+                               return -EBUSY;
+                       }
+                       get_hwpoison_page(page);
+                       put_hwpoison_page(hpage);
+               }
+
                if (PageHuge(page))
                        ret = soft_offline_huge_page(page, flags);
                else