Skip to content

Commit 0f48947

Browse files
rpptakpm00
authored andcommitted
userfaultfd: introduce vm_uffd_ops
Current userfaultfd implementation works only with memory managed by core MM: anonymous, shmem and hugetlb. First, there is no fundamental reason to limit userfaultfd support only to the core memory types and userfaults can be handled similarly to regular page faults provided a VMA owner implements appropriate callbacks. Second, historically various code paths were conditioned on vma_is_anonymous(), vma_is_shmem() and is_vm_hugetlb_page() and some of these conditions can be expressed as operations implemented by a particular memory type. Introduce vm_uffd_ops extension to vm_operations_struct that will delegate memory type specific operations to a VMA owner. Operations for anonymous memory are handled internally in userfaultfd using anon_uffd_ops that implicitly assigned to anonymous VMAs. Start with a single operation, ->can_userfault() that will verify that a VMA meets requirements for userfaultfd support at registration time. Implement that method for anonymous, shmem and hugetlb and move relevant parts of vma_can_userfault() into the new callbacks. [rppt@kernel.org: relocate VM_DROPPABLE test, per Tal] Link: https://lore.kernel.org/adffgfM5ANxtPIEF@kernel.org Link: https://lore.kernel.org/20260402041156.1377214-8-rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Andrei Vagin <avagin@google.com> Cc: Axel Rasmussen <axelrasmussen@google.com> Cc: Baolin Wang <baolin.wang@linux.alibaba.com> Cc: David Hildenbrand (Arm) <david@kernel.org> Cc: Harry Yoo <harry.yoo@oracle.com> Cc: Harry Yoo (Oracle) <harry@kernel.org> Cc: Hugh Dickins <hughd@google.com> Cc: James Houghton <jthoughton@google.com> Cc: Liam Howlett <liam.howlett@oracle.com> Cc: Lorenzo Stoakes (Oracle) <ljs@kernel.org> Cc: Matthew Wilcox (Oracle) <willy@infradead.org> Cc: Michal Hocko <mhocko@suse.com> Cc: Muchun Song <muchun.song@linux.dev> Cc: Nikita Kalyazin <kalyazin@amazon.com> Cc: Oscar Salvador <osalvador@suse.de> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Xu <peterx@redhat.com> Cc: Sean Christopherson <seanjc@google.com> Cc: Shuah Khan <shuah@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@suse.cz> Cc: David Carlier <devnexen@gmail.com> Cc: Tal Zussman <tz2294@columbia.edu> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent a5bb866 commit 0f48947

5 files changed

Lines changed: 69 additions & 10 deletions

File tree

include/linux/mm.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,8 @@ struct vm_fault {
758758
*/
759759
};
760760

761+
struct vm_uffd_ops;
762+
761763
/*
762764
* These are the virtual MM functions - opening of an area, closing and
763765
* unmapping it (needed to keep files on disk up-to-date etc), pointer
@@ -865,6 +867,9 @@ struct vm_operations_struct {
865867
struct page *(*find_normal_page)(struct vm_area_struct *vma,
866868
unsigned long addr);
867869
#endif /* CONFIG_FIND_NORMAL_PAGE */
870+
#ifdef CONFIG_USERFAULTFD
871+
const struct vm_uffd_ops *uffd_ops;
872+
#endif
868873
};
869874

870875
#ifdef CONFIG_NUMA_BALANCING

include/linux/userfaultfd_k.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ struct userfaultfd_ctx {
8383

8484
extern vm_fault_t handle_userfault(struct vm_fault *vmf, unsigned long reason);
8585

86+
/* VMA userfaultfd operations */
87+
struct vm_uffd_ops {
88+
/* Checks if a VMA can support userfaultfd */
89+
bool (*can_userfault)(struct vm_area_struct *vma, vm_flags_t vm_flags);
90+
};
91+
8692
/* A combined operation mode + behavior flags. */
8793
typedef unsigned int __bitwise uffd_flags_t;
8894

mm/hugetlb.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4792,6 +4792,18 @@ static vm_fault_t hugetlb_vm_op_fault(struct vm_fault *vmf)
47924792
return 0;
47934793
}
47944794

