spin_unlock(&ticket->lock);
}
+/*
+ * stripe_alloc DATA admission gate. On a raid56 stripe_alloc DATA space_info a
+ * write can only be placed crash-safely in a fully-free whole stripe, so admit a
+ * data reservation only while the whole-stripe supply (bytes_stripe_claimable,
+ * which errs LOW now that the incremental positive credit is gone) covers all
+ * outstanding reservations plus this one. This refuses at reservation time --
+ * before the range becomes delalloc and later parks at writeback with nowhere to
+ * land -- and makes write() -ENOSPC coincide with statfs f_bavail==0 (both on the
+ * whole-stripe bound). Non-stripe_alloc / metadata / non-raid56 space_infos are
+ * unaffected: stripe_margin_unit is 0 unless a raid56 DATA bg exists. Caller
+ * holds space_info->lock.
+ *
+ * Phase 1 is deliberately PESSIMISTIC: it gates on claimable alone, so it can
+ * refuse a write that would have landed in a partially-open stripe (over-refuses
+ * by the open remainder, healed by the commit rescan raising claimable + the
+ * 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)
+{
+ struct btrfs_fs_info *fs_info = space_info->fs_info;
+
+ if (!btrfs_test_opt(fs_info, STRIPE_ALLOC) ||
+ !(space_info->flags & BTRFS_BLOCK_GROUP_DATA) ||
+ !READ_ONCE(fs_info->stripe_margin_unit))
+ return true;
+ return space_info->bytes_may_use + bytes <= space_info->bytes_stripe_claimable;
+}
+
/*
* This is for space we already have accounted in space_info->bytes_may_use, so
* basically when we're returning space from block_rsv's.
used_after = used + ticket->bytes;
/* Check and see if our ticket can be satisfied now. */
- if (used_after <= space_info->total_bytes ||
- can_overcommit(space_info, used, ticket->bytes, flush)) {
+ if ((used_after <= space_info->total_bytes ||
+ can_overcommit(space_info, used, ticket->bytes, flush)) &&
+ stripe_claimable_admit(space_info, ticket->bytes)) {
btrfs_space_info_update_bytes_may_use(space_info, ticket->bytes);
remove_ticket(space_info, ticket, 0);
space_info->tickets_id++;
*/
if (!pending_tickets &&
((used + orig_bytes <= space_info->total_bytes) ||
- can_overcommit(space_info, used, orig_bytes, flush))) {
+ can_overcommit(space_info, used, orig_bytes, flush)) &&
+ stripe_claimable_admit(space_info, orig_bytes)) {
btrfs_space_info_update_bytes_may_use(space_info, orig_bytes);
ret = 0;
}