mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-05 16:53:55 -06:00
block/mirror: Wait for in-flight op conflicts
This patch makes the mirror code differentiate between simply waiting for any operation to complete (mirror_wait_for_free_in_flight_slot()) and specifically waiting for all operations touching a certain range of the virtual disk to complete (mirror_wait_on_conflicts()). Signed-off-by: Max Reitz <mreitz@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Message-id: 20180613181823.13618-5-mreitz@redhat.com Signed-off-by: Max Reitz <mreitz@redhat.com>
This commit is contained in:
parent
12aa40822d
commit
1181e19a6d
1 changed files with 84 additions and 18 deletions
102
block/mirror.c
102
block/mirror.c
|
@ -14,6 +14,7 @@
|
||||||
#include "qemu/osdep.h"
|
#include "qemu/osdep.h"
|
||||||
#include "qemu/cutils.h"
|
#include "qemu/cutils.h"
|
||||||
#include "qemu/coroutine.h"
|
#include "qemu/coroutine.h"
|
||||||
|
#include "qemu/range.h"
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include "block/blockjob_int.h"
|
#include "block/blockjob_int.h"
|
||||||
#include "block/block_int.h"
|
#include "block/block_int.h"
|
||||||
|
@ -86,6 +87,7 @@ struct MirrorOp {
|
||||||
* mirror_co_discard() before yielding for the first time */
|
* mirror_co_discard() before yielding for the first time */
|
||||||
int64_t *bytes_handled;
|
int64_t *bytes_handled;
|
||||||
|
|
||||||
|
bool is_pseudo_op;
|
||||||
CoQueue waiting_requests;
|
CoQueue waiting_requests;
|
||||||
|
|
||||||
QTAILQ_ENTRY(MirrorOp) next;
|
QTAILQ_ENTRY(MirrorOp) next;
|
||||||
|
@ -110,6 +112,41 @@ static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void coroutine_fn mirror_wait_on_conflicts(MirrorOp *self,
|
||||||
|
MirrorBlockJob *s,
|
||||||
|
uint64_t offset,
|
||||||
|
uint64_t bytes)
|
||||||
|
{
|
||||||
|
uint64_t self_start_chunk = offset / s->granularity;
|
||||||
|
uint64_t self_end_chunk = DIV_ROUND_UP(offset + bytes, s->granularity);
|
||||||
|
uint64_t self_nb_chunks = self_end_chunk - self_start_chunk;
|
||||||
|
|
||||||
|
while (find_next_bit(s->in_flight_bitmap, self_end_chunk,
|
||||||
|
self_start_chunk) < self_end_chunk &&
|
||||||
|
s->ret >= 0)
|
||||||
|
{
|
||||||
|
MirrorOp *op;
|
||||||
|
|
||||||
|
QTAILQ_FOREACH(op, &s->ops_in_flight, next) {
|
||||||
|
uint64_t op_start_chunk = op->offset / s->granularity;
|
||||||
|
uint64_t op_nb_chunks = DIV_ROUND_UP(op->offset + op->bytes,
|
||||||
|
s->granularity) -
|
||||||
|
op_start_chunk;
|
||||||
|
|
||||||
|
if (op == self) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranges_overlap(self_start_chunk, self_nb_chunks,
|
||||||
|
op_start_chunk, op_nb_chunks))
|
||||||
|
{
|
||||||
|
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret)
|
static void coroutine_fn mirror_iteration_done(MirrorOp *op, int ret)
|
||||||
{
|
{
|
||||||
MirrorBlockJob *s = op->s;
|
MirrorBlockJob *s = op->s;
|
||||||
|
@ -232,13 +269,22 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mirror_wait_for_io(MirrorBlockJob *s)
|
static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
MirrorOp *op;
|
MirrorOp *op;
|
||||||
|
|
||||||
op = QTAILQ_FIRST(&s->ops_in_flight);
|
QTAILQ_FOREACH(op, &s->ops_in_flight, next) {
|
||||||
assert(op);
|
/* Do not wait on pseudo ops, because it may in turn wait on
|
||||||
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
* some other operation to start, which may in fact be the
|
||||||
|
* caller of this function. Since there is only one pseudo op
|
||||||
|
* at any given time, we will always find some real operation
|
||||||
|
* to wait on. */
|
||||||
|
if (!op->is_pseudo_op) {
|
||||||
|
qemu_co_queue_wait(&op->waiting_requests, NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform a mirror copy operation.
|
/* Perform a mirror copy operation.
|
||||||
|
@ -282,7 +328,7 @@ static void coroutine_fn mirror_co_read(void *opaque)
|
||||||
|
|
||||||
while (s->buf_free_count < nb_chunks) {
|
while (s->buf_free_count < nb_chunks) {
|
||||||
trace_mirror_yield_in_flight(s, op->offset, s->in_flight);
|
trace_mirror_yield_in_flight(s, op->offset, s->in_flight);
|
||||||
mirror_wait_for_io(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
||||||
|
@ -382,8 +428,9 @@ static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset,
|
||||||
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
BlockDriverState *source = s->source;
|
BlockDriverState *source = s->source;
|
||||||
int64_t offset, first_chunk;
|
MirrorOp *pseudo_op;
|
||||||
uint64_t delay_ns = 0;
|
int64_t offset;
|
||||||
|
uint64_t delay_ns = 0, ret = 0;
|
||||||
/* At least the first dirty chunk is mirrored in one iteration. */
|
/* At least the first dirty chunk is mirrored in one iteration. */
|
||||||
int nb_chunks = 1;
|
int nb_chunks = 1;
|
||||||
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
|
bool write_zeroes_ok = bdrv_can_write_zeroes_with_unmap(blk_bs(s->target));
|
||||||
|
@ -399,11 +446,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
}
|
}
|
||||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||||
|
|
||||||
first_chunk = offset / s->granularity;
|
mirror_wait_on_conflicts(NULL, s, offset, 1);
|
||||||
while (test_bit(first_chunk, s->in_flight_bitmap)) {
|
|
||||||
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
|
||||||
mirror_wait_for_io(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
job_pause_point(&s->common.job);
|
job_pause_point(&s->common.job);
|
||||||
|
|
||||||
|
@ -440,6 +483,21 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
nb_chunks * s->granularity);
|
nb_chunks * s->granularity);
|
||||||
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
bdrv_dirty_bitmap_unlock(s->dirty_bitmap);
|
||||||
|
|
||||||
|
/* Before claiming an area in the in-flight bitmap, we have to
|
||||||
|
* create a MirrorOp for it so that conflicting requests can wait
|
||||||
|
* for it. mirror_perform() will create the real MirrorOps later,
|
||||||
|
* for now we just create a pseudo operation that will wake up all
|
||||||
|
* conflicting requests once all real operations have been
|
||||||
|
* launched. */
|
||||||
|
pseudo_op = g_new(MirrorOp, 1);
|
||||||
|
*pseudo_op = (MirrorOp){
|
||||||
|
.offset = offset,
|
||||||
|
.bytes = nb_chunks * s->granularity,
|
||||||
|
.is_pseudo_op = true,
|
||||||
|
};
|
||||||
|
qemu_co_queue_init(&pseudo_op->waiting_requests);
|
||||||
|
QTAILQ_INSERT_TAIL(&s->ops_in_flight, pseudo_op, next);
|
||||||
|
|
||||||
bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks);
|
bitmap_set(s->in_flight_bitmap, offset / s->granularity, nb_chunks);
|
||||||
while (nb_chunks > 0 && offset < s->bdev_length) {
|
while (nb_chunks > 0 && offset < s->bdev_length) {
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -475,11 +533,12 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
|
|
||||||
while (s->in_flight >= MAX_IN_FLIGHT) {
|
while (s->in_flight >= MAX_IN_FLIGHT) {
|
||||||
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
trace_mirror_yield_in_flight(s, offset, s->in_flight);
|
||||||
mirror_wait_for_io(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s->ret < 0) {
|
if (s->ret < 0) {
|
||||||
return 0;
|
ret = 0;
|
||||||
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
io_bytes = mirror_clip_bytes(s, offset, io_bytes);
|
io_bytes = mirror_clip_bytes(s, offset, io_bytes);
|
||||||
|
@ -494,7 +553,14 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
|
nb_chunks -= DIV_ROUND_UP(io_bytes, s->granularity);
|
||||||
delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct);
|
delay_ns = block_job_ratelimit_get_delay(&s->common, io_bytes_acct);
|
||||||
}
|
}
|
||||||
return delay_ns;
|
|
||||||
|
ret = delay_ns;
|
||||||
|
fail:
|
||||||
|
QTAILQ_REMOVE(&s->ops_in_flight, pseudo_op, next);
|
||||||
|
qemu_co_queue_restart_all(&pseudo_op->waiting_requests);
|
||||||
|
g_free(pseudo_op);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mirror_free_init(MirrorBlockJob *s)
|
static void mirror_free_init(MirrorBlockJob *s)
|
||||||
|
@ -521,7 +587,7 @@ static void mirror_free_init(MirrorBlockJob *s)
|
||||||
static void mirror_wait_for_all_io(MirrorBlockJob *s)
|
static void mirror_wait_for_all_io(MirrorBlockJob *s)
|
||||||
{
|
{
|
||||||
while (s->in_flight > 0) {
|
while (s->in_flight > 0) {
|
||||||
mirror_wait_for_io(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +742,7 @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
|
||||||
if (s->in_flight >= MAX_IN_FLIGHT) {
|
if (s->in_flight >= MAX_IN_FLIGHT) {
|
||||||
trace_mirror_yield(s, UINT64_MAX, s->buf_free_count,
|
trace_mirror_yield(s, UINT64_MAX, s->buf_free_count,
|
||||||
s->in_flight);
|
s->in_flight);
|
||||||
mirror_wait_for_io(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +915,7 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 ||
|
||||||
(cnt == 0 && s->in_flight > 0)) {
|
(cnt == 0 && s->in_flight > 0)) {
|
||||||
trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight);
|
trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight);
|
||||||
mirror_wait_for_io(s);
|
mirror_wait_for_free_in_flight_slot(s);
|
||||||
continue;
|
continue;
|
||||||
} else if (cnt != 0) {
|
} else if (cnt != 0) {
|
||||||
delay_ns = mirror_iteration(s);
|
delay_ns = mirror_iteration(s);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue