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/