Skip to content

Commit 9aa5103

Browse files
fdmananaopsiff
authored andcommitted
btrfs: fix clearing of BTRFS_FS_RELOC_RUNNING if relocation already running
commit 7e5a598 upstream. When starting relocation, at reloc_chunk_start(), if we happen to find the flag BTRFS_FS_RELOC_RUNNING is already set we return an error (-EINPROGRESS) to the callers, however the callers call reloc_chunk_end() which will clear the flag BTRFS_FS_RELOC_RUNNING, which is wrong since relocation was started by another task and still running. Finding the BTRFS_FS_RELOC_RUNNING flag already set is an unexpected scenario, but still our current behaviour is not correct. Fix this by never calling reloc_chunk_end() if reloc_chunk_start() has returned an error, which is what logically makes sense, since the general widespread pattern is to have end functions called only if the counterpart start functions succeeded. This requires changing reloc_chunk_start() to clear BTRFS_FS_RELOC_RUNNING if there's a pending cancel request. Fixes: 907d271 ("btrfs: add cancellable chunk relocation support") CC: [email protected] # 5.15+ Reviewed-by: Boris Burkov <[email protected]> Reviewed-by: Johannes Thumshirn <[email protected]> Reviewed-by: Qu Wenruo <[email protected]> Signed-off-by: Filipe Manana <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> (cherry picked from commit 7c068bcc12d226cd66c1819b163d56eefdf849b4)
1 parent 6dfc9a4 commit 9aa5103

File tree

1 file changed

+7
-6
lines changed

1 file changed

+7
-6
lines changed

fs/btrfs/relocation.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3877,6 +3877,7 @@ static noinline_for_stack struct inode *create_reloc_inode(
38773877
/*
38783878
* Mark start of chunk relocation that is cancellable. Check if the cancellation
38793879
* has been requested meanwhile and don't start in that case.
3880+
* NOTE: if this returns an error, reloc_chunk_end() must not be called.
38803881
*
38813882
* Return:
38823883
* 0 success
@@ -3893,10 +3894,8 @@ static int reloc_chunk_start(struct btrfs_fs_info *fs_info)
38933894

38943895
if (atomic_read(&fs_info->reloc_cancel_req) > 0) {
38953896
btrfs_info(fs_info, "chunk relocation canceled on start");
3896-
/*
3897-
* On cancel, clear all requests but let the caller mark
3898-
* the end after cleanup operations.
3899-
*/
3897+
/* On cancel, clear all requests. */
3898+
clear_and_wake_up_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags);
39003899
atomic_set(&fs_info->reloc_cancel_req, 0);
39013900
return -ECANCELED;
39023901
}
@@ -3905,9 +3904,11 @@ static int reloc_chunk_start(struct btrfs_fs_info *fs_info)
39053904

39063905
/*
39073906
* Mark end of chunk relocation that is cancellable and wake any waiters.
3907+
* NOTE: call only if a previous call to reloc_chunk_start() succeeded.
39083908
*/
39093909
static void reloc_chunk_end(struct btrfs_fs_info *fs_info)
39103910
{
3911+
ASSERT(test_bit(BTRFS_FS_RELOC_RUNNING, &fs_info->flags));
39113912
/* Requested after start, clear bit first so any waiters can continue */
39123913
if (atomic_read(&fs_info->reloc_cancel_req) > 0)
39133914
btrfs_info(fs_info, "chunk relocation canceled during operation");
@@ -4119,9 +4120,9 @@ int btrfs_relocate_block_group(struct btrfs_fs_info *fs_info, u64 group_start)
41194120
if (err && rw)
41204121
btrfs_dec_block_group_ro(rc->block_group);
41214122
iput(rc->data_inode);
4123+
reloc_chunk_end(fs_info);
41224124
out_put_bg:
41234125
btrfs_put_block_group(bg);
4124-
reloc_chunk_end(fs_info);
41254126
free_reloc_control(rc);
41264127
return err;
41274128
}
@@ -4311,8 +4312,8 @@ int btrfs_recover_relocation(struct btrfs_fs_info *fs_info)
43114312
err = ret;
43124313
out_unset:
43134314
unset_reloc_control(rc);
4314-
out_end:
43154315
reloc_chunk_end(fs_info);
4316+
out_end:
43164317
free_reloc_control(rc);
43174318
out:
43184319
free_reloc_roots(&reloc_roots);

0 commit comments

Comments
 (0)