add patch migrate-into-vfs-crypto-engine
[ext4-patch-queue.git] / fix-warn-on-once-in-ext4_commit_super
blob6463563a8a5665b83402608be81bb5fc8e4616a3
1 ext4: Fix WARN_ON_ONCE in ext4_commit_super()
3 From: "Pranay Kr. Srivastava" <pranjas@gmail.com>
5 If there are racing calls to ext4_commit_super() it's possible for
6 another writeback of the superblock to result in the buffer being
7 marked with an error after we check if the buffer is marked as having
8 a write error and the buffer up-to-date flag is set again.  If that
9 happens mark_buffer_dirty() can end up throwing a WARN_ON_ONCE.
11 Fix this by moving this check to write before we call
12 write_buffer_dirty(), and keeping the buffer locked during this whole
13 sequence.
15 Signed-off-by: Pranay Kr. Srivastava <pranjas@gmail.com>
16 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
17 ---
18  fs/ext4/super.c | 30 ++++++++++++++++--------------
19  1 file changed, 16 insertions(+), 14 deletions(-)
21 diff --git a/fs/ext4/super.c b/fs/ext4/super.c
22 index 6e2f9d6..5664ee6 100644
23 --- a/fs/ext4/super.c
24 +++ b/fs/ext4/super.c
25 @@ -4327,20 +4327,6 @@ static int ext4_commit_super(struct super_block *sb, int sync)
27         if (!sbh || block_device_ejected(sb))
28                 return error;
29 -       if (buffer_write_io_error(sbh)) {
30 -               /*
31 -                * Oh, dear.  A previous attempt to write the
32 -                * superblock failed.  This could happen because the
33 -                * USB device was yanked out.  Or it could happen to
34 -                * be a transient write error and maybe the block will
35 -                * be remapped.  Nothing we can do but to retry the
36 -                * write and hope for the best.
37 -                */
38 -               ext4_msg(sb, KERN_ERR, "previous I/O error to "
39 -                      "superblock detected");
40 -               clear_buffer_write_io_error(sbh);
41 -               set_buffer_uptodate(sbh);
42 -       }
43         /*
44          * If the file system is mounted read-only, don't update the
45          * superblock write time.  This avoids updating the superblock
46 @@ -4371,7 +4357,23 @@ static int ext4_commit_super(struct super_block *sb, int sync)
47                                 &EXT4_SB(sb)->s_freeinodes_counter));
48         BUFFER_TRACE(sbh, "marking dirty");
49         ext4_superblock_csum_set(sb);
50 +       lock_buffer(sbh);
51 +       if (buffer_write_io_error(sbh)) {
52 +               /*
53 +                * Oh, dear.  A previous attempt to write the
54 +                * superblock failed.  This could happen because the
55 +                * USB device was yanked out.  Or it could happen to
56 +                * be a transient write error and maybe the block will
57 +                * be remapped.  Nothing we can do but to retry the
58 +                * write and hope for the best.
59 +                */
60 +               ext4_msg(sb, KERN_ERR, "previous I/O error to "
61 +                      "superblock detected");
62 +               clear_buffer_write_io_error(sbh);
63 +               set_buffer_uptodate(sbh);
64 +       }
65         mark_buffer_dirty(sbh);
66 +       unlock_buffer(sbh);
67         if (sync) {
68                 error = __sync_dirty_buffer(sbh,
69                         test_opt(sb, BARRIER) ? WRITE_FUA : WRITE_SYNC);