aboutsummaryrefslogtreecommitdiffstats
path: root/mm/madvise.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/madvise.c')
-rw-r--r--mm/madvise.c44
1 files changed, 41 insertions, 3 deletions
diff --git a/mm/madvise.c b/mm/madvise.c
index dc5927c812d3..7a2abf0127ae 100644
--- a/mm/madvise.c
+++ b/mm/madvise.c
@@ -513,7 +513,43 @@ static long madvise_dontneed(struct vm_area_struct *vma,
513 if (!can_madv_dontneed_vma(vma)) 513 if (!can_madv_dontneed_vma(vma))
514 return -EINVAL; 514 return -EINVAL;
515 515
516 userfaultfd_remove(vma, prev, start, end); 516 if (!userfaultfd_remove(vma, start, end)) {
517 *prev = NULL; /* mmap_sem has been dropped, prev is stale */
518
519 down_read(&current->mm->mmap_sem);
520 vma = find_vma(current->mm, start);
521 if (!vma)
522 return -ENOMEM;
523 if (start < vma->vm_start) {
524 /*
525 * This "vma" under revalidation is the one
526 * with the lowest vma->vm_start where start
527 * is also < vma->vm_end. If start <
528 * vma->vm_start it means an hole materialized
529 * in the user address space within the
530 * virtual range passed to MADV_DONTNEED.
531 */
532 return -ENOMEM;
533 }
534 if (!can_madv_dontneed_vma(vma))
535 return -EINVAL;
536 if (end > vma->vm_end) {
537 /*
538 * Don't fail if end > vma->vm_end. If the old
539 * vma was splitted while the mmap_sem was
540 * released the effect of the concurrent
541 * operation may not cause MADV_DONTNEED to
542 * have an undefined result. There may be an
543 * adjacent next vma that we'll walk
544 * next. userfaultfd_remove() will generate an
545 * UFFD_EVENT_REMOVE repetition on the
546 * end-vma->vm_end range, but the manager can
547 * handle a repetition fine.
548 */
549 end = vma->vm_end;
550 }
551 VM_WARN_ON(start >= end);
552 }
517 zap_page_range(vma, start, end - start); 553 zap_page_range(vma, start, end - start);
518 return 0; 554 return 0;
519} 555}
@@ -554,8 +590,10 @@ static long madvise_remove(struct vm_area_struct *vma,
554 * mmap_sem. 590 * mmap_sem.
555 */ 591 */
556 get_file(f); 592 get_file(f);
557 userfaultfd_remove(vma, prev, start, end); 593 if (userfaultfd_remove(vma, start, end)) {
558 up_read(&current->mm->mmap_sem); 594 /* mmap_sem was not released by userfaultfd_remove() */
595 up_read(&current->mm->mmap_sem);
596 }
559 error = vfs_fallocate(f, 597 error = vfs_fallocate(f,
560 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 598 FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
561 offset, end - start); 599 offset, end - start);