]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - fs/jbd2/transaction.c
jbd2: delay discarding buffers in journal_unmap_buffer
[mv-sheeva.git] / fs / jbd2 / transaction.c
index a0512700542f3fd93c768dde727e23f77a5e3597..bfc70f57900fddb0d63039fb0a2cd662abf963ff 100644 (file)
@@ -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)