The following oops was observed when mmap() with MAP_POPULATE
pre-faulted pmd mappings of a DAX file.  follow_trans_huge_pmd()
expects that a target address has a struct page.

  BUG: unable to handle kernel paging request at ffffea0012220000
  follow_trans_huge_pmd+0xba/0x390
  follow_page_mask+0x33d/0x420
  __get_user_pages+0xdc/0x800
  populate_vma_page_range+0xb5/0xe0
  __mm_populate+0xc5/0x150
  vm_mmap_pgoff+0xd5/0xe0
  SyS_mmap_pgoff+0x1c1/0x290
  SyS_mmap+0x1b/0x30

Fix it by making the PMD pre-fault handling consistent with PTE.
After pre-faulted in faultin_page(), follow_page_mask() calls
follow_trans_huge_pmd(), which is changed to call follow_pfn_pmd()
for VM_PFNMAP or VM_MIXEDMAP.  follow_pfn_pmd() handles FOLL_TOUCH
and returns with -EEXIST.

Reported-by: Mauricio Porto <[email protected]>
Signed-off-by: Toshi Kani <[email protected]>
Cc: Andrew Morton <[email protected]>
Cc: Kirill A. Shutemov <[email protected]>
Cc: Matthew Wilcox <[email protected]>
Cc: Dan Williams <[email protected]>
Cc: Ross Zwisler <[email protected]>
---
 mm/huge_memory.c |   34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)

diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index d5b8920..f56e034 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1267,6 +1267,32 @@ out_unlock:
        return ret;
 }
 
+/*
+ * Follow a pmd inserted by vmf_insert_pfn_pmd(). See follow_pfn_pte() for pte.
+ */
+static int follow_pfn_pmd(struct vm_area_struct *vma, unsigned long address,
+               pmd_t *pmd, unsigned int flags)
+{
+       /* No page to get reference */
+       if (flags & FOLL_GET)
+               return -EFAULT;
+
+       if (flags & FOLL_TOUCH) {
+               pmd_t entry = *pmd;
+
+               /* Set the dirty bit per follow_trans_huge_pmd() */
+               entry = pmd_mkyoung(pmd_mkdirty(entry));
+
+               if (!pmd_same(*pmd, entry)) {
+                       set_pmd_at(vma->vm_mm, address, pmd, entry);
+                       update_mmu_cache_pmd(vma, address, pmd);
+               }
+       }
+
+       /* Proper page table entry exists, but no corresponding struct page */
+       return -EEXIST;
+}
+
 struct page *follow_trans_huge_pmd(struct vm_area_struct *vma,
                                   unsigned long addr,
                                   pmd_t *pmd,
@@ -1274,6 +1300,7 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct 
*vma,
 {
        struct mm_struct *mm = vma->vm_mm;
        struct page *page = NULL;
+       int ret;
 
        assert_spin_locked(pmd_lockptr(mm, pmd));
 
@@ -1288,6 +1315,13 @@ struct page *follow_trans_huge_pmd(struct vm_area_struct 
*vma,
        if ((flags & FOLL_NUMA) && pmd_protnone(*pmd))
                goto out;
 
+       /* pfn map does not have a struct page */
+       if (vma->vm_flags & (VM_PFNMAP | VM_MIXEDMAP)) {
+               ret = follow_pfn_pmd(vma, addr, pmd, flags);
+               page = ERR_PTR(ret);
+               goto out;
+       }
+
        page = pmd_page(*pmd);
        VM_BUG_ON_PAGE(!PageHead(page), page);
        if (flags & FOLL_TOUCH) {
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
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