#define SLICE_TIME 100000000ULL /* ns */
#define MAX_IN_FLIGHT 16
-#define DEFAULT_MIRROR_BUF_SIZE (10 << 20)
+#define MAX_IO_SECTORS ((1 << 20) >> BDRV_SECTOR_BITS) /* 1 Mb */
+#define DEFAULT_MIRROR_BUF_SIZE \
+ (MAX_IN_FLIGHT * MAX_IO_SECTORS * BDRV_SECTOR_SIZE)
/* The mirroring buffer is a list of granularity-sized chunks.
* Free chunks are organized in a list.
int64_t bdev_length;
unsigned long *cow_bitmap;
BdrvDirtyBitmap *dirty_bitmap;
- HBitmapIter hbi;
+ BdrvDirtyBitmapIter *dbi;
uint8_t *buf;
QSIMPLEQ_HEAD(, MirrorBuffer) buf_free;
int buf_free_count;
+ uint64_t last_pause_ns;
unsigned long *in_flight_bitmap;
int in_flight;
- int sectors_in_flight;
+ int64_t sectors_in_flight;
int ret;
bool unmap;
bool waiting_for_io;
g_free(op);
if (s->waiting_for_io) {
- qemu_coroutine_enter(s->common.co, NULL);
+ qemu_coroutine_enter(s->common.co);
}
}
int sectors_per_chunk, nb_chunks;
int ret;
MirrorOp *op;
+ int max_sectors;
sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
+ max_sectors = sectors_per_chunk * s->max_iov;
/* We can only handle as much as buf_size at a time. */
nb_sectors = MIN(s->buf_size >> BDRV_SECTOR_BITS, nb_sectors);
+ nb_sectors = MIN(max_sectors, nb_sectors);
assert(nb_sectors);
ret = nb_sectors;
s->in_flight++;
s->sectors_in_flight += nb_sectors;
if (is_discard) {
- blk_aio_discard(s->target, sector_num, op->nb_sectors,
- mirror_write_complete, op);
+ blk_aio_pdiscard(s->target, sector_num << BDRV_SECTOR_BITS,
+ op->nb_sectors << BDRV_SECTOR_BITS,
+ mirror_write_complete, op);
} else {
blk_aio_pwrite_zeroes(s->target, sector_num * BDRV_SECTOR_SIZE,
op->nb_sectors * BDRV_SECTOR_SIZE,
int nb_chunks = 1;
int64_t end = s->bdev_length / BDRV_SECTOR_SIZE;
int sectors_per_chunk = s->granularity >> BDRV_SECTOR_BITS;
+ 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,
+ MAX_IO_SECTORS);
- sector_num = hbitmap_iter_next(&s->hbi);
+ sector_num = bdrv_dirty_iter_next(s->dbi);
if (sector_num < 0) {
- bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
- sector_num = hbitmap_iter_next(&s->hbi);
+ bdrv_set_dirty_iter(s->dbi, 0);
+ sector_num = bdrv_dirty_iter_next(s->dbi);
trace_mirror_restart_iter(s, bdrv_get_dirty_count(s->dirty_bitmap));
assert(sector_num >= 0);
}
/* Find the number of consective dirty chunks following the first dirty
* one, and wait for in flight requests in them. */
while (nb_chunks * sectors_per_chunk < (s->buf_size >> BDRV_SECTOR_BITS)) {
- int64_t hbitmap_next;
+ int64_t next_dirty;
int64_t next_sector = sector_num + nb_chunks * sectors_per_chunk;
int64_t next_chunk = next_sector / sectors_per_chunk;
if (next_sector >= end ||
break;
}
- hbitmap_next = hbitmap_iter_next(&s->hbi);
- if (hbitmap_next > next_sector || hbitmap_next < 0) {
+ next_dirty = bdrv_dirty_iter_next(s->dbi);
+ if (next_dirty > next_sector || next_dirty < 0) {
/* The bitmap iterator's cache is stale, refresh it */
- bdrv_set_dirty_iter(&s->hbi, next_sector);
- hbitmap_next = hbitmap_iter_next(&s->hbi);
+ bdrv_set_dirty_iter(s->dbi, next_sector);
+ next_dirty = bdrv_dirty_iter_next(s->dbi);
}
- assert(hbitmap_next == next_sector);
+ assert(next_dirty == next_sector);
nb_chunks++;
}
bitmap_set(s->in_flight_bitmap, sector_num / sectors_per_chunk, nb_chunks);
while (nb_chunks > 0 && sector_num < end) {
int ret;
- int io_sectors;
+ int io_sectors, io_sectors_acct;
BlockDriverState *file;
enum MirrorMethod {
MIRROR_METHOD_COPY,
nb_chunks * sectors_per_chunk,
&io_sectors, &file);
if (ret < 0) {
- io_sectors = nb_chunks * sectors_per_chunk;
+ io_sectors = MIN(nb_chunks * sectors_per_chunk, max_io_sectors);
+ } else if (ret & BDRV_BLOCK_DATA) {
+ io_sectors = MIN(io_sectors, max_io_sectors);
}
io_sectors -= io_sectors % sectors_per_chunk;
}
}
+ while (s->in_flight >= MAX_IN_FLIGHT) {
+ trace_mirror_yield_in_flight(s, sector_num, s->in_flight);
+ mirror_wait_for_io(s);
+ }
+
+ if (s->ret < 0) {
+ return 0;
+ }
+
mirror_clip_sectors(s, sector_num, &io_sectors);
switch (mirror_method) {
case MIRROR_METHOD_COPY:
io_sectors = mirror_do_read(s, sector_num, io_sectors);
+ io_sectors_acct = io_sectors;
break;
case MIRROR_METHOD_ZERO:
- mirror_do_zero_or_discard(s, sector_num, io_sectors, false);
- break;
case MIRROR_METHOD_DISCARD:
- mirror_do_zero_or_discard(s, sector_num, io_sectors, true);
+ mirror_do_zero_or_discard(s, sector_num, io_sectors,
+ mirror_method == MIRROR_METHOD_DISCARD);
+ if (write_zeroes_ok) {
+ io_sectors_acct = 0;
+ } else {
+ io_sectors_acct = io_sectors;
+ }
break;
default:
abort();
assert(io_sectors);
sector_num += io_sectors;
nb_chunks -= DIV_ROUND_UP(io_sectors, sectors_per_chunk);
- delay_ns += ratelimit_calculate_delay(&s->limit, io_sectors);
+ if (s->common.speed) {
+ delay_ns = ratelimit_calculate_delay(&s->limit, io_sectors_acct);
+ }
}
return delay_ns;
}
}
}
-static void mirror_drain(MirrorBlockJob *s)
+/* This is also used for the .pause callback. There is no matching
+ * mirror_resume() because mirror_run() will begin iterating again
+ * when the job is resumed.
+ */
+static void mirror_wait_for_all_io(MirrorBlockJob *s)
{
while (s->in_flight > 0) {
mirror_wait_for_io(s);
g_free(s->replaces);
bdrv_op_unblock_all(target_bs, s->common.blocker);
blk_unref(s->target);
+ s->target = NULL;
block_job_completed(&s->common, data->ret);
g_free(data);
bdrv_drained_end(src);
- if (qemu_get_aio_context() == bdrv_get_aio_context(src)) {
- aio_enable_external(iohandler_get_aio_context());
- }
bdrv_unref(src);
}
+static void mirror_throttle(MirrorBlockJob *s)
+{
+ int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+
+ if (now - s->last_pause_ns > SLICE_TIME) {
+ s->last_pause_ns = now;
+ block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
+ } else {
+ block_job_pause_point(&s->common);
+ }
+}
+
+static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
+{
+ int64_t sector_num, end;
+ BlockDriverState *base = s->base;
+ BlockDriverState *bs = blk_bs(s->common.blk);
+ BlockDriverState *target_bs = blk_bs(s->target);
+ int ret, n;
+
+ end = s->bdev_length / BDRV_SECTOR_SIZE;
+
+ if (base == NULL && !bdrv_has_zero_init(target_bs)) {
+ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) {
+ bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, end);
+ return 0;
+ }
+
+ for (sector_num = 0; sector_num < end; ) {
+ int nb_sectors = MIN(end - sector_num,
+ QEMU_ALIGN_DOWN(INT_MAX, s->granularity) >> BDRV_SECTOR_BITS);
+
+ mirror_throttle(s);
+
+ if (block_job_is_cancelled(&s->common)) {
+ return 0;
+ }
+
+ if (s->in_flight >= MAX_IN_FLIGHT) {
+ trace_mirror_yield(s, s->in_flight, s->buf_free_count, -1);
+ mirror_wait_for_io(s);
+ continue;
+ }
+
+ mirror_do_zero_or_discard(s, sector_num, nb_sectors, false);
+ sector_num += nb_sectors;
+ }
+
+ mirror_wait_for_all_io(s);
+ }
+
+ /* First part, loop on the sectors and initialize the dirty bitmap. */
+ for (sector_num = 0; sector_num < end; ) {
+ /* Just to make sure we are not exceeding int limit. */
+ int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS,
+ end - sector_num);
+
+ mirror_throttle(s);
+
+ if (block_job_is_cancelled(&s->common)) {
+ return 0;
+ }
+
+ ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n);
+ if (ret < 0) {
+ return ret;
+ }
+
+ assert(n > 0);
+ if (ret == 1) {
+ bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
+ }
+ sector_num += n;
+ }
+ return 0;
+}
+
static void coroutine_fn mirror_run(void *opaque)
{
MirrorBlockJob *s = opaque;
MirrorExitData *data;
BlockDriverState *bs = blk_bs(s->common.blk);
BlockDriverState *target_bs = blk_bs(s->target);
- int64_t sector_num, end, length;
- uint64_t last_pause_ns;
+ bool need_drain = true;
+ int64_t length;
BlockDriverInfo bdi;
char backing_filename[2]; /* we only need 2 characters because we are only
checking for a NULL string */
int ret = 0;
- int n;
int target_cluster_size = BDRV_SECTOR_SIZE;
if (block_job_is_cancelled(&s->common)) {
s->target_cluster_sectors = target_cluster_size >> BDRV_SECTOR_BITS;
s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov);
- end = s->bdev_length / BDRV_SECTOR_SIZE;
s->buf = qemu_try_blockalign(bs, s->buf_size);
if (s->buf == NULL) {
ret = -ENOMEM;
mirror_free_init(s);
- last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
if (!s->is_none_mode) {
- /* First part, loop on the sectors and initialize the dirty bitmap. */
- BlockDriverState *base = s->base;
- bool mark_all_dirty = s->base == NULL && !bdrv_has_zero_init(target_bs);
-
- for (sector_num = 0; sector_num < end; ) {
- /* Just to make sure we are not exceeding int limit. */
- int nb_sectors = MIN(INT_MAX >> BDRV_SECTOR_BITS,
- end - sector_num);
- int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
-
- if (now - last_pause_ns > SLICE_TIME) {
- last_pause_ns = now;
- block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, 0);
- } else {
- block_job_pause_point(&s->common);
- }
-
- if (block_job_is_cancelled(&s->common)) {
- goto immediate_exit;
- }
-
- ret = bdrv_is_allocated_above(bs, base, sector_num, nb_sectors, &n);
-
- if (ret < 0) {
- goto immediate_exit;
- }
-
- assert(n > 0);
- if (ret == 1 || mark_all_dirty) {
- bdrv_set_dirty_bitmap(s->dirty_bitmap, sector_num, n);
- }
- sector_num += n;
+ ret = mirror_dirty_init(s);
+ if (ret < 0 || block_job_is_cancelled(&s->common)) {
+ goto immediate_exit;
}
}
- bdrv_dirty_iter_init(s->dirty_bitmap, &s->hbi);
+ assert(!s->dbi);
+ s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap, 0);
for (;;) {
uint64_t delay_ns = 0;
- int64_t cnt;
+ int64_t cnt, delta;
bool should_complete;
if (s->ret < 0) {
* We do so every SLICE_TIME nanoseconds, or when there is an error,
* or when the source is clean, whichever comes first.
*/
- if (qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - last_pause_ns < SLICE_TIME &&
+ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns;
+ if (delta < SLICE_TIME &&
s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
- 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)) {
trace_mirror_yield(s, s->in_flight, s->buf_free_count, cnt);
mirror_wait_for_io(s);
* source has dirty data to copy!
*
* Note that I/O can be submitted by the guest while
- * mirror_populate runs.
+ * mirror_populate runs, so pause it now. Before deciding
+ * whether to switch to target check one last time if I/O has
+ * come in the meanwhile, and if not flush the data to disk.
*/
trace_mirror_before_drain(s, cnt);
- bdrv_co_drain(bs);
+
+ bdrv_drained_begin(bs);
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
+ if (cnt > 0) {
+ bdrv_drained_end(bs);
+ continue;
+ }
+
+ /* The two disks are in sync. Exit and report successful
+ * completion.
+ */
+ assert(QLIST_EMPTY(&bs->tracked_requests));
+ s->common.cancelled = false;
+ need_drain = false;
+ break;
}
ret = 0;
} else if (!should_complete) {
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
block_job_sleep_ns(&s->common, QEMU_CLOCK_REALTIME, delay_ns);
- } else if (cnt == 0) {
- /* The two disks are in sync. Exit and report successful
- * completion.
- */
- assert(QLIST_EMPTY(&bs->tracked_requests));
- s->common.cancelled = false;
- break;
}
- last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
+ s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
}
immediate_exit:
* the target is a copy of the source.
*/
assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
- mirror_drain(s);
+ assert(need_drain);
+ mirror_wait_for_all_io(s);
}
assert(s->in_flight == 0);
qemu_vfree(s->buf);
g_free(s->cow_bitmap);
g_free(s->in_flight_bitmap);
+ bdrv_dirty_iter_free(s->dbi);
bdrv_release_dirty_bitmap(bs, s->dirty_bitmap);
data = g_malloc(sizeof(*data));
data->ret = ret;
- /* Before we switch to target in mirror_exit, make sure data doesn't
- * change. */
- bdrv_drained_begin(bs);
- if (qemu_get_aio_context() == bdrv_get_aio_context(bs)) {
- /* FIXME: virtio host notifiers run on iohandler_ctx, therefore the
- * above bdrv_drained_end isn't enough to quiesce it. This is ugly, we
- * need a block layer API change to achieve this. */
- aio_disable_external(iohandler_get_aio_context());
+
+ if (need_drain) {
+ bdrv_drained_begin(bs);
}
block_job_defer_to_main_loop(&s->common, mirror_exit, data);
}
target = blk_bs(s->target);
if (!s->synced) {
- error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
+ error_setg(errp, "The active block job '%s' cannot be completed",
+ job->id);
return;
}
}
}
- /* check the target bs is not blocked and block all operations on it */
+ /* block all operations on to_replace bs */
if (s->replaces) {
AioContext *replace_aio_context;
block_job_enter(&s->common);
}
-/* There is no matching mirror_resume() because mirror_run() will begin
- * iterating again when the job is resumed.
- */
-static void coroutine_fn mirror_pause(BlockJob *job)
+static void mirror_pause(BlockJob *job)
{
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
- mirror_drain(s);
+ mirror_wait_for_all_io(s);
}
static void mirror_attached_aio_context(BlockJob *job, AioContext *new_context)
blk_set_aio_context(s->target, new_context);
}
+static void mirror_drain(BlockJob *job)
+{
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
+
+ /* Need to keep a reference in case blk_drain triggers execution
+ * of mirror_complete...
+ */
+ if (s->target) {
+ BlockBackend *target = s->target;
+ blk_ref(target);
+ blk_drain(target);
+ blk_unref(target);
+ }
+}
+
static const BlockJobDriver mirror_job_driver = {
.instance_size = sizeof(MirrorBlockJob),
.job_type = BLOCK_JOB_TYPE_MIRROR,
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
+ .drain = mirror_drain,
};
static const BlockJobDriver commit_active_job_driver = {
.complete = mirror_complete,
.pause = mirror_pause,
.attached_aio_context = mirror_attached_aio_context,
+ .drain = mirror_drain,
};
-static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
- const char *replaces,
+static void mirror_start_job(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target, const char *replaces,
int64_t speed, uint32_t granularity,
int64_t buf_size,
BlockMirrorBackingMode backing_mode,
BlockCompletionFunc *cb,
void *opaque, Error **errp,
const BlockJobDriver *driver,
- bool is_none_mode, BlockDriverState *base)
+ bool is_none_mode, BlockDriverState *base,
+ bool auto_complete)
{
MirrorBlockJob *s;
buf_size = DEFAULT_MIRROR_BUF_SIZE;
}
- s = block_job_create(driver, bs, speed, cb, opaque, errp);
+ s = block_job_create(job_id, driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
s->granularity = granularity;
s->buf_size = ROUND_UP(buf_size, granularity);
s->unmap = unmap;
+ if (auto_complete) {
+ s->should_complete = true;
+ }
s->dirty_bitmap = bdrv_create_dirty_bitmap(bs, granularity, NULL, errp);
if (!s->dirty_bitmap) {
bdrv_op_block_all(target, s->common.blocker);
- s->common.co = qemu_coroutine_create(mirror_run);
+ s->common.co = qemu_coroutine_create(mirror_run, s);
trace_mirror_start(bs, s, s->common.co, opaque);
- qemu_coroutine_enter(s->common.co, s);
+ qemu_coroutine_enter(s->common.co);
}
-void mirror_start(BlockDriverState *bs, BlockDriverState *target,
- const char *replaces,
+void mirror_start(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *target, const char *replaces,
int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
}
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
- mirror_start_job(bs, target, replaces,
+ mirror_start_job(job_id, bs, target, replaces,
speed, granularity, buf_size, backing_mode,
on_source_error, on_target_error, unmap, cb, opaque, errp,
- &mirror_job_driver, is_none_mode, base);
+ &mirror_job_driver, is_none_mode, base, false);
}
-void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
- int64_t speed,
+void commit_active_start(const char *job_id, BlockDriverState *bs,
+ BlockDriverState *base, int64_t speed,
BlockdevOnError on_error,
BlockCompletionFunc *cb,
- void *opaque, Error **errp)
+ void *opaque, Error **errp,
+ bool auto_complete)
{
int64_t length, base_length;
int orig_base_flags;
}
}
- mirror_start_job(bs, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN,
+ mirror_start_job(job_id, bs, base, NULL, speed, 0, 0,
+ MIRROR_LEAVE_BACKING_CHAIN,
on_error, on_error, false, cb, opaque, &local_err,
- &commit_active_job_driver, false, base);
+ &commit_active_job_driver, false, base, auto_complete);
if (local_err) {
error_propagate(errp, local_err);
goto error_restore_flags;