diff options
author | Sage Weil <sage@newdream.net> | 2010-10-29 15:37:34 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2010-10-29 15:37:34 -0400 |
commit | 99d16cbcaf694c803a1b6bf7e851694ffe1d255d (patch) | |
tree | dacf7f8eca75dc0d92566b5d3876f3efe8196e35 /fs | |
parent | fccdae435c1b295cca546f23f6f43126a28ffac3 (diff) |
Btrfs: fix deadlock in btrfs_commit_transaction
We calculate timeout (either 1 or MAX_SCHEDULE_TIMEOUT) based on whether
num_writers > 1 or should_grow at the top of the loop. Then, much much
later, we wait for that timeout if either num_writers or should_grow is
true. However, it's possible for a racing process (calling
btrfs_end_transaction()) to decrement num_writers such that we wait
forever instead of for 1.
Fix this by deciding how long to wait when we wait. Include a smp_mb()
before checking if the waitqueue is active to ensure the num_writers
is visible.
Signed-off-by: Sage Weil <sage@newdream.net>
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/transaction.c | 13 |
1 files changed, 5 insertions, 8 deletions
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index 325d9a5f0128..700dc4b34ada 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c | |||
@@ -402,6 +402,7 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, | |||
402 | WARN_ON(cur_trans->num_writers < 1); | 402 | WARN_ON(cur_trans->num_writers < 1); |
403 | cur_trans->num_writers--; | 403 | cur_trans->num_writers--; |
404 | 404 | ||
405 | smp_mb(); | ||
405 | if (waitqueue_active(&cur_trans->writer_wait)) | 406 | if (waitqueue_active(&cur_trans->writer_wait)) |
406 | wake_up(&cur_trans->writer_wait); | 407 | wake_up(&cur_trans->writer_wait); |
407 | put_transaction(cur_trans); | 408 | put_transaction(cur_trans); |
@@ -1010,7 +1011,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
1010 | struct btrfs_root *root) | 1011 | struct btrfs_root *root) |
1011 | { | 1012 | { |
1012 | unsigned long joined = 0; | 1013 | unsigned long joined = 0; |
1013 | unsigned long timeout = 1; | ||
1014 | struct btrfs_transaction *cur_trans; | 1014 | struct btrfs_transaction *cur_trans; |
1015 | struct btrfs_transaction *prev_trans = NULL; | 1015 | struct btrfs_transaction *prev_trans = NULL; |
1016 | DEFINE_WAIT(wait); | 1016 | DEFINE_WAIT(wait); |
@@ -1081,11 +1081,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
1081 | snap_pending = 1; | 1081 | snap_pending = 1; |
1082 | 1082 | ||
1083 | WARN_ON(cur_trans != trans->transaction); | 1083 | WARN_ON(cur_trans != trans->transaction); |
1084 | if (cur_trans->num_writers > 1) | ||
1085 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
1086 | else if (should_grow) | ||
1087 | timeout = 1; | ||
1088 | |||
1089 | mutex_unlock(&root->fs_info->trans_mutex); | 1084 | mutex_unlock(&root->fs_info->trans_mutex); |
1090 | 1085 | ||
1091 | if (flush_on_commit || snap_pending) { | 1086 | if (flush_on_commit || snap_pending) { |
@@ -1107,8 +1102,10 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, | |||
1107 | TASK_UNINTERRUPTIBLE); | 1102 | TASK_UNINTERRUPTIBLE); |
1108 | 1103 | ||
1109 | smp_mb(); | 1104 | smp_mb(); |
1110 | if (cur_trans->num_writers > 1 || should_grow) | 1105 | if (cur_trans->num_writers > 1) |
1111 | schedule_timeout(timeout); | 1106 | schedule_timeout(MAX_SCHEDULE_TIMEOUT); |
1107 | else if (should_grow) | ||
1108 | schedule_timeout(1); | ||
1112 | 1109 | ||
1113 | mutex_lock(&root->fs_info->trans_mutex); | 1110 | mutex_lock(&root->fs_info->trans_mutex); |
1114 | finish_wait(&cur_trans->writer_wait, &wait); | 1111 | finish_wait(&cur_trans->writer_wait, &wait); |