btrfs: factor out space reservation code from btrfs_buffered_write()
authorQu Wenruo <wqu@suse.com>
Thu, 20 Mar 2025 03:00:59 +0000 (13:30 +1030)
committerDavid Sterba <dsterba@suse.com>
Thu, 15 May 2025 12:30:39 +0000 (14:30 +0200)
Inside the main loop of btrfs_buffered_write(), we have a complex data
and metadata space reservation code, which tries to reserve space for
a COW write, if failed then fallback to check if we can do a NOCOW
write.

Factor out that part of code into a dedicated helper, reserve_space(),
to make the main loop a little easier to read.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/file.c

index 0b0ae95962b686c7abff40d23181b7b8f64f3e45..1234394c76573072c5f43d614953aa09a384e5f0 100644 (file)
@@ -1094,6 +1094,61 @@ static void release_space(struct btrfs_inode *inode, struct extent_changeset *da
        }
 }
 
+/*
+ * Reserve data and metadata space for this buffered write range.
+ *
+ * Return >0 for the number of bytes reserved, which is always block aligned.
+ * Return <0 for error.
+ */
+static ssize_t reserve_space(struct btrfs_inode *inode,
+                            struct extent_changeset **data_reserved,
+                            u64 start, size_t *len, bool nowait,
+                            bool *only_release_metadata)
+{
+       const struct btrfs_fs_info *fs_info = inode->root->fs_info;
+       const unsigned int block_offset = (start & (fs_info->sectorsize - 1));
+       size_t reserve_bytes;
+       int ret;
+
+       ret = btrfs_check_data_free_space(inode, data_reserved, start, *len, nowait);
+       if (ret < 0) {
+               int can_nocow;
+
+               if (nowait && (ret == -ENOSPC || ret == -EAGAIN))
+                       return -EAGAIN;
+
+               /*
+                * If we don't have to COW at the offset, reserve metadata only.
+                * write_bytes may get smaller than requested here.
+                */
+               can_nocow = btrfs_check_nocow_lock(inode, start, len, nowait);
+               if (can_nocow < 0)
+                       ret = can_nocow;
+               if (can_nocow > 0)
+                       ret = 0;
+               if (ret)
+                       return ret;
+               *only_release_metadata = true;
+       }
+
+       reserve_bytes = round_up(*len + block_offset, fs_info->sectorsize);
+       WARN_ON(reserve_bytes == 0);
+       ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes,
+                                             reserve_bytes, nowait);
+       if (ret) {
+               if (!*only_release_metadata)
+                       btrfs_free_reserved_data_space(inode, *data_reserved,
+                                                      start, *len);
+               else
+                       btrfs_check_nocow_unlock(inode);
+
+               if (nowait && ret == -ENOSPC)
+                       ret = -EAGAIN;
+               return ret;
+       }
+       return reserve_bytes;
+}
+
 ssize_t btrfs_buffered_write(struct kiocb *iocb, struct iov_iter *i)
 {
        struct file *file = iocb->ki_filp;
@@ -1159,52 +1214,11 @@ ssize_t btrfs_buffered_write(struct kiocb *iocb, struct iov_iter *i)
                sector_offset = pos & (fs_info->sectorsize - 1);
 
                extent_changeset_release(data_reserved);
-               ret = btrfs_check_data_free_space(BTRFS_I(inode),
-                                                 &data_reserved, pos,
-                                                 write_bytes, nowait);
-               if (ret < 0) {
-                       int can_nocow;
-
-                       if (nowait && (ret == -ENOSPC || ret == -EAGAIN)) {
-                               ret = -EAGAIN;
-                               break;
-                       }
-
-                       /*
-                        * If we don't have to COW at the offset, reserve
-                        * metadata only. write_bytes may get smaller than
-                        * requested here.
-                        */
-                       can_nocow = btrfs_check_nocow_lock(BTRFS_I(inode), pos,
-                                                          &write_bytes, nowait);
-                       if (can_nocow < 0)
-                               ret = can_nocow;
-                       if (can_nocow > 0)
-                               ret = 0;
-                       if (ret)
-                               break;
-                       only_release_metadata = true;
-               }
-
-               reserve_bytes = round_up(write_bytes + sector_offset,
-                                        fs_info->sectorsize);
-               WARN_ON(reserve_bytes == 0);
-               ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode),
-                                                     reserve_bytes,
-                                                     reserve_bytes, nowait);
-               if (ret) {
-                       if (!only_release_metadata)
-                               btrfs_free_reserved_data_space(BTRFS_I(inode),
-                                               data_reserved, pos,
-                                               write_bytes);
-                       else
-                               btrfs_check_nocow_unlock(BTRFS_I(inode));
-
-                       if (nowait && ret == -ENOSPC)
-                               ret = -EAGAIN;
+               ret = reserve_space(BTRFS_I(inode), &data_reserved, pos,
+                                   &write_bytes, nowait, &only_release_metadata);
+               if (ret < 0)
                        break;
-               }
-
+               reserve_bytes = ret;
                release_bytes = reserve_bytes;
 again:
                ret = balance_dirty_pages_ratelimited_flags(inode->i_mapping, bdp_flags);