#include <linux/mm_inline.h>
 #include <linux/pgtable.h>
 #include <linux/sched/sysctl.h>
+#include <linux/userfaultfd_k.h>
 #include <asm/cacheflush.h>
 #include <asm/mmu_context.h>
 #include <asm/tlbflush.h>
                                        newpte = pte_swp_mksoft_dirty(newpte);
                                if (pte_swp_uffd_wp(oldpte))
                                        newpte = pte_swp_mkuffd_wp(newpte);
-                       } else if (is_pte_marker_entry(entry)) {
-                               /* Skip it, the same as none pte */
+                       } else if (pte_marker_entry_uffd_wp(entry)) {
+                               /*
+                                * If this is uffd-wp pte marker and we'd like
+                                * to unprotect it, drop it; the next page
+                                * fault will trigger without uffd trapping.
+                                */
+                               if (uffd_wp_resolve) {
+                                       pte_clear(vma->vm_mm, addr, pte);
+                                       pages++;
+                               }
                                continue;
                        } else {
                                newpte = oldpte;
                                set_pte_at(vma->vm_mm, addr, pte, newpte);
                                pages++;
                        }
+               } else {
+                       /* It must be an none page, or what else?.. */
+                       WARN_ON_ONCE(!pte_none(oldpte));
+                       if (unlikely(uffd_wp && !vma_is_anonymous(vma))) {
+                               /*
+                                * For file-backed mem, we need to be able to
+                                * wr-protect a none pte, because even if the
+                                * pte is none, the page/swap cache could
+                                * exist.  Doing that by install a marker.
+                                */
+                               set_pte_at(vma->vm_mm, addr, pte,
+                                          make_pte_marker(PTE_MARKER_UFFD_WP));
+                               pages++;
+                       }
                }
        } while (pte++, addr += PAGE_SIZE, addr != end);
        arch_leave_lazy_mmu_mode();
        return 0;
 }
 
+/* Return true if we're uffd wr-protecting file-backed memory, or false */
+static inline bool
+uffd_wp_protect_file(struct vm_area_struct *vma, unsigned long cp_flags)
+{
+       return (cp_flags & MM_CP_UFFD_WP) && !vma_is_anonymous(vma);
+}
+
+/*
+ * If wr-protecting the range for file-backed, populate pgtable for the case
+ * when pgtable is empty but page cache exists.  When {pte|pmd|...}_alloc()
+ * failed it means no memory, we don't have a better option but stop.
+ */
+#define  change_pmd_prepare(vma, pmd, cp_flags)                                \
+       do {                                                            \
+               if (unlikely(uffd_wp_protect_file(vma, cp_flags))) {    \
+                       if (WARN_ON_ONCE(pte_alloc(vma->vm_mm, pmd)))   \
+                               break;                                  \
+               }                                                       \
+       } while (0)
+/*
+ * This is the general pud/p4d/pgd version of change_pmd_prepare(). We need to
+ * have separate change_pmd_prepare() because pte_alloc() returns 0 on success,
+ * while {pmd|pud|p4d}_alloc() returns the valid pointer on success.
+ */
+#define  change_prepare(vma, high, low, addr, cp_flags)                        \
+       do {                                                            \
+               if (unlikely(uffd_wp_protect_file(vma, cp_flags))) {    \
+                       low##_t *p = low##_alloc(vma->vm_mm, high, addr); \
+                       if (WARN_ON_ONCE(p == NULL))                    \
+                               break;                                  \
+               }                                                       \
+       } while (0)
+
 static inline unsigned long change_pmd_range(struct mmu_gather *tlb,
                struct vm_area_struct *vma, pud_t *pud, unsigned long addr,
                unsigned long end, pgprot_t newprot, unsigned long cp_flags)
 
                next = pmd_addr_end(addr, end);
 
+               change_pmd_prepare(vma, pmd, cp_flags);
                /*
                 * Automatic NUMA balancing walks the tables with mmap_lock
                 * held for read. It's possible a parallel update to occur
        pud = pud_offset(p4d, addr);
        do {
                next = pud_addr_end(addr, end);
+               change_prepare(vma, pud, pmd, addr, cp_flags);
                if (pud_none_or_clear_bad(pud))
                        continue;
                pages += change_pmd_range(tlb, vma, pud, addr, next, newprot,
        p4d = p4d_offset(pgd, addr);
        do {
                next = p4d_addr_end(addr, end);
+               change_prepare(vma, p4d, pud, addr, cp_flags);
                if (p4d_none_or_clear_bad(p4d))
                        continue;
                pages += change_pud_range(tlb, vma, p4d, addr, next, newprot,
        tlb_start_vma(tlb, vma);
        do {
                next = pgd_addr_end(addr, end);
+               change_prepare(vma, pgd, p4d, addr, cp_flags);
                if (pgd_none_or_clear_bad(pgd))
                        continue;
                pages += change_p4d_range(tlb, vma, pgd, addr, next, newprot,