aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2005-09-06 18:19:09 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2005-09-07 19:57:54 -0400
commite6c9f5c1888097c936334bf9740024520ca47b8e (patch)
tree6fcf2cccb7e4d155dd663f10001efdb2a9d7daae /fs
parentcbf0d27a131639f4f3e4faa94373c5c6f89f8f07 (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>
Diffstat (limited to 'fs')
-rw-r--r--fs/jbd/commit.c34
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
722restart_loop: 722restart_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;