1. Add the new helper, compound_head_put_tail(page) which returns
   the stable compound_head() and drops the reference to this page
   if it is the sub-page of __compound_tail_refcounted(head).

   Note: this patch tries to be as simple as possible. But we need
   to unify the new helper with __put_page_tail() later, or at least
   factor out the common code.

2. Remove the nasty __get_user_pages_fast() code in get_futex_key(),
   it can use the new helper to get page_head.

   Note: with or without this change basepage_index(page) after
   put_page(page) looks confusing at least, this will be addressed
   later.

Suggested-by: Andrea Arcangeli <aarca...@redhat.com>
Signed-off-by: Oleg Nesterov <o...@redhat.com>
---
 include/linux/mm.h |    2 ++
 kernel/futex.c     |   37 +------------------------------------
 mm/swap.c          |   35 +++++++++++++++++++++++++++++++++++
 3 files changed, 38 insertions(+), 36 deletions(-)

diff --git a/include/linux/mm.h b/include/linux/mm.h
index 13495bd..545df45 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -501,6 +501,8 @@ static inline void __ClearPageBuddy(struct page *page)
 void put_page(struct page *page);
 void put_pages_list(struct list_head *pages);
 
+struct page *compound_head_put_tail(struct page *page);
+
 void split_page(struct page *page, unsigned int order);
 int split_free_page(struct page *page);
 
diff --git a/kernel/futex.c b/kernel/futex.c
index c3a1a55..1fd7031 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -282,42 +282,7 @@ again:
        else
                err = 0;
 
-#ifdef CONFIG_TRANSPARENT_HUGEPAGE
-       page_head = page;
-       if (unlikely(PageTail(page))) {
-               put_page(page);
-               /* serialize against __split_huge_page_splitting() */
-               local_irq_disable();
-               if (likely(__get_user_pages_fast(address, 1, 1, &page) == 1)) {
-                       page_head = compound_head(page);
-                       /*
-                        * page_head is valid pointer but we must pin
-                        * it before taking the PG_lock and/or
-                        * PG_compound_lock. The moment we re-enable
-                        * irqs __split_huge_page_splitting() can
-                        * return and the head page can be freed from
-                        * under us. We can't take the PG_lock and/or
-                        * PG_compound_lock on a page that could be
-                        * freed from under us.
-                        */
-                       if (page != page_head) {
-                               get_page(page_head);
-                               put_page(page);
-                       }
-                       local_irq_enable();
-               } else {
-                       local_irq_enable();
-                       goto again;
-               }
-       }
-#else
-       page_head = compound_head(page);
-       if (page != page_head) {
-               get_page(page_head);
-               put_page(page);
-       }
-#endif
-
+       page_head = compound_head_put_tail(page);
        lock_page(page_head);
 
        /*
diff --git a/mm/swap.c b/mm/swap.c
index 972923d..6742c85 100644
--- a/mm/swap.c
+++ b/mm/swap.c
@@ -215,6 +215,41 @@ void put_page(struct page *page)
 EXPORT_SYMBOL(put_page);
 
 /*
+ * Like compound_head() but also drops the additional reference
+ * this page can have. Unlike compound_head() it returns the page
+ * which has a reference, and can't race with split_huge_page().
+ */
+struct page *compound_head_put_tail(struct page *page)
+{
+       struct page *page_head;
+       unsigned long flags;
+
+       if (!PageTail(page))
+               return page;
+
+       page_head = compound_trans_head(page);
+
+       if (!__compound_tail_refcounted(page_head)) {
+               smp_rmb();
+               if (likely(PageTail(page)))
+                       return page_head;
+               else
+                       return page;
+       }
+
+       if (likely(get_lock_thp_head(page_head, page, &flags))) {
+               if (put_page_testzero(page_head))
+                       VM_BUG_ON(1);
+               atomic_dec(&page->_mapcount);
+               compound_unlock_irqrestore(page_head, flags);
+
+               return page_head;
+       }
+
+       return page;
+}
+
+/*
  * This function is exported but must not be called by anything other
  * than get_page(). It implements the slow path of get_page().
  */
-- 
1.5.5.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to