diff options
author | Jan Kara <jack@suse.cz> | 2007-12-05 02:45:27 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-12-05 12:21:20 -0500 |
commit | d4beaf4ab5f89496f2bcf67db62ad95d99bfeff6 (patch) | |
tree | ae6d005199017e9a0ade169fd69cb73d6c0ac88f | |
parent | 369b8f5a70402d9fe77006cd0044c8a3fcd08430 (diff) |
jbd: Fix assertion failure in fs/jbd/checkpoint.c
Before we start committing a transaction, we call
__journal_clean_checkpoint_list() to cleanup transaction's written-back
buffers.
If this call happens to remove all of them (and there were already some
buffers), __journal_remove_checkpoint() will decide to free the transaction
because it isn't (yet) a committing transaction and soon we fail some
assertion - the transaction really isn't ready to be freed :).
We change the check in __journal_remove_checkpoint() to free only a
transaction in T_FINISHED state. The locking there is subtle though (as
everywhere in JBD ;(). We use j_list_lock to protect the check and a
subsequent call to __journal_drop_transaction() and do the same in the end
of journal_commit_transaction() which is the only place where a transaction
can get to T_FINISHED state.
Probably I'm too paranoid here and such locking is not really necessary -
checkpoint lists are processed only from log_do_checkpoint() where a
transaction must be already committed to be processed or from
__journal_clean_checkpoint_list() where kjournald itself calls it and thus
transaction cannot change state either. Better be safe if something
changes in future...
Signed-off-by: Jan Kara <jack@suse.cz>
Cc: <linux-ext4@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | fs/jbd/checkpoint.c | 12 | ||||
-rw-r--r-- | fs/jbd/commit.c | 8 | ||||
-rw-r--r-- | include/linux/jbd.h | 2 |
3 files changed, 12 insertions, 10 deletions
diff --git a/fs/jbd/checkpoint.c b/fs/jbd/checkpoint.c index 47552d4a6324..0f69c416eebc 100644 --- a/fs/jbd/checkpoint.c +++ b/fs/jbd/checkpoint.c | |||
@@ -602,15 +602,15 @@ int __journal_remove_checkpoint(struct journal_head *jh) | |||
602 | 602 | ||
603 | /* | 603 | /* |
604 | * There is one special case to worry about: if we have just pulled the | 604 | * There is one special case to worry about: if we have just pulled the |
605 | * buffer off a committing transaction's forget list, then even if the | 605 | * buffer off a running or committing transaction's checkpoing list, |
606 | * checkpoint list is empty, the transaction obviously cannot be | 606 | * then even if the checkpoint list is empty, the transaction obviously |
607 | * dropped! | 607 | * cannot be dropped! |
608 | * | 608 | * |
609 | * The locking here around j_committing_transaction is a bit sleazy. | 609 | * The locking here around t_state is a bit sleazy. |
610 | * See the comment at the end of journal_commit_transaction(). | 610 | * See the comment at the end of journal_commit_transaction(). |
611 | */ | 611 | */ |
612 | if (transaction == journal->j_committing_transaction) { | 612 | if (transaction->t_state != T_FINISHED) { |
613 | JBUFFER_TRACE(jh, "belongs to committing transaction"); | 613 | JBUFFER_TRACE(jh, "belongs to running/committing transaction"); |
614 | goto out; | 614 | goto out; |
615 | } | 615 | } |
616 | 616 | ||
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c index 8f1f2aa5fb39..610264b99a8e 100644 --- a/fs/jbd/commit.c +++ b/fs/jbd/commit.c | |||
@@ -858,10 +858,10 @@ restart_loop: | |||
858 | } | 858 | } |
859 | spin_unlock(&journal->j_list_lock); | 859 | spin_unlock(&journal->j_list_lock); |
860 | /* | 860 | /* |
861 | * This is a bit sleazy. We borrow j_list_lock to protect | 861 | * This is a bit sleazy. We use j_list_lock to protect transition |
862 | * journal->j_committing_transaction in __journal_remove_checkpoint. | 862 | * of a transaction into T_FINISHED state and calling |
863 | * Really, __journal_remove_checkpoint should be using j_state_lock but | 863 | * __journal_drop_transaction(). Otherwise we could race with |
864 | * it's a bit hassle to hold that across __journal_remove_checkpoint | 864 | * other checkpointing code processing the transaction... |
865 | */ | 865 | */ |
866 | spin_lock(&journal->j_state_lock); | 866 | spin_lock(&journal->j_state_lock); |
867 | spin_lock(&journal->j_list_lock); | 867 | spin_lock(&journal->j_list_lock); |
diff --git a/include/linux/jbd.h b/include/linux/jbd.h index 16e7ed855a18..d9ecd13393b0 100644 --- a/include/linux/jbd.h +++ b/include/linux/jbd.h | |||
@@ -439,6 +439,8 @@ struct transaction_s | |||
439 | /* | 439 | /* |
440 | * Transaction's current state | 440 | * Transaction's current state |
441 | * [no locking - only kjournald alters this] | 441 | * [no locking - only kjournald alters this] |
442 | * [j_list_lock] guards transition of a transaction into T_FINISHED | ||
443 | * state and subsequent call of __journal_drop_transaction() | ||
442 | * FIXME: needs barriers | 444 | * FIXME: needs barriers |
443 | * KLUDGE: [use j_state_lock] | 445 | * KLUDGE: [use j_state_lock] |
444 | */ | 446 | */ |