Re: [RFC PATCH 1/5] mm: let folio_mkclean() report which pages had dirty PTEs
From: Kiryl Shutsemau
Date: Thu Sep 10 2026 - 09:53:40 EST
On Wed, Sep 09, 2026 at 03:12:10AM -0700, Usama Arif wrote:
> The old whole-folio behavior did not need to know which PTE became
> dirty, so this timing window was harmless. Sub-folio tracking makes the
> final dirty state correctness-critical.
Good catch, thanks. I should have known better.
Fixup below.
diff --git a/mm/rmap.c b/mm/rmap.c
index aaf45ac79fa8..9b8b9428f802 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -1140,6 +1140,15 @@ static int page_vma_mkclean_one(struct page_vma_mapped_walk *pvmw,
if (!pte_dirty(entry) && !pte_write(entry))
continue;
+ flush_cache_page(vma, address, pte_pfn(entry));
+ entry = ptep_clear_flush(vma, address, pte);
+
+ /*
+ * Take the dirty bit from what the clear returned, not
+ * from the value read above. The entry is writable, so
+ * the CPU can set the bit at any point before the
+ * clear, and the page table lock does not stop it.
+ */
if (dirty_map && pte_dirty(entry)) {
pgoff_t idx = linear_page_index(vma, address) -
pvmw->pgoff;
@@ -1149,8 +1158,6 @@ static int page_vma_mkclean_one(struct page_vma_mapped_walk *pvmw,
__set_bit(idx, dirty_map);
}
- flush_cache_page(vma, address, pte_pfn(entry));
- entry = ptep_clear_flush(vma, address, pte);
entry = pte_wrprotect(entry);
entry = pte_mkclean(entry);
set_pte_at(vma->vm_mm, address, pte, entry);
@@ -1170,12 +1177,14 @@ static int page_vma_mkclean_one(struct page_vma_mapped_walk *pvmw,
if (!pmd_dirty(entry) && !pmd_write(entry))
continue;
- if (dirty_map && pmd_dirty(entry))
- bitmap_set(dirty_map, 0, pvmw->nr_pages);
-
flush_cache_range(vma, address,
address + HPAGE_PMD_SIZE);
entry = pmdp_invalidate(vma, address, pmd);
+
+ /* See the PTE case above */
+ if (dirty_map && pmd_dirty(entry))
+ bitmap_set(dirty_map, 0, pvmw->nr_pages);
+
entry = pmd_wrprotect(entry);
entry = pmd_mkclean(entry);
set_pmd_at(vma->vm_mm, address, pmd, entry);
--
Kiryl Shutsemau / Kirill A. Shutemov