Skip to content

Commit 46de2e5

Browse files
Eric Whitneyjosephhz
authored andcommitted
ext4: fix bigalloc cluster freeing when hole punching under load
commit 7bd7523 upstream. Ext4 may not free clusters correctly when punching holes in bigalloc file systems under high load conditions. If it's not possible to extend and restart the journal in ext4_ext_rm_leaf() when preparing to remove blocks from a punched region, a retry of the entire punch operation is triggered in ext4_ext_remove_space(). This causes a partial cluster to be set to the first cluster in the extent found to the right of the punched region. However, if the punch operation prior to the retry had made enough progress to delete one or more extents and a partial cluster candidate for freeing had already been recorded, the retry would overwrite the partial cluster. The loss of this information makes it impossible to correctly free the original partial cluster in all cases. This bug can cause generic/476 to fail when run as part of xfstests-bld's bigalloc and bigalloc_1k test cases. The failure is reported when e2fsck detects bad iblocks counts greater than expected in units of whole clusters and also detects a number of negative block bitmap differences equal to the iblocks discrepancy in cluster units. Signed-off-by: Eric Whitney <enwlinux@gmail.com> Signed-off-by: Theodore Ts'o <tytso@mit.edu> Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com>
1 parent f2d52af commit 46de2e5

1 file changed

Lines changed: 10 additions & 7 deletions

File tree

fs/ext4/extents.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2969,14 +2969,17 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
29692969
if (err < 0)
29702970
goto out;
29712971

2972-
} else if (sbi->s_cluster_ratio > 1 && end >= ex_end) {
2972+
} else if (sbi->s_cluster_ratio > 1 && end >= ex_end &&
2973+
partial.state == initial) {
29732974
/*
2974-
* If there's an extent to the right its first cluster
2975-
* contains the immediate right boundary of the
2976-
* truncated/punched region. Set partial_cluster to
2977-
* its negative value so it won't be freed if shared
2978-
* with the current extent. The end < ee_block case
2979-
* is handled in ext4_ext_rm_leaf().
2975+
* If we're punching, there's an extent to the right.
2976+
* If the partial cluster hasn't been set, set it to
2977+
* that extent's first cluster and its state to nofree
2978+
* so it won't be freed should it contain blocks to be
2979+
* removed. If it's already set (tofree/nofree), we're
2980+
* retrying and keep the original partial cluster info
2981+
* so a cluster marked tofree as a result of earlier
2982+
* extent removal is not lost.
29802983
*/
29812984
lblk = ex_end + 1;
29822985
err = ext4_ext_search_right(inode, path, &lblk, &pblk,

0 commit comments

Comments
 (0)