add patch make-mb-block-cache-names-more-explicit
[ext4-patch-queue.git] / avoid-deadlock-while-expanding-inode-size
blobc6f4ff08d064327b7016cf90f5acbc4cf16a8972
1 ext4: avoid deadlock when expanding inode size
3 From: Jan Kara <jack@suse.cz>
5 When we need to move xattrs into external xattr block, we call
6 ext4_xattr_block_set() from ext4_expand_extra_isize_ea(). That may end
7 up calling ext4_mark_inode_dirty() again which will recurse back into
8 the inode expansion code leading to deadlocks.
10 Protect from recursion using EXT4_STATE_NO_EXPAND inode flag and move
11 its management into ext4_expand_extra_isize_ea() since its manipulation
12 is safe there (due to xattr_sem) from possible races with
13 ext4_xattr_set_handle() which plays with it as well.
15 CC: stable@vger.kernel.org   # 4.4.x
16 Signed-off-by: Jan Kara <jack@suse.cz>
17 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
18 ---
19  fs/ext4/inode.c |  2 --
20  fs/ext4/xattr.c | 19 +++++++++++++------
21  2 files changed, 13 insertions(+), 8 deletions(-)
23 diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
24 index 3131747199e1..c6ea25a190f8 100644
25 --- a/fs/ext4/inode.c
26 +++ b/fs/ext4/inode.c
27 @@ -5466,8 +5466,6 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
28                                                       sbi->s_want_extra_isize,
29                                                       iloc, handle);
30                         if (ret) {
31 -                               ext4_set_inode_state(inode,
32 -                                                    EXT4_STATE_NO_EXPAND);
33                                 if (mnt_count !=
34                                         le16_to_cpu(sbi->s_es->s_mnt_count)) {
35                                         ext4_warning(inode->i_sb,
36 diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
37 index c893f00b6dc0..2eb935ca5d9e 100644
38 --- a/fs/ext4/xattr.c
39 +++ b/fs/ext4/xattr.c
40 @@ -1358,12 +1358,14 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
41         int isize_diff; /* How much do we need to grow i_extra_isize */
43         down_write(&EXT4_I(inode)->xattr_sem);
44 +       /*
45 +        * Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
46 +        */
47 +       ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
48  retry:
49         isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
50 -       if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
51 -               up_write(&EXT4_I(inode)->xattr_sem);
52 -               return 0;
53 -       }
54 +       if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
55 +               goto out;
57         header = IHDR(inode, raw_inode);
58         entry = IFIRST(header);
59 @@ -1392,8 +1394,7 @@ retry:
60                                 (void *)header, total_ino,
61                                 inode->i_sb->s_blocksize);
62                 EXT4_I(inode)->i_extra_isize = new_extra_isize;
63 -               error = 0;
64 -               goto cleanup;
65 +               goto out;
66         }
68         /*
69 @@ -1553,6 +1554,8 @@ retry:
70                 kfree(bs);
71         }
72         brelse(bh);
73 +out:
74 +       ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
75         up_write(&EXT4_I(inode)->xattr_sem);
76         return 0;
78 @@ -1564,6 +1567,10 @@ cleanup:
79         kfree(is);
80         kfree(bs);
81         brelse(bh);
82 +       /*
83 +        * We deliberately leave EXT4_STATE_NO_EXPAND set here since inode
84 +        * size expansion failed.
85 +        */
86         up_write(&EXT4_I(inode)->xattr_sem);
87         return error;
88  }
89 -- 
90 2.6.6