The iomap code was limited to PAGE_SIZE bytes; generalise it to cover
an arbitrary-sized folio, and move it to be a common helper.

Signed-off-by: Matthew Wilcox (Oracle) <[email protected]>
---
 fs/iomap/buffered-io.c  | 14 ++------------
 include/linux/highmem.h | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+), 12 deletions(-)

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index f72df2babe56..093c4515b22a 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -305,28 +305,18 @@ static int iomap_read_inline_data(const struct iomap_iter 
*iter,
 {
        const struct iomap *iomap = iomap_iter_srcmap(iter);
        size_t size = i_size_read(iter->inode) - iomap->offset;
-       size_t poff = offset_in_page(iomap->offset);
        size_t offset = offset_in_folio(folio, iomap->offset);
-       void *addr;
 
        if (folio_test_uptodate(folio))
                return 0;
 
-       if (WARN_ON_ONCE(size > PAGE_SIZE - poff))
-               return -EIO;
-       if (WARN_ON_ONCE(size > PAGE_SIZE -
-                        offset_in_page(iomap->inline_data)))
-               return -EIO;
        if (WARN_ON_ONCE(size > iomap->length))
                return -EIO;
        if (offset > 0)
                ifs_alloc(iter->inode, folio, iter->flags);
 
-       addr = kmap_local_folio(folio, offset);
-       memcpy(addr, iomap->inline_data, size);
-       memset(addr + size, 0, PAGE_SIZE - poff - size);
-       kunmap_local(addr);
-       iomap_set_range_uptodate(folio, offset, PAGE_SIZE - poff);
+       folio_fill_tail(folio, offset, iomap->inline_data, size);
+       iomap_set_range_uptodate(folio, offset, folio_size(folio) - offset);
        return 0;
 }
 
diff --git a/include/linux/highmem.h b/include/linux/highmem.h
index 1b81416196dd..0fbb60ffefc9 100644
--- a/include/linux/highmem.h
+++ b/include/linux/highmem.h
@@ -521,6 +521,44 @@ static inline __must_check void *folio_zero_tail(struct 
folio *folio,
        return kaddr;
 }
 
+/**
+ * folio_fill_tail - Copy some data to a folio and pad with zeroes.
+ * @folio: The destination folio.
+ * @offset: The offset into @folio at which to start copying.
+ * @from: The data to copy.
+ * @len: How many bytes of data to copy.
+ *
+ * This function is most useful for filesystems which support inline data.
+ * When they want to copy data from the inode into the page cache, this
+ * function does everything for them.  It supports large folios even on
+ * HIGHMEM configurations.
+ */
+static inline void folio_fill_tail(struct folio *folio, size_t offset,
+               const char *from, size_t len)
+{
+       char *to = kmap_local_folio(folio, offset);
+
+       VM_BUG_ON(offset + len > folio_size(folio));
+
+       if (folio_test_highmem(folio)) {
+               size_t max = PAGE_SIZE - offset_in_page(offset);
+
+               while (len > max) {
+                       memcpy(to, from, max);
+                       kunmap_local(to);
+                       len -= max;
+                       from += max;
+                       offset += max;
+                       max = PAGE_SIZE;
+                       to = kmap_local_folio(folio, offset);
+               }
+       }
+
+       memcpy(to, from, len);
+       to = folio_zero_tail(folio, offset, to);
+       kunmap_local(to);
+}
+
 /**
  * memcpy_from_file_folio - Copy some bytes from a file folio.
  * @to: The destination buffer.
-- 
2.42.0

Reply via email to