Skip to content

Commit 87ffc11

Browse files
aagittorvalds
authored andcommitted
userfaultfd: hugetlbfs: gup: support VM_FAULT_RETRY
Add support for VM_FAULT_RETRY to follow_hugetlb_page() so that get_user_pages_unlocked/locked and "nonblocking/FOLL_NOWAIT" features will work on hugetlbfs. This is required for fully functional userfaultfd non-present support on hugetlbfs. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Andrea Arcangeli <[email protected]> Reviewed-by: Mike Kravetz <[email protected]> Cc: "Dr. David Alan Gilbert" <[email protected]> Cc: Hillf Danton <[email protected]> Cc: Michael Rapoport <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Pavel Emelyanov <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 369cd21 commit 87ffc11

File tree

3 files changed

+44
-11
lines changed

3 files changed

+44
-11
lines changed

include/linux/hugetlb.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ int hugetlb_mempolicy_sysctl_handler(struct ctl_table *, int,
6565
int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *);
6666
long follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *,
6767
struct page **, struct vm_area_struct **,
68-
unsigned long *, unsigned long *, long, unsigned int);
68+
unsigned long *, unsigned long *, long, unsigned int,
69+
int *);
6970
void unmap_hugepage_range(struct vm_area_struct *,
7071
unsigned long, unsigned long, struct page *);
7172
void __unmap_hugepage_range_final(struct mmu_gather *tlb,
@@ -136,7 +137,7 @@ static inline unsigned long hugetlb_total_pages(void)
136137
return 0;
137138
}
138139

139-
#define follow_hugetlb_page(m,v,p,vs,a,b,i,w) ({ BUG(); 0; })
140+
#define follow_hugetlb_page(m,v,p,vs,a,b,i,w,n) ({ BUG(); 0; })
140141
#define follow_huge_addr(mm, addr, write) ERR_PTR(-EINVAL)
141142
#define copy_hugetlb_page_range(src, dst, vma) ({ BUG(); 0; })
142143
static inline void hugetlb_report_meminfo(struct seq_file *m)

mm/gup.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ static long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
572572
if (is_vm_hugetlb_page(vma)) {
573573
i = follow_hugetlb_page(mm, vma, pages, vmas,
574574
&start, &nr_pages, i,
575-
gup_flags);
575+
gup_flags, nonblocking);
576576
continue;
577577
}
578578
}

mm/hugetlb.c

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4065,7 +4065,7 @@ int hugetlb_mcopy_atomic_pte(struct mm_struct *dst_mm,
40654065
long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
40664066
struct page **pages, struct vm_area_struct **vmas,
40674067
unsigned long *position, unsigned long *nr_pages,
4068-
long i, unsigned int flags)
4068+
long i, unsigned int flags, int *nonblocking)
40694069
{
40704070
unsigned long pfn_offset;
40714071
unsigned long vaddr = *position;
@@ -4128,16 +4128,43 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
41284128
((flags & FOLL_WRITE) &&
41294129
!huge_pte_write(huge_ptep_get(pte)))) {
41304130
int ret;
4131+
unsigned int fault_flags = 0;
41314132

41324133
if (pte)
41334134
spin_unlock(ptl);
4134-
ret = hugetlb_fault(mm, vma, vaddr,
4135-
(flags & FOLL_WRITE) ? FAULT_FLAG_WRITE : 0);
4136-
if (!(ret & VM_FAULT_ERROR))
4137-
continue;
4138-
4139-
remainder = 0;
4140-
break;
4135+
if (flags & FOLL_WRITE)
4136+
fault_flags |= FAULT_FLAG_WRITE;
4137+
if (nonblocking)
4138+
fault_flags |= FAULT_FLAG_ALLOW_RETRY;
4139+
if (flags & FOLL_NOWAIT)
4140+
fault_flags |= FAULT_FLAG_ALLOW_RETRY |
4141+
FAULT_FLAG_RETRY_NOWAIT;
4142+
if (flags & FOLL_TRIED) {
4143+
VM_WARN_ON_ONCE(fault_flags &
4144+
FAULT_FLAG_ALLOW_RETRY);
4145+
fault_flags |= FAULT_FLAG_TRIED;
4146+
}
4147+
ret = hugetlb_fault(mm, vma, vaddr, fault_flags);
4148+
if (ret & VM_FAULT_ERROR) {
4149+
remainder = 0;
4150+
break;
4151+
}
4152+
if (ret & VM_FAULT_RETRY) {
4153+
if (nonblocking)
4154+
*nonblocking = 0;
4155+
*nr_pages = 0;
4156+
/*
4157+
* VM_FAULT_RETRY must not return an
4158+
* error, it will return zero
4159+
* instead.
4160+
*
4161+
* No need to update "position" as the
4162+
* caller will not check it after
4163+
* *nr_pages is set to 0.
4164+
*/
4165+
return i;
4166+
}
4167+
continue;
41414168
}
41424169

41434170
pfn_offset = (vaddr & ~huge_page_mask(h)) >> PAGE_SHIFT;
@@ -4166,6 +4193,11 @@ long follow_hugetlb_page(struct mm_struct *mm, struct vm_area_struct *vma,
41664193
spin_unlock(ptl);
41674194
}
41684195
*nr_pages = remainder;
4196+
/*
4197+
* setting position is actually required only if remainder is
4198+
* not zero but it's faster not to add a "if (remainder)"
4199+
* branch.
4200+
*/
41694201
*position = vaddr;
41704202

41714203
return i ? i : -EFAULT;

0 commit comments

Comments
 (0)