|
@@ -48,11 +48,22 @@ static inline int gup_pte_range(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+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 inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
|
|
|
unsigned long end, int write, struct page **pages, int *nr)
|
|
|
{
|
|
|
unsigned long mask, result;
|
|
|
- struct page *head, *page;
|
|
|
+ struct page *head, *page, *tail;
|
|
|
int refs;
|
|
|
|
|
|
result = write ? 0 : _SEGMENT_ENTRY_RO;
|
|
@@ -64,6 +75,7 @@ static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
|
|
|
refs = 0;
|
|
|
head = pmd_page(pmd);
|
|
|
page = head + ((addr & ~PMD_MASK) >> PAGE_SHIFT);
|
|
|
+ tail = page;
|
|
|
do {
|
|
|
VM_BUG_ON(compound_head(page) != head);
|
|
|
pages[*nr] = page;
|
|
@@ -81,6 +93,16 @@ static inline int gup_huge_pmd(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
|
|
|
*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;
|