Re: [PATCH v4 01/20] mm/vma: introduce VMA anon page offset field and add helpers
Suren Baghdasaryan <[email protected]> Sat, 8 Aug 2026 17:51:10 -0700
| Newsgroups | gmane.linux.kernel,gmane.linux.kernel.mm,gmane.linux.file-systems,gmane.comp.emulators.kvm.devel,gmane.comp.freedesktop.amd-gfx,gmane.comp.video.dri.devel,gmane.linux.kernel.perf.user |
|---|---|
| Message-ID | <CAJuCfpFAA1H2pOi6EOarNtrdzLQDJyhm6QOGsfZ4SFTuM5eiLQ@mail.gmail.com> |
On Thu, Aug 6, 2026 at 1:22 PM Lorenzo Stoakes (ARM) <[email protected]> wrote: > > Establish fields in vm_area_struct to store the anonymous page offset of > VMAs. > > Initially, the anonymous page offset of a VMA is vma->vm_start >> > PAGE_SHIFT. > > When a VMA is remapped to new_address its anonymous page offset is either > updated to new_address >> PAGE_SHIFT if unfaulted or, if faulted, remains > equal to the anonymous page offset it had when first faulted. > > Currently, anonymous folios belonging to CoW'd MAP_PRIVATE-mapped > file-backed VMAs are tracked by their file offsets. By adding anonymous > offset as a property of VMAs, we can now track them by their anonymous page > offset instead. > > By tracking this, we provide the means by which to eliminate this > inconsistency, and more importantly lay the foundations for future work for > the scalable CoW anonymous rmap rework. > > This patch simply adds the fields and some simple helpers. Subsequent > patches will update mm code to make use of these fields correctly. > > The fields chosen are packed in the VMA such that, for 64-bit kernel > builds, no additional space is taken up. > > The first field is present on cacheline 0 containing key VMA fields, and > the second on cacheline 3, which contains file-backed reverse mapping > fields. > > Given the relative time spent accessing reverse mapping fields as well as > updating them, there shouldn't be any performance impact here from false > sharing. > > Update the VMA userland tests to account for this change. > > No callsites are updated yet, so no functional change intended. > > Acked-by: David Hildenbrand (Arm) <[email protected]> > Reviewed-by: Gregory Price (Meta) <[email protected]> > Reviewed-by: Xu Xin <[email protected]> > Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]> > --- > include/linux/mm.h | 59 +++++++++++++++++++++++++++++++++++++++++ > include/linux/mm_types.h | 12 +++++++++ > mm/vma.h | 14 ++++++++++ > mm/vma_init.c | 1 + > tools/testing/vma/include/dup.h | 26 ++++++++++++++++++ > 5 files changed, 112 insertions(+) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index 87feaa5a2b78..df78847f5f07 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -4393,6 +4393,65 @@ static inline pgoff_t vma_last_pgoff(const struct vm_area_struct *vma) > return vma_end_pgoff(vma) - 1; > } > > +/** > + * 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 > + * 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 > + * anonymous page offset. I assume this function should not be used with shared file-backed mappings, right? If so, maybe add a comment like the one you have for linear_anon_page_index(): "It is not valid to call this function for shared file-backed mappings."? > + * > + * Returns: The anonymous page offset of the start of @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_anon_pgoff_hi; > + pgoff <<= 32; > +#endif > + pgoff += vma->__vm_anon_pgoff_lo; > + return pgoff; > +} > + > +/** > + * vma_end_anon_pgoff() - Get the anonymous page offset of the exclusive end of > + * @vma. > + * @vma: The VMA whose end anonymous page offset is required. > + * > + * This returns the anonymous exclusive end page offset of @vma, which is useful > + * for expressing page offset ranges. > + * > + * See the description of vma_start_anon_pgoff() for a description of VMA > + * anonymous page offsets. > + * > + * Returns: The exclusive end anonymous page offset of @vma. > + */ > +static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma) > +{ > + return vma_start_anon_pgoff(vma) + vma_pages(vma); > +} > + > +/** > + * vma_last_anon_pgoff() - Get the anonymous page offset of the last page in > + * @vma. > + * @vma: The VMA whose last anonymous page offset is required. > + * > + * See the description of vma_start_anon_pgoff() for a description of VMA > + * anonymous page offsets. > + * > + * Returns: The last anonymous page offset of @vma. > + */ > +static inline pgoff_t vma_last_anon_pgoff(const struct vm_area_struct *vma) > +{ > + return vma_end_anon_pgoff(vma) - 1; > +} > + > static inline unsigned long vma_desc_size(const struct vm_area_desc *desc) > { > return desc->end - desc->start; > diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h > index 939b5ea8c9e0..ebf0d912be7d 100644 > --- a/include/linux/mm_types.h > +++ b/include/linux/mm_types.h > @@ -967,6 +967,11 @@ struct vm_area_struct { > */ > unsigned int vm_lock_seq; > #endif > + /* > + * Low 32-bits of anonymous page offset. > + * See vma_start_anon_pgoff() comment for details. > + */ > + 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 > @@ -1041,6 +1046,13 @@ struct vm_area_struct { > #ifdef CONFIG_DEBUG_LOCK_ALLOC > struct lockdep_map vmlock_dep_map; > #endif > +#endif > +#ifdef CONFIG_64BIT > + /* > + * High 32-bits of anonymous page offset. > + * See vma_start_anon_pgoff() comment for details. > + */ > + unsigned int __vm_anon_pgoff_hi; > #endif > /* > * For areas with an address space and backing store, > diff --git a/mm/vma.h b/mm/vma.h > index 0bc7d521e976..54ed7c744e3b 100644 > --- a/mm/vma.h > +++ b/mm/vma.h > @@ -283,6 +283,20 @@ static inline void vma_set_pgoff(struct vm_area_struct *vma, pgoff_t pgoff) > vma->vm_pgoff = pgoff; > } > > +static inline void __vma_set_anon_pgoff(struct vm_area_struct *vma, pgoff_t pgoff) > +{ > +#ifdef CONFIG_64BIT > + vma->__vm_anon_pgoff_hi = pgoff >> 32; > +#endif > + vma->__vm_anon_pgoff_lo = pgoff & GENMASK(31, 0); > +} > + > +static inline void vma_set_anon_pgoff(struct vm_area_struct *vma, pgoff_t pgoff) > +{ > + vma_assert_can_modify(vma); > + __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); > diff --git a/mm/vma_init.c b/mm/vma_init.c > index 715feee283f0..baa7e82f47e3 100644 > --- a/mm/vma_init.c > +++ b/mm/vma_init.c > @@ -51,6 +51,7 @@ static void vm_area_init_from(const struct vm_area_struct *src, > dest->vm_end = src->vm_end; > dest->anon_vma = src->anon_vma; > dest->vm_pgoff = vma_start_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); > diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h > index c52b23773cd2..26d9210b1e8b 100644 > --- a/tools/testing/vma/include/dup.h > +++ b/tools/testing/vma/include/dup.h > @@ -577,6 +577,7 @@ struct vm_area_struct { > */ > unsigned int vm_lock_seq; > #endif > + unsigned int __vm_anon_pgoff_lo; > > /* > * A file's MAP_PRIVATE vma can be in both i_mmap tree and anon_vma > @@ -612,6 +613,9 @@ struct vm_area_struct { > #ifdef CONFIG_PER_VMA_LOCK > /* Unstable RCU readers are allowed to read this. */ > refcount_t vm_refcnt; > +#endif > +#ifdef CONFIG_64BIT > + unsigned int __vm_anon_pgoff_hi; > #endif > /* > * For areas with an address space and backing store, > @@ -1320,6 +1324,28 @@ static inline pgoff_t vma_end_pgoff(const struct vm_area_struct *vma) > return vma_start_pgoff(vma) + vma_pages(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_anon_pgoff_hi; > + pgoff <<= 32; > +#endif > + pgoff += vma->__vm_anon_pgoff_lo; > + return pgoff; > +} > + > +static inline pgoff_t vma_end_anon_pgoff(const struct vm_area_struct *vma) > +{ > + return vma_start_anon_pgoff(vma) + vma_pages(vma); > +} > + > +static inline pgoff_t vma_last_anon_pgoff(const struct vm_area_struct *vma) > +{ > + return vma_end_anon_pgoff(vma) - 1; > +} > + > static inline int vfs_mmap_prepare(struct file *file, struct vm_area_desc *desc) > { > return file->f_op->mmap_prepare(desc); > > -- > 2.55.0 >