diff mbox series

[01/17] mm/highmem: Lift memcpy_[to|from]_page and memset_page to core

Message ID 20201124060755.1405602-2-ira.weiny@intel.com (mailing list archive)
State New
Headers show
Series kmap: Create mem*_page interfaces | expand

Commit Message

Ira Weiny Nov. 24, 2020, 6:07 a.m. UTC
From: Ira Weiny <ira.weiny@intel.com>

Working through a conversion to a call such as kmap_thread() revealed
many places where the pattern kmap/memcpy/kunmap occurred.

Eric Biggers, Matthew Wilcox, Christoph Hellwig, Dan Williams, and Al
Viro all suggested putting this code into helper functions.  Al Viro
further pointed out that these functions already existed in the iov_iter
code.[1]

Placing these functions in 'highmem.h' is suboptimal especially with the
changes being proposed in the functionality of kmap.  From a caller
perspective including/using 'highmem.h' implies that the functions
defined in that header are only required when highmem is in use which is
increasingly not the case with modern processors.  Some headers like
mm.h or string.h seem ok but don't really portray the functionality
well.  'pagemap.h', on the other hand, makes sense and is already
included in many of the places we want to convert.

Another alternative would be to create a new header for the promoted
memcpy functions, but it masks the fact that these are designed to copy
to/from pages using the kernel direct mappings and complicates matters
with a new header.

Lift memcpy_to_page(), memcpy_from_page(), and memzero_page() to
pagemap.h.

Also, add a memcpy_page(), memmove_page, and memset_page() to cover more
kmap/mem*/kunmap. patterns.

[1] https://lore.kernel.org/lkml/20201013200149.GI3576660@ZenIV.linux.org.uk/
    https://lore.kernel.org/lkml/20201013112544.GA5249@infradead.org/

Cc: Dave Hansen <dave.hansen@intel.com>
Suggested-by: Matthew Wilcox <willy@infradead.org>
Suggested-by: Christoph Hellwig <hch@infradead.org>
Suggested-by: Dan Williams <dan.j.williams@intel.com>
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
Suggested-by: Eric Biggers <ebiggers@kernel.org>
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
---
 include/linux/pagemap.h | 49 +++++++++++++++++++++++++++++++++++++++++
 lib/iov_iter.c          | 21 ------------------
 2 files changed, 49 insertions(+), 21 deletions(-)

Comments

Matthew Wilcox Nov. 24, 2020, 2:19 p.m. UTC | #1
On Mon, Nov 23, 2020 at 10:07:39PM -0800, ira.weiny@intel.com wrote:
> +static inline void memzero_page(struct page *page, size_t offset, size_t len)
> +{
> +	memset_page(page, 0, offset, len);
> +}

This is a less-capable zero_user_segments().
Ira Weiny Nov. 24, 2020, 7:21 p.m. UTC | #2
On Tue, Nov 24, 2020 at 02:19:41PM +0000, Matthew Wilcox wrote:
> On Mon, Nov 23, 2020 at 10:07:39PM -0800, ira.weiny@intel.com wrote:
> > +static inline void memzero_page(struct page *page, size_t offset, size_t len)
> > +{
> > +	memset_page(page, 0, offset, len);
> > +}
> 
> This is a less-capable zero_user_segments().

