@@ -1161,6 +1161,17 @@ int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered,
if (tree->last == node)
tree->last = NULL;
+ if (pre) {
+ spin_unlock_irq(&tree->lock);
+ oe = clone_ordered_extent(ordered, 0, pre);
+ ret = IS_ERR(oe) ? PTR_ERR(oe) : 0;
+ if (!ret && ret_pre)
+ *ret_pre = oe;
+ if (ret)
+ goto out;
+ spin_lock_irq(&tree->lock);
+ }
+
ordered->file_offset += pre;
ordered->disk_bytenr += pre;
ordered->num_bytes -= (pre + post);
@@ -1176,18 +1187,13 @@ int btrfs_split_ordered_extent(struct btrfs_ordered_extent *ordered,
spin_unlock_irq(&tree->lock);
- if (pre) {
- oe = clone_ordered_extent(ordered, 0, pre);
- ret = IS_ERR(oe) ? PTR_ERR(oe) : 0;
- if (!ret && ret_pre)
- *ret_pre = oe;
- }
- if (!ret && post) {
+ if (post) {
oe = clone_ordered_extent(ordered, pre + ordered->disk_num_bytes, post);
ret = IS_ERR(oe) ? PTR_ERR(oe) : 0;
if (!ret && ret_post)
*ret_post = oe;
}
+out:
return ret;
}
if pre != 0 in btrfs_split_ordered_extent, then we do the following: 1. remove ordered (at file_offset) from the rb tree 2. modify file_offset+=pre 3. re-insert ordered 4. clone an ordered extent at offset 0 length pre from ordered. 5. clone an ordered extent for the post range, if necessary. step 4 is not correct, as at this point, the start of ordered is already the end of the desired new pre extent. Further this causes a panic when btrfs_alloc_ordered_extent sees that the node (from the modified and re-inserted ordered) is already present at file_offset + 0 = file_offset. We can fix this by either using a negative offset, or by moving the clone of the pre extent to after we remove the original one, but before we modify and re-insert it. The former feels quite kludgy, as we are "cloning" from outside the range of the ordered extent, so opt for the latter, which does have some locking annoyances. Signed-off-by: Boris Burkov <boris@bur.io> --- fs/btrfs/ordered-data.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-)