aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jbd/commit.c
diff options
context:
space:
mode:
authorNeil Brown <neilb@suse.de>2008-02-08 07:22:13 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 12:22:44 -0500
commit28ae094c625a9b719c01cf5ec45b8640e6911f53 (patch)
tree74d34775267bf7a141bc9eb3f042e8a7a0251916 /fs/jbd/commit.c
parent2dafe1c4d69345539735cca64250f2d4657bd057 (diff)
ext3 can fail badly when device stops accepting BIO_RW_BARRIER requests
Some devices - notably dm and md - can change their behaviour in response to BIO_RW_BARRIER requests. They might start out accepting such requests but on reconfiguration, they find out that they cannot any more. ext3 (and other filesystems) deal with this by always testing if BIO_RW_BARRIER requests fail with EOPNOTSUPP, and retrying the write requests without the barrier (probably after waiting for any pending writes to complete). However there is a bug in the handling for this for ext3. When ext3 (jbd actually) decides to submit a BIO_RW_BARRIER request, it sets the buffer_ordered flag on the buffer head. If the request completes successfully, the flag STAYS SET. Other code might then write the same buffer_head after the device has been reconfigured to not accept barriers. This write will then fail, but the "other code" is not ready to handle EOPNOTSUPP errors and the error will be treated as fatal. This can be seen without having to reconfigure a device at exactly the wrong time by putting: if (buffer_ordered(bh)) printk("OH DEAR, and ordered buffer\n"); in the while loop in "commit phase 5" of journal_commit_transaction. If it ever prints the "OH DEAR ..." message (as it does sometimes for me), then that request could (in different circumstances) have failed with EOPNOTSUPP, but that isn't tested for. My proposed fix is to clear the buffer_ordered flag after it has been used, as in the following patch. Signed-off-by: Neil Brown <neilb@suse.de> Cc: <linux-ext4@vger.kernel.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/jbd/commit.c')
-rw-r--r--fs/jbd/commit.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index 31853eb65b4c..8e08efcaede2 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -131,6 +131,8 @@ static int journal_write_commit_record(journal_t *journal,
131 barrier_done = 1; 131 barrier_done = 1;
132 } 132 }
133 ret = sync_dirty_buffer(bh); 133 ret = sync_dirty_buffer(bh);
134 if (barrier_done)
135 clear_buffer_ordered(bh);
134 /* is it possible for another commit to fail at roughly 136 /* is it possible for another commit to fail at roughly
135 * the same time as this one? If so, we don't want to 137 * the same time as this one? If so, we don't want to
136 * trust the barrier flag in the super, but instead want 138 * trust the barrier flag in the super, but instead want
@@ -148,7 +150,6 @@ static int journal_write_commit_record(journal_t *journal,
148 spin_unlock(&journal->j_state_lock); 150 spin_unlock(&journal->j_state_lock);
149 151
150 /* And try again, without the barrier */ 152 /* And try again, without the barrier */
151 clear_buffer_ordered(bh);
152 set_buffer_uptodate(bh); 153 set_buffer_uptodate(bh);
153 set_buffer_dirty(bh); 154 set_buffer_dirty(bh);
154 ret = sync_dirty_buffer(bh); 155 ret = sync_dirty_buffer(bh);