mm: add folio_fill_tail() and use it in iomap
authorMatthew Wilcox (Oracle) <willy@infradead.org>
Tue, 7 Nov 2023 21:26:41 +0000 (21:26 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Mon, 11 Dec 2023 00:51:36 +0000 (16:51 -0800)
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.

[akpm@linux-foundation.org: fix folio_fill_tail(), per Andreas Gruenbacher]
Link: https://lkml.kernel.org/r/20231107212643.3490372-3-willy@infradead.org
Signed-off-by: Matthew Wilcox (Oracle) <willy@infradead.org>
Reviewed-by: Andreas Gruenbacher <agruenba@redhat.com>
Cc: Andreas Dilger <adilger.kernel@dilger.ca>
Cc: Darrick J. Wong <djwong@kernel.org>
Cc: Theodore Ts'o <tytso@mit.edu>
Cc: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/iomap/buffered-io.c
include/linux/highmem.h

index f72df2b..093c451 100644 (file)
@@ -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;
 }
 
index 5ebd5e4..451c1df 100644 (file)
@@ -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 + len, to + len);
+       kunmap_local(to);
+}
+
 /**
  * memcpy_from_file_folio - Copy some bytes from a file folio.
  * @to: The destination buffer.