On Thu, 06 Aug 2026 21:21:25 +0100 "Lorenzo Stoakes (ARM)" <[email protected]> 
wrote:

> In memory management we've managed to manufacture a great deal of confusion
> around the concept of anonymous memory. We have:
> 
> 1. 'Pure anon' memory - anonymous VMAs whose folios are anonymous and
>    swap-backed (thus for reclaim purposes, treated as anonymous). These are
>    simple enough.
> 
> 2. shmem - file-backed VMAs, file-backed folios (from rmap perspective) so
>    present in the page cache and mapped by an address_space object, but
>    whose folios are also swap-backed (thus treated as anonymous for reclaim
>    purposes).
> 
> 3. MAP_PRIVATE-mapped /dev/zero - a strange beast whose VMAs have
>    vma->vm_file set, but which clears vma->vm_ops to satisfy
>    vma_is_anonymous(), resulting in VMAs that were mmap()'d referencing a
>    file, but are in every other sense anonymous, including the folios.
> 
> 4. Other MAP_PRIVATE-file backed mappings - These possess file-backed VMAs
>    and have file-backed folios until CoW'd, at which point those CoW'd
>    folios are anonymous.
> 
> This series fixes issues 3 and 4.

Thanks, I've updated mm.git's mm-unstable branch to this version.

It's about 450 patches away from head-of-queue, so it will be a
second-week-of-merge-window thing.  Three weeks to go.

> v4:
> - Updated tags (thanks everyone!)
> - Adjusted some prose as per David.
> - Adjusted whitespace to 2 tabs in 2/15 as per David.
> - Improved linear_page_index(), __linear_anon_page_index() to be more
>   succinct in 2/15 as per David and updated commit message to reflect it.
> - Added commit to provide vma_[flags_]is_cow_mapping() - nearly all callers
>   are calling is_cow_mapping() as is_cow_mapping(vma->vm_flags) so just
>   provide a helper to do this for them.
> - In the same commit update all is_cow_mapping() callers and remove the
>   now-unused function.
> - Change linear_anon_page_index() to assert on !CoW mapping using
>   vma_is_cow_mapping() in 2/15 as per David.
> - Updated 4/15 to output index differently depending on whether the VMA is
>   a CoW mapping or not or whether the file index differs from the anon
>   index as per Gregory.
> - Updated 6/15 to change the 'update page offset' logic in copy_vma() to
>   not be gated on CoW or VMA_SHARED_BIT as discussed with David.
> - Updated 6/15 to improve the 'faulted in anon vma' assert stuff. It was
>   very unclear so rename the variable sensibly and update the comment.
> - Added a separate commit to fix the mess that is the faulted_in_anon_vma
>   and the VM_WARN_ON_ONCE_VMA() assert in copy_vma() - make it actually
>   only update the vmap in cases where the VMA was replaced (backwards
>   remap), update the checks to reflect this in a way that's actually
>   understandable and improve the comment.
> - Updated needs_adjacent_anon_gpoff() in 9/15 to use
>   vma_flags_is_cow_mapping() as per David. Updated the comment to reflect
>   it.
> - Broke out changes to vma_address_end() into a separate patch as per David.
> - Eliminated pgoff in vma_address_end() as it adds confusion - just use
>   pgoff_end, which was what pgoff used to be (confusingly).
> - Placed more variables in vma_address_end() as constants at the start of
>   the function.
> - Dropped KSM comment in 9/15 as per David.
> - Dropped linear_folio_page_index() patch altogether as per
>   David.
> - Realised all off uffd is only anon so use linear_anon_page_index()
>   throughout there and also the huge memory case for the same reason.
> - Added a patch to make remove_migration_pmd() accept a folio instead of a
>   page.
> - Added a patch to calculate large folio index using PFN as suggested by
>   David, eliminating the need for linear page index lookup at all.
> - Added a patch to add self merge VMA userland tests.

Well this grew up.  Here's how v4 altered mm.git:


 arch/s390/mm/gmap_helpers.c                            |    2 
 drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c                |    4 
 drivers/gpu/drm/drm_gem_shmem_helper.c                 |    2 
 drivers/gpu/drm/panthor/panthor_gem.c                  |    2 
 drivers/gpu/drm/ttm/ttm_bo_vm.c                        |    2 
 drivers/gpu/drm/xe/xe_device.c                         |    2 
 fs/proc/task_mmu.c                                     |    2 
 include/linux/mm.h                                     |   61 ++---
 include/linux/mm_types.h                               |   12 -
 include/linux/pagemap.h                                |   61 +----
 include/linux/rmap.h                                   |    6 
 include/linux/swapops.h                                |    6 
 kernel/events/uprobes.c                                |    2 
 mm/gup.c                                               |    2 
 mm/huge_memory.c                                       |   27 +-
 mm/hugetlb.c                                           |    2 
 mm/internal.h                                          |   45 +--
 mm/interval_tree.c                                     |    4 
 mm/ksm.c                                               |    7 
 mm/memory.c                                            |   40 +--
 mm/mempolicy.c                                         |    2 
 mm/migrate.c                                           |   23 -
 mm/mremap.c                                            |    6 
 mm/rmap.c                                              |    8 
 mm/userfaultfd.c                                       |    6 
 mm/vma.c                                               |  118 +++++-----
 mm/vma.h                                               |   70 -----
 mm/vma_init.c                                          |    2 
 tools/testing/selftests/mm/merge.c                     |   10 
 tools/testing/selftests/proc/proc-self-map-files-001.c |    2 
 tools/testing/selftests/proc/proc-self-map-files-002.c |    2 
 tools/testing/vma/include/dup.h                        |   41 ++-
 tools/testing/vma/shared.c                             |    2 
 tools/testing/vma/tests/merge.c                        |   59 ++---
 tools/testing/vma/tests/mmap.c                         |    6 
 tools/testing/vma/tests/vma.c                          |   46 +++
 36 files changed, 337 insertions(+), 357 deletions(-)

