aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPranay Kr. Srivastava <pranjas@gmail.com>2016-07-04 10:24:52 -0400
committerTheodore Ts'o <tytso@mit.edu>2016-07-04 10:24:52 -0400
commit4743f83990614af6adb09ea7aa3c37b78c4031ab (patch)
tree86784086a26c2d2181270426145540310de8acd0
parent646caa9c8e196880b41cd3e3d33a2ebc752bdb85 (diff)
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>
-rw-r--r--fs/ext4/super.c30
1 files changed, 16 insertions, 14 deletions
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 6e2f9d628c48..5664ee66b301 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -4327,20 +4327,6 @@ static int ext4_commit_super(struct super_block *sb, int sync)
4327 4327
4328 if (!sbh || block_device_ejected(sb)) 4328 if (!sbh || block_device_ejected(sb))
4329 return error; 4329 return error;
4330 if (buffer_write_io_error(sbh)) {
4331 /*
4332 * Oh, dear. A previous attempt to write the
4333 * superblock failed. This could happen because the
4334 * USB device was yanked out. Or it could happen to
4335 * be a transient write error and maybe the block will
4336 * be remapped. Nothing we can do but to retry the
4337 * write and hope for the best.
4338 */
4339 ext4_msg(sb, KERN_ERR, "previous I/O error to "
4340 "superblock detected");
4341 clear_buffer_write_io_error(sbh);
4342 set_buffer_uptodate(sbh);
4343 }
4344 /* 4330 /*
4345 * If the file system is mounted read-only, don't update the 4331 * If the file system is mounted read-only, don't update the
4346 * superblock write time. This avoids updating the superblock 4332 * superblock write time. This avoids updating the superblock
@@ -4371,7 +4357,23 @@ static int ext4_commit_super(struct super_block *sb, int sync)
4371 &EXT4_SB(sb)->s_freeinodes_counter)); 4357 &EXT4_SB(sb)->s_freeinodes_counter));
4372 BUFFER_TRACE(sbh, "marking dirty"); 4358 BUFFER_TRACE(sbh, "marking dirty");
4373 ext4_superblock_csum_set(sb); 4359 ext4_superblock_csum_set(sb);
4360 lock_buffer(sbh);
4361 if (buffer_write_io_error(sbh)) {
4362 /*
4363 * Oh, dear. A previous attempt to write the
4364 * superblock failed. This could happen because the
4365 * USB device was yanked out. Or it could happen to
4366 * be a transient write error and maybe the block will
4367 * be remapped. Nothing we can do but to retry the
4368 * write and hope for the best.
4369 */
4370 ext4_msg(sb, KERN_ERR, "previous I/O error to "
4371 "superblock detected");
4372 clear_buffer_write_io_error(sbh);
4373 set_buffer_uptodate(sbh);
4374 }
4374 mark_buffer_dirty(sbh); 4375 mark_buffer_dirty(sbh);
4376 unlock_buffer(sbh);
4375 if (sync) { 4377 if (sync) {
4376 error = __sync_dirty_buffer(sbh, 4378 error = __sync_dirty_buffer(sbh,
4377 test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC); 4379 test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC);