@@ -3853,6 +3853,52 @@ static int open_ctree_fs_root_init(struct btrfs_fs_info *fs_info)
return 0;
}
+static int open_ctree_kthread_init(struct btrfs_fs_info *fs_info)
+{
+ int ret;
+
+ fs_info->cleaner_kthread = kthread_run(cleaner_kthread, fs_info,
+ "btrfs-cleaner");
+ if (IS_ERR(fs_info->cleaner_kthread)) {
+ ret = PTR_ERR(fs_info->cleaner_kthread);
+ return ret;
+ }
+
+ fs_info->transaction_kthread = kthread_run(transaction_kthread,
+ fs_info->tree_root,
+ "btrfs-transaction");
+ if (IS_ERR(fs_info->transaction_kthread)) {
+ kthread_stop(fs_info->cleaner_kthread);
+
+ /*
+ * Cleanup thread may have already started a trans.
+ * The dirtied tree blocks will be invalidated at
+ * open_ctree_btree_inode_exit() thus we don't need to bother.
+ */
+ btrfs_cleanup_transaction(fs_info);
+
+ ret = PTR_ERR(fs_info->cleaner_kthread);
+ return ret;
+ }
+ /*
+ * Mount does not set all options immediately, we can do it now and do
+ * not have to wait for transaction commit
+ */
+ btrfs_apply_pending_changes(fs_info);
+ return 0;
+}
+
+static void open_ctree_kthread_exit(struct btrfs_fs_info *fs_info)
+{
+ kthread_stop(fs_info->transaction_kthread);
+ kthread_stop(fs_info->cleaner_kthread);
+ /*
+ * Cleanup any unfinished transaction started by transaction/cleaner
+ * kthread.
+ */
+ btrfs_cleanup_transaction(fs_info);
+}
+
struct init_sequence {
int (*init_func)(struct btrfs_fs_info *fs_info);
void (*exit_func)(struct btrfs_fs_info *fs_info);
@@ -3897,6 +3943,9 @@ static const struct init_sequence open_ctree_seq[] = {
*/
.init_func = open_ctree_fs_root_init,
.exit_func = btrfs_free_fs_roots,
+ }, {
+ .init_func = open_ctree_kthread_init,
+ .exit_func = open_ctree_kthread_exit,
}
};
@@ -3922,26 +3971,9 @@ int __cold open_ctree(struct super_block *sb, char *options)
open_ctree_res[i] = true;
}
- fs_info->cleaner_kthread = kthread_run(cleaner_kthread, fs_info,
- "btrfs-cleaner");
- if (IS_ERR(fs_info->cleaner_kthread))
- goto fail;
-
- fs_info->transaction_kthread = kthread_run(transaction_kthread,
- fs_info->tree_root,
- "btrfs-transaction");
- if (IS_ERR(fs_info->transaction_kthread))
- goto fail_cleaner;
-
- /*
- * Mount does not set all options immediately, we can do it now and do
- * not have to wait for transaction commit
- */
- btrfs_apply_pending_changes(fs_info);
-
ret = btrfs_read_qgroup_config(fs_info);
if (ret)
- goto fail_trans_kthread;
+ goto fail;
if (btrfs_build_ref_tree(fs_info))
btrfs_err(fs_info, "couldn't build ref tree");
@@ -3993,17 +4025,6 @@ int __cold open_ctree(struct super_block *sb, char *options)
fail_qgroup:
btrfs_free_qgroup_config(fs_info);
-fail_trans_kthread:
- kthread_stop(fs_info->transaction_kthread);
- btrfs_cleanup_transaction(fs_info);
-fail_cleaner:
- kthread_stop(fs_info->cleaner_kthread);
-
- /*
- * make sure we're done with the btree inode before we stop our
- * kthreads
- */
- filemap_write_and_wait(fs_info->btree_inode->i_mapping);
fail:
for (i = ARRAY_SIZE(open_ctree_seq) - 1; i >= 0; i--) {
There are several changes involved: - Change the timing of btrfs_cleanup_transaction() That call is to address any unfinished transaction mostly caused by the cleaner/commit kthread. Thus at exit function and error handling path, we should stop all kthread, then cleanup the unfinished transaction. Not calling it before stopping cleaner thread. - Remove the filemap_write_and_wait() call Now we have open_ctree_btree_inode_exit() call, which will invalidate all dirty pages of btree inode. Thus there is no need to writeback those dirtied tree blocks anymore. Signed-off-by: Qu Wenruo <wqu@suse.com> --- fs/btrfs/disk-io.c | 79 +++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 29 deletions(-)