diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 2c18a4eb4d5a1f68bb7a07bddbc99c8a95084e02..dcaf55695e6f1139f24d5e702b222d62e25d9d41 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1657,9 +1657,10 @@ static int transaction_kthread(void *arg) spin_unlock(&root->fs_info->trans_lock); /* If the file system is aborted, this will always fail. */ - trans = btrfs_join_transaction(root); + trans = btrfs_attach_transaction(root); if (IS_ERR(trans)) { - cannot_commit = true; + if (PTR_ERR(trans) != -ENOENT) + cannot_commit = true; goto sleep; } if (transid == trans->transid) { diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index fb260bbf59c67cb06779732c9a4338f2f38bfc43..f8b803f30ed8c20f403288df0b0ad5eb7dfa4f6c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -854,10 +854,10 @@ int btrfs_sync_fs(struct super_block *sb, int wait) btrfs_wait_ordered_extents(root, 0); - trans = btrfs_join_transaction_freeze(root); + trans = btrfs_attach_transaction(root); if (IS_ERR(trans)) { - /* Frozen, don't bother */ - if (PTR_ERR(trans) == -EPERM) + /* no transaction, don't bother */ + if (PTR_ERR(trans) == -ENOENT) return 0; return PTR_ERR(trans); } @@ -1511,7 +1511,17 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd, static int btrfs_freeze(struct super_block *sb) { - return 0; + struct btrfs_trans_handle *trans; + struct btrfs_root *root = btrfs_sb(sb)->tree_root; + + trans = btrfs_attach_transaction(root); + if (IS_ERR(trans)) { + /* no transaction, don't bother */ + if (PTR_ERR(trans) == -ENOENT) + return 0; + return PTR_ERR(trans); + } + return btrfs_commit_transaction(trans, root); } static int btrfs_unfreeze(struct super_block *sb) diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index d0a2b7e493814c61a28c21f3e636b6b1a1d31dd1..69139a356f7188593bb73c96cfcce4dd9071ec01 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -53,7 +53,7 @@ static noinline void switch_commit_root(struct btrfs_root *root) /* * either allocate a new transaction or hop into the existing one */ -static noinline int join_transaction(struct btrfs_root *root, int nofail) +static noinline int join_transaction(struct btrfs_root *root, int type) { struct btrfs_transaction *cur_trans; struct btrfs_fs_info *fs_info = root->fs_info; @@ -67,7 +67,13 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail) } if (fs_info->trans_no_join) { - if (!nofail) { + /* + * If we are JOIN_NOLOCK we're already committing a current + * transaction, we just need a handle to deal with something + * when committing the transaction, such as inode cache and + * space cache. It is a special case. + */ + if (type != TRANS_JOIN_NOLOCK) { spin_unlock(&fs_info->trans_lock); return -EBUSY; } @@ -87,6 +93,13 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail) } spin_unlock(&fs_info->trans_lock); + /* + * If we are ATTACH, we just want to catch the current transaction, + * and commit it. If there is no transaction, just return ENOENT. + */ + if (type == TRANS_ATTACH) + return -ENOENT; + cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS); if (!cur_trans) return -ENOMEM; @@ -340,27 +353,28 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root, * because we're already holding a ref. We need this because we could * have raced in and did an fsync() on a file which can kick a commit * and then we deadlock with somebody doing a freeze. + * + * If we are ATTACH, it means we just want to catch the current + * transaction and commit it, so we needn't do sb_start_intwrite(). */ - if (type != TRANS_JOIN_NOLOCK && - !__sb_start_write(root->fs_info->sb, SB_FREEZE_FS, false)) { - if (type == TRANS_JOIN_FREEZE) { - kmem_cache_free(btrfs_trans_handle_cachep, h); - return ERR_PTR(-EPERM); - } + if (type < TRANS_JOIN_NOLOCK) sb_start_intwrite(root->fs_info->sb); - } if (may_wait_transaction(root, type)) wait_current_trans(root); do { - ret = join_transaction(root, type == TRANS_JOIN_NOLOCK); + ret = join_transaction(root, type); if (ret == -EBUSY) wait_current_trans(root); } while (ret == -EBUSY); if (ret < 0) { - sb_end_intwrite(root->fs_info->sb); + /* We must get the transaction if we are JOIN_NOLOCK. */ + BUG_ON(type == TRANS_JOIN_NOLOCK); + + if (type < TRANS_JOIN_NOLOCK) + sb_end_intwrite(root->fs_info->sb); kmem_cache_free(btrfs_trans_handle_cachep, h); return ERR_PTR(ret); } @@ -432,9 +446,9 @@ struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *root return start_transaction(root, 0, TRANS_USERSPACE, 0); } -struct btrfs_trans_handle *btrfs_join_transaction_freeze(struct btrfs_root *root) +struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root) { - return start_transaction(root, 0, TRANS_JOIN_FREEZE, 0); + return start_transaction(root, 0, TRANS_ATTACH, 0); } /* wait for a transaction commit to be fully complete */ @@ -605,7 +619,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, } } - if (lock) + if (trans->type < TRANS_JOIN_NOLOCK) sb_end_intwrite(root->fs_info->sb); WARN_ON(cur_trans != info->running_transaction); @@ -1678,7 +1692,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, put_transaction(cur_trans); put_transaction(cur_trans); - sb_end_intwrite(root->fs_info->sb); + if (trans->type < TRANS_JOIN_NOLOCK) + sb_end_intwrite(root->fs_info->sb); trace_btrfs_transaction_commit(root); diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 0630bd19396a4ab5a0a681cb90707f747767f218..80961947a6b27df59273f080f9f19316c8d98ffc 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -52,7 +52,7 @@ enum btrfs_trans_type { TRANS_JOIN, TRANS_USERSPACE, TRANS_JOIN_NOLOCK, - TRANS_JOIN_FREEZE, + TRANS_ATTACH, }; struct btrfs_trans_handle { @@ -109,7 +109,7 @@ struct btrfs_trans_handle *btrfs_start_transaction_noflush( struct btrfs_root *root, int num_items); struct btrfs_trans_handle *btrfs_join_transaction(struct btrfs_root *root); struct btrfs_trans_handle *btrfs_join_transaction_nolock(struct btrfs_root *root); -struct btrfs_trans_handle *btrfs_join_transaction_freeze(struct btrfs_root *root); +struct btrfs_trans_handle *btrfs_attach_transaction(struct btrfs_root *root); struct btrfs_trans_handle *btrfs_start_ioctl_transaction(struct btrfs_root *root); int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid); int btrfs_write_and_wait_transaction(struct btrfs_trans_handle *trans,