Skip to content

Commit

Permalink
[PATCH] Fix JBD race in t_forget list handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Jan Kara authored and Linus Torvalds committed Sep 7, 2005
1 parent cbf0d27 commit e6c9f5c
Showing 1 changed file with 24 additions and 10 deletions.
34 changes: 24 additions & 10 deletions fs/jbd/commit.c
Original file line number Diff line number Diff line change
Expand Up @@ -720,11 +720,17 @@ void journal_commit_transaction(journal_t *journal)
J_ASSERT(commit_transaction->t_log_list == NULL);

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) {
transaction_t *cp_transaction;
struct buffer_head *bh;

jh = commit_transaction->t_forget;
spin_unlock(&journal->j_list_lock);
bh = jh2bh(jh);
jbd_lock_bh_state(bh);
J_ASSERT_JH(jh, jh->b_transaction == commit_transaction ||
Expand Down Expand Up @@ -792,9 +798,25 @@ void journal_commit_transaction(journal_t *journal)
journal_remove_journal_head(bh); /* needs a brelse */
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);
if (cond_resched())
goto restart_loop;
spin_unlock(&journal->j_state_lock);
goto restart_loop;
}

/* Done with this transaction! */
Expand All @@ -803,14 +825,6 @@ void journal_commit_transaction(journal_t *journal)

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;
J_ASSERT(commit_transaction == journal->j_committing_transaction);
journal->j_commit_sequence = commit_transaction->t_tid;
Expand Down

0 comments on commit e6c9f5c

Please sign in to comment.