[PATCH 16/21] btrfs: lock extent before pages in encoded write

Goldwyn Rodrigues
2023-03-02T22:25:45Z

Lock the extent range while performing direct encoded writes, as opposed
to individual pages.

Signed-off-by: Goldwyn Rodrigues <rgoldwyn@suse.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
---
fs/btrfs/inode.c | 52 +++++++++++++++++++++++++-----------------------
1 file changed, 27 insertions(+), 25 deletions(-)

diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 729def5969d8..70cf852a3efd 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -10289,37 +10289,18 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
pages = kvcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL_ACCOUNT);
if (!pages)
return -ENOMEM;
- for (i = 0; i < nr_pages; i++) {
- size_t bytes = min_t(size_t, PAGE_SIZE, iov_iter_count(from));
- char *kaddr;
-
- pages[i] = alloc_page(GFP_KERNEL_ACCOUNT);
- if (!pages[i]) {
- ret = -ENOMEM;
- goto out_pages;
- }
- kaddr = kmap_local_page(pages[i]);
- if (copy_from_iter(kaddr, bytes, from) != bytes) {
- kunmap_local(kaddr);
- ret = -EFAULT;
- goto out_pages;
- }
- if (bytes < PAGE_SIZE)
- memset(kaddr + bytes, 0, PAGE_SIZE - bytes);
- kunmap_local(kaddr);
- }

for (;;) {
struct btrfs_ordered_extent *ordered;

ret = btrfs_wait_ordered_range(&inode->vfs_inode, start, num_bytes);
if (ret)
- goto out_pages;
+ goto out;
ret = invalidate_inode_pages2_range(inode->vfs_inode.i_mapping,
start >> PAGE_SHIFT,
end >> PAGE_SHIFT);
if (ret)
- goto out_pages;
+ goto out;
lock_extent(io_tree, start, end, &cached_state);
ordered = btrfs_lookup_ordered_range(inode, start, num_bytes);
if (!ordered &&
@@ -10331,6 +10312,26 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
cond_resched();
}

+ for (i = 0; i < nr_pages; i++) {
+ size_t bytes = min_t(size_t, PAGE_SIZE, iov_iter_count(from));
+ char *kaddr;
+
+ pages[i] = alloc_page(GFP_KERNEL_ACCOUNT);
+ if (!pages[i]) {
+ ret = -ENOMEM;
+ goto out_pages;
+ }
+ kaddr = kmap_local_page(pages[i]);
+ if (copy_from_iter(kaddr, bytes, from) != bytes) {
+ kunmap_local(kaddr);
+ ret = -EFAULT;
+ goto out_pages;
+ }
+ if (bytes < PAGE_SIZE)
+ memset(kaddr + bytes, 0, PAGE_SIZE - bytes);
+ kunmap_local(kaddr);
+ }
+
/*
* We don't use the higher-level delalloc space functions because our
* num_bytes and disk_num_bytes are different.
@@ -10389,8 +10390,6 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
if (start + encoded->len > inode->vfs_inode.i_size)
i_size_write(&inode->vfs_inode, start + encoded->len);

- unlock_extent(io_tree, start, end, &cached_state);
-
btrfs_delalloc_release_extents(inode, num_bytes);

if (btrfs_submit_compressed_write(inode, start, num_bytes, ins.objectid,
@@ -10400,6 +10399,9 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
ret = -EIO;
goto out_pages;
}
+
+ unlock_extent(io_tree, start, end, &cached_state);
+
ret = orig_count;
goto out;

@@ -10419,14 +10421,14 @@ ssize_t btrfs_do_encoded_write(struct kiocb *iocb, struct iov_iter *from,
*/
if (!extent_reserved)
btrfs_free_reserved_data_space_noquota(fs_info, disk_num_bytes);
-out_unlock:
- unlock_extent(io_tree, start, end, &cached_state);
out_pages:
for (i = 0; i < nr_pages; i++) {
if (pages[i])
__free_page(pages[i]);
}
kvfree(pages);
+out_unlock:
+ unlock_extent(io_tree, start, end, &cached_state);
out:
if (ret >= 0)
iocb->ki_pos += encoded->len;
--
2.39.2