diff options
Diffstat (limited to 'mm/madvise.c')
-rw-r--r-- | mm/madvise.c | 44 |
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(¤t->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(¤t->mm->mmap_sem); | 594 | /* mmap_sem was not released by userfaultfd_remove() */ |
595 | up_read(¤t->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); |