list_del(&run->list);
bitmap_free(run->live);
kfree(run);
+ /*
+ * Wholly free stripes inside this run's range were counted as trapped
+ * by the last stripe_unusable scan (btrfs_stripe_run_stranded_stripes());
+ * with the run gone they are claimable again, so have the next commit
+ * rescan the group and credit them back.
+ */
+ if (READ_ONCE(bg->stripe_unusable_ready))
+ set_bit(BLOCK_GROUP_FLAG_STRIPE_UNUSABLE_DIRTY, &bg->runtime_flags);
wake_up_var(&bg->open_stripe_runs);
}
return true;
}
+/*
+ * For the stripe_unusable scan: wholly free stripes that overlap a live
+ * stripe run's range cannot be claimed while the run object exists (see
+ * btrfs_stripe_run_range_usable()) -- the run was claimed over them, the
+ * extents allocated there were freed again, and the run has not been freed
+ * yet; a nodatacow inode's private run lives until the inode is evicted.
+ * Counted as claimable, they let the admission gate admit writes the claim
+ * path then refuses, and writeback drops the data. Take them out of @freep
+ * (so the caller counts them neither as claimable nor as partial) and return
+ * their total; free_open_stripe_run() marks the group for rescan so they
+ * are credited back when the run dies. Caller holds ctl->tree_lock;
+ * stripe_run_lock nests inside it.
+ */
+u64 btrfs_stripe_run_stranded_stripes(struct btrfs_block_group *bg, u32 *freep,
+ u64 nstripes)
+{
+ const u64 fsl = bg->full_stripe_len;
+ struct btrfs_open_stripe_run *run;
+ unsigned long flags;
+ u64 stranded = 0;
+
+ spin_lock_irqsave(&bg->stripe_run_lock, flags);
+ list_for_each_entry(run, &bg->open_stripe_runs, list) {
+ u64 idx, last;
+
+ if (run->end <= run->start)
+ continue;
+ idx = div64_u64(run->start - bg->start, fsl);
+ last = div64_u64(run->end - 1 - bg->start, fsl);
+ for (; idx <= last && idx < nstripes; idx++) {
+ if (freep[idx] != fsl)
+ continue;
+ freep[idx] = 0;
+ stranded += fsl;
+ }
+ }
+ spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
+ return stranded;
+}
+
/*
* Parse a stripe_alloc_allow_rmw word list ("nodatacow",
* "prealloc", "fsync") into its mask. Words are separated by comma,
u64 total = 0;
u64 total_claimable = 0;
u64 total_claimable_reloc = 0;
+ u64 total_stranded = 0;
u64 largest = 0;
u64 unit = 0;
u32 nr_rw = 0;
*/
if (READ_ONCE(bg->stripe_unusable_ready)) {
total += READ_ONCE(bg->stripe_unusable);
+ total_stranded += READ_ONCE(bg->stripe_stranded);
total_claimable +=
READ_ONCE(bg->stripe_claimable);
if (bg->start == READ_ONCE(fs_info->data_reloc_bg))
sinfo->bytes_stripe_unusable = total;
sinfo->bytes_stripe_claimable = total_claimable;
sinfo->bytes_stripe_claimable_reloc = total_claimable_reloc;
+ sinfo->bytes_stripe_stranded = total_stranded;
if (!(sinfo->flags & BTRFS_BLOCK_GROUP_DATA)) {
u64 reserve = 0;
* (honest f_bavail); the reservation layer is deliberately unchanged.
*/
u64 stripe_unusable;
+ /*
+ * The part of stripe_unusable that is wholly free stripes lying inside
+ * live stripe-run ranges (stripe_alloc): the run was claimed over them,
+ * the extents allocated there were freed again, and the run object
+ * still exists, so btrfs_stripe_run_range_usable() refuses to claim
+ * them. Trapped for admission, but they come back by themselves when
+ * the run is freed, so reclaim leaves them out. Set by the same scan,
+ * under the same lock.
+ */
+ u64 stripe_stranded;
/* jiffies when the reclaim trigger last queued this group (stripe_alloc) */
unsigned long stripe_reclaim_jiffies;
/*
const struct writeback_control *wbc);
bool btrfs_stripe_open_run_class(struct btrfs_fs_info *fs_info, u64 logical,
enum btrfs_stripe_run_class *class);
+u64 btrfs_stripe_run_stranded_stripes(struct btrfs_block_group *bg, u32 *freep,
+ u64 nstripes);
bool btrfs_stripe_run_range_usable(struct btrfs_block_group *bg,
u64 run_start, u64 *run_len);
/*
"stripe_unusable_scan counted=%llu != free_space=%llu",
counted, ctl->free_space);
+ /*
+ * Whole stripes stranded behind live stripe runs are free but not
+ * claimable until their run is freed: trapped, like a partial stripe's
+ * free bytes, so the admission gate does not admit writes against them.
+ */
+ bg->stripe_stranded = btrfs_stripe_run_stranded_stripes(bg, freep, nstripes);
+ trapped = bg->stripe_stranded;
for (i = 0; i < nstripes; i++)
if (freep[i] && freep[i] < fsl)
trapped += freep[i];
struct btrfs_space_info *sinfo = bg->space_info;
const int thresh = READ_ONCE(sinfo->bg_reclaim_threshold);
const u64 used = READ_ONCE(bg->used);
- const u64 trapped = READ_ONCE(bg->stripe_unusable);
+ const u64 unusable = READ_ONCE(bg->stripe_unusable);
+ /*
+ * Stripes stranded behind live runs are trapped for admission but not
+ * for reclaim: they come back by themselves when the run is freed, and
+ * relocating the group would only force that.
+ */
+ const u64 trapped = unusable - min(unusable, READ_ONCE(bg->stripe_stranded));
u64 elsewhere;
u64 promised;
(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 stripe_unusable=%llu stripe_open=%llu stripe_claimable=%llu stripe_margin=%llu stripe_reserve=%llu",
+"space_info total=%llu, used=%llu, pinned=%llu, reserved=%llu, may_use=%llu, readonly=%llu zone_unusable=%llu stripe_unusable=%llu stripe_stranded=%llu stripe_open=%llu stripe_claimable=%llu stripe_margin=%llu stripe_reserve=%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_stripe_unusable, info->bytes_stripe_open,
+ info->bytes_stripe_unusable, info->bytes_stripe_stranded,
+ info->bytes_stripe_open,
info->bytes_stripe_claimable,
info->bytes_stripe_margin, info->bytes_stripe_reserve);
}
cannot admit writes against it, and
subtracted in statfs (from a different
base -- the free space cache walk). */
+ u64 bytes_stripe_stranded; /* the part of bytes_stripe_unusable that
+ is whole stripes stranded behind live
+ stripe runs (see the block group's
+ stripe_stranded); back to claimable when
+ the runs are freed */
u64 bytes_stripe_claimable_reloc; /* the part of bytes_stripe_claimable
in the group dedicated to data
relocation (fs_info->data_reloc_bg):
SPACE_INFO_ATTR(bytes_readonly);
SPACE_INFO_ATTR(bytes_zone_unusable);
SPACE_INFO_ATTR(bytes_stripe_unusable);
+SPACE_INFO_ATTR(bytes_stripe_stranded);
SPACE_INFO_ATTR(bytes_stripe_claimable);
SPACE_INFO_ATTR(bytes_stripe_open);
SPACE_INFO_ATTR(bytes_stripe_margin);
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_stranded),
BTRFS_ATTR_PTR(space_info, bytes_stripe_claimable),
BTRFS_ATTR_PTR(space_info, bytes_stripe_open),
BTRFS_ATTR_PTR(space_info, bytes_stripe_margin),