}
}
+static void stripe_claimable_mod(struct btrfs_block_group *bg, s64 delta);
+static u64 stripe_touched_bytes(const struct btrfs_block_group *bg,
+ u64 bytenr, u64 size);
+
static int __btrfs_add_free_space(struct btrfs_block_group *block_group,
u64 offset, u64 bytes,
enum btrfs_trim_state trim_state)
if (ret)
kmem_cache_free(btrfs_free_space_cachep, info);
out:
+ /*
+ * Do NOT incrementally credit newly-freed whole stripes here. Add paths
+ * re-add space that a low-level remove never de-credited -- the async
+ * discard trim (unlink_free_space / bitmap_clear_bits + do_trimming's
+ * re-add) and btrfs_remove_free_space's middle-split tail -- so crediting
+ * on add double-counts and stripe_claimable drifts ABOVE the authoritative
+ * scan. That is the dangerous direction: once admission gates on the
+ * counter, an over-count admits reservations against phantom supply that
+ * writeback then drops. The incremental rules now only ever DEBIT
+ * (removes/claims, which err low); the per-commit rescan is the sole
+ * upward correction, so the counter can only under-count (early, clean
+ * write()-time ENOSPC) -- never over-count.
+ */
btrfs_discard_update_discardable(block_group);
spin_unlock(&ctl->tree_lock);
static void stripe_unusable_mark_dirty(struct btrfs_block_group *bg);
+/*
+ * stripe_alloc: keep the directly measured claimable whole-stripe supply
+ * in step with free space mutations. Positive deltas come from returned
+ * ranges' interior whole stripes, negative from claims and removals. The
+ * blind rules only ever err by counting claimable LOW (early write()-time
+ * ENOSPC, corrected upward by the commit rescan), never HIGH: a
+ * reservation admitted against phantom supply fails at writeback and
+ * drops buffered data. Caller holds ctl->tree_lock.
+ */
+static void stripe_claimable_mod(struct btrfs_block_group *bg, s64 delta)
+{
+ struct btrfs_space_info *sinfo = bg->space_info;
+
+ if (!READ_ONCE(bg->stripe_unusable_ready) || !delta)
+ return;
+ if (delta < 0 && bg->stripe_claimable < (u64)-delta)
+ delta = -(s64)bg->stripe_claimable;
+ if (!delta)
+ return;
+ bg->stripe_claimable += delta;
+ if (!sinfo)
+ return;
+ spin_lock(&sinfo->lock);
+ if (delta < 0 && sinfo->bytes_stripe_claimable < (u64)-delta)
+ sinfo->bytes_stripe_claimable = 0;
+ else
+ btrfs_space_info_update_bytes_stripe_claimable(sinfo, delta);
+ spin_unlock(&sinfo->lock);
+}
+
+/* The whole-stripe span [bytenr, bytenr + size) touches at all. */
+static u64 stripe_touched_bytes(const struct btrfs_block_group *bg,
+ u64 bytenr, u64 size)
+{
+ const u64 fsl = bg->full_stripe_len;
+ const u64 rel = bytenr - bg->start;
+ const u64 astart = div64_u64(rel, fsl) * fsl;
+ const u64 aend = div64_u64(rel + size + fsl - 1, fsl) * fsl;
+
+ return aend - astart;
+}
+
/*
* stripe_alloc: count the sub-stripe fragments of a returning free range
* as trapped immediately, without waiting for the commit-time rescan.
spin_lock(&ctl->tree_lock);
+ /*
+ * The removed bytes leave free space; every whole stripe the range
+ * touches may lose wholeness (a boundary fragment removal breaks a
+ * fully free stripe as surely as an interior one empties it).
+ * Rounding the decrement OUT to touched stripes errs claimable-low.
+ * The trapped counter is deliberately not decremented for removed
+ * fragments: leaving it high is the safe direction, and the rescan
+ * settles both.
+ */
+ if (btrfs_is_stripe_alloc_bg(block_group))
+ stripe_claimable_mod(block_group,
+ -(s64)stripe_touched_bytes(block_group, offset, bytes));
+
again:
ret = 0;
if (!bytes)
void btrfs_block_group_init_stripe_unusable(struct btrfs_block_group *bg)
{
struct btrfs_free_space_ctl *ctl = bg->free_space_ctl;
- u64 trapped, nstripes;
+ u64 trapped, claimable, nstripes;
u32 *freep;
if (!btrfs_is_stripe_alloc_bg(bg))
}
trapped = stripe_unusable_scan(bg, freep, nstripes);
bg->stripe_unusable = trapped;
+ /* Every free byte is claimable or trapped; the scan split them. */
+ bg->stripe_claimable = ctl->free_space - trapped;
+ claimable = bg->stripe_claimable;
bg->stripe_unusable_ready = true;
spin_unlock(&ctl->tree_lock);
kvfree(freep);
- if (trapped) {
+ if (trapped || claimable) {
spin_lock(&bg->space_info->lock);
btrfs_space_info_update_bytes_stripe_unusable(bg->space_info,
trapped);
+ btrfs_space_info_update_bytes_stripe_claimable(bg->space_info,
+ claimable);
spin_unlock(&bg->space_info->lock);
}
}
{
struct btrfs_free_space_ctl *ctl = bg->free_space_ctl;
u64 trapped;
+ u64 claimable;
if (!btrfs_is_stripe_alloc_bg(bg))
return;
return;
}
trapped = bg->stripe_unusable;
+ claimable = bg->stripe_claimable;
+ bg->stripe_claimable = 0;
bg->stripe_unusable_ready = false;
spin_unlock(&ctl->tree_lock);
- if (trapped) {
+ if (trapped || claimable) {
spin_lock(&bg->space_info->lock);
btrfs_space_info_update_bytes_stripe_unusable(bg->space_info,
-(s64)trapped);
+ btrfs_space_info_update_bytes_stripe_claimable(bg->space_info,
+ -(s64)claimable);
spin_unlock(&bg->space_info->lock);
}
}
return;
spin_lock(&ctl->tree_lock);
- if (bg->stripe_unusable_ready) /* skip if disarmed (read-only) */
+ if (bg->stripe_unusable_ready) { /* skip if disarmed (read-only) */
+ u64 scanned;
+
bg->stripe_unusable = stripe_unusable_scan(bg, freep, nstripes);
+ scanned = ctl->free_space - bg->stripe_unusable;
+ /*
+ * The incremental rules may only under-count claimable
+ * supply; counting more than the scan finds means a missed
+ * consumption site, the direction that admits reservations
+ * writeback cannot honor. Rate-limited rather than _ONCE: a
+ * persistent gap -- e.g. a filesystem carrying extents from the
+ * legacy allocator, a layout the incremental rules were not
+ * shaped for -- recurs every commit, and hiding all but the
+ * first hit loses that signal in the field. The clamp below
+ * keeps each rescan authoritative regardless.
+ */
+ WARN_RATELIMIT(bg->stripe_claimable > scanned,
+ "bg %llu incremental stripe_claimable %llu above scanned %llu",
+ bg->start, bg->stripe_claimable, scanned);
+ bg->stripe_claimable = scanned;
+ }
spin_unlock(&ctl->tree_lock);
kvfree(freep);
/*
goto out;
spare_used = claim_stripe_run_pieces(block_group, run_start,
run_len, spare);
+ stripe_claimable_mod(block_group, -(s64)run_len);
*start = run_start;
*len = run_len;
ret = 0;
kmem_cache_free(btrfs_free_space_cachep, entry);
}
}
+ stripe_claimable_mod(block_group, -(s64)run_len);
*start = run_start;
*len = run_len;
ret = 0;