From: Rik van Riel <riel@surriel.com>
To: linux-kernel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.org>,
David Hildenbrand <david@kernel.org>,
Jason Gunthorpe <jgg@ziepe.ca>,
John Hubbard <jhubbard@nvidia.com>, Peter Xu <peterx@redhat.com>,
Suren Baghdasaryan <surenb@google.com>,
Yuan-Hao Hsu <aa9736195201@gmail.com>,
Christoph Hellwig <hch@infradead.org>,
linux-mm@kvack.org, Rik van Riel <riel@surriel.com>
Subject: [PATCH 3/8] mm/gup: split out folio grab-and-store helper
Date: Mon, 2 Nov 2026 14:30:21 -0500 [thread overview]
Message-ID: <20261102193026.3860700-4-riel@surriel.com> (raw)
In-Reply-To: <20261102193026.3860700-1-riel@surriel.com>
follow_page_pte() does two things once it has resolved a present PTE
to a page: run the per-PTE safety checks (write-fault, unshare), then
grab a folio reference and hand the page back: fault it in if pinning,
mark it dirty/accessed, and fill the caller's array slot.
Split the second part into gup_grab_folio_pages(), which
takes its inputs as parameters, so the checks and the grab-and-store
can be applied at different granularities. The name states what the
helper does, not 'commit', and it takes the PTE's dirtiness as a bool.
The helper stores the pages but leaves the cache flushes to the
caller: split them into gup_flush_pages(), which follow_page_pte()
runs after dropping the PTE lock. Storing one slot under the lock is
cheap; flushing is not, and needs no lock once the folio reference
is held.
No functional changes intended.
Suggested-by: David Hildenbrand <david@kernel.org>
Signed-off-by: Rik van Riel <riel@surriel.com>
---
mm/gup.c | 101 +++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 72 insertions(+), 29 deletions(-)
diff --git a/mm/gup.c b/mm/gup.c
index f7d940e7e5ffb..27ae2a1d7c36d 100644
--- a/mm/gup.c
+++ b/mm/gup.c
@@ -650,6 +650,25 @@ static void gup_fill_pages(struct vm_area_struct *vma, unsigned long address,
}
}
+/*
+ * Flush the caches for @nr pages stored in @pages. The caller already
+ * holds folio references, so the pages cannot go away; this runs
+ * without any page table lock held.
+ */
+static void gup_flush_pages(struct vm_area_struct *vma, unsigned long address,
+ struct page **pages, unsigned long nr)
+{
+ unsigned long i;
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < nr; i++) {
+ flush_anon_page(vma, pages[i], address + i * PAGE_SIZE);
+ flush_dcache_page(pages[i]);
+ }
+}
+
#ifdef CONFIG_PGTABLE_HAS_HUGE_LEAVES
/* FOLL_FORCE can write to even unwritable PUDs in COW mappings. */
static inline bool can_follow_write_pud(pud_t pud, struct page *page,
@@ -822,6 +841,54 @@ static inline bool can_follow_write_pte(pte_t pte, struct page *page,
return !userfaultfd_pte_wp(vma, pte);
}
+/*
+ * Grab a folio reference for @nr pages starting at @page, mark the folio
+ * accessed (and dirty for FOLL_WRITE), and store the pages for the caller.
+ * The caller flushes the caches after dropping the page table lock.
+ */
+static long gup_grab_folio_pages(struct folio *folio,
+ struct page *page, unsigned long nr, bool dirty,
+ unsigned int flags, struct page **pages)
+{
+ unsigned long i;
+ long ret;
+
+ /* try_grab_folio() does nothing unless FOLL_GET or FOLL_PIN is set. */
+ ret = try_grab_folio(folio, nr, flags);
+ if (unlikely(ret))
+ return ret;
+
+ /*
+ * We need to make the page accessible if and only if we are going
+ * to access its content (the FOLL_PIN case). Please see
+ * Documentation/core-api/pin_user_pages.rst for details.
+ */
+ if (flags & FOLL_PIN) {
+ ret = arch_make_folio_accessible(folio);
+ if (ret) {
+ gup_put_folio(folio, nr, flags);
+ return ret;
+ }
+ }
+ if (flags & FOLL_TOUCH) {
+ if ((flags & FOLL_WRITE) && !dirty && !folio_test_dirty(folio))
+ folio_mark_dirty(folio);
+ /*
+ * pte_mkyoung() would be more correct here, but atomic care
+ * is needed to avoid losing the dirty bit: it is easier to use
+ * folio_mark_accessed().
+ */
+ folio_mark_accessed(folio);
+ }
+
+ if (pages) {
+ for (i = 0; i < nr; i++)
+ pages[i] = page + i;
+ }
+
+ return 0;
+}
+
static long follow_page_pte(struct vm_area_struct *vma,
unsigned long address, pmd_t *pmd, unsigned int flags,
struct page **pages)
@@ -877,39 +944,15 @@ static long follow_page_pte(struct vm_area_struct *vma,
VM_WARN_ON_ONCE_PAGE((flags & FOLL_PIN) && PageAnon(page) &&
!PageAnonExclusive(page), page);
- /* try_grab_folio() does nothing unless FOLL_GET or FOLL_PIN is set. */
- ret = try_grab_folio(folio, 1, flags);
- if (unlikely(ret))
+ ret = gup_grab_folio_pages(folio, page, 1,
+ pte_dirty(pte), flags, pages);
+ if (ret)
goto out;
-
- /*
- * We need to make the page accessible if and only if we are going
- * to access its content (the FOLL_PIN case). Please see
- * Documentation/core-api/pin_user_pages.rst for details.
- */
- if (flags & FOLL_PIN) {
- ret = arch_make_folio_accessible(folio);
- if (ret) {
- gup_put_folio(folio, 1, flags);
- goto out;
- }
- }
- if (flags & FOLL_TOUCH) {
- if ((flags & FOLL_WRITE) &&
- !pte_dirty(pte) && !folio_test_dirty(folio))
- folio_mark_dirty(folio);
- /*
- * pte_mkyoung() would be more correct here, but atomic care
- * is needed to avoid losing the dirty bit: it is easier to use
- * folio_mark_accessed().
- */
- folio_mark_accessed(folio);
- }
-
- gup_fill_pages(vma, address, page, 1, pages);
ret = 1;
out:
pte_unmap_unlock(ptep, ptl);
+ if (ret > 0)
+ gup_flush_pages(vma, address, pages, ret);
return ret;
no_page:
pte_unmap_unlock(ptep, ptl);
--
2.53.0-Meta
next prev parent reply other threads:[~2026-11-02 19:30 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-02 19:30 [RFC PATCH v5 0/8] batch lookups in follow_page_mask() Rik van Riel
2026-11-02 19:30 ` [PATCH 1/8] mm/gup: break out gup_fill_pages() helper Rik van Riel
2026-11-02 19:30 ` [PATCH 2/8] mm/gup: convert follow_page_mask() to return a long Rik van Riel
2026-11-02 19:30 ` Rik van Riel [this message]
2026-11-02 19:30 ` [PATCH 4/8] mm/gup: break out gup_lookup_pte() helper Rik van Riel
2026-11-02 19:30 ` [PATCH 5/8] mm/gup: fill the pages array outside the pud/pmd lock Rik van Riel
2026-11-02 19:30 ` [PATCH 6/8] mm/gup: return a huge page's full count from follow_page_mask() Rik van Riel
2026-11-02 19:30 ` [PATCH 7/8] mm/gup: walk multiple PTEs per follow_page_pte() call Rik van Riel
2026-11-02 19:30 ` [PATCH 8/8] mm/gup: batch contiguous same-folio PTEs into one refcount grab Rik van Riel
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=20261102193026.3860700-4-riel@surriel.com \
--to=riel@surriel.com \
--cc=aa9736195201@gmail.com \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=hch@infradead.org \
--cc=jgg@ziepe.ca \
--cc=jhubbard@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=peterx@redhat.com \
--cc=surenb@google.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
all inboxes | Powered by JetHome®