Skip to content

Commit e1cbfd7

Browse files
committed
Btrfs: send, fix file hole not being preserved due to inline extent
Normally we don't have inline extents followed by regular extents, but there's currently at least one harmless case where this happens. For example, when the page size is 4Kb and compression is enabled: $ mkfs.btrfs -f /dev/sdb $ mount -o compress /dev/sdb /mnt $ xfs_io -f -c "pwrite -S 0xaa 0 4K" -c "fsync" /mnt/foobar $ xfs_io -c "pwrite -S 0xbb 8K 4K" -c "fsync" /mnt/foobar In this case we get a compressed inline extent, representing 4Kb of data, followed by a hole extent and then a regular data extent. The inline extent was not expanded/converted to a regular extent exactly because it represents 4Kb of data. This does not cause any apparent problem (such as the issue solved by commit e1699d2 ("btrfs: add missing memset while reading compressed inline extents")) except trigger an unexpected case in the incremental send code path that makes us issue an operation to write a hole when it's not needed, resulting in more writes at the receiver and wasting space at the receiver. So teach the incremental send code to deal with this particular case. The issue can be currently triggered by running fstests btrfs/137 with compression enabled (MOUNT_OPTIONS="-o compress" ./check btrfs/137). Signed-off-by: Filipe Manana <[email protected]> Reviewed-by: Liu Bo <[email protected]>
1 parent be2d253 commit e1cbfd7

File tree

1 file changed

+21
-2
lines changed

1 file changed

+21
-2
lines changed

fs/btrfs/send.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5184,13 +5184,19 @@ static int is_extent_unchanged(struct send_ctx *sctx,
51845184
while (key.offset < ekey->offset + left_len) {
51855185
ei = btrfs_item_ptr(eb, slot, struct btrfs_file_extent_item);
51865186
right_type = btrfs_file_extent_type(eb, ei);
5187-
if (right_type != BTRFS_FILE_EXTENT_REG) {
5187+
if (right_type != BTRFS_FILE_EXTENT_REG &&
5188+
right_type != BTRFS_FILE_EXTENT_INLINE) {
51885189
ret = 0;
51895190
goto out;
51905191
}
51915192

51925193
right_disknr = btrfs_file_extent_disk_bytenr(eb, ei);
5193-
right_len = btrfs_file_extent_num_bytes(eb, ei);
5194+
if (right_type == BTRFS_FILE_EXTENT_INLINE) {
5195+
right_len = btrfs_file_extent_inline_len(eb, slot, ei);
5196+
right_len = PAGE_ALIGN(right_len);
5197+
} else {
5198+
right_len = btrfs_file_extent_num_bytes(eb, ei);
5199+
}
51945200
right_offset = btrfs_file_extent_offset(eb, ei);
51955201
right_gen = btrfs_file_extent_generation(eb, ei);
51965202

@@ -5204,6 +5210,19 @@ static int is_extent_unchanged(struct send_ctx *sctx,
52045210
goto out;
52055211
}
52065212

5213+
/*
5214+
* We just wanted to see if when we have an inline extent, what
5215+
* follows it is a regular extent (wanted to check the above
5216+
* condition for inline extents too). This should normally not
5217+
* happen but it's possible for example when we have an inline
5218+
* compressed extent representing data with a size matching
5219+
* the page size (currently the same as sector size).
5220+
*/
5221+
if (right_type == BTRFS_FILE_EXTENT_INLINE) {
5222+
ret = 0;
5223+
goto out;
5224+
}
5225+
52075226
left_offset_fixed = left_offset;
52085227
if (key.offset < ekey->offset) {
52095228
/* Fix the right offset for 2a and 7. */

0 commit comments

Comments
 (0)