From: Rik van Riel <riel@surriel.com>
To: "David Hildenbrand (Arm)" <david@kernel.org>,
linux-kernel@vger.kernel.org
Cc: Andrew Morton <akpm@linux-foundation.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
Subject: Re: [PATCH 1/8] mm/gup: break out gup_fill_pages() helper
Date: Tue, 03 Nov 2026 11:29:45 -0500 [thread overview]
Message-ID: <3a7d12b52a5de61277842f2cca0dd90d42728135.camel@surriel.com> (raw)
In-Reply-To: <df9c504e-e62a-40c0-a513-e8b8c758dd57@kernel.org>
On Tue, 2026-11-03 at 09:43 +0100, David Hildenbrand (Arm) wrote:
> On 11/2/26 20:30, Rik van Riel wrote:
> > __get_user_pages() fills pages[] and flushes each page's caches in
> > an
> > open-coded loop.
> >
> > Move it into a gup_fill_pages() helper, which the
> > follow_page_mask()
> > call chain can then use to fill its own pages[] slots.
> >
> > No functional changes intended.
> >
> > Suggested-by: David Hildenbrand <david@kernel.org>
> > Reviewed-by: Suren Baghdasaryan <surenb@google.com>
> > Signed-off-by: Rik van Riel <riel@surriel.com>
> > ---
> > mm/gup.c | 27 ++++++++++++++++++---------
> > 1 file changed, 18 insertions(+), 9 deletions(-)
> >
> > diff --git a/mm/gup.c b/mm/gup.c
> > index 7d43ce717b246..cdbe2ed3e6442 100644
> > --- a/mm/gup.c
> > +++ b/mm/gup.c
> > @@ -633,6 +633,23 @@ static struct page *no_page_table(struct
> > vm_area_struct *vma,
> > return NULL;
> > }
> >
> > +static void gup_fill_pages(struct vm_area_struct *vma, unsigned
> > long address,
> > + struct page *page, unsigned long nr, struct page
> > **pages)
>
> unsigned long nr_pages ?
Will do. Thank you.
>
>
> > +{
> > + unsigned long i;
> > +
> > + if (!pages)
> > + return;
>
> Why is that check needed? Why should someone try filling something
> that doesn't
> exist?
faultin_page_range() and populate_vma_page_range
both call the GUP code with a NULL pages pointer.
>
> > +
> > + for (i = 0; i < nr; i++) {
> > + struct page *subpage = page + i;
>
> There is no such thing as a subpage as Willy keeps saying :)
>
> Why not reuse "page" ?
>
> for (i = 0; i < nr; i++, page++) {
Will do.
>
> > +
> > + pages[i] = subpage;
> > + flush_anon_page(vma, subpage, address + i *
> > PAGE_SIZE);
> > + flush_dcache_page(subpage);
>
> Now I wonder why GUP-fast doesn't need any of that. Likely because
> architectures
> that support GUP-fast don't need any such magic.
These cache flushes seem to be common on architectures
with virtually indexed caches, where the cache needs
to be flushed if the memory is accessed through a
different virtual address than where userspace might
have written data.
On 32 bit ARM, MIPS, and sh, the kernel seems to
implement an actual cache flush here, but also
claims to support GUP-fast.
I don't understand how that would possibly be safe,
since the pages returned by GUP-fast could be
accessed under a different cache alias than where
userspace accesses the memory.
I think one reason GUP-fast can get away without
a dcache flush is that most of the callers take
care of that, for example by using raw_copy_to_user()
to copy data using the user's virtual address, which
takes care of VIVT cache aliasing.
Likewise, drivers that do zero-copy DMA into user
memory will get the cache flushed on VIVT architectures
inside dma_unmap().
The dcache flush inside GUP covers rarer cases, where
the kernel accesses memory at a different address than
where userspace wrote to that memory.
cat /proc/<pid>/cmdline KERNEL
---- -------------------
read(buf=U)
-------------------------------->
get_mm_cmdline(mm, U):
access_remote_vm(mm, arg_start,
kbuf, len, FOLL_ANON):
get_user_page_vma_remote()
(slow GUP + its flushes)
kmap_local_folio(P)
memcpy(kbuf <- K) // read
kunmap_local(K)
copy_to_user(U, kbuf) // write
through U itself
read buf at U
------>
clean: the final write to U went
through U. Same line. No hazard.
In this case, the dcache flush in slow GUP ensures
that data written before the GUP is visible to the
reader.
In the write path, e.g. for PTRACE_POKE, __access_remote_vm
flushes after the write has been done, so the other
process will see the data on the next read.
It looks like process_vm_writev() might be missing that
cache flush after a write, though.
However, now we have a situation where:
1) Only a few oddball callers of GUP need the dcache flush, and
2) Those need to do their OWN flush after a write, and
3) The dcache flush is not needed for most other GUP callers.
We could do two different things here to solve the
asymmetry.
Either we could add dcache flushing to
unpin_user_pages_dirty_lock(), and make sure all
the callers that need to call that function, or
we could move the cache flushing to the GUP callers.
Then we have KSM, which uses GUP, and which would like
to see the latest page contents...
>
> Maybe we should in fact base this on the gup-fast batching series and
> also use
> the helper in there (passing vma=NULL)?
I just moved this code out of __get_user_pages(),
but if we can get rid of it completely by using
a different helper, all the better.
Which GUP-fast batching series are you talking
about, here?
In Yeoreum Yun's series, the batch filling seems
to be done inline in gup_fast_pte_range(), and not
in a helper:
https://lore.kernel.org/all/20261102-refactor_gup_fast-v2-4-bc1ac018c834@arm.com/
Should both paths be using the same gup_fill_pages()
helper?
The cache flushing part is broken out into its own
helper function later in this series, so we could
sequence those differently.
--
All Rights Reversed.
next prev parent reply other threads:[~2026-11-03 16:29 UTC|newest]
Thread overview: 18+ 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-03 8:43 ` David Hildenbrand (Arm)
2026-11-03 16:29 ` Rik van Riel [this message]
2026-11-03 16:39 ` David Hildenbrand (Arm)
2026-11-03 17:00 ` Rik van Riel
2026-11-03 17:04 ` David Hildenbrand (Arm)
2026-11-03 19:30 ` 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 ` [PATCH 3/8] mm/gup: split out folio grab-and-store helper Rik van Riel
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
2026-11-03 8:35 ` David Hildenbrand (Arm)
2026-11-03 16:42 ` Rik van Riel
2026-11-03 16:55 ` David Hildenbrand (Arm)
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=3a7d12b52a5de61277842f2cca0dd90d42728135.camel@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®