| From 131a821a243f89be312ced9e62ccc37b2cf3846c Mon Sep 17 00:00:00 2001 |
| From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> |
| Date: Sat, 6 Apr 2024 04:45:02 -0400 |
| Subject: btrfs: fallback if compressed IO fails for ENOSPC |
| |
| From: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> |
| |
| commit 131a821a243f89be312ced9e62ccc37b2cf3846c upstream. |
| |
| In commit b4ccace878f4 ("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: b4ccace878f4 ("btrfs: refactor submit_compressed_extents()") |
| CC: stable@vger.kernel.org # 6.1+ |
| Reviewed-by: Qu Wenruo <wqu@suse.com> |
| Co-developed-by: Neal Gompa <neal@gompa.dev> |
| Signed-off-by: Neal Gompa <neal@gompa.dev> |
| Signed-off-by: Sweet Tea Dorminy <sweettea-kernel@dorminy.me> |
| Reviewed-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: David Sterba <dsterba@suse.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| fs/btrfs/inode.c | 13 ++++++------- |
| 1 file changed, 6 insertions(+), 7 deletions(-) |
| |
| --- a/fs/btrfs/inode.c |
| +++ b/fs/btrfs/inode.c |
| @@ -1134,13 +1134,13 @@ static void submit_one_async_extent(stru |
| 0, *alloc_hint, &ins, 1, 1); |
| if (ret) { |
| /* |
| - * Here we used to try again by going back to non-compressed |
| - * path for ENOSPC. But we can't reserve space even for |
| - * compressed size, how could it work for uncompressed size |
| - * which requires larger size? So here we directly go error |
| - * path. |
| + * We can't reserve contiguous space for the compressed size. |
| + * Unlikely, but it's possible that we could have enough |
| + * non-contiguous space for the uncompressed size instead. So |
| + * fall back to uncompressed. |
| */ |
| - goto out_free; |
| + submit_uncompressed_range(inode, async_extent, locked_page); |
| + goto done; |
| } |
| |
| /* Here we're doing allocation and writeback of the compressed pages */ |
| @@ -1192,7 +1192,6 @@ done: |
| out_free_reserve: |
| btrfs_dec_block_group_reservations(fs_info, ins.objectid); |
| btrfs_free_reserved_extent(fs_info, ins.objectid, ins.offset, 1); |
| -out_free: |
| mapping_set_error(inode->vfs_inode.i_mapping, -EIO); |
| extent_clear_unlock_delalloc(inode, start, end, |
| NULL, EXTENT_LOCKED | EXTENT_DELALLOC | |