Commit dff6825e authored by Darrick J. Wong's avatar Darrick J. Wong Committed by Jan Kara

ext3/jbd: Avoid WARN() messages when failing to write the superblock

This fixes a WARN backtrace in mark_buffer_dirty() that occurs during unmount
when the underlying block device is removed.  This bug has been seen on System
Z when removing all paths from a multipath-backed ext3 mount; on System P when
injecting enough PCI EEH errors to make the SCSI controller go offline; and
similar warnings have been seen (and patched) with ext2/ext4.

The super block update from a previous operation has marked the buffer as in
error, and the flag has to be cleared before doing the update. Similar changes
have been made to ext4 by commit 914258bf.
Signed-off-by: default avatarDarrick J. Wong <djwong@us.ibm.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 8117f98c
...@@ -2361,6 +2361,21 @@ static int ext3_commit_super(struct super_block *sb, ...@@ -2361,6 +2361,21 @@ static int ext3_commit_super(struct super_block *sb,
if (!sbh) if (!sbh)
return error; return error;
if (buffer_write_io_error(sbh)) {
/*
* Oh, dear. A previous attempt to write the
* superblock failed. This could happen because the
* USB device was yanked out. Or it could happen to
* be a transient write error and maybe the block will
* be remapped. Nothing we can do but to retry the
* write and hope for the best.
*/
ext3_msg(sb, KERN_ERR, "previous I/O error to "
"superblock detected");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
/* /*
* If the file system is mounted read-only, don't update the * If the file system is mounted read-only, don't update the
* superblock write time. This avoids updating the superblock * superblock write time. This avoids updating the superblock
...@@ -2377,8 +2392,15 @@ static int ext3_commit_super(struct super_block *sb, ...@@ -2377,8 +2392,15 @@ static int ext3_commit_super(struct super_block *sb,
es->s_free_inodes_count = cpu_to_le32(ext3_count_free_inodes(sb)); es->s_free_inodes_count = cpu_to_le32(ext3_count_free_inodes(sb));
BUFFER_TRACE(sbh, "marking dirty"); BUFFER_TRACE(sbh, "marking dirty");
mark_buffer_dirty(sbh); mark_buffer_dirty(sbh);
if (sync) if (sync) {
error = sync_dirty_buffer(sbh); error = sync_dirty_buffer(sbh);
if (buffer_write_io_error(sbh)) {
ext3_msg(sb, KERN_ERR, "I/O error while writing "
"superblock");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
}
return error; return error;
} }
......
...@@ -85,6 +85,7 @@ EXPORT_SYMBOL(journal_force_commit); ...@@ -85,6 +85,7 @@ EXPORT_SYMBOL(journal_force_commit);
static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *); static int journal_convert_superblock_v1(journal_t *, journal_superblock_t *);
static void __journal_abort_soft (journal_t *journal, int errno); static void __journal_abort_soft (journal_t *journal, int errno);
static const char *journal_dev_name(journal_t *journal, char *buffer);
/* /*
* Helper function used to manage commit timeouts * Helper function used to manage commit timeouts
...@@ -1011,6 +1012,23 @@ void journal_update_superblock(journal_t *journal, int wait) ...@@ -1011,6 +1012,23 @@ void journal_update_superblock(journal_t *journal, int wait)
goto out; goto out;
} }
if (buffer_write_io_error(bh)) {
char b[BDEVNAME_SIZE];
/*
* Oh, dear. A previous attempt to write the journal
* superblock failed. This could happen because the
* USB device was yanked out. Or it could happen to
* be a transient write error and maybe the block will
* be remapped. Nothing we can do but to retry the
* write and hope for the best.
*/
printk(KERN_ERR "JBD: previous I/O error detected "
"for journal superblock update for %s.\n",
journal_dev_name(journal, b));
clear_buffer_write_io_error(bh);
set_buffer_uptodate(bh);
}
spin_lock(&journal->j_state_lock); spin_lock(&journal->j_state_lock);
jbd_debug(1,"JBD: updating superblock (start %u, seq %d, errno %d)\n", jbd_debug(1,"JBD: updating superblock (start %u, seq %d, errno %d)\n",
journal->j_tail, journal->j_tail_sequence, journal->j_errno); journal->j_tail, journal->j_tail_sequence, journal->j_errno);
...@@ -1022,9 +1040,17 @@ void journal_update_superblock(journal_t *journal, int wait) ...@@ -1022,9 +1040,17 @@ void journal_update_superblock(journal_t *journal, int wait)
BUFFER_TRACE(bh, "marking dirty"); BUFFER_TRACE(bh, "marking dirty");
mark_buffer_dirty(bh); mark_buffer_dirty(bh);
if (wait) if (wait) {
sync_dirty_buffer(bh); sync_dirty_buffer(bh);
else if (buffer_write_io_error(bh)) {
char b[BDEVNAME_SIZE];
printk(KERN_ERR "JBD: I/O error detected "
"when updating journal superblock for %s.\n",
journal_dev_name(journal, b));
clear_buffer_write_io_error(bh);
set_buffer_uptodate(bh);
}
} else
write_dirty_buffer(bh, WRITE); write_dirty_buffer(bh, WRITE);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment