if (!unit || !sinfo || mod == 0)
return;
+ /*
+ * Relocation's extents are already placed; its reservations are
+ * charged a margin of their own in stripe_claimable_admit().
+ */
+ if (btrfs_is_data_reloc_root(inode->root))
+ return;
if (btrfs_test_opt(fs_info, STRIPE_ALLOC)) {
/* charges and releases both live */
} else if (mod > 0 || READ_ONCE(sinfo->bytes_stripe_margin) == 0) {
* FLUSH_DATA ticket retry). A later phase adds bytes_stripe_open to the bound to
* match statfs exactly and recover that space.
*/
-static bool stripe_claimable_admit(struct btrfs_space_info *space_info, u64 bytes)
+static bool stripe_claimable_admit(struct btrfs_space_info *space_info, u64 bytes,
+ bool reloc)
{
struct btrfs_fs_info *fs_info = space_info->fs_info;
+ const u64 unit = READ_ONCE(fs_info->stripe_margin_unit);
if (!btrfs_test_opt(fs_info, STRIPE_ALLOC) ||
- !(space_info->flags & BTRFS_BLOCK_GROUP_DATA) ||
- !READ_ONCE(fs_info->stripe_margin_unit))
+ !(space_info->flags & BTRFS_BLOCK_GROUP_DATA) || !unit)
return true;
+ /*
+ * Relocation carries no per-extent margin (its extents are already
+ * 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;
}
if (((used + ticket->bytes <= space_info->total_bytes) ||
btrfs_can_overcommit(fs_info, space_info, ticket->bytes,
flush)) &&
- stripe_claimable_admit(space_info, ticket->bytes)) {
+ stripe_claimable_admit(space_info, ticket->bytes, false)) {
btrfs_space_info_update_bytes_may_use(space_info, ticket->bytes);
remove_ticket(space_info, ticket);
ticket->bytes = 0;
if (!pending_tickets &&
((used + orig_bytes <= space_info->total_bytes) ||
btrfs_can_overcommit(fs_info, space_info, orig_bytes, flush)) &&
- stripe_claimable_admit(space_info, orig_bytes)) {
+ stripe_claimable_admit(space_info, orig_bytes,
+ flush == BTRFS_RESERVE_FLUSH_DATA_RELOC)) {
btrfs_space_info_update_bytes_may_use(space_info, orig_bytes);
ret = 0;
}