mirror of https://github.com/xemu-project/xemu.git
mirror: Switch MirrorBlockJob to byte-based
We are gradually converting to byte-based interfaces, as they are easier to reason about than sector-based. Continue by converting an internal structure (no semantic change), and all references to the buffer size. Add an assertion that our use of s->granularity >> BDRV_SECTOR_BITS (necessary for interaction with sector-based dirty bitmaps, until a later patch converts those to be byte-based) does not suffer from truncation problems. [checkpatch has a false positive on use of MIN() in this patch] Signed-off-by: Eric Blake <eblake@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
parent
317a6676a2
commit
b436982f04
|
@ -24,9 +24,8 @@
|
||||||
|
|
||||||
#define SLICE_TIME 100000000ULL /* ns */
|
#define SLICE_TIME 100000000ULL /* ns */
|
||||||
#define MAX_IN_FLIGHT 16
|
#define MAX_IN_FLIGHT 16
|
||||||
#define MAX_IO_SECTORS ((1 << 20) >> BDRV_SECTOR_BITS) /* 1 Mb */
|
#define MAX_IO_BYTES (1 << 20) /* 1 Mb */
|
||||||
#define DEFAULT_MIRROR_BUF_SIZE \
|
#define DEFAULT_MIRROR_BUF_SIZE (MAX_IN_FLIGHT * MAX_IO_BYTES)
|
||||||
(MAX_IN_FLIGHT * MAX_IO_SECTORS * BDRV_SECTOR_SIZE)
|
|
||||||
|
|
||||||
/* The mirroring buffer is a list of granularity-sized chunks.
|
/* The mirroring buffer is a list of granularity-sized chunks.
|
||||||
* Free chunks are organized in a list.
|
* Free chunks are organized in a list.
|
||||||
|
@ -67,11 +66,11 @@ typedef struct MirrorBlockJob {
|
||||||
uint64_t last_pause_ns;
|
uint64_t last_pause_ns;
|
||||||
unsigned long *in_flight_bitmap;
|
unsigned long *in_flight_bitmap;
|
||||||
int in_flight;
|
int in_flight;
|
||||||
int64_t sectors_in_flight;
|
int64_t bytes_in_flight;
|
||||||
int ret;
|
int ret;
|
||||||
bool unmap;
|
bool unmap;
|
||||||
bool waiting_for_io;
|
bool waiting_for_io;
|
||||||
int target_cluster_sectors;
|
int target_cluster_size;
|
||||||
int max_iov;
|
int max_iov;
|
||||||
bool initial_zeroing_ongoing;
|
bool initial_zeroing_ongoing;
|
||||||
} MirrorBlockJob;
|
} MirrorBlockJob;
|
||||||
|
@ -79,8 +78,8 @@ typedef struct MirrorBlockJob {
|
||||||
typedef struct MirrorOp {
|
typedef struct MirrorOp {
|
||||||
MirrorBlockJob *s;
|
MirrorBlockJob *s;
|
||||||
QEMUIOVector qiov;
|
QEMUIOVector qiov;
|
||||||
int64_t sector_num;
|
int64_t offset;
|
||||||
int nb_sectors;
|
uint64_t bytes;
|
||||||
} MirrorOp;
|
} MirrorOp;
|
||||||
|
|
||||||
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
static BlockErrorAction mirror_error_action(MirrorBlockJob *s, bool read,
|
||||||
|
@ -101,13 +100,12 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||||
MirrorBlockJob *s = op->s;
|
MirrorBlockJob *s = op->s;
|
||||||
struct iovec *iov;
|
struct iovec *iov;
|
||||||
int64_t chunk_num;
|
int64_t chunk_num;
|
||||||
int i, nb_chunks, sectors_per_chunk;
|
int i, nb_chunks;
|
||||||
|
|
||||||
trace_mirror_iteration_done(s, op->sector_num * BDRV_SECTOR_SIZE,
|
trace_mirror_iteration_done(s, op->offset, op->bytes, ret);
|
||||||
op->nb_sectors * BDRV_SECTOR_SIZE, ret);
|
|
||||||
|
|
||||||
s->in_flight--;
|
s->in_flight--;
|
||||||
s->sectors_in_flight -= op->nb_sectors;
|
s->bytes_in_flight -= op->bytes;
|
||||||
iov = op->qiov.iov;
|
iov = op->qiov.iov;
|
||||||
for (i = 0; i < op->qiov.niov; i++) {
|
for (i = 0; i < op->qiov.niov; i++) {
|
||||||
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
|
MirrorBuffer *buf = (MirrorBuffer *) iov[i].iov_base;
|
||||||
|
@ -115,16 +113,15 @@ static void mirror_iteration_done(MirrorOp *op, int ret)
|
||||||
s->buf_free_count++;
|
s->buf_free_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
chunk_num = op->offset / s->granularity;
|
||||||
chunk_num = op->sector_num / sectors_per_chunk;
|
nb_chunks = DIV_ROUND_UP(op->bytes, s->granularity);
|
||||||
nb_chunks = DIV_ROUND_UP(op->nb_sectors, sectors_per_chunk);
|
|
||||||
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
|
bitmap_clear(s->in_flight_bitmap, chunk_num, nb_chunks);
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
if (s->cow_bitmap) {
|
if (s->cow_bitmap) {
|
||||||
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
|
||||||
}
|
}
|
||||||
if (!s->initial_zeroing_ongoing) {
|
if (!s->initial_zeroing_ongoing) {
|
||||||
s->common.offset += (uint64_t)op->nb_sectors * BDRV_SECTOR_SIZE;
|
s->common.offset += op->bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu_iovec_destroy(&op->qiov);
|
qemu_iovec_destroy(&op->qiov);
|
||||||
|
@ -144,7 +141,8 @@ static void mirror_write_complete(void *opaque, int ret)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
BlockErrorAction action;
|
BlockErrorAction action;
|
||||||
|
|
||||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors);
|
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS,
|
||||||
|
op->bytes >> BDRV_SECTOR_BITS);
|
||||||
action = mirror_error_action(s, false, -ret);
|
action = mirror_error_action(s, false, -ret);
|
||||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||||
s->ret = ret;
|
s->ret = ret;
|
||||||
|
@ -163,7 +161,8 @@ static void mirror_read_complete(void *opaque, int ret)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
BlockErrorAction action;
|
BlockErrorAction action;
|
||||||
|
|
||||||
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->sector_num, op->nb_sectors);
|
bdrv_set_dirty_bitmap(s->dirty_bitmap, op->offset >> BDRV_SECTOR_BITS,
|
||||||
|
op->bytes >> BDRV_SECTOR_BITS);
|
||||||
action = mirror_error_action(s, true, -ret);
|
action = mirror_error_action(s, true, -ret);
|
||||||
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
if (action == BLOCK_ERROR_ACTION_REPORT && s->ret >= 0) {
|
||||||
s->ret = ret;
|
s->ret = ret;
|
||||||
|
@ -171,7 +170,7 @@ static void mirror_read_complete(void *opaque, int ret)
|
||||||
|
|
||||||
mirror_iteration_done(op, ret);
|
mirror_iteration_done(op, ret);
|
||||||
} else {
|
} else {
|
||||||
blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov,
|
blk_aio_pwritev(s->target, op->offset, &op->qiov,
|
||||||
0, mirror_write_complete, op);
|
0, mirror_write_complete, op);
|
||||||
}
|
}
|
||||||
aio_context_release(blk_get_aio_context(s->common.blk));
|
aio_context_release(blk_get_aio_context(s->common.blk));
|
||||||
|
@ -211,7 +210,8 @@ static int mirror_cow_align(MirrorBlockJob *s,
|
||||||
align_nb_sectors = max_sectors;
|
align_nb_sectors = max_sectors;
|
||||||
if (need_cow) {
|
if (need_cow) {
|
||||||
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
align_nb_sectors = QEMU_ALIGN_DOWN(align_nb_sectors,
|
||||||
s->target_cluster_sectors);
|
s->target_cluster_size >>
|
||||||
|
BDRV_SECTOR_BITS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Clipping may result in align_nb_sectors unaligned to chunk boundary, but
|
/* Clipping may result in align_nb_sectors unaligned to chunk boundary, but
|
||||||
|
@ -277,8 +277,8 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||||
/* Allocate a MirrorOp that is used as an AIO callback. */
|
/* Allocate a MirrorOp that is used as an AIO callback. */
|
||||||
op = g_new(MirrorOp, 1);
|
op = g_new(MirrorOp, 1);
|
||||||
op->s = s;
|
op->s = s;
|
||||||
op->sector_num = sector_num;
|
op->offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
op->nb_sectors = nb_sectors;
|
op->bytes = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
/* Now make a QEMUIOVector taking enough granularity-sized chunks
|
||||||
* from s->buf_free.
|
* from s->buf_free.
|
||||||
|
@ -295,7 +295,7 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
|
||||||
|
|
||||||
/* Copy the dirty cluster. */
|
/* Copy the dirty cluster. */
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
s->sectors_in_flight += nb_sectors;
|
s->bytes_in_flight += nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
trace_mirror_one_iteration(s, sector_num * BDRV_SECTOR_SIZE,
|
trace_mirror_one_iteration(s, sector_num * BDRV_SECTOR_SIZE,
|
||||||
nb_sectors * BDRV_SECTOR_SIZE);
|
nb_sectors * BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
|
@ -315,19 +315,17 @@ static void mirror_do_zero_or_discard(MirrorBlockJob *s,
|
||||||
* so the freeing in mirror_iteration_done is nop. */
|
* so the freeing in mirror_iteration_done is nop. */
|
||||||
op = g_new0(MirrorOp, 1);
|
op = g_new0(MirrorOp, 1);
|
||||||
op->s = s;
|
op->s = s;
|
||||||
op->sector_num = sector_num;
|
op->offset = sector_num * BDRV_SECTOR_SIZE;
|
||||||
op->nb_sectors = nb_sectors;
|
op->bytes = nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
s->in_flight++;
|
s->in_flight++;
|
||||||
s->sectors_in_flight += nb_sectors;
|
s->bytes_in_flight += nb_sectors * BDRV_SECTOR_SIZE;
|
||||||
if (is_discard) {
|
if (is_discard) {
|
||||||
blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS,
|
blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS,
|
||||||
op->nb_sectors << BDRV_SECTOR_BITS,
|
op->bytes, mirror_write_complete, op);
|
||||||
mirror_write_complete, op);
|
|
||||||
} else {
|
} else {
|
||||||
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE,
|
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE,
|
||||||
op->nb_sectors * BDRV_SECTOR_SIZE,
|
op->bytes, s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
||||||
s->unmap ? BDRV_REQ_MAY_UNMAP : 0,
|
|
||||||
mirror_write_complete, op);
|
mirror_write_complete, op);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -342,8 +340,7 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
|
||||||
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
|
||||||
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));
|
||||||
int max_io_sectors = MAX((s->buf_size >> BDRV_SECTOR_BITS) / MAX_IN_FLIGHT,
|
int max_io_bytes = MAX(s->buf_size / MAX_IN_FLIGHT, MAX_IO_BYTES);
|
||||||
MAX_IO_SECTORS);
|
|
||||||
|
|
||||||
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
bdrv_dirty_bitmap_lock(s->dirty_bitmap);
|
||||||
sector_num = bdrv_dirty_iter_next(s->dbi);
|
sector_num = bdrv_dirty_iter_next(s->dbi);
|
||||||
|
@ -415,9 +412,10 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
|
||||||
nb_chunks * sectors_per_chunk,
|
nb_chunks * sectors_per_chunk,
|
||||||
&io_sectors, &file);
|
&io_sectors, &file);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
io_sectors = MIN(nb_chunks * sectors_per_chunk, max_io_sectors);
|
io_sectors = MIN(nb_chunks * sectors_per_chunk,
|
||||||
|
max_io_bytes >> BDRV_SECTOR_BITS);
|
||||||
} else if (ret & BDRV_BLOCK_DATA) {
|
} else if (ret & BDRV_BLOCK_DATA) {
|
||||||
io_sectors = MIN(io_sectors, max_io_sectors);
|
io_sectors = MIN(io_sectors, max_io_bytes >> BDRV_SECTOR_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_sectors -= io_sectors % sectors_per_chunk;
|
io_sectors -= io_sectors % sectors_per_chunk;
|
||||||
|
@ -719,7 +717,6 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
char backing_filename[2]; /* we only need 2 characters because we are only
|
char backing_filename[2]; /* we only need 2 characters because we are only
|
||||||
checking for a NULL string */
|
checking for a NULL string */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
int target_cluster_size = BDRV_SECTOR_SIZE;
|
|
||||||
|
|
||||||
if (block_job_is_cancelled(&s->common)) {
|
if (block_job_is_cancelled(&s->common)) {
|
||||||
goto immediate_exit;
|
goto immediate_exit;
|
||||||
|
@ -771,14 +768,15 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
bdrv_get_backing_filename(target_bs, backing_filename,
|
bdrv_get_backing_filename(target_bs, backing_filename,
|
||||||
sizeof(backing_filename));
|
sizeof(backing_filename));
|
||||||
if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) {
|
if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) {
|
||||||
target_cluster_size = bdi.cluster_size;
|
s->target_cluster_size = bdi.cluster_size;
|
||||||
|
} else {
|
||||||
|
s->target_cluster_size = BDRV_SECTOR_SIZE;
|
||||||
}
|
}
|
||||||
if (backing_filename[0] && !target_bs->backing
|
if (backing_filename[0] && !target_bs->backing &&
|
||||||
&& s->granularity < target_cluster_size) {
|
s->granularity < s->target_cluster_size) {
|
||||||
s->buf_size = MAX(s->buf_size, target_cluster_size);
|
s->buf_size = MAX(s->buf_size, s->target_cluster_size);
|
||||||
s->cow_bitmap = bitmap_new(length);
|
s->cow_bitmap = bitmap_new(length);
|
||||||
}
|
}
|
||||||
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
|
|
||||||
s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
|
s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
|
||||||
|
|
||||||
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
s->buf = qemu_try_blockalign(bs, s->buf_size);
|
||||||
|
@ -814,10 +812,10 @@ static void coroutine_fn mirror_run(void *opaque)
|
||||||
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
|
||||||
/* s->common.offset contains the number of bytes already processed so
|
/* s->common.offset contains the number of bytes already processed so
|
||||||
* far, cnt is the number of dirty sectors remaining and
|
* far, cnt is the number of dirty sectors remaining and
|
||||||
* s->sectors_in_flight is the number of sectors currently being
|
* s->bytes_in_flight is the number of bytes currently being
|
||||||
* processed; together those are the current total operation length */
|
* processed; together those are the current total operation length */
|
||||||
s->common.len = s->common.offset +
|
s->common.len = s->common.offset + s->bytes_in_flight +
|
||||||
(cnt + s->sectors_in_flight) * BDRV_SECTOR_SIZE;
|
cnt * BDRV_SECTOR_SIZE;
|
||||||
|
|
||||||
/* Note that even when no rate limit is applied we need to yield
|
/* Note that even when no rate limit is applied we need to yield
|
||||||
* periodically with no pending I/O so that bdrv_drain_all() returns.
|
* periodically with no pending I/O so that bdrv_drain_all() returns.
|
||||||
|
@ -1150,6 +1148,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert ((granularity & (granularity - 1)) == 0);
|
assert ((granularity & (granularity - 1)) == 0);
|
||||||
|
/* Granularity must be large enough for sector-based dirty bitmap */
|
||||||
|
assert(granularity >= BDRV_SECTOR_SIZE);
|
||||||
|
|
||||||
if (buf_size < 0) {
|
if (buf_size < 0) {
|
||||||
error_setg(errp, "Invalid parameter 'buf-size'");
|
error_setg(errp, "Invalid parameter 'buf-size'");
|
||||||
|
|
Loading…
Reference in New Issue