Add 'mode' field to struct vm_uffd_state and define UFFD_MODE_ flags.

Use this field to differentiate VMA registration with userfaultfd
instead of relying on VM_UFFD_* flags.

A VMA registered with userfaultfd will have a single VM_UFFD flag set
and its registration mode (MISSING, MINOR, WP, RWP) is determined by
vm_uffd_state.mode.

This frees three vm_flags bits (12, 41, 43).

Update the relevant code to use UFFD_MODE_* instead of VM_UFFD_* flags.

Remove VM_UFFD_WP and VM_UFFD_RWP from VM_COPY_ON_FORK, adding an
explicit userfaultfd_protected() check in vma_needs_copy() instead.

/proc/pid/smaps representation of VmFlags is slightly changed:
- any VMA registered with UFFD shows 'uf'
- the existing userfault markers ('um', 'uw', 'ui', 'ur') are shown after
  VmFlags rather than in the middle

Assisted-by: copilot:claude-opus-4.6
Signed-off-by: Mike Rapoport (Microsoft) <[email protected]>
---
 fs/proc/task_mmu.c              |  18 +++---
 include/linux/mm.h              |  67 +++++-----------------
 include/linux/mm_types.h        |   1 +
 include/linux/pgtable.h         |   4 +-
 include/linux/userfaultfd_k.h   |  37 +++++++-----
 include/trace/events/mmflags.h  |  17 +-----
 mm/gup.c                        |   5 +-
 mm/hugetlb.c                    |   2 +-
 mm/khugepaged.c                 |   2 +-
 mm/memory.c                     |   6 +-
 mm/mprotect.c                   |   2 +-
 mm/shmem.c                      |   2 +-
 mm/userfaultfd.c                | 123 +++++++++++++++++++++-------------------
 tools/testing/vma/include/dup.h |  18 ++----
 14 files changed, 134 insertions(+), 170 deletions(-)

diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 5c54aebe2118..8d344c6ee14e 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -23,6 +23,7 @@
 #include <linux/minmax.h>
 #include <linux/overflow.h>
 #include <linux/buildid.h>
+#include <linux/userfaultfd_k.h>
 
 #include <asm/elf.h>
 #include <asm/tlb.h>
@@ -1216,8 +1217,7 @@ static void show_smap_vma_flags(struct seq_file *m, 
struct vm_area_struct *vma)
                [ilog2(VM_HUGEPAGE)]    = "hg",
                [ilog2(VM_NOHUGEPAGE)]  = "nh",
                [ilog2(VM_MERGEABLE)]   = "mg",
-               [ilog2(VM_UFFD_MISSING)]= "um",
-               [ilog2(VM_UFFD_WP)]     = "uw",
+               [ilog2(VM_UFFD)]        = "uf",
 #ifdef CONFIG_ARM64_MTE
                [ilog2(VM_MTE)]         = "mt",
                [ilog2(VM_MTE_ALLOWED)] = "",
@@ -1234,12 +1234,6 @@ static void show_smap_vma_flags(struct seq_file *m, 
struct vm_area_struct *vma)
                [ilog2(VM_PKEY_BIT4)]   = "",
 #endif
 #endif /* CONFIG_ARCH_HAS_PKEYS */
-#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
-               [ilog2(VM_UFFD_MINOR)]  = "ui",
-#endif /* CONFIG_HAVE_ARCH_USERFAULTFD_MINOR */
-#ifdef CONFIG_USERFAULTFD_RWP
-               [ilog2(VM_UFFD_RWP)]    = "ur",
-#endif
 #ifdef CONFIG_ARCH_HAS_USER_SHADOW_STACK
                [ilog2(VM_SHADOW_STACK)] = "ss",
 #endif
@@ -1259,6 +1253,14 @@ static void show_smap_vma_flags(struct seq_file *m, 
struct vm_area_struct *vma)
                if (vma->vm_flags & (1UL << i))
                        seq_printf(m, "%s ", mnemonics[i]);
        }
+       if (userfaultfd_missing(vma))
+               seq_puts(m, "um ");
+       if (userfaultfd_wp(vma))
+               seq_puts(m, "uw ");
+       if (userfaultfd_minor(vma))
+               seq_puts(m, "ui ");
+       if (userfaultfd_rwp(vma))
+               seq_puts(m, "ur ");
        seq_putc(m, '\n');
 }
 
