|
@@ -385,12 +385,23 @@ follow_huge_pmd(struct mm_struct *mm, unsigned long address,
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
+static inline void get_huge_page_tail(struct page *page)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * __split_huge_page_refcount() cannot run
|
|
|
+ * from under us.
|
|
|
+ */
|
|
|
+ VM_BUG_ON(page_mapcount(page) < 0);
|
|
|
+ VM_BUG_ON(atomic_read(&page->_count) != 0);
|
|
|
+ atomic_inc(&page->_mapcount);
|
|
|
+}
|
|
|
+
|
|
|
static noinline int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long addr,
|
|
|
unsigned long end, int write, struct page **pages, int *nr)
|
|
|
{
|
|
|
unsigned long mask;
|
|
|
unsigned long pte_end;
|
|
|
- struct page *head, *page;
|
|
|
+ struct page *head, *page, *tail;
|
|
|
pte_t pte;
|
|
|
int refs;
|
|
|
|
|
@@ -413,6 +424,7 @@ static noinline int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long add
|
|
|
head = pte_page(pte);
|
|
|
|
|
|
page = head + ((addr & (sz-1)) >> PAGE_SHIFT);
|
|
|
+ tail = page;
|
|
|
do {
|
|
|
VM_BUG_ON(compound_head(page) != head);
|
|
|
pages[*nr] = page;
|
|
@@ -431,6 +443,16 @@ static noinline int gup_hugepte(pte_t *ptep, unsigned long sz, unsigned long add
|
|
|
*nr -= refs;
|
|
|
while (refs--)
|
|
|
put_page(head);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Any tail page need their mapcount reference taken
|
|
|
+ * before we return.
|
|
|
+ */
|
|
|
+ while (refs--) {
|
|
|
+ if (PageTail(tail))
|
|
|
+ get_huge_page_tail(tail);
|
|
|
+ tail++;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return 1;
|