Re: [PATCH v7] iomap: make inline data support more flexible

From: Matthew Wilcox
Date: Mon Jul 26 2021 - 08:34:01 EST


On Mon, Jul 26, 2021 at 01:06:11PM +0200, Andreas Gruenbacher wrote:
> @@ -671,11 +683,11 @@ static size_t iomap_write_end_inline(struct inode *inode, struct page *page,
> void *addr;
>
> WARN_ON_ONCE(!PageUptodate(page));
> - BUG_ON(pos + copied > PAGE_SIZE - offset_in_page(iomap->inline_data));
> + BUG_ON(!iomap_inline_data_size_valid(iomap));
>
> flush_dcache_page(page);
> addr = kmap_atomic(page);
> - memcpy(iomap->inline_data + pos, addr + pos, copied);
> + memcpy(iomap_inline_data(iomap, pos), addr + pos, copied);
> kunmap_atomic(addr);
>
> mark_inode_dirty(inode);

Only tangentially related ... why do we memcpy the data into the tail
at write_end() time instead of at writepage() time? I see there's a
workaround for that in gfs2's page_mkwrite():

if (gfs2_is_stuffed(ip)) {
err = gfs2_unstuff_dinode(ip);

(an mmap store cannot change the size of the file, so this would be
unnecessary)

Something like this ...

diff --git a/fs/iomap/buffered-io.c b/fs/iomap/buffered-io.c
index 87ccb3438bec..3aeebe899fc5 100644
--- a/fs/iomap/buffered-io.c
+++ b/fs/iomap/buffered-io.c
@@ -665,9 +665,10 @@ static size_t __iomap_write_end(struct inode *inode, loff_t pos, size_t len,
return copied;
}

-static size_t iomap_write_end_inline(struct inode *inode, struct page *page,
- struct iomap *iomap, loff_t pos, size_t copied)
+static int iomap_write_inline_data(struct inode *inode, struct page *page,
+ struct iomap *iomap)
{
+ size_t size = i_size_read(inode) - page_offset(page);
void *addr;

WARN_ON_ONCE(!PageUptodate(page));
@@ -675,11 +676,10 @@ static size_t iomap_write_end_inline(struct inode *inode, struct page *page,

flush_dcache_page(page);
addr = kmap_atomic(page);
- memcpy(iomap->inline_data + pos, addr + pos, copied);
+ memcpy(iomap->inline_data, addr, size);
kunmap_atomic(addr);

- mark_inode_dirty(inode);
- return copied;
+ return 0;
}

/* Returns the number of bytes copied. May be 0. Cannot be an errno. */
@@ -691,9 +691,7 @@ static size_t iomap_write_end(struct inode *inode, loff_t pos, size_t len,
loff_t old_size = inode->i_size;
size_t ret;

- if (srcmap->type == IOMAP_INLINE) {
- ret = iomap_write_end_inline(inode, page, iomap, pos, copied);
- } else if (srcmap->flags & IOMAP_F_BUFFER_HEAD) {
+ if (srcmap->flags & IOMAP_F_BUFFER_HEAD) {
ret = block_write_end(NULL, inode->i_mapping, pos, len, copied,
page, NULL);
} else {
@@ -1314,6 +1312,9 @@ iomap_writepage_map(struct iomap_writepage_ctx *wpc,

WARN_ON_ONCE(iop && atomic_read(&iop->write_bytes_pending) != 0);

+ if (wpc->iomap.type == IOMAP_INLINE)
+ return iomap_write_inline_data(inode, page, iomap);
+
/*
* Walk through the page to find areas to write back. If we run off the
* end of the current map or find the current map invalid, grab a new
@@ -1328,8 +1329,6 @@ iomap_writepage_map(struct iomap_writepage_ctx *wpc,
error = wpc->ops->map_blocks(wpc, inode, file_offset);
if (error)
break;
- if (WARN_ON_ONCE(wpc->iomap.type == IOMAP_INLINE))
- continue;
if (wpc->iomap.type == IOMAP_HOLE)
continue;
iomap_add_to_ioend(inode, file_offset, page, iop, wpc, wbc,