]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
mm: tail page refcounting optimization for slab and hugetlbfs
authorAndrea Arcangeli <aarcange@redhat.com>
Mon, 16 Dec 2013 23:45:02 +0000 (10:45 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Mon, 16 Dec 2013 23:45:02 +0000 (10:45 +1100)
This skips the _mapcount mangling for slab and hugetlbfs pages.

The main trouble in doing this is to guarantee that PageSlab and
PageHeadHuge remains constant for all get_page/put_page run on the
tail of slab or hugetlbfs compound pages. Otherwise if they're set
during get_page but not set during put_page, the _mapcount of the tail
page would underflow.

PageHeadHuge will remain true until the compound page is released and
enters the buddy allocator so it won't risk to change even if the tail
page is the last reference left on the page.

PG_slab instead is cleared before the slab frees the head page with
put_page, so if the tail pin is released after the slab freed the
page, we would have a problem. But in the slab case the tail pin
cannot be the last reference left on the page. This is because the
slab code is free to reuse the compound page after a
kfree/kmem_cache_free without having to check if there's any tail pin
left. In turn all tail pins must be always released while the head is
still pinned by the slab code and so we know PG_slab will be still set
too.

Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>
Reviewed-by: Khalid Aziz <khalid.aziz@oracle.com>
Cc: Pravin Shelar <pshelar@nicira.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Cc: Christoph Lameter <cl@linux.com>
Cc: Johannes Weiner <jweiner@redhat.com>
Cc: Mel Gorman <mgorman@suse.de>
Cc: Rik van Riel <riel@redhat.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: Minchan Kim <minchan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
include/linux/hugetlb.h
include/linux/mm.h
mm/internal.h
mm/swap.c

index 251233c1494df793ae7d9d90e4bc5ed5e794ab28..d01cc972a1d97ac056f61b7de662418c6acf1755 100644 (file)
@@ -31,7 +31,6 @@ struct hugepage_subpool *hugepage_new_subpool(long nr_blocks);
 void hugepage_put_subpool(struct hugepage_subpool *spool);
 
 int PageHuge(struct page *page);
-int PageHeadHuge(struct page *page_head);
 
 void reset_vma_resv_huge_pages(struct vm_area_struct *vma);
 int hugetlb_sysctl_handler(struct ctl_table *, int, void __user *, size_t *, loff_t *);
@@ -104,11 +103,6 @@ static inline int PageHuge(struct page *page)
        return 0;
 }
 
-static inline int PageHeadHuge(struct page *page_head)
-{
-       return 0;
-}
-
 static inline void reset_vma_resv_huge_pages(struct vm_area_struct *vma)
 {
 }
index 1cedd000cf293f4486575b87086b01e2ee1b26e5..e3d053e8768c175b265f9ccdfae7a7c63b50e5ea 100644 (file)
@@ -414,15 +414,45 @@ static inline int page_count(struct page *page)
        return atomic_read(&compound_head(page)->_count);
 }
 
+#ifdef CONFIG_HUGETLB_PAGE
+extern int PageHeadHuge(struct page *page_head);
+#else /* CONFIG_HUGETLB_PAGE */
+static inline int PageHeadHuge(struct page *page_head)
+{
+       return 0;
+}
+#endif /* CONFIG_HUGETLB_PAGE */
+
+static inline bool __compound_tail_refcounted(struct page *page)
+{
+       return !PageSlab(page) && !PageHeadHuge(page);
+}
+
+/*
+ * This takes a head page as parameter and tells if the
+ * tail page reference counting can be skipped.
+ *
+ * For this to be safe, PageSlab and PageHeadHuge must remain true on
+ * any given page where they return true here, until all tail pins
+ * have been released.
+ */
+static inline bool compound_tail_refcounted(struct page *page)
+{
+       VM_BUG_ON(!PageHead(page));
+       return __compound_tail_refcounted(page);
+}
+
 static inline void get_huge_page_tail(struct page *page)
 {
        /*
         * __split_huge_page_refcount() cannot run
         * from under us.
+        * In turn no need of compound_trans_head here.
         */
        VM_BUG_ON(page_mapcount(page) < 0);
        VM_BUG_ON(atomic_read(&page->_count) != 0);
-       atomic_inc(&page->_mapcount);
+       if (compound_tail_refcounted(compound_head(page)))
+               atomic_inc(&page->_mapcount);
 }
 
 extern bool __get_page_tail(struct page *page);
index 684f7aa9692aecc9e002a3095468a23c5c5c4ed4..a85a3ab1f7ef2d2d77b736fd627b3c8fde1341f9 100644 (file)
@@ -51,7 +51,8 @@ static inline void __get_page_tail_foll(struct page *page,
        VM_BUG_ON(page_mapcount(page) < 0);
        if (get_page_head)
                atomic_inc(&page->first_page->_count);
-       atomic_inc(&page->_mapcount);
+       if (compound_tail_refcounted(page->first_page))
+               atomic_inc(&page->_mapcount);
 }
 
 /*
index dbf5427672e2043c9cf45dbca52eb4492854f8c7..b4c49bf626f45a368990cb20a8e4a05baef58d27 100644 (file)
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -88,8 +88,9 @@ static void put_compound_page(struct page *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
+                * compound_lock() and skip the tail page refcounting
+                * (in _mapcount) too. 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().
@@ -102,7 +103,7 @@ static void put_compound_page(struct page *page)
                 * PageTail clear after smp_rmb() and we'll threat it
                 * as a single page.
                 */
-               if (PageSlab(page_head) || PageHeadHuge(page_head)) {
+               if (!__compound_tail_refcounted(page_head)) {
                        /*
                         * If "page" is a THP tail, we must read the tail page
                         * flags after the head page flags. The
@@ -117,10 +118,30 @@ static void put_compound_page(struct page *page)
                                 * 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))
+                               VM_BUG_ON(page_mapcount(page) != 0);
+                               if (put_page_testzero(page_head)) {
+                                       /*
+                                        * If this is the tail of a
+                                        * slab compound page, the
+                                        * tail pin must not be the
+                                        * last reference held on the
+                                        * page, because the PG_slab
+                                        * cannot be cleared before
+                                        * all tail pins (which skips
+                                        * the _mapcount tail
+                                        * refcounting) have been
+                                        * released. For hugetlbfs the
+                                        * tail pin may be the last
+                                        * reference on the page
+                                        * instead, because
+                                        * PageHeadHuge will not go
+                                        * away until the compound
+                                        * page enters the buddy
+                                        * allocator.
+                                        */
+                                       VM_BUG_ON(PageSlab(page_head));
                                        __put_compound_page(page_head);
+                               }
                                return;
                        } else
                                /*