Skip to content

Commit 6fed839

Browse files
lostjeffletytso
authored andcommitted
ext4: fix reserved space counter leakage
When ext4_insert_delayed block receives and recovers from an error from ext4_es_insert_delayed_block(), e.g., ENOMEM, it does not release the space it has reserved for that block insertion as it should. One effect of this bug is that s_dirtyclusters_counter is not decremented and remains incorrectly elevated until the file system has been unmounted. This can result in premature ENOSPC returns and apparent loss of free space. Another effect of this bug is that /sys/fs/ext4/<dev>/delayed_allocation_blocks can remain non-zero even after syncfs has been executed on the filesystem. Besides, add check for s_dirtyclusters_counter when inode is going to be evicted and freed. s_dirtyclusters_counter can still keep non-zero until inode is written back in .evict_inode(), and thus the check is delayed to .destroy_inode(). Fixes: 51865fd ("ext4: let ext4 maintain extent status tree") Cc: [email protected] Suggested-by: Gao Xiang <[email protected]> Signed-off-by: Jeffle Xu <[email protected]> Reviewed-by: Eric Whitney <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent a2c2f08 commit 6fed839

File tree

2 files changed

+11
-0
lines changed

2 files changed

+11
-0
lines changed

fs/ext4/inode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,6 +1628,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
16281628
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
16291629
int ret;
16301630
bool allocated = false;
1631+
bool reserved = false;
16311632

16321633
/*
16331634
* If the cluster containing lblk is shared with a delayed,
@@ -1644,6 +1645,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
16441645
ret = ext4_da_reserve_space(inode);
16451646
if (ret != 0) /* ENOSPC */
16461647
goto errout;
1648+
reserved = true;
16471649
} else { /* bigalloc */
16481650
if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk)) {
16491651
if (!ext4_es_scan_clu(inode,
@@ -1656,6 +1658,7 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
16561658
ret = ext4_da_reserve_space(inode);
16571659
if (ret != 0) /* ENOSPC */
16581660
goto errout;
1661+
reserved = true;
16591662
} else {
16601663
allocated = true;
16611664
}
@@ -1666,6 +1669,8 @@ static int ext4_insert_delayed_block(struct inode *inode, ext4_lblk_t lblk)
16661669
}
16671670

16681671
ret = ext4_es_insert_delayed_block(inode, lblk, allocated);
1672+
if (ret && reserved)
1673+
ext4_da_release_space(inode, 1);
16691674

16701675
errout:
16711676
return ret;

fs/ext4/super.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1352,6 +1352,12 @@ static void ext4_destroy_inode(struct inode *inode)
13521352
true);
13531353
dump_stack();
13541354
}
1355+
1356+
if (EXT4_I(inode)->i_reserved_data_blocks)
1357+
ext4_msg(inode->i_sb, KERN_ERR,
1358+
"Inode %lu (%p): i_reserved_data_blocks (%u) not cleared!",
1359+
inode->i_ino, EXT4_I(inode),
1360+
EXT4_I(inode)->i_reserved_data_blocks);
13551361
}
13561362

13571363
static void init_once(void *foo)

0 commit comments

Comments
 (0)