Skip to content

Commit

Permalink
ext4: fix partial cluster handling for bigalloc file systems
Browse files Browse the repository at this point in the history
Commit 9cb0041, which enables hole punching for bigalloc file
systems, exposed a bug introduced by commit 6ae06ff in an earlier
release.  When run on a bigalloc file system, xfstests generic/013, 068,
075, 083, 091, 100, 112, 127, 263, 269, and 270 fail with e2fsck errors
or cause kernel error messages indicating that previously freed blocks
are being freed again.

The latter commit optimizes the selection of the starting extent in
ext4_ext_rm_leaf() when hole punching by beginning with the extent
supplied in the path argument rather than with the last extent in the
leaf node (as is still done when truncating).  However, the code in
rm_leaf that initially sets partial_cluster to track cluster sharing on
extent boundaries is only guaranteed to run if rm_leaf starts with the
last node in the leaf.  Consequently, partial_cluster is not correctly
initialized when hole punching, and a cluster on the boundary of a
punched region that should be retained may instead be deallocated.

Signed-off-by: Eric Whitney <enwlinux@gmail.com>
Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Cc: stable@vger.kernel.org
  • Loading branch information
Eric Whitney authored and Theodore Ts'o committed Mar 14, 2014
1 parent 31cf0f2 commit c063449
Showing 1 changed file with 21 additions and 0 deletions.
21 changes: 21 additions & 0 deletions fs/ext4/extents.c
Original file line number Diff line number Diff line change
Expand Up @@ -2597,6 +2597,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
ex_ee_block = le32_to_cpu(ex->ee_block);
ex_ee_len = ext4_ext_get_actual_len(ex);

/*
* If we're starting with an extent other than the last one in the
* node, we need to see if it shares a cluster with the extent to
* the right (towards the end of the file). If its leftmost cluster
* is this extent's rightmost cluster and it is not cluster aligned,
* we'll mark it as a partial that is not to be deallocated.
*/

if (ex != EXT_LAST_EXTENT(eh)) {
ext4_fsblk_t current_pblk, right_pblk;
long long current_cluster, right_cluster;

current_pblk = ext4_ext_pblock(ex) + ex_ee_len - 1;
current_cluster = (long long)EXT4_B2C(sbi, current_pblk);
right_pblk = ext4_ext_pblock(ex + 1);
right_cluster = (long long)EXT4_B2C(sbi, right_pblk);
if (current_cluster == right_cluster &&
EXT4_PBLK_COFF(sbi, right_pblk))
*partial_cluster = -right_cluster;
}

trace_ext4_ext_rm_leaf(inode, start, ex, *partial_cluster);

while (ex >= EXT_FIRST_EXTENT(eh) &&
Expand Down

0 comments on commit c063449

Please sign in to comment.