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: 51865fda28e5 ("ext4: let ext4 maintain extent status tree")
Cc: stable@kernel.org
Suggested-by: Gao Xiang <hsiangkao@linux.alibaba.com>
Signed-off-by: Jeffle Xu <jefflexu@linux.alibaba.com>
Reviewed-by: Eric Whitney <enwlinux@gmail.com>
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Link: https://lore.kernel.org/r/20210823061358.84473-1-jefflexu@linux.alibaba.com

authored by Jeffle Xu and committed by Theodore Ts'o 6fed8395 a2c2f082

+11
+5
fs/ext4/inode.c
··· 1628 1628 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 1629 1629 int ret; 1630 1630 bool allocated = false; 1631 + bool reserved = false; 1631 1632 1632 1633 /* 1633 1634 * If the cluster containing lblk is shared with a delayed, ··· 1645 1644 ret = ext4_da_reserve_space(inode); 1646 1645 if (ret != 0) /* ENOSPC */ 1647 1646 goto errout; 1647 + reserved = true; 1648 1648 } else { /* bigalloc */ 1649 1649 if (!ext4_es_scan_clu(inode, &ext4_es_is_delonly, lblk)) { 1650 1650 if (!ext4_es_scan_clu(inode, ··· 1658 1656 ret = ext4_da_reserve_space(inode); 1659 1657 if (ret != 0) /* ENOSPC */ 1660 1658 goto errout; 1659 + reserved = true; 1661 1660 } else { 1662 1661 allocated = true; 1663 1662 } ··· 1669 1666 } 1670 1667 1671 1668 ret = ext4_es_insert_delayed_block(inode, lblk, allocated); 1669 + if (ret && reserved) 1670 + ext4_da_release_space(inode, 1); 1672 1671 1673 1672 errout: 1674 1673 return ret;
+6
fs/ext4/super.c
··· 1352 1352 true); 1353 1353 dump_stack(); 1354 1354 } 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); 1355 1361 } 1356 1362 1357 1363 static void init_once(void *foo)