Actually it is a duplicate of zero_user()...  Sorry I did not notice those...
:-(

Why are they called '_user_'?

Ira
Matthew Wilcox Nov. 24, 2020, 8:20 p.m. UTC | #3
On Tue, Nov 24, 2020 at 11:21:13AM -0800, Ira Weiny wrote:
> On Tue, Nov 24, 2020 at 02:19:41PM +0000, Matthew Wilcox wrote:
> > On Mon, Nov 23, 2020 at 10:07:39PM -0800, ira.weiny@intel.com wrote:
> > > +static inline void memzero_page(struct page *page, size_t offset, size_t len)
> > > +{
> > > +	memset_page(page, 0, offset, len);
> > > +}
> > 
> > This is a less-capable zero_user_segments().
> 
> Actually it is a duplicate of zero_user()...  Sorry I did not notice those...
> :-(
> 
> Why are they called '_user_'?

git knows ...

commit 01f2705daf5a36208e69d7cf95db9c330f843af6
Author: Nate Diller <nate.diller@gmail.com>
Date:   Wed May 9 02:35:07 2007 -0700

    fs: convert core functions to zero_user_page
    
    It's very common for file systems to need to zero part or all of a page,
    the simplist way is just to use kmap_atomic() and memset().  There's
    actually a library function in include/linux/highmem.h that does exactly
    that, but it's confusingly named memclear_highpage_flush(), which is
    descriptive of *how* it does the work rather than what the *purpose* is.
    So this patchset renames the function to zero_user_page(), and calls it
    from the various places that currently open code it.
    
    This first patch introduces the new function call, and converts all the
    core kernel callsites, both the open-coded ones and the old
    memclear_highpage_flush() ones.  Following this patch is a series of
    conversions for each file system individually, per AKPM, and finally a
    patch deprecating the old call.  The diffstat below shows the entire
    patchset.
Matthew Wilcox Nov. 27, 2020, 1:20 p.m. UTC | #4
On Fri, Nov 27, 2020 at 03:06:24PM +0200, Joonas Lahtinen wrote:
> Quoting ira.weiny@intel.com (2020-11-24 08:07:39)
> > From: Ira Weiny <ira.weiny@intel.com>
> > 
> > Working through a conversion to a call such as kmap_thread() revealed
> > many places where the pattern kmap/memcpy/kunmap occurred.
> > 
> > Eric Biggers, Matthew Wilcox, Christoph Hellwig, Dan Williams, and Al
> > Viro all suggested putting this code into helper functions.  Al Viro
> > further pointed out that these functions already existed in the iov_iter
> > code.[1]
> > 
> > Placing these functions in 'highmem.h' is suboptimal especially with the
> > changes being proposed in the functionality of kmap.  From a caller
> > perspective including/using 'highmem.h' implies that the functions
> > defined in that header are only required when highmem is in use which is
> > increasingly not the case with modern processors.  Some headers like
> > mm.h or string.h seem ok but don't really portray the functionality
> > well.  'pagemap.h', on the other hand, makes sense and is already
> > included in many of the places we want to convert.
> > 
> > Another alternative would be to create a new header for the promoted
> > memcpy functions, but it masks the fact that these are designed to copy
> > to/from pages using the kernel direct mappings and complicates matters
> > with a new header.
> > 
> > Lift memcpy_to_page(), memcpy_from_page(), and memzero_page() to
> > pagemap.h.
> > 
> > Also, add a memcpy_page(), memmove_page, and memset_page() to cover more
> > kmap/mem*/kunmap. patterns.
> > 
> > [1] https://lore.kernel.org/lkml/20201013200149.GI3576660@ZenIV.linux.org.uk/
> >     https://lore.kernel.org/lkml/20201013112544.GA5249@infradead.org/
> > 
> > Cc: Dave Hansen <dave.hansen@intel.com>
> > Suggested-by: Matthew Wilcox <willy@infradead.org>
> > Suggested-by: Christoph Hellwig <hch@infradead.org>
> > Suggested-by: Dan Williams <dan.j.williams@intel.com>
> > Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
> > Suggested-by: Eric Biggers <ebiggers@kernel.org>
> > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> 
> <SNIP>
> 
> > +static inline void memset_page(struct page *page, int val, size_t offset, size_t len)
> > +{
> > +       char *addr = kmap_atomic(page);
> > +       memset(addr + offset, val, len);
> > +       kunmap_atomic(addr);
> > +}
> 
> Other functions have (page, offset) pair. Insertion of 'val' in the middle here required
> to take a double look during review.

Let's be explicit here.  Your suggested order is:

	(page, offset, val, len)

right?  I think I would prefer that to (page, val, offset, len).
Ira Weiny Dec. 3, 2020, 6:25 p.m. UTC | #5
On Mon, Nov 30, 2020 at 11:22:32AM +0200, Joonas Lahtinen wrote:
> Quoting Matthew Wilcox (2020-11-27 15:20:06)
> > On Fri, Nov 27, 2020 at 03:06:24PM +0200, Joonas Lahtinen wrote:
> > > Quoting ira.weiny@intel.com (2020-11-24 08:07:39)
> > > > From: Ira Weiny <ira.weiny@intel.com>
> > > > 
> > > > Working through a conversion to a call such as kmap_thread() revealed
> > > > many places where the pattern kmap/memcpy/kunmap occurred.
> > > > 
> > > > Eric Biggers, Matthew Wilcox, Christoph Hellwig, Dan Williams, and Al
> > > > Viro all suggested putting this code into helper functions.  Al Viro
> > > > further pointed out that these functions already existed in the iov_iter
> > > > code.[1]
> > > > 
> > > > Placing these functions in 'highmem.h' is suboptimal especially with the
> > > > changes being proposed in the functionality of kmap.  From a caller
> > > > perspective including/using 'highmem.h' implies that the functions
> > > > defined in that header are only required when highmem is in use which is
> > > > increasingly not the case with modern processors.  Some headers like
> > > > mm.h or string.h seem ok but don't really portray the functionality
> > > > well.  'pagemap.h', on the other hand, makes sense and is already
> > > > included in many of the places we want to convert.
> > > > 
> > > > Another alternative would be to create a new header for the promoted
> > > > memcpy functions, but it masks the fact that these are designed to copy
> > > > to/from pages using the kernel direct mappings and complicates matters
> > > > with a new header.
> > > > 
> > > > Lift memcpy_to_page(), memcpy_from_page(), and memzero_page() to
> > > > pagemap.h.
> > > > 
> > > > Also, add a memcpy_page(), memmove_page, and memset_page() to cover more
> > > > kmap/mem*/kunmap. patterns.
> > > > 
> > > > [1] https://lore.kernel.org/lkml/20201013200149.GI3576660@ZenIV.linux.org.uk/
> > > >     https://lore.kernel.org/lkml/20201013112544.GA5249@infradead.org/
> > > > 
> > > > Cc: Dave Hansen <dave.hansen@intel.com>
> > > > Suggested-by: Matthew Wilcox <willy@infradead.org>
> > > > Suggested-by: Christoph Hellwig <hch@infradead.org>
> > > > Suggested-by: Dan Williams <dan.j.williams@intel.com>
> > > > Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
> > > > Suggested-by: Eric Biggers <ebiggers@kernel.org>
> > > > Signed-off-by: Ira Weiny <ira.weiny@intel.com>
> > > 
> > > <SNIP>
> > > 
> > > > +static inline void memset_page(struct page *page, int val, size_t offset, size_t len)
> > > > +{
> > > > +       char *addr = kmap_atomic(page);
> > > > +       memset(addr + offset, val, len);
> > > > +       kunmap_atomic(addr);
> > > > +}
> > > 
> > > Other functions have (page, offset) pair. Insertion of 'val' in the middle here required
> > > to take a double look during review.
> > 
> > Let's be explicit here.  Your suggested order is:
> > 
> >         (page, offset, val, len)
> > 
> > right?  I think I would prefer that to (page, val, offset, len).
> 
> Yeah, I think that would be most consistent order.

Yes as I have been reworking these I have found it odd as well.  I'm going to
swap it around.  Been learning Coccinelle which has helped find other
instances...  So V2 is taking a bit of time.

Thanks,
Ira
diff mbox series

Patch

diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
index c77b7c31b2e4..82a0af6bc843 100644
--- a/include/linux/pagemap.h
+++ b/include/linux/pagemap.h
@@ -1028,4 +1028,53 @@  unsigned int i_blocks_per_page(struct inode *inode, struct page *page)
 {
 	return thp_size(page) >> inode->i_blkbits;
 }
+
+static inline void memcpy_page(struct page *dst_page, size_t dst_off,
+			       struct page *src_page, size_t src_off,
+			       size_t len)
+{
+	char *dst = kmap_atomic(dst_page);
+	char *src = kmap_atomic(src_page);
+	memcpy(dst + dst_off, src + src_off, len);
+	kunmap_atomic(src);
+	kunmap_atomic(dst);
+}
+
+static inline void memmove_page(struct page *dst_page, size_t dst_off,
+			       struct page *src_page, size_t src_off,
+			       size_t len)
+{
+	char *dst = kmap_atomic(dst_page);
+	char *src = kmap_atomic(src_page);
+	memmove(dst + dst_off, src + src_off, len);
+	kunmap_atomic(src);
+	kunmap_atomic(dst);
+}
+
+static inline void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
+{
+	char *from = kmap_atomic(page);
+	memcpy(to, from + offset, len);
+	kunmap_atomic(from);
+}
+
+static inline void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
+{
+	char *to = kmap_atomic(page);
+	memcpy(to + offset, from, len);
+	kunmap_atomic(to);
+}
+
+static inline void memset_page(struct page *page, int val, size_t offset, size_t len)
+{
+	char *addr = kmap_atomic(page);
+	memset(addr + offset, val, len);
+	kunmap_atomic(addr);
+}
+
+static inline void memzero_page(struct page *page, size_t offset, size_t len)
+{
+	memset_page(page, 0, offset, len);
+}
+
 #endif /* _LINUX_PAGEMAP_H */
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 1635111c5bd2..2439a8b4f0d2 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -466,27 +466,6 @@  void iov_iter_init(struct iov_iter *i, unsigned int direction,
 }
 EXPORT_SYMBOL(iov_iter_init);
 
-static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len)
-{
-	char *from = kmap_atomic(page);
-	memcpy(to, from + offset, len);
-	kunmap_atomic(from);
-}
-
-static void memcpy_to_page(struct page *page, size_t offset, const char *from, size_t len)
-{
-	char *to = kmap_atomic(page);
-	memcpy(to + offset, from, len);
-	kunmap_atomic(to);
-}
-
-static void memzero_page(struct page *page, size_t offset, size_t len)
-{
-	char *addr = kmap_atomic(page);
-	memset(addr + offset, 0, len);
-	kunmap_atomic(addr);
-}
-
 static inline bool allocated(struct pipe_buffer *buf)
 {
 	return buf->ops == &default_pipe_buf_ops;