|
@@ -34,6 +34,7 @@ static noinline void put_transaction(struct btrfs_transaction *transaction)
|
|
{
|
|
{
|
|
WARN_ON(atomic_read(&transaction->use_count) == 0);
|
|
WARN_ON(atomic_read(&transaction->use_count) == 0);
|
|
if (atomic_dec_and_test(&transaction->use_count)) {
|
|
if (atomic_dec_and_test(&transaction->use_count)) {
|
|
|
|
+ BUG_ON(!list_empty(&transaction->list));
|
|
memset(transaction, 0, sizeof(*transaction));
|
|
memset(transaction, 0, sizeof(*transaction));
|
|
kmem_cache_free(btrfs_transaction_cachep, transaction);
|
|
kmem_cache_free(btrfs_transaction_cachep, transaction);
|
|
}
|
|
}
|
|
@@ -48,47 +49,73 @@ 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)
|
|
|
|
|
|
+static noinline int join_transaction(struct btrfs_root *root, int nofail)
|
|
{
|
|
{
|
|
struct btrfs_transaction *cur_trans;
|
|
struct btrfs_transaction *cur_trans;
|
|
|
|
+
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
|
|
+ if (root->fs_info->trans_no_join) {
|
|
|
|
+ if (!nofail) {
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
+ return -EBUSY;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
cur_trans = root->fs_info->running_transaction;
|
|
cur_trans = root->fs_info->running_transaction;
|
|
- if (!cur_trans) {
|
|
|
|
- cur_trans = kmem_cache_alloc(btrfs_transaction_cachep,
|
|
|
|
- GFP_NOFS);
|
|
|
|
- if (!cur_trans)
|
|
|
|
- return -ENOMEM;
|
|
|
|
- root->fs_info->generation++;
|
|
|
|
- atomic_set(&cur_trans->num_writers, 1);
|
|
|
|
- cur_trans->num_joined = 0;
|
|
|
|
- cur_trans->transid = root->fs_info->generation;
|
|
|
|
- init_waitqueue_head(&cur_trans->writer_wait);
|
|
|
|
- init_waitqueue_head(&cur_trans->commit_wait);
|
|
|
|
- cur_trans->in_commit = 0;
|
|
|
|
- cur_trans->blocked = 0;
|
|
|
|
- atomic_set(&cur_trans->use_count, 1);
|
|
|
|
- cur_trans->commit_done = 0;
|
|
|
|
- cur_trans->start_time = get_seconds();
|
|
|
|
-
|
|
|
|
- cur_trans->delayed_refs.root = RB_ROOT;
|
|
|
|
- cur_trans->delayed_refs.num_entries = 0;
|
|
|
|
- cur_trans->delayed_refs.num_heads_ready = 0;
|
|
|
|
- cur_trans->delayed_refs.num_heads = 0;
|
|
|
|
- cur_trans->delayed_refs.flushing = 0;
|
|
|
|
- cur_trans->delayed_refs.run_delayed_start = 0;
|
|
|
|
- spin_lock_init(&cur_trans->delayed_refs.lock);
|
|
|
|
-
|
|
|
|
- INIT_LIST_HEAD(&cur_trans->pending_snapshots);
|
|
|
|
- list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
|
|
|
|
- extent_io_tree_init(&cur_trans->dirty_pages,
|
|
|
|
- root->fs_info->btree_inode->i_mapping,
|
|
|
|
- GFP_NOFS);
|
|
|
|
- spin_lock(&root->fs_info->new_trans_lock);
|
|
|
|
- root->fs_info->running_transaction = cur_trans;
|
|
|
|
- spin_unlock(&root->fs_info->new_trans_lock);
|
|
|
|
- } else {
|
|
|
|
|
|
+ if (cur_trans) {
|
|
|
|
+ atomic_inc(&cur_trans->use_count);
|
|
|
|
+ atomic_inc(&cur_trans->num_writers);
|
|
|
|
+ cur_trans->num_joined++;
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
+
|
|
|
|
+ cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS);
|
|
|
|
+ if (!cur_trans)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
|
|
+ if (root->fs_info->running_transaction) {
|
|
|
|
+ kmem_cache_free(btrfs_transaction_cachep, cur_trans);
|
|
|
|
+ cur_trans = root->fs_info->running_transaction;
|
|
|
|
+ atomic_inc(&cur_trans->use_count);
|
|
atomic_inc(&cur_trans->num_writers);
|
|
atomic_inc(&cur_trans->num_writers);
|
|
cur_trans->num_joined++;
|
|
cur_trans->num_joined++;
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
|
|
+ atomic_set(&cur_trans->num_writers, 1);
|
|
|
|
+ cur_trans->num_joined = 0;
|
|
|
|
+ init_waitqueue_head(&cur_trans->writer_wait);
|
|
|
|
+ init_waitqueue_head(&cur_trans->commit_wait);
|
|
|
|
+ cur_trans->in_commit = 0;
|
|
|
|
+ cur_trans->blocked = 0;
|
|
|
|
+ /*
|
|
|
|
+ * One for this trans handle, one so it will live on until we
|
|
|
|
+ * commit the transaction.
|
|
|
|
+ */
|
|
|
|
+ atomic_set(&cur_trans->use_count, 2);
|
|
|
|
+ cur_trans->commit_done = 0;
|
|
|
|
+ cur_trans->start_time = get_seconds();
|
|
|
|
+
|
|
|
|
+ cur_trans->delayed_refs.root = RB_ROOT;
|
|
|
|
+ cur_trans->delayed_refs.num_entries = 0;
|
|
|
|
+ cur_trans->delayed_refs.num_heads_ready = 0;
|
|
|
|
+ cur_trans->delayed_refs.num_heads = 0;
|
|
|
|
+ cur_trans->delayed_refs.flushing = 0;
|
|
|
|
+ cur_trans->delayed_refs.run_delayed_start = 0;
|
|
|
|
+ spin_lock_init(&cur_trans->commit_lock);
|
|
|
|
+ spin_lock_init(&cur_trans->delayed_refs.lock);
|
|
|
|
+
|
|
|
|
+ INIT_LIST_HEAD(&cur_trans->pending_snapshots);
|
|
|
|
+ list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
|
|
|
|
+ extent_io_tree_init(&cur_trans->dirty_pages,
|
|
|
|
+ root->fs_info->btree_inode->i_mapping,
|
|
|
|
+ GFP_NOFS);
|
|
|
|
+ root->fs_info->generation++;
|
|
|
|
+ cur_trans->transid = root->fs_info->generation;
|
|
|
|
+ root->fs_info->running_transaction = cur_trans;
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -99,39 +126,28 @@ static noinline int join_transaction(struct btrfs_root *root)
|
|
* to make sure the old root from before we joined the transaction is deleted
|
|
* to make sure the old root from before we joined the transaction is deleted
|
|
* when the transaction commits
|
|
* when the transaction commits
|
|
*/
|
|
*/
|
|
-static noinline int record_root_in_trans(struct btrfs_trans_handle *trans,
|
|
|
|
- struct btrfs_root *root)
|
|
|
|
|
|
+int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
|
|
|
|
+ struct btrfs_root *root)
|
|
{
|
|
{
|
|
if (root->ref_cows && root->last_trans < trans->transid) {
|
|
if (root->ref_cows && root->last_trans < trans->transid) {
|
|
WARN_ON(root == root->fs_info->extent_root);
|
|
WARN_ON(root == root->fs_info->extent_root);
|
|
WARN_ON(root->commit_root != root->node);
|
|
WARN_ON(root->commit_root != root->node);
|
|
|
|
|
|
|
|
+ spin_lock(&root->fs_info->fs_roots_radix_lock);
|
|
|
|
+ if (root->last_trans == trans->transid) {
|
|
|
|
+ spin_unlock(&root->fs_info->fs_roots_radix_lock);
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ root->last_trans = trans->transid;
|
|
radix_tree_tag_set(&root->fs_info->fs_roots_radix,
|
|
radix_tree_tag_set(&root->fs_info->fs_roots_radix,
|
|
(unsigned long)root->root_key.objectid,
|
|
(unsigned long)root->root_key.objectid,
|
|
BTRFS_ROOT_TRANS_TAG);
|
|
BTRFS_ROOT_TRANS_TAG);
|
|
- root->last_trans = trans->transid;
|
|
|
|
|
|
+ spin_unlock(&root->fs_info->fs_roots_radix_lock);
|
|
btrfs_init_reloc_root(trans, root);
|
|
btrfs_init_reloc_root(trans, root);
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
-int btrfs_record_root_in_trans(struct btrfs_trans_handle *trans,
|
|
|
|
- struct btrfs_root *root)
|
|
|
|
-{
|
|
|
|
- if (!root->ref_cows)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
- if (root->last_trans == trans->transid) {
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- record_root_in_trans(trans, root);
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
/* wait for commit against the current transaction to become unblocked
|
|
/* wait for commit against the current transaction to become unblocked
|
|
* when this is done, it is safe to start a new transaction, but the current
|
|
* when this is done, it is safe to start a new transaction, but the current
|
|
* transaction might not be fully on disk.
|
|
* transaction might not be fully on disk.
|
|
@@ -140,21 +156,23 @@ static void wait_current_trans(struct btrfs_root *root)
|
|
{
|
|
{
|
|
struct btrfs_transaction *cur_trans;
|
|
struct btrfs_transaction *cur_trans;
|
|
|
|
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
cur_trans = root->fs_info->running_transaction;
|
|
cur_trans = root->fs_info->running_transaction;
|
|
if (cur_trans && cur_trans->blocked) {
|
|
if (cur_trans && cur_trans->blocked) {
|
|
DEFINE_WAIT(wait);
|
|
DEFINE_WAIT(wait);
|
|
atomic_inc(&cur_trans->use_count);
|
|
atomic_inc(&cur_trans->use_count);
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
while (1) {
|
|
while (1) {
|
|
prepare_to_wait(&root->fs_info->transaction_wait, &wait,
|
|
prepare_to_wait(&root->fs_info->transaction_wait, &wait,
|
|
TASK_UNINTERRUPTIBLE);
|
|
TASK_UNINTERRUPTIBLE);
|
|
if (!cur_trans->blocked)
|
|
if (!cur_trans->blocked)
|
|
break;
|
|
break;
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
schedule();
|
|
schedule();
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
}
|
|
}
|
|
finish_wait(&root->fs_info->transaction_wait, &wait);
|
|
finish_wait(&root->fs_info->transaction_wait, &wait);
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
|
|
+ } else {
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -167,10 +185,16 @@ enum btrfs_trans_type {
|
|
|
|
|
|
static int may_wait_transaction(struct btrfs_root *root, int type)
|
|
static int may_wait_transaction(struct btrfs_root *root, int type)
|
|
{
|
|
{
|
|
- if (!root->fs_info->log_root_recovering &&
|
|
|
|
- ((type == TRANS_START && !root->fs_info->open_ioctl_trans) ||
|
|
|
|
- type == TRANS_USERSPACE))
|
|
|
|
|
|
+ if (root->fs_info->log_root_recovering)
|
|
|
|
+ return 0;
|
|
|
|
+
|
|
|
|
+ if (type == TRANS_USERSPACE)
|
|
|
|
+ return 1;
|
|
|
|
+
|
|
|
|
+ if (type == TRANS_START &&
|
|
|
|
+ !atomic_read(&root->fs_info->open_ioctl_trans))
|
|
return 1;
|
|
return 1;
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -198,23 +222,21 @@ again:
|
|
if (!h)
|
|
if (!h)
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
- if (type != TRANS_JOIN_NOLOCK)
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
if (may_wait_transaction(root, type))
|
|
if (may_wait_transaction(root, type))
|
|
wait_current_trans(root);
|
|
wait_current_trans(root);
|
|
|
|
|
|
- ret = join_transaction(root);
|
|
|
|
|
|
+ do {
|
|
|
|
+ ret = join_transaction(root, type == TRANS_JOIN_NOLOCK);
|
|
|
|
+ if (ret == -EBUSY)
|
|
|
|
+ wait_current_trans(root);
|
|
|
|
+ } while (ret == -EBUSY);
|
|
|
|
+
|
|
if (ret < 0) {
|
|
if (ret < 0) {
|
|
kmem_cache_free(btrfs_trans_handle_cachep, h);
|
|
kmem_cache_free(btrfs_trans_handle_cachep, h);
|
|
- if (type != TRANS_JOIN_NOLOCK)
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
return ERR_PTR(ret);
|
|
return ERR_PTR(ret);
|
|
}
|
|
}
|
|
|
|
|
|
cur_trans = root->fs_info->running_transaction;
|
|
cur_trans = root->fs_info->running_transaction;
|
|
- atomic_inc(&cur_trans->use_count);
|
|
|
|
- if (type != TRANS_JOIN_NOLOCK)
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
|
|
h->transid = cur_trans->transid;
|
|
h->transid = cur_trans->transid;
|
|
h->transaction = cur_trans;
|
|
h->transaction = cur_trans;
|
|
@@ -253,11 +275,7 @@ again:
|
|
}
|
|
}
|
|
|
|
|
|
got_it:
|
|
got_it:
|
|
- if (type != TRANS_JOIN_NOLOCK)
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
- record_root_in_trans(h, root);
|
|
|
|
- if (type != TRANS_JOIN_NOLOCK)
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+ btrfs_record_root_in_trans(h, root);
|
|
|
|
|
|
if (!current->journal_info && type != TRANS_USERSPACE)
|
|
if (!current->journal_info && type != TRANS_USERSPACE)
|
|
current->journal_info = h;
|
|
current->journal_info = h;
|
|
@@ -289,17 +307,13 @@ static noinline int wait_for_commit(struct btrfs_root *root,
|
|
struct btrfs_transaction *commit)
|
|
struct btrfs_transaction *commit)
|
|
{
|
|
{
|
|
DEFINE_WAIT(wait);
|
|
DEFINE_WAIT(wait);
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
while (!commit->commit_done) {
|
|
while (!commit->commit_done) {
|
|
prepare_to_wait(&commit->commit_wait, &wait,
|
|
prepare_to_wait(&commit->commit_wait, &wait,
|
|
TASK_UNINTERRUPTIBLE);
|
|
TASK_UNINTERRUPTIBLE);
|
|
if (commit->commit_done)
|
|
if (commit->commit_done)
|
|
break;
|
|
break;
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
schedule();
|
|
schedule();
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
}
|
|
}
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
finish_wait(&commit->commit_wait, &wait);
|
|
finish_wait(&commit->commit_wait, &wait);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -309,50 +323,49 @@ int btrfs_wait_for_commit(struct btrfs_root *root, u64 transid)
|
|
struct btrfs_transaction *cur_trans = NULL, *t;
|
|
struct btrfs_transaction *cur_trans = NULL, *t;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
-
|
|
|
|
ret = 0;
|
|
ret = 0;
|
|
if (transid) {
|
|
if (transid) {
|
|
if (transid <= root->fs_info->last_trans_committed)
|
|
if (transid <= root->fs_info->last_trans_committed)
|
|
- goto out_unlock;
|
|
|
|
|
|
+ goto out;
|
|
|
|
|
|
/* find specified transaction */
|
|
/* find specified transaction */
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
list_for_each_entry(t, &root->fs_info->trans_list, list) {
|
|
list_for_each_entry(t, &root->fs_info->trans_list, list) {
|
|
if (t->transid == transid) {
|
|
if (t->transid == transid) {
|
|
cur_trans = t;
|
|
cur_trans = t;
|
|
|
|
+ atomic_inc(&cur_trans->use_count);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
if (t->transid > transid)
|
|
if (t->transid > transid)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
ret = -EINVAL;
|
|
ret = -EINVAL;
|
|
if (!cur_trans)
|
|
if (!cur_trans)
|
|
- goto out_unlock; /* bad transid */
|
|
|
|
|
|
+ goto out; /* bad transid */
|
|
} else {
|
|
} else {
|
|
/* find newest transaction that is committing | committed */
|
|
/* find newest transaction that is committing | committed */
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
list_for_each_entry_reverse(t, &root->fs_info->trans_list,
|
|
list_for_each_entry_reverse(t, &root->fs_info->trans_list,
|
|
list) {
|
|
list) {
|
|
if (t->in_commit) {
|
|
if (t->in_commit) {
|
|
if (t->commit_done)
|
|
if (t->commit_done)
|
|
- goto out_unlock;
|
|
|
|
|
|
+ goto out;
|
|
cur_trans = t;
|
|
cur_trans = t;
|
|
|
|
+ atomic_inc(&cur_trans->use_count);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
if (!cur_trans)
|
|
if (!cur_trans)
|
|
- goto out_unlock; /* nothing committing|committed */
|
|
|
|
|
|
+ goto out; /* nothing committing|committed */
|
|
}
|
|
}
|
|
|
|
|
|
- atomic_inc(&cur_trans->use_count);
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
-
|
|
|
|
wait_for_commit(root, cur_trans);
|
|
wait_for_commit(root, cur_trans);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
ret = 0;
|
|
ret = 0;
|
|
-out_unlock:
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+out:
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -401,10 +414,8 @@ harder:
|
|
|
|
|
|
void btrfs_throttle(struct btrfs_root *root)
|
|
void btrfs_throttle(struct btrfs_root *root)
|
|
{
|
|
{
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
- if (!root->fs_info->open_ioctl_trans)
|
|
|
|
|
|
+ if (!atomic_read(&root->fs_info->open_ioctl_trans))
|
|
wait_current_trans(root);
|
|
wait_current_trans(root);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static int should_end_transaction(struct btrfs_trans_handle *trans,
|
|
static int should_end_transaction(struct btrfs_trans_handle *trans,
|
|
@@ -422,6 +433,7 @@ int btrfs_should_end_transaction(struct btrfs_trans_handle *trans,
|
|
struct btrfs_transaction *cur_trans = trans->transaction;
|
|
struct btrfs_transaction *cur_trans = trans->transaction;
|
|
int updates;
|
|
int updates;
|
|
|
|
|
|
|
|
+ smp_mb();
|
|
if (cur_trans->blocked || cur_trans->delayed_refs.flushing)
|
|
if (cur_trans->blocked || cur_trans->delayed_refs.flushing)
|
|
return 1;
|
|
return 1;
|
|
|
|
|
|
@@ -467,9 +479,11 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
btrfs_trans_release_metadata(trans, root);
|
|
btrfs_trans_release_metadata(trans, root);
|
|
|
|
|
|
- if (lock && !root->fs_info->open_ioctl_trans &&
|
|
|
|
- should_end_transaction(trans, root))
|
|
|
|
|
|
+ if (lock && !atomic_read(&root->fs_info->open_ioctl_trans) &&
|
|
|
|
+ should_end_transaction(trans, root)) {
|
|
trans->transaction->blocked = 1;
|
|
trans->transaction->blocked = 1;
|
|
|
|
+ smp_wmb();
|
|
|
|
+ }
|
|
|
|
|
|
if (lock && cur_trans->blocked && !cur_trans->in_commit) {
|
|
if (lock && cur_trans->blocked && !cur_trans->in_commit) {
|
|
if (throttle)
|
|
if (throttle)
|
|
@@ -739,9 +753,9 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
|
|
*/
|
|
*/
|
|
int btrfs_add_dead_root(struct btrfs_root *root)
|
|
int btrfs_add_dead_root(struct btrfs_root *root)
|
|
{
|
|
{
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
list_add(&root->root_list, &root->fs_info->dead_roots);
|
|
list_add(&root->root_list, &root->fs_info->dead_roots);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -757,6 +771,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
|
|
int ret;
|
|
int ret;
|
|
int err = 0;
|
|
int err = 0;
|
|
|
|
|
|
|
|
+ spin_lock(&fs_info->fs_roots_radix_lock);
|
|
while (1) {
|
|
while (1) {
|
|
ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_radix,
|
|
ret = radix_tree_gang_lookup_tag(&fs_info->fs_roots_radix,
|
|
(void **)gang, 0,
|
|
(void **)gang, 0,
|
|
@@ -769,6 +784,7 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
|
|
radix_tree_tag_clear(&fs_info->fs_roots_radix,
|
|
radix_tree_tag_clear(&fs_info->fs_roots_radix,
|
|
(unsigned long)root->root_key.objectid,
|
|
(unsigned long)root->root_key.objectid,
|
|
BTRFS_ROOT_TRANS_TAG);
|
|
BTRFS_ROOT_TRANS_TAG);
|
|
|
|
+ spin_unlock(&fs_info->fs_roots_radix_lock);
|
|
|
|
|
|
btrfs_free_log(trans, root);
|
|
btrfs_free_log(trans, root);
|
|
btrfs_update_reloc_root(trans, root);
|
|
btrfs_update_reloc_root(trans, root);
|
|
@@ -783,10 +799,12 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
|
|
err = btrfs_update_root(trans, fs_info->tree_root,
|
|
err = btrfs_update_root(trans, fs_info->tree_root,
|
|
&root->root_key,
|
|
&root->root_key,
|
|
&root->root_item);
|
|
&root->root_item);
|
|
|
|
+ spin_lock(&fs_info->fs_roots_radix_lock);
|
|
if (err)
|
|
if (err)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ spin_unlock(&fs_info->fs_roots_radix_lock);
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -972,7 +990,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|
parent = dget_parent(dentry);
|
|
parent = dget_parent(dentry);
|
|
parent_inode = parent->d_inode;
|
|
parent_inode = parent->d_inode;
|
|
parent_root = BTRFS_I(parent_inode)->root;
|
|
parent_root = BTRFS_I(parent_inode)->root;
|
|
- record_root_in_trans(trans, parent_root);
|
|
|
|
|
|
+ btrfs_record_root_in_trans(trans, parent_root);
|
|
|
|
|
|
/*
|
|
/*
|
|
* insert the directory item
|
|
* insert the directory item
|
|
@@ -990,7 +1008,7 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
|
|
ret = btrfs_update_inode(trans, parent_root, parent_inode);
|
|
ret = btrfs_update_inode(trans, parent_root, parent_inode);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
|
|
|
|
- record_root_in_trans(trans, root);
|
|
|
|
|
|
+ btrfs_record_root_in_trans(trans, root);
|
|
btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
|
|
btrfs_set_root_last_snapshot(&root->root_item, trans->transid);
|
|
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
|
|
memcpy(new_root_item, &root->root_item, sizeof(*new_root_item));
|
|
btrfs_check_and_init_root_item(new_root_item);
|
|
btrfs_check_and_init_root_item(new_root_item);
|
|
@@ -1080,20 +1098,20 @@ static void update_super_roots(struct btrfs_root *root)
|
|
int btrfs_transaction_in_commit(struct btrfs_fs_info *info)
|
|
int btrfs_transaction_in_commit(struct btrfs_fs_info *info)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
- spin_lock(&info->new_trans_lock);
|
|
|
|
|
|
+ spin_lock(&info->trans_lock);
|
|
if (info->running_transaction)
|
|
if (info->running_transaction)
|
|
ret = info->running_transaction->in_commit;
|
|
ret = info->running_transaction->in_commit;
|
|
- spin_unlock(&info->new_trans_lock);
|
|
|
|
|
|
+ spin_unlock(&info->trans_lock);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
int btrfs_transaction_blocked(struct btrfs_fs_info *info)
|
|
int btrfs_transaction_blocked(struct btrfs_fs_info *info)
|
|
{
|
|
{
|
|
int ret = 0;
|
|
int ret = 0;
|
|
- spin_lock(&info->new_trans_lock);
|
|
|
|
|
|
+ spin_lock(&info->trans_lock);
|
|
if (info->running_transaction)
|
|
if (info->running_transaction)
|
|
ret = info->running_transaction->blocked;
|
|
ret = info->running_transaction->blocked;
|
|
- spin_unlock(&info->new_trans_lock);
|
|
|
|
|
|
+ spin_unlock(&info->trans_lock);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1117,9 +1135,7 @@ static void wait_current_trans_commit_start(struct btrfs_root *root,
|
|
&wait);
|
|
&wait);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
schedule();
|
|
schedule();
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
|
|
finish_wait(&root->fs_info->transaction_blocked_wait, &wait);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -1145,9 +1161,7 @@ static void wait_current_trans_commit_start_and_unblock(struct btrfs_root *root,
|
|
&wait);
|
|
&wait);
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
schedule();
|
|
schedule();
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
finish_wait(&root->fs_info->transaction_wait,
|
|
finish_wait(&root->fs_info->transaction_wait,
|
|
&wait);
|
|
&wait);
|
|
}
|
|
}
|
|
@@ -1193,22 +1207,18 @@ int btrfs_commit_transaction_async(struct btrfs_trans_handle *trans,
|
|
}
|
|
}
|
|
|
|
|
|
/* take transaction reference */
|
|
/* take transaction reference */
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
cur_trans = trans->transaction;
|
|
cur_trans = trans->transaction;
|
|
atomic_inc(&cur_trans->use_count);
|
|
atomic_inc(&cur_trans->use_count);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
btrfs_end_transaction(trans, root);
|
|
schedule_delayed_work(&ac->work, 0);
|
|
schedule_delayed_work(&ac->work, 0);
|
|
|
|
|
|
/* wait for transaction to start and unblock */
|
|
/* wait for transaction to start and unblock */
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
if (wait_for_unblock)
|
|
if (wait_for_unblock)
|
|
wait_current_trans_commit_start_and_unblock(root, cur_trans);
|
|
wait_current_trans_commit_start_and_unblock(root, cur_trans);
|
|
else
|
|
else
|
|
wait_current_trans_commit_start(root, cur_trans);
|
|
wait_current_trans_commit_start(root, cur_trans);
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -1252,38 +1262,41 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
ret = btrfs_run_delayed_refs(trans, root, 0);
|
|
ret = btrfs_run_delayed_refs(trans, root, 0);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_lock(&cur_trans->commit_lock);
|
|
if (cur_trans->in_commit) {
|
|
if (cur_trans->in_commit) {
|
|
|
|
+ spin_unlock(&cur_trans->commit_lock);
|
|
atomic_inc(&cur_trans->use_count);
|
|
atomic_inc(&cur_trans->use_count);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
btrfs_end_transaction(trans, root);
|
|
btrfs_end_transaction(trans, root);
|
|
|
|
|
|
ret = wait_for_commit(root, cur_trans);
|
|
ret = wait_for_commit(root, cur_trans);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
trans->transaction->in_commit = 1;
|
|
trans->transaction->in_commit = 1;
|
|
trans->transaction->blocked = 1;
|
|
trans->transaction->blocked = 1;
|
|
|
|
+ spin_unlock(&cur_trans->commit_lock);
|
|
wake_up(&root->fs_info->transaction_blocked_wait);
|
|
wake_up(&root->fs_info->transaction_blocked_wait);
|
|
|
|
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
if (cur_trans->list.prev != &root->fs_info->trans_list) {
|
|
if (cur_trans->list.prev != &root->fs_info->trans_list) {
|
|
prev_trans = list_entry(cur_trans->list.prev,
|
|
prev_trans = list_entry(cur_trans->list.prev,
|
|
struct btrfs_transaction, list);
|
|
struct btrfs_transaction, list);
|
|
if (!prev_trans->commit_done) {
|
|
if (!prev_trans->commit_done) {
|
|
atomic_inc(&prev_trans->use_count);
|
|
atomic_inc(&prev_trans->use_count);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
|
|
wait_for_commit(root, prev_trans);
|
|
wait_for_commit(root, prev_trans);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
put_transaction(prev_trans);
|
|
put_transaction(prev_trans);
|
|
|
|
+ } else {
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
}
|
|
}
|
|
|
|
+ } else {
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
}
|
|
}
|
|
|
|
|
|
if (now < cur_trans->start_time || now - cur_trans->start_time < 1)
|
|
if (now < cur_trans->start_time || now - cur_trans->start_time < 1)
|
|
@@ -1291,12 +1304,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
do {
|
|
do {
|
|
int snap_pending = 0;
|
|
int snap_pending = 0;
|
|
|
|
+
|
|
joined = cur_trans->num_joined;
|
|
joined = cur_trans->num_joined;
|
|
if (!list_empty(&trans->transaction->pending_snapshots))
|
|
if (!list_empty(&trans->transaction->pending_snapshots))
|
|
snap_pending = 1;
|
|
snap_pending = 1;
|
|
|
|
|
|
WARN_ON(cur_trans != trans->transaction);
|
|
WARN_ON(cur_trans != trans->transaction);
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
|
|
|
|
if (flush_on_commit || snap_pending) {
|
|
if (flush_on_commit || snap_pending) {
|
|
btrfs_start_delalloc_inodes(root, 1);
|
|
btrfs_start_delalloc_inodes(root, 1);
|
|
@@ -1316,14 +1329,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
prepare_to_wait(&cur_trans->writer_wait, &wait,
|
|
prepare_to_wait(&cur_trans->writer_wait, &wait,
|
|
TASK_UNINTERRUPTIBLE);
|
|
TASK_UNINTERRUPTIBLE);
|
|
|
|
|
|
- smp_mb();
|
|
|
|
if (atomic_read(&cur_trans->num_writers) > 1)
|
|
if (atomic_read(&cur_trans->num_writers) > 1)
|
|
schedule_timeout(MAX_SCHEDULE_TIMEOUT);
|
|
schedule_timeout(MAX_SCHEDULE_TIMEOUT);
|
|
else if (should_grow)
|
|
else if (should_grow)
|
|
schedule_timeout(1);
|
|
schedule_timeout(1);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
finish_wait(&cur_trans->writer_wait, &wait);
|
|
finish_wait(&cur_trans->writer_wait, &wait);
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
|
|
+ root->fs_info->trans_no_join = 1;
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
} while (atomic_read(&cur_trans->num_writers) > 1 ||
|
|
} while (atomic_read(&cur_trans->num_writers) > 1 ||
|
|
(should_grow && cur_trans->num_joined != joined));
|
|
(should_grow && cur_trans->num_joined != joined));
|
|
|
|
|
|
@@ -1364,9 +1378,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
btrfs_prepare_extent_commit(trans, root);
|
|
btrfs_prepare_extent_commit(trans, root);
|
|
|
|
|
|
cur_trans = root->fs_info->running_transaction;
|
|
cur_trans = root->fs_info->running_transaction;
|
|
- spin_lock(&root->fs_info->new_trans_lock);
|
|
|
|
- root->fs_info->running_transaction = NULL;
|
|
|
|
- spin_unlock(&root->fs_info->new_trans_lock);
|
|
|
|
|
|
|
|
btrfs_set_root_node(&root->fs_info->tree_root->root_item,
|
|
btrfs_set_root_node(&root->fs_info->tree_root->root_item,
|
|
root->fs_info->tree_root->node);
|
|
root->fs_info->tree_root->node);
|
|
@@ -1387,10 +1398,13 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
sizeof(root->fs_info->super_copy));
|
|
sizeof(root->fs_info->super_copy));
|
|
|
|
|
|
trans->transaction->blocked = 0;
|
|
trans->transaction->blocked = 0;
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
|
|
+ root->fs_info->running_transaction = NULL;
|
|
|
|
+ root->fs_info->trans_no_join = 0;
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
|
|
wake_up(&root->fs_info->transaction_wait);
|
|
wake_up(&root->fs_info->transaction_wait);
|
|
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
ret = btrfs_write_and_wait_transaction(trans, root);
|
|
ret = btrfs_write_and_wait_transaction(trans, root);
|
|
BUG_ON(ret);
|
|
BUG_ON(ret);
|
|
write_ctree_super(trans, root, 0);
|
|
write_ctree_super(trans, root, 0);
|
|
@@ -1403,22 +1417,21 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|
|
|
|
|
btrfs_finish_extent_commit(trans, root);
|
|
btrfs_finish_extent_commit(trans, root);
|
|
|
|
|
|
- mutex_lock(&root->fs_info->trans_mutex);
|
|
|
|
-
|
|
|
|
cur_trans->commit_done = 1;
|
|
cur_trans->commit_done = 1;
|
|
|
|
|
|
root->fs_info->last_trans_committed = cur_trans->transid;
|
|
root->fs_info->last_trans_committed = cur_trans->transid;
|
|
|
|
|
|
wake_up(&cur_trans->commit_wait);
|
|
wake_up(&cur_trans->commit_wait);
|
|
|
|
|
|
|
|
+ spin_lock(&root->fs_info->trans_lock);
|
|
list_del_init(&cur_trans->list);
|
|
list_del_init(&cur_trans->list);
|
|
|
|
+ spin_unlock(&root->fs_info->trans_lock);
|
|
|
|
+
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
put_transaction(cur_trans);
|
|
|
|
|
|
trace_btrfs_transaction_commit(root);
|
|
trace_btrfs_transaction_commit(root);
|
|
|
|
|
|
- mutex_unlock(&root->fs_info->trans_mutex);
|
|
|
|
-
|
|
|
|
if (current->journal_info == trans)
|
|
if (current->journal_info == trans)
|
|
current->journal_info = NULL;
|
|
current->journal_info = NULL;
|
|
|
|
|
|
@@ -1438,9 +1451,9 @@ int btrfs_clean_old_snapshots(struct btrfs_root *root)
|
|
LIST_HEAD(list);
|
|
LIST_HEAD(list);
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
|
|
|
|
|
- mutex_lock(&fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_lock(&fs_info->trans_lock);
|
|
list_splice_init(&fs_info->dead_roots, &list);
|
|
list_splice_init(&fs_info->dead_roots, &list);
|
|
- mutex_unlock(&fs_info->trans_mutex);
|
|
|
|
|
|
+ spin_unlock(&fs_info->trans_lock);
|
|
|
|
|
|
while (!list_empty(&list)) {
|
|
while (!list_empty(&list)) {
|
|
root = list_entry(list.next, struct btrfs_root, root_list);
|
|
root = list_entry(list.next, struct btrfs_root, root_list);
|