|
@@ -1727,6 +1727,21 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
|
|
|
if (!jh)
|
|
|
goto zap_buffer_no_jh;
|
|
|
|
|
|
+ /*
|
|
|
+ * We cannot remove the buffer from checkpoint lists until the
|
|
|
+ * transaction adding inode to orphan list (let's call it T)
|
|
|
+ * is committed. Otherwise if the transaction changing the
|
|
|
+ * buffer would be cleaned from the journal before T is
|
|
|
+ * committed, a crash will cause that the correct contents of
|
|
|
+ * the buffer will be lost. On the other hand we have to
|
|
|
+ * clear the buffer dirty bit at latest at the moment when the
|
|
|
+ * transaction marking the buffer as freed in the filesystem
|
|
|
+ * structures is committed because from that moment on the
|
|
|
+ * buffer can be reallocated and used by a different page.
|
|
|
+ * Since the block hasn't been freed yet but the inode has
|
|
|
+ * already been added to orphan list, it is safe for us to add
|
|
|
+ * the buffer to BJ_Forget list of the newest transaction.
|
|
|
+ */
|
|
|
transaction = jh->b_transaction;
|
|
|
if (transaction == NULL) {
|
|
|
/* First case: not on any transaction. If it
|
|
@@ -1783,16 +1798,15 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
|
|
|
} else if (transaction == journal->j_committing_transaction) {
|
|
|
JBUFFER_TRACE(jh, "on committing transaction");
|
|
|
/*
|
|
|
- * If it is committing, we simply cannot touch it. We
|
|
|
- * can remove it's next_transaction pointer from the
|
|
|
- * running transaction if that is set, but nothing
|
|
|
- * else. */
|
|
|
+ * The buffer is committing, we simply cannot touch
|
|
|
+ * it. So we just set j_next_transaction to the
|
|
|
+ * running transaction (if there is one) and mark
|
|
|
+ * buffer as freed so that commit code knows it should
|
|
|
+ * clear dirty bits when it is done with the buffer.
|
|
|
+ */
|
|
|
set_buffer_freed(bh);
|
|
|
- if (jh->b_next_transaction) {
|
|
|
- J_ASSERT(jh->b_next_transaction ==
|
|
|
- journal->j_running_transaction);
|
|
|
- jh->b_next_transaction = NULL;
|
|
|
- }
|
|
|
+ if (journal->j_running_transaction && buffer_jbddirty(bh))
|
|
|
+ jh->b_next_transaction = journal->j_running_transaction;
|
|
|
jbd2_journal_put_journal_head(jh);
|
|
|
spin_unlock(&journal->j_list_lock);
|
|
|
jbd_unlock_bh_state(bh);
|
|
@@ -1969,7 +1983,7 @@ void jbd2_journal_file_buffer(struct journal_head *jh,
|
|
|
*/
|
|
|
void __jbd2_journal_refile_buffer(struct journal_head *jh)
|
|
|
{
|
|
|
- int was_dirty;
|
|
|
+ int was_dirty, jlist;
|
|
|
struct buffer_head *bh = jh2bh(jh);
|
|
|
|
|
|
J_ASSERT_JH(jh, jbd_is_locked_bh_state(bh));
|
|
@@ -1991,8 +2005,13 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
|
|
|
__jbd2_journal_temp_unlink_buffer(jh);
|
|
|
jh->b_transaction = jh->b_next_transaction;
|
|
|
jh->b_next_transaction = NULL;
|
|
|
- __jbd2_journal_file_buffer(jh, jh->b_transaction,
|
|
|
- jh->b_modified ? BJ_Metadata : BJ_Reserved);
|
|
|
+ if (buffer_freed(bh))
|
|
|
+ jlist = BJ_Forget;
|
|
|
+ else if (jh->b_modified)
|
|
|
+ jlist = BJ_Metadata;
|
|
|
+ else
|
|
|
+ jlist = BJ_Reserved;
|
|
|
+ __jbd2_journal_file_buffer(jh, jh->b_transaction, jlist);
|
|
|
J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
|
|
|
|
|
|
if (was_dirty)
|