[PATCH v2 2/2] btrfs: zoned: don't treat a negative zone reservation as free zones
From: Mark Korenberg via B4 Relay
Date: Wed Nov 04 2026 - 13:48:52 EST
From: Mark Korenberg <socketpair@xxxxxxxxx>
reserved_active_zones goes negative when more metadata and system block
groups are active than were reserved. btrfs_can_activate_zone() then
reports a free active zone for data even when none is left, so the
allocator neither finishes a zone nor waits for one. find_free_extent()
fails with -ENOSPC on a device with plenty of space and cow_file_range()
drops the dirty range; only the next fsync() or syncfs() reports it.
A negative reservation holds nothing back. Treat it as zero for data
block groups.
Fixes: a7e1ac7bdc5a ("btrfs: zoned: reserve zones for an active metadata/system block group")
Cc: stable@xxxxxxxxxxxxxxx # 7.0.x
Signed-off-by: Mark Korenberg <socketpair@xxxxxxxxx>
---
fs/btrfs/zoned.c | 17 ++++++++++++-----
fs/btrfs/zoned.h | 6 +++++-
2 files changed, 17 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/zoned.c b/fs/btrfs/zoned.c
index 1d64c6e162ca..20b99325117b 100644
--- a/fs/btrfs/zoned.c
+++ b/fs/btrfs/zoned.c
@@ -2465,12 +2465,13 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group)
if (zinfo->max_active_zones == 0)
continue;
- if (is_data)
- reserved = zinfo->reserved_active_zones;
/*
- * For the data block group, leave active zones for one
- * metadata block group and one system block group.
+ * For the data block group, leave the active zones reserved
+ * for metadata, tree-log and system block groups. A negative
+ * reservation holds nothing back, see btrfs_can_activate_zone().
*/
+ if (is_data)
+ reserved = max(zinfo->reserved_active_zones, 0);
if (atomic_read(&zinfo->active_zones_left) <= reserved) {
ret = false;
goto out_unlock;
@@ -2744,8 +2745,14 @@ bool btrfs_can_activate_zone(struct btrfs_fs_devices *fs_devices, u64 flags)
break;
}
+ /*
+ * A negative reservation holds nothing back. Do not count it
+ * as free zones: the check below would pass with no active
+ * zone left, and the caller would allocate a chunk it cannot
+ * activate instead of finishing a zone, then fail with -ENOSPC.
+ */
if (flags & BTRFS_BLOCK_GROUP_DATA)
- reserved = zinfo->reserved_active_zones;
+ reserved = max(zinfo->reserved_active_zones, 0);
switch (flags & BTRFS_BLOCK_GROUP_PROFILE_MASK) {
case 0: /* single */
diff --git a/fs/btrfs/zoned.h b/fs/btrfs/zoned.h
index 510e9d118c7c..2225090839f3 100644
--- a/fs/btrfs/zoned.h
+++ b/fs/btrfs/zoned.h
@@ -38,7 +38,11 @@ struct btrfs_zoned_device_info {
u32 nr_zones;
unsigned int max_active_zones;
/*
- * Reserved active zones for one metadata and one system block group.
+ * Reserved active zones for one metadata, one tree-log and one system
+ * block group: the reservation made at mount minus the zones of such
+ * block groups active on this device now. It is negative when more of
+ * them are active than were reserved, which does not mean that there
+ * are extra active zones.
* It can vary per-device depending on the allocation status.
*/
int reserved_active_zones;
--
2.56.0