Commit e6c9f5c1 authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

[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: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent cbf0d27a
...@@ -720,11 +720,17 @@ void journal_commit_transaction(journal_t *journal) ...@@ -720,11 +720,17 @@ void journal_commit_transaction(journal_t *journal)
J_ASSERT(commit_transaction->t_log_list == NULL); J_ASSERT(commit_transaction->t_log_list == NULL);
restart_loop: restart_loop:
/*
* As there are other places (journal_unmap_buffer()) adding buffers
* to this list we have to be careful and hold the j_list_lock.
*/
spin_lock(&journal->j_list_lock);
while (commit_transaction->t_forget) { while (commit_transaction->t_forget) {
transaction_t *cp_transaction; transaction_t *cp_transaction;
struct buffer_head *bh; struct buffer_head *bh;
jh = commit_transaction->t_forget; jh = commit_transaction->t_forget;
spin_unlock(&journal->j_list_lock);
bh = jh2bh(jh); bh = jh2bh(jh);
jbd_lock_bh_state(bh); jbd_lock_bh_state(bh);
J_ASSERT_JH(jh, jh->b_transaction == commit_transaction || J_ASSERT_JH(jh, jh->b_transaction == commit_transaction ||
...@@ -792,9 +798,25 @@ void journal_commit_transaction(journal_t *journal) ...@@ -792,9 +798,25 @@ void journal_commit_transaction(journal_t *journal)
journal_remove_journal_head(bh); /* needs a brelse */ journal_remove_journal_head(bh); /* needs a brelse */
release_buffer_page(bh); release_buffer_page(bh);
} }
cond_resched_lock(&journal->j_list_lock);
}
spin_unlock(&journal->j_list_lock);
/*
* This is a bit sleazy. We borrow j_list_lock to protect
* journal->j_committing_transaction in __journal_remove_checkpoint.
* Really, __journal_remove_checkpoint should be using j_state_lock but
* it's a bit hassle to hold that across __journal_remove_checkpoint
*/
spin_lock(&journal->j_state_lock);
spin_lock(&journal->j_list_lock);
/*
* Now recheck if some buffers did not get attached to the transaction
* while the lock was dropped...
*/
if (commit_transaction->t_forget) {
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
if (cond_resched()) spin_unlock(&journal->j_state_lock);
goto restart_loop; goto restart_loop;
} }
/* Done with this transaction! */ /* Done with this transaction! */
...@@ -803,14 +825,6 @@ void journal_commit_transaction(journal_t *journal) ...@@ -803,14 +825,6 @@ void journal_commit_transaction(journal_t *journal)
J_ASSERT(commit_transaction->t_state == T_COMMIT); J_ASSERT(commit_transaction->t_state == T_COMMIT);
/*
* This is a bit sleazy. We borrow j_list_lock to protect
* journal->j_committing_transaction in __journal_remove_checkpoint.
* Really, __jornal_remove_checkpoint should be using j_state_lock but
* it's a bit hassle to hold that across __journal_remove_checkpoint
*/
spin_lock(&journal->j_state_lock);
spin_lock(&journal->j_list_lock);
commit_transaction->t_state = T_FINISHED; commit_transaction->t_state = T_FINISHED;
J_ASSERT(commit_transaction == journal->j_committing_transaction); J_ASSERT(commit_transaction == journal->j_committing_transaction);
journal->j_commit_sequence = commit_transaction->t_tid; journal->j_commit_sequence = commit_transaction->t_tid;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment