Commit 9ada7340 authored by Jan Kara's avatar Jan Kara Committed by Linus Torvalds

[PATCH] jbd: fix BUG in journal_commit_transaction()

Fix possible assertion failure in journal_commit_transaction() on
jh->b_next_transaction == NULL (when we are processing BJ_Forget list and
buffer is not jbddirty).

!jbddirty buffers can be placed on BJ_Forget list for example by
journal_forget() or by __dispose_buffer() - generally such buffer means
that it has been freed by this transaction.

Freed buffers should not be reallocated until the transaction has committed
(that's why we have the assertion there) but they *can* be reallocated when
the transaction has already been committed to disk and we are just
processing the BJ_Forget list (as soon as we remove b_committed_data from
the bitmap bh, ext3 will be able to reallocate buffers freed by the
committing transaction).  So we have to also count with the case that the
buffer has been reallocated and b_next_transaction has been already set.

And one more subtle point: it can happen that we manage to reallocate the
buffer and also mark it jbddirty.  Then we also add the freed buffer to the
checkpoint list of the committing trasaction.  But that should do no harm.

Non-jbddirty buffers should be filed to BJ_Reserved and not BJ_Metadata
list.  It can actually happen that we refile such buffers during the commit
phase when we reallocate in the running transaction blocks deleted in
committing transaction (and that can happen if the committing transaction
already wrote all the data and is just cleaning up BJ_Forget list).
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Acked-by: default avatar"Stephen C. Tweedie" <sct@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 8e0a43d8
...@@ -790,11 +790,22 @@ void journal_commit_transaction(journal_t *journal) ...@@ -790,11 +790,22 @@ void journal_commit_transaction(journal_t *journal)
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
} else { } else {
J_ASSERT_BH(bh, !buffer_dirty(bh)); J_ASSERT_BH(bh, !buffer_dirty(bh));
J_ASSERT_JH(jh, jh->b_next_transaction == NULL); /* The buffer on BJ_Forget list and not jbddirty means
__journal_unfile_buffer(jh); * it has been freed by this transaction and hence it
jbd_unlock_bh_state(bh); * could not have been reallocated until this
journal_remove_journal_head(bh); /* needs a brelse */ * transaction has committed. *BUT* it could be
release_buffer_page(bh); * reallocated once we have written all the data to
* disk and before we process the buffer on BJ_Forget
* list. */
JBUFFER_TRACE(jh, "refile or unfile freed buffer");
__journal_refile_buffer(jh);
if (!jh->b_transaction) {
jbd_unlock_bh_state(bh);
/* needs a brelse */
journal_remove_journal_head(bh);
release_buffer_page(bh);
} else
jbd_unlock_bh_state(bh);
} }
cond_resched_lock(&journal->j_list_lock); cond_resched_lock(&journal->j_list_lock);
} }
......
...@@ -2038,7 +2038,8 @@ void __journal_refile_buffer(struct journal_head *jh) ...@@ -2038,7 +2038,8 @@ void __journal_refile_buffer(struct journal_head *jh)
__journal_temp_unlink_buffer(jh); __journal_temp_unlink_buffer(jh);
jh->b_transaction = jh->b_next_transaction; jh->b_transaction = jh->b_next_transaction;
jh->b_next_transaction = NULL; jh->b_next_transaction = NULL;
__journal_file_buffer(jh, jh->b_transaction, BJ_Metadata); __journal_file_buffer(jh, jh->b_transaction,
was_dirty ? BJ_Metadata : BJ_Reserved);
J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING); J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
if (was_dirty) if (was_dirty)
......
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