mm/gup: track FOLL_PIN pages
[linux-2.6-microblaze.git] / mm / huge_memory.c
index 24ad53b..b1e069e 100644 (file)
@@ -958,6 +958,11 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
         */
        WARN_ONCE(flags & FOLL_COW, "mm: In follow_devmap_pmd with FOLL_COW set");
 
+       /* FOLL_GET and FOLL_PIN are mutually exclusive. */
+       if (WARN_ON_ONCE((flags & (FOLL_PIN | FOLL_GET)) ==
+                        (FOLL_PIN | FOLL_GET)))
+               return NULL;
+
        if (flags & FOLL_WRITE && !pmd_write(*pmd))
                return NULL;
 
@@ -973,7 +978,7 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
         * device mapped pages can only be returned if the
         * caller will manage the page reference count.
         */
-       if (!(flags & FOLL_GET))
+       if (!(flags & (FOLL_GET | FOLL_PIN)))
                return ERR_PTR(-EEXIST);
 
        pfn += (addr & ~PMD_MASK) >> PAGE_SHIFT;
@@ -981,7 +986,8 @@ struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
        if (!*pgmap)
                return ERR_PTR(-EFAULT);
        page = pfn_to_page(pfn);
-       get_page(page);
+       if (!try_grab_page(page, flags))
+               page = ERR_PTR(-ENOMEM);
 
        return page;
 }
@@ -1101,6 +1107,11 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
        if (flags & FOLL_WRITE && !pud_write(*pud))
                return NULL;
 
+       /* FOLL_GET and FOLL_PIN are mutually exclusive. */
+       if (WARN_ON_ONCE((flags & (FOLL_PIN | FOLL_GET)) ==
+                        (FOLL_PIN | FOLL_GET)))
+               return NULL;
+
        if (pud_present(*pud) && pud_devmap(*pud))
                /* pass */;
        else
@@ -1112,8 +1123,10 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
        /*
         * device mapped pages can only be returned if the
         * caller will manage the page reference count.
+        *
+        * At least one of FOLL_GET | FOLL_PIN must be set, so assert that here:
         */
-       if (!(flags & FOLL_GET))
+       if (!(flags & (FOLL_GET | FOLL_PIN)))
                return ERR_PTR(-EEXIST);
 
        pfn += (addr & ~PUD_MASK) >> PAGE_SHIFT;
@@ -1121,7 +1134,8 @@ struct page *follow_devmap_pud(struct vm_area_struct *vma, unsigned long addr,
        if (!*pgmap)
                return ERR_PTR(-EFAULT);
        page = pfn_to_page(pfn);
-       get_page(page);
+       if (!try_grab_page(page, flags))
+               page = ERR_PTR(-ENOMEM);
 
        return page;
 }
@@ -1497,8 +1511,13 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
 
        page = pmd_page(*pmd);
        VM_BUG_ON_PAGE(!PageHead(page) && !is_zone_device_page(page), page);
+
+       if (!try_grab_page(page, flags))
+               return ERR_PTR(-ENOMEM);
+
        if (flags & FOLL_TOUCH)
                touch_pmd(vma, addr, pmd, flags);
+
        if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
                /*
                 * We don't mlock() pte-mapped THPs. This way we can avoid
@@ -1535,8 +1554,6 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
 skip_mlock:
        page += (addr & ~HPAGE_PMD_MASK) >> PAGE_SHIFT;
        VM_BUG_ON_PAGE(!PageCompound(page) && !is_zone_device_page(page), page);
-       if (flags & FOLL_GET)
-               get_page(page);
 
 out:
        return page;