4795+
#ifdef CONFIG_USERFAULTFD
4796+
static bool hugetlb_can_userfault(struct vm_area_struct *vma,
4797+
vm_flags_t vm_flags)
4798+
{
4799+
return true;
4800+
}
4801+
4802+
static const struct vm_uffd_ops hugetlb_uffd_ops = {
4803+
.can_userfault = hugetlb_can_userfault,
4804+
};
4805+
#endif
4806+
47954807
/*
47964808
* When a new function is introduced to vm_operations_struct and added
47974809
* to hugetlb_vm_ops, please consider adding the function to shm_vm_ops.
@@ -4805,6 +4817,9 @@ const struct vm_operations_struct hugetlb_vm_ops = {
48054817
.close = hugetlb_vm_op_close,
48064818
.may_split = hugetlb_vm_op_split,
48074819
.pagesize = hugetlb_vm_op_pagesize,
4820+
#ifdef CONFIG_USERFAULTFD
4821+
.uffd_ops = &hugetlb_uffd_ops,
4822+
#endif
48084823
};
48094824

48104825
static pte_t make_huge_pte(struct vm_area_struct *vma, struct folio *folio,

mm/shmem.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3288,6 +3288,15 @@ int shmem_mfill_atomic_pte(pmd_t *dst_pmd,
32883288
shmem_inode_unacct_blocks(inode, 1);
32893289
return ret;
32903290
}
3291+
3292+
static bool shmem_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags)
3293+
{
3294+
return true;
3295+
}
3296+
3297+
static const struct vm_uffd_ops shmem_uffd_ops = {
3298+
.can_userfault = shmem_can_userfault,
3299+
};
32913300
#endif /* CONFIG_USERFAULTFD */
32923301

32933302
#ifdef CONFIG_TMPFS
@@ -5307,6 +5316,9 @@ static const struct vm_operations_struct shmem_vm_ops = {
53075316
.set_policy = shmem_set_policy,
53085317
.get_policy = shmem_get_policy,
53095318
#endif
5319+
#ifdef CONFIG_USERFAULTFD
5320+
.uffd_ops = &shmem_uffd_ops,
5321+
#endif
53105322
};
53115323

53125324
static const struct vm_operations_struct shmem_anon_vm_ops = {
@@ -5316,6 +5328,9 @@ static const struct vm_operations_struct shmem_anon_vm_ops = {
53165328
.set_policy = shmem_set_policy,
53175329
.get_policy = shmem_get_policy,
53185330
#endif
5331+
#ifdef CONFIG_USERFAULTFD
5332+
.uffd_ops = &shmem_uffd_ops,
5333+
#endif
53195334
};
53205335

53215336
int shmem_init_fs_context(struct fs_context *fc)

mm/userfaultfd.c

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,25 @@ struct mfill_state {
3434
pmd_t *pmd;
3535
};
3636

37+
static bool anon_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags)
38+
{
39+
/* anonymous memory does not support MINOR mode */
40+
if (vm_flags & VM_UFFD_MINOR)
41+
return false;
42+
return true;
43+
}
44+
45+
static const struct vm_uffd_ops anon_uffd_ops = {
46+
.can_userfault = anon_can_userfault,
47+
};
48+
49+
static const struct vm_uffd_ops *vma_uffd_ops(struct vm_area_struct *vma)
50+
{
51+
if (vma_is_anonymous(vma))
52+
return &anon_uffd_ops;
53+
return vma->vm_ops ? vma->vm_ops->uffd_ops : NULL;
54+
}
55+
3756
static __always_inline
3857
bool validate_dst_vma(struct vm_area_struct *dst_vma, unsigned long dst_end)
3958
{
@@ -2021,34 +2040,33 @@ ssize_t move_pages(struct userfaultfd_ctx *ctx, unsigned long dst_start,
20212040
bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags,
20222041
bool wp_async)
20232042
{
2024-
vm_flags &= __VM_UFFD_FLAGS;
2043+
const struct vm_uffd_ops *ops = vma_uffd_ops(vma);
20252044

20262045
if (vma->vm_flags & VM_DROPPABLE)
20272046
return false;
20282047

2029-
if ((vm_flags & VM_UFFD_MINOR) &&
2030-
(!is_vm_hugetlb_page(vma) && !vma_is_shmem(vma)))
2031-
return false;
2048+
vm_flags &= __VM_UFFD_FLAGS;
20322049

20332050
/*
2034-
* If wp async enabled, and WP is the only mode enabled, allow any
2051+
* If WP is the only mode enabled and context is wp async, allow any
20352052
* memory type.
20362053
*/
20372054
if (wp_async && (vm_flags == VM_UFFD_WP))
20382055
return true;
20392056

2057+
/* For any other mode reject VMAs that don't implement vm_uffd_ops */
2058+
if (!ops)
2059+
return false;
2060+
20402061
/*
20412062
* If user requested uffd-wp but not enabled pte markers for
2042-
* uffd-wp, then shmem & hugetlbfs are not supported but only
2043-
* anonymous.
2063+
* uffd-wp, then only anonymous memory is supported
20442064
*/
20452065
if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) &&
20462066
!vma_is_anonymous(vma))
20472067
return false;
20482068

2049-
/* By default, allow any of anon|shmem|hugetlb */
2050-
return vma_is_anonymous(vma) || is_vm_hugetlb_page(vma) ||
2051-
vma_is_shmem(vma);
2069+
return ops->can_userfault(vma, vm_flags);
20522070
}
20532071

20542072
static void userfaultfd_set_vm_flags(struct vm_area_struct *vma,

0 commit comments

Comments
 (0)