Instead of unmapping the folio after copying the data to it, then mapping
it again to zero the tail, provide folio_zero_tail() to zero the tail
of an already-mapped folio.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
 fs/ext4/inline.c        |  3 +--
 include/linux/highmem.h | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c
index 9a84a5f9fef4..d5bd1e3a5d36 100644
--- a/fs/ext4/inline.c
+++ b/fs/ext4/inline.c
@@ -502,9 +502,8 @@ static int ext4_read_inline_folio(struct inode *inode, 
struct folio *folio)
        BUG_ON(len > PAGE_SIZE);
        kaddr = kmap_local_folio(folio, 0);
        ret = ext4_read_inline_data(inode, kaddr, len, &iloc);
-       flush_dcache_folio(folio);
+       kaddr = folio_zero_tail(folio, len, kaddr + len);
        kunmap_local(kaddr);
-       folio_zero_segment(folio, len, folio_size(folio));
        folio_mark_uptodate(folio);
        brelse(iloc.bh);
 
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 4cacc0e43b51..1b81416196dd 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -483,6 +483,44 @@ static inline void memcpy_to_folio(struct folio *folio, 
size_t offset,
        flush_dcache_folio(folio);
 }
 
+/**
+ * folio_zero_tail - Zero the tail of a folio.
+ * @folio: The folio to zero.
+ * @kaddr: The address the folio is currently mapped to.
+ * @offset: The byte offset in the folio to start zeroing at.
+ *
+ * If you have already used kmap_local_folio() to map a folio, written
+ * some data to it and now need to zero the end of the folio (and flush
+ * the dcache), you can use this function.  If you do not have the
+ * folio kmapped (eg the folio has been partially populated by DMA),
+ * use folio_zero_range() or folio_zero_segment() instead.
+ *
+ * Return: An address which can be passed to kunmap_local().
+ */
+static inline __must_check void *folio_zero_tail(struct folio *folio,
+               size_t offset, void *kaddr)
+{
+       size_t len = folio_size(folio) - offset;
+
+       if (folio_test_highmem(folio)) {
+               size_t max = PAGE_SIZE - offset_in_page(offset);
+
+               while (len > max) {
+                       memset(kaddr, 0, max);
+                       kunmap_local(kaddr);
+                       len -= max;
+                       offset += max;
+                       max = PAGE_SIZE;
+                       kaddr = kmap_local_folio(folio, offset);
+               }
+       }
+
+       memset(kaddr, 0, len);
+       flush_dcache_folio(folio);
+
+       return kaddr;
+}
+
 /**
  * memcpy_from_file_folio - Copy some bytes from a file folio.
  * @to: The destination buffer.
-- 
2.42.0

Reply via email to