diff options
| author | Qu Wenruo <wqu@suse.com> | 2026-02-04 05:54:08 +0300 |
|---|---|---|
| committer | David Sterba <dsterba@suse.com> | 2026-04-07 19:55:53 +0300 |
| commit | 2672a26a75517a2b4409f2a379ee2bb5c38cff06 (patch) | |
| tree | 6b66df18e1e0f900af85dd2363551a63c7f49951 | |
| parent | c84053d9f7f758c79320716caa098cafc70a74da (diff) | |
| download | linux-2672a26a75517a2b4409f2a379ee2bb5c38cff06.tar.xz | |
btrfs: use per-profile available space in calc_available_free_space()
For the following disk layout, can_overcommit() can cause false
confidence in available space:
devid 1 unallocated: 1GiB
devid 2 unallocated: 50GiB
metadata type: RAID1
As can_overcommit() simply uses unallocated space with factor to
calculate the allocatable metadata chunk size, resulting 25.5GiB
available space.
But in reality we can only allocate one 1GiB RAID1 chunk, the remaining
49GiB on devid 2 will never be utilized to fulfill a RAID1 chunk.
This leads to various ENOSPC related transaction abort and flips the fs
read-only.
Now use per-profile available space in calc_available_free_space(), and
only when that failed we fall back to the old factor based estimation.
And for zoned devices or for the very low chance of temporary memory
allocation failure, we will still fallback to factor based estimation.
But I hope in reality it's very rare.
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
| -rw-r--r-- | fs/btrfs/space-info.c | 27 |
1 files changed, 15 insertions, 12 deletions
diff --git a/fs/btrfs/space-info.c b/fs/btrfs/space-info.c index 87cbc051cb12..0bd671e90643 100644 --- a/fs/btrfs/space-info.c +++ b/fs/btrfs/space-info.c @@ -444,6 +444,7 @@ static u64 calc_available_free_space(const struct btrfs_space_info *space_info, enum btrfs_reserve_flush_enum flush) { struct btrfs_fs_info *fs_info = space_info->fs_info; + bool has_per_profile; u64 profile; u64 avail; u64 data_chunk_size; @@ -454,19 +455,21 @@ static u64 calc_available_free_space(const struct btrfs_space_info *space_info, else profile = btrfs_metadata_alloc_profile(fs_info); - avail = atomic64_read(&fs_info->free_chunk_space); - - /* - * If we have dup, raid1 or raid10 then only half of the free - * space is actually usable. For raid56, the space info used - * doesn't include the parity drive, so we don't have to - * change the math - */ - factor = btrfs_bg_type_to_factor(profile); - avail = div_u64(avail, factor); - if (avail == 0) - return 0; + has_per_profile = btrfs_get_per_profile_avail(fs_info, profile, &avail); + if (!has_per_profile) { + avail = atomic64_read(&fs_info->free_chunk_space); + /* + * If we have dup, raid1 or raid10 then only half of the free + * space is actually usable. For raid56, the space info used + * doesn't include the parity drive, so we don't have to + * change the math + */ + factor = btrfs_bg_type_to_factor(profile); + avail = div_u64(avail, factor); + if (avail == 0) + return 0; + } data_chunk_size = calc_effective_data_chunk_size(fs_info); /* |