--- a/arch/s390/mm/gmap_helpers.c~b
+++ a/arch/s390/mm/gmap_helpers.c
@@ -200,7 +200,7 @@ static int find_zeropage_pte_entry(pte_t
                 * currently only works in COW mappings, which is also where
                 * mm_forbids_zeropage() is checked.
                 */
-               if (!is_cow_mapping(walk->vma->vm_flags))
+               if (!vma_is_cow_mapping(walk->vma))
                        return -EFAULT;
 
                *found_addr = addr;
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c~b
+++ a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c
@@ -377,9 +377,9 @@ static int amdgpu_gem_object_mmap(struct
        /* Workaround for Thunk bug creating PROT_NONE,MAP_PRIVATE mappings
         * for debugger access to invisible VRAM. Should have used MAP_SHARED
         * instead. Clearing VM_MAYWRITE prevents the mapping from ever
-        * becoming writable and makes is_cow_mapping(vm_flags) false.
+        * becoming writable and makes vma_is_cow_mapping(vma) false.
         */
-       if (is_cow_mapping(vma->vm_flags) &&
+       if (vma_is_cow_mapping(vma) &&
            !(vma->vm_flags & VM_ACCESS_FLAGS))
                vm_flags_clear(vma, VM_MAYWRITE);
 
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c~b
+++ a/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -753,7 +753,7 @@ int drm_gem_shmem_mmap(struct drm_gem_sh
                return ret;
        }
 
-       if (is_cow_mapping(vma->vm_flags))
+       if (vma_is_cow_mapping(vma))
                return -EINVAL;
 
        dma_resv_lock(shmem->base.resv, NULL);
--- a/drivers/gpu/drm/panthor/panthor_gem.c~b
+++ a/drivers/gpu/drm/panthor/panthor_gem.c
@@ -761,7 +761,7 @@ static int panthor_gem_mmap(struct drm_g
                return ret;
        }
 
-       if (is_cow_mapping(vma->vm_flags))
+       if (vma_is_cow_mapping(vma))
                return -EINVAL;
 
        if (!refcount_inc_not_zero(&bo->cmap.mmap_count)) {
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c~b
+++ a/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -489,7 +489,7 @@ static const struct vm_operations_struct
 int ttm_bo_mmap_obj(struct vm_area_struct *vma, struct ttm_buffer_object *bo)
 {
        /* Enforce no COW since would have really strange behavior with it. */
-       if (is_cow_mapping(vma->vm_flags))
+       if (vma_is_cow_mapping(vma))
                return -EINVAL;
 
        drm_gem_object_get(&bo->base);
--- a/drivers/gpu/drm/xe/xe_device.c~b
+++ a/drivers/gpu/drm/xe/xe_device.c
@@ -330,7 +330,7 @@ static int xe_pci_barrier_mmap(struct fi
        if (vma->vm_end - vma->vm_start > SZ_4K)
                return -EINVAL;
 
-       if (is_cow_mapping(vma->vm_flags))
+       if (vma_is_cow_mapping(vma))
                return -EINVAL;
 
        if (vma->vm_flags & (VM_READ | VM_EXEC))
--- a/fs/proc/task_mmu.c~b
+++ a/fs/proc/task_mmu.c
@@ -1693,7 +1693,7 @@ static inline bool pte_is_pinned(struct
 
        if (!pte_write(pte))
                return false;
-       if (!is_cow_mapping(vma->vm_flags))
+       if (!vma_is_cow_mapping(vma))
                return false;
        if (likely(!mm_flags_test(MMF_HAS_PINNED, vma->vm_mm)))
                return false;
--- a/include/linux/mm.h~b
+++ a/include/linux/mm.h
@@ -2269,17 +2269,20 @@ void unpin_user_pages(struct page **page
 void unpin_user_folio(struct folio *folio, unsigned long npages);
 void unpin_folios(struct folio **folios, unsigned long nfolios);
 
-static inline bool is_cow_mapping(vm_flags_t flags)
+static inline bool vma_flags_is_cow_mapping(const vma_flags_t *flags)
 {
-       return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE;
+       return vma_flags_test(flags, VMA_MAYWRITE_BIT) &&
+               !vma_flags_test(flags, VMA_SHARED_BIT);
 }
 
-static inline bool vma_desc_is_cow_mapping(struct vm_area_desc *desc)
+static inline bool vma_is_cow_mapping(const struct vm_area_struct *vma)
 {
-       const vma_flags_t *flags = &desc->vma_flags;
+       return vma_flags_is_cow_mapping(&vma->flags);
+}
 
-       return vma_flags_test(flags, VMA_MAYWRITE_BIT) &&
-               !vma_flags_test(flags, VMA_SHARED_BIT);
+static inline bool vma_desc_is_cow_mapping(struct vm_area_desc *desc)
+{
+       return vma_flags_is_cow_mapping(&desc->vma_flags);
 }
 
 #ifndef CONFIG_MMU
@@ -4391,62 +4394,62 @@ static inline pgoff_t vma_last_pgoff(con
 }
 
 /**
- * vma_start_virt_pgoff() - Get the virtual page offset of the start of @vma
- * @vma: The VMA whose virtual page offset is required.
+ * vma_start_anon_pgoff() - Get the anonymous page offset of the start of @vma
+ * @vma: The VMA whose anonymous page offset is required.
  *
  * If unfaulted, then this is vma->vm_start >> PAGE_SHIFT, if faulted then the
- * virtual page offset at the time of first fault.
+ * anonymous page offset at the time of first fault.
  *
  * If the VMA is anonymous, this returns the same value as vma_start_pgoff().
  *
  * This value is used for tracking MAP_PRIVATE file-backed mappings by their
- * virtual page offset.
+ * anonymous page offset.
  *
- * Returns: The virtual page offset of the start of @vma.
+ * Returns: The anonymous page offset of the start of @vma.
  */
-static inline pgoff_t vma_start_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_start_anon_pgoff(const struct vm_area_struct *vma)
 {
        pgoff_t pgoff = 0;
 
 #ifdef CONFIG_64BIT
-       pgoff += vma->__vm_virt_pgoff_hi;
+       pgoff += vma->__vm_anon_pgoff_hi;
        pgoff <<= 32;
 #endif
-       pgoff += vma->__vm_virt_pgoff_lo;
+       pgoff += vma->__vm_anon_pgoff_lo;
        return pgoff;
 }
 
 /**
- * vma_end_virt_pgoff() - Get the virtual page offset of the exclusive end of
+ * vma_end_anon_pgoff() - Get the anonymous page offset of the exclusive end of
  * @vma.
- * @vma: The VMA whose end virtual page offset is required.
+ * @vma: The VMA whose end anonymous page offset is required.
  *
- * This returns the virtual exclusive end page offset of @vma, which is useful
+ * This returns the anonymous exclusive end page offset of @vma, which is 
useful
  * for expressing page offset ranges.
  *
- * See the description of vma_start_virt_pgoff() for a description of VMA
- * virtual page offsets.
+ * See the description of vma_start_anon_pgoff() for a description of VMA
+ * anonymous page offsets.
  *
- * Returns: The exclusive end virtual page offset of @vma.
+ * Returns: The exclusive end anonymous page offset of @vma.
  */
-static inline pgoff_t vma_end_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma)
 {
-       return vma_start_virt_pgoff(vma) + vma_pages(vma);
+       return vma_start_anon_pgoff(vma) + vma_pages(vma);
 }
 
 /**
- * vma_last_virt_pgoff() - Get the virtual page offset of the last page in
+ * vma_last_anon_pgoff() - Get the anonymous page offset of the last page in
  * @vma.
- * @vma: The VMA whose last virtual page offset is required.
+ * @vma: The VMA whose last anonymous page offset is required.
  *
- * See the description of vma_start_virt_pgoff() for a description of VMA
- * virtual page offsets.
+ * See the description of vma_start_anon_pgoff() for a description of VMA
+ * anonymous page offsets.
  *
- * Returns: The last virtual page offset of @vma.
+ * Returns: The last anonymous page offset of @vma.
  */
-static inline pgoff_t vma_last_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_last_anon_pgoff(const struct vm_area_struct *vma)
 {
-       return vma_end_virt_pgoff(vma) - 1;
+       return vma_end_anon_pgoff(vma) - 1;
 }
 
 static inline unsigned long vma_desc_size(const struct vm_area_desc *desc)
--- a/include/linux/mm_types.h~b
+++ a/include/linux/mm_types.h
@@ -968,10 +968,10 @@ struct vm_area_struct {
        unsigned int vm_lock_seq;
 #endif
        /*
-        * Low 32-bits of virtual page offset.
-        * See vma_start_virt_pgoff() comment for details.
+        * Low 32-bits of anonymous page offset.
+        * See vma_start_anon_pgoff() comment for details.
         */
-       unsigned int __vm_virt_pgoff_lo;
+       unsigned int __vm_anon_pgoff_lo;
        /*
         * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
         * list, after a COW of one of the file pages.  A MAP_SHARED vma
@@ -1049,10 +1049,10 @@ struct vm_area_struct {
 #endif
 #ifdef CONFIG_64BIT
        /*
-        * High 32-bits of virtual page offset.
-        * See vma_start_virt_pgoff() comment for details.
+        * High 32-bits of anonymous page offset.
+        * See vma_start_anon_pgoff() comment for details.
         */
-       unsigned int __vm_virt_pgoff_hi;
+       unsigned int __vm_anon_pgoff_hi;
 #endif
        /*
         * For areas with an address space and backing store,
--- a/include/linux/pagemap.h~b
+++ a/include/linux/pagemap.h
@@ -1094,79 +1094,46 @@ static inline pgoff_t linear_page_delta(
 static inline pgoff_t linear_page_index(const struct vm_area_struct *vma,
                                        const unsigned long address)
 {
-       pgoff_t pgoff;
-
-       pgoff = linear_page_delta(vma, address);
-       pgoff += vma_start_pgoff(vma);
-       return pgoff;
+       return linear_page_delta(vma, address) + vma_start_pgoff(vma);
 }
 
-static inline pgoff_t __linear_virt_page_index(const struct vm_area_struct 
*vma,
-                                              const unsigned long address)
+static inline pgoff_t __linear_anon_page_index(const struct vm_area_struct 
*vma,
+               const unsigned long address)
 {
-       pgoff_t pgoff;
-
-       pgoff = linear_page_delta(vma, address);
-       pgoff += vma_start_virt_pgoff(vma);
-       return pgoff;
+       return linear_page_delta(vma, address) + vma_start_anon_pgoff(vma);
 }
 
 /**
- * linear_virt_page_index() - Determine the absolute virtual page offset of
+ * linear_anon_page_index() - Determine the absolute anonymous page offset of
  * @address within @vma.
  * @vma: An anonymous or MAP_PRIVATE file-backed VMA in which @address resides.
  * @address: The address whose absolute page offset is required.
  *
- * This returns the virtual page offset of @address, which is the page offset
+ * This returns the anonymous page offset of @address, which is the page offset
  * the address possessed at the time the VMA was first faulted.
  *
  * For anonymous mappings, this returns the same value as linear_page_index().
  *
- * For MAP_PRIVATE file-backed mappings, this returns the virtual page offset 
of
- * @address, which is the page offset the address possessed at the time the VMA
- * was first faulted.
+ * For MAP_PRIVATE file-backed mappings, this returns the anonymous page offset
+ * of @address, which is the page offset the address possessed at the time the
+ * VMA was first faulted.
  *
  * It is not valid to call this function for shared file-backed mappings.
  *
- * Returns: The absolute virtual page offset of @address within @vma.
+ * Returns: The absolute anonymous page offset of @address within @vma.
  */
-static inline pgoff_t linear_virt_page_index(const struct vm_area_struct *vma,
-                                            const unsigned long address)
+static inline pgoff_t linear_anon_page_index(const struct vm_area_struct *vma,
+               const unsigned long address)
 {
-       const pgoff_t pgoff = __linear_virt_page_index(vma, address);
+       const pgoff_t pgoff = __linear_anon_page_index(vma, address);
 
-       VM_WARN_ON_ONCE(vma_test(vma, VMA_SHARED_BIT));
+       VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma));
        if (vma_is_anonymous(vma))
                VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address));
 
        return pgoff;
 }
 
-/**
- * linear_folio_page_index() - Determine the absolute page offset of
- * @address within @vma from @folio.
- * @folio: The folio whose linear page index is sought.
- * @vma: The VMA in which @address resides.
- * @address: The address whose absolute page offset is required.
- *
- * Determines whether to obtain the virtual linear page index based on whether
- * @folio is anonymous or not.
- *
- * See the descriptions of linear_virt_page_index() and linear_page_index() for
- * details of each.
- *
- * Returns: The absolute page offset of @address within @vma.
- */
-static inline pgoff_t linear_folio_page_index(const struct folio *folio,
-                                             const struct vm_area_struct *vma,
-                                             const unsigned long address)
-{
-       if (folio_test_anon(folio))
-               return linear_virt_page_index(vma, address);
-
-       return linear_page_index(vma, address);
-}
-
 struct wait_page_key {
        struct folio *folio;
        int bit_nr;
--- a/include/linux/rmap.h~b
+++ a/include/linux/rmap.h
@@ -864,14 +864,14 @@ struct page *make_device_exclusive(struc
 struct page_vma_mapped_walk {
        unsigned long pfn;
        unsigned long nr_pages;
-       pgoff_t pgoff;
+       pgoff_t pgoff;  /* Only meaningful if nr_pages > 1 and not a KSM walk */
        struct vm_area_struct *vma;
        unsigned long address;
        pmd_t *pmd;
        pte_t *pte;
        spinlock_t *ptl;
        unsigned int flags;
-       bool is_anon_walk;
+       bool pgoff_is_anon : 1;
 };
 
 #define DEFINE_FOLIO_VMA_WALK(name, _folio, _vma, _address, _flags)    \
@@ -882,7 +882,7 @@ struct page_vma_mapped_walk {
                .vma = _vma,                                            \
                .address = _address,                                    \
                .flags = _flags,                                        \
-               .is_anon_walk = folio_test_anon(_folio),                \
+               .pgoff_is_anon = folio_test_anon(_folio),               \
        }
 
 static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw)
--- a/include/linux/swapops.h~b
+++ a/include/linux/swapops.h
@@ -325,8 +325,8 @@ struct page_vma_mapped_walk;
 extern int set_pmd_migration_entry(struct page_vma_mapped_walk *pvmw,
                struct page *page);
 
-extern void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
-               struct page *new);
+void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
+               struct folio *folio);
 
 extern void pmd_migration_entry_wait(struct mm_struct *mm, pmd_t *pmd);
 
@@ -346,7 +346,7 @@ static inline int set_pmd_migration_entr
 }
 
 static inline void remove_migration_pmd(struct page_vma_mapped_walk *pvmw,
-               struct page *new)
+               struct folio *folio)
 {
        BUILD_BUG();
 }
--- a/kernel/events/uprobes.c~b
+++ a/kernel/events/uprobes.c
@@ -513,7 +513,7 @@ int uprobe_write(struct arch_uprobe *aup
 
        uprobe = container_of(auprobe, struct uprobe, arch);
 
-       if (WARN_ON_ONCE(!is_cow_mapping(vma->vm_flags)))
+       if (WARN_ON_ONCE(!vma_is_cow_mapping(vma)))
                return -EINVAL;
 
        /*
--- a/mm/gup.c~b
+++ a/mm/gup.c
@@ -1236,7 +1236,7 @@ static int check_vma_flags(struct vm_are
                         * Anon pages in shared mappings are surprising: now
                         * just reject it.
                         */
-                       if (!is_cow_mapping(vm_flags))
+                       if (!vma_is_cow_mapping(vma))
                                return -EFAULT;
                }
        } else if (!(vm_flags & VM_READ)) {
--- a/mm/huge_memory.c~b
+++ a/mm/huge_memory.c
@@ -1681,7 +1681,7 @@ vm_fault_t vmf_insert_pfn_pmd(struct vm_
        BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
        BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
                                                (VM_PFNMAP|VM_MIXEDMAP));
-       BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
+       BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
 
        pfnmap_setup_cachemode_pfn(pfn, &pgprot);
 
@@ -1789,7 +1789,7 @@ vm_fault_t vmf_insert_pfn_pud(struct vm_
        BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
        BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
                                                (VM_PFNMAP|VM_MIXEDMAP));
-       BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
+       BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
 
        pfnmap_setup_cachemode_pfn(pfn, &pgprot);
 
@@ -1931,7 +1931,7 @@ int copy_huge_pmd(struct mm_struct *dst_
                 * applied special bit, or we made the PRIVATE mapping be
                 * able to wrongly write to the backend MMIO.
                 */
-               VM_WARN_ON_ONCE(is_cow_mapping(src_vma->vm_flags) && 
pmd_write(pmd));
+               VM_WARN_ON_ONCE(vma_is_cow_mapping(src_vma) && pmd_write(pmd));
                goto set_pmd;
        }
 
@@ -2052,7 +2052,7 @@ int copy_huge_pud(struct mm_struct *dst_
         * TODO: once we support anonymous pages, use
         * folio_try_dup_anon_rmap_*() and split if duplicating fails.
         */
-       if (is_cow_mapping(vma->vm_flags) && pud_write(pud)) {
+       if (vma_is_cow_mapping(vma) && pud_write(pud)) {
                pudp_set_wrprotect(src_mm, addr, src_pud);
                pud = pud_wrprotect(pud);
        }
@@ -2930,8 +2930,7 @@ int move_pages_huge_pmd(struct mm_struct
                }
 
                folio_move_anon_rmap(src_folio, dst_vma);
-               src_folio->index = linear_folio_page_index(src_folio, dst_vma,
-                                                          dst_addr);
+               src_folio->index = linear_anon_page_index(dst_vma, dst_addr);
 
                _dst_pmd = folio_mk_pmd(src_folio, dst_vma->vm_page_prot);
                /* Follow mremap() behavior and treat the entry dirty after the 
move */
@@ -5078,9 +5077,8 @@ int set_pmd_migration_entry(struct page_
        return 0;
 }
 
-void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct page *new)
+void remove_migration_pmd(struct page_vma_mapped_walk *pvmw, struct folio 
*folio)
 {
-       struct folio *folio = page_folio(new);
        struct vm_area_struct *vma = pvmw->vma;
        struct mm_struct *mm = vma->vm_mm;
        unsigned long address = pvmw->address;
@@ -5116,11 +5114,9 @@ void remove_migration_pmd(struct page_vm
                swp_entry_t entry;
 
                if (pmd_write(pmde))
-                       entry = make_writable_device_private_entry(
-                                                       page_to_pfn(new));
+                       entry = 
make_writable_device_private_entry(folio_pfn(folio));
                else
-                       entry = make_readable_device_private_entry(
-                                                       page_to_pfn(new));
+                       entry = 
make_readable_device_private_entry(folio_pfn(folio));
                pmde = softleaf_to_pmd(entry);
 
                if (pmd_swp_soft_dirty(*pvmw->pmd))
@@ -5135,11 +5131,12 @@ void remove_migration_pmd(struct page_vm
                if (!softleaf_is_migration_read(entry))
                        rmap_flags |= RMAP_EXCLUSIVE;
 
-               folio_add_anon_rmap_pmd(folio, new, vma, haddr, rmap_flags);
+               folio_add_anon_rmap_pmd(folio, &folio->page, vma, haddr, 
rmap_flags);
        } else {
-               folio_add_file_rmap_pmd(folio, new, vma);
+               folio_add_file_rmap_pmd(folio, &folio->page, vma);
        }
-       VM_BUG_ON(pmd_write(pmde) && folio_test_anon(folio) && 
!PageAnonExclusive(new));
+       VM_WARN_ON_ONCE(pmd_write(pmde) && folio_test_anon(folio) &&
+                       !PageAnonExclusive(&folio->page));
        set_pmd_at(mm, haddr, pvmw->pmd, pmde);
 
        /* No need to invalidate - it was non-present before */
--- a/mm/hugetlb.c~b
+++ a/mm/hugetlb.c
@@ -4898,7 +4898,7 @@ int copy_hugetlb_page_range(struct mm_st
        pte_t *src_pte, *dst_pte, entry;
        struct folio *pte_folio;
        unsigned long addr;
-       bool cow = is_cow_mapping(src_vma->vm_flags);
+       bool cow = vma_is_cow_mapping(src_vma);
        struct hstate *h = hstate_vma(src_vma);
        unsigned long sz = huge_page_size(h);
        unsigned long npages = pages_per_huge_page(h);
--- a/mm/internal.h~b
+++ a/mm/internal.h
@@ -240,19 +240,18 @@ static inline int mmap_file(struct file
 {
        int err = vfs_mmap(file, vma);
 
-       /* Hooks cannot mark themselves anonymous. */
-       if (WARN_ON_ONCE(vma_is_anonymous(vma)))
-               err = -EINVAL;
-
-       if (likely(!err))
-               return 0;
-
        /*
-        * OK, we tried to call the file hook for mmap(), but an error
-        * arose. The mapping is in an inconsistent state and we must not invoke
-        * any further hooks on it.
+        * Either we tried to call the file hook for mmap() and an error arose
+        * or a driver set vma->vm_ops = NULL intending there to be no VMA
+        * operations.
+        *
+        * In the former case the VMA is in an inconsistent state and we mustn't
+        * invoke any further hooks on it, in the latter case the hook actually
+        * wanted no further hooks to be invoked, so fix both by setting dummy
+        * VMA ops.
         */
-       vma->vm_ops = &vma_dummy_vm_ops;
+       if (unlikely(err || !vma->vm_ops))
+               vma->vm_ops = &vma_dummy_vm_ops;
 
        return err;
 }
@@ -950,7 +949,7 @@ folio_within_range(struct folio *folio,
 
        pgoff_folio = folio_pgoff(folio);
        pgoff_vma_start = folio_test_anon(folio) ?
-               vma_start_virt_pgoff(vma) : vma_start_pgoff(vma);
+               vma_start_anon_pgoff(vma) : vma_start_pgoff(vma);
 
        if (start < vma->vm_start)
                start = vma->vm_start;
@@ -1064,20 +1063,20 @@ static inline unsigned long vma_fileback
  * vma_anon_address - Find the virtual address an anonymous page range is 
mapped
  * at.
  * @vma: The vma which maps this object.
- * @pgoff_virt: The virtual page index belonging to the folio.
+ * @pgoff_anon: The anonymous page index belonging to the folio.
  * @nr_pages: The number of pages to consider.
  *
  * This is only valid for anonymous or MAP_PRIVATE-mapped file-backed VMAs.
  *
- * Returns: If any page in this range is mapped by this VMA, return the first 
address
- * where any of these pages appear. Otherwise, return -EFAULT.
+ * Returns: If any page in this range is mapped by this VMA, return the first
+ * address where any of these pages appear. Otherwise, return -EFAULT.
  */
 static inline unsigned long vma_anon_address(const struct vm_area_struct *vma,
-               pgoff_t pgoff_virt, unsigned long nr_pages)
+               pgoff_t pgoff_anon, unsigned long nr_pages)
 {
-       VM_WARN_ON_ONCE(!vma_is_anonymous(vma) && vma_test(vma, 
VMA_SHARED_BIT));
+       VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma));
 
-       return __vma_address(vma, pgoff_virt, vma_start_virt_pgoff(vma), 
nr_pages);
+       return __vma_address(vma, pgoff_anon, vma_start_anon_pgoff(vma), 
nr_pages);
 }
 
 /*
@@ -1086,22 +1085,20 @@ static inline unsigned long vma_anon_add
  */
 static inline unsigned long vma_address_end(struct page_vma_mapped_walk *pvmw)
 {
+       const pgoff_t pgoff_end = pvmw->pgoff + pvmw->nr_pages;
        const struct vm_area_struct *vma = pvmw->vma;
-       const pgoff_t pgoff = pvmw->pgoff;
        pgoff_t pgoff_vma_start;
        unsigned long address;
-       pgoff_t pgoff_end;
 
        /* Common case, plus ->pgoff is invalid for KSM */
        if (pvmw->nr_pages == 1)
                return pvmw->address + PAGE_SIZE;
 
-       if (pvmw->is_anon_walk)
-               pgoff_vma_start = vma_start_virt_pgoff(vma);
+       if (pvmw->pgoff_is_anon)
+               pgoff_vma_start = vma_start_anon_pgoff(vma);
        else
                pgoff_vma_start = vma_start_pgoff(vma);
 
-       pgoff_end = pgoff + pvmw->nr_pages;
        address = vma->vm_start +
                ((pgoff_end - pgoff_vma_start) << PAGE_SHIFT);
        /* Check for address beyond vma (or wrapped through 0?) */
@@ -1393,7 +1390,7 @@ static inline bool gup_must_unshare(stru
                 * ... because we only care about writable private ("COW")
                 * mappings where we have to break COW early.
                 */
-               return is_cow_mapping(vma->vm_flags);
+               return vma_is_cow_mapping(vma);
        }
 
        /* Paired with a memory barrier in folio_try_share_anon_rmap_*(). */
--- a/mm/interval_tree.c~b
+++ a/mm/interval_tree.c
@@ -83,12 +83,12 @@ mapping_rmap_tree_iter_next(struct vm_ar
 
 static pgoff_t avc_start_pgoff(struct anon_vma_chain *avc)
 {
-       return vma_start_virt_pgoff(avc->vma);
+       return vma_start_anon_pgoff(avc->vma);
 }
 
 static pgoff_t avc_last_pgoff(struct anon_vma_chain *avc)
 {
-       return vma_last_virt_pgoff(avc->vma);
+       return vma_last_anon_pgoff(avc->vma);
 }
 
 INTERVAL_TREE_DEFINE(struct anon_vma_chain, rb, pgoff_t, rb_subtree_last,
--- a/mm/ksm.c~b
+++ a/mm/ksm.c
@@ -1625,8 +1625,7 @@ static int try_to_merge_with_ksm_page(st
         * stable_tree, break_cow() will clean it up.
         */
        rmap_item->anon_vma = vma->anon_vma;
-       /* The VMA is always anon/MAP_PRIVATE-file backed so use anon index. */
-       rmap_item->linear_page_index = linear_virt_page_index(vma, 
rmap_item->address);
+       rmap_item->linear_page_index = linear_anon_page_index(vma, 
rmap_item->address);
        get_anon_vma(vma->anon_vma);
 out:
        mmap_read_unlock(mm);
@@ -3153,7 +3152,7 @@ struct folio *ksm_might_need_to_copy(str
                        return folio;   /* no need to copy it */
        } else if (!anon_vma) {
                return folio;           /* no need to copy it */
-       } else if (folio->index == linear_virt_page_index(vma, addr) &&
+       } else if (folio->index == linear_anon_page_index(vma, addr) &&
                        anon_vma->root == vma->anon_vma->root) {
                return folio;           /* still no need to copy it */
        }
@@ -3223,7 +3222,7 @@ again:
                /*
                 * Currently, KSM folios are always small folios, so it's
                 * sufficient to search for a single page. We can simply use
-                * the linear_virt_page_index of the original de-duplicate
+                * the linear_anon_page_index of the original de-duplicate
                 * anonymous page that we remembered in the rmap_item while
                 * de-duplicating. Note that mremap() always de-duplicates KSM
                 * folios: so if there was mremap() in our parent or our child,
--- a/mm/memory.c~b
+++ a/mm/memory.c
@@ -631,14 +631,14 @@ static void print_bad_page_map(struct vm
 {
        struct address_space *mapping;
        char entry_str[PTVAL_STR_MAX];
-       pgoff_t index, virt_index;
+       pgoff_t index, anon_index;
 
        if (is_bad_page_map_ratelimited())
                return;
 
        mapping = vma->vm_file ? vma->vm_file->f_mapping : NULL;
        index = linear_page_index(vma, addr);
-       virt_index = __linear_virt_page_index(vma, addr);
+       anon_index = __linear_anon_page_index(vma, addr);
 
        ptval_bytes_to_hex_str(entry_str, sizeof(entry_str), entry, entry_size);
        pr_alert("BUG: Bad page map in process %s  %s:%s", current->comm,
@@ -646,9 +646,14 @@ static void print_bad_page_map(struct vm
        __print_bad_page_map_pgtable(vma->vm_mm, addr);
        if (page)
                dump_page(page, "bad page map");
-       pr_alert("addr:%px vm_flags:%08lx anon_vma:%px mapping:%px index:%lx 
virt_index:%lx\n",
-                (void *)addr, vma->vm_flags, vma->anon_vma, mapping, index,
-                virt_index);
+       pr_alert("addr:%px vm_flags:%08lx anon_vma:%px mapping:%px",
+                (void *)addr, vma->vm_flags, vma->anon_vma, mapping);
+       if (!vma_is_cow_mapping(vma) || index == anon_index) {
+               pr_cont(" index:%lx\n", index);
+       } else {
+               pr_cont(" index:%lx (file) %lx (anon)\n", index, anon_index);
+       }
+
        pr_alert("file:%pD fault:%ps mmap:%ps mmap_prepare: %ps 
read_folio:%ps\n",
                 vma->vm_file,
                 vma->vm_ops ? vma->vm_ops->fault : NULL,
@@ -782,7 +787,7 @@ static inline struct page *__vm_normal_p
                                /* Only CoW'ed anon folios are "normal". */
                                if (pfn == index)
                                        return NULL;
-                               if (!is_cow_mapping(vma->vm_flags))
+                               if (!vma_is_cow_mapping(vma))
                                        return NULL;
                        }
                }
@@ -1004,7 +1009,6 @@ copy_nonpresent_pte(struct mm_struct *ds
                pte_t *dst_pte, pte_t *src_pte, struct vm_area_struct *dst_vma,
                struct vm_area_struct *src_vma, unsigned long addr, int *rss)
 {
-       vm_flags_t vm_flags = dst_vma->vm_flags;
        pte_t orig_pte = ptep_get(src_pte);
        softleaf_t entry = softleaf_from_pte(orig_pte);
        pte_t pte = orig_pte;
@@ -1028,7 +1032,7 @@ copy_nonpresent_pte(struct mm_struct *ds
                rss[mm_counter(folio)]++;
 
                if (!softleaf_is_migration_read(entry) &&
-                               is_cow_mapping(vm_flags)) {
+                               vma_is_cow_mapping(dst_vma)) {
                        /*
                         * COW mappings require pages in both parent and child
                         * to be set to read. A previously exclusive entry is
@@ -1069,7 +1073,7 @@ copy_nonpresent_pte(struct mm_struct *ds
                 * save and restore device driver state).
                 */
                if (softleaf_is_device_private_write(entry) &&
-                   is_cow_mapping(vm_flags)) {
+                   vma_is_cow_mapping(dst_vma)) {
                        entry = make_readable_device_private_entry(
                                                        swp_offset(entry));
                        pte = swp_entry_to_pte(entry);
@@ -1084,7 +1088,7 @@ copy_nonpresent_pte(struct mm_struct *ds
                 * exclusive entries currently only support private writable
                 * (ie. COW) mappings.
                 */
-               VM_BUG_ON(!is_cow_mapping(src_vma->vm_flags));
+               VM_BUG_ON(!vma_is_cow_mapping(src_vma));
                if (try_restore_exclusive_pte(src_vma, addr, src_pte, orig_pte))
                        return -EBUSY;
                return -ENOENT;
@@ -1183,7 +1187,7 @@ static __always_inline void __copy_prese
        }
 
        /* If it's a COW mapping, write protect it both processes. */
-       if (is_cow_mapping(src_vma->vm_flags) && writable) {
+       if (vma_is_cow_mapping(src_vma) && writable) {
                wrprotect_ptes(src_mm, addr, src_pte, nr);
                pte = pte_wrprotect(pte);
        }
@@ -1604,9 +1608,9 @@ copy_page_range(struct vm_area_struct *d
         * We need to invalidate the secondary MMU mappings only when
         * there could be a permission downgrade on the ptes of the
         * parent mm. And a permission downgrade will only happen if
-        * is_cow_mapping() returns true.
+        * vma_is_cow_mapping() returns true.
         */
-       is_cow = is_cow_mapping(src_vma->vm_flags);
+       is_cow = vma_is_cow_mapping(src_vma);
 
        if (is_cow) {
                mmu_notifier_range_init(&range, MMU_NOTIFY_PROTECTION_PAGE,
@@ -2439,7 +2443,7 @@ static bool vm_mixed_zeropage_allowed(st
        if (mm_forbids_zeropage(vma->vm_mm))
                return false;
        /* zeropages in COW mappings are common and unproblematic. */
-       if (is_cow_mapping(vma->vm_flags))
+       if (vma_is_cow_mapping(vma))
                return true;
        /* Mappings that do not allow for writable PTEs are unproblematic. */
        if (!(vma->vm_flags & (VM_WRITE | VM_MAYWRITE)))
@@ -2890,7 +2894,7 @@ vm_fault_t vmf_insert_pfn_prot(struct vm
        BUG_ON(!(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)));
        BUG_ON((vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP)) ==
                                                (VM_PFNMAP|VM_MIXEDMAP));
-       BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags));
+       BUG_ON((vma->vm_flags & VM_PFNMAP) && vma_is_cow_mapping(vma));
        BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn));
 
        if (addr < vma->vm_start || addr >= vma->vm_end)
@@ -3302,7 +3306,7 @@ static int remap_pfn_range_prepare_vma(s
                                       unsigned long size)
 {
        const unsigned long end = addr + PAGE_ALIGN(size);
-       const bool is_cow = is_cow_mapping(vma->vm_flags);
+       const bool is_cow = vma_is_cow_mapping(vma);
        int err;
 
        err = get_remap_pgoff(is_cow, addr, end, vma->vm_start, vma->vm_end,
@@ -6802,7 +6806,7 @@ static vm_fault_t sanitize_fault_flags(s
                 * FAULT_FLAG_UNSHARE only applies to COW mappings. Let's
                 * just treat it like an ordinary read-fault otherwise.
                 */
-               if (!is_cow_mapping(vma->vm_flags))
+               if (!vma_is_cow_mapping(vma))
                        *flags &= ~FAULT_FLAG_UNSHARE;
        } else if (*flags & FAULT_FLAG_WRITE) {
                /* Write faults on read-only mappings are impossible ... */
@@ -6810,7 +6814,7 @@ static vm_fault_t sanitize_fault_flags(s
                        return VM_FAULT_SIGSEGV;
                /* ... and FOLL_FORCE only applies to COW mappings. */
                if (WARN_ON_ONCE(!(vma->vm_flags & VM_WRITE) &&
-                                !is_cow_mapping(vma->vm_flags)))
+                                !vma_is_cow_mapping(vma)))
                        return VM_FAULT_SIGSEGV;
        }
 #ifdef CONFIG_PER_VMA_LOCK
--- a/mm/mempolicy.c~b
+++ a/mm/mempolicy.c
@@ -844,7 +844,7 @@ bool folio_can_map_prot_numa(struct foli
                return false;
 
        /* Also skip shared copy-on-write folios */
-       if (is_cow_mapping(vma->vm_flags) && folio_maybe_mapped_shared(folio))
+       if (vma_is_cow_mapping(vma) && folio_maybe_mapped_shared(folio))
                return false;
 
        /* Folios are pinned and can't be migrated */
--- a/mm/migrate.c~b
+++ a/mm/migrate.c
@@ -356,25 +356,18 @@ static bool remove_migration_pte(struct
 
        while (page_vma_mapped_walk(&pvmw)) {
                rmap_t rmap_flags = RMAP_NONE;
-               pte_t old_pte;
-               pte_t pte;
+               unsigned long idx = 0;
                softleaf_t entry;
                struct page *new;
-               unsigned long idx = 0;
-
-               /* pgoff is invalid for ksm pages, but they are never large */
-               if (folio_test_large(folio) && !folio_test_hugetlb(folio)) {
-                       idx += linear_folio_page_index(folio, vma, 
pvmw.address);
-                       idx -= pvmw.pgoff;
-               }
-               new = folio_page(folio, idx);
+               pte_t old_pte;
+               pte_t pte;
 
 #ifdef CONFIG_ARCH_HAS_PMD_SOFTLEAVES
                /* PMD-mapped THP migration entry */
                if (!pvmw.pte) {
                        VM_BUG_ON_FOLIO(folio_test_hugetlb(folio) ||
                                        !folio_test_pmd_mappable(folio), folio);
-                       remove_migration_pmd(&pvmw, new);
+                       remove_migration_pmd(&pvmw, folio);
                        continue;
                }
 #endif
@@ -383,14 +376,18 @@ static bool remove_migration_pte(struct
                                                pvmw.pte);
                else
                        old_pte = ptep_get(pvmw.pte);
+
+               entry = softleaf_from_pte(old_pte);
+               if (folio_test_large(folio) && !folio_test_hugetlb(folio))
+                       idx = softleaf_to_pfn(entry) - pvmw.pfn;
+
                if (rmap_walk_arg->map_unused_to_zeropage &&
                    try_to_map_unused_to_zeropage(&pvmw, folio, old_pte, idx))
                        continue;
 
                folio_get(folio);
+               new = folio_page(folio, idx);
                pte = mk_pte(new, READ_ONCE(vma->vm_page_prot));
-
-               entry = softleaf_from_pte(old_pte);
                if (!softleaf_is_migration_young(entry))
                        pte = pte_mkold(pte);
                if (folio_test_dirty(folio) && 
softleaf_is_migration_dirty(entry))
--- a/mm/mremap.c~b
+++ a/mm/mremap.c
@@ -1266,8 +1266,8 @@ static int copy_vma_and_data(struct vma_
                             struct vm_area_struct **new_vma_ptr)
 {
        const pgoff_t new_pgoff = linear_page_index(vrm->vma, vrm->addr);
-       const pgoff_t new_virt_pgoff =
-               __linear_virt_page_index(vrm->vma, vrm->addr);
+       const pgoff_t new_anon_pgoff =
+               __linear_anon_page_index(vrm->vma, vrm->addr);
        struct vm_area_struct *vma = vrm->vma;
        struct vm_area_struct *new_vma;
        unsigned long moved_len;
@@ -1275,7 +1275,7 @@ static int copy_vma_and_data(struct vma_
        PAGETABLE_MOVE(pmc, NULL, NULL, vrm->addr, vrm->new_addr, vrm->old_len);
 
        new_vma = copy_vma(&vma, vrm->new_addr, vrm->new_len, new_pgoff,
-                          new_virt_pgoff, &pmc.need_rmap_locks);
+                          new_anon_pgoff, &pmc.need_rmap_locks);
        if (!new_vma) {
                vrm_uncharge(vrm);
                *new_vma_ptr = NULL;
--- a/mm/rmap.c~b
+++ a/mm/rmap.c
@@ -1240,7 +1240,7 @@ static bool mapping_wrprotect_range_one(
                .vma            = vma,
                .address        = address,
                .flags          = PVMW_SYNC,
-               .is_anon_walk   = false,
+               .pgoff_is_anon  = false,
        };
 
        state->cleaned += page_vma_mkclean_one(&pvmw);
@@ -1318,7 +1318,7 @@ int pfn_mkclean_range(unsigned long pfn,
                .pgoff          = pgoff,
                .vma            = vma,
                .flags          = PVMW_SYNC,
-               .is_anon_walk   = false,
+               .pgoff_is_anon  = false,
        };
 
        if (invalid_mkclean_vma(vma, NULL))
@@ -1485,7 +1485,7 @@ static void __folio_set_anon(struct foli
         */
        anon_vma = (void *) anon_vma + FOLIO_MAPPING_ANON;
        WRITE_ONCE(folio->mapping, (struct address_space *) anon_vma);
-       folio->index = linear_virt_page_index(vma, address);
+       folio->index = linear_anon_page_index(vma, address);
 }
 
 /**
@@ -1513,7 +1513,7 @@ static void __page_check_anon_rmap(const
        VM_BUG_ON_FOLIO(folio_anon_vma(folio)->root != vma->anon_vma->root,
                        folio);
        VM_BUG_ON_PAGE(page_pgoff(folio, page) !=
-                      linear_virt_page_index(vma, address), page);
+                      linear_anon_page_index(vma, address), page);
 }
 
 static __always_inline void __folio_add_anon_rmap(struct folio *folio,
--- a/mm/userfaultfd.c~b
+++ a/mm/userfaultfd.c
@@ -1352,8 +1352,7 @@ static long move_present_ptes(struct mm_
                }
 
                folio_move_anon_rmap(src_folio, dst_vma);
-               src_folio->index = linear_folio_page_index(src_folio, dst_vma,
-                                                          dst_addr);
+               src_folio->index = linear_anon_page_index(dst_vma, dst_addr);
 
                orig_dst_pte = folio_mk_pte(src_folio, dst_vma->vm_page_prot);
                /* Set soft dirty bit so userspace can notice the pte was moved 
*/
@@ -1429,8 +1428,7 @@ static int move_swap_pte(struct mm_struc
         */
        if (src_folio) {
                folio_move_anon_rmap(src_folio, dst_vma);
-               src_folio->index = linear_folio_page_index(src_folio, dst_vma,
-                                                          dst_addr);
+               src_folio->index = linear_anon_page_index(dst_vma, dst_addr);
        } else {
                /*
                 * Check if the swap entry is cached after acquiring the src_pte
--- a/mm/vma.c~b
+++ a/mm/vma.c
@@ -18,7 +18,7 @@ struct mmap_state {
        unsigned long addr;
        unsigned long end;
        pgoff_t pgoff;
-       pgoff_t virt_pgoff;
+       pgoff_t anon_pgoff;
        unsigned long pglen;
        union {
                vm_flags_t vm_flags;
@@ -47,22 +47,14 @@ struct mmap_state {
        bool file_doesnt_need_get :1;
 };
 
-static inline pgoff_t map_anon_pgoff(const struct mmap_state *map)
-{
-       if (vma_flags_test(&map->vma_flags, VMA_SHARED_BIT))
-               return map->pgoff;
-
-       return map->virt_pgoff;
-}
-
-#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, virt_pgoff_, 
vma_flags_, file_) \
+#define MMAP_STATE(name, mm_, vmi_, addr_, len_, pgoff_, anon_pgoff_, 
vma_flags_, file_) \
        struct mmap_state name = {                                      \
                .mm = mm_,                                              \
                .vmi = vmi_,                                            \
                .addr = addr_,                                          \
                .end = (addr_) + (len_),                                \
                .pgoff = pgoff_,                                        \
-               .virt_pgoff = virt_pgoff_,                              \
+               .anon_pgoff = anon_pgoff_,                              \
                .pglen = PHYS_PFN(len_),                                \
                .vma_flags = vma_flags_,                                \
                .file = file_,                                          \
@@ -77,7 +69,7 @@ static inline pgoff_t map_anon_pgoff(con
                .end = (map_)->end,                                     \
                .vma_flags = (map_)->vma_flags,                         \
                .pgoff = (map_)->pgoff,                                 \
-               .anon_pgoff = map_anon_pgoff(map_),                     \
+               .anon_pgoff = (map_)->anon_pgoff,                       \
                .file = (map_)->file,                                   \
                .prev = (map_)->prev,                                   \
                .middle = vma_,                                         \
@@ -93,11 +85,11 @@ static void __vma_set_range(struct vm_ar
 }
 
 static void vma_set_range(struct vm_area_struct *vma, unsigned long start,
-                         unsigned long end, pgoff_t pgoff, pgoff_t virt_pgoff)
+                         unsigned long end, pgoff_t pgoff, pgoff_t anon_pgoff)
 {
        __vma_set_range(vma, start, end);
        vma_set_pgoff(vma, pgoff);
-       vma_set_virt_pgoff(vma, virt_pgoff);
+       vma_set_anon_pgoff(vma, anon_pgoff);
 }
 
 /* Was this VMA ever forked from a parent, i.e. maybe contains CoW mappings? */
@@ -213,6 +205,25 @@ static void init_multi_vma_prep(struct v
 }
 
 /*
+ * Does this merge require that adjacent VMAs must have adjacent anonymous page
+ * offsets in addition to having adjacent vma->vm_pgoff?
+ *
+ * This is only required for MAP_PRIVATE-file backed mappings as the page 
offset
+ * for pure anonymous VMAs is equal to the anonymous page offset.
+ *
+ * Read-only shared mappings (with VMA_SHARED_BIT cleared) are always unfaulted
+ * so automatically have correct anonymous page offset (as it is always updated
+ * on remap).
+ *
+ * 'Special' mappings in the sense of VDSO, VVAR etc. have !file but would in
+ * any case not be candidates for merge nor be mergeable.
+ */
+static bool needs_adjacent_anon_pgoff(const struct vma_merge_struct *vmg)
+{
+       return vmg->file && vma_flags_is_cow_mapping(&vmg->vma_flags);
+}
+
+/*
  * Return true if we can merge this (vma_flags,anon_vma,file,vm_pgoff)
  * in front of (at a lower virtual address and file offset than) the vma.
  *
@@ -233,7 +244,8 @@ static bool can_vma_merge_before(struct
                return false;
        if (vmg_end_pgoff(vmg) != vma_start_pgoff(vmg->next))
                return false;
-       if (vmg_end_anon_pgoff(vmg) != vma_start_anon_pgoff(vmg->next))
+       if (needs_adjacent_anon_pgoff(vmg) &&
+           vmg_end_anon_pgoff(vmg) != vma_start_anon_pgoff(vmg->next))
                return false;
        return true;
 }
@@ -255,7 +267,8 @@ static bool can_vma_merge_after(struct v
                return false;
        if (vma_end_pgoff(vmg->prev) != vmg_start_pgoff(vmg))
                return false;
-       if (vma_end_anon_pgoff(vmg->prev) != vmg_start_anon_pgoff(vmg))
+       if (needs_adjacent_anon_pgoff(vmg) &&
+           vma_end_anon_pgoff(vmg->prev) != vmg_start_anon_pgoff(vmg))
                return false;
        return true;
 }
@@ -1930,27 +1943,27 @@ static int vma_link(struct mm_struct *mm
  */
 struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
        unsigned long addr, unsigned long len, pgoff_t pgoff,
-       pgoff_t virt_pgoff, bool *need_rmap_locks)
+       pgoff_t anon_pgoff, bool *need_rmap_locks)
 {
        struct vm_area_struct *vma = *vmap;
-       const bool is_shared = vma_test(vma, VMA_SHARED_BIT);
-       unsigned long vma_start = vma->vm_start;
+       unsigned long old_vma_start = vma->vm_start;
        struct mm_struct *mm = vma->vm_mm;
        struct vm_area_struct *new_vma;
-       bool faulted_in_anon_vma = true;
+       bool can_self_merge = false;
        VMA_ITERATOR(vmi, mm, addr);
        VMG_VMA_STATE(vmg, &vmi, NULL, vma, addr, addr + len);
 
        /*
-        * If a vma has not yet been faulted, update its virtual pgoff to match
-        * the new location to increase its chance of merging.
+        * If a vma has not yet been faulted, update its anonymous pgoff to
+        * match the new location to increase its chance of merging.
         */
-       if (!vma->anon_vma && !is_shared) {
-               virt_pgoff = addr >> PAGE_SHIFT;
+       if (!vma->anon_vma) {
+               anon_pgoff = addr >> PAGE_SHIFT;
 
-               if (vma_is_anonymous(vma))
-                       pgoff = virt_pgoff;
-               faulted_in_anon_vma = false;
+               if (vma_is_anonymous(vma)) {
+                       pgoff = anon_pgoff;
+                       can_self_merge = true;
+               }
        }
 
        /*
@@ -1966,39 +1979,35 @@ struct vm_area_struct *copy_vma(struct v
                return NULL;    /* should never get here */
 
        vmg.pgoff = pgoff;
-       vmg.anon_pgoff = is_shared ? pgoff : virt_pgoff;
+       vmg.anon_pgoff = anon_pgoff;
        vmg.next = vma_iter_next_rewind(&vmi, NULL);
        new_vma = vma_merge_copied_range(&vmg);
 
        if (new_vma) {
-               /*
-                * Source vma may have been merged into new_vma
-                */
-               if (unlikely(vma_start >= new_vma->vm_start &&
-                            vma_start < new_vma->vm_end)) {
+               /* Self-merged and VMA replaced. */
+               if (unlikely(new_vma->vm_start < old_vma_start &&
+                            new_vma->vm_end > old_vma_start)) {
                        /*
-                        * The only way we can get a vma_merge with
-                        * self during an mremap is if the vma hasn't
-                        * been faulted in yet and we were allowed to
-                        * reset the dst vma->vm_pgoff to the
-                        * destination address of the mremap to allow
-                        * the merge to happen. mremap must change the
-                        * vm_pgoff linearity between src and dst vmas
-                        * (in turn preventing a vma_merge) to be
-                        * safe. It is only safe to keep the vm_pgoff
-                        * linear if there are no pages mapped yet.
+                        * The only way a VMA can both self-merge and be
+                        * replaced is if the remap places the new VMA
+                        * immediately prior to its old self ('next') and
+                        * immediately after another VMA ('prev') causing the
+                        * next to be removed and prev to be expanded to cover
+                        * the entire range.
+                        *
+                        * This should only be possible if the anonymous page
+                        * offset was updated, i.e. the VMA is unfaulted.
                         */
-                       VM_WARN_ON_ONCE_VMA(faulted_in_anon_vma, new_vma);
+                       VM_WARN_ON_ONCE_VMA(!can_self_merge, new_vma);
                        *vmap = vma = new_vma;
                }
                *need_rmap_locks =
-                       (vma_start_pgoff(new_vma) <= vma_start_pgoff(vma)) ||
-                       (vma_start_anon_pgoff(new_vma) <= 
vma_start_anon_pgoff(vma));
+                       (vma_start_pgoff(new_vma) <= vma_start_pgoff(vma));
        } else {
                new_vma = vm_area_dup(vma);
                if (!new_vma)
                        goto out;
-               vma_set_range(new_vma, addr, addr + len, pgoff, virt_pgoff);
+               vma_set_range(new_vma, addr, addr + len, pgoff, anon_pgoff);
                if (vma_dup_policy(vma, new_vma))
                        goto out_free_vma;
                if (anon_vma_clone(new_vma, vma, VMA_OP_REMAP))
@@ -2066,7 +2075,7 @@ static int anon_vma_compatible(struct vm
        /* Page offset must align. */
        if (vma_end_pgoff(a) != vma_start_pgoff(b))
                return false;
-       /* Anon page offset must align. */
+       /* Only reached from anon path, so either MAP_PRIVATE file or anon. */
        if (vma_end_anon_pgoff(a) != vma_start_anon_pgoff(b))
                return false;
        return true;
@@ -2615,8 +2624,11 @@ static int __mmap_new_file_vma(struct mm
 static bool map_is_dev_zero(const struct mmap_state *map)
 {
        const struct file *file = map->file;
-       const struct inode *inode = file_inode(file);
+       struct inode *inode;
 
+       if (!file)
+               return false;
+       inode = file_inode(file);
        if (!S_ISCHR(inode->i_mode))
                return false;
        return imajor(inode) == MEM_MAJOR && iminor(inode) == DEVZERO_MINOR;
@@ -2671,7 +2683,7 @@ static int __mmap_new_vma(struct mmap_st
        if (is_anon)
                vma_set_anonymous(vma);
 
-       vma_set_range(vma, map->addr, map->end, map->pgoff, map->virt_pgoff);
+       vma_set_range(vma, map->addr, map->end, map->pgoff, map->anon_pgoff);
        vma->flags = map->vma_flags;
        vma->vm_page_prot = map->page_prot;
 
@@ -2869,8 +2881,8 @@ static unsigned long __mmap_region(struc
        struct vm_area_struct *vma = NULL;
        bool have_mmap_prepare = file && file->f_op->mmap_prepare;
        VMA_ITERATOR(vmi, mm, addr);
-       const pgoff_t virt_pgoff = addr >> PAGE_SHIFT;
-       MMAP_STATE(map, mm, &vmi, addr, len, pgoff, virt_pgoff, vma_flags, 
file);
+       const pgoff_t anon_pgoff = addr >> PAGE_SHIFT;
+       MMAP_STATE(map, mm, &vmi, addr, len, pgoff, anon_pgoff, vma_flags, 
file);
        struct vm_area_desc desc = {
                .mm = mm,
                .file = file,
@@ -3452,7 +3464,7 @@ int insert_vm_struct(struct mm_struct *m
                WARN_ON_ONCE(vma->anon_vma);
                vma_set_pgoff(vma, vma->vm_start >> PAGE_SHIFT);
        }
-       vma_set_virt_pgoff(vma, vma->vm_start >> PAGE_SHIFT);
+       vma_set_anon_pgoff(vma, vma->vm_start >> PAGE_SHIFT);
 
        if (vma_link(mm, vma)) {
                if (vma_test(vma, VMA_ACCOUNT_BIT))
--- a/mm/vma.h~b
+++ a/mm/vma.h
@@ -291,86 +291,32 @@ static inline pgoff_t vmg_end_anon_pgoff
        return vmg_start_anon_pgoff(vmg) + vmg_pages(vmg);
 }
 
-static inline void __vma_set_virt_pgoff(struct vm_area_struct *vma, pgoff_t 
pgoff)
+static inline void __vma_set_anon_pgoff(struct vm_area_struct *vma, pgoff_t 
pgoff)
 {
 #ifdef CONFIG_64BIT
-       vma->__vm_virt_pgoff_hi = pgoff >> 32;
+       vma->__vm_anon_pgoff_hi = pgoff >> 32;
 #endif
-       vma->__vm_virt_pgoff_lo = pgoff & GENMASK(31, 0);
+       vma->__vm_anon_pgoff_lo = pgoff & GENMASK(31, 0);
 }
 
-static inline void vma_set_virt_pgoff(struct vm_area_struct *vma, pgoff_t 
pgoff)
+static inline void vma_set_anon_pgoff(struct vm_area_struct *vma, pgoff_t 
pgoff)
 {
        vma_assert_can_modify(vma);
-       __vma_set_virt_pgoff(vma, pgoff);
+       __vma_set_anon_pgoff(vma, pgoff);
 }
 
 static inline void vma_add_pgoff(struct vm_area_struct *vma, pgoff_t delta)
 {
        vma_assert_can_modify(vma);
        vma_set_pgoff(vma, vma_start_pgoff(vma) + delta);
-       vma_set_virt_pgoff(vma, vma_start_virt_pgoff(vma) + delta);
+       vma_set_anon_pgoff(vma, vma_start_anon_pgoff(vma) + delta);
 }
 
 static inline void vma_sub_pgoff(struct vm_area_struct *vma, pgoff_t delta)
 {
        vma_assert_can_modify(vma);
        vma_set_pgoff(vma, vma_start_pgoff(vma) - delta);
-       vma_set_virt_pgoff(vma, vma_start_virt_pgoff(vma) - delta);
-}
-
-/**
- * vma_anon_pgoff_addr() - Calculates the absolute anonymous page offset of
- * @address.
- * @vma: The VMA whose anonymous page offset is required.
- * @address: The address whose absolute page offset is required.
- *
- * If the VMA is a shared file-backed mapping, then the file-based page offset
- * is returned.
- *
- * Otherwise, the virtual page offset is returned.
- *
- * This means that shared file-backed mappings are correctly merged based on
- * their file page offset compatibility.
- *
- * Returns: The absolute anonymous page offset of @address within @vma.
- */
-static inline pgoff_t vma_anon_pgoff_addr(const struct vm_area_struct *vma,
-                                         unsigned long address)
-{
-       if (vma_test(vma, VMA_SHARED_BIT))
-               return linear_page_index(vma, address);
-
-       return linear_virt_page_index(vma, address);
-}
-
-/**
- * vma_start_anon_pgoff() - Calculates the absolute anonymous page offset used
- * for purposes of merge compatibility.
- * @vma: The VMA whose anonymous page offset is required.
- *
- * See vma_anon_pgoff_addr().
- *
- * Returns: The absolute anonymous page offset of @vma for purposes of merging.
- */
-static inline pgoff_t vma_start_anon_pgoff(const struct vm_area_struct *vma)
-{
-       return vma_anon_pgoff_addr(vma, vma->vm_start);
-}
-
-/**
- * vma_end_anon_pgoff() - Calculates the absolute exclusive end anonymous page
- * offset used for purposes of merge compatibility.
- * @vma: The VMA whose anonymous end page offset is required.
- *
- * See vma_start_anon_pgoff().
- *
- * Returns: The absolute exclusive end anonymous page offset of @vma for
- * purposes of merging.
- */
-static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma)
-{
-       return vma_start_anon_pgoff(vma) + vma_pages(vma);
+       vma_set_anon_pgoff(vma, vma_start_anon_pgoff(vma) - delta);
 }
 
 #define VMG_STATE(name, mm_, vmi_, start_, end_, vma_flags_, pgoff_, 
anon_pgoff_) \
@@ -396,7 +342,7 @@ static inline pgoff_t vma_end_anon_pgoff
                .end = end_,                                            \
                .vm_flags = vma_->vm_flags,                             \
                .pgoff = linear_page_index(vma_, start_),               \
-               .anon_pgoff = vma_anon_pgoff_addr(vma_, start_),        \
+               .anon_pgoff = __linear_anon_page_index(vma_, start_),   \
                .file = vma_->vm_file,                                  \
                .anon_vma = vma_->anon_vma,                             \
                .policy = vma_policy(vma_),                             \
--- a/mm/vma_init.c~b
+++ a/mm/vma_init.c
@@ -51,7 +51,7 @@ static void vm_area_init_from(const stru
        dest->vm_end = src->vm_end;
        dest->anon_vma = src->anon_vma;
        dest->vm_pgoff = vma_start_pgoff(src);
-       __vma_set_virt_pgoff(dest, vma_start_virt_pgoff(src));
+       __vma_set_anon_pgoff(dest, vma_start_anon_pgoff(src));
        dest->vm_file = src->vm_file;
        dest->vm_private_data = src->vm_private_data;
        vm_flags_init(dest, src->vm_flags);
--- a/tools/testing/selftests/mm/merge.c~b
+++ a/tools/testing/selftests/mm/merge.c
@@ -1305,7 +1305,7 @@ TEST_F(merge, merge_vmas_with_mseal)
        ASSERT_EQ(procmap->query.vma_end, (unsigned long)ptr + 2 * page_size);
 }
 
-TEST_F(merge, virt_and_page_offset_mismatch_memfd)
+TEST_F(merge, anon_and_page_offset_mismatch_memfd)
 {
        struct procmap_fd *procmap = &self->procmap;
        unsigned int page_size = self->page_size;
@@ -1314,7 +1314,7 @@ TEST_F(merge, virt_and_page_offset_misma
        int fd;
 
        /* Create a 10 page memfd descriptor. */
-       fd = memfd_create("virt_page_offset_test", MFD_CLOEXEC);
+       fd = memfd_create("anon_page_offset_test", MFD_CLOEXEC);
        ASSERT_NE(fd, -1);
        ASSERT_EQ(ftruncate(fd, 10 * page_size), 0);
 
@@ -1346,10 +1346,10 @@ TEST_F(merge, virt_and_page_offset_misma
         * | unfaulted |           | faulted |
         * |-----------|           |---------|
         *
-        * Because virtual page offset of the faulted region is now
+        * Because the anonymous page offset of the faulted region is now
         * &carveout[10 * page_size], despite the two regions being mergeable
-        * due to file page offset, they are NOT mergeable due to virtual page
-        * offset.
+        * due to file page offset, they are NOT mergeable due to anonymous
+        * page offset.
         */
        ptr2 = sys_mremap(ptr2, 5 * page_size, 5 * page_size,
                          MREMAP_MAYMOVE | MREMAP_FIXED,
--- a/tools/testing/selftests/proc/proc-self-map-files-001.c~b
+++ a/tools/testing/selftests/proc/proc-self-map-files-001.c
@@ -51,7 +51,7 @@ int main(void)
        int fd;
        unsigned long a, b;
 
-       fd = open("/dev/zero", O_RDONLY);
+       fd = open("/proc/self/exe", O_RDONLY);
        if (fd == -1)
                return 1;
 
--- a/tools/testing/selftests/proc/proc-self-map-files-002.c~b
+++ a/tools/testing/selftests/proc/proc-self-map-files-002.c
@@ -57,7 +57,7 @@ int main(void)
        int fd;
        unsigned long a, b;
 
-       fd = open("/dev/zero", O_RDONLY);
+       fd = open("/proc/self/exe", O_RDONLY);
        if (fd == -1)
                return 1;
 
--- a/tools/testing/vma/include/dup.h~b
+++ a/tools/testing/vma/include/dup.h
@@ -599,7 +599,7 @@ struct vm_area_struct {
         */
        unsigned int vm_lock_seq;
 #endif
-       unsigned int __vm_virt_pgoff_lo;
+       unsigned int __vm_anon_pgoff_lo;
 
        /*
         * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma
@@ -637,7 +637,7 @@ struct vm_area_struct {
        refcount_t vm_refcnt;
 #endif
 #ifdef CONFIG_64BIT
-       unsigned int __vm_virt_pgoff_hi;
+       unsigned int __vm_anon_pgoff_hi;
 #endif
        /*
         * For areas with an address space and backing store,
@@ -1184,6 +1184,17 @@ static inline bool vma_is_shared_maywrit
        return is_shared_maywrite(&vma->flags);
 }
 
+static inline bool vma_flags_is_cow_mapping(const vma_flags_t *flags)
+{
+       return vma_flags_test(flags, VMA_MAYWRITE_BIT) &&
+               !vma_flags_test(flags, VMA_SHARED_BIT);
+}
+
+static inline bool vma_is_cow_mapping(const struct vm_area_struct *vma)
+{
+       return vma_flags_is_cow_mapping(&vma->flags);
+}
+
 static inline struct vm_area_struct *vma_next(struct vma_iterator *vmi)
 {
        /*
@@ -1346,26 +1357,26 @@ static inline pgoff_t vma_end_pgoff(cons
        return vma_start_pgoff(vma) + vma_pages(vma);
 }
 
-static inline pgoff_t vma_start_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_start_anon_pgoff(const struct vm_area_struct *vma)
 {
        pgoff_t pgoff = 0;
 
 #ifdef CONFIG_64BIT
-       pgoff += vma->__vm_virt_pgoff_hi;
+       pgoff += vma->__vm_anon_pgoff_hi;
        pgoff <<= 32;
 #endif
-       pgoff += vma->__vm_virt_pgoff_lo;
+       pgoff += vma->__vm_anon_pgoff_lo;
        return pgoff;
 }
 
-static inline pgoff_t vma_end_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma)
 {
-       return vma_start_virt_pgoff(vma) + vma_pages(vma);
+       return vma_start_anon_pgoff(vma) + vma_pages(vma);
 }
 
-static inline pgoff_t vma_last_virt_pgoff(const struct vm_area_struct *vma)
+static inline pgoff_t vma_last_anon_pgoff(const struct vm_area_struct *vma)
 {
-       return vma_end_virt_pgoff(vma) - 1;
+       return vma_end_anon_pgoff(vma) - 1;
 }
 
 static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc 
*desc)
@@ -1633,22 +1644,22 @@ static inline pgprot_t vma_get_page_prot
        return vma_flags_to_page_prot(vma->flags);
 }
 
-static inline pgoff_t __linear_virt_page_index(const struct vm_area_struct 
*vma,
+static inline pgoff_t __linear_anon_page_index(const struct vm_area_struct 
*vma,
                                               const unsigned long address)
 {
        pgoff_t pgoff;
 
        pgoff = linear_page_delta(vma, address);
-       pgoff += vma_start_virt_pgoff(vma);
+       pgoff += vma_start_anon_pgoff(vma);
        return pgoff;
 }
 
-static inline pgoff_t linear_virt_page_index(const struct vm_area_struct *vma,
-                                            const unsigned long address)
+static inline pgoff_t linear_anon_page_index(const struct vm_area_struct *vma,
+               const unsigned long address)
 {
-       const pgoff_t pgoff = __linear_virt_page_index(vma, address);
+       const pgoff_t pgoff = __linear_anon_page_index(vma, address);
 
-       VM_WARN_ON_ONCE(vma_test(vma, VMA_SHARED_BIT));
+       VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma));
        if (vma_is_anonymous(vma))
                VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address));
 
--- a/tools/testing/vma/shared.c~b
+++ a/tools/testing/vma/shared.c
@@ -24,7 +24,7 @@ struct vm_area_struct *alloc_vma(struct
        vma->vm_start = start;
        vma->vm_end = end;
        vma_set_pgoff(vma, pgoff);
-       vma_set_virt_pgoff(vma, start >> PAGE_SHIFT);
+       vma_set_anon_pgoff(vma, start >> PAGE_SHIFT);
        vma->flags = vma_flags;
        vma_assert_detached(vma);
 
--- a/tools/testing/vma/tests/merge.c~b
+++ a/tools/testing/vma/tests/merge.c
@@ -121,7 +121,7 @@ static bool test_simple_merge(void)
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x3000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_FLAGS_SAME_MASK(&vma->flags, vma_flags);
 
        detach_free_vma(vma);
@@ -154,7 +154,7 @@ static bool test_simple_modify(void)
        ASSERT_EQ(vma->vm_start, 0x1000);
        ASSERT_EQ(vma->vm_end, 0x2000);
        ASSERT_EQ(vma_start_pgoff(vma), 1);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 1);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 1);
 
        /*
         * Now walk through the three split VMAs and make sure they are as
@@ -167,7 +167,7 @@ static bool test_simple_modify(void)
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x1000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
 
        detach_free_vma(vma);
        vma_iter_clear(&vmi);
@@ -177,7 +177,7 @@ static bool test_simple_modify(void)
        ASSERT_EQ(vma->vm_start, 0x1000);
        ASSERT_EQ(vma->vm_end, 0x2000);
        ASSERT_EQ(vma_start_pgoff(vma), 1);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 1);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 1);
 
        detach_free_vma(vma);
        vma_iter_clear(&vmi);
@@ -187,7 +187,7 @@ static bool test_simple_modify(void)
        ASSERT_EQ(vma->vm_start, 0x2000);
        ASSERT_EQ(vma->vm_end, 0x3000);
        ASSERT_EQ(vma_start_pgoff(vma), 2);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 2);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 2);
 
        detach_free_vma(vma);
        mtree_destroy(&mm.mm_mt);
@@ -217,7 +217,7 @@ static bool test_simple_expand(void)
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x3000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
 
        detach_free_vma(vma);
        mtree_destroy(&mm.mm_mt);
@@ -240,7 +240,7 @@ static bool test_simple_shrink(void)
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x1000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
 
        detach_free_vma(vma);
        mtree_destroy(&mm.mm_mt);
@@ -353,7 +353,7 @@ static bool __test_merge_new(bool is_sti
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x5000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 3);
@@ -375,7 +375,7 @@ static bool __test_merge_new(bool is_sti
        ASSERT_EQ(vma->vm_start, 0x6000);
        ASSERT_EQ(vma->vm_end, 0x9000);
        ASSERT_EQ(vma_start_pgoff(vma), 6);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 6);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 6);
        ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 3);
@@ -396,7 +396,7 @@ static bool __test_merge_new(bool is_sti
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x9000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 2);
@@ -417,7 +417,7 @@ static bool __test_merge_new(bool is_sti
        ASSERT_EQ(vma->vm_start, 0xa000);
        ASSERT_EQ(vma->vm_end, 0xc000);
        ASSERT_EQ(vma_start_pgoff(vma), 0xa);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0xa);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0xa);
        ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 2);
@@ -437,7 +437,7 @@ static bool __test_merge_new(bool is_sti
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0xc000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 1);
@@ -458,7 +458,7 @@ static bool __test_merge_new(bool is_sti
                ASSERT_EQ(vma->vm_start, 0);
                ASSERT_EQ(vma->vm_end, 0xc000);
                ASSERT_EQ(vma_start_pgoff(vma), 0);
-               ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+               ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
                ASSERT_EQ(vma->anon_vma, &dummy_anon_vma);
 
                detach_free_vma(vma);
@@ -655,7 +655,8 @@ static bool test_vma_merge_with_close(vo
        ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x5000);
-       ASSERT_EQ(vma_prev->vm_pgoff, 0);
+       ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
 
        ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
 
@@ -766,7 +767,8 @@ static bool test_vma_merge_with_close(vo
        ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x5000);
-       ASSERT_EQ(vma_prev->vm_pgoff, 0);
+       ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
 
        ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
 
@@ -821,7 +823,7 @@ static bool test_vma_merge_new_with_clos
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x5000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_EQ(vma->vm_ops, &vm_ops);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 2);
@@ -878,12 +880,12 @@ static bool __test_merge_existing(bool p
        ASSERT_EQ(vma_next->vm_start, 0x3000);
        ASSERT_EQ(vma_next->vm_end, 0x9000);
        ASSERT_EQ(vma_start_pgoff(vma_next), 3);
-       ASSERT_EQ(vma_start_virt_pgoff(vma_next), 3);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_next), 3);
        ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
        ASSERT_EQ(vma->vm_start, 0x2000);
        ASSERT_EQ(vma->vm_end, 0x3000);
        ASSERT_EQ(vma_start_pgoff(vma), 2);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 2);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 2);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_TRUE(vma_write_started(vma_next));
        ASSERT_EQ(mm.map_count, 2);
@@ -913,7 +915,8 @@ static bool __test_merge_existing(bool p
        ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
        ASSERT_EQ(vma_next->vm_start, 0x2000);
        ASSERT_EQ(vma_next->vm_end, 0x9000);
-       ASSERT_EQ(vma_next->vm_pgoff, 2);
+       ASSERT_EQ(vma_start_pgoff(vma_next), 2);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_next), 2);
        ASSERT_EQ(vma_next->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma_next));
        ASSERT_EQ(mm.map_count, 1);
@@ -946,12 +949,12 @@ static bool __test_merge_existing(bool p
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x6000);
        ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
        ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
        ASSERT_EQ(vma->vm_start, 0x6000);
        ASSERT_EQ(vma->vm_end, 0x7000);
        ASSERT_EQ(vma_start_pgoff(vma), 6);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 6);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 6);
        ASSERT_TRUE(vma_write_started(vma_prev));
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 2);
@@ -983,7 +986,7 @@ static bool __test_merge_existing(bool p
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x7000);
        ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
        ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma_prev));
        ASSERT_EQ(mm.map_count, 1);
@@ -1016,7 +1019,7 @@ static bool __test_merge_existing(bool p
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x9000);
        ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
        ASSERT_EQ(vma_prev->anon_vma, &dummy_anon_vma);
        ASSERT_TRUE(vma_write_started(vma_prev));
        ASSERT_EQ(mm.map_count, 1);
@@ -1146,7 +1149,8 @@ static bool test_anon_vma_non_mergeable(
        ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x7000);
-       ASSERT_EQ(vma_prev->vm_pgoff, 0);
+       ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
        ASSERT_TRUE(vma_write_started(vma_prev));
        ASSERT_FALSE(vma_write_started(vma_next));
 
@@ -1177,7 +1181,8 @@ static bool test_anon_vma_non_mergeable(
        ASSERT_EQ(vmg.state, VMA_MERGE_SUCCESS);
        ASSERT_EQ(vma_prev->vm_start, 0);
        ASSERT_EQ(vma_prev->vm_end, 0x7000);
-       ASSERT_EQ(vma_prev->vm_pgoff, 0);
+       ASSERT_EQ(vma_start_pgoff(vma_prev), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma_prev), 0);
        ASSERT_TRUE(vma_write_started(vma_prev));
        ASSERT_FALSE(vma_write_started(vma_next));
 
@@ -1439,7 +1444,7 @@ static bool test_merge_extend(void)
        ASSERT_EQ(vma->vm_start, 0);
        ASSERT_EQ(vma->vm_end, 0x4000);
        ASSERT_EQ(vma_start_pgoff(vma), 0);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(mm.map_count, 1);
 
@@ -1480,7 +1485,7 @@ static bool test_expand_only_mode(void)
        ASSERT_EQ(vma->vm_start, 0x3000);
        ASSERT_EQ(vma->vm_end, 0x9000);
        ASSERT_EQ(vma_start_pgoff(vma), 3);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 3);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 3);
        ASSERT_TRUE(vma_write_started(vma));
        ASSERT_EQ(vma_iter_addr(&vmi), 0x3000);
        vma_assert_attached(vma);
--- a/tools/testing/vma/tests/mmap.c~b
+++ a/tools/testing/vma/tests/mmap.c
@@ -74,7 +74,7 @@ static bool test_pure_anon_dev_zero(void
 
        /*
         * Map a MAP_PRIVATE-/dev/zero mapping at address 0x300000 with a page
-        * offset of 0x10, which we expect to be reset to the virtual page
+        * offset of 0x10, which we expect to be reset to the anonymous page
         * offset.
         */
        addr = __mmap_region(&file, 0x300000, 0x3000, vma_flags, 0x10, NULL);
@@ -86,9 +86,9 @@ static bool test_pure_anon_dev_zero(void
        ASSERT_TRUE(vma_is_anonymous(vma));
        ASSERT_EQ(vma->vm_file, NULL);
        ASSERT_EQ(vma->vm_private_data, NULL);
-       /* Expect virtual page offsets. */
+       /* Expect anonymous page offsets. */
        ASSERT_EQ(vma->vm_pgoff, 0x300);
-       ASSERT_EQ(vma_start_virt_pgoff(vma), 0x300);
+       ASSERT_EQ(vma_start_anon_pgoff(vma), 0x300);
 
        cleanup_mm(&mm, &vmi);
        return true;
--- a/tools/testing/vma/tests/vma.c~b
+++ a/tools/testing/vma/tests/vma.c
@@ -33,7 +33,51 @@ static bool test_copy_vma(void)
        struct mm_struct mm = {};
        bool need_locks = false;
        VMA_ITERATOR(vmi, &mm, 0);
-       struct vm_area_struct *vma, *vma_new, *vma_next;
+       struct vm_area_struct *vma, *vma_prev, *vma_new, *vma_next, *vma_orig;
+
+       /* Move forwards, adjacent to old self - self-merge. */
+
+       vma = alloc_and_link_vma(&mm, 0x1000, 0x2000, 1, vma_flags);
+       vma_set_anonymous(vma);
+       vma_orig = vma;
+       vma_new = copy_vma(&vma, 0x2000, 0x1000, 1, 1, &need_locks);
+       ASSERT_EQ(vma_new, vma_orig);
+       ASSERT_EQ(vma, vma_orig);
+       ASSERT_EQ(vma_new->vm_start, 0x1000);
+       ASSERT_EQ(vma_new->vm_end, 0x3000);
+
+       cleanup_mm(&mm, &vmi);
+
+       /* Move backwards, adjacent to old self - self-merge. */
+
+       vma = alloc_and_link_vma(&mm, 0x2000, 0x3000, 2, vma_flags);
+       vma_set_anonymous(vma);
+       vma_orig = vma;
+       vma_new = copy_vma(&vma, 0x1000, 0x1000, 2, 2, &need_locks);
+       ASSERT_EQ(vma_new, vma_orig);
+       ASSERT_EQ(vma, vma_orig);
+       ASSERT_EQ(vma_new->vm_start, 0x1000);
+       ASSERT_EQ(vma_new->vm_end, 0x3000);
+
+       cleanup_mm(&mm, &vmi);
+
+       /*
+        * Move backwards between prior VMA and old self - self-merge and vma
+        * updated to a new VMA.
+        */
+
+       vma_prev = alloc_and_link_vma(&mm, 0x1000, 0x2000, 1, vma_flags);
+       vma_set_anonymous(vma_prev);
+       vma = alloc_and_link_vma(&mm, 0x3000, 0x4000, 3, vma_flags);
+       vma_set_anonymous(vma);
+       vma_orig = vma;
+       vma_new = copy_vma(&vma, 0x2000, 0x1000, 3, 3, &need_locks);
+       ASSERT_NE(vma_new, vma_orig);
+       ASSERT_EQ(vma_new, vma);
+       ASSERT_EQ(vma_new->vm_start, 0x1000);
+       ASSERT_EQ(vma_new->vm_end, 0x4000);
+
+       cleanup_mm(&mm, &vmi);
 
        /* Move backwards and do not merge. */
 
_




Reply via email to