diff options
author | Chris Mason <chris.mason@oracle.com> | 2011-11-06 03:26:19 -0500 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2011-11-06 03:26:19 -0500 |
commit | d43317dcd074818d4bd12ddd4184a29aff98907b (patch) | |
tree | 233033cf8c7f3cb38dc549d2a1be7f0fc9ecfd80 /fs/btrfs/transaction.c | |
parent | 56d2a48f81a1bde827c625b90221fade72fe9c61 (diff) |
Btrfs: fix race during transaction joins
While we're allocating ram for a new transaction, we drop our spinlock.
When we get the lock back, we do check to see if a transaction started
while we slept, but we don't check to make sure it isn't blocked
because a commit has already started.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/transaction.c')
-rw-r--r-- | fs/btrfs/transaction.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 29f782cc2cc9..960835eaf4da 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -55,6 +55,7 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail) | |||
55 | struct btrfs_transaction *cur_trans; | 55 | struct btrfs_transaction *cur_trans; |
56 | 56 | ||
57 | spin_lock(&root->fs_info->trans_lock); | 57 | spin_lock(&root->fs_info->trans_lock); |
58 | loop: | ||
58 | if (root->fs_info->trans_no_join) { | 59 | if (root->fs_info->trans_no_join) { |
59 | if (!nofail) { | 60 | if (!nofail) { |
60 | spin_unlock(&root->fs_info->trans_lock); | 61 | spin_unlock(&root->fs_info->trans_lock); |
@@ -75,16 +76,18 @@ static noinline int join_transaction(struct btrfs_root *root, int nofail) | |||
75 | cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS); | 76 | cur_trans = kmem_cache_alloc(btrfs_transaction_cachep, GFP_NOFS); |
76 | if (!cur_trans) | 77 | if (!cur_trans) |
77 | return -ENOMEM; | 78 | return -ENOMEM; |
79 | |||
78 | spin_lock(&root->fs_info->trans_lock); | 80 | spin_lock(&root->fs_info->trans_lock); |
79 | if (root->fs_info->running_transaction) { | 81 | if (root->fs_info->running_transaction) { |
82 | /* | ||
83 | * someone started a transaction after we unlocked. Make sure | ||
84 | * to redo the trans_no_join checks above | ||
85 | */ | ||
80 | kmem_cache_free(btrfs_transaction_cachep, cur_trans); | 86 | kmem_cache_free(btrfs_transaction_cachep, cur_trans); |
81 | cur_trans = root->fs_info->running_transaction; | 87 | cur_trans = root->fs_info->running_transaction; |
82 | atomic_inc(&cur_trans->use_count); | 88 | goto loop; |
83 | atomic_inc(&cur_trans->num_writers); | ||
84 | cur_trans->num_joined++; | ||
85 | spin_unlock(&root->fs_info->trans_lock); | ||
86 | return 0; | ||
87 | } | 89 | } |
90 | |||
88 | atomic_set(&cur_trans->num_writers, 1); | 91 | atomic_set(&cur_trans->num_writers, 1); |
89 | cur_trans->num_joined = 0; | 92 | cur_trans->num_joined = 0; |
90 | init_waitqueue_head(&cur_trans->writer_wait); | 93 | init_waitqueue_head(&cur_trans->writer_wait); |