[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC 13/24] block/block-copy: use aio-task-pool API
From: |
Vladimir Sementsov-Ogievskiy |
Subject: |
[RFC 13/24] block/block-copy: use aio-task-pool API |
Date: |
Fri, 15 Nov 2019 17:14:33 +0300 |
Run block_copy iterations in parallel in aio tasks.
Signed-off-by: Vladimir Sementsov-Ogievskiy <address@hidden>
---
block/block-copy.c | 102 +++++++++++++++++++++++++++++++++++++++------
1 file changed, 90 insertions(+), 12 deletions(-)
diff --git a/block/block-copy.c b/block/block-copy.c
index 4e8c64a45d..b2e7825b53 100644
--- a/block/block-copy.c
+++ b/block/block-copy.c
@@ -19,15 +19,27 @@
#include "block/block-copy.h"
#include "sysemu/block-backend.h"
#include "qemu/units.h"
+#include "qemu/coroutine.h"
+#include "block/aio_task.h"
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
#define BLOCK_COPY_MAX_MEM (128 * MiB)
+#define BLOCK_COPY_MAX_WORKERS 64
+
+typedef struct BlockCopyCallState {
+ bool failed;
+ bool error_is_read;
+} BlockCopyCallState;
typedef struct BlockCopyTask {
+ AioTask task;
+
BlockCopyState *s;
+ BlockCopyCallState *call_state;
int64_t offset;
int64_t bytes;
+ bool zeroes;
QLIST_ENTRY(BlockCopyTask) list;
CoQueue wait_queue; /* coroutines blocked on this task */
} BlockCopyTask;
@@ -217,6 +229,30 @@ void block_copy_set_callbacks(
s->progress_opaque = progress_opaque;
}
+/* Takes ownership on @task */
+static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
+ BlockCopyTask *task)
+{
+ if (!pool) {
+ int ret = task->task.func(&task->task);
+
+ g_free(task);
+ return ret;
+ }
+
+ aio_task_pool_wait_slot(pool);
+ if (aio_task_pool_status(pool) < 0) {
+ co_put_to_shres(task->s->mem, task->bytes);
+ block_copy_task_end(task, -EAGAIN);
+ g_free(task);
+ return aio_task_pool_status(pool);
+ }
+
+ aio_task_pool_start_task(pool, &task->task);
+
+ return 0;
+}
+
/*
* block_copy_do_copy
*
@@ -316,8 +352,31 @@ out:
return ret;
}
+static coroutine_fn int block_copy_task_entry(AioTask *task)
+{
+ BlockCopyTask *t = container_of(task, BlockCopyTask, task);
+ bool error_is_read;
+ int ret;
+
+ ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
+ &error_is_read);
+ if (ret < 0 && !t->call_state->failed) {
+ t->call_state->failed = true;
+ t->call_state->error_is_read = error_is_read;
+ } else {
+ t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
+ }
+ co_put_to_shres(t->s->mem, t->bytes);
+ block_copy_task_end(t, ret);
+
+ return ret;
+}
+
+/* Called only on full-dirty region */
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
- int64_t offset, int64_t bytes)
+ BlockCopyCallState *call_state,
+ int64_t offset,
+ int64_t bytes)
{
int64_t next_zero;
BlockCopyTask *task = g_new(BlockCopyTask, 1);
@@ -338,7 +397,9 @@ static BlockCopyTask *block_copy_task_create(BlockCopyState
*s,
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
*task = (BlockCopyTask) {
+ .task.func = block_copy_task_entry,
.s = s,
+ .call_state = call_state,
.offset = offset,
.bytes = bytes,
};
@@ -459,6 +520,8 @@ static int coroutine_fn
block_copy_dirty_clusters(BlockCopyState *s,
{
int ret = 0;
bool found_dirty = false;
+ AioTaskPool *aio = NULL;
+ BlockCopyCallState call_state = {false, false};
/*
* block_copy() user is responsible for keeping source and target in same
@@ -470,8 +533,8 @@ static int coroutine_fn
block_copy_dirty_clusters(BlockCopyState *s,
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
- while (bytes) {
- g_autofree BlockCopyTask *task = NULL;
+ while (bytes && aio_task_pool_status(aio) == 0) {
+ BlockCopyTask *task;
int64_t status_bytes;
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
@@ -483,36 +546,51 @@ static int coroutine_fn
block_copy_dirty_clusters(BlockCopyState *s,
found_dirty = true;
- task = block_copy_task_create(s, offset, bytes);
+ task = block_copy_task_create(s, &call_state, offset, bytes);
ret = block_copy_block_status(s, offset, task->bytes, &status_bytes);
block_copy_task_shrink(task, status_bytes);
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
block_copy_task_end(task, 0);
+ g_free(task);
s->progress_reset_callback(s->progress_opaque);
trace_block_copy_skip_range(s, offset, status_bytes);
offset += status_bytes;
bytes -= status_bytes;
continue;
}
+ task->zeroes = ret & BDRV_BLOCK_ZERO;
trace_block_copy_process(s, offset);
co_get_from_shres(s->mem, task->bytes);
- ret = block_copy_do_copy(s, offset, task->bytes, ret & BDRV_BLOCK_ZERO,
- error_is_read);
- co_put_to_shres(s->mem, task->bytes);
- block_copy_task_end(task, ret);
- if (ret < 0) {
- return ret;
+
+ if (!aio && task->bytes != bytes) {
+ aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS);
}
- s->progress_bytes_callback(task->bytes, s->progress_opaque);
offset += task->bytes;
bytes -= task->bytes;
+
+ ret = block_copy_task_run(aio, task);
+ if (ret < 0) {
+ goto out;
+ }
+ }
+
+out:
+ if (aio) {
+ aio_task_pool_wait_all(aio);
+ if (ret == 0) {
+ ret = aio_task_pool_status(aio);
+ }
+ g_free(aio);
+ }
+ if (error_is_read && ret < 0) {
+ *error_is_read = call_state.error_is_read;
}
- return found_dirty;
+ return ret < 0 ? ret : found_dirty;
}
int coroutine_fn block_copy(BlockCopyState *s, int64_t start, uint64_t bytes,
--
2.21.0
- [RFC 12/24] block/block-copy: move block_copy_task_create down, (continued)
- [RFC 12/24] block/block-copy: move block_copy_task_create down, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 09/24] block/block-copy: alloc task on each iteration, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 18/24] block/block-copy: add block_copy_cancel, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 23/24] python: add qemu/bench_block_job.py, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 17/24] block/block-copy: add ratelimit to block-copy, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 15/24] block/block-copy: implement block_copy_async, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 14/24] block/block-copy: More explicit call_state, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 08/24] block/block-copy: rename in-flight requests to tasks, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 02/24] block/block-copy: use block_status, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 06/24] block/block-copy: reduce intersecting request lock, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 13/24] block/block-copy: use aio-task-pool API,
Vladimir Sementsov-Ogievskiy <=
- [RFC 22/24] python: add simplebench.py, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 10/24] block/block-copy: add state pointer to BlockCopyTask, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 04/24] block/block-copy: refactor interfaces to use bytes instead of end, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 01/24] block/block-copy: specialcase first copy_range request, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 05/24] block/block-copy: rename start to offset in interfaces, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 07/24] block/block-copy: hide structure definitions, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 11/24] block/block-copy: move task size initial calculation to _task_create, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 24/24] python: benchmark new backup architecture, Vladimir Sementsov-Ogievskiy, 2019/11/15
- [RFC 21/24] backup: move to block-copy, Vladimir Sementsov-Ogievskiy, 2019/11/15