Factor out one small part of the shmem pmd handling: the arch-specific
get_user_pages_fast() has special code to cope with the peculiar
refcounting on anonymous THP tail pages (and on hugetlbfs tail pages):
which must be avoided in the straightforward shmem pmd case.

Signed-off-by: Hugh Dickins <hu...@google.com>
---
 arch/mips/mm/gup.c  |   17 ++++++++++++-----
 arch/s390/mm/gup.c  |   22 +++++++++++++++++++++-
 arch/sparc/mm/gup.c |   22 +++++++++++++++++++++-
 arch/x86/mm/gup.c   |   17 ++++++++++++-----
 mm/gup.c            |   22 +++++++++++++++++++++-
 5 files changed, 87 insertions(+), 13 deletions(-)

--- thpfs.orig/arch/mips/mm/gup.c       2015-02-08 18:54:22.000000000 -0800
+++ thpfs/arch/mips/mm/gup.c    2015-02-20 19:34:26.971957306 -0800
@@ -64,7 +64,8 @@ static inline void get_head_page_multipl
 {
        VM_BUG_ON(page != compound_head(page));
        VM_BUG_ON(page_count(page) == 0);
-       atomic_add(nr, &page->_count);
+       if (nr)
+               atomic_add(nr, &page->_count);
        SetPageReferenced(page);
 }
 
@@ -85,13 +86,19 @@ static int gup_huge_pmd(pmd_t pmd, unsig
        head = pte_page(pte);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
        do {
-               VM_BUG_ON(compound_head(page) != head);
-               pages[*nr] = page;
-               if (PageTail(page))
+               if (PageTail(page)) {
+                       VM_BUG_ON(compound_head(page) != head);
                        get_huge_page_tail(page);
+                       refs++;
+               } else {
+                       /*
+                        * Handle head or huge tmpfs with normal refcounting.
+                        */
+                       get_page(page);
+               }
+               pages[*nr] = page;
                (*nr)++;
                page++;
-               refs++;
        } while (addr += PAGE_SIZE, addr != end);
 
        get_head_page_multiple(head, refs);
--- thpfs.orig/arch/s390/mm/gup.c       2014-01-19 18:40:07.000000000 -0800
+++ thpfs/arch/s390/mm/gup.c    2015-02-20 19:34:26.971957306 -0800
@@ -61,10 +61,30 @@ static inline int gup_huge_pmd(pmd_t *pm
                return 0;
        VM_BUG_ON(!pfn_valid(pmd_val(pmd) >> PAGE_SHIFT));
 
-       refs = 0;
        head = pmd_page(pmd);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
+
+       if (!PageHead(head)) {
+               /*
+                * Handle a huge tmpfs team with normal refcounting.
+                */
+               do {
+                       if (!page_cache_get_speculative(page))
+                               return 0;
+                       if (unlikely(pmd_val(pmd) != pmd_val(*pmdp))) {
+                               put_page(page);
+                               return 0;
+                       }
+                       pages[*nr] = page;
+                       (*nr)++;
+                       page++;
+               } while (addr += PAGE_SIZE, addr != end);
+               return 1;
+       }
+
        tail = page;
+       refs = 0;
+
        do {
                VM_BUG_ON(compound_head(page) != head);
                pages[*nr] = page;
--- thpfs.orig/arch/sparc/mm/gup.c      2014-12-07 14:21:05.000000000 -0800
+++ thpfs/arch/sparc/mm/gup.c   2015-02-20 19:34:26.975957297 -0800
@@ -79,10 +79,30 @@ static int gup_huge_pmd(pmd_t *pmdp, pmd
        if (write && !pmd_write(pmd))
                return 0;
 
-       refs = 0;
        head = pmd_page(pmd);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
+
+       if (!PageHead(head)) {
+               /*
+                * Handle a huge tmpfs team with normal refcounting.
+                */
+               do {
+                       if (!page_cache_get_speculative(page))
+                               return 0;
+                       if (unlikely(pmd_val(pmd) != pmd_val(*pmdp))) {
+                               put_page(page);
+                               return 0;
+                       }
+                       pages[*nr] = page;
+                       (*nr)++;
+                       page++;
+               } while (addr += PAGE_SIZE, addr != end);
+               return 1;
+       }
+
        tail = page;
+       refs = 0;
+
        do {
                VM_BUG_ON(compound_head(page) != head);
                pages[*nr] = page;
--- thpfs.orig/arch/x86/mm/gup.c        2015-02-08 18:54:22.000000000 -0800
+++ thpfs/arch/x86/mm/gup.c     2015-02-20 19:34:26.975957297 -0800
@@ -110,7 +110,8 @@ static inline void get_head_page_multipl
 {
        VM_BUG_ON_PAGE(page != compound_head(page), page);
        VM_BUG_ON_PAGE(page_count(page) == 0, page);
-       atomic_add(nr, &page->_count);
+       if (nr)
+               atomic_add(nr, &page->_count);
        SetPageReferenced(page);
 }
 
@@ -135,13 +136,19 @@ static noinline int gup_huge_pmd(pmd_t p
        head = pte_page(pte);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
        do {
-               VM_BUG_ON_PAGE(compound_head(page) != head, page);
-               pages[*nr] = page;
-               if (PageTail(page))
+               if (PageTail(page)) {
+                       VM_BUG_ON_PAGE(compound_head(page) != head, page);
                        get_huge_page_tail(page);
+                       refs++;
+               } else {
+                       /*
+                        * Handle head or huge tmpfs with normal refcounting.
+                        */
+                       get_page(page);
+               }
+               pages[*nr] = page;
                (*nr)++;
                page++;
-               refs++;
        } while (addr += PAGE_SIZE, addr != end);
        get_head_page_multiple(head, refs);
 
--- thpfs.orig/mm/gup.c 2015-02-08 18:54:22.000000000 -0800
+++ thpfs/mm/gup.c      2015-02-20 19:34:26.975957297 -0800
@@ -795,10 +795,30 @@ static int gup_huge_pmd(pmd_t orig, pmd_
        if (write && !pmd_write(orig))
                return 0;
 
-       refs = 0;
        head = pmd_page(orig);
        page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
+
+       if (!PageHead(head)) {
+               /*
+                * Handle a huge tmpfs team with normal refcounting.
+                */
+               do {
+                       if (!page_cache_get_speculative(page))
+                               return 0;
+                       if (unlikely(pmd_val(orig) != pmd_val(*pmdp))) {
+                               put_page(page);
+                               return 0;
+                       }
+                       pages[*nr] = page;
+                       (*nr)++;
+                       page++;
+               } while (addr += PAGE_SIZE, addr != end);
+               return 1;
+       }
+
        tail = page;
+       refs = 0;
+
        do {
                VM_BUG_ON_PAGE(compound_head(page) != head, page);
                pages[*nr] = page;
--
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