@@ -3345,6 +3345,10 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode, u64 start,
u64 end, int drop_cache);
+int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
+ const u64 start, const u64 end,
+ const bool insert_holes,
+ struct btrfs_trans_handle **trans_out);
int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
struct btrfs_inode *inode, u64 start, u64 end);
int btrfs_release_file(struct inode *inode, struct file *file);
@@ -2452,9 +2452,10 @@ static int btrfs_punch_hole_lock_range(struct inode *inode,
* The respective range must have been previously locked, as well as the inode.
* The end offset is inclusive (last byte of the range).
*/
-static int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
- const u64 start, const u64 end,
- struct btrfs_trans_handle **trans_out)
+int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
+ const u64 start, const u64 end,
+ const bool insert_holes,
+ struct btrfs_trans_handle **trans_out)
{
struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
u64 min_size = btrfs_calc_trans_metadata_size(fs_info, 1);
@@ -2482,9 +2483,14 @@ static int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
/*
* 1 - update the inode
* 1 - removing the extents in the range
- * 1 - adding the hole extent if no_holes isn't set
+ * 1 - adding the hole extent if no_holes isn't set or !insert_holes
+ * (used for extent cloning)
*/
- rsv_count = btrfs_fs_incompat(fs_info, NO_HOLES) ? 2 : 3;
+ if (!btrfs_fs_incompat(fs_info, NO_HOLES) || !insert_holes)
+ rsv_count = 3;
+ else
+ rsv_count = 2;
+
trans = btrfs_start_transaction(root, rsv_count);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
@@ -2507,7 +2513,8 @@ static int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
trans->block_rsv = &fs_info->trans_block_rsv;
- if (cur_offset < drop_end && cur_offset < ino_size) {
+ if (insert_holes && cur_offset < drop_end &&
+ cur_offset < ino_size) {
ret = fill_holes(trans, BTRFS_I(inode), path,
cur_offset, drop_end);
if (ret) {
@@ -2574,7 +2581,7 @@ static int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
* (because it's useless) or if it represents a 0 bytes range (when
* cur_offset == drop_end).
*/
- if (cur_offset < ino_size && cur_offset < drop_end) {
+ if (insert_holes && cur_offset < ino_size && cur_offset < drop_end) {
ret = fill_holes(trans, BTRFS_I(inode), path,
cur_offset, drop_end);
if (ret) {
@@ -2589,7 +2596,7 @@ static int btrfs_punch_hole_range(struct inode *inode, struct btrfs_path *path,
goto out_free;
trans->block_rsv = &fs_info->trans_block_rsv;
- if (ret)
+ if (ret && insert_holes)
btrfs_end_transaction(trans);
else
*trans_out = trans;
@@ -2719,7 +2726,8 @@ static int btrfs_punch_hole(struct inode *inode, loff_t offset, loff_t len)
goto out;
}
- ret = btrfs_punch_hole_range(inode, path, lockstart, lockend, &trans);
+ ret = btrfs_punch_hole_range(inode, path, lockstart, lockend, true,
+ &trans);
btrfs_free_path(path);
if (ret)
goto out;
@@ -3682,17 +3682,6 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
else
drop_start = new_key.offset;
- /*
- * 1 - adjusting old extent (we may have to split it)
- * 1 - add new extent
- * 1 - inode update
- */
- trans = btrfs_start_transaction(root, 3);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- goto out;
- }
-
if (type == BTRFS_FILE_EXTENT_REG ||
type == BTRFS_FILE_EXTENT_PREALLOC) {
/*
@@ -3710,17 +3699,20 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
datal -= off - key.offset;
}
- ret = btrfs_drop_extents(trans, root, inode,
- drop_start,
- new_key.offset + datal,
- 1);
+ trans = NULL;
+ ret = btrfs_punch_hole_range(inode, path,
+ drop_start,
+ new_key.offset + datal - 1,
+ false, &trans);
if (ret) {
- if (ret != -EOPNOTSUPP)
+ if (trans && ret != -EOPNOTSUPP)
btrfs_abort_transaction(trans,
ret);
- btrfs_end_transaction(trans);
+ if (trans)
+ btrfs_end_transaction(trans);
goto out;
}
+ ASSERT(trans != NULL);
ret = btrfs_insert_empty_item(trans, root, path,
&new_key, size);
@@ -3781,12 +3773,27 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
if (comp && (skip || trim)) {
ret = -EINVAL;
- btrfs_end_transaction(trans);
goto out;
}
size -= skip + trim;
datal -= skip + trim;
+ /*
+ * If our extent is inline, we know we will drop
+ * or adjust at most 1 extent item in the
+ * destination root.
+ *
+ * 1 - adjusting old extent (we may have to
+ * split it)
+ * 1 - add new extent
+ * 1 - inode update
+ */
+ trans = btrfs_start_transaction(root, 3);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+
ret = clone_copy_inline_extent(inode,
trans, path,
&new_key,
@@ -3843,24 +3850,22 @@ static int btrfs_clone(struct inode *src, struct inode *inode,
* fully or partially overlaps our cloning range at its end.
*/
btrfs_release_path(path);
+ path->leave_spinning = 0;
- /*
- * 1 - remove extent(s)
- * 1 - inode update
- */
- trans = btrfs_start_transaction(root, 2);
- if (IS_ERR(trans)) {
- ret = PTR_ERR(trans);
- goto out;
- }
- ret = btrfs_drop_extents(trans, root, inode,
- last_dest_end, destoff + len, 1);
+ trans = NULL;
+ ret = btrfs_punch_hole_range(inode, path,
+ last_dest_end, destoff + len - 1,
+ false, &trans);
if (ret) {
- if (ret != -EOPNOTSUPP)
- btrfs_abort_transaction(trans, ret);
- btrfs_end_transaction(trans);
+ if (trans) {
+ if (ret != -EOPNOTSUPP)
+ btrfs_abort_transaction(trans, ret);
+ btrfs_end_transaction(trans);
+ }
goto out;
}
+ ASSERT(trans != NULL);
+
clone_update_extent_map(BTRFS_I(inode), trans, NULL,
last_dest_end,
destoff + len - last_dest_end);