do_allocation_stripe() dedicates one block group to data relocation
(fs_info->data_reloc_bg, as the zoned allocator does): while a
relocation runs, its allocations go only there and everyone else's skip
it, until the relocation finishes or the group fills. The admission
gate did not know: it counted that group's claimable stripes in the
supply for ordinary writes. At the fill edge with background reclaim
running, the dedicated group was the only one with claimable stripes
left, ordinary writes were admitted against them, and their writebacks
were dropped:
stripe_alloc DROP DUMP: sinfo may_use
3837952 claimable
561053696 ...
bg
7048265728 ... claimable
561053696 ... runs open <- data_reloc_bg
every other group: claimable 0
Track the dedicated group's claimable bytes in the space_info
(bytes_stripe_claimable_reloc: set when a group is dedicated, cleared
when the dedication is released or the group is removed, followed by
the per-group claimable deltas and re-summed by the commit rescan) and
admit ordinary reservations against the supply minus that amount.
Relocation itself is admitted against the whole supply, dedicated group
included. Confining it to the dedicated group's bytes was tried first
and refused relocation as soon as that group -- the first one the
allocator visits, often holding only a couple of free stripes -- ran
short of a reservation's whole stripes plus one, while hundreds of
megabytes of whole stripes sat in other groups: the allocator would
have dropped the dedication and moved on had it been asked, but the
gate failed each queued group with ENOSPC within fifteen milliseconds
and the reclaim worker retried every thirty seconds, with no relocation
ever completing. bytes_may_use is one counter for both sides, so the
sum admitted still fits in the supply.
Assisted-by: Claude:claude-fable-5
Assisted-by: Claude:claude-fable-5-1
list_for_each_entry(sinfo, &fs_info->space_info, list) {
u64 total = 0;
u64 total_claimable = 0;
+ u64 total_claimable_reloc = 0;
int raid;
if (!(sinfo->flags & BTRFS_BLOCK_GROUP_DATA))
total += READ_ONCE(bg->stripe_unusable);
total_claimable +=
READ_ONCE(bg->stripe_claimable);
+ if (bg->start == READ_ONCE(fs_info->data_reloc_bg))
+ total_claimable_reloc +=
+ READ_ONCE(bg->stripe_claimable);
}
}
}
spin_lock(&sinfo->lock);
sinfo->bytes_stripe_unusable = total;
sinfo->bytes_stripe_claimable = total_claimable;
+ sinfo->bytes_stripe_claimable_reloc = total_claimable_reloc;
/*
* The rescan usually lowers the counter (mid-transaction
* incremental adds overcount conservatively); admission
{
struct btrfs_fs_info *fs_info = bg->fs_info;
+ bool cleared = false;
+
spin_lock(&fs_info->relocation_bg_lock);
- if (fs_info->data_reloc_bg == bg->start)
+ if (fs_info->data_reloc_bg == bg->start) {
fs_info->data_reloc_bg = 0;
+ cleared = true;
+ }
spin_unlock(&fs_info->relocation_bg_lock);
+ if (cleared && bg->space_info) {
+ spin_lock(&bg->space_info->lock);
+ bg->space_info->bytes_stripe_claimable_reloc = 0;
+ spin_unlock(&bg->space_info->lock);
+ }
}
/*
bg = btrfs_lookup_block_group(fs_info, bytenr);
if (!bg)
return;
+ if (bg->space_info) {
+ spin_lock(&bg->space_info->lock);
+ bg->space_info->bytes_stripe_claimable_reloc = 0;
+ spin_unlock(&bg->space_info->lock);
+ }
btrfs_close_bg_open_stripes(bg);
btrfs_put_block_group(bg);
}
{
struct btrfs_fs_info *fs_info = block_group->fs_info;
enum btrfs_stripe_run_class class = BTRFS_STRIPE_RUN_COW;
+ bool dedicated_now = false;
u64 available = 0;
u64 offset;
bool skip = false;
spin_lock(&fs_info->relocation_bg_lock);
if (ffe_ctl->for_data_reloc) {
class = BTRFS_STRIPE_RUN_RELOC;
- if (!fs_info->data_reloc_bg)
+ if (!fs_info->data_reloc_bg) {
fs_info->data_reloc_bg = block_group->start;
+ dedicated_now = true;
+ }
}
if (fs_info->data_reloc_bg &&
(ffe_ctl->for_data_reloc !=
(block_group->start == fs_info->data_reloc_bg)))
skip = true;
spin_unlock(&fs_info->relocation_bg_lock);
+ if (dedicated_now && block_group->space_info) {
+ /* its claimable stripes now belong to relocation alone */
+ spin_lock(&block_group->space_info->lock);
+ block_group->space_info->bytes_stripe_claimable_reloc =
+ READ_ONCE(block_group->stripe_claimable);
+ spin_unlock(&block_group->space_info->lock);
+ }
if (skip)
return 1;
sinfo->bytes_stripe_claimable = 0;
else
btrfs_space_info_update_bytes_stripe_claimable(sinfo, delta);
+ if (bg->start == READ_ONCE(bg->fs_info->data_reloc_bg)) {
+ if (delta < 0 && sinfo->bytes_stripe_claimable_reloc < (u64)-delta)
+ sinfo->bytes_stripe_claimable_reloc = 0;
+ else
+ sinfo->bytes_stripe_claimable_reloc += delta;
+ }
spin_unlock(&sinfo->lock);
}
-(s64)trapped);
btrfs_space_info_update_bytes_stripe_claimable(bg->space_info,
-(s64)claimable);
+ if (bg->start == READ_ONCE(bg->fs_info->data_reloc_bg))
+ bg->space_info->bytes_stripe_claimable_reloc = 0;
spin_unlock(&bg->space_info->lock);
}
}
* placed): charge it whole stripes plus one, the tail its own open
* runs can lose at the next commit.
*/
- if (reloc)
- bytes = round_up(bytes, unit) + unit;
- return space_info->bytes_may_use + bytes <= space_info->bytes_stripe_claimable;
+ {
+ u64 supply = space_info->bytes_stripe_claimable;
+ const u64 dedicated = space_info->bytes_stripe_claimable_reloc;
+
+ if (reloc) {
+ /*
+ * Against the whole supply, dedicated group included.
+ * Relocation prefers its dedicated group, but when
+ * that group runs dry the allocator drops the
+ * dedication and moves on to another group; confining
+ * admission to the dedicated group's claimable bytes
+ * refused relocation (ENOSPC after a handful of 16K
+ * clusters, retried every 30 seconds) while hundreds
+ * of megabytes of whole stripes sat in other groups.
+ */
+ bytes = round_up(bytes, unit) + unit;
+ } else {
+ /* ...and that group is out of reach for everyone else */
+ supply -= min(supply, dedicated);
+ }
+ return space_info->bytes_may_use + bytes <= supply;
+ }
}
/*
cannot admit writes against it, and
subtracted in statfs (from a different
base -- the free space cache walk). */
+ u64 bytes_stripe_claimable_reloc; /* the part of bytes_stripe_claimable
+ in the group dedicated to data
+ relocation (fs_info->data_reloc_bg):
+ out of reach for other allocations
+ while the dedication lasts */
u64 bytes_stripe_claimable; /* sum of armed groups' directly
measured claimable whole-stripe
bytes; see stripe_claimable in