diff --git a/include/linux/mm.h b/include/linux/mm.h
index 4daf9cd6ae8e..416de7663951 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -303,7 +303,7 @@ enum {
        DECLARE_VMA_BIT(MAYSHARE, 7),
        DECLARE_VMA_BIT(GROWSDOWN, 8),  /* general info on the segment */
 #ifdef CONFIG_MMU
-       DECLARE_VMA_BIT(UFFD_MISSING, 9),/* missing pages tracking */
+       DECLARE_VMA_BIT(UFFD, 9),       /* userfaultfd registered */
 #else
        /* nommu: R/O MAP_PRIVATE mapping that might overlay a file mapping */
        DECLARE_VMA_BIT(MAYOVERLAY, 9),
@@ -311,7 +311,7 @@ enum {
        /* Page-ranges managed without "struct page", just pure PFN */
        DECLARE_VMA_BIT(PFNMAP, 10),
        DECLARE_VMA_BIT(MAYBE_GUARD, 11),
-       DECLARE_VMA_BIT(UFFD_WP, 12),   /* wrprotect pages tracking */
+       /* Bit 12 is free */
        DECLARE_VMA_BIT(LOCKED, 13),
        DECLARE_VMA_BIT(IO, 14),        /* Memory mapped I/O or similar */
        DECLARE_VMA_BIT(SEQ_READ, 15),  /* App will access data sequentially */
@@ -352,9 +352,8 @@ enum {
 #elif defined(CONFIG_64BIT)
        DECLARE_VMA_BIT(DROPPABLE, 40),
 #endif
-       DECLARE_VMA_BIT(UFFD_MINOR, 41),
+       /* Bits 41 and 43 are free */
        DECLARE_VMA_BIT(SEALED, 42),
-       DECLARE_VMA_BIT(UFFD_RWP, 43),
        /* Flags that reuse flags above. */
        DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),
        DECLARE_VMA_BIT_ALIAS(PKEY_BIT1, HIGH_ARCH_1),
@@ -408,14 +407,14 @@ enum {
 #define VM_MAYSHARE    INIT_VM_FLAG(MAYSHARE)
 #define VM_GROWSDOWN   INIT_VM_FLAG(GROWSDOWN)
 #ifdef CONFIG_MMU
-#define VM_UFFD_MISSING        INIT_VM_FLAG(UFFD_MISSING)
+#define VM_UFFD                INIT_VM_FLAG(UFFD)
+#define VMA_UFFD       mk_vma_flags(VMA_UFFD_BIT)
 #else
-#define VM_UFFD_MISSING        VM_NONE
+#define VM_UFFD                VM_NONE
 #define VM_MAYOVERLAY  INIT_VM_FLAG(MAYOVERLAY)
 #endif
 #define VM_PFNMAP      INIT_VM_FLAG(PFNMAP)
 #define VM_MAYBE_GUARD INIT_VM_FLAG(MAYBE_GUARD)
-#define VM_UFFD_WP     INIT_VM_FLAG(UFFD_WP)
 #define VM_LOCKED      INIT_VM_FLAG(LOCKED)
 #define VM_IO          INIT_VM_FLAG(IO)
 #define VM_SEQ_READ    INIT_VM_FLAG(SEQ_READ)
@@ -499,36 +498,6 @@ enum {
 #define VM_MTE         VM_NONE
 #define VM_MTE_ALLOWED VM_NONE
 #endif
-#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
-#define VM_UFFD_MINOR  INIT_VM_FLAG(UFFD_MINOR)
-#else
-#define VM_UFFD_MINOR  VM_NONE
-#endif
-#ifdef CONFIG_USERFAULTFD_RWP
-#define VM_UFFD_RWP            INIT_VM_FLAG(UFFD_RWP)
-#else
-#define VM_UFFD_RWP            VM_NONE
-#endif
-
-/*
- * vma_flags_t masks for the userfaultfd VMA flags. The two high-bit modes are
- * gated on the same configs as their VM_* flags above -- both of which imply
- * 64BIT -- so an out-of-range bit is never fed to mk_vma_flags() on a build
- * whose bitmap cannot hold it.
- */
-#define VMA_UFFD_MISSING       mk_vma_flags(VMA_UFFD_MISSING_BIT)
-#define VMA_UFFD_WP            mk_vma_flags(VMA_UFFD_WP_BIT)
-#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
-#define VMA_UFFD_MINOR         mk_vma_flags(VMA_UFFD_MINOR_BIT)
-#else
-#define VMA_UFFD_MINOR         EMPTY_VMA_FLAGS
-#endif
-#ifdef CONFIG_USERFAULTFD_RWP
-#define VMA_UFFD_RWP           mk_vma_flags(VMA_UFFD_RWP_BIT)
-#else
-#define VMA_UFFD_RWP           EMPTY_VMA_FLAGS
-#endif
-
 #ifdef CONFIG_64BIT
 #define VM_ALLOW_ANY_UNCACHED  INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
 #define VM_SEALED              INIT_VM_FLAG(SEALED)
@@ -668,32 +637,26 @@ enum {
  * reconsistuted upon page fault, so necessitate page table copying upon fork.
  *
  * Note that these flags should be compared with the DESTINATION VMA not the
- * source: VM_UFFD_WP and VM_UFFD_RWP may be cleared on the destination
+ * source: uffd WP/RWP mode may be cleared on the destination
  * (dup_userfaultfd() -> userfaultfd_reset_ctx() when the parent context did
  * not negotiate UFFD_FEATURE_EVENT_FORK), while all other flags propagate.
  *
  * VM_PFNMAP / VM_MIXEDMAP - These contain kernel-mapped data which cannot be
  *                           reasonably reconstructed on page fault.
  *
- *              VM_UFFD_WP - Encodes metadata about an installed uffd
- *              VM_UFFD_RWP  write- or read-write-protect handler, which
- *                           cannot be reconstructed on page fault.
- *
- *                           We always copy pgtables when dst_vma has the
- *                           uffd PTE bit in use even if it's file-backed
- *                           (e.g. shmem). Because when the uffd bit is
- *                           in use, the pgtable contains the protection
- *                           information, that's something we can't
- *                           retrieve from page cache, and skip copying
- *                           will lose those info.
- *
  *          VM_MAYBE_GUARD - Could contain page guard region markers which
  *                           by design are a property of the page tables
  *                           only and thus cannot be reconstructed on page
  *                           fault.
+ *
+ *       uffd WP/RWP modes - Encode metadata about an installed uffd
+ *                           write- or read-write-protect handler, which
+ *                           cannot be reconstructed on page fault.
+ *                           This is checked separately via
+ *                           userfaultfd_protected() in vma_needs_copy().
+ *
  */
-#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_UFFD_RWP | \
-                        VM_MAYBE_GUARD)
+#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_MAYBE_GUARD)
 
 /*
  * mapping from the currently active vm_flags protection bits (the
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index d6deb655d82e..8354d1c18b29 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -723,6 +723,7 @@ struct vm_region {
 #define NULL_VM_UFFD_STATE ((struct vm_uffd_state) { NULL, })
 struct vm_uffd_state {
        struct userfaultfd_ctx *ctx;
+       unsigned int mode;
 };
 #else /* CONFIG_USERFAULTFD */
 #define NULL_VM_UFFD_STATE ((struct vm_uffd_state) {})
diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h
index 8c093c119e5a..4b74c298cc14 100644
--- a/include/linux/pgtable.h
+++ b/include/linux/pgtable.h
@@ -2119,8 +2119,8 @@ static inline int pud_trans_unstable(pud_t *pud)
  * In an accessible VMA, pte_protnone() reliably indicates a present
  * PROT_NONE page protection. Today the kernel uses such PTEs for two
  * purposes: NUMA hinting faults, and userfaultfd RWP tracking on
- * VM_UFFD_RWP VMAs. The two are distinguished by the uffd PTE bit and
- * the VMA flag; see include/linux/userfaultfd_k.h.
+ * uffd-RWP VMAs. The two are distinguished by the uffd PTE bit and
+ * the VMA uffd state; see include/linux/userfaultfd_k.h.
  *
  * So, to reliably identify PROT_NONE PTEs that require kernel handling,
  * looking at the VMA accessibility (and the uffd bit on RWP VMAs) is
diff --git a/include/linux/userfaultfd_k.h b/include/linux/userfaultfd_k.h
index f401623f315d..b4261038d10a 100644
--- a/include/linux/userfaultfd_k.h
+++ b/include/linux/userfaultfd_k.h
@@ -32,12 +32,13 @@ enum uf_reason {
 #include <asm-generic/pgtable_uffd.h>
 #include <linux/hugetlb_inline.h>
 
-/* The set of all possible UFFD-related VM flags. */
-#define __VM_UFFD_FLAGS (VM_UFFD_MISSING | VM_UFFD_MINOR | \
-                        VM_UFFD_WP | VM_UFFD_RWP)
-
-#define __VMA_UFFD_FLAGS mk_vma_flags_from_masks(VMA_UFFD_MISSING, 
VMA_UFFD_WP, \
-                                                VMA_UFFD_MINOR, VMA_UFFD_RWP)
+/* Per-VMA uffd modes */
+#define UFFD_MODE_MISSING      BIT(0)
+#define UFFD_MODE_MINOR                BIT(1)
+#define UFFD_MODE_RWP          BIT(2)
+#define UFFD_MODE_WP           BIT(3)
+#define UFFD_MODE_ALL          (UFFD_MODE_MISSING | UFFD_MODE_MINOR | \
+                                UFFD_MODE_RWP | UFFD_MODE_WP)
 
 /*
  * CAREFUL: Check include/uapi/asm-generic/fcntl.h when defining
@@ -99,7 +100,7 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, enum 
uf_reason reason);
 /* VMA userfaultfd operations */
 struct vm_uffd_ops {
        /* Checks if a VMA can support userfaultfd */
-       bool (*can_userfault)(struct vm_area_struct *vma, vm_flags_t vm_flags);
+       bool (*can_userfault)(struct vm_area_struct *vma, unsigned int mode);
        /*
         * Called to resolve UFFDIO_CONTINUE request.
         * Should return the folio found at pgoff in the VMA's pagecache if it
@@ -174,25 +175,34 @@ int move_pages_huge_pmd(struct mm_struct *mm, pmd_t 
*dst_pmd, pmd_t *src_pmd, pm
                        unsigned long dst_addr, unsigned long src_addr);
 
 /* mm helpers */
+static inline unsigned int uffd_mode(const struct vm_area_struct *vma)
+{
+       return vma->vm_uffd_state.mode;
+}
+
 static inline bool is_mergeable_vm_uffd_state(struct vm_area_struct *vma,
                                        struct vm_uffd_state vm_ctx)
 {
-       return vma->vm_uffd_state.ctx == vm_ctx.ctx;
+       return vma->vm_uffd_state.ctx == vm_ctx.ctx &&
+              uffd_mode(vma) == vm_ctx.mode;
 }
 
 static inline bool userfaultfd_missing(const struct vm_area_struct *vma)
 {
-       return vma_test_any_mask(vma, VMA_UFFD_MISSING);
+       return vma_test(vma, VMA_UFFD_BIT) &&
+              (uffd_mode(vma) & UFFD_MODE_MISSING);
 }
 
 static inline bool userfaultfd_wp(const struct vm_area_struct *vma)
 {
-       return vma_test_any_mask(vma, VMA_UFFD_WP);
+       return vma_test(vma, VMA_UFFD_BIT) &&
+              (uffd_mode(vma) & UFFD_MODE_WP);
 }
 
 static inline bool userfaultfd_minor(const struct vm_area_struct *vma)
 {
-       return vma_test_any_mask(vma, VMA_UFFD_MINOR);
+       return vma_test(vma, VMA_UFFD_BIT) &&
+              (uffd_mode(vma) & UFFD_MODE_MINOR);
 }
 
 static inline bool userfaultfd_rwp(const struct vm_area_struct *vma)
@@ -203,7 +213,8 @@ static inline bool userfaultfd_rwp(const struct 
vm_area_struct *vma)
         */
        if (!IS_ENABLED(CONFIG_ARCH_HAS_PTE_PROTNONE))
                return false;
-       return vma_test_single_mask(vma, VMA_UFFD_RWP);
+       return vma_test(vma, VMA_UFFD_BIT) &&
+              (uffd_mode(vma) & UFFD_MODE_RWP);
 }
 
 static inline bool userfaultfd_protected(const struct vm_area_struct *vma)
@@ -271,7 +282,7 @@ static inline bool userfaultfd_huge_pmd_rwp(struct 
vm_area_struct *vma,
 
 static inline bool userfaultfd_armed(struct vm_area_struct *vma)
 {
-       return vma_test_any_mask(vma, __VMA_UFFD_FLAGS);
+       return vma_test(vma, VMA_UFFD_BIT);
 }
 
 static inline bool vma_has_uffd_without_event_remap(struct vm_area_struct *vma)
diff --git a/include/trace/events/mmflags.h b/include/trace/events/mmflags.h
index 935893e5ea53..aacdd90e0a64 100644
--- a/include/trace/events/mmflags.h
+++ b/include/trace/events/mmflags.h
@@ -180,18 +180,6 @@ IF_HAVE_PG_ARCH_3(arch_3)
 #define IF_HAVE_VM_SOFTDIRTY(flag,name)
 #endif
 
-#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
-# define IF_HAVE_UFFD_MINOR(flag, name) {flag, name},
-#else
-# define IF_HAVE_UFFD_MINOR(flag, name)
-#endif
-
-#ifdef CONFIG_USERFAULTFD_RWP
-# define IF_HAVE_UFFD_RWP(flag, name) {flag, name},
-#else
-# define IF_HAVE_UFFD_RWP(flag, name)
-#endif
-
 #if defined(CONFIG_64BIT) || defined(CONFIG_PPC32)
 # define IF_HAVE_VM_DROPPABLE(flag, name) {flag, name},
 #else
@@ -208,12 +196,9 @@ IF_HAVE_PG_ARCH_3(arch_3)
        {VM_MAYEXEC,                    "mayexec"       },              \
        {VM_MAYSHARE,                   "mayshare"      },              \
        {VM_GROWSDOWN,                  "growsdown"     },              \
-       {VM_UFFD_MISSING,               "uffd_missing"  },              \
-IF_HAVE_UFFD_MINOR(VM_UFFD_MINOR,      "uffd_minor"    )               \
+       {VM_UFFD,                       "uffd"          },              \
        {VM_PFNMAP,                     "pfnmap"        },              \
        {VM_MAYBE_GUARD,                "maybe_guard"   },              \
-       {VM_UFFD_WP,                    "uffd_wp"       },              \
-IF_HAVE_UFFD_RWP(VM_UFFD_RWP,          "uffd_rwp"      )               \
        {VM_LOCKED,                     "locked"        },              \
        {VM_IO,                         "io"            },              \
        {VM_SEQ_READ,                   "seqread"       },              \
diff --git a/mm/gup.c b/mm/gup.c
index 500e2aa99e48..9243c41a0c0e 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -12,6 +12,7 @@
 #include <linux/swap.h>
 #include <linux/swapops.h>
 #include <linux/secretmem.h>
+#include <linux/userfaultfd_k.h>
 
 #include <linux/sched/signal.h>
 #include <linux/rwsem.h>
@@ -641,7 +642,7 @@ static inline bool gup_can_follow_protnone(const struct 
vm_area_struct *vma,
                                           unsigned int flags)
 {
        /*
-        * VM_UFFD_RWP uses protnone as an access-tracking marker, not for
+        * uffd-RWP uses protnone as an access-tracking marker, not for
         * NUMA hinting. GUP must always take a fault so the access is
         * delivered to userfaultfd, regardless of FOLL_HONOR_NUMA_FAULT.
         *
@@ -651,7 +652,7 @@ static inline bool gup_can_follow_protnone(const struct 
vm_area_struct *vma,
         * no progress on protnone in an inaccessible VMA, and the access is
         * denied regardless of RWP anyway.
         */
-       if (vma_test_single_mask(vma, VMA_UFFD_RWP) && vma_is_accessible(vma))
+       if (userfaultfd_rwp(vma) && vma_is_accessible(vma))
                return false;
 
        /*
diff --git a/mm/hugetlb.c b/mm/hugetlb.c
index 5e2ed80c1938..a15c443474d5 100644
--- a/mm/hugetlb.c
+++ b/mm/hugetlb.c
@@ -4808,7 +4808,7 @@ static vm_fault_t hugetlb_vm_op_fault(struct vm_fault 
*vmf)
 
 #ifdef CONFIG_USERFAULTFD
 static bool hugetlb_can_userfault(struct vm_area_struct *vma,
-                                 vm_flags_t vm_flags)
+                                 unsigned int mode)
 {
        return true;
 }
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
index 79effd3f3da4..7f590a1d3ca3 100644
--- a/mm/khugepaged.c
+++ b/mm/khugepaged.c
@@ -2112,7 +2112,7 @@ static bool file_backed_vma_is_retractable(struct 
vm_area_struct *vma)
        /*
         * When a vma is registered with uffd-wp or RWP, we cannot recycle
         * the page table because there may be pte markers installed.
-        * VM_UFFD_RWP ranges similarly rely on per-PTE uffd state
+        * uffd-RWP ranges similarly rely on per-PTE uffd state
         * and cannot be recycled to a shared PMD. Other vmas can still
         * have the same file mapped hugely, but skip this one: it will
         * always be mapped in small page size for these registrations.
diff --git a/mm/memory.c b/mm/memory.c
index 1a9b41704b0c..c12ec979199c 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -1565,10 +1565,12 @@ vma_needs_copy(struct vm_area_struct *dst_vma, struct 
vm_area_struct *src_vma)
 {
        /*
         * We check against dst_vma as while sane VMA flags will have been
-        * copied, VM_UFFD_WP may be set only on dst_vma.
+        * copied, userfaultfd WP/RWP mode may be set only on dst_vma.
         */
        if (dst_vma->vm_flags & VM_COPY_ON_FORK)
                return true;
+       if (userfaultfd_protected(dst_vma))
+               return true;
        /*
         * The presence of an anon_vma indicates an anonymous VMA has page
         * tables which naturally cannot be reconstituted on page fault.
@@ -6563,7 +6565,7 @@ static vm_fault_t handle_pte_fault(struct vm_fault *vmf)
        if (pte_protnone(vmf->orig_pte) && vma_is_accessible(vmf->vma)) {
                /*
                 * RWP-protected PTEs are protnone plus the uffd bit. On a
-                * VM_UFFD_RWP VMA, a protnone PTE without the uffd bit is
+                * uffd-RWP VMA, a protnone PTE without the uffd bit is
                 * NUMA hinting and must still fall through to do_numa_page().
                 */
                if (userfaultfd_pte_rwp(vmf->vma, vmf->orig_pte))
diff --git a/mm/mprotect.c b/mm/mprotect.c
index 2888ee638d87..b98d4372677b 100644
--- a/mm/mprotect.c
+++ b/mm/mprotect.c
@@ -297,7 +297,7 @@ static __always_inline void change_present_ptes(struct 
mmu_gather *tlb,
                ptent = pte_clear_uffd(ptent);
 
        /*
-        * The uffd bit on a VM_UFFD_RWP VMA carries PROT_NONE
+        * The uffd bit on a uffd-RWP VMA carries PROT_NONE
         * semantics. If mprotect() or NUMA hinting changed the
         * base protection, restore PAGE_NONE so the PTE still
         * traps on any access. pte_modify() preserves
diff --git a/mm/shmem.c b/mm/shmem.c
index 2138a4e6b549..c8db9f93dde1 100644
--- a/mm/shmem.c
+++ b/mm/shmem.c
@@ -3220,7 +3220,7 @@ static struct folio *shmem_get_folio_noalloc(struct inode 
*inode, pgoff_t pgoff)
        return folio;
 }
 
-static bool shmem_can_userfault(struct vm_area_struct *vma, vm_flags_t 
vm_flags)
+static bool shmem_can_userfault(struct vm_area_struct *vma, unsigned int mode)
 {
        return true;
 }
diff --git a/mm/userfaultfd.c b/mm/userfaultfd.c
index 83587d34b189..193f6e65d875 100644
--- a/mm/userfaultfd.c
+++ b/mm/userfaultfd.c
@@ -50,10 +50,10 @@ struct mfill_state {
        pmd_t *pmd;
 };
 
-static bool anon_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags)
+static bool anon_can_userfault(struct vm_area_struct *vma, unsigned int mode)
 {
        /* anonymous memory does not support MINOR mode */
-       if (vm_flags & VM_UFFD_MINOR)
+       if (mode & UFFD_MODE_MINOR)
                return false;
        return true;
 }
@@ -462,7 +462,7 @@ static int mfill_copy_folio_locked(struct folio *folio, 
unsigned long src_addr)
 }
 
 #define MFILL_RETRY_STATE_VMA_FLAGS \
-       append_vma_flags(__VMA_UFFD_FLAGS, VMA_SHARED_BIT)
+       append_vma_flags(VMA_UFFD, VMA_SHARED_BIT)
 
 /*
  * VMA state saved before dropping the locks in mfill_copy_folio_retry().
@@ -2194,7 +2194,7 @@ static ssize_t move_pages(struct userfaultfd_ctx *ctx, 
unsigned long dst_start,
        return moved ? moved : err;
 }
 
-static bool vma_can_userfault(struct vm_area_struct *vma, vm_flags_t vm_flags,
+static bool vma_can_userfault(struct vm_area_struct *vma, unsigned int mode,
                       bool wp_async)
 {
        const struct vm_uffd_ops *ops = vma_uffd_ops(vma);
@@ -2205,13 +2205,11 @@ static bool vma_can_userfault(struct vm_area_struct 
*vma, vm_flags_t vm_flags,
        if (!is_vm_hugetlb_page(vma) && (vma->vm_flags & VM_SPECIAL))
                return false;
 
-       vm_flags &= __VM_UFFD_FLAGS;
-
        /*
         * If WP is the only mode enabled and context is wp async, allow any
         * memory type.
         */
-       if (wp_async && (vm_flags == VM_UFFD_WP))
+       if (wp_async && (mode == UFFD_MODE_WP))
                return true;
 
        /* For any other mode reject VMAs that don't implement vm_uffd_ops */
@@ -2222,19 +2220,31 @@ static bool vma_can_userfault(struct vm_area_struct 
*vma, vm_flags_t vm_flags,
         * If user requested uffd-wp but not enabled pte markers for
         * uffd-wp, then only anonymous memory is supported
         */
-       if (!uffd_supports_wp_marker() && (vm_flags & VM_UFFD_WP) &&
+       if (!uffd_supports_wp_marker() && (mode & UFFD_MODE_WP) &&
            !vma_is_anonymous(vma))
                return false;
 
-       return ops->can_userfault(vma, vm_flags);
+       return ops->can_userfault(vma, mode);
 }
 
-static void userfaultfd_set_vm_flags(struct vm_area_struct *vma,
-                                    vm_flags_t vm_flags)
+static void userfaultfd_set_ctx(struct vm_area_struct *vma,
+                               struct userfaultfd_ctx *ctx,
+                               unsigned int mode)
 {
-       const bool uffd_wp_changed = (vma->vm_flags ^ vm_flags) & VM_UFFD_WP;
+       const bool uffd_wp_changed = (uffd_mode(vma) ^ mode) & UFFD_MODE_WP;
+
+       vma_start_write(vma);
+
+       vma->vm_uffd_state = (struct vm_uffd_state){
+               .ctx = ctx,
+               .mode = mode,
+       };
+
+       if (mode)
+               vma_set_flags(vma, VMA_UFFD_BIT);
+       else
+               vma_clear_flags(vma, VMA_UFFD_BIT);
 
-       vm_flags_reset(vma, vm_flags);
        /*
         * For shared mappings, we want to enable writenotify while
         * userfaultfd-wp is enabled (see vma_wants_writenotify()). We'll simply
@@ -2244,16 +2254,6 @@ static void userfaultfd_set_vm_flags(struct 
vm_area_struct *vma,
                vma_set_page_prot(vma);
 }
 
-static void userfaultfd_set_ctx(struct vm_area_struct *vma,
-                               struct userfaultfd_ctx *ctx,
-                               vm_flags_t vm_flags)
-{
-       vma_start_write(vma);
-       vma->vm_uffd_state = (struct vm_uffd_state){ctx};
-       userfaultfd_set_vm_flags(vma,
-                                (vma->vm_flags & ~__VM_UFFD_FLAGS) | vm_flags);
-}
-
 static void userfaultfd_reset_ctx(struct vm_area_struct *vma)
 {
        userfaultfd_set_ctx(vma, NULL, 0);
@@ -2269,7 +2269,7 @@ static struct vm_area_struct 
*userfaultfd_clear_vma(struct vma_iterator *vmi,
        bool give_up_on_oom = false;
        vma_flags_t new_vma_flags = vma->flags;
 
-       vma_flags_clear_mask(&new_vma_flags, __VMA_UFFD_FLAGS);
+       vma_flags_clear_mask(&new_vma_flags, VMA_UFFD);
 
        /*
         * If we are modifying only and not splitting, just give up on the merge
@@ -2313,11 +2313,10 @@ static struct vm_area_struct 
*userfaultfd_clear_vma(struct vma_iterator *vmi,
 /* Assumes mmap write lock taken, and mm_struct pinned. */
 static int userfaultfd_register_range(struct userfaultfd_ctx *ctx,
                               struct vm_area_struct *vma,
-                              vm_flags_t vm_flags,
+                              unsigned int mode,
                               unsigned long start, unsigned long end,
                               bool wp_async)
 {
-       vma_flags_t vma_flags = legacy_to_vma_flags(vm_flags);
        VMA_ITERATOR(vmi, ctx->mm, start);
        struct vm_area_struct *prev = vma_prev(&vmi);
        unsigned long vma_end;
@@ -2329,7 +2328,7 @@ static int userfaultfd_register_range(struct 
userfaultfd_ctx *ctx,
        for_each_vma_range(vmi, vma, end) {
                cond_resched();
 
-               VM_WARN_ON_ONCE(!vma_can_userfault(vma, vm_flags, wp_async));
+               VM_WARN_ON_ONCE(!vma_can_userfault(vma, mode, wp_async));
                VM_WARN_ON_ONCE(vma->vm_uffd_state.ctx &&
                                vma->vm_uffd_state.ctx != ctx);
                VM_WARN_ON_ONCE(!vma_test(vma, VMA_MAYWRITE_BIT));
@@ -2339,28 +2338,31 @@ static int userfaultfd_register_range(struct 
userfaultfd_ctx *ctx,
                 * userfaultfd and with the right tracking mode too.
                 */
                if (vma->vm_uffd_state.ctx == ctx &&
-                   vma_test_all_mask(vma, vma_flags))
+                   (uffd_mode(vma) & mode) == mode)
                        goto skip;
 
                /*
                 * Pre-scan in userfaultfd_register() already rejected mode
-                * switches that would drop VM_UFFD_WP or VM_UFFD_RWP, so a
-                * stray bit here is a bug.
+                * switches that would drop WP or RWP, so a stray bit here
+                * is a bug.
                 */
                VM_WARN_ON_ONCE(vma->vm_uffd_state.ctx == ctx &&
-                               vma->vm_flags & (VM_UFFD_WP | VM_UFFD_RWP) & 
~vm_flags);
+                               uffd_mode(vma) &
+                               (UFFD_MODE_WP | UFFD_MODE_RWP) & ~mode);
 
                if (vma->vm_start > start)
                        start = vma->vm_start;
                vma_end = min(end, vma->vm_end);
 
                new_vma_flags = vma->flags;
-               vma_flags_clear_mask(&new_vma_flags, __VMA_UFFD_FLAGS);
-               vma_flags_set_mask(&new_vma_flags, vma_flags);
+               vma_flags_set_mask(&new_vma_flags, VMA_UFFD);
 
                vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
                                            &new_vma_flags,
-                                           (struct vm_uffd_state){ctx},
+                                           (struct vm_uffd_state){
+                                               .ctx = ctx,
+                                               .mode = mode,
+                                           },
                                            /* give_up_on_oom = */false);
                if (IS_ERR(vma))
                        return PTR_ERR(vma);
@@ -2370,7 +2372,7 @@ static int userfaultfd_register_range(struct 
userfaultfd_ctx *ctx,
                 * the next vma was merged into the current one and
                 * the current one has not been updated yet.
                 */
-               userfaultfd_set_ctx(vma, ctx, vm_flags);
+               userfaultfd_set_ctx(vma, ctx, mode);
 
                if (is_vm_hugetlb_page(vma) && uffd_disable_huge_pmd_share(vma))
                        hugetlb_unshare_all_pmds(vma);
@@ -2420,7 +2422,7 @@ static void userfaultfd_release_all(struct mm_struct *mm,
        for_each_vma(vmi, vma) {
                cond_resched();
                VM_WARN_ON_ONCE(!!vma->vm_uffd_state.ctx ^
-                               !!(vma->vm_flags & __VM_UFFD_FLAGS));
+                               vma_test(vma, VMA_UFFD_BIT));
                if (vma->vm_uffd_state.ctx != ctx) {
                        prev = vma;
                        continue;
@@ -2876,9 +2878,9 @@ vm_fault_t handle_userfault(struct vm_fault *vmf, enum 
uf_reason reason)
         * NOTE: it should become possible to return VM_FAULT_RETRY
         * even if FAULT_FLAG_TRIED is set without leading to gup()
         * -EBUSY failures, if the userfaultfd is to be extended for
-        * VM_UFFD_WP tracking and we intend to arm the userfault
+        * WP tracking and we intend to arm the userfault
         * without first stopping userland access to the memory. For
-        * VM_UFFD_MISSING userfaults this is enough for now.
+        * MISSING userfaults this is enough for now.
         */
        if (unlikely(!(vmf->flags & FAULT_FLAG_ALLOW_RETRY))) {
                /*
@@ -3723,7 +3725,7 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
        int ret;
        struct uffdio_register uffdio_register;
        struct uffdio_register __user *user_uffdio_register;
-       vm_flags_t vm_flags;
+       unsigned int mode;
        bool found;
        bool basic_ioctls;
        unsigned long start, end;
@@ -3742,21 +3744,22 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
                goto out;
        if (uffdio_register.mode & ~UFFD_API_REGISTER_MODES)
                goto out;
-       vm_flags = 0;
+       mode = 0;
        if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MISSING)
-               vm_flags |= VM_UFFD_MISSING;
+               mode |= UFFD_MODE_MISSING;
        if (uffdio_register.mode & UFFDIO_REGISTER_MODE_WP) {
                if (!pgtable_supports_uffd())
                        goto out;
 
-               vm_flags |= VM_UFFD_WP;
+               mode |= UFFD_MODE_WP;
        }
        if (uffdio_register.mode & UFFDIO_REGISTER_MODE_RWP) {
-               if (!pgtable_supports_uffd() || VM_UFFD_RWP == VM_NONE)
+               if (!pgtable_supports_uffd() ||
+                   !IS_ENABLED(CONFIG_USERFAULTFD_RWP))
                        goto out;
                if (!(userfaultfd_features(ctx) & UFFD_FEATURE_RWP))
                        goto out;
-               vm_flags |= VM_UFFD_RWP;
+               mode |= UFFD_MODE_RWP;
        }
 
        /*
@@ -3764,14 +3767,14 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
         * cannot coexist in the same VMA — the bit would carry ambiguous
         * semantics. Reject the combination up front.
         */
-       if ((vm_flags & VM_UFFD_WP) && (vm_flags & VM_UFFD_RWP))
+       if ((mode & UFFD_MODE_WP) && (mode & UFFD_MODE_RWP))
                goto out;
 
        if (uffdio_register.mode & UFFDIO_REGISTER_MODE_MINOR) {
 #ifndef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
                goto out;
 #endif
-               vm_flags |= VM_UFFD_MINOR;
+               mode |= UFFD_MODE_MINOR;
        }
 
        ret = validate_range(mm, uffdio_register.range.start,
@@ -3814,11 +3817,11 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
                cond_resched();
 
                VM_WARN_ON_ONCE(!!cur->vm_uffd_state.ctx ^
-                               !!(cur->vm_flags & __VM_UFFD_FLAGS));
+                               vma_test(cur, VMA_UFFD_BIT));
 
                /* check not compatible vmas */
                ret = -EINVAL;
-               if (!vma_can_userfault(cur, vm_flags, wp_async))
+               if (!vma_can_userfault(cur, mode, wp_async))
                        goto out_unlock;
 
                /*
@@ -3829,7 +3832,7 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
                 * mprotect() must still be unregisterable, so this is not
                 * part of vma_can_userfault().
                 */
-               if ((vm_flags & VM_UFFD_RWP) && !vma_is_accessible(cur))
+               if ((mode & UFFD_MODE_RWP) && !vma_is_accessible(cur))
                        goto out_unlock;
 
                /*
@@ -3857,7 +3860,8 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
                        if (end & (vma_hpagesize - 1))
                                goto out_unlock;
                }
-               if ((vm_flags & VM_UFFD_WP) && !(cur->vm_flags & VM_MAYWRITE))
+               if ((mode & UFFD_MODE_WP) &&
+                   !vma_test(cur, VMA_MAYWRITE_BIT))
                        goto out_unlock;
 
                /*
@@ -3872,13 +3876,13 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
                        goto out_unlock;
 
                /*
-                * Mode switches that drop VM_UFFD_WP or VM_UFFD_RWP would
-                * leave PTE markers without the flag that describes them;
+                * Mode switches that drop WP or RWP would leave PTE markers
+                * without the mode that describes them;
                 * subsequent mprotect() would then promote stale markers
                 * into the other mode. Require an unregister first.
                 */
                if (cur->vm_uffd_state.ctx == ctx &&
-                   cur->vm_flags & (VM_UFFD_WP | VM_UFFD_RWP) & ~vm_flags)
+                   uffd_mode(cur) & (UFFD_MODE_WP | UFFD_MODE_RWP) & ~mode)
                        goto out_unlock;
 
                /*
@@ -3891,7 +3895,7 @@ static int userfaultfd_register(struct userfaultfd_ctx 
*ctx,
        } for_each_vma_range(vmi, cur, end);
        VM_WARN_ON_ONCE(!found);
 
-       ret = userfaultfd_register_range(ctx, vma, vm_flags, start, end,
+       ret = userfaultfd_register_range(ctx, vma, mode, start, end,
                                         wp_async);
 
 out_unlock:
@@ -3986,7 +3990,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx 
*ctx,
                cond_resched();
 
                VM_WARN_ON_ONCE(!!cur->vm_uffd_state.ctx ^
-                               !!(cur->vm_flags & __VM_UFFD_FLAGS));
+                               vma_test(cur, VMA_UFFD_BIT));
 
                /*
                 * Prevent unregistering through a different userfaultfd than
@@ -4003,7 +4007,7 @@ static int userfaultfd_unregister(struct userfaultfd_ctx 
*ctx,
                 * provides for more strict behavior to notice
                 * unregistration errors.
                 */
-               if (!vma_can_userfault(cur, cur->vm_flags, wp_async))
+               if (!vma_can_userfault(cur, uffd_mode(cur), wp_async))
                        goto out_unlock;
 
                found = true;
@@ -4024,7 +4028,8 @@ static int userfaultfd_unregister(struct userfaultfd_ctx 
*ctx,
                        goto skip;
 
                VM_WARN_ON_ONCE(vma->vm_uffd_state.ctx != ctx);
-               VM_WARN_ON_ONCE(!vma_can_userfault(vma, vma->vm_flags, 
wp_async));
+               VM_WARN_ON_ONCE(!vma_can_userfault(vma, uffd_mode(vma),
+                                                  wp_async));
                VM_WARN_ON_ONCE(!(vma->vm_flags & VM_MAYWRITE));
 
                if (vma->vm_start > start)
@@ -4329,12 +4334,12 @@ static __u64 uffd_api_available_features(void)
                       UFFD_FEATURE_WP_ASYNC);
        /*
         * RWP needs both PROT_NONE support and the uffd PTE bit. The
-        * VM_UFFD_RWP check covers compile-time unavailability; the
+        * IS_ENABLED check covers compile-time unavailability; the
         * pgtable_supports_uffd() check covers runtime (e.g. riscv
         * without the SVRSW60T59B extension) where the PTE bit is declared
         * but not actually usable.
         */
-       if (VM_UFFD_RWP == VM_NONE || !pgtable_supports_uffd())
+       if (!IS_ENABLED(CONFIG_USERFAULTFD_RWP) || !pgtable_supports_uffd())
                f &= ~(UFFD_FEATURE_RWP | UFFD_FEATURE_RWP_ASYNC);
        return f;
 }
diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h
index 1a01c3529d22..05a39c14eab2 100644
--- a/tools/testing/vma/include/dup.h
+++ b/tools/testing/vma/include/dup.h
@@ -109,7 +109,7 @@ enum {
        DECLARE_VMA_BIT(MAYSHARE, 7),
        DECLARE_VMA_BIT(GROWSDOWN, 8),  /* general info on the segment */
 #ifdef CONFIG_MMU
-       DECLARE_VMA_BIT(UFFD_MISSING, 9),/* missing pages tracking */
+       DECLARE_VMA_BIT(UFFD, 9),       /* userfaultfd registered */
 #else
        /* nommu: R/O MAP_PRIVATE mapping that might overlay a file mapping */
        DECLARE_VMA_BIT(MAYOVERLAY, 9),
@@ -117,7 +117,7 @@ enum {
        /* Page-ranges managed without "struct page", just pure PFN */
        DECLARE_VMA_BIT(PFNMAP, 10),
        DECLARE_VMA_BIT(MAYBE_GUARD, 11),
-       DECLARE_VMA_BIT(UFFD_WP, 12),   /* wrprotect pages tracking */
+       /* Bit 12 is free */
        DECLARE_VMA_BIT(LOCKED, 13),
        DECLARE_VMA_BIT(IO, 14),        /* Memory mapped I/O or similar */
        DECLARE_VMA_BIT(SEQ_READ, 15),  /* App will access data sequentially */
@@ -158,7 +158,7 @@ enum {
 #else
        DECLARE_VMA_BIT(DROPPABLE, 40),
 #endif
-       DECLARE_VMA_BIT(UFFD_MINOR, 41),
+       /* Bit 41 is free */
        DECLARE_VMA_BIT(SEALED, 42),
        /* Flags that reuse flags above. */
        DECLARE_VMA_BIT_ALIAS(PKEY_BIT0, HIGH_ARCH_0),
@@ -211,14 +211,13 @@ enum {
 #define VM_MAYSHARE    INIT_VM_FLAG(MAYSHARE)
 #define VM_GROWSDOWN   INIT_VM_FLAG(GROWSDOWN)
 #ifdef CONFIG_MMU
-#define VM_UFFD_MISSING        INIT_VM_FLAG(UFFD_MISSING)
+#define VM_UFFD                INIT_VM_FLAG(UFFD)
 #else
-#define VM_UFFD_MISSING        VM_NONE
+#define VM_UFFD                VM_NONE
 #define VM_MAYOVERLAY  INIT_VM_FLAG(MAYOVERLAY)
 #endif
 #define VM_PFNMAP      INIT_VM_FLAG(PFNMAP)
 #define VM_MAYBE_GUARD INIT_VM_FLAG(MAYBE_GUARD)
-#define VM_UFFD_WP     INIT_VM_FLAG(UFFD_WP)
 #define VM_LOCKED      INIT_VM_FLAG(LOCKED)
 #define VM_IO          INIT_VM_FLAG(IO)
 #define VM_SEQ_READ    INIT_VM_FLAG(SEQ_READ)
@@ -297,11 +296,6 @@ enum {
 #define VM_MTE         VM_NONE
 #define VM_MTE_ALLOWED VM_NONE
 #endif
-#ifdef CONFIG_HAVE_ARCH_USERFAULTFD_MINOR
-#define VM_UFFD_MINOR  INIT_VM_FLAG(UFFD_MINOR)
-#else
-#define VM_UFFD_MINOR  VM_NONE
-#endif
 #ifdef CONFIG_64BIT
 #define VM_ALLOW_ANY_UNCACHED  INIT_VM_FLAG(ALLOW_ANY_UNCACHED)
 #define VM_SEALED              INIT_VM_FLAG(SEALED)
@@ -387,7 +381,7 @@ enum {
 
 #define VMA_IGNORE_MERGE_FLAGS VMA_STICKY_FLAGS
 
-#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_UFFD_WP | VM_MAYBE_GUARD)
+#define VM_COPY_ON_FORK (VM_PFNMAP | VM_MIXEDMAP | VM_MAYBE_GUARD)
 
 #define pgprot_val(x)          ((x).pgprot)
 #define __pgprot(x)            ((pgprot_t) { (x) } )

-- 
2.53.0


Reply via email to