|
@@ -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
|
|
* 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_transaction *cur_trans;
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
@@ -67,7 +67,13 @@ loop:
|
|
}
|
|
}
|
|
|
|
|
|
if (fs_info->trans_no_join) {
|
|
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);
|
|
spin_unlock(&fs_info->trans_lock);
|
|
return -EBUSY;
|
|
return -EBUSY;
|
|
}
|
|
}
|
|
@@ -87,6 +93,13 @@ loop:
|
|
}
|
|
}
|
|
spin_unlock(&fs_info->trans_lock);
|
|
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);
|
|
cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS);
|
|
if (!cur_trans)
|
|
if (!cur_trans)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
@@ -340,27 +353,28 @@ again:
|
|
* because we're already holding a ref. We need this because we could
|
|
* 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
|
|
* have raced in and did an fsync() on a file which can kick a commit
|
|
* and then we deadlock with somebody doing a freeze.
|
|
* 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);
|
|
sb_start_intwrite(root->fs_info->sb);
|
|
- }
|
|
|
|
|
|
|
|
if (may_wait_transaction(root, type))
|
|
if (may_wait_transaction(root, type))
|
|
wait_current_trans(root);
|
|
wait_current_trans(root);
|
|
|
|
|
|
do {
|
|
do {
|
|
- ret = join_transaction(root, type == TRANS_JOIN_NOLOCK);
|
|
|
|
|
|
+ ret = join_transaction(root, type);
|
|
if (ret == -EBUSY)
|
|
if (ret == -EBUSY)
|
|
wait_current_trans(root);
|
|
wait_current_trans(root);
|
|
} while (ret == -EBUSY);
|
|
} while (ret == -EBUSY);
|
|
|
|
|
|
if (ret < 0) {
|
|
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);
|
|
kmem_cache_free(btrfs_trans_handle_cachep, h);
|
|
return ERR_PTR(ret);
|
|
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);
|
|
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 */
|
|
/* 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);
|
|
sb_end_intwrite(root->fs_info->sb);
|
|
|
|
|
|
WARN_ON(cur_trans != info->running_transaction);
|
|
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);
|
|
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);
|
|
trace_btrfs_transaction_commit(root);
|
|
|
|
|