From: "Jose A. Perez de Azpillaga" <azpijr@gmail.com>
To: Andrew Morton <akpm@linux-foundation.org>,
Mike Rapoport <rppt@kernel.org>,
David Hildenbrand <david@kernel.org>,
Jonathan Corbet <corbet@lwn.net>
Cc: Peter Xu <peterx@redhat.com>,
"Liam R. Howlett" <liam@infradead.org>,
Vlastimil Babka <vbabka@kernel.org>,
Suren Baghdasaryan <surenb@google.com>,
Michal Hocko <mhocko@suse.com>,
Shuah Khan <skhan@linuxfoundation.org>,
Randy Dunlap <rdunlap@infradead.org>,
linux-mm@kvack.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, Kiryl Shutsemau <kas@kernel.org>,
Tal Zussman <tz2294@columbia.edu>,
Linus Torvalds <torvalds@linux-foundation.org>,
"Jose A. Perez de Azpillaga" <azpijr@gmail.com>
Subject: [PATCH] Documentation/userfaultfd: document RWP behavior when the PTE is dropped
Date: Fri, 13 Nov 2026 12:27:42 +0100 [thread overview]
Message-ID: <20261113112806.109578-1-azpijr@gmail.com> (raw)
"Scope of protection" lists only the PTE drops requested by the user and
says the range has to be re-armed afterwards. On shmem the kernel drops
the PTE as well (page reclaim, collapse), and RWP has no marker by design:
the next access is a minor or a missing fault, and the user is expected to
register for those. Document this, together with the modes that sync and
async tracking need.
The VMM example registers guest_mem with RWP and MISSING only, so a vCPU
write to an offset that is not mapped there can be lost during eviction.
Add MINOR to the registration and describe how the handler treats minor
faults.
Link: https://lore.kernel.org/linux-mm/856cdf21-7982-475a-b244-a7ceff4eb6aa@kernel.org/
Link: https://lore.kernel.org/linux-mm/avWnaR7VVEGa7yNp@thinkstation/
Suggested-by: Tal Zussman <tz2294@columbia.edu>
Signed-off-by: Jose A. Perez de Azpillaga <azpijr@gmail.com>
---
this is the first time I'm writing documentation, so I accept any
suggestions to improve it :) and I tried to follow the documentation's
own style.
Documentation/admin-guide/mm/userfaultfd.rst | 65 ++++++++++++++++----
1 file changed, 53 insertions(+), 12 deletions(-)
diff --git a/Documentation/admin-guide/mm/userfaultfd.rst b/Documentation/admin-guide/mm/userfaultfd.rst
index 97daea83b7f1..19bdac668f93 100644
--- a/Documentation/admin-guide/mm/userfaultfd.rst
+++ b/Documentation/admin-guide/mm/userfaultfd.rst
@@ -384,17 +384,45 @@ normal missing path (``do_anonymous_page()``, ``do_swap_page()``,
the population itself, co-register the range with
``UFFDIO_REGISTER_MODE_MISSING``.
-Protection is preserved across page reclaim: a page swapped out while
-RWP-protected carries the marker on its swap entry, and swap-in restores
-the PROT_NONE state so the first access after swap-in still faults. The
-same applies to pages temporarily replaced by migration entries.
-
-Operations that drop the PTE entirely — ``MADV_DONTNEED`` on anonymous
-memory, hole-punch on shmem, truncation of a file mapping — also drop the
-RWP marker: the next access re-populates the range without protection.
-Unlike WP (which persists via ``PTE_MARKER_UFFD_WP``), there is no
-persistent RWP marker today. The user needs to re-arm the range with
-``UFFDIO_RWPROTECT`` after any operation that explicitly frees PTEs.
+Protection of anonymous memory is preserved across page reclaim: a page
+swapped out while RWP-protected carries the marker on its swap entry, and
+swap-in restores the PROT_NONE state so the first access after swap-in
+still faults. The same applies to pages temporarily replaced by migration
+entries. Shmem is different: reclaiming a shmem page leaves the PTE empty
+rather than installing a swap entry, so the protection is dropped along
+with the PTE.
+
+Operations that drop the PTE entirely also drop the RWP protection. Some
+are requested by the user — ``MADV_DONTNEED``, hole-punch on shmem,
+truncation of a file mapping — and some are initiated by the kernel on
+shmem: page reclaim, and collapse into a huge page by khugepaged or
+``MADV_COLLAPSE``. Unlike WP (which persists via ``PTE_MARKER_UFFD_WP``),
+there is no persistent RWP marker, and this is intentional: once the PTE is
+gone, the next access takes a page fault that userfaultfd can deliver, and
+that fault is the notification. Which fault it is depends on what happened
+to the page:
+
+- The page is still in the page cache or in swap: the access is a minor
+ fault, delivered if the range is co-registered with
+ ``UFFDIO_REGISTER_MODE_MINOR``.
+
+- The page is gone: the access is a missing fault, delivered if the range
+ is co-registered with ``UFFDIO_REGISTER_MODE_MISSING``.
+
+WP cannot use the same scheme — a minor fault would trap reads as well as
+writes — which is why it needs a marker.
+
+Without the matching mode the page is mapped back silently and without
+protection, so what to co-register depends on the tracking mode:
+
+- Sync mode: the access would be lost. Co-register shmem and hugetlbfs
+ ranges with ``UFFDIO_REGISTER_MODE_MINOR`` (and
+ ``UFFDIO_REGISTER_MODE_MISSING`` if the file can have holes), and
+ anonymous ranges, where minor faults are not available, with
+ ``UFFDIO_REGISTER_MODE_MISSING``.
+
+- Async mode: neither is needed. A page faulted back in is mapped without
+ the uffd bit, so ``PAGEMAP_SCAN`` reports it as accessed.
**Fault Handling:**
@@ -486,10 +514,16 @@ never traps on its own ::
ioctl(uffd, UFFDIO_API, &api);
if (!(api.features & UFFD_FEATURE_RWP))
/* RWP unavailable on this kernel/arch -- fall back. */
+ /*
+ * MINOR: an offset in the page cache but not mapped in guest_mem
+ * would be mapped back without a fault, and a vCPU write between
+ * the pwrite() and the hole punch in step 4 would be lost.
+ */
ioctl(uffd, UFFDIO_REGISTER, &(struct uffdio_register){
.range = { guest_mem, guest_size },
.mode = UFFDIO_REGISTER_MODE_RWP |
- UFFDIO_REGISTER_MODE_MISSING,
+ UFFDIO_REGISTER_MODE_MISSING |
+ UFFDIO_REGISTER_MODE_MINOR,
});
/* Tracking loop */
@@ -550,6 +584,13 @@ progress. After ``fallocate()`` punches the page out and ``UFFDIO_WAKE``
fires, the vCPU retries the access, faults as ``MISSING``, and the
handler resolves it with ``UFFDIO_COPY`` from storage.
+The same holds for an offset being evicted that is not mapped in
+``guest_mem``, except that the vCPU blocks with a
+``UFFD_PAGEFAULT_FLAG_MINOR`` fault, which the handler leaves pending until
+the page has been punched out. Minor faults on any other offset are
+resolved with ``UFFDIO_CONTINUE``: the page is mapped without RWP
+protection, so the next scan reports it as accessed.
+
This workflow targets shmem and hugetlbfs (both support a private
``io_mem`` mapping over the same fd). Anonymous-memory backings need a
different inner-loop strategy because the VMM has no way to read the
base-commit: 31a696f9c37defab0f28f774e00ac7f135889ec3
--
2.56.0
next reply other threads:[~2026-11-13 11:28 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-13 11:27 Jose A. Perez de Azpillaga [this message]
2026-11-13 12:44 ` [PATCH] Documentation/userfaultfd: document RWP behavior when the PTE is dropped 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=20261113112806.109578-1-azpijr@gmail.com \
--to=azpijr@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=corbet@lwn.net \
--cc=david@kernel.org \
--cc=kas@kernel.org \
--cc=liam@infradead.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=mhocko@suse.com \
--cc=peterx@redhat.com \
--cc=rdunlap@infradead.org \
--cc=rppt@kernel.org \
--cc=skhan@linuxfoundation.org \
--cc=surenb@google.com \
--cc=torvalds@linux-foundation.org \
--cc=tz2294@columbia.edu \
--cc=vbabka@kernel.org \
/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