btrfs: run btrfs_error_commit_super() early
[ Upstream commit df94a342efb451deb0e32b495d1d6cd4bb3a1648 ] [BUG] Even after all the error fixes related the "ASSERT(list_empty(&fs_info->delayed_iputs));" in close_ctree(), I can still hit it reliably with my experimental 2K block size. [CAUSE] In my case, all the error is triggered after the fs is already in error status. I find the following call trace to be the cause of race: Main thread | endio_write_workers ---------------------------------------------+--------------------------- close_ctree() | |- btrfs_error_commit_super() | | |- btrfs_cleanup_transaction() | | | |- btrfs_destroy_all_ordered_extents() | | | |- btrfs_wait_ordered_roots() | | |- btrfs_run_delayed_iputs() | | | btrfs_finish_ordered_io() | | |- btrfs_put_ordered_extent() | | |- btrfs_add_delayed_iput() |- ASSERT(list_empty(delayed_iputs)) | !!! Triggered !!! The root cause is that, btrfs_wait_ordered_roots() only wait for ordered extents to finish their IOs, not to wait for them to finish and removed. [FIX] Since btrfs_error_commit_super() will flush and wait for all ordered extents, it should be executed early, before we start flushing the workqueues. And since btrfs_error_commit_super() now runs early, there is no need to run btrfs_run_delayed_iputs() inside it, so just remove the btrfs_run_delayed_iputs() call from btrfs_error_commit_super(). Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
1a012fd4eb
commit
de635f9bba
@@ -4313,6 +4313,14 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
|
||||
/* clear out the rbtree of defraggable inodes */
|
||||
btrfs_cleanup_defrag_inodes(fs_info);
|
||||
|
||||
/*
|
||||
* Handle the error fs first, as it will flush and wait for all ordered
|
||||
* extents. This will generate delayed iputs, thus we want to handle
|
||||
* it first.
|
||||
*/
|
||||
if (unlikely(BTRFS_FS_ERROR(fs_info)))
|
||||
btrfs_error_commit_super(fs_info);
|
||||
|
||||
/*
|
||||
* Wait for any fixup workers to complete.
|
||||
* If we don't wait for them here and they are still running by the time
|
||||
@@ -4402,9 +4410,6 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
|
||||
btrfs_err(fs_info, "commit super ret %d", ret);
|
||||
}
|
||||
|
||||
if (BTRFS_FS_ERROR(fs_info))
|
||||
btrfs_error_commit_super(fs_info);
|
||||
|
||||
kthread_stop(fs_info->transaction_kthread);
|
||||
kthread_stop(fs_info->cleaner_kthread);
|
||||
|
||||
@@ -4541,10 +4546,6 @@ static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info)
|
||||
/* cleanup FS via transaction */
|
||||
btrfs_cleanup_transaction(fs_info);
|
||||
|
||||
mutex_lock(&fs_info->cleaner_mutex);
|
||||
btrfs_run_delayed_iputs(fs_info);
|
||||
mutex_unlock(&fs_info->cleaner_mutex);
|
||||
|
||||
down_write(&fs_info->cleanup_work_sem);
|
||||
up_write(&fs_info->cleanup_work_sem);
|
||||
}
|
||||
|
Reference in New Issue
Block a user