Skip to content
This repository was archived by the owner on Sep 24, 2020. It is now read-only.

Commit 2e81a4e

Browse files
jankaratytso
authored andcommitted
ext4: avoid deadlock when expanding inode size
When we need to move xattrs into external xattr block, we call ext4_xattr_block_set() from ext4_expand_extra_isize_ea(). That may end up calling ext4_mark_inode_dirty() again which will recurse back into the inode expansion code leading to deadlocks. Protect from recursion using EXT4_STATE_NO_EXPAND inode flag and move its management into ext4_expand_extra_isize_ea() since its manipulation is safe there (due to xattr_sem) from possible races with ext4_xattr_set_handle() which plays with it as well. CC: [email protected] # 4.4.x Signed-off-by: Jan Kara <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]>
1 parent 443a8c4 commit 2e81a4e

File tree

2 files changed

+13
-8
lines changed

2 files changed

+13
-8
lines changed

fs/ext4/inode.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5466,8 +5466,6 @@ int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
54665466
sbi->s_want_extra_isize,
54675467
iloc, handle);
54685468
if (ret) {
5469-
ext4_set_inode_state(inode,
5470-
EXT4_STATE_NO_EXPAND);
54715469
if (mnt_count !=
54725470
le16_to_cpu(sbi->s_es->s_mnt_count)) {
54735471
ext4_warning(inode->i_sb,

fs/ext4/xattr.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,12 +1358,14 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
13581358
int isize_diff; /* How much do we need to grow i_extra_isize */
13591359

13601360
down_write(&EXT4_I(inode)->xattr_sem);
1361+
/*
1362+
* Set EXT4_STATE_NO_EXPAND to avoid recursion when marking inode dirty
1363+
*/
1364+
ext4_set_inode_state(inode, EXT4_STATE_NO_EXPAND);
13611365
retry:
13621366
isize_diff = new_extra_isize - EXT4_I(inode)->i_extra_isize;
1363-
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize) {
1364-
up_write(&EXT4_I(inode)->xattr_sem);
1365-
return 0;
1366-
}
1367+
if (EXT4_I(inode)->i_extra_isize >= new_extra_isize)
1368+
goto out;
13671369

13681370
header = IHDR(inode, raw_inode);
13691371
entry = IFIRST(header);
@@ -1392,8 +1394,7 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
13921394
(void *)header, total_ino,
13931395
inode->i_sb->s_blocksize);
13941396
EXT4_I(inode)->i_extra_isize = new_extra_isize;
1395-
error = 0;
1396-
goto cleanup;
1397+
goto out;
13971398
}
13981399

13991400
/*
@@ -1553,6 +1554,8 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
15531554
kfree(bs);
15541555
}
15551556
brelse(bh);
1557+
out:
1558+
ext4_clear_inode_state(inode, EXT4_STATE_NO_EXPAND);
15561559
up_write(&EXT4_I(inode)->xattr_sem);
15571560
return 0;
15581561

@@ -1564,6 +1567,10 @@ int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize,
15641567
kfree(is);
15651568
kfree(bs);
15661569
brelse(bh);
1570+
/*
1571+
* We deliberately leave EXT4_STATE_NO_EXPAND set here since inode
1572+
* size expansion failed.
1573+
*/
15671574
up_write(&EXT4_I(inode)->xattr_sem);
15681575
return error;
15691576
}

0 commit comments

Comments
 (0)