[PATCH v3 07/15] mm/rmap: track whether the page VMA mapped pgoff is anonymous

From: Lorenzo Stoakes (ARM)

Date: Wed Jul 29 2026 - 13:23:11 EST


Update the page_vma_mapped_walk structure to track whether the pgoff being
tracked is an anonymous pgoff or not and update the comments to reflect
this.

This is necessary in order to determine the correct VMA page
offset in vma_address_end() when pvmw->nr_pages > 1.

Also document that pvmw->pgoff is meaningless for pvmw->nr_pages == 1 and
for KSM.

Do not set this field where pgoff is not specified.

This is laying the groundwork for eventually using anonymous page offsets
as the index for all anonymous folios.

No functional change intended.

Signed-off-by: Lorenzo Stoakes (ARM) <ljs@xxxxxxxxxx>
---
include/linux/rmap.h | 4 +++-
mm/internal.h | 13 ++++++++-----
mm/rmap.c | 2 ++
3 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/include/linux/rmap.h b/include/linux/rmap.h
index 8dc0871e5f00..0574537a355c 100644
--- a/include/linux/rmap.h
+++ b/include/linux/rmap.h
@@ -864,13 +864,14 @@ struct page *make_device_exclusive(struct mm_struct *mm, unsigned long addr,
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 pgoff_is_anon : 1;
};

#define DEFINE_FOLIO_VMA_WALK(name, _folio, _vma, _address, _flags) \
@@ -881,6 +882,7 @@ struct page_vma_mapped_walk {
.vma = _vma, \
.address = _address, \
.flags = _flags, \
+ .pgoff_is_anon = folio_test_anon(_folio), \
}

static inline void page_vma_mapped_walk_done(struct page_vma_mapped_walk *pvmw)
diff --git a/mm/internal.h b/mm/internal.h
index 920872904bda..fbfa68d8ecc3 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -1079,22 +1079,25 @@ static inline unsigned long vma_anon_address(const struct vm_area_struct *vma,
}

/*
- * Then at what user virtual address will none of the range be found in vma?
+ * At what user virtual address will none of the range be found in vma?
* Assumes that vma_address() already returned a good starting address.
*/
static inline unsigned long vma_address_end(struct page_vma_mapped_walk *pvmw)
{
- struct vm_area_struct *vma = pvmw->vma;
- pgoff_t pgoff;
+ 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;

- pgoff = pvmw->pgoff + pvmw->nr_pages;
+ pgoff_vma_start = vma_start_pgoff(vma);
+ pgoff_end = pgoff + pvmw->nr_pages;
address = vma->vm_start +
- ((pgoff - vma_start_pgoff(vma)) << PAGE_SHIFT);
+ ((pgoff_end - pgoff_vma_start) << PAGE_SHIFT);
/* Check for address beyond vma (or wrapped through 0?) */
if (address < vma->vm_start || address > vma->vm_end)
address = vma->vm_end;
diff --git a/mm/rmap.c b/mm/rmap.c
index 5798427d007f..ab3f879454de 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1240,6 +1240,7 @@ static bool mapping_wrprotect_range_one(struct folio *folio,
.vma = vma,
.address = address,
.flags = PVMW_SYNC,
+ .pgoff_is_anon = false,
};

state->cleaned += page_vma_mkclean_one(&pvmw);
@@ -1317,6 +1318,7 @@ int pfn_mkclean_range(unsigned long pfn, unsigned long nr_pages, pgoff_t pgoff,
.pgoff = pgoff,
.vma = vma,
.flags = PVMW_SYNC,
+ .pgoff_is_anon = false,
};

if (invalid_mkclean_vma(vma, NULL))

--
2.55.0