Commit 131a821a authored by Sweet Tea Dorminy's avatar Sweet Tea Dorminy Committed by David Sterba

btrfs: fallback if compressed IO fails for ENOSPC

In commit b4ccace8 ("btrfs: refactor submit_compressed_extents()"), if
an async extent compressed but failed to find enough space, we changed
from falling back to an uncompressed write to just failing the write
altogether. The principle was that if there's not enough space to write
the compressed version of the data, there can't possibly be enough space
to write the larger, uncompressed version of the data.

However, this isn't necessarily true: due to fragmentation, there could
be enough discontiguous free blocks to write the uncompressed version,
but not enough contiguous free blocks to write the smaller but
unsplittable compressed version.

This has occurred to an internal workload which relied on write()'s
return value indicating there was space. While rare, it has happened a
few times.

Thus, in order to prevent early ENOSPC, re-add a fallback to
uncompressed writing.

Fixes: b4ccace8 ("btrfs: refactor submit_compressed_extents()")
CC: stable@vger.kernel.org # 6.1+
Reviewed-by: default avatarQu Wenruo <wqu@suse.com>
Co-developed-by: default avatarNeal Gompa <neal@gompa.dev>
Signed-off-by: default avatarNeal Gompa <neal@gompa.dev>
Signed-off-by: default avatarSweet Tea Dorminy <sweettea-kernel@dorminy.me>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 7192833c
...@@ -1145,13 +1145,13 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, ...@@ -1145,13 +1145,13 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
0, *alloc_hint, &ins, 1, 1); 0, *alloc_hint, &ins, 1, 1);
if (ret) { if (ret) {
/* /*
* Here we used to try again by going back to non-compressed * We can't reserve contiguous space for the compressed size.
* path for ENOSPC. But we can't reserve space even for * Unlikely, but it's possible that we could have enough
* compressed size, how could it work for uncompressed size * non-contiguous space for the uncompressed size instead. So
* which requires larger size? So here we directly go error * fall back to uncompressed.
* path.
*/ */
goto out_free; submit_uncompressed_range(inode, async_extent, locked_page);
goto done;
} }
/* Here we're doing allocation and writeback of the compressed pages */ /* Here we're doing allocation and writeback of the compressed pages */
...@@ -1203,7 +1203,6 @@ static void submit_one_async_extent(struct async_chunk *async_chunk, ...@@ -1203,7 +1203,6 @@ static void submit_one_async_extent(struct async_chunk *async_chunk,
out_free_reserve: out_free_reserve:
btrfs_dec_block_group_reservations(fs_info, ins.objectid); btrfs_dec_block_group_reservations(fs_info, ins.objectid);
btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 1); btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 1);
out_free:
mapping_set_error(inode->vfs_inode.i_mapping, -EIO); mapping_set_error(inode->vfs_inode.i_mapping, -EIO);
extent_clear_unlock_delalloc(inode, start, end, extent_clear_unlock_delalloc(inode, start, end,
NULL, EXTENT_LOCKED | EXTENT_DELALLOC | NULL, EXTENT_LOCKED | EXTENT_DELALLOC |
......
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