u64 offset; /* next unallocated byte */
u64 inflight_bytes; /* reserved bytes with data IO pending */
u64 open_seq; /* fs_info->stripe_retire_seq at open */
+ enum btrfs_stripe_run_class class;
bool open; /* accepting allocations */
};
struct btrfs_open_stripe_run *run)
{
ASSERT(open_stripe_run_drained(run));
- ASSERT(bg->open_stripe != run);
+ ASSERT(bg->open_stripe[run->class] != run);
list_del(&run->list);
kfree(run);
wake_up_var(&bg->open_stripe_runs);
}
+/*
+ * Close a run: no further allocation may join it, and its unallocated tail
+ * must be returned to the free space cache by the caller (after dropping
+ * the lock). Caller holds bg->stripe_run_lock. Returns the tail length
+ * and sets *tail_start.
+ */
+static u64 close_open_stripe_run(struct btrfs_block_group *bg,
+ struct btrfs_open_stripe_run *run,
+ u64 *tail_start)
+{
+ u64 tail_len;
+
+ ASSERT(run->open);
+ ASSERT(bg->open_stripe[run->class] == run);
+ run->open = false;
+ bg->open_stripe[run->class] = NULL;
+ *tail_start = run->offset;
+ tail_len = run->end - run->offset;
+ run->end = run->offset;
+ if (open_stripe_run_drained(run))
+ free_open_stripe_run(bg, run);
+ return tail_len;
+}
+
static void open_stripe_add_bg(struct btrfs_fs_info *fs_info,
struct btrfs_block_group *bg)
{
* this block group can satisfy the allocation, or -ENOMEM.
*/
int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes,
- u64 *ret_offset)
+ enum btrfs_stripe_run_class class,
+ u64 *ret_offset, u64 *available)
{
struct btrfs_fs_info *fs_info = bg->fs_info;
const u64 fsl = bg->full_stripe_len;
- struct btrfs_open_stripe_run *new_run = NULL;
+ struct btrfs_open_stripe_run *new_run;
struct btrfs_open_stripe_run *run;
unsigned long flags;
u64 tail_start = 0;
int ret;
ASSERT(num_bytes);
-again:
+ *available = 0;
+
spin_lock_irqsave(&bg->stripe_run_lock, flags);
- run = bg->open_stripe;
- if (run) {
- if (num_bytes <= run->end - run->offset) {
- *ret_offset = run->offset;
- run->offset += num_bytes;
- run->inflight_bytes += num_bytes;
- if (run->offset == run->end) {
- run->open = false;
- bg->open_stripe = NULL;
- }
- spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
- ret = 0;
- goto out;
+ run = bg->open_stripe[class];
+ if (run && num_bytes <= run->end - run->offset) {
+ *ret_offset = run->offset;
+ run->offset += num_bytes;
+ run->inflight_bytes += num_bytes;
+ if (run->offset == run->end) {
+ run->open = false;
+ bg->open_stripe[class] = NULL;
}
- /*
- * Doesn't fit. Close the run; the unallocated tail becomes
- * free space in a partially filled stripe and stays
- * unclaimable until the whole stripe frees.
- */
- run->open = false;
- bg->open_stripe = NULL;
- tail_start = run->offset;
- tail_len = run->end - run->offset;
- run->end = run->offset;
- if (open_stripe_run_drained(run))
- free_open_stripe_run(bg, run);
+ spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
+ return 0;
}
+ if (run)
+ *available = run->end - run->offset;
spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
- if (tail_len) {
- btrfs_add_free_space(bg, tail_start, tail_len);
- tail_len = 0;
- }
- if (!new_run) {
- new_run = kmalloc(sizeof(*new_run), GFP_NOFS);
- if (!new_run) {
- ret = -ENOMEM;
- goto out;
- }
- }
+ new_run = kmalloc(sizeof(*new_run), GFP_NOFS);
+ if (!new_run)
+ return -ENOMEM;
ret = btrfs_claim_free_stripe_run(bg,
div64_u64(num_bytes + fsl - 1, fsl) * fsl,
if (ret)
goto out;
if (len < num_bytes) {
- /* Only a shorter run is free; put it back untouched. */
+ /*
+ * Only a shorter run is free. Put it back untouched (it is
+ * still fully-free stripes) and let the caller retry with a
+ * smaller allocation.
+ */
btrfs_add_free_space(bg, start, len);
+ *available = max(*available, len);
ret = -ENOSPC;
goto out;
}
spin_lock_irqsave(&bg->stripe_run_lock, flags);
- if (bg->open_stripe) {
+ if (bg->ro) {
/*
- * Lost a race with another opener. Our claim is still made
- * of fully-free stripes, so it can simply go back.
+ * Lost a race with this block group turning read-only; run
+ * retirement for it may already have happened, so a new run
+ * must not be installed.
*/
spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
btrfs_add_free_space(bg, start, len);
- goto again;
+ *available = 0;
+ ret = -ENOSPC;
+ goto out;
}
+ /*
+ * Close this class's open run if one (re)appeared: it was too small
+ * for us, or a racing opener's. Closing is always safe; the last
+ * opener wins.
+ */
+ run = bg->open_stripe[class];
+ if (run)
+ tail_len = close_open_stripe_run(bg, run, &tail_start);
new_run->bg = bg;
+ new_run->class = class;
new_run->start = start;
new_run->end = start + len;
new_run->offset = start + num_bytes;
new_run->open_seq = READ_ONCE(fs_info->stripe_retire_seq);
new_run->open = (new_run->offset != new_run->end);
list_add_tail(&new_run->list, &bg->open_stripe_runs);
- bg->open_stripe = new_run->open ? new_run : NULL;
+ bg->open_stripe[class] = new_run->open ? new_run : NULL;
spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
+ if (tail_len)
+ btrfs_add_free_space(bg, tail_start, tail_len);
*ret_offset = start;
new_run = NULL;
ret = 0;
}
/*
- * Close the block group's open run if it predates @seq and return its
- * unallocated tail to the free space cache. Does not wait for inflight
- * IO. Only bg->open_stripe can ever be open, so a single pass suffices.
+ * Close the block group's open runs that predate @seq and return their
+ * unallocated tails to the free space cache. Does not wait for inflight
+ * IO. Only the bg->open_stripe[] slots can ever be open, so one pass over
+ * the classes suffices.
*/
static void close_block_group_stripe_runs(struct btrfs_block_group *bg,
u64 seq)
{
+ u64 tail_start[BTRFS_STRIPE_RUN_NR_CLASSES];
+ u64 tail_len[BTRFS_STRIPE_RUN_NR_CLASSES];
struct btrfs_open_stripe_run *run;
unsigned long flags;
- u64 tail_start = 0;
- u64 tail_len = 0;
+ int class;
spin_lock_irqsave(&bg->stripe_run_lock, flags);
- run = bg->open_stripe;
- if (run && run->open_seq < seq) {
- ASSERT(run->open);
- run->open = false;
- bg->open_stripe = NULL;
- tail_start = run->offset;
- tail_len = run->end - run->offset;
- run->end = run->offset;
- if (open_stripe_run_drained(run))
- free_open_stripe_run(bg, run);
+ for (class = 0; class < BTRFS_STRIPE_RUN_NR_CLASSES; class++) {
+ tail_len[class] = 0;
+ run = bg->open_stripe[class];
+ if (run && run->open_seq < seq)
+ tail_len[class] = close_open_stripe_run(bg, run,
+ &tail_start[class]);
}
spin_unlock_irqrestore(&bg->stripe_run_lock, flags);
- if (tail_len)
- btrfs_add_free_space(bg, tail_start, tail_len);
+ for (class = 0; class < BTRFS_STRIPE_RUN_NR_CLASSES; class++) {
+ if (tail_len[class])
+ btrfs_add_free_space(bg, tail_start[class],
+ tail_len[class]);
+ }
+}
+
+/*
+ * Close all of a block group's open stripe runs without waiting for their
+ * inflight IO to drain.
+ */
+void btrfs_close_bg_open_stripes(struct btrfs_block_group *bg)
+{
+ close_block_group_stripe_runs(bg, U64_MAX);
}
/* No run opened before @seq remains (open or draining). */
}
}
+/*
+ * Drop the dedication of a block group to data relocation. Shared by the
+ * zoned allocator and the stripe allocation policy; both dedicate one
+ * block group at a time via fs_info->data_reloc_bg.
+ */
+void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg)
+{
+ struct btrfs_fs_info *fs_info = bg->fs_info;
+
+ spin_lock(&fs_info->relocation_bg_lock);
+ if (fs_info->data_reloc_bg == bg->start)
+ fs_info->data_reloc_bg = 0;
+ spin_unlock(&fs_info->relocation_bg_lock);
+}
+
+/*
+ * Release the block group currently dedicated to data relocation, closing
+ * its open stripe runs so ordinary allocations can use the remaining fully
+ * free stripes. Used by the stripe allocation policy when relocation of a
+ * block group finishes.
+ */
+void btrfs_release_data_reloc_bg(struct btrfs_fs_info *fs_info)
+{
+ struct btrfs_block_group *bg;
+ u64 bytenr;
+
+ spin_lock(&fs_info->relocation_bg_lock);
+ bytenr = fs_info->data_reloc_bg;
+ fs_info->data_reloc_bg = 0;
+ spin_unlock(&fs_info->relocation_bg_lock);
+
+ if (!bytenr)
+ return;
+ bg = btrfs_lookup_block_group(fs_info, bytenr);
+ if (!bg)
+ return;
+ btrfs_close_bg_open_stripes(bg);
+ btrfs_put_block_group(bg);
+}
+
struct btrfs_caching_control *btrfs_get_caching_control(
struct btrfs_block_group *cache)
{
btrfs_clear_treelog_bg(block_group);
btrfs_clear_data_reloc_bg(block_group);
+ /*
+ * Removal requires the group to have been read-only, and setting it
+ * read-only retires its stripe runs; nothing may remain here.
+ */
+ WARN_ON(!list_empty(&block_group->open_stripe_runs));
path = btrfs_alloc_path();
if (unlikely(!path)) {
mutex_unlock(&fs_info->ro_block_group_mutex);
btrfs_end_transaction(trans);
+ /*
+ * The group is read-only: close and drain its open stripe runs. An
+ * allocation racing with the read-only transition either installed
+ * its run before this (closed and drained here), or observes ->ro
+ * under the stripe_run_lock and backs out.
+ */
+ if (!ret)
+ btrfs_retire_block_group_stripes(cache);
return ret;
}
struct btrfs_open_stripe_run;
struct btrfs_trans_handle;
+/*
+ * Allocation classes for open stripe runs. Two classes never share a run
+ * (and therefore never share a raid56 stripe): relocation overwrites its
+ * preallocated extents in place, which must not be able to tear stripes
+ * containing ordinary cow data.
+ */
+enum btrfs_stripe_run_class {
+ BTRFS_STRIPE_RUN_COW,
+ BTRFS_STRIPE_RUN_RELOC,
+ BTRFS_STRIPE_RUN_NR_CLASSES,
+};
enum btrfs_block_group_size_class {
/* Unset */
BTRFS_BG_SZ_NONE,
*/
spinlock_t stripe_run_lock;
struct list_head open_stripe_runs;
- struct btrfs_open_stripe_run *open_stripe;
+ struct btrfs_open_stripe_run *open_stripe[BTRFS_STRIPE_RUN_NR_CLASSES];
/*
* Membership in fs_info->open_stripe_bgs, protected by
* fs_info->open_stripe_lock; holds a block group reference.
const u64 start);
void btrfs_wait_block_group_reservations(struct btrfs_block_group *bg);
int btrfs_alloc_from_open_stripe(struct btrfs_block_group *bg, u64 num_bytes,
- u64 *ret_offset);
+ enum btrfs_stripe_run_class class,
+ u64 *ret_offset, u64 *available);
void btrfs_open_stripe_write_done(struct btrfs_block_group *bg, u64 start,
u64 num_bytes);
void btrfs_open_stripe_write_done_run(struct btrfs_open_stripe_run *run,
u64 num_bytes);
+void btrfs_close_bg_open_stripes(struct btrfs_block_group *bg);
void btrfs_retire_block_group_stripes(struct btrfs_block_group *bg);
void btrfs_retire_open_stripes(struct btrfs_fs_info *fs_info);
+void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg);
+void btrfs_release_data_reloc_bg(struct btrfs_fs_info *fs_info);
struct btrfs_block_group *btrfs_inc_nocow_writers(struct btrfs_fs_info *fs_info,
u64 bytenr);
void btrfs_dec_nocow_writers(struct btrfs_block_group *bg);
return ret;
}
+/*
+ * Should this block group use stripe-exclusive allocation? Never issuing
+ * sub-stripe writes into stripes containing committed data is what closes
+ * the raid56 write hole for datacow writes; see the "Open stripe runs"
+ * comment in block-group.c.
+ */
+static bool btrfs_is_stripe_alloc_bg(const struct btrfs_block_group *bg)
+{
+ return btrfs_test_opt(bg->fs_info, STRIPE_ALLOC) &&
+ (bg->flags & BTRFS_BLOCK_GROUP_DATA) &&
+ (bg->flags & BTRFS_BLOCK_GROUP_RAID56_MASK);
+}
+
+static int do_allocation_stripe(struct btrfs_block_group *block_group,
+ struct find_free_extent_ctl *ffe_ctl,
+ struct btrfs_block_group **bg_ret)
+{
+ struct btrfs_fs_info *fs_info = block_group->fs_info;
+ enum btrfs_stripe_run_class class = BTRFS_STRIPE_RUN_COW;
+ u64 available = 0;
+ u64 offset;
+ bool skip = false;
+ int ret;
+
+ /*
+ * Soft dedication of one block group to relocation, mirroring the
+ * zoned data_reloc_bg rule: it keeps relocation's prealloc-then-
+ * overwrite writes in their own block group. The class tag on the
+ * stripe runs is what enforces that the two classes never share a
+ * stripe even if this dedication races.
+ */
+ spin_lock(&fs_info->relocation_bg_lock);
+ if (ffe_ctl->for_data_reloc) {
+ class = BTRFS_STRIPE_RUN_RELOC;
+ if (!fs_info->data_reloc_bg)
+ fs_info->data_reloc_bg = block_group->start;
+ }
+ if (fs_info->data_reloc_bg &&
+ (ffe_ctl->for_data_reloc !=
+ (block_group->start == fs_info->data_reloc_bg)))
+ skip = true;
+ spin_unlock(&fs_info->relocation_bg_lock);
+ if (skip)
+ return 1;
+
+ ret = btrfs_alloc_from_open_stripe(block_group, ffe_ctl->num_bytes,
+ class, &offset, &available);
+ if (ret == -ENOMEM)
+ return ret;
+ if (ret) {
+ if (available > ffe_ctl->max_extent_size)
+ ffe_ctl->max_extent_size = available;
+ /* Let relocation move on and claim another block group. */
+ if (ffe_ctl->for_data_reloc)
+ btrfs_clear_data_reloc_bg(block_group);
+ return 1;
+ }
+ ffe_ctl->found_offset = offset;
+ ffe_ctl->search_start = offset;
+ return 0;
+}
+
static int do_allocation(struct btrfs_block_group *block_group,
struct find_free_extent_ctl *ffe_ctl,
struct btrfs_block_group **bg_ret)
{
switch (ffe_ctl->policy) {
case BTRFS_EXTENT_ALLOC_CLUSTERED:
+ if (btrfs_is_stripe_alloc_bg(block_group))
+ return do_allocation_stripe(block_group, ffe_ctl,
+ bg_ret);
return do_allocation_clustered(block_group, ffe_ctl, bg_ret);
case BTRFS_EXTENT_ALLOC_ZONED:
return do_allocation_zoned(block_group, ffe_ctl, bg_ret);
BTRFS_MOUNT_IGNOREMETACSUMS = (1ULL << 31),
BTRFS_MOUNT_IGNORESUPERFLAGS = (1ULL << 32),
BTRFS_MOUNT_REF_TRACKER = (1ULL << 33),
+ BTRFS_MOUNT_STRIPE_ALLOC = (1ULL << 34),
};
/* These mount options require a full read-only fs, no new transaction is allowed. */
btrfs_dec_block_group_ro(rc->block_group);
if (!btrfs_fs_incompat(fs_info, REMAP_TREE))
iput(rc->data_inode);
+ /*
+ * Drop the stripe policy's relocation block group dedication; the
+ * zoned allocator manages fs_info->data_reloc_bg on its own.
+ */
+ if (!btrfs_is_zoned(fs_info))
+ btrfs_release_data_reloc_bg(fs_info);
btrfs_free_path(path);
reloc_chunk_end(fs_info);
out_put_rc:
Opt_rescan_uuid_tree,
Opt_skip_balance,
Opt_space_cache,
+ Opt_stripe_alloc,
Opt_space_cache_version,
Opt_ssd,
Opt_ssd_spread,
fsparam_flag("skip_balance", Opt_skip_balance),
fsparam_flag_no("space_cache", Opt_space_cache),
fsparam_enum("space_cache", Opt_space_cache_version, btrfs_parameter_space_cache),
+ fsparam_flag_no("stripe_alloc", Opt_stripe_alloc),
fsparam_flag_no("ssd", Opt_ssd),
fsparam_flag_no("ssd_spread", Opt_ssd_spread),
fsparam_string("subvol", Opt_subvol),
else
btrfs_set_opt(ctx->mount_opt, FLUSHONCOMMIT);
break;
+ case Opt_stripe_alloc:
+ if (result.negated)
+ btrfs_clear_opt(ctx->mount_opt, STRIPE_ALLOC);
+ else
+ btrfs_set_opt(ctx->mount_opt, STRIPE_ALLOC);
+ break;
case Opt_ratio:
ctx->metadata_ratio = result.uint_32;
break;
print_rescue_option(seq, "ignoresuperflags", &printed);
if (btrfs_test_opt(info, FLUSHONCOMMIT))
seq_puts(seq, ",flushoncommit");
+ if (btrfs_test_opt(info, STRIPE_ALLOC))
+ seq_puts(seq, ",stripe_alloc");
if (btrfs_test_opt(info, DISCARD_SYNC))
seq_puts(seq, ",discard");
if (btrfs_test_opt(info, DISCARD_ASYNC))
const u64 orig_fsl = cache->full_stripe_len;
const u64 fsl = 16 * sectorsize;
u64 offset;
+ u64 avail;
int ret;
test_msg("running open stripe run tests");
cache->full_stripe_len = fsl;
/* Empty cache: no stripe run to open. */
- ret = btrfs_alloc_from_open_stripe(cache, sectorsize, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
if (ret != -ENOSPC) {
test_err("alloc from empty cache returned %d", ret);
ret = -EINVAL;
goto out;
}
- /* Free space [sectorsize, 4*fsl): the first aligned stripe is #1. */
- ret = btrfs_add_free_space(cache, sectorsize, 4 * fsl - sectorsize);
+ /* Free space [sectorsize, 6*fsl): the first aligned stripe is #1. */
+ ret = btrfs_add_free_space(cache, sectorsize, 6 * fsl - sectorsize);
if (ret) {
test_err("error adding free space %d", ret);
goto out;
}
/* Sequential allocations share one run. */
- ret = btrfs_alloc_from_open_stripe(cache, 2 * sectorsize, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, 2 * sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
if (ret || offset != fsl) {
test_err("first alloc wrong: ret %d offset %llu", ret, offset);
ret = -EINVAL;
goto out;
}
- ret = btrfs_alloc_from_open_stripe(cache, 2 * sectorsize, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, 2 * sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
if (ret || offset != fsl + 2 * sectorsize) {
test_err("second alloc wrong: ret %d offset %llu", ret, offset);
ret = -EINVAL;
}
/* Doesn't fit the remainder: closes the run, opens the next stripe. */
- ret = btrfs_alloc_from_open_stripe(cache, fsl, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, fsl, BTRFS_STRIPE_RUN_COW,
+ &offset, &avail);
if (ret || offset != 2 * fsl) {
test_err("misfit alloc wrong: ret %d offset %llu", ret, offset);
ret = -EINVAL;
goto out;
}
/* ...but is part of a partial stripe, so the next alloc skips it. */
- ret = btrfs_alloc_from_open_stripe(cache, sectorsize, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
if (ret || offset != 3 * fsl) {
test_err("post-close alloc wrong: ret %d offset %llu",
ret, offset);
goto out;
}
+ /* A different class never joins the cow run: fresh stripe. */
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_RELOC, &offset,
+ &avail);
+ if (ret || offset != 4 * fsl) {
+ test_err("reloc class alloc wrong: ret %d offset %llu",
+ ret, offset);
+ ret = -EINVAL;
+ goto out;
+ }
+ /* ...and the cow run keeps filling, untouched by the reloc open. */
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
+ if (ret || offset != 3 * fsl + sectorsize) {
+ test_err("cow run lost by class open: ret %d offset %llu",
+ ret, offset);
+ ret = -EINVAL;
+ goto out;
+ }
+
/* Report all data IO complete, in pieces. */
btrfs_open_stripe_write_done(cache, fsl, 2 * sectorsize);
btrfs_open_stripe_write_done(cache, fsl + 2 * sectorsize,
2 * sectorsize);
btrfs_open_stripe_write_done(cache, 2 * fsl, fsl);
btrfs_open_stripe_write_done(cache, 3 * fsl, sectorsize);
+ btrfs_open_stripe_write_done(cache, 3 * fsl + sectorsize, sectorsize);
+ btrfs_open_stripe_write_done(cache, 4 * fsl, sectorsize);
/*
- * Commit-style retirement: closes the open run, returns its tail,
- * drains (instantly, all IO reported done) and drops the block
- * group's membership in fs_info->open_stripe_bgs.
+ * Commit-style retirement: closes both classes' open runs, returns
+ * their tails, drains (instantly, all IO reported done) and drops
+ * the block group's membership in fs_info->open_stripe_bgs.
*/
btrfs_retire_open_stripes(fs_info);
- if (!test_check_exists(cache, 3 * fsl + sectorsize,
+ if (!test_check_exists(cache, 3 * fsl + 2 * sectorsize,
+ fsl - 2 * sectorsize)) {
+ test_err("retired cow run tail missing from free space");
+ ret = -EINVAL;
+ goto out;
+ }
+ if (!test_check_exists(cache, 4 * fsl + sectorsize,
fsl - sectorsize)) {
- test_err("retired run tail missing from free space");
+ test_err("retired reloc run tail missing from free space");
ret = -EINVAL;
goto out;
}
+ /* Stripe #5 was never claimed and is still fully free. */
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
+ if (ret || offset != 5 * fsl) {
+ test_err("post-retire alloc wrong: ret %d offset %llu",
+ ret, offset);
+ ret = -EINVAL;
+ goto out;
+ }
+ btrfs_open_stripe_write_done(cache, 5 * fsl, sectorsize);
+ btrfs_retire_open_stripes(fs_info);
+
/* Only partially filled stripes remain: nothing left to claim. */
- ret = btrfs_alloc_from_open_stripe(cache, sectorsize, &offset);
+ ret = btrfs_alloc_from_open_stripe(cache, sectorsize,
+ BTRFS_STRIPE_RUN_COW, &offset,
+ &avail);
if (ret != -ENOSPC) {
test_err("alloc after retirement returned %d", ret);
ret = -EINVAL;
goto out;
}
- /* Retiring with nothing open is a no-op. */
- btrfs_retire_open_stripes(fs_info);
-
- /* Clean up the three partial-stripe remainders. */
+ /* Clean up the partial-stripe remainders. */
ret = btrfs_remove_free_space(cache, sectorsize, fsl - sectorsize);
if (ret) {
test_err("error cleaning up head %d", ret);
test_err("error cleaning up tail 1 %d", ret);
goto out;
}
- ret = btrfs_remove_free_space(cache, 3 * fsl + sectorsize,
- fsl - sectorsize);
+ ret = btrfs_remove_free_space(cache, 3 * fsl + 2 * sectorsize,
+ fsl - 2 * sectorsize);
if (ret) {
test_err("error cleaning up tail 2 %d", ret);
goto out;
}
+ ret = btrfs_remove_free_space(cache, 4 * fsl + sectorsize,
+ fsl - sectorsize);
+ if (ret) {
+ test_err("error cleaning up tail 3 %d", ret);
+ goto out;
+ }
+ ret = btrfs_remove_free_space(cache, 5 * fsl + sectorsize,
+ fsl - sectorsize);
+ if (ret) {
+ test_err("error cleaning up tail 4 %d", ret);
+ goto out;
+ }
ret = 0;
out:
cache->full_stripe_len = orig_fsl;
queue_work(system_dfl_wq, &bg->zone_finish_work);
}
-void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg)
-{
- struct btrfs_fs_info *fs_info = bg->fs_info;
-
- spin_lock(&fs_info->relocation_bg_lock);
- if (fs_info->data_reloc_bg == bg->start)
- fs_info->data_reloc_bg = 0;
- spin_unlock(&fs_info->relocation_bg_lock);
-}
-
void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info)
{
struct btrfs_space_info *data_sinfo = fs_info->data_sinfo;
u64 length);
void btrfs_schedule_zone_finish_bg(struct btrfs_block_group *bg,
struct extent_buffer *eb);
-void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg);
void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info);
void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info);
bool btrfs_zoned_should_reclaim(const struct btrfs_fs_info *fs_info);
static inline void btrfs_schedule_zone_finish_bg(struct btrfs_block_group *bg,
struct extent_buffer *eb) { }
-static inline void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg) { }
-
static inline void btrfs_zoned_reserve_data_reloc_bg(struct btrfs_fs_info *fs_info) { }
static inline void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info) { }