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 | |
| 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>
| -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); |
