hugetlbfs: Use i_mmap_rwsem to fix page fault/truncate race
[sfrench/cifs-2.6.git] / mm / hugetlb.c
index 87fd3ab809c68feab81c67a19837c5b8a5557ce8..e37efd5d831830123ca117a0efec6eead901eaa7 100644 (file)
@@ -3755,16 +3755,16 @@ static vm_fault_t hugetlb_no_page(struct mm_struct *mm,
        }
 
        /*
-        * Use page lock to guard against racing truncation
-        * before we get page_table_lock.
+        * We can not race with truncation due to holding i_mmap_rwsem.
+        * Check once here for faults beyond end of file.
         */
+       size = i_size_read(mapping->host) >> huge_page_shift(h);
+       if (idx >= size)
+               goto out;
+
 retry:
        page = find_lock_page(mapping, idx);
        if (!page) {
-               size = i_size_read(mapping->host) >> huge_page_shift(h);
-               if (idx >= size)
-                       goto out;
-
                /*
                 * Check for page in userfault range
                 */
@@ -3854,9 +3854,6 @@ retry:
        }
 
        ptl = huge_pte_lock(h, mm, ptep);
-       size = i_size_read(mapping->host) >> huge_page_shift(h);
-       if (idx >= size)
-               goto backout;
 
        ret = 0;
        if (!huge_pte_none(huge_ptep_get(ptep)))
@@ -3959,8 +3956,10 @@ vm_fault_t hugetlb_fault(struct mm_struct *mm, struct vm_area_struct *vma,
 
        /*
         * Acquire i_mmap_rwsem before calling huge_pte_alloc and hold
-        * until finished with ptep.  This prevents huge_pmd_unshare from
-        * being called elsewhere and making the ptep no longer valid.
+        * until finished with ptep.  This serves two purposes:
+        * 1) It prevents huge_pmd_unshare from being called elsewhere
+        *    and making the ptep no longer valid.
+        * 2) It synchronizes us with file truncation.
         *
         * ptep could have already be assigned via huge_pte_offset.  That
         * is OK, as huge_pte_alloc will return the same value unless