On Thu, Aug 6, 2026 at 1:23 PM Lorenzo Stoakes (ARM) <[email protected]> wrote: > > This function provides the anonymous equivalent of linear_page_index(), > instead offsetting based on the anonymous page offset of the VMA. > > It is valid only for anonymous or MAP_PRIVATE file-backed mappings, in > other words CoW mappings. > > For pure anon VMAs, this will be equal to linear_page_index(). > > Assert that both of these invariants are true In linear_anon_page_index()
s/In/in > and implement the algorithm in __linear_anon_page_index(). > > Note that MAP_PRIVATE-/dev/zero mappings will satisfy vma_is_anonymous() > but not fulfill this invariant, so when asserting this we check > vma->vm_file to account for this. > > We do not update callsites yet, so no functional change intended. > > Also const-ify vma_is_anonymous() to make it compatible with the > const-ified linear_anon_page_index(). > > While we're here, update linear_page_index() to be more succinct. > > VMA userland tests are also updated accordingly. > > Reviewed-by: Gregory Price (Meta) <[email protected]> > Signed-off-by: Lorenzo Stoakes (ARM) <[email protected]> > --- > include/linux/mm.h | 2 +- > include/linux/pagemap.h | 40 +++++++++++++++++++++++++++++++++++++--- > tools/testing/vma/include/dup.h | 25 ++++++++++++++++++++++++- > 3 files changed, 62 insertions(+), 5 deletions(-) > > diff --git a/include/linux/mm.h b/include/linux/mm.h > index a3368c542947..da36a6cc907c 100644 > --- a/include/linux/mm.h > +++ b/include/linux/mm.h > @@ -1556,7 +1556,7 @@ static inline void vma_desc_set_anonymous(struct > vm_area_desc *desc) > desc->vm_ops = NULL; > } > > -static inline bool vma_is_anonymous(struct vm_area_struct *vma) > +static inline bool vma_is_anonymous(const struct vm_area_struct *vma) > { > return !vma->vm_ops; > } > diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h > index c6fc783aaee5..0adfa6605653 100644 > --- a/include/linux/pagemap.h > +++ b/include/linux/pagemap.h > @@ -1094,10 +1094,44 @@ static inline pgoff_t linear_page_delta(const struct > vm_area_struct *vma, > static inline pgoff_t linear_page_index(const struct vm_area_struct *vma, > const unsigned long address) > { > - pgoff_t pgoff; > + return linear_page_delta(vma, address) + vma_start_pgoff(vma); > +} > + > +static inline pgoff_t __linear_anon_page_index(const struct vm_area_struct > *vma, > + const unsigned long address) > +{ > + return linear_page_delta(vma, address) + vma_start_anon_pgoff(vma); > +} > + > +/** > + * 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 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 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 anonymous page offset of @address within @vma. > + */ > +static inline pgoff_t linear_anon_page_index(const struct vm_area_struct > *vma, > + const unsigned long address) > +{ > + const pgoff_t pgoff = __linear_anon_page_index(vma, address); > + > + VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma)); As discussed off the list, this assertion works even for read-only anon mappings because, as you said, "there's no such thing as a pure anon mapping that is !VMA_MAYWRITE_BIT, there is no way to achieve that." It took me some time to see that after reading your discussion with David at [1] and [2], and I don't think it's very intuitive. Could you please add a comment explaining that? Maybe add a comment at vma_is_cow_mapping() definition since you are using the same assertion in another patch of this series? [1] https://lore.kernel.org/linux-mm/anBjpdMicqaGADr-@lucifer/ [2] https://lore.kernel.org/linux-mm/anLyhaI6kW-EWk13@lucifer/ > + /* Account for MAP_PRIVATE-/dev/zero which is only semi-anonymous. */ > + if (vma_is_anonymous(vma) && !vma->vm_file) > + VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address)); > > - pgoff = linear_page_delta(vma, address); > - pgoff += vma_start_pgoff(vma); > return pgoff; > } > > diff --git a/tools/testing/vma/include/dup.h b/tools/testing/vma/include/dup.h > index 40ad83936b28..4c58487b764e 100644 > --- a/tools/testing/vma/include/dup.h > +++ b/tools/testing/vma/include/dup.h > @@ -1428,7 +1428,7 @@ static inline void vma_iter_set(struct vma_iterator > *vmi, unsigned long addr) > mas_set(&vmi->mas, addr); > } > > -static inline bool vma_is_anonymous(struct vm_area_struct *vma) > +static inline bool vma_is_anonymous(const struct vm_area_struct *vma) > { > return !vma->vm_ops; > } > @@ -1621,3 +1621,26 @@ static inline pgprot_t vma_get_page_prot(const struct > vm_area_struct *vma) > { > return vma_flags_to_page_prot(vma->flags); > } > + > +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_anon_pgoff(vma); > + return pgoff; > +} > + > +static inline pgoff_t linear_anon_page_index(const struct vm_area_struct > *vma, > + const unsigned long address) > +{ > + const pgoff_t pgoff = __linear_anon_page_index(vma, address); > + > + VM_WARN_ON_ONCE(!vma_is_cow_mapping(vma)); > + /* Account for MAP_PRIVATE-/dev/zero which is only semi-anonymous. */ > + if (vma_is_anonymous(vma) && !vma->vm_file) > + VM_WARN_ON_ONCE(pgoff != linear_page_index(vma, address)); > + > + return pgoff; > +} > > -- > 2.55.0 >
