mm: hugetlbfs: move the put/get_page slab and hugetlbfs optimization in a faster...
[cascardo/linux.git] / mm / swap.c
index 84b26aa..e2757fb 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -86,45 +86,61 @@ static void put_compound_page(struct page *page)
                /* __split_huge_page_refcount can run under us */
                struct page *page_head = compound_trans_head(page);
 
+               /*
+                * THP can not break up slab pages so avoid taking
+                * compound_lock(). Slab performs non-atomic bit ops
+                * on page->flags for better performance. In
+                * particular slab_unlock() in slub used to be a hot
+                * path. It is still hot on arches that do not support
+                * this_cpu_cmpxchg_double().
+                *
+                * If "page" is part of a slab or hugetlbfs page it
+                * cannot be splitted and the head page cannot change
+                * from under us. And if "page" is part of a THP page
+                * under splitting, if the head page pointed by the
+                * THP tail isn't a THP head anymore, we'll find
+                * PageTail clear after smp_rmb() and we'll treat it
+                * as a single page.
+                */
+               if (PageSlab(page_head) || PageHeadHuge(page_head)) {
+                       /*
+                        * If "page" is a THP tail, we must read the tail page
+                        * flags after the head page flags. The
+                        * split_huge_page side enforces write memory
+                        * barriers between clearing PageTail and before the
+                        * head page can be freed and reallocated.
+                        */
+                       smp_rmb();
+                       if (likely(PageTail(page))) {
+                               /*
+                                * __split_huge_page_refcount
+                                * cannot race here.
+                                */
+                               VM_BUG_ON(!PageHead(page_head));
+                               VM_BUG_ON(page_mapcount(page) <= 0);
+                               atomic_dec(&page->_mapcount);
+                               if (put_page_testzero(page_head))
+                                       __put_compound_page(page_head);
+                               return;
+                       } else
+                               /*
+                                * __split_huge_page_refcount
+                                * run before us, "page" was a
+                                * THP tail. The split
+                                * page_head has been freed
+                                * and reallocated as slab or
+                                * hugetlbfs page of smaller
+                                * order (only possible if
+                                * reallocated as slab on
+                                * x86).
+                                */
+                               goto out_put_single;
+               }
+
                if (likely(page != page_head &&
                           get_page_unless_zero(page_head))) {
                        unsigned long flags;
 
-                       /*
-                        * THP can not break up slab pages so avoid taking
-                        * compound_lock().  Slab performs non-atomic bit ops
-                        * on page->flags for better performance.  In particular
-                        * slab_unlock() in slub used to be a hot path.  It is
-                        * still hot on arches that do not support
-                        * this_cpu_cmpxchg_double().
-                        */
-                       if (PageSlab(page_head) || PageHeadHuge(page_head)) {
-                               if (likely(PageTail(page))) {
-                                       /*
-                                        * __split_huge_page_refcount
-                                        * cannot race here.
-                                        */
-                                       VM_BUG_ON(!PageHead(page_head));
-                                       atomic_dec(&page->_mapcount);
-                                       if (put_page_testzero(page_head))
-                                               VM_BUG_ON(1);
-                                       if (put_page_testzero(page_head))
-                                               __put_compound_page(page_head);
-                                       return;
-                               } else
-                                       /*
-                                        * __split_huge_page_refcount
-                                        * run before us, "page" was a
-                                        * THP tail. The split
-                                        * page_head has been freed
-                                        * and reallocated as slab or
-                                        * hugetlbfs page of smaller
-                                        * order (only possible if
-                                        * reallocated as slab on
-                                        * x86).
-                                        */
-                                       goto skip_lock;
-                       }
                        /*
                         * page_head wasn't a dangling pointer but it
                         * may not be a head page anymore by the time
@@ -135,7 +151,6 @@ static void put_compound_page(struct page *page)
                        if (unlikely(!PageTail(page))) {
                                /* __split_huge_page_refcount run before us */
                                compound_unlock_irqrestore(page_head, flags);
-skip_lock:
                                if (put_page_testzero(page_head)) {
                                        /*
                                         * The head page may have been
@@ -221,36 +236,37 @@ bool __get_page_tail(struct page *page)
         * split_huge_page().
         */
        unsigned long flags;
-       bool got = false;
+       bool got;
        struct page *page_head = compound_trans_head(page);
 
-       if (likely(page != page_head && get_page_unless_zero(page_head))) {
-               /* Ref to put_compound_page() comment. */
-               if (PageSlab(page_head) || PageHeadHuge(page_head)) {
-                       if (likely(PageTail(page))) {
-                               /*
-                                * This is a hugetlbfs page or a slab
-                                * page. __split_huge_page_refcount
-                                * cannot race here.
-                                */
-                               VM_BUG_ON(!PageHead(page_head));
-                               __get_page_tail_foll(page, false);
-                               return true;
-                       } else {
-                               /*
-                                * __split_huge_page_refcount run
-                                * before us, "page" was a THP
-                                * tail. The split page_head has been
-                                * freed and reallocated as slab or
-                                * hugetlbfs page of smaller order
-                                * (only possible if reallocated as
-                                * slab on x86).
-                                */
-                               put_page(page_head);
-                               return false;
-                       }
+       /* Ref to put_compound_page() comment. */
+       if (PageSlab(page_head) || PageHeadHuge(page_head)) {
+               smp_rmb();
+               if (likely(PageTail(page))) {
+                       /*
+                        * This is a hugetlbfs page or a slab
+                        * page. __split_huge_page_refcount
+                        * cannot race here.
+                        */
+                       VM_BUG_ON(!PageHead(page_head));
+                       __get_page_tail_foll(page, true);
+                       return true;
+               } else {
+                       /*
+                        * __split_huge_page_refcount run
+                        * before us, "page" was a THP
+                        * tail. The split page_head has been
+                        * freed and reallocated as slab or
+                        * hugetlbfs page of smaller order
+                        * (only possible if reallocated as
+                        * slab on x86).
+                        */
+                       return false;
                }
+       }
 
+       got = false;
+       if (likely(page != page_head && get_page_unless_zero(page_head))) {
                /*
                 * page_head wasn't a dangling pointer but it
                 * may not be a head page anymore by the time