mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 09:20:13 -04:00
mm/pagemap: add PAGE_IS_ACCESSED for RWP tracking
PAGEMAP_SCAN already reports PAGE_IS_WRITTEN from the inverted uffd PTE bit, targeting the UFFDIO_WRITEPROTECT workflow. UFFDIO_RWPROTECT reuses the same PTE bit as a marker for read-write protection, but "has been written" and "has been accessed" are distinct semantic signals — they happen to share one PTE bit today only because the two implementations share infrastructure. Give RWP its own pagemap category so the UAPI does not conflate them: PAGE_IS_WRITTEN reported on VM_UFFD_WP VMAs, !pte_uffd(pte) PAGE_IS_ACCESSED reported on VM_UFFD_RWP VMAs, !pte_uffd(pte) Both still read the same PTE bit today, but each is scoped to the VMA whose registered mode makes the bit meaningful. If a future implementation moves RWP to a separate PTE bit, only PAGE_IS_ACCESSED switches over. This is a UAPI narrowing. Outside VM_UFFD_WP VMAs the uffd bit is always clear, so PAGEMAP_SCAN used to flag PAGE_IS_WRITTEN on every present PTE there — a meaningless duplicate of PAGE_IS_PRESENT. Now PAGE_IS_WRITTEN fires only inside VM_UFFD_WP VMAs. pagemap_hugetlb_category() now takes the vma like its PTE/PMD peers. Link: https://lore.kernel.org/20260708111417.173443-12-kirill@shutemov.name Signed-off-by: Kiryl Shutsemau <kas@kernel.org> Assisted-by: Claude:claude-opus-4-6 Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: David Hildenbrand <david@kernel.org> Cc: James Houghton <jthoughton@google.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Liam Howlett <liam@infradead.org> Cc: Lorenzo Stoakes <ljs@kernel.org> Cc: Paolo Bonzini <pbonzini@redhat.com> Cc: Peter Xu <peterx@redhat.com> Cc: Sean Christopherson <seanjc@google.com> Cc: SeongJae Park <sj@kernel.org> Cc: Suren Baghdasaryan <surenb@google.com> Cc: Vlastimil Babka <vbabka@kernel.org> Cc: Zi Yan <ziy@nvidia.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
committed by
Andrew Morton
parent
2d427bb016
commit
3389648819
@@ -2284,7 +2284,7 @@ static const struct mm_walk_ops pagemap_ops = {
|
||||
* Bits 5-54 swap offset if swapped
|
||||
* Bit 55 pte is soft-dirty (see Documentation/admin-guide/mm/soft-dirty.rst)
|
||||
* Bit 56 page exclusively mapped
|
||||
* Bit 57 pte is uffd-wp write-protected
|
||||
* Bit 57 pte is tracked by userfaultfd (uffd-wp or RWP)
|
||||
* Bit 58 pte is a guard region
|
||||
* Bits 59-60 zero
|
||||
* Bit 61 page is file-page or shared-anon
|
||||
@@ -2419,7 +2419,7 @@ static int pagemap_release(struct inode *inode, struct file *file)
|
||||
PAGE_IS_FILE | PAGE_IS_PRESENT | \
|
||||
PAGE_IS_SWAPPED | PAGE_IS_PFNZERO | \
|
||||
PAGE_IS_HUGE | PAGE_IS_SOFT_DIRTY | \
|
||||
PAGE_IS_GUARD)
|
||||
PAGE_IS_GUARD | PAGE_IS_ACCESSED)
|
||||
#define PM_SCAN_FLAGS (PM_SCAN_WP_MATCHING | PM_SCAN_CHECK_WPASYNC)
|
||||
|
||||
struct pagemap_scan_private {
|
||||
@@ -2438,15 +2438,17 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p,
|
||||
|
||||
if (pte_none(pte)) {
|
||||
/*
|
||||
* An unpopulated pte carries no uffd-wp marker, i.e. it is not
|
||||
* write-protected, the same condition under which the present
|
||||
* and swap cases below report PAGE_IS_WRITTEN. Report it here
|
||||
* too so this generic path agrees with the PAGE_IS_WRITTEN fast
|
||||
* path in pagemap_scan_pmd_entry(), which reports pte_none as
|
||||
* written and, under PM_SCAN_WP_MATCHING, arms a marker. The
|
||||
* fast path applies no VMA test, so neither does this.
|
||||
* An unpopulated pte carries no uffd bit, i.e. it is not
|
||||
* write-protected. The PAGE_IS_WRITTEN fast path in
|
||||
* pagemap_scan_pmd_entry() is now gated on a VM_UFFD_WP VMA;
|
||||
* gate the pte_none report here the same way so the two paths
|
||||
* still agree. RWP has no such fast path and an unpopulated
|
||||
* page is not part of the RWP working set, so it is reported as
|
||||
* neither.
|
||||
*/
|
||||
return PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_wp(vma))
|
||||
return PAGE_IS_WRITTEN;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pte_present(pte)) {
|
||||
@@ -2454,8 +2456,12 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p,
|
||||
|
||||
categories = PAGE_IS_PRESENT;
|
||||
|
||||
if (!pte_uffd(pte))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!pte_uffd(pte)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
|
||||
if (p->masks_of_interest & PAGE_IS_FILE) {
|
||||
page = vm_normal_page(vma, addr, pte);
|
||||
@@ -2472,8 +2478,12 @@ static unsigned long pagemap_page_category(struct pagemap_scan_private *p,
|
||||
|
||||
categories = PAGE_IS_SWAPPED;
|
||||
|
||||
if (!pte_swp_uffd_any(pte))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!pte_swp_uffd_any(pte)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
|
||||
entry = softleaf_from_pte(pte);
|
||||
if (softleaf_is_guard_marker(entry))
|
||||
@@ -2522,8 +2532,12 @@ static unsigned long pagemap_thp_category(struct pagemap_scan_private *p,
|
||||
struct page *page;
|
||||
|
||||
categories |= PAGE_IS_PRESENT;
|
||||
if (!pmd_uffd(pmd))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!pmd_uffd(pmd)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
|
||||
if (p->masks_of_interest & PAGE_IS_FILE) {
|
||||
page = vm_normal_page_pmd(vma, addr, pmd);
|
||||
@@ -2537,8 +2551,12 @@ static unsigned long pagemap_thp_category(struct pagemap_scan_private *p,
|
||||
categories |= PAGE_IS_SOFT_DIRTY;
|
||||
} else {
|
||||
categories |= PAGE_IS_SWAPPED;
|
||||
if (!pmd_swp_uffd(pmd))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!pmd_swp_uffd(pmd)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
if (pmd_swp_soft_dirty(pmd))
|
||||
categories |= PAGE_IS_SOFT_DIRTY;
|
||||
|
||||
@@ -2571,7 +2589,8 @@ static void make_uffd_wp_pmd(struct vm_area_struct *vma,
|
||||
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||
|
||||
#ifdef CONFIG_HUGETLB_PAGE
|
||||
static unsigned long pagemap_hugetlb_category(pte_t pte)
|
||||
static unsigned long pagemap_hugetlb_category(struct vm_area_struct *vma,
|
||||
pte_t pte)
|
||||
{
|
||||
unsigned long categories = PAGE_IS_HUGE;
|
||||
|
||||
@@ -2586,8 +2605,12 @@ static unsigned long pagemap_hugetlb_category(pte_t pte)
|
||||
if (pte_present(pte)) {
|
||||
categories |= PAGE_IS_PRESENT;
|
||||
|
||||
if (!huge_pte_uffd(pte))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!huge_pte_uffd(pte)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
if (!PageAnon(pte_page(pte)))
|
||||
categories |= PAGE_IS_FILE;
|
||||
if (is_zero_pfn(pte_pfn(pte)))
|
||||
@@ -2597,8 +2620,12 @@ static unsigned long pagemap_hugetlb_category(pte_t pte)
|
||||
} else {
|
||||
categories |= PAGE_IS_SWAPPED;
|
||||
|
||||
if (!pte_swp_uffd_any(pte))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (!pte_swp_uffd_any(pte)) {
|
||||
if (userfaultfd_wp(vma))
|
||||
categories |= PAGE_IS_WRITTEN;
|
||||
if (userfaultfd_rwp(vma))
|
||||
categories |= PAGE_IS_ACCESSED;
|
||||
}
|
||||
if (pte_swp_soft_dirty(pte))
|
||||
categories |= PAGE_IS_SOFT_DIRTY;
|
||||
}
|
||||
@@ -2874,7 +2901,8 @@ static int pagemap_scan_pmd_entry(pmd_t *pmd, unsigned long start,
|
||||
goto flush_and_return;
|
||||
}
|
||||
|
||||
if (!p->arg.category_anyof_mask && !p->arg.category_inverted &&
|
||||
if (userfaultfd_wp(vma) && !p->arg.category_anyof_mask &&
|
||||
!p->arg.category_inverted &&
|
||||
p->arg.category_mask == PAGE_IS_WRITTEN &&
|
||||
p->arg.return_mask == PAGE_IS_WRITTEN) {
|
||||
for (addr = start; addr < end; pte++, addr += PAGE_SIZE) {
|
||||
@@ -2949,7 +2977,8 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask,
|
||||
/* Go the short route when not write-protecting pages. */
|
||||
|
||||
pte = huge_ptep_get(walk->mm, start, ptep);
|
||||
categories = p->cur_vma_category | pagemap_hugetlb_category(pte);
|
||||
categories = p->cur_vma_category |
|
||||
pagemap_hugetlb_category(vma, pte);
|
||||
|
||||
if (!pagemap_scan_is_interesting_page(categories, p))
|
||||
return 0;
|
||||
@@ -2961,7 +2990,7 @@ static int pagemap_scan_hugetlb_entry(pte_t *ptep, unsigned long hmask,
|
||||
ptl = huge_pte_lock(hstate_vma(vma), vma->vm_mm, ptep);
|
||||
|
||||
pte = huge_ptep_get(walk->mm, start, ptep);
|
||||
categories = p->cur_vma_category | pagemap_hugetlb_category(pte);
|
||||
categories = p->cur_vma_category | pagemap_hugetlb_category(vma, pte);
|
||||
|
||||
if (!pagemap_scan_is_interesting_page(categories, p))
|
||||
goto out_unlock;
|
||||
|
||||
Reference in New Issue
Block a user