mirror of https://github.com/xemu-project/xemu.git
block: intoduce reqlist
Split intersecting-requests functionality out of block-copy to be reused in copy-before-write filter. Note: while being here, fix tiny typo in MAINTAINERS. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> Reviewed-by: Hanna Reitz <hreitz@redhat.com> Message-Id: <20220303194349.2304213-7-vsementsov@virtuozzo.com> Signed-off-by: Hanna Reitz <hreitz@redhat.com>
This commit is contained in:
parent
177541e671
commit
d088e6a48a
|
@ -2515,7 +2515,9 @@ F: block/stream.c
|
||||||
F: block/mirror.c
|
F: block/mirror.c
|
||||||
F: qapi/job.json
|
F: qapi/job.json
|
||||||
F: block/block-copy.c
|
F: block/block-copy.c
|
||||||
F: include/block/block-copy.c
|
F: include/block/block-copy.h
|
||||||
|
F: block/reqlist.c
|
||||||
|
F: include/block/reqlist.h
|
||||||
F: block/copy-before-write.h
|
F: block/copy-before-write.h
|
||||||
F: block/copy-before-write.c
|
F: block/copy-before-write.c
|
||||||
F: include/block/aio_task.h
|
F: include/block/aio_task.h
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "block/block-copy.h"
|
#include "block/block-copy.h"
|
||||||
|
#include "block/reqlist.h"
|
||||||
#include "sysemu/block-backend.h"
|
#include "sysemu/block-backend.h"
|
||||||
#include "qemu/units.h"
|
#include "qemu/units.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
|
@ -83,7 +84,6 @@ typedef struct BlockCopyTask {
|
||||||
*/
|
*/
|
||||||
BlockCopyState *s;
|
BlockCopyState *s;
|
||||||
BlockCopyCallState *call_state;
|
BlockCopyCallState *call_state;
|
||||||
int64_t offset;
|
|
||||||
/*
|
/*
|
||||||
* @method can also be set again in the while loop of
|
* @method can also be set again in the while loop of
|
||||||
* block_copy_dirty_clusters(), but it is never accessed concurrently
|
* block_copy_dirty_clusters(), but it is never accessed concurrently
|
||||||
|
@ -94,21 +94,17 @@ typedef struct BlockCopyTask {
|
||||||
BlockCopyMethod method;
|
BlockCopyMethod method;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fields whose state changes throughout the execution
|
* Generally, req is protected by lock in BlockCopyState, Still req.offset
|
||||||
* Protected by lock in BlockCopyState.
|
* is only set on task creation, so may be read concurrently after creation.
|
||||||
|
* req.bytes is changed at most once, and need only protecting the case of
|
||||||
|
* parallel read while updating @bytes value in block_copy_task_shrink().
|
||||||
*/
|
*/
|
||||||
CoQueue wait_queue; /* coroutines blocked on this task */
|
BlockReq req;
|
||||||
/*
|
|
||||||
* Only protect the case of parallel read while updating @bytes
|
|
||||||
* value in block_copy_task_shrink().
|
|
||||||
*/
|
|
||||||
int64_t bytes;
|
|
||||||
QLIST_ENTRY(BlockCopyTask) list;
|
|
||||||
} BlockCopyTask;
|
} BlockCopyTask;
|
||||||
|
|
||||||
static int64_t task_end(BlockCopyTask *task)
|
static int64_t task_end(BlockCopyTask *task)
|
||||||
{
|
{
|
||||||
return task->offset + task->bytes;
|
return task->req.offset + task->req.bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct BlockCopyState {
|
typedef struct BlockCopyState {
|
||||||
|
@ -136,7 +132,7 @@ typedef struct BlockCopyState {
|
||||||
CoMutex lock;
|
CoMutex lock;
|
||||||
int64_t in_flight_bytes;
|
int64_t in_flight_bytes;
|
||||||
BlockCopyMethod method;
|
BlockCopyMethod method;
|
||||||
QLIST_HEAD(, BlockCopyTask) tasks; /* All tasks from all block-copy calls */
|
BlockReqList reqs;
|
||||||
QLIST_HEAD(, BlockCopyCallState) calls;
|
QLIST_HEAD(, BlockCopyCallState) calls;
|
||||||
/*
|
/*
|
||||||
* skip_unallocated:
|
* skip_unallocated:
|
||||||
|
@ -160,42 +156,6 @@ typedef struct BlockCopyState {
|
||||||
RateLimit rate_limit;
|
RateLimit rate_limit;
|
||||||
} BlockCopyState;
|
} BlockCopyState;
|
||||||
|
|
||||||
/* Called with lock held */
|
|
||||||
static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
|
|
||||||
int64_t offset, int64_t bytes)
|
|
||||||
{
|
|
||||||
BlockCopyTask *t;
|
|
||||||
|
|
||||||
QLIST_FOREACH(t, &s->tasks, list) {
|
|
||||||
if (offset + bytes > t->offset && offset < t->offset + t->bytes) {
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If there are no intersecting tasks return false. Otherwise, wait for the
|
|
||||||
* first found intersecting tasks to finish and return true.
|
|
||||||
*
|
|
||||||
* Called with lock held. May temporary release the lock.
|
|
||||||
* Return value of 0 proves that lock was NOT released.
|
|
||||||
*/
|
|
||||||
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
|
|
||||||
int64_t bytes)
|
|
||||||
{
|
|
||||||
BlockCopyTask *task = find_conflicting_task(s, offset, bytes);
|
|
||||||
|
|
||||||
if (!task) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu_co_queue_wait(&task->wait_queue, &s->lock);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Called with lock held */
|
/* Called with lock held */
|
||||||
static int64_t block_copy_chunk_size(BlockCopyState *s)
|
static int64_t block_copy_chunk_size(BlockCopyState *s)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +199,7 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state,
|
||||||
bytes = QEMU_ALIGN_UP(bytes, s->cluster_size);
|
bytes = QEMU_ALIGN_UP(bytes, s->cluster_size);
|
||||||
|
|
||||||
/* region is dirty, so no existent tasks possible in it */
|
/* region is dirty, so no existent tasks possible in it */
|
||||||
assert(!find_conflicting_task(s, offset, bytes));
|
assert(!reqlist_find_conflict(&s->reqs, offset, bytes));
|
||||||
|
|
||||||
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
|
||||||
s->in_flight_bytes += bytes;
|
s->in_flight_bytes += bytes;
|
||||||
|
@ -249,12 +209,9 @@ block_copy_task_create(BlockCopyState *s, BlockCopyCallState *call_state,
|
||||||
.task.func = block_copy_task_entry,
|
.task.func = block_copy_task_entry,
|
||||||
.s = s,
|
.s = s,
|
||||||
.call_state = call_state,
|
.call_state = call_state,
|
||||||
.offset = offset,
|
|
||||||
.bytes = bytes,
|
|
||||||
.method = s->method,
|
.method = s->method,
|
||||||
};
|
};
|
||||||
qemu_co_queue_init(&task->wait_queue);
|
reqlist_init_req(&s->reqs, &task->req, offset, bytes);
|
||||||
QLIST_INSERT_HEAD(&s->tasks, task, list);
|
|
||||||
|
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
@ -270,34 +227,34 @@ static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
|
||||||
int64_t new_bytes)
|
int64_t new_bytes)
|
||||||
{
|
{
|
||||||
QEMU_LOCK_GUARD(&task->s->lock);
|
QEMU_LOCK_GUARD(&task->s->lock);
|
||||||
if (new_bytes == task->bytes) {
|
if (new_bytes == task->req.bytes) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(new_bytes > 0 && new_bytes < task->bytes);
|
assert(new_bytes > 0 && new_bytes < task->req.bytes);
|
||||||
|
|
||||||
task->s->in_flight_bytes -= task->bytes - new_bytes;
|
task->s->in_flight_bytes -= task->req.bytes - new_bytes;
|
||||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap,
|
bdrv_set_dirty_bitmap(task->s->copy_bitmap,
|
||||||
task->offset + new_bytes, task->bytes - new_bytes);
|
task->req.offset + new_bytes,
|
||||||
|
task->req.bytes - new_bytes);
|
||||||
|
|
||||||
task->bytes = new_bytes;
|
reqlist_shrink_req(&task->req, new_bytes);
|
||||||
qemu_co_queue_restart_all(&task->wait_queue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
|
||||||
{
|
{
|
||||||
QEMU_LOCK_GUARD(&task->s->lock);
|
QEMU_LOCK_GUARD(&task->s->lock);
|
||||||
task->s->in_flight_bytes -= task->bytes;
|
task->s->in_flight_bytes -= task->req.bytes;
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
|
bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->req.offset,
|
||||||
|
task->req.bytes);
|
||||||
}
|
}
|
||||||
QLIST_REMOVE(task, list);
|
|
||||||
if (task->s->progress) {
|
if (task->s->progress) {
|
||||||
progress_set_remaining(task->s->progress,
|
progress_set_remaining(task->s->progress,
|
||||||
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
bdrv_get_dirty_count(task->s->copy_bitmap) +
|
||||||
task->s->in_flight_bytes);
|
task->s->in_flight_bytes);
|
||||||
}
|
}
|
||||||
qemu_co_queue_restart_all(&task->wait_queue);
|
reqlist_remove_req(&task->req);
|
||||||
}
|
}
|
||||||
|
|
||||||
void block_copy_state_free(BlockCopyState *s)
|
void block_copy_state_free(BlockCopyState *s)
|
||||||
|
@ -450,7 +407,7 @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
|
||||||
|
|
||||||
ratelimit_init(&s->rate_limit);
|
ratelimit_init(&s->rate_limit);
|
||||||
qemu_co_mutex_init(&s->lock);
|
qemu_co_mutex_init(&s->lock);
|
||||||
QLIST_INIT(&s->tasks);
|
QLIST_INIT(&s->reqs);
|
||||||
QLIST_INIT(&s->calls);
|
QLIST_INIT(&s->calls);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
|
@ -483,7 +440,7 @@ static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
|
||||||
|
|
||||||
aio_task_pool_wait_slot(pool);
|
aio_task_pool_wait_slot(pool);
|
||||||
if (aio_task_pool_status(pool) < 0) {
|
if (aio_task_pool_status(pool) < 0) {
|
||||||
co_put_to_shres(task->s->mem, task->bytes);
|
co_put_to_shres(task->s->mem, task->req.bytes);
|
||||||
block_copy_task_end(task, -ECANCELED);
|
block_copy_task_end(task, -ECANCELED);
|
||||||
g_free(task);
|
g_free(task);
|
||||||
return -ECANCELED;
|
return -ECANCELED;
|
||||||
|
@ -596,7 +553,8 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
||||||
BlockCopyMethod method = t->method;
|
BlockCopyMethod method = t->method;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = block_copy_do_copy(s, t->offset, t->bytes, &method, &error_is_read);
|
ret = block_copy_do_copy(s, t->req.offset, t->req.bytes, &method,
|
||||||
|
&error_is_read);
|
||||||
|
|
||||||
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
WITH_QEMU_LOCK_GUARD(&s->lock) {
|
||||||
if (s->method == t->method) {
|
if (s->method == t->method) {
|
||||||
|
@ -609,10 +567,10 @@ static coroutine_fn int block_copy_task_entry(AioTask *task)
|
||||||
t->call_state->error_is_read = error_is_read;
|
t->call_state->error_is_read = error_is_read;
|
||||||
}
|
}
|
||||||
} else if (s->progress) {
|
} else if (s->progress) {
|
||||||
progress_work_done(s->progress, t->bytes);
|
progress_work_done(s->progress, t->req.bytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
co_put_to_shres(s->mem, t->bytes);
|
co_put_to_shres(s->mem, t->req.bytes);
|
||||||
block_copy_task_end(t, ret);
|
block_copy_task_end(t, ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -771,22 +729,22 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
||||||
trace_block_copy_skip_range(s, offset, bytes);
|
trace_block_copy_skip_range(s, offset, bytes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (task->offset > offset) {
|
if (task->req.offset > offset) {
|
||||||
trace_block_copy_skip_range(s, offset, task->offset - offset);
|
trace_block_copy_skip_range(s, offset, task->req.offset - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
found_dirty = true;
|
found_dirty = true;
|
||||||
|
|
||||||
ret = block_copy_block_status(s, task->offset, task->bytes,
|
ret = block_copy_block_status(s, task->req.offset, task->req.bytes,
|
||||||
&status_bytes);
|
&status_bytes);
|
||||||
assert(ret >= 0); /* never fail */
|
assert(ret >= 0); /* never fail */
|
||||||
if (status_bytes < task->bytes) {
|
if (status_bytes < task->req.bytes) {
|
||||||
block_copy_task_shrink(task, status_bytes);
|
block_copy_task_shrink(task, status_bytes);
|
||||||
}
|
}
|
||||||
if (qatomic_read(&s->skip_unallocated) &&
|
if (qatomic_read(&s->skip_unallocated) &&
|
||||||
!(ret & BDRV_BLOCK_ALLOCATED)) {
|
!(ret & BDRV_BLOCK_ALLOCATED)) {
|
||||||
block_copy_task_end(task, 0);
|
block_copy_task_end(task, 0);
|
||||||
trace_block_copy_skip_range(s, task->offset, task->bytes);
|
trace_block_copy_skip_range(s, task->req.offset, task->req.bytes);
|
||||||
offset = task_end(task);
|
offset = task_end(task);
|
||||||
bytes = end - offset;
|
bytes = end - offset;
|
||||||
g_free(task);
|
g_free(task);
|
||||||
|
@ -807,11 +765,11 @@ block_copy_dirty_clusters(BlockCopyCallState *call_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ratelimit_calculate_delay(&s->rate_limit, task->bytes);
|
ratelimit_calculate_delay(&s->rate_limit, task->req.bytes);
|
||||||
|
|
||||||
trace_block_copy_process(s, task->offset);
|
trace_block_copy_process(s, task->req.offset);
|
||||||
|
|
||||||
co_get_from_shres(s->mem, task->bytes);
|
co_get_from_shres(s->mem, task->req.bytes);
|
||||||
|
|
||||||
offset = task_end(task);
|
offset = task_end(task);
|
||||||
bytes = end - offset;
|
bytes = end - offset;
|
||||||
|
@ -879,8 +837,8 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
||||||
* Check that there is no task we still need to
|
* Check that there is no task we still need to
|
||||||
* wait to complete
|
* wait to complete
|
||||||
*/
|
*/
|
||||||
ret = block_copy_wait_one(s, call_state->offset,
|
ret = reqlist_wait_one(&s->reqs, call_state->offset,
|
||||||
call_state->bytes);
|
call_state->bytes, &s->lock);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
/*
|
/*
|
||||||
* No pending tasks, but check again the bitmap in this
|
* No pending tasks, but check again the bitmap in this
|
||||||
|
@ -888,7 +846,7 @@ static int coroutine_fn block_copy_common(BlockCopyCallState *call_state)
|
||||||
* between this and the critical section in
|
* between this and the critical section in
|
||||||
* block_copy_dirty_clusters().
|
* block_copy_dirty_clusters().
|
||||||
*
|
*
|
||||||
* block_copy_wait_one return value 0 also means that it
|
* reqlist_wait_one return value 0 also means that it
|
||||||
* didn't release the lock. So, we are still in the same
|
* didn't release the lock. So, we are still in the same
|
||||||
* critical section, not interrupted by any concurrent
|
* critical section, not interrupted by any concurrent
|
||||||
* access to state.
|
* access to state.
|
||||||
|
|
|
@ -32,6 +32,7 @@ block_ss.add(files(
|
||||||
'qcow2.c',
|
'qcow2.c',
|
||||||
'quorum.c',
|
'quorum.c',
|
||||||
'raw-format.c',
|
'raw-format.c',
|
||||||
|
'reqlist.c',
|
||||||
'snapshot.c',
|
'snapshot.c',
|
||||||
'throttle-groups.c',
|
'throttle-groups.c',
|
||||||
'throttle.c',
|
'throttle.c',
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* reqlist API
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Proxmox Server Solutions
|
||||||
|
* Copyright (c) 2021 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Dietmar Maurer (dietmar@proxmox.com)
|
||||||
|
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "qemu/osdep.h"
|
||||||
|
|
||||||
|
#include "block/reqlist.h"
|
||||||
|
|
||||||
|
void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
assert(!reqlist_find_conflict(reqs, offset, bytes));
|
||||||
|
|
||||||
|
*req = (BlockReq) {
|
||||||
|
.offset = offset,
|
||||||
|
.bytes = bytes,
|
||||||
|
};
|
||||||
|
qemu_co_queue_init(&req->wait_queue);
|
||||||
|
QLIST_INSERT_HEAD(reqs, req, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes)
|
||||||
|
{
|
||||||
|
BlockReq *r;
|
||||||
|
|
||||||
|
QLIST_FOREACH(r, reqs, list) {
|
||||||
|
if (offset + bytes > r->offset && offset < r->offset + r->bytes) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock)
|
||||||
|
{
|
||||||
|
BlockReq *r = reqlist_find_conflict(reqs, offset, bytes);
|
||||||
|
|
||||||
|
if (!r) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_co_queue_wait(&r->wait_queue, lock);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes)
|
||||||
|
{
|
||||||
|
if (new_bytes == req->bytes) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(new_bytes > 0 && new_bytes < req->bytes);
|
||||||
|
|
||||||
|
req->bytes = new_bytes;
|
||||||
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void coroutine_fn reqlist_remove_req(BlockReq *req)
|
||||||
|
{
|
||||||
|
QLIST_REMOVE(req, list);
|
||||||
|
qemu_co_queue_restart_all(&req->wait_queue);
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* reqlist API
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Proxmox Server Solutions
|
||||||
|
* Copyright (c) 2021 Virtuozzo International GmbH.
|
||||||
|
*
|
||||||
|
* Authors:
|
||||||
|
* Dietmar Maurer (dietmar@proxmox.com)
|
||||||
|
* Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
|
||||||
|
*
|
||||||
|
* This work is licensed under the terms of the GNU GPL, version 2 or later.
|
||||||
|
* See the COPYING file in the top-level directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef REQLIST_H
|
||||||
|
#define REQLIST_H
|
||||||
|
|
||||||
|
#include "qemu/coroutine.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The API is not thread-safe and shouldn't be. The struct is public to be part
|
||||||
|
* of other structures and protected by third-party locks, see
|
||||||
|
* block/block-copy.c for example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct BlockReq {
|
||||||
|
int64_t offset;
|
||||||
|
int64_t bytes;
|
||||||
|
|
||||||
|
CoQueue wait_queue; /* coroutines blocked on this req */
|
||||||
|
QLIST_ENTRY(BlockReq) list;
|
||||||
|
} BlockReq;
|
||||||
|
|
||||||
|
typedef QLIST_HEAD(, BlockReq) BlockReqList;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize new request and add it to the list. Caller must be sure that
|
||||||
|
* there are no conflicting requests in the list.
|
||||||
|
*/
|
||||||
|
void reqlist_init_req(BlockReqList *reqs, BlockReq *req, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
|
/* Search for request in the list intersecting with @offset/@bytes area. */
|
||||||
|
BlockReq *reqlist_find_conflict(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there are no intersecting requests return false. Otherwise, wait for the
|
||||||
|
* first found intersecting request to finish and return true.
|
||||||
|
*
|
||||||
|
* @lock is passed to qemu_co_queue_wait()
|
||||||
|
* False return value proves that lock was released at no point.
|
||||||
|
*/
|
||||||
|
bool coroutine_fn reqlist_wait_one(BlockReqList *reqs, int64_t offset,
|
||||||
|
int64_t bytes, CoMutex *lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Shrink request and wake all waiting coroutines (maybe some of them are not
|
||||||
|
* intersecting with shrunk request).
|
||||||
|
*/
|
||||||
|
void coroutine_fn reqlist_shrink_req(BlockReq *req, int64_t new_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remove request and wake all waiting coroutines. Do not release any memory.
|
||||||
|
*/
|
||||||
|
void coroutine_fn reqlist_remove_req(BlockReq *req);
|
||||||
|
|
||||||
|
#endif /* REQLIST_H */
|
Loading…
Reference in New Issue