Kernel KVM virtualization development
 help / color / mirror / Atom feed
From: Usama Arif <usama.arif@linux.dev>
To: Kiryl Shutsemau <kirill@shutemov.name>
Cc: Usama Arif <usama.arif@linux.dev>,
	akpm@linux-foundation.org, rppt@kernel.org, peterx@redhat.com,
	david@kernel.org, ljs@kernel.org, surenb@google.com,
	vbabka@kernel.org, Liam.Howlett@oracle.com, ziy@nvidia.com,
	corbet@lwn.net, skhan@linuxfoundation.org, seanjc@google.com,
	pbonzini@redhat.com, jthoughton@google.com, aarcange@redhat.com,
	sj@kernel.org, linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	linux-doc@vger.kernel.org, linux-kselftest@vger.kernel.org,
	kvm@vger.kernel.org, kernel-team@meta.com, kas@kernel.org
Subject: Re: [PATCH v9 11/15] mm/pagemap: add PAGE_IS_ACCESSED for RWP tracking
Date: Mon,  6 Jul 2026 05:57:24 -0700	[thread overview]
Message-ID: <20260706125726.729431-1-usama.arif@linux.dev> (raw)
In-Reply-To: <20260706084805.8400-12-kirill@shutemov.name>

On Mon,  6 Jul 2026 09:48:00 +0100 Kiryl Shutsemau <kirill@shutemov.name> wrote:

> From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
> 
> 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.
> 
> Signed-off-by: Kiryl Shutsemau <kas@kernel.org>
> Assisted-by: Claude:claude-opus-4-6
> Acked-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
>  Documentation/admin-guide/mm/pagemap.rst | 13 +++--
>  fs/proc/task_mmu.c                       | 63 +++++++++++++++++-------
>  include/uapi/linux/fs.h                  |  1 +
>  tools/include/uapi/linux/fs.h            |  1 +
>  4 files changed, 57 insertions(+), 21 deletions(-)
> 
> diff --git a/Documentation/admin-guide/mm/pagemap.rst b/Documentation/admin-guide/mm/pagemap.rst
> index c57e61b5d8aa..ffa690a171c8 100644
> --- a/Documentation/admin-guide/mm/pagemap.rst
> +++ b/Documentation/admin-guide/mm/pagemap.rst
> @@ -19,8 +19,11 @@ There are four components to pagemap:
>      * Bit  55    pte is soft-dirty (see
>        Documentation/admin-guide/mm/soft-dirty.rst)
>      * Bit  56    page exclusively mapped (since 4.2)
> -    * Bit  57    pte is uffd-wp write-protected (since 5.13) (see
> -      Documentation/admin-guide/mm/userfaultfd.rst)
> +    * Bit  57    pte is tracked by userfaultfd (since 5.13) — in a
> +      ``VM_UFFD_WP`` VMA this indicates a write-protected PTE; in a
> +      ``VM_UFFD_RWP`` VMA it indicates an RWP-protected PTE. WP and
> +      RWP are mutually exclusive per VMA, so the meaning is
> +      unambiguous. See Documentation/admin-guide/mm/userfaultfd.rst.
>      * Bit  58    pte is a guard region (since 6.15) (see madvise (2) man page)
>      * Bits 59-60 zero
>      * Bit  61    page is file-page or shared-anon (since 3.5)
> @@ -244,7 +247,8 @@ in this IOCTL:
>  Following flags about pages are currently supported:
>  
>  - ``PAGE_IS_WPALLOWED`` - Page has async-write-protection enabled
> -- ``PAGE_IS_WRITTEN`` - Page has been written to from the time it was write protected
> +- ``PAGE_IS_WRITTEN`` - Page in a ``UFFDIO_REGISTER_MODE_WP`` VMA has been
> +  written to since it was write-protected. Only reported inside such VMAs.
>  - ``PAGE_IS_FILE`` - Page is file backed
>  - ``PAGE_IS_PRESENT`` - Page is present in the memory
>  - ``PAGE_IS_SWAPPED`` - Page is in swapped
> @@ -252,6 +256,9 @@ Following flags about pages are currently supported:
>  - ``PAGE_IS_HUGE`` - Page is PMD-mapped THP or Hugetlb backed
>  - ``PAGE_IS_SOFT_DIRTY`` - Page is soft-dirty
>  - ``PAGE_IS_GUARD`` - Page is a part of a guard region
> +- ``PAGE_IS_ACCESSED`` - Page in a ``UFFDIO_REGISTER_MODE_RWP`` VMA has been
> +  accessed since RWP was applied. Only reported inside such VMAs. See
> +  Documentation/admin-guide/mm/userfaultfd.rst for the RWP workflow.
>  
>  The ``struct pm_scan_arg`` is used as the argument of the IOCTL.
>  
> diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
> index 5b55c59573a3..2d724156763d 100644
> --- a/fs/proc/task_mmu.c
> +++ b/fs/proc/task_mmu.c
> @@ -2283,7 +2283,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
> @@ -2418,7 +2418,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 {
> @@ -2443,8 +2443,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;
> +		}

The above is an ABI change for existing PAGEMAP_SCAN users although not
sure if there is someone that cares. Would below be a better alternative
to limit the ABI change?

if (!pte_uffd(pte)) {
	if (userfaultfd_rwp(vma))
		categories |= PAGE_IS_ACCESSED;
	else
		categories |= PAGE_IS_WRITTEN;
}


>  
>  		if (p->masks_of_interest & PAGE_IS_FILE) {
>  			page = vm_normal_page(vma, addr, pte);
> @@ -2461,8 +2465,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))
> @@ -2511,8 +2519,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);
> @@ -2526,8 +2538,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;
>  
> @@ -2560,7 +2576,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;
>  
> @@ -2575,8 +2592,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)))
> @@ -2586,8 +2607,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;
>  	}
> @@ -2863,7 +2888,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) {
> @@ -2938,7 +2964,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;
> @@ -2950,7 +2977,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;
> diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
> index bd87262f2e34..34c6f219462a 100644
> --- a/include/uapi/linux/fs.h
> +++ b/include/uapi/linux/fs.h
> @@ -471,6 +471,7 @@ typedef int __bitwise __kernel_rwf_t;
>  #define PAGE_IS_HUGE		(1 << 6)
>  #define PAGE_IS_SOFT_DIRTY	(1 << 7)
>  #define PAGE_IS_GUARD		(1 << 8)
> +#define PAGE_IS_ACCESSED	(1 << 9)
>  
>  /*
>   * struct page_region - Page region with flags
> diff --git a/tools/include/uapi/linux/fs.h b/tools/include/uapi/linux/fs.h
> index 24ddf7bc4f25..f0a26309b6d5 100644
> --- a/tools/include/uapi/linux/fs.h
> +++ b/tools/include/uapi/linux/fs.h
> @@ -364,6 +364,7 @@ typedef int __bitwise __kernel_rwf_t;
>  #define PAGE_IS_HUGE		(1 << 6)
>  #define PAGE_IS_SOFT_DIRTY	(1 << 7)
>  #define PAGE_IS_GUARD		(1 << 8)
> +#define PAGE_IS_ACCESSED	(1 << 9)
>  
>  /*
>   * struct page_region - Page region with flags
> -- 
> 2.54.0
> 
> 

  parent reply	other threads:[~2026-07-06 12:57 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-06  8:47 [PATCH v9 00/15] userfaultfd: working set tracking for VM guest memory Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 01/15] mm: decouple protnone helpers from CONFIG_NUMA_BALANCING Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 02/15] mm: rename uffd-wp PTE bit macros to uffd Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 03/15] mm: rename uffd-wp PTE accessors " Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 04/15] userfaultfd: test uffd VMA flags through the vma_flags_t API Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 05/15] mm: add VM_UFFD_RWP VMA flag Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 06/15] mm: add MM_CP_UFFD_RWP change_protection() flag Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 07/15] mm: preserve RWP marker across PTE rewrites Kiryl Shutsemau
2026-07-06  9:20   ` sashiko-bot
2026-07-06  8:47 ` [PATCH v9 08/15] mm: handle VM_UFFD_RWP in khugepaged, rmap, and GUP Kiryl Shutsemau
2026-07-06  8:47 ` [PATCH v9 09/15] userfaultfd: add UFFDIO_REGISTER_MODE_RWP and UFFDIO_RWPROTECT plumbing Kiryl Shutsemau
2026-07-06  9:06   ` sashiko-bot
2026-07-06  8:47 ` [PATCH v9 10/15] mm/userfaultfd: add RWP fault delivery and expose UFFDIO_REGISTER_MODE_RWP Kiryl Shutsemau
2026-07-06  9:22   ` sashiko-bot
2026-07-06  8:48 ` [PATCH v9 11/15] mm/pagemap: add PAGE_IS_ACCESSED for RWP tracking Kiryl Shutsemau
2026-07-06  9:15   ` sashiko-bot
2026-07-06 12:57   ` Usama Arif [this message]
2026-07-06 13:52     ` Kiryl Shutsemau
2026-07-06  8:48 ` [PATCH v9 12/15] userfaultfd: add UFFD_FEATURE_RWP_ASYNC for async fault resolution Kiryl Shutsemau
2026-07-06  8:48 ` [PATCH v9 13/15] userfaultfd: add UFFDIO_SET_MODE for runtime sync/async toggle Kiryl Shutsemau
2026-07-06  9:20   ` sashiko-bot
2026-07-06  8:48 ` [PATCH v9 14/15] selftests/mm: add userfaultfd RWP tests Kiryl Shutsemau
2026-07-06  9:19   ` sashiko-bot
2026-07-06 10:49   ` [PATCH v9.1 " Kiryl Shutsemau
2026-07-06  8:48 ` [PATCH v9 15/15] Documentation/userfaultfd: document RWP working set tracking Kiryl Shutsemau
2026-07-06 10:56 ` Addressing Sashiko AI review Kiryl Shutsemau

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260706125726.729431-1-usama.arif@linux.dev \
    --to=usama.arif@linux.dev \
    --cc=Liam.Howlett@oracle.com \
    --cc=aarcange@redhat.com \
    --cc=akpm@linux-foundation.org \
    --cc=corbet@lwn.net \
    --cc=david@kernel.org \
    --cc=jthoughton@google.com \
    --cc=kas@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=kirill@shutemov.name \
    --cc=kvm@vger.kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=ljs@kernel.org \
    --cc=pbonzini@redhat.com \
    --cc=peterx@redhat.com \
    --cc=rppt@kernel.org \
    --cc=seanjc@google.com \
    --cc=sj@kernel.org \
    --cc=skhan@linuxfoundation.org \
    --cc=surenb@google.com \
    --cc=vbabka@kernel.org \
    --cc=ziy@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox