spin_lock(&sinfo->lock);
sinfo->bytes_stripe_unusable = total;
+ /*
+ * The rescan usually lowers the counter (mid-transaction
+ * incremental adds overcount conservatively); admission
+ * tickets may be waiting on exactly that headroom.
+ */
+ btrfs_try_granting_tickets(sinfo);
spin_unlock(&sinfo->lock);
}
}
return btrfs_ino(inode) != BTRFS_BTREE_INODE_OBJECTID;
}
+void btrfs_stripe_margin_mod(struct btrfs_inode *inode, int mod);
+
static inline void btrfs_mod_outstanding_extents(struct btrfs_inode *inode,
int mod)
{
return;
trace_btrfs_inode_mod_outstanding_extents(inode->root, btrfs_ino(inode),
mod, inode->outstanding_extents);
+ btrfs_stripe_margin_mod(inode, mod);
}
/*
{
struct btrfs_fs_info *fs_info = inode->root->fs_info;
enum btrfs_reserve_flush_enum flush = BTRFS_RESERVE_FLUSH_DATA;
+ u64 stripe_margin = 0;
int ret;
/* align the range */
else if (btrfs_is_free_space_inode(inode))
flush = BTRFS_RESERVE_FLUSH_FREE_SPACE_INODE;
- ret = btrfs_reserve_data_bytes(data_sinfo_for_inode(inode), len, flush);
+ /*
+ * stripe_alloc: probe for the pessimistic reservation margin on top
+ * of the bytes themselves, so the ticketed flush below has to
+ * produce enough claimable whole stripes to cover this write's
+ * worst case, and a writer that cannot be covered gets an honest
+ * ENOSPC here rather than a dropped writeback later. The margin is
+ * released again immediately: the moment the range becomes delalloc,
+ * btrfs_mod_outstanding_extents() re-charges the same worst case
+ * into bytes_stripe_margin, which it then carries until writeback.
+ */
+ if (flush == BTRFS_RESERVE_FLUSH_DATA &&
+ btrfs_test_opt(fs_info, STRIPE_ALLOC)) {
+ const u64 unit = READ_ONCE(fs_info->stripe_margin_unit);
+
+ if (unit)
+ stripe_margin = (u64)count_max_extents(fs_info, len) * unit;
+ }
+
+ ret = btrfs_reserve_data_bytes(data_sinfo_for_inode(inode),
+ len + stripe_margin, flush);
if (ret < 0)
return ret;
+ if (stripe_margin)
+ btrfs_space_info_free_bytes_may_use(data_sinfo_for_inode(inode),
+ stripe_margin);
/* Use new btrfs_qgroup_reserve_data to reserve precious data space. */
ret = btrfs_qgroup_reserve_data(inode, reserved, start, len);
num_bytes = min(num_bytes >> 1, ins->offset);
num_bytes = round_down(num_bytes,
fs_info->sectorsize);
+ /*
+ * stripe_alloc: when a data allocation must be split
+ * across free space fragments, keep every non-final
+ * piece a whole-stripe multiple so at most one piece
+ * per delalloc extent can strand a sub-stripe tail.
+ * The reservation margin pre-paid exactly one tail;
+ * splitting must not mint more.
+ */
+ if (is_data && btrfs_test_opt(fs_info, STRIPE_ALLOC)) {
+ const u64 unit =
+ READ_ONCE(fs_info->stripe_margin_unit);
+
+ /* Not a power of two: rounddown(), not round_down(). */
+ if (unit && num_bytes > unit)
+ num_bytes = rounddown(num_bytes, unit);
+ }
num_bytes = max(num_bytes, min_alloc_size);
ram_bytes = num_bytes;
if (num_bytes == min_alloc_size)
btrfs_dump_space_info(fs_info, sinfo,
num_bytes, 1);
}
+ /*
+ * The pessimistic reservation margin should make a data
+ * writeback allocation failure impossible: admission held
+ * back a full stripe per delalloc extent. If it happens
+ * anyway the pages are dropped, so say so loudly.
+ */
+ if (is_data && delalloc && btrfs_test_opt(fs_info, STRIPE_ALLOC))
+ btrfs_warn_rl(fs_info,
+ "stripe_alloc: data writeback allocation of %llu bytes returned ENOSPC despite reservation margin; buffered data in this range will be dropped",
+ num_bytes);
}
return ret;
static void stripe_unusable_mark_dirty(struct btrfs_block_group *bg);
+/*
+ * stripe_alloc: count the sub-stripe fragments of a returning free range
+ * as trapped immediately, without waiting for the commit-time rescan.
+ *
+ * The rescan remains the authority, but reservations (which now carry the
+ * pessimistic per-extent margin) read bytes_stripe_unusable in the middle
+ * of a transaction, and any mid-transaction return of free space -- an
+ * unpinned deletion, a same-transaction free, a drained run tail, a freed
+ * reservation -- otherwise leaves its fragments backing admissions the
+ * claim rule can never honor. Whole stripes lying fully inside the
+ * returned range are trivially fully free (hence claimable) and are not
+ * counted; the partial head and tail fragments are counted trapped even
+ * though neighbouring free space may complete their stripes. That is a
+ * bounded overcount in the safe direction (early ENOSPC at admission,
+ * reconciled by the next rescan), never an undercount.
+ */
+static void stripe_unusable_account_add(struct btrfs_block_group *bg,
+ u64 bytenr, u64 size)
+{
+ struct btrfs_space_info *sinfo = bg->space_info;
+ const u64 fsl = bg->full_stripe_len;
+ u64 rel_start, rel_end, aligned_start, aligned_end, frag;
+
+ if (!READ_ONCE(bg->stripe_unusable_ready) || !sinfo)
+ return;
+ if (!btrfs_is_stripe_alloc_bg(bg))
+ return;
+
+ rel_start = bytenr - bg->start;
+ rel_end = rel_start + size;
+ aligned_start = div64_u64(rel_start + fsl - 1, fsl) * fsl;
+ aligned_end = div64_u64(rel_end, fsl) * fsl;
+
+ if (aligned_end > aligned_start)
+ frag = size - (aligned_end - aligned_start);
+ else
+ frag = size;
+ if (!frag)
+ return;
+
+ spin_lock(&sinfo->lock);
+ sinfo->bytes_stripe_unusable += frag;
+ spin_unlock(&sinfo->lock);
+}
+
int btrfs_add_free_space(struct btrfs_block_group *block_group,
u64 bytenr, u64 size)
{
trim_state = BTRFS_TRIM_STATE_TRIMMED;
stripe_unusable_mark_dirty(block_group);
+ stripe_unusable_account_add(block_group, bytenr, size);
return __btrfs_add_free_space(block_group, bytenr, size, trim_state);
}
*/
u64 max_extent_size;
+ /*
+ * Widest full-stripe width among raid56 DATA block groups; the unit
+ * of the stripe_alloc pessimistic reservation margin (one unit per
+ * outstanding delalloc extent). Zero when the filesystem has no
+ * raid56 data. Grows monotonically as block groups appear.
+ */
+ u64 stripe_margin_unit;
+
/* Block groups and devices containing active swapfiles. */
spinlock_t swapfile_pins_lock;
struct rb_root swapfile_pins;
#include "extent-tree.h"
#include "zoned.h"
#include "delayed-inode.h"
+#include "btrfs_inode.h"
/*
* HOW DOES SPACE RESERVATION WORK
s_info->bytes_pinned + s_info->bytes_readonly +
s_info->bytes_zone_unusable +
s_info->bytes_stripe_unusable + s_info->bytes_stripe_open +
+ s_info->bytes_stripe_margin +
(may_use_included ? s_info->bytes_may_use : 0);
}
factor = btrfs_bg_type_to_factor(block_group->flags);
+ /*
+ * Track the widest raid56 data full stripe as the unit of the
+ * stripe_alloc pessimistic reservation margin. Pure geometry;
+ * whether a margin is actually charged is decided per reservation
+ * by the mount option.
+ */
+ if ((block_group->flags & BTRFS_BLOCK_GROUP_DATA) &&
+ (block_group->flags & BTRFS_BLOCK_GROUP_RAID56_MASK) &&
+ block_group->full_stripe_len > READ_ONCE(info->stripe_margin_unit))
+ WRITE_ONCE(info->stripe_margin_unit,
+ block_group->full_stripe_len);
+
spin_lock(&space_info->lock);
space_info->total_bytes += block_group->length;
space_info->disk_total += block_group->length * factor;
up_write(&space_info->groups_sem);
}
+/*
+ * stripe_alloc pessimistic reservation margin.
+ *
+ * A byte-counted data reservation holds no collateral against the stripe
+ * claim rule: between admission at write() time and the allocation at
+ * writeback, every transaction commit closes open stripe runs and traps
+ * their sub-stripe tails, quietly converting bytes the reservation was
+ * counting on into bytes_stripe_unusable. The reservation cannot fail at
+ * that point -- delalloc writeback gets no second chance -- so pre-pay the
+ * worst case instead: charge one full stripe width for every outstanding
+ * delalloc extent, and release it when the extent is allocated (or its
+ * delalloc range dropped). The actual allocation is optimistic -- extents
+ * pack into shared runs and usually trap nothing -- and the unused margin
+ * returns the moment the extent completes.
+ *
+ * The charge rides btrfs_mod_outstanding_extents(), the same bookkeeping
+ * that sizes metadata reservations, so it stays exact across delalloc
+ * merges and splits. Charges are gated on the mount option but releases
+ * are not (and clamp at zero), so toggling stripe_alloc across a remount
+ * with dirty delalloc drains the counter instead of stranding it.
+ */
+void btrfs_stripe_margin_mod(struct btrfs_inode *inode, int mod)
+{
+ struct btrfs_fs_info *fs_info = inode->root->fs_info;
+ struct btrfs_space_info *sinfo = fs_info->data_sinfo;
+ const u64 unit = READ_ONCE(fs_info->stripe_margin_unit);
+ u64 bytes;
+
+ if (!unit || !sinfo || mod == 0)
+ 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) {
+ /*
+ * Option off: never charge, and skip the lock once the
+ * counter has drained (with no charges it only falls, so
+ * reading zero is stable).
+ */
+ return;
+ }
+
+ bytes = (u64)abs(mod) * unit;
+ spin_lock(&sinfo->lock);
+ if (mod > 0) {
+ sinfo->bytes_stripe_margin += bytes;
+ } else {
+ sinfo->bytes_stripe_margin -= min(bytes, sinfo->bytes_stripe_margin);
+ btrfs_try_granting_tickets(sinfo);
+ }
+ spin_unlock(&sinfo->lock);
+}
+
+
struct btrfs_space_info *btrfs_find_space_info(struct btrfs_fs_info *info,
u64 flags)
{
(s64)(info->total_bytes - btrfs_space_info_used(info, true)),
info->full ? "" : "not ");
btrfs_info(fs_info,
-"space_info total=%llu, used=%llu, pinned=%llu, reserved=%llu, may_use=%llu, readonly=%llu zone_unusable=%llu",
+"space_info total=%llu, used=%llu, pinned=%llu, reserved=%llu, may_use=%llu, readonly=%llu zone_unusable=%llu stripe_unusable=%llu stripe_open=%llu stripe_margin=%llu",
info->total_bytes, info->bytes_used, info->bytes_pinned,
info->bytes_reserved, info->bytes_may_use,
- info->bytes_readonly, info->bytes_zone_unusable);
+ info->bytes_readonly, info->bytes_zone_unusable,
+ info->bytes_stripe_unusable, info->bytes_stripe_open,
+ info->bytes_stripe_margin);
}
void btrfs_dump_space_info(struct btrfs_fs_info *fs_info,
reservations must not count on it
either. Synced from the block groups'
stripe_open_remainder. */
+ u64 bytes_stripe_margin; /* stripe_alloc pessimistic reservation
+ margin: one full stripe width per
+ outstanding delalloc extent. Pre-pays
+ the worst-case sub-stripe tail each
+ future extent can strand when its run
+ closes, so commits between write() and
+ writeback can never trap space out
+ from under an admitted reservation.
+ Charged and released in lockstep with
+ the inodes' outstanding_extents. */
u64 max_extent_size; /* This will hold the maximum extent size of
the space info if we had an ENOSPC in the
DECLARE_SPACE_INFO_UPDATE(bytes_stripe_unusable, "stripe_unusable");
+struct btrfs_inode;
+
int btrfs_init_space_info(struct btrfs_fs_info *fs_info);
+void btrfs_stripe_margin_mod(struct btrfs_inode *inode, int mod);
void btrfs_add_bg_to_space_info(struct btrfs_fs_info *info,
struct btrfs_block_group *block_group);
void btrfs_update_space_info_chunk_size(struct btrfs_space_info *space_info,
SPACE_INFO_ATTR(bytes_readonly);
SPACE_INFO_ATTR(bytes_zone_unusable);
SPACE_INFO_ATTR(bytes_stripe_unusable);
+SPACE_INFO_ATTR(bytes_stripe_open);
+SPACE_INFO_ATTR(bytes_stripe_margin);
SPACE_INFO_ATTR(disk_used);
SPACE_INFO_ATTR(disk_total);
SPACE_INFO_ATTR(reclaim_count);
BTRFS_ATTR_PTR(space_info, bytes_readonly),
BTRFS_ATTR_PTR(space_info, bytes_zone_unusable),
BTRFS_ATTR_PTR(space_info, bytes_stripe_unusable),
+ BTRFS_ATTR_PTR(space_info, bytes_stripe_open),
+ BTRFS_ATTR_PTR(space_info, bytes_stripe_margin),
BTRFS_ATTR_PTR(space_info, disk_used),
BTRFS_ATTR_PTR(space_info, disk_total),
BTRFS_ATTR_PTR(space_info, bg_reclaim_threshold),