diff mbox

jbd2-abort-instead-of-waiting-for-nonexistent-transactions.patch

Message ID 20080922140851.bc3f9319.akpm@linux-foundation.org
State Accepted, archived
Headers show

Commit Message

Andrew Morton Sept. 22, 2008, 9:08 p.m. UTC
Guys, I have a note here that this might be needed in 2.6.27.

I also have a note that Stephen had issues with it, but I
don't recall what they were.

Can we get this sorted out please?



From: "Duane Griffin" <duaneg@dghda.com>

The __jbd2_log_wait_for_space function sits in a loop checkpointing
transactions until there is sufficient space free in the journal. 
However, if there are no transactions to be processed (e.g.  because the
free space calculation is wrong due to a corrupted filesystem) it will
never progress.

Check for space being required when no transactions are outstanding and
abort the journal instead of endlessly looping.

This patch fixes the bug reported by Sami Liedes at:
http://bugzilla.kernel.org/show_bug.cgi?id=10976

Signed-off-by: Duane Griffin <duaneg@dghda.com>
Cc: Sami Liedes <sliedes@cc.hut.fi>
Cc: <linux-ext4@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
---

 fs/jbd2/checkpoint.c |   19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

Comments

Duane Griffin Sept. 23, 2008, 4:56 p.m. UTC | #1
2008/9/22 Andrew Morton <akpm@linux-foundation.org>:
> Guys, I have a note here that this might be needed in 2.6.27.
>
> I also have a note that Stephen had issues with it, but I
> don't recall what they were.

Stephen suggested that it would be better to sanity check the journal
start/end pointers on mount, rather than catching the error later like
this. I never quite convinced myself I'd worked out the right way to
do that, sorry. Perhaps someone would like to confirm (or otherwise)
whether or not the following is correct:

In journal_reset (?) check that:

journal->j_first == 1 (this seems to be the only valid value)

and

journal->j_last >= JFS_MIN_JOURNAL_BLOCKS

Additionally, it should be possible to check the journal->j_last more
precisely. For internal journals it seems straight-forward, we can
just check that journal->j_last == inode->i_size >>
inode->i_sb->s_blocksize_bits. For external journals we'd need to load
the device's superblock and check journal->j_last == s_blocks_count.

> Can we get this sorted out please?

If the above is confirmed I'll send a patch to that effect for jdb,
jdb2 and for e2fsprogs (fsck doesn't check j_first/j_last either).

Regardless, I think the original patch may be a good idea. It improves
robustness and matches the other locations where we call
jbd2_log_do_checkpoint. They are all in loops that test that
journal->j_checkpoint_transactions != NULL.

Cheers,
Duane.
Theodore Ts'o Sept. 29, 2008, 2:24 a.m. UTC | #2
On Tue, Sep 23, 2008 at 05:56:27PM +0100, Duane Griffin wrote:
> Stephen suggested that it would be better to sanity check the journal
> start/end pointers on mount, rather than catching the error later like
> this. I never quite convinced myself I'd worked out the right way to
> do that, sorry. Perhaps someone would like to confirm (or otherwise)
> whether or not the following is correct:
> 
> In journal_reset (?) check that:
> 
> journal->j_first == 1 (this seems to be the only valid value)
> 
> and
> 
> journal->j_last >= JFS_MIN_JOURNAL_BLOCKS

Yes, for all existing currently created, j_first will be 1.  I can't
think of a good reason for why we might want to reserve some space at
the beginning of the journal, but the safest check would be:

    (journal->j_last - journal->j_first +1) >= JFS_MIN_JOURNAL_BLOCKS

> Additionally, it should be possible to check the journal->j_last more
> precisely. For internal journals it seems straight-forward, we can
> just check that journal->j_last == inode->i_size >>
> inode->i_sb->s_blocksize_bits. For external journals we'd need to load
> the device's superblock and check journal->j_last == s_blocks_count.

Yep, agreed.

> Regardless, I think the original patch may be a good idea. It improves
> robustness and matches the other locations where we call
> jbd2_log_do_checkpoint. They are all in loops that test that
> journal->j_checkpoint_transactions != NULL.

Agreed.  I've included it in the ext4 patch queue, and will be soon
putting out a new ext4 patchset consisting of the patches I plan to
push during the next merge window.

						- Ted
--
To unsubscribe from this list: send the line "unsubscribe linux-ext4" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff -puN fs/jbd2/checkpoint.c~jbd2-abort-instead-of-waiting-for-nonexistent-transactions fs/jbd2/checkpoint.c
--- a/fs/jbd2/checkpoint.c~jbd2-abort-instead-of-waiting-for-nonexistent-transactions
+++ a/fs/jbd2/checkpoint.c
@@ -126,14 +126,29 @@  void __jbd2_log_wait_for_space(journal_t
 
 		/*
 		 * Test again, another process may have checkpointed while we
-		 * were waiting for the checkpoint lock
+		 * were waiting for the checkpoint lock. If there are no
+		 * outstanding transactions there is nothing to checkpoint and
+		 * we can't make progress. Abort the journal in this case.
 		 */
 		spin_lock(&journal->j_state_lock);
+		spin_lock(&journal->j_list_lock);
 		nblocks = jbd_space_needed(journal);
 		if (__jbd2_log_space_left(journal) < nblocks) {
+			int chkpt = journal->j_checkpoint_transactions != NULL;
+
+			spin_unlock(&journal->j_list_lock);
 			spin_unlock(&journal->j_state_lock);
-			jbd2_log_do_checkpoint(journal);
+			if (chkpt) {
+				jbd2_log_do_checkpoint(journal);
+			} else {
+				printk(KERN_ERR "%s: no transactions\n",
+				       __func__);
+				jbd2_journal_abort(journal, 0);
+			}
+
 			spin_lock(&journal->j_state_lock);
+		} else {
+			spin_unlock(&journal->j_list_lock);
 		}
 		mutex_unlock(&journal->j_checkpoint_mutex);
 	}