Commit e3ccfa98 authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

Btrfs: async delalloc flushing under space pressure

This patch moves the delalloc flushing that occurs when we are under space
pressure off to a async thread pool.  This helps since we only free up
metadata space when we actually insert the extent item, which means it takes
quite a while for space to be free'ed up if we wait on all ordered extents.
However, if space is freed up due to inline extents being inserted, we can
wake people who are waiting up early, and they can finish their work.
Signed-off-by: default avatarJosef Bacik <jbacik@redhat.com>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 32c00aff
...@@ -691,17 +691,17 @@ struct btrfs_space_info { ...@@ -691,17 +691,17 @@ struct btrfs_space_info {
struct list_head list; struct list_head list;
/* for controlling how we free up space for allocations */
wait_queue_head_t allocate_wait;
wait_queue_head_t flush_wait;
int allocating_chunk;
int flushing;
/* for block groups in our same type */ /* for block groups in our same type */
struct list_head block_groups; struct list_head block_groups;
spinlock_t lock; spinlock_t lock;
struct rw_semaphore groups_sem; struct rw_semaphore groups_sem;
atomic_t caching_threads; atomic_t caching_threads;
int allocating_chunk;
wait_queue_head_t wait;
int flushing;
wait_queue_head_t flush_wait;
}; };
/* /*
...@@ -918,6 +918,7 @@ struct btrfs_fs_info { ...@@ -918,6 +918,7 @@ struct btrfs_fs_info {
struct btrfs_workers endio_meta_write_workers; struct btrfs_workers endio_meta_write_workers;
struct btrfs_workers endio_write_workers; struct btrfs_workers endio_write_workers;
struct btrfs_workers submit_workers; struct btrfs_workers submit_workers;
struct btrfs_workers enospc_workers;
/* /*
* fixup workers take dirty pages that didn't properly go through * fixup workers take dirty pages that didn't properly go through
* the cow mechanism and make them safe to write. It happens * the cow mechanism and make them safe to write. It happens
......
...@@ -1762,6 +1762,9 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1762,6 +1762,9 @@ struct btrfs_root *open_ctree(struct super_block *sb,
min_t(u64, fs_devices->num_devices, min_t(u64, fs_devices->num_devices,
fs_info->thread_pool_size), fs_info->thread_pool_size),
&fs_info->generic_worker); &fs_info->generic_worker);
btrfs_init_workers(&fs_info->enospc_workers, "enospc",
fs_info->thread_pool_size,
&fs_info->generic_worker);
/* a higher idle thresh on the submit workers makes it much more /* a higher idle thresh on the submit workers makes it much more
* likely that bios will be send down in a sane order to the * likely that bios will be send down in a sane order to the
...@@ -1809,6 +1812,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1809,6 +1812,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
btrfs_start_workers(&fs_info->endio_meta_workers, 1); btrfs_start_workers(&fs_info->endio_meta_workers, 1);
btrfs_start_workers(&fs_info->endio_meta_write_workers, 1); btrfs_start_workers(&fs_info->endio_meta_write_workers, 1);
btrfs_start_workers(&fs_info->endio_write_workers, 1); btrfs_start_workers(&fs_info->endio_write_workers, 1);
btrfs_start_workers(&fs_info->enospc_workers, 1);
fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super); fs_info->bdi.ra_pages *= btrfs_super_num_devices(disk_super);
fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages, fs_info->bdi.ra_pages = max(fs_info->bdi.ra_pages,
...@@ -2023,6 +2027,7 @@ fail_sb_buffer: ...@@ -2023,6 +2027,7 @@ fail_sb_buffer:
btrfs_stop_workers(&fs_info->endio_meta_write_workers); btrfs_stop_workers(&fs_info->endio_meta_write_workers);
btrfs_stop_workers(&fs_info->endio_write_workers); btrfs_stop_workers(&fs_info->endio_write_workers);
btrfs_stop_workers(&fs_info->submit_workers); btrfs_stop_workers(&fs_info->submit_workers);
btrfs_stop_workers(&fs_info->enospc_workers);
fail_iput: fail_iput:
invalidate_inode_pages2(fs_info->btree_inode->i_mapping); invalidate_inode_pages2(fs_info->btree_inode->i_mapping);
iput(fs_info->btree_inode); iput(fs_info->btree_inode);
...@@ -2449,6 +2454,7 @@ int close_ctree(struct btrfs_root *root) ...@@ -2449,6 +2454,7 @@ int close_ctree(struct btrfs_root *root)
btrfs_stop_workers(&fs_info->endio_meta_write_workers); btrfs_stop_workers(&fs_info->endio_meta_write_workers);
btrfs_stop_workers(&fs_info->endio_write_workers); btrfs_stop_workers(&fs_info->endio_write_workers);
btrfs_stop_workers(&fs_info->submit_workers); btrfs_stop_workers(&fs_info->submit_workers);
btrfs_stop_workers(&fs_info->enospc_workers);
btrfs_close_devices(fs_info->fs_devices); btrfs_close_devices(fs_info->fs_devices);
btrfs_mapping_tree_free(&fs_info->mapping_tree); btrfs_mapping_tree_free(&fs_info->mapping_tree);
......
...@@ -2866,9 +2866,66 @@ static void check_force_delalloc(struct btrfs_space_info *meta_sinfo) ...@@ -2866,9 +2866,66 @@ static void check_force_delalloc(struct btrfs_space_info *meta_sinfo)
meta_sinfo->force_delalloc = 0; meta_sinfo->force_delalloc = 0;
} }
struct async_flush {
struct btrfs_root *root;
struct btrfs_space_info *info;
struct btrfs_work work;
};
static noinline void flush_delalloc_async(struct btrfs_work *work)
{
struct async_flush *async;
struct btrfs_root *root;
struct btrfs_space_info *info;
async = container_of(work, struct async_flush, work);
root = async->root;
info = async->info;
btrfs_start_delalloc_inodes(root);
wake_up(&info->flush_wait);
btrfs_wait_ordered_extents(root, 0);
spin_lock(&info->lock);
info->flushing = 0;
spin_unlock(&info->lock);
wake_up(&info->flush_wait);
kfree(async);
}
static void wait_on_flush(struct btrfs_space_info *info)
{
DEFINE_WAIT(wait);
u64 used;
while (1) {
prepare_to_wait(&info->flush_wait, &wait,
TASK_UNINTERRUPTIBLE);
spin_lock(&info->lock);
if (!info->flushing) {
spin_unlock(&info->lock);
break;
}
used = info->bytes_used + info->bytes_reserved +
info->bytes_pinned + info->bytes_readonly +
info->bytes_super + info->bytes_root +
info->bytes_may_use + info->bytes_delalloc;
if (used < info->total_bytes) {
spin_unlock(&info->lock);
break;
}
spin_unlock(&info->lock);
schedule();
}
finish_wait(&info->flush_wait, &wait);
}
static void flush_delalloc(struct btrfs_root *root, static void flush_delalloc(struct btrfs_root *root,
struct btrfs_space_info *info) struct btrfs_space_info *info)
{ {
struct async_flush *async;
bool wait = false; bool wait = false;
spin_lock(&info->lock); spin_lock(&info->lock);
...@@ -2883,11 +2940,24 @@ static void flush_delalloc(struct btrfs_root *root, ...@@ -2883,11 +2940,24 @@ static void flush_delalloc(struct btrfs_root *root,
spin_unlock(&info->lock); spin_unlock(&info->lock);
if (wait) { if (wait) {
wait_event(info->flush_wait, wait_on_flush(info);
!info->flushing);
return; return;
} }
async = kzalloc(sizeof(*async), GFP_NOFS);
if (!async)
goto flush;
async->root = root;
async->info = info;
async->work.func = flush_delalloc_async;
btrfs_queue_worker(&root->fs_info->enospc_workers,
&async->work);
wait_on_flush(info);
return;
flush:
btrfs_start_delalloc_inodes(root); btrfs_start_delalloc_inodes(root);
btrfs_wait_ordered_extents(root, 0); btrfs_wait_ordered_extents(root, 0);
...@@ -2927,7 +2997,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root, ...@@ -2927,7 +2997,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root,
if (!info->allocating_chunk) { if (!info->allocating_chunk) {
info->force_alloc = 1; info->force_alloc = 1;
info->allocating_chunk = 1; info->allocating_chunk = 1;
init_waitqueue_head(&info->wait); init_waitqueue_head(&info->allocate_wait);
} else { } else {
wait = true; wait = true;
} }
...@@ -2935,7 +3005,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root, ...@@ -2935,7 +3005,7 @@ static int maybe_allocate_chunk(struct btrfs_root *root,
spin_unlock(&info->lock); spin_unlock(&info->lock);
if (wait) { if (wait) {
wait_event(info->wait, wait_event(info->allocate_wait,
!info->allocating_chunk); !info->allocating_chunk);
return 1; return 1;
} }
...@@ -2956,7 +3026,7 @@ out: ...@@ -2956,7 +3026,7 @@ out:
spin_lock(&info->lock); spin_lock(&info->lock);
info->allocating_chunk = 0; info->allocating_chunk = 0;
spin_unlock(&info->lock); spin_unlock(&info->lock);
wake_up(&info->wait); wake_up(&info->allocate_wait);
if (ret) if (ret)
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment