Commit 4a3560c4 authored by Liu Bo's avatar Liu Bo Committed by Chris Mason

Btrfs: fix defrag to merge tail file extent

The file layout is

[extent 1]...[extent n][4k extent][HOLE][extent x]

extent 1~n and 4k extent can be merged during defrag, and the whole
defrag bytes is larger than our defrag thresh(256k), 4k extent as a
tail is left unmerged since we check if its next extent can be merged
(the next one is a hole, so the check will fail), the layout thus can
be

[new extent][4k extent][HOLE][extent x]
 (1~n)

To fix it, beside looking at the next one, this also looks at the
previous one by checking @defrag_end, which is set to 0 when we
decide to stop merging contiguous extents, otherwise, we can merge
the previous one with our extent.

Also, this makes btrfs behave consistent with how xfs and ext4 do.
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent acdf898d
...@@ -1030,6 +1030,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh, ...@@ -1030,6 +1030,7 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh,
struct extent_map *em; struct extent_map *em;
int ret = 1; int ret = 1;
bool next_mergeable = true; bool next_mergeable = true;
bool prev_mergeable = true;
/* /*
* make sure that once we start defragging an extent, we keep on * make sure that once we start defragging an extent, we keep on
...@@ -1050,13 +1051,16 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh, ...@@ -1050,13 +1051,16 @@ static int should_defrag_range(struct inode *inode, u64 start, u32 thresh,
goto out; goto out;
} }
if (!*defrag_end)
prev_mergeable = false;
next_mergeable = defrag_check_next_extent(inode, em); next_mergeable = defrag_check_next_extent(inode, em);
/* /*
* we hit a real extent, if it is big or the next extent is not a * we hit a real extent, if it is big or the next extent is not a
* real extent, don't bother defragging it * real extent, don't bother defragging it
*/ */
if (!compress && (*last_len == 0 || *last_len >= thresh) && if (!compress && (*last_len == 0 || *last_len >= thresh) &&
(em->len >= thresh || !next_mergeable)) (em->len >= thresh || (!next_mergeable && !prev_mergeable)))
ret = 0; ret = 0;
out: out:
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment