add patch fix-bigalloc-cluster-free-when-hole-punching-under-load
[ext4-patch-queue.git] / fix-bigalloc-cluster-free-when-hole-punching-under-load
blob541665215a4ab07cc1dcc32c176550021c57f037
1 ext4: fix bigalloc cluster freeing when hole punching under load
3 From: Eric Whitney <enwlinux@gmail.com>
5 Ext4 may not free clusters correctly when punching holes in bigalloc
6 file systems under high load conditions.  If it's not possible to
7 extend and restart the journal in ext4_ext_rm_leaf() when preparing to
8 remove blocks from a punched region, a retry of the entire punch
9 operation is triggered in ext4_ext_remove_space().  This causes a
10 partial cluster to be set to the first cluster in the extent found to
11 the right of the punched region.  However, if the punch operation
12 prior to the retry had made enough progress to delete one or more
13 extents and a partial cluster candidate for freeing had already been
14 recorded, the retry would overwrite the partial cluster.  The loss of
15 this information makes it impossible to correctly free the original
16 partial cluster in all cases.
18 This bug can cause generic/476 to fail when run as part of
19 xfstests-bld's bigalloc and bigalloc_1k test cases.  The failure is
20 reported when e2fsck detects bad iblocks counts greater than expected
21 in units of whole clusters and also detects a number of negative block
22 bitmap differences equal to the iblocks discrepancy in cluster units.
24 Signed-off-by: Eric Whitney <enwlinux@gmail.com>
25 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
26 ---
27  fs/ext4/extents.c | 17 ++++++++++-------
28  1 file changed, 10 insertions(+), 7 deletions(-)
30 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
31 index 240b6dea5441..252bbbb5a2f4 100644
32 --- a/fs/ext4/extents.c
33 +++ b/fs/ext4/extents.c
34 @@ -2956,14 +2956,17 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
35                         if (err < 0)
36                                 goto out;
38 -               } else if (sbi->s_cluster_ratio > 1 && end >= ex_end) {
39 +               } else if (sbi->s_cluster_ratio > 1 && end >= ex_end &&
40 +                          partial.state == initial) {
41                         /*
42 -                        * If there's an extent to the right its first cluster
43 -                        * contains the immediate right boundary of the
44 -                        * truncated/punched region.  Set partial_cluster to
45 -                        * its negative value so it won't be freed if shared
46 -                        * with the current extent.  The end < ee_block case
47 -                        * is handled in ext4_ext_rm_leaf().
48 +                        * If we're punching, there's an extent to the right.
49 +                        * If the partial cluster hasn't been set, set it to
50 +                        * that extent's first cluster and its state to nofree
51 +                        * so it won't be freed should it contain blocks to be
52 +                        * removed. If it's already set (tofree/nofree), we're
53 +                        * retrying and keep the original partial cluster info
54 +                        * so a cluster marked tofree as a result of earlier
55 +                        * extent removal is not lost.
56                          */
57                         lblk = ex_end + 1;
58                         err = ext4_ext_search_right(inode, path, &lblk, &pblk,
59 -- 
60 2.11.0