diff options
Diffstat (limited to 'fs/userfaultfd.c')
| -rw-r--r-- | fs/userfaultfd.c | 109 |
1 files changed, 102 insertions, 7 deletions
diff --git a/fs/userfaultfd.c b/fs/userfaultfd.c index 18406158e13f..625b7285a37b 100644 --- a/fs/userfaultfd.c +++ b/fs/userfaultfd.c | |||
| @@ -71,6 +71,13 @@ struct userfaultfd_fork_ctx { | |||
| 71 | struct list_head list; | 71 | struct list_head list; |
| 72 | }; | 72 | }; |
| 73 | 73 | ||
| 74 | struct userfaultfd_unmap_ctx { | ||
| 75 | struct userfaultfd_ctx *ctx; | ||
| 76 | unsigned long start; | ||
| 77 | unsigned long end; | ||
| 78 | struct list_head list; | ||
| 79 | }; | ||
| 80 | |||
| 74 | struct userfaultfd_wait_queue { | 81 | struct userfaultfd_wait_queue { |
| 75 | struct uffd_msg msg; | 82 | struct uffd_msg msg; |
| 76 | wait_queue_t wq; | 83 | wait_queue_t wq; |
| @@ -681,16 +688,16 @@ void mremap_userfaultfd_complete(struct vm_userfaultfd_ctx *vm_ctx, | |||
| 681 | userfaultfd_event_wait_completion(ctx, &ewq); | 688 | userfaultfd_event_wait_completion(ctx, &ewq); |
| 682 | } | 689 | } |
| 683 | 690 | ||
| 684 | void madvise_userfault_dontneed(struct vm_area_struct *vma, | 691 | void userfaultfd_remove(struct vm_area_struct *vma, |
| 685 | struct vm_area_struct **prev, | 692 | struct vm_area_struct **prev, |
| 686 | unsigned long start, unsigned long end) | 693 | unsigned long start, unsigned long end) |
| 687 | { | 694 | { |
| 688 | struct mm_struct *mm = vma->vm_mm; | 695 | struct mm_struct *mm = vma->vm_mm; |
| 689 | struct userfaultfd_ctx *ctx; | 696 | struct userfaultfd_ctx *ctx; |
| 690 | struct userfaultfd_wait_queue ewq; | 697 | struct userfaultfd_wait_queue ewq; |
| 691 | 698 | ||
| 692 | ctx = vma->vm_userfaultfd_ctx.ctx; | 699 | ctx = vma->vm_userfaultfd_ctx.ctx; |
| 693 | if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_MADVDONTNEED)) | 700 | if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_REMOVE)) |
| 694 | return; | 701 | return; |
| 695 | 702 | ||
| 696 | userfaultfd_ctx_get(ctx); | 703 | userfaultfd_ctx_get(ctx); |
| @@ -700,15 +707,101 @@ void madvise_userfault_dontneed(struct vm_area_struct *vma, | |||
| 700 | 707 | ||
| 701 | msg_init(&ewq.msg); | 708 | msg_init(&ewq.msg); |
| 702 | 709 | ||
| 703 | ewq.msg.event = UFFD_EVENT_MADVDONTNEED; | 710 | ewq.msg.event = UFFD_EVENT_REMOVE; |
| 704 | ewq.msg.arg.madv_dn.start = start; | 711 | ewq.msg.arg.remove.start = start; |
| 705 | ewq.msg.arg.madv_dn.end = end; | 712 | ewq.msg.arg.remove.end = end; |
| 706 | 713 | ||
| 707 | userfaultfd_event_wait_completion(ctx, &ewq); | 714 | userfaultfd_event_wait_completion(ctx, &ewq); |
| 708 | 715 | ||
| 709 | down_read(&mm->mmap_sem); | 716 | down_read(&mm->mmap_sem); |
| 710 | } | 717 | } |
| 711 | 718 | ||
| 719 | static bool has_unmap_ctx(struct userfaultfd_ctx *ctx, struct list_head *unmaps, | ||
| 720 | unsigned long start, unsigned long end) | ||
| 721 | { | ||
| 722 | struct userfaultfd_unmap_ctx *unmap_ctx; | ||
| 723 | |||
| 724 | list_for_each_entry(unmap_ctx, unmaps, list) | ||
| 725 | if (unmap_ctx->ctx == ctx && unmap_ctx->start == start && | ||
| 726 | unmap_ctx->end == end) | ||
| 727 | return true; | ||
| 728 | |||
| 729 | return false; | ||
| 730 | } | ||
| 731 | |||
| 732 | int userfaultfd_unmap_prep(struct vm_area_struct *vma, | ||
| 733 | unsigned long start, unsigned long end, | ||
| 734 | struct list_head *unmaps) | ||
| 735 | { | ||
| 736 | for ( ; vma && vma->vm_start < end; vma = vma->vm_next) { | ||
| 737 | struct userfaultfd_unmap_ctx *unmap_ctx; | ||
| 738 | struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; | ||
| 739 | |||
| 740 | if (!ctx || !(ctx->features & UFFD_FEATURE_EVENT_UNMAP) || | ||
| 741 | has_unmap_ctx(ctx, unmaps, start, end)) | ||
| 742 | continue; | ||
| 743 | |||
| 744 | unmap_ctx = kzalloc(sizeof(*unmap_ctx), GFP_KERNEL); | ||
| 745 | if (!unmap_ctx) | ||
| 746 | return -ENOMEM; | ||
| 747 | |||
| 748 | userfaultfd_ctx_get(ctx); | ||
| 749 | unmap_ctx->ctx = ctx; | ||
| 750 | unmap_ctx->start = start; | ||
| 751 | unmap_ctx->end = end; | ||
| 752 | list_add_tail(&unmap_ctx->list, unmaps); | ||
| 753 | } | ||
| 754 | |||
| 755 | return 0; | ||
| 756 | } | ||
| 757 | |||
| 758 | void userfaultfd_unmap_complete(struct mm_struct *mm, struct list_head *uf) | ||
| 759 | { | ||
| 760 | struct userfaultfd_unmap_ctx *ctx, *n; | ||
| 761 | struct userfaultfd_wait_queue ewq; | ||
| 762 | |||
| 763 | list_for_each_entry_safe(ctx, n, uf, list) { | ||
| 764 | msg_init(&ewq.msg); | ||
| 765 | |||
| 766 | ewq.msg.event = UFFD_EVENT_UNMAP; | ||
| 767 | ewq.msg.arg.remove.start = ctx->start; | ||
| 768 | ewq.msg.arg.remove.end = ctx->end; | ||
| 769 | |||
| 770 | userfaultfd_event_wait_completion(ctx->ctx, &ewq); | ||
| 771 | |||
| 772 | list_del(&ctx->list); | ||
| 773 | kfree(ctx); | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | void userfaultfd_exit(struct mm_struct *mm) | ||
| 778 | { | ||
| 779 | struct vm_area_struct *vma = mm->mmap; | ||
| 780 | |||
| 781 | /* | ||
| 782 | * We can do the vma walk without locking because the caller | ||
| 783 | * (exit_mm) knows it now has exclusive access | ||
| 784 | */ | ||
| 785 | while (vma) { | ||
| 786 | struct userfaultfd_ctx *ctx = vma->vm_userfaultfd_ctx.ctx; | ||
| 787 | |||
| 788 | if (ctx && (ctx->features & UFFD_FEATURE_EVENT_EXIT)) { | ||
| 789 | struct userfaultfd_wait_queue ewq; | ||
| 790 | |||
| 791 | userfaultfd_ctx_get(ctx); | ||
| 792 | |||
| 793 | msg_init(&ewq.msg); | ||
| 794 | ewq.msg.event = UFFD_EVENT_EXIT; | ||
| 795 | |||
| 796 | userfaultfd_event_wait_completion(ctx, &ewq); | ||
| 797 | |||
| 798 | ctx->features &= ~UFFD_FEATURE_EVENT_EXIT; | ||
| 799 | } | ||
| 800 | |||
| 801 | vma = vma->vm_next; | ||
| 802 | } | ||
| 803 | } | ||
| 804 | |||
| 712 | static int userfaultfd_release(struct inode *inode, struct file *file) | 805 | static int userfaultfd_release(struct inode *inode, struct file *file) |
| 713 | { | 806 | { |
| 714 | struct userfaultfd_ctx *ctx = file->private_data; | 807 | struct userfaultfd_ctx *ctx = file->private_data; |
| @@ -1514,6 +1607,8 @@ static int userfaultfd_copy(struct userfaultfd_ctx *ctx, | |||
| 1514 | ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src, | 1607 | ret = mcopy_atomic(ctx->mm, uffdio_copy.dst, uffdio_copy.src, |
| 1515 | uffdio_copy.len); | 1608 | uffdio_copy.len); |
| 1516 | mmput(ctx->mm); | 1609 | mmput(ctx->mm); |
| 1610 | } else { | ||
| 1611 | return -ENOSPC; | ||
| 1517 | } | 1612 | } |
| 1518 | if (unlikely(put_user(ret, &user_uffdio_copy->copy))) | 1613 | if (unlikely(put_user(ret, &user_uffdio_copy->copy))) |
| 1519 | return -EFAULT; | 1614 | return -EFAULT; |
