#include "misc.h"
#include "ctree.h"
#include "block-group.h"
+#include "ordered-data.h"
+#include "btrfs_inode.h"
#include "space-info.h"
#include "disk-io.h"
#include "free-space-cache.h"
* when the root directory inode loads its properties -- before any user IO
* -- or at runtime from btrfs_set_prop().
*/
+/*
+ * Drain the writes the legacy allocator placed before stripe_alloc was
+ * enabled on a live filesystem, then arm the write-hole invariant check.
+ * Runs in a worker: the property that enables the policy is applied inside
+ * a transaction, where flushing delalloc cannot wait.
+ */
+void btrfs_stripe_alloc_enable_work(struct work_struct *work)
+{
+ struct btrfs_fs_info *fs_info = container_of(work, struct btrfs_fs_info,
+ stripe_alloc_enable_work);
+
+ btrfs_start_delalloc_roots(fs_info, LONG_MAX, false);
+ btrfs_wait_ordered_roots(fs_info, U64_MAX, NULL);
+ clear_bit(BTRFS_FS_STRIPE_ALLOC_ENABLING, &fs_info->flags);
+ btrfs_info(fs_info,
+ "stripe_alloc: writes placed before the policy was enabled have landed");
+}
+
int btrfs_enable_stripe_alloc(struct btrfs_fs_info *fs_info)
{
+ bool live = test_bit(BTRFS_FS_OPEN, &fs_info->flags);
int ret;
if (btrfs_test_opt(fs_info, STRIPE_ALLOC))
ret = btrfs_stripe_alloc_check_support(fs_info);
if (ret)
return ret;
+ /*
+ * On a live filesystem the legacy allocator may still have writes in
+ * flight. Their extents sit in partly used stripes that no stripe run
+ * covers, so the write-hole invariant check would report them although
+ * they were placed before the policy existed. Keep the check off until
+ * every ordered extent that could have been allocated before the flip
+ * has landed; extents allocated after it have stripe runs. A mount-time
+ * enable has nothing in flight and skips the drain.
+ */
+ if (live)
+ set_bit(BTRFS_FS_STRIPE_ALLOC_ENABLING, &fs_info->flags);
btrfs_set_opt(fs_info->mount_opt, STRIPE_ALLOC);
/*
* Arm stripe_unusable accounting for raid56 data groups whose caches
stripe_alloc_sweep_groups(fs_info, true);
btrfs_info(fs_info,
"using stripe-exclusive allocation for raid56 data");
+ if (live)
+ queue_work(system_unbound_wq, &fs_info->stripe_alloc_enable_work);
return 0;
}
if (!btrfs_test_opt(fs_info, STRIPE_ALLOC))
return;
+ /* the legacy allocator's writes are still landing after a runtime enable */
+ if (test_bit(BTRFS_FS_STRIPE_ALLOC_ENABLING, &fs_info->flags))
+ return;
bg = btrfs_lookup_block_group(fs_info, full_stripe_start);
if (!bg)
return;
INIT_LIST_HEAD(&fs_info->delalloc_roots);
INIT_LIST_HEAD(&fs_info->caching_block_groups);
INIT_LIST_HEAD(&fs_info->open_stripe_bgs);
+ INIT_WORK(&fs_info->stripe_alloc_enable_work, btrfs_stripe_alloc_enable_work);
fs_info->stripe_park_timeout_ms = BTRFS_RBIO_PARK_TIMEOUT_MS;
fs_info->stripe_park_sync_timeout_ms = BTRFS_RBIO_PARK_SYNC_TIMEOUT_MS;
spin_lock_init(&fs_info->open_stripe_lock);
*/
btrfs_run_delayed_iputs(fs_info);
+ flush_work(&fs_info->stripe_alloc_enable_work);
cancel_work_sync(&fs_info->async_reclaim_work);
cancel_work_sync(&fs_info->async_data_reclaim_work);
cancel_work_sync(&fs_info->preempt_reclaim_work);
*/
BTRFS_FS_UNALIGNED_TREE_BLOCK,
+ /*
+ * stripe_alloc was enabled on a live filesystem and the legacy
+ * allocator's writes have not all landed yet: the write-hole
+ * invariant check stays off until they have.
+ */
+ BTRFS_FS_STRIPE_ALLOC_ENABLING,
+
#if BITS_PER_LONG == 32
/* Indicate if we have error/warn message printed on 32bit systems */
BTRFS_FS_32BIT_ERROR,
struct semaphore uuid_tree_rescan_sem;
/* Used to reclaim the metadata space in the background. */
+ /* drains the legacy allocator's writes after a runtime stripe_alloc enable */
+ struct work_struct stripe_alloc_enable_work;
struct work_struct async_reclaim_work;
struct work_struct async_data_reclaim_work;
struct work_struct preempt_reclaim_work;