/* commit COW file into the raw image */ int bdrv_commit(BlockDriverState *bs) { BlockBackend *src, *backing; BlockDriverState *backing_file_bs = NULL; BlockDriverState *commit_top_bs = NULL; BlockDriver *drv = bs->drv; int64_t offset, length, backing_length; int ro; int64_t n; int ret = 0; uint8_t *buf = NULL; Error *local_err = NULL; if (!drv) return -ENOMEDIUM; if (!bs->backing) { return -ENOTSUP; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || bdrv_op_is_blocked(bs->backing->bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { return -EBUSY; } ro = bs->backing->bs->read_only; if (ro) { if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { return -EACCES; } } src = blk_new(BLK_PERM_CONSISTENT_READ, BLK_PERM_ALL); backing = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); ret = blk_insert_bs(src, bs, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } /* Insert commit_top block node above backing, so we can write to it */ backing_file_bs = backing_bs(bs); commit_top_bs = bdrv_new_open_driver(&bdrv_commit_top, NULL, BDRV_O_RDWR, &local_err); if (commit_top_bs == NULL) { error_report_err(local_err); goto ro_cleanup; } bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(backing_file_bs)); bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); ret = blk_insert_bs(backing, backing_file_bs, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } length = blk_getlength(src); if (length < 0) { ret = length; goto ro_cleanup; } backing_length = blk_getlength(backing); if (backing_length < 0) { ret = backing_length; goto ro_cleanup; } /* If our top snapshot is larger than the backing file image, * grow the backing file image if possible. If not possible, * we must return an error */ if (length > backing_length) { ret = blk_truncate(backing, length, PREALLOC_MODE_OFF, &local_err); if (ret < 0) { error_report_err(local_err); goto ro_cleanup; } } /* blk_try_blockalign() for src will choose an alignment that works for * backing as well, so no need to compare the alignment manually. */ buf = blk_try_blockalign(src, COMMIT_BUF_SIZE); if (buf == NULL) { ret = -ENOMEM; goto ro_cleanup; } for (offset = 0; offset < length; offset += n) { ret = bdrv_is_allocated(bs, offset, COMMIT_BUF_SIZE, &n); if (ret < 0) { goto ro_cleanup; } if (ret) { ret = blk_pread(src, offset, buf, n); if (ret < 0) { goto ro_cleanup; } ret = blk_pwrite(backing, offset, buf, n, 0); if (ret < 0) { goto ro_cleanup; } } } if (drv->bdrv_make_empty) { ret = drv->bdrv_make_empty(bs); if (ret < 0) { goto ro_cleanup; } blk_flush(src); } /* * Make sure all data we wrote to the backing device is actually * stable on disk. */ blk_flush(backing); ret = 0; ro_cleanup: qemu_vfree(buf); blk_unref(backing); if (backing_file_bs) { bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); } bdrv_unref(commit_top_bs); blk_unref(src); if (ro) { /* ignoring error return here */ bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); } return ret; }
static void coroutine_fn mirror_run(void *opaque) { MirrorBlockJob *s = opaque; MirrorExitData *data; BlockDriverState *bs = s->source; BlockDriverState *target_bs = blk_bs(s->target); 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; if (block_job_is_cancelled(&s->common)) { goto immediate_exit; } s->bdev_length = bdrv_getlength(bs); if (s->bdev_length < 0) { ret = s->bdev_length; goto immediate_exit; } /* Active commit must resize the base image if its size differs from the * active layer. */ if (s->base == blk_bs(s->target)) { int64_t base_length; base_length = blk_getlength(s->target); if (base_length < 0) { ret = base_length; goto immediate_exit; } if (s->bdev_length > base_length) { ret = blk_truncate(s->target, s->bdev_length, PREALLOC_MODE_OFF, NULL); if (ret < 0) { goto immediate_exit; } } } if (s->bdev_length == 0) { /* Report BLOCK_JOB_READY and wait for complete. */ block_job_event_ready(&s->common); s->synced = true; while (!block_job_is_cancelled(&s->common) && !s->should_complete) { block_job_yield(&s->common); } s->common.cancelled = false; goto immediate_exit; } length = DIV_ROUND_UP(s->bdev_length, s->granularity); s->in_flight_bitmap = bitmap_new(length); /* If we have no backing file yet in the destination, we cannot let * the destination do COW. Instead, we copy sectors around the * dirty data if needed. We need a bitmap to do that. */ bdrv_get_backing_filename(target_bs, backing_filename, sizeof(backing_filename)); if (!bdrv_get_info(target_bs, &bdi) && 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 && s->granularity < s->target_cluster_size) { s->buf_size = MAX(s->buf_size, s->target_cluster_size); s->cow_bitmap = bitmap_new(length); } s->max_iov = MIN(bs->bl.max_iov, target_bs->bl.max_iov); s->buf = qemu_try_blockalign(bs, s->buf_size); if (s->buf == NULL) { ret = -ENOMEM; goto immediate_exit; } mirror_free_init(s); s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); if (!s->is_none_mode) { ret = mirror_dirty_init(s); if (ret < 0 || block_job_is_cancelled(&s->common)) { goto immediate_exit; } } assert(!s->dbi); s->dbi = bdrv_dirty_iter_new(s->dirty_bitmap); for (;;) { uint64_t delay_ns = 0; int64_t cnt, delta; bool should_complete; if (s->ret < 0) { ret = s->ret; goto immediate_exit; } block_job_pause_point(&s->common); cnt = bdrv_get_dirty_count(s->dirty_bitmap); /* cnt is the number of dirty bytes remaining and s->bytes_in_flight is * the number of bytes currently being processed; together those are * the current remaining operation length */ block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt); /* 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. * We do so every BLKOCK_JOB_SLICE_TIME nanoseconds, or when there is * an error, or when the source is clean, whichever comes first. */ delta = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - s->last_pause_ns; if (delta < BLOCK_JOB_SLICE_TIME && s->common.iostatus == BLOCK_DEVICE_IO_STATUS_OK) { if (s->in_flight >= MAX_IN_FLIGHT || s->buf_free_count == 0 || (cnt == 0 && s->in_flight > 0)) { trace_mirror_yield(s, cnt, s->buf_free_count, s->in_flight); mirror_wait_for_io(s); continue; } else if (cnt != 0) { delay_ns = mirror_iteration(s); } } should_complete = false; if (s->in_flight == 0 && cnt == 0) { trace_mirror_before_flush(s); if (!s->synced) { if (mirror_flush(s) < 0) { /* Go check s->ret. */ continue; } /* We're out of the streaming phase. From now on, if the job * is cancelled we will actually complete all pending I/O and * report completion. This way, block-job-cancel will leave * the target in a consistent state. */ block_job_event_ready(&s->common); s->synced = true; } should_complete = s->should_complete || block_job_is_cancelled(&s->common); cnt = bdrv_get_dirty_count(s->dirty_bitmap); } if (cnt == 0 && should_complete) { /* The dirty bitmap is not updated while operations are pending. * If we're about to exit, wait for pending operations before * calling bdrv_get_dirty_count(bs), or we may exit while the * source has dirty data to copy! * * Note that I/O can be submitted by the guest while * 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_drained_begin(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); if (cnt > 0 || mirror_flush(s) < 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; if (s->synced && !should_complete) { delay_ns = (s->in_flight == 0 && cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0); } trace_mirror_before_sleep(s, cnt, s->synced, delay_ns); block_job_sleep_ns(&s->common, delay_ns); if (block_job_is_cancelled(&s->common) && (!s->synced || s->common.force)) { break; } s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } immediate_exit: if (s->in_flight > 0) { /* We get here only if something went wrong. Either the job failed, * or it was cancelled prematurely so that we do not guarantee that * the target is a copy of the source. */ assert(ret < 0 || ((s->common.force || !s->synced) && block_job_is_cancelled(&s->common))); 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); data = g_malloc(sizeof(*data)); data->ret = ret; if (need_drain) { bdrv_drained_begin(bs); } block_job_defer_to_main_loop(&s->common, mirror_exit, data); }
static int coroutine_fn commit_run(Job *job, Error **errp) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); int64_t offset; uint64_t delay_ns = 0; int ret = 0; int64_t n = 0; /* bytes */ void *buf = NULL; int bytes_written = 0; int64_t len, base_len; ret = len = blk_getlength(s->top); if (len < 0) { goto out; } job_progress_set_remaining(&s->common.job, len); ret = base_len = blk_getlength(s->base); if (base_len < 0) { goto out; } if (base_len < len) { ret = blk_truncate(s->base, len, PREALLOC_MODE_OFF, NULL); if (ret) { goto out; } } buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); for (offset = 0; offset < len; offset += n) { bool copy; /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ job_sleep_ns(&s->common.job, delay_ns); if (job_is_cancelled(&s->common.job)) { break; } /* Copy if allocated above the base */ ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base), offset, COMMIT_BUFFER_SIZE, &n); copy = (ret == 1); trace_commit_one_iteration(s, offset, n, ret); if (copy) { ret = commit_populate(s->top, s->base, offset, n, buf); bytes_written += n; } if (ret < 0) { BlockErrorAction action = block_job_error_action(&s->common, false, s->on_error, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { goto out; } else { n = 0; continue; } } /* Publish progress */ job_progress_update(&s->common.job, n); if (copy) { delay_ns = block_job_ratelimit_get_delay(&s->common, n); } else { delay_ns = 0; } } ret = 0; out: qemu_vfree(buf); return ret; }