*/
#include "qemu/osdep.h"
+#include "qemu/cutils.h"
#include "trace.h"
#include "block/block_int.h"
#include "block/blockjob_int.h"
} CommitBlockJob;
static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
- int64_t sector_num, int nb_sectors,
+ int64_t offset, uint64_t bytes,
void *buf)
{
int ret = 0;
QEMUIOVector qiov;
struct iovec iov = {
.iov_base = buf,
- .iov_len = nb_sectors * BDRV_SECTOR_SIZE,
+ .iov_len = bytes,
};
+ assert(bytes < SIZE_MAX);
qemu_iovec_init_external(&qiov, &iov, 1);
- ret = blk_co_preadv(bs, sector_num * BDRV_SECTOR_SIZE,
- qiov.size, &qiov, 0);
+ ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0);
if (ret < 0) {
return ret;
}
- ret = blk_co_pwritev(base, sector_num * BDRV_SECTOR_SIZE,
- qiov.size, &qiov, 0);
+ ret = blk_co_pwritev(base, offset, qiov.size, &qiov, 0);
if (ret < 0) {
return ret;
}
int ret = data->ret;
bool remove_commit_top_bs = false;
+ /* Make sure overlay_bs and top stay around until bdrv_set_backing_hd() */
+ bdrv_ref(top);
+ bdrv_ref(overlay_bs);
+
/* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before
* the normal backing chain can be restored. */
blk_unref(s->base);
}
g_free(s->backing_file_str);
blk_unref(s->top);
+
+ /* If there is more than one reference to the job (e.g. if called from
+ * block_job_finish_sync()), block_job_completed() won't free it and
+ * therefore the blockers on the intermediate nodes remain. This would
+ * cause bdrv_set_backing_hd() to fail. */
+ block_job_remove_all_bdrv(job);
+
block_job_completed(&s->common, ret);
g_free(data);
if (remove_commit_top_bs) {
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
}
+
+ bdrv_unref(overlay_bs);
+ bdrv_unref(top);
}
static void coroutine_fn commit_run(void *opaque)
{
CommitBlockJob *s = opaque;
CommitCompleteData *data;
- int64_t sector_num, end;
+ int64_t offset;
uint64_t delay_ns = 0;
int ret = 0;
- int n = 0;
+ int64_t n = 0; /* bytes */
void *buf = NULL;
int bytes_written = 0;
int64_t base_len;
ret = s->common.len = blk_getlength(s->top);
-
if (s->common.len < 0) {
goto out;
}
}
if (base_len < s->common.len) {
- ret = blk_truncate(s->base, s->common.len);
+ ret = blk_truncate(s->base, s->common.len, NULL);
if (ret) {
goto out;
}
}
- end = s->common.len >> BDRV_SECTOR_BITS;
buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE);
- for (sector_num = 0; sector_num < end; sector_num += n) {
+ for (offset = 0; offset < s->common.len; offset += n) {
bool copy;
/* Note that even when no rate limit is applied we need to yield
}
/* Copy if allocated above the base */
ret = bdrv_is_allocated_above(blk_bs(s->top), blk_bs(s->base),
- sector_num,
- COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE,
- &n);
+ offset, COMMIT_BUFFER_SIZE, &n);
copy = (ret == 1);
- trace_commit_one_iteration(s, sector_num, n, ret);
+ trace_commit_one_iteration(s, offset, n, ret);
if (copy) {
- ret = commit_populate(s->top, s->base, sector_num, n, buf);
- bytes_written += n * BDRV_SECTOR_SIZE;
+ ret = commit_populate(s->top, s->base, offset, n, buf);
+ bytes_written += n;
}
if (ret < 0) {
BlockErrorAction action =
}
}
/* Publish progress */
- s->common.offset += n * BDRV_SECTOR_SIZE;
+ s->common.offset += n;
if (copy && s->common.speed) {
delay_ns = ratelimit_calculate_delay(&s->limit, n);
error_setg(errp, QERR_INVALID_PARAMETER, "speed");
return;
}
- ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME);
+ ratelimit_set_speed(&s->limit, speed, SLICE_TIME);
}
static const BlockJobDriver commit_job_driver = {
return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags);
}
+static int64_t coroutine_fn bdrv_commit_top_get_block_status(
+ BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum,
+ BlockDriverState **file)
+{
+ *pnum = nb_sectors;
+ *file = bs->backing->bs;
+ return BDRV_BLOCK_RAW | BDRV_BLOCK_OFFSET_VALID |
+ (sector_num << BDRV_SECTOR_BITS);
+}
+
+static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts)
+{
+ bdrv_refresh_filename(bs->backing->bs);
+ pstrcpy(bs->exact_filename, sizeof(bs->exact_filename),
+ bs->backing->bs->filename);
+}
+
static void bdrv_commit_top_close(BlockDriverState *bs)
{
}
/* Dummy node that provides consistent read to its users without requiring it
* from its backing file and that allows writes on the backing file chain. */
static BlockDriver bdrv_commit_top = {
- .format_name = "commit_top",
- .bdrv_co_preadv = bdrv_commit_top_preadv,
- .bdrv_close = bdrv_commit_top_close,
- .bdrv_child_perm = bdrv_commit_top_child_perm,
+ .format_name = "commit_top",
+ .bdrv_co_preadv = bdrv_commit_top_preadv,
+ .bdrv_co_get_block_status = bdrv_commit_top_get_block_status,
+ .bdrv_refresh_filename = bdrv_commit_top_refresh_filename,
+ .bdrv_close = bdrv_commit_top_close,
+ .bdrv_child_perm = bdrv_commit_top_child_perm,
};
void commit_start(const char *job_id, BlockDriverState *bs,
if (commit_top_bs == NULL) {
goto fail;
}
+ commit_top_bs->total_sectors = top->total_sectors;
+ bdrv_set_aio_context(commit_top_bs, bdrv_get_aio_context(top));
- bdrv_set_backing_hd(commit_top_bs, top, &error_abort);
- bdrv_set_backing_hd(overlay_bs, commit_top_bs, &error_abort);
+ bdrv_set_backing_hd(commit_top_bs, top, &local_err);
+ if (local_err) {
+ bdrv_unref(commit_top_bs);
+ commit_top_bs = NULL;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
+ bdrv_set_backing_hd(overlay_bs, commit_top_bs, &local_err);
+ if (local_err) {
+ bdrv_unref(commit_top_bs);
+ commit_top_bs = NULL;
+ error_propagate(errp, local_err);
+ goto fail;
+ }
s->commit_top_bs = commit_top_bs;
bdrv_unref(commit_top_bs);
/* Required permissions are already taken with block_job_add_bdrv() */
s->top = blk_new(0, BLK_PERM_ALL);
- blk_insert_bs(s->top, top, errp);
+ ret = blk_insert_bs(s->top, top, errp);
if (ret < 0) {
goto fail;
}
if (commit_top_bs) {
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
}
- block_job_unref(&s->common);
+ block_job_early_fail(&s->common);
}
-#define COMMIT_BUF_SECTORS 2048
+#define COMMIT_BUF_SIZE (2048 * BDRV_SECTOR_SIZE)
/* commit COW file into the raw image */
int bdrv_commit(BlockDriverState *bs)
BlockDriverState *backing_file_bs = NULL;
BlockDriverState *commit_top_bs = NULL;
BlockDriver *drv = bs->drv;
- int64_t sector, total_sectors, length, backing_length;
- int n, ro, open_flags;
+ int64_t offset, length, backing_length;
+ int ro, open_flags;
+ int64_t n;
int ret = 0;
uint8_t *buf = NULL;
Error *local_err = 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);
* grow the backing file image if possible. If not possible,
* we must return an error */
if (length > backing_length) {
- ret = blk_truncate(backing, length);
+ ret = blk_truncate(backing, length, &local_err);
if (ret < 0) {
+ error_report_err(local_err);
goto ro_cleanup;
}
}
- total_sectors = length >> BDRV_SECTOR_BITS;
-
/* 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_SECTORS * BDRV_SECTOR_SIZE);
+ buf = blk_try_blockalign(src, COMMIT_BUF_SIZE);
if (buf == NULL) {
ret = -ENOMEM;
goto ro_cleanup;
}
- for (sector = 0; sector < total_sectors; sector += n) {
- ret = bdrv_is_allocated(bs, sector, COMMIT_BUF_SECTORS, &n);
+ 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, sector * BDRV_SECTOR_SIZE, buf,
- n * BDRV_SECTOR_SIZE);
+ ret = blk_pread(src, offset, buf, n);
if (ret < 0) {
goto ro_cleanup;
}
- ret = blk_pwrite(backing, sector * BDRV_SECTOR_SIZE, buf,
- n * BDRV_SECTOR_SIZE, 0);
+ ret = blk_pwrite(backing, offset, buf, n, 0);
if (ret < 0) {
goto ro_cleanup;
}