btrfs: handle completed ordered extents in btrfs_split_ordered_extent
authorChristoph Hellwig <hch@lst.de>
Wed, 24 May 2023 15:03:15 +0000 (17:03 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 19 Jun 2023 11:59:33 +0000 (13:59 +0200)
To delay splitting ordered_extents to I/O completion time we need to be
able to handle fully completed ordered extents in
btrfs_split_ordered_extent.  Besides a bit of accounting this primarily
involved moving over the csums to the split bio for the range that it
covers, which is simple enough because we always have one
btrfs_ordered_sum per bio.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ordered-data.c

index 0dc2bd0..bb76bf0 100644 (file)
@@ -1141,9 +1141,11 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
        struct btrfs_fs_info *fs_info = root->fs_info;
        u64 file_offset = ordered->file_offset;
        u64 disk_bytenr = ordered->disk_bytenr;
-       unsigned long flags = ordered->flags & BTRFS_ORDERED_TYPE_FLAGS;
+       unsigned long flags = ordered->flags;
+       struct btrfs_ordered_sum *sum, *tmpsum;
        struct btrfs_ordered_extent *new;
        struct rb_node *node;
+       u64 offset = 0;
 
        trace_btrfs_ordered_extent_split(inode, ordered);
 
@@ -1155,15 +1157,15 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
         */
        if (WARN_ON_ONCE(len >= ordered->num_bytes))
                return ERR_PTR(-EINVAL);
-       /* We cannot split once ordered extent is past end_bio. */
-       if (WARN_ON_ONCE(ordered->bytes_left != ordered->disk_num_bytes))
-               return ERR_PTR(-EINVAL);
+       /* We cannot split partially completed ordered extents. */
+       if (ordered->bytes_left) {
+               ASSERT(!(flags & ~BTRFS_ORDERED_TYPE_FLAGS));
+               if (WARN_ON_ONCE(ordered->bytes_left != ordered->disk_num_bytes))
+                       return ERR_PTR(-EINVAL);
+       }
        /* We cannot split a compressed ordered extent. */
        if (WARN_ON_ONCE(ordered->disk_num_bytes != ordered->num_bytes))
                return ERR_PTR(-EINVAL);
-       /* Checksum list should be empty. */
-       if (WARN_ON_ONCE(!list_empty(&ordered->list)))
-               return ERR_PTR(-EINVAL);
 
        new = alloc_ordered_extent(inode, file_offset, len, len, disk_bytenr,
                                   len, 0, flags, ordered->compress_type);
@@ -1186,7 +1188,29 @@ struct btrfs_ordered_extent *btrfs_split_ordered_extent(
        ordered->disk_bytenr += len;
        ordered->num_bytes -= len;
        ordered->disk_num_bytes -= len;
-       ordered->bytes_left -= len;
+
+       if (test_bit(BTRFS_ORDERED_IO_DONE, &ordered->flags)) {
+               ASSERT(ordered->bytes_left == 0);
+               new->bytes_left = 0;
+       } else {
+               ordered->bytes_left -= len;
+       }
+
+       if (test_bit(BTRFS_ORDERED_TRUNCATED, &ordered->flags)) {
+               if (ordered->truncated_len > len) {
+                       ordered->truncated_len -= len;
+               } else {
+                       new->truncated_len = ordered->truncated_len;
+                       ordered->truncated_len = 0;
+               }
+       }
+
+       list_for_each_entry_safe(sum, tmpsum, &ordered->list, list) {
+               if (offset == len)
+                       break;
+               list_move_tail(&sum->list, &new->list);
+               offset += sum->len;
+       }
 
        /* Re-insert the node */
        node = tree_insert(&tree->tree, ordered->file_offset, &ordered->rb_node);