#include "sysemu/arch_init.h"
#include "sysemu/qtest.h"
#include "sysemu/runstate.h"
+#include "sysemu/replay.h"
#include "qemu/cutils.h"
#include "qemu/help_option.h"
#include "qemu/main-loop.h"
info->vm_state_size = sn.vm_state_size;
info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
+ if (sn.icount != -1ULL) {
+ info->icount = sn.icount;
+ info->has_icount = true;
+ }
return info;
sn->date_sec = tv.tv_sec;
sn->date_nsec = tv.tv_usec * 1000;
sn->vm_clock_nsec = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ if (replay_mode != REPLAY_MODE_NONE) {
+ sn->icount = replay_get_current_icount();
+ } else {
+ sn->icount = -1ULL;
+ }
ret1 = bdrv_snapshot_create(bs, sn);
if (ret1 < 0) {
goto out;
}
- if (state->new_bs->backing != NULL) {
+ if (state->new_bs->drv->is_filter) {
+ error_setg(errp, "Filters cannot be used as overlays");
+ goto out;
+ }
+
+ if (bdrv_cow_child(state->new_bs)) {
error_setg(errp, "The overlay already has a backing image");
goto out;
}
/* We don't need (or want) to use the transactional
* bdrv_reopen_multiple() across all the entries at once, because we
* don't want to abort all of them if one of them fails the reopen */
- if (!atomic_read(&state->old_bs->copy_on_read)) {
+ if (!qatomic_read(&state->old_bs->copy_on_read)) {
bdrv_reopen_set_read_only(state->old_bs, true, NULL);
}
* on top of.
*/
if (backup->sync == MIRROR_SYNC_MODE_TOP) {
- source = backing_bs(bs);
+ /*
+ * Backup will not replace the source by the target, so none
+ * of the filters skipped here will be removed (in contrast to
+ * mirror). Therefore, we can skip all of them when looking
+ * for the first COW relationship.
+ */
+ source = bdrv_cow_bs(bdrv_skip_filters(bs));
if (!source) {
backup->sync = MIRROR_SYNC_MODE_FULL;
}
if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
assert(backup->format);
if (source) {
- bdrv_refresh_filename(source);
- bdrv_img_create(backup->target, backup->format, source->filename,
- source->drv->format_name, NULL,
+ /* Implicit filters should not appear in the filename */
+ BlockDriverState *explicit_backing =
+ bdrv_skip_implicit_filters(source);
+
+ bdrv_refresh_filename(explicit_backing);
+ bdrv_img_create(backup->target, backup->format,
+ explicit_backing->filename,
+ explicit_backing->drv->format_name, NULL,
size, flags, false, &local_err);
} else {
bdrv_img_create(backup->target, backup->format, NULL, NULL, NULL,
if (set_backing_hd) {
bdrv_set_backing_hd(target_bs, source, &local_err);
if (local_err) {
+ error_propagate(errp, local_err);
goto unref;
}
}
return ret;
}
-void qmp_block_resize(bool has_device, const char *device,
- bool has_node_name, const char *node_name,
- int64_t size, Error **errp)
+void coroutine_fn qmp_block_resize(bool has_device, const char *device,
+ bool has_node_name, const char *node_name,
+ int64_t size, Error **errp)
{
Error *local_err = NULL;
- BlockBackend *blk = NULL;
+ BlockBackend *blk;
BlockDriverState *bs;
- AioContext *aio_context;
+ AioContext *old_ctx;
bs = bdrv_lookup_bs(has_device ? device : NULL,
has_node_name ? node_name : NULL,
return;
}
- aio_context = bdrv_get_aio_context(bs);
- aio_context_acquire(aio_context);
-
if (size < 0) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "size", "a >0 size");
- goto out;
+ return;
}
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
error_setg(errp, QERR_DEVICE_IN_USE, device);
- goto out;
+ return;
}
blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
if (!blk) {
- goto out;
+ return;
}
+ bdrv_co_lock(bs);
bdrv_drained_begin(bs);
+ bdrv_co_unlock(bs);
+
+ old_ctx = bdrv_co_enter(bs);
blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
- bdrv_drained_end(bs);
+ bdrv_co_leave(bs, old_ctx);
-out:
+ bdrv_co_lock(bs);
+ bdrv_drained_end(bs);
blk_unref(blk);
- aio_context_release(aio_context);
+ bdrv_co_unlock(bs);
}
void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
bool has_backing_file, const char *backing_file,
bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
+ bool has_filter_node_name, const char *filter_node_name,
bool has_auto_finalize, bool auto_finalize,
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
BlockDriverState *base_bs = NULL;
AioContext *aio_context;
Error *local_err = NULL;
- const char *base_name = NULL;
int job_flags = JOB_DEFAULT;
if (!has_on_error) {
if (has_base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
- error_setg(errp, QERR_BASE_NOT_FOUND, base);
+ error_setg(errp, "Can't find '%s' in the backing chain", base);
goto out;
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
- base_name = base;
}
if (has_base_node) {
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
bdrv_refresh_filename(base_bs);
- base_name = base_bs->filename;
}
/* Check for op blockers in the whole chain between bs and base */
- for (iter = bs; iter && iter != base_bs; iter = backing_bs(iter)) {
+ for (iter = bs; iter && iter != base_bs;
+ iter = bdrv_filter_or_cow_bs(iter))
+ {
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
goto out;
}
goto out;
}
- /* backing_file string overrides base bs filename */
- base_name = has_backing_file ? backing_file : base_name;
-
if (has_auto_finalize && !auto_finalize) {
job_flags |= JOB_MANUAL_FINALIZE;
}
job_flags |= JOB_MANUAL_DISMISS;
}
- stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
- job_flags, has_speed ? speed : 0, on_error, &local_err);
+ stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
+ job_flags, has_speed ? speed : 0, on_error,
+ filter_node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
AioContext *aio_context;
Error *local_err = NULL;
int job_flags = JOB_DEFAULT;
+ uint64_t top_perm, top_shared;
if (!has_speed) {
speed = 0;
}
} else if (has_base && base) {
base_bs = bdrv_find_backing_image(top_bs, base);
+ if (base_bs == NULL) {
+ error_setg(errp, "Can't find '%s' in the backing chain", base);
+ goto out;
+ }
} else {
base_bs = bdrv_find_base(top_bs);
- }
-
- if (base_bs == NULL) {
- error_setg(errp, QERR_BASE_NOT_FOUND, base ? base : "NULL");
- goto out;
+ if (base_bs == NULL) {
+ error_setg(errp, "There is no backimg image");
+ goto out;
+ }
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
- for (iter = top_bs; iter != backing_bs(base_bs); iter = backing_bs(iter)) {
+ for (iter = top_bs; iter != bdrv_filter_or_cow_bs(base_bs);
+ iter = bdrv_filter_or_cow_bs(iter))
+ {
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
goto out;
}
goto out;
}
- if (top_bs == bs) {
+ /*
+ * Active commit is required if and only if someone has taken a
+ * WRITE permission on the top node. Historically, we have always
+ * used active commit for top nodes, so continue that practice
+ * lest we possibly break clients that rely on this behavior, e.g.
+ * to later attach this node to a writing parent.
+ * (Active commit is never really wrong.)
+ */
+ bdrv_get_cumulative_perm(top_bs, &top_perm, &top_shared);
+ if (top_perm & BLK_PERM_WRITE ||
+ bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
+ {
if (has_backing_file) {
- error_setg(errp, "'backing-file' specified,"
- " but 'top' is the active layer");
+ if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) {
+ error_setg(errp, "'backing-file' specified,"
+ " but 'top' is the active layer");
+ } else {
+ error_setg(errp, "'backing-file' specified, but 'top' has a "
+ "writer on it");
+ }
goto out;
}
- commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
- job_flags, speed, on_error,
+ if (!has_job_id) {
+ /*
+ * Emulate here what block_job_create() does, because it
+ * is possible that @bs != @top_bs (the block job should
+ * be named after @bs, even if @top_bs is the actual
+ * source)
+ */
+ job_id = bdrv_get_device_name(bs);
+ }
+ commit_active_start(job_id, top_bs, base_bs, job_flags, speed, on_error,
filter_node_name, NULL, NULL, false, &local_err);
} else {
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
+ BlockDriverState *unfiltered_bs;
int job_flags = JOB_DEFAULT;
if (!has_speed) {
}
if (granularity & (granularity - 1)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
- "power of 2");
+ "a power of 2");
return;
}
return;
}
- if (!bs->backing && sync == MIRROR_SYNC_MODE_TOP) {
+ if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) {
sync = MIRROR_SYNC_MODE_FULL;
}
+ if (!has_replaces) {
+ /* We want to mirror from @bs, but keep implicit filters on top */
+ unfiltered_bs = bdrv_skip_implicit_filters(bs);
+ if (unfiltered_bs != bs) {
+ replaces = unfiltered_bs->node_name;
+ has_replaces = true;
+ }
+ }
+
if (has_replaces) {
BlockDriverState *to_replace_bs;
AioContext *replace_aio_context;
void qmp_drive_mirror(DriveMirror *arg, Error **errp)
{
BlockDriverState *bs;
- BlockDriverState *source, *target_bs;
+ BlockDriverState *target_backing_bs, *target_bs;
AioContext *aio_context;
AioContext *old_context;
BlockMirrorBackingMode backing_mode;
}
flags = bs->open_flags | BDRV_O_RDWR;
- source = backing_bs(bs);
- if (!source && arg->sync == MIRROR_SYNC_MODE_TOP) {
+ target_backing_bs = bdrv_cow_bs(bdrv_skip_filters(bs));
+ if (!target_backing_bs && arg->sync == MIRROR_SYNC_MODE_TOP) {
arg->sync = MIRROR_SYNC_MODE_FULL;
}
if (arg->sync == MIRROR_SYNC_MODE_NONE) {
- source = bs;
+ target_backing_bs = bs;
}
size = bdrv_getlength(bs);
/* Don't open backing image in create() */
flags |= BDRV_O_NO_BACKING;
- if ((arg->sync == MIRROR_SYNC_MODE_FULL || !source)
+ if ((arg->sync == MIRROR_SYNC_MODE_FULL || !target_backing_bs)
&& arg->mode != NEW_IMAGE_MODE_EXISTING)
{
/* create new image w/o backing file */
bdrv_img_create(arg->target, format,
NULL, NULL, NULL, size, flags, false, &local_err);
} else {
+ /* Implicit filters should not appear in the filename */
+ BlockDriverState *explicit_backing =
+ bdrv_skip_implicit_filters(target_backing_bs);
+
switch (arg->mode) {
case NEW_IMAGE_MODE_EXISTING:
break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
/* create new image with backing file */
- bdrv_refresh_filename(source);
+ bdrv_refresh_filename(explicit_backing);
bdrv_img_create(arg->target, format,
- source->filename,
- source->drv->format_name,
+ explicit_backing->filename,
+ explicit_backing->drv->format_name,
NULL, size, flags, false, &local_err);
break;
default:
}
ret = bdrv_change_backing_file(image_bs, backing_file,
- image_bs->drv ? image_bs->drv->format_name : "");
+ image_bs->drv ? image_bs->drv->format_name : "",
+ false);
if (ret < 0) {
error_setg_errno(errp, -ret, "Could not change backing file to '%s'",