ext4: Fix WARN_ON_ONCE in ext4_commit_super()
If there are racing calls to ext4_commit_super() it's possible for another writeback of the superblock to result in the buffer being marked with an error after we check if the buffer is marked as having a write error and the buffer up-to-date flag is set again. If that happens mark_buffer_dirty() can end up throwing a WARN_ON_ONCE. Fix this by moving this check to write before we call write_buffer_dirty(), and keeping the buffer locked during this whole sequence. Signed-off-by: Pranay Kr. Srivastava <pranjas@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
646caa9c8e
commit
4743f83990
|
@ -4327,20 +4327,6 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
|||
|
||||
if (!sbh || block_device_ejected(sb))
|
||||
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.
|
||||
*/
|
||||
ext4_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
|
||||
* superblock write time. This avoids updating the superblock
|
||||
|
@ -4371,7 +4357,23 @@ static int ext4_commit_super(struct super_block *sb, int sync)
|
|||
&EXT4_SB(sb)->s_freeinodes_counter));
|
||||
BUFFER_TRACE(sbh, "marking dirty");
|
||||
ext4_superblock_csum_set(sb);
|
||||
lock_buffer(sbh);
|
||||
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.
|
||||
*/
|
||||
ext4_msg(sb, KERN_ERR, "previous I/O error to "
|
||||
"superblock detected");
|
||||
clear_buffer_write_io_error(sbh);
|
||||
set_buffer_uptodate(sbh);
|
||||
}
|
||||
mark_buffer_dirty(sbh);
|
||||
unlock_buffer(sbh);
|
||||
if (sync) {
|
||||
error = __sync_dirty_buffer(sbh,
|
||||
test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC);
|
||||
|
|
Loading…
Reference in New Issue