Skip to content

Commit 2be7cfe

Browse files
danieljordan10torvalds
authored andcommitted
mm/hugetlb.c: __get_user_pages ignores certain follow_hugetlb_page errors
Commit 9a291a7 ("mm/hugetlb: report -EHWPOISON not -EFAULT when FOLL_HWPOISON is specified") causes __get_user_pages to ignore certain errors from follow_hugetlb_page. After such error, __get_user_pages subsequently calls faultin_page on the same VMA and start address that follow_hugetlb_page failed on instead of returning the error immediately as it should. In follow_hugetlb_page, when hugetlb_fault returns a value covered under VM_FAULT_ERROR, follow_hugetlb_page returns it without setting nr_pages to 0 as __get_user_pages expects in this case, which causes the following to happen in __get_user_pages: the "while (nr_pages)" check succeeds, we skip the "if (!vma..." check because we got a VMA the last time around, we find no page with follow_page_mask, and we call faultin_page, which calls hugetlb_fault for the second time. This issue also slightly changes how __get_user_pages works. Before, it only returned error if it had made no progress (i = 0). But now, follow_hugetlb_page can clobber "i" with an error code since its new return path doesn't check for progress. So if "i" is nonzero before a failing call to follow_hugetlb_page, that indication of progress is lost and __get_user_pages can return error even if some pages were successfully pinned. To fix this, change follow_hugetlb_page so that it updates nr_pages, allowing __get_user_pages to fail immediately and restoring the "error only if no progress" behavior to __get_user_pages. Tested that __get_user_pages returns when expected on error from hugetlb_fault in follow_hugetlb_page. Fixes: 9a291a7 ("mm/hugetlb: report -EHWPOISON not -EFAULT when FOLL_HWPOISON is specified") Link: http://lkml.kernel.org/r/1500406795-58462-1-git-send-email-daniel.m.jordan@oracle.com Signed-off-by: Daniel Jordan <daniel.m.jordan@oracle.com> Acked-by: Punit Agrawal <punit.agrawal@arm.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: "Aneesh Kumar K.V" <aneesh.kumar@linux.vnet.ibm.com> Cc: Gerald Schaefer <gerald.schaefer@de.ibm.com> Cc: James Morse <james.morse@arm.com> Cc: "Kirill A. Shutemov" <kirill.shutemov@linux.intel.com> Cc: Michal Hocko <mhocko@suse.com> Cc: Mike Kravetz <mike.kravetz@oracle.com> Cc: Naoya Horiguchi <n-horiguchi@ah.jp.nec.com> Cc: zhong jiang <zhongjiang@huawei.com> Cc: <stable@vger.kernel.org> [4.12.x] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 4d3f5d0 commit 2be7cfe

File tree

1 file changed

+3
-6
lines changed

1 file changed

+3
-6
lines changed

mm/hugetlb.c

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4078,6 +4078,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
40784078
unsigned long vaddr = *position;
40794079
unsigned long remainder = *nr_pages;
40804080
struct hstate *h = hstate_vma(vma);
4081+
int err = -EFAULT;
40814082

40824083
while (vaddr < vma->vm_end && remainder) {
40834084
pte_t *pte;
@@ -4154,11 +4155,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
41544155
}
41554156
ret = hugetlb_fault(mm, vma, vaddr, fault_flags);
41564157
if (ret & VM_FAULT_ERROR) {
4157-
int err = vm_fault_to_errno(ret, flags);
4158-
4159-
if (err)
4160-
return err;
4161-
4158+
err = vm_fault_to_errno(ret, flags);
41624159
remainder = 0;
41634160
break;
41644161
}
@@ -4213,7 +4210,7 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
42134210
*/
42144211
*position = vaddr;
42154212

4216-
return i ? i : -EFAULT;
4213+
return i ? i : err;
42174214
}
42184215

42194216
#ifndef __HAVE_ARCH_FLUSH_HUGETLB_TLB_RANGE

0 commit comments

Comments
 (0)