diff options
author | Jan Kara <jack@suse.cz> | 2005-09-06 18:19:09 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2005-09-07 19:57:54 -0400 |
commit | e6c9f5c1888097c936334bf9740024520ca47b8e (patch) | |
tree | 6fcf2cccb7e4d155dd663f10001efdb2a9d7daae | |
parent | cbf0d27a131639f4f3e4faa94373c5c6f89f8f07 (diff) |
[PATCH] Fix JBD race in t_forget list handling
Fix race between journal_commit_transaction() and other places as
journal_unmap_buffer() that are adding buffers to transaction's t_forget list.
We have to protect against such places by holding j_list_lock even when
traversing the t_forget list. The fact that other places can only add buffers
to the list makes the locking easier. OTOH the lock ranking complicates the
stuff...
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r-- | fs/jbd/commit.c | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index dac720c837ab..9d0494dcc571 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c | |||
@@ -720,11 +720,17 @@ wait_for_iobuf: | |||
720 | J_ASSERT(commit_transaction->t_log_list == NULL); | 720 | J_ASSERT(commit_transaction->t_log_list == NULL); |
721 | 721 | ||
722 | restart_loop: | 722 | restart_loop: |
723 | /* | ||
724 | * As there are other places (journal_unmap_buffer()) adding buffers | ||
725 | * to this list we have to be careful and hold the j_list_lock. | ||
726 | */ | ||
727 | spin_lock(&journal->j_list_lock); | ||
723 | while (commit_transaction->t_forget) { | 728 | while (commit_transaction->t_forget) { |
724 | transaction_t *cp_transaction; | 729 | transaction_t *cp_transaction; |
725 | struct buffer_head *bh; | 730 | struct buffer_head *bh; |
726 | 731 | ||
727 | jh = commit_transaction->t_forget; | 732 | jh = commit_transaction->t_forget; |
733 | spin_unlock(&journal->j_list_lock); | ||
728 | bh = jh2bh(jh); | 734 | bh = jh2bh(jh); |
729 | jbd_lock_bh_state(bh); | 735 | jbd_lock_bh_state(bh); |
730 | J_ASSERT_JH(jh, jh->b_transaction == commit_transaction || | 736 | J_ASSERT_JH(jh, jh->b_transaction == commit_transaction || |
@@ -792,9 +798,25 @@ restart_loop: | |||
792 | journal_remove_journal_head(bh); /* needs a brelse */ | 798 | journal_remove_journal_head(bh); /* needs a brelse */ |
793 | release_buffer_page(bh); | 799 | release_buffer_page(bh); |
794 | } | 800 | } |
801 | cond_resched_lock(&journal->j_list_lock); | ||
802 | } | ||
803 | spin_unlock(&journal->j_list_lock); | ||
804 | /* | ||
805 | * This is a bit sleazy. We borrow j_list_lock to protect | ||
806 | * journal->j_committing_transaction in __journal_remove_checkpoint. | ||
807 | * Really, __journal_remove_checkpoint should be using j_state_lock but | ||
808 | * it's a bit hassle to hold that across __journal_remove_checkpoint | ||
809 | */ | ||
810 | spin_lock(&journal->j_state_lock); | ||
811 | spin_lock(&journal->j_list_lock); | ||
812 | /* | ||
813 | * Now recheck if some buffers did not get attached to the transaction | ||
814 | * while the lock was dropped... | ||
815 | */ | ||
816 | if (commit_transaction->t_forget) { | ||
795 | spin_unlock(&journal->j_list_lock); | 817 | spin_unlock(&journal->j_list_lock); |
796 | if (cond_resched()) | 818 | spin_unlock(&journal->j_state_lock); |
797 | goto restart_loop; | 819 | goto restart_loop; |
798 | } | 820 | } |
799 | 821 | ||
800 | /* Done with this transaction! */ | 822 | /* Done with this transaction! */ |
@@ -803,14 +825,6 @@ restart_loop: | |||
803 | 825 | ||
804 | J_ASSERT(commit_transaction->t_state == T_COMMIT); | 826 | J_ASSERT(commit_transaction->t_state == T_COMMIT); |
805 | 827 | ||
806 | /* | ||
807 | * This is a bit sleazy. We borrow j_list_lock to protect | ||
808 | * journal->j_committing_transaction in __journal_remove_checkpoint. | ||
809 | * Really, __jornal_remove_checkpoint should be using j_state_lock but | ||
810 | * it's a bit hassle to hold that across __journal_remove_checkpoint | ||
811 | */ | ||
812 | spin_lock(&journal->j_state_lock); | ||
813 | spin_lock(&journal->j_list_lock); | ||
814 | commit_transaction->t_state = T_FINISHED; | 828 | commit_transaction->t_state = T_FINISHED; |
815 | J_ASSERT(commit_transaction == journal->j_committing_transaction); | 829 | J_ASSERT(commit_transaction == journal->j_committing_transaction); |
816 | journal->j_commit_sequence = commit_transaction->t_tid; | 830 | journal->j_commit_sequence = commit_transaction->t_tid; |