Merge branch 'for-next' into for-linus
[safe/jmp/linux-2.6] / fs / jbd / transaction.c
index 57ae203..5ae71e7 100644 (file)
@@ -1864,6 +1864,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
@@ -1929,16 +1944,15 @@ static int journal_unmap_buffer(journal_t *journal, struct buffer_head *bh)
                        goto zap_buffer;
                }
                /*
-                * 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;
                journal_put_journal_head(jh);
                spin_unlock(&journal->j_list_lock);
                jbd_unlock_bh_state(bh);
@@ -2120,7 +2134,7 @@ void journal_file_buffer(struct journal_head *jh,
  */
 void __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));
@@ -2142,8 +2156,13 @@ void __journal_refile_buffer(struct journal_head *jh)
        __journal_temp_unlink_buffer(jh);
        jh->b_transaction = jh->b_next_transaction;
        jh->b_next_transaction = NULL;
-       __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;
+       __journal_file_buffer(jh, jh->b_transaction, jlist);
        J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
 
        if (was_dirty)