qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY,
read_only ? "on" : "off");
+ qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on");
assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
if (runstate_check(RUN_STATE_INMIGRATE)) {
},
};
-DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
+DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type,
+ Error **errp)
{
const char *value;
BlockBackend *blk;
qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to,
&local_err);
if (local_err) {
- error_report_err(local_err);
+ error_propagate(errp, local_err);
return NULL;
}
}
bool writethrough;
if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) {
- error_report("invalid cache option");
+ error_setg(errp, "invalid cache option");
return NULL;
}
&error_abort);
qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err);
if (local_err) {
- error_report_err(local_err);
+ error_propagate(errp, local_err);
goto fail;
}
media = MEDIA_CDROM;
read_only = true;
} else {
- error_report("'%s' invalid media", value);
+ error_setg(errp, "'%s' invalid media", value);
goto fail;
}
}
type++) {
}
if (type == IF_COUNT) {
- error_report("unsupported bus type '%s'", value);
+ error_setg(errp, "unsupported bus type '%s'", value);
goto fail;
}
} else {
if (index != -1) {
if (bus_id != 0 || unit_id != -1) {
- error_report("index cannot be used with bus and unit");
+ error_setg(errp, "index cannot be used with bus and unit");
goto fail;
}
bus_id = drive_index_to_bus_id(type, index);
}
if (max_devs && unit_id >= max_devs) {
- error_report("unit %d too big (max is %d)", unit_id, max_devs - 1);
+ error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1);
goto fail;
}
if (drive_get(type, bus_id, unit_id) != NULL) {
- error_report("drive with bus=%d, unit=%d (index=%d) exists",
- bus_id, unit_id, index);
+ error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists",
+ bus_id, unit_id, index);
goto fail;
}
if (werror != NULL) {
if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO &&
type != IF_NONE) {
- error_report("werror is not supported by this bus type");
+ error_setg(errp, "werror is not supported by this bus type");
goto fail;
}
qdict_put_str(bs_opts, "werror", werror);
if (rerror != NULL) {
if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI &&
type != IF_NONE) {
- error_report("rerror is not supported by this bus type");
+ error_setg(errp, "rerror is not supported by this bus type");
goto fail;
}
qdict_put_str(bs_opts, "rerror", rerror);
bs_opts = NULL;
if (!blk) {
if (local_err) {
- error_report_err(local_err);
+ error_propagate(errp, local_err);
}
goto fail;
} else {
}
options = qdict_new();
- if (s->has_snapshot_node_name) {
+ if (snapshot_node_name) {
qdict_put_str(options, "node-name", snapshot_node_name);
}
qdict_put_str(options, "driver", format);
return;
}
- if (bdrv_dirty_bitmap_frozen(state->bitmap)) {
- error_setg(errp, "Cannot modify a frozen bitmap");
- return;
- } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) {
- error_setg(errp, "Cannot modify a locked bitmap");
- return;
- } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) {
- error_setg(errp, "Cannot clear a disabled bitmap");
+ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
+ error_setg(errp, "Cannot modify a bitmap in use by another operation");
return;
} else if (bdrv_dirty_bitmap_readonly(state->bitmap)) {
error_setg(errp, "Cannot clear a readonly bitmap");
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
}
-static void block_dirty_bitmap_clear_abort(BlkActionState *common)
+static void block_dirty_bitmap_restore(BlkActionState *common)
{
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
if (state->backup) {
- bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup);
+ bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
}
}
-static void block_dirty_bitmap_clear_commit(BlkActionState *common)
+static void block_dirty_bitmap_free_backup(BlkActionState *common)
{
BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
common, common);
return;
}
+ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be enabled", action->name);
+ return;
+ }
+
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_enable_dirty_bitmap(state->bitmap);
}
return;
}
+ if (bdrv_dirty_bitmap_user_locked(state->bitmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be disabled", action->name);
+ return;
+ }
+
state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap);
bdrv_disable_dirty_bitmap(state->bitmap);
}
}
}
+static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
+ Error **errp)
+{
+ BlockDirtyBitmapMerge *action;
+ BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
+ common, common);
+ BdrvDirtyBitmap *merge_source;
+
+ if (action_check_completion_mode(common, errp) < 0) {
+ return;
+ }
+
+ action = common->action->u.x_block_dirty_bitmap_merge.data;
+ state->bitmap = block_dirty_bitmap_lookup(action->node,
+ action->dst_name,
+ &state->bs,
+ errp);
+ if (!state->bitmap) {
+ return;
+ }
+
+ merge_source = bdrv_find_dirty_bitmap(state->bs, action->src_name);
+ if (!merge_source) {
+ return;
+ }
+
+ bdrv_merge_dirty_bitmap(state->bitmap, merge_source, &state->backup, errp);
+}
+
static void abort_prepare(BlkActionState *common, Error **errp)
{
error_setg(errp, "Transaction aborted using Abort action");
[TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
.instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_clear_prepare,
- .commit = block_dirty_bitmap_clear_commit,
- .abort = block_dirty_bitmap_clear_abort,
+ .commit = block_dirty_bitmap_free_backup,
+ .abort = block_dirty_bitmap_restore,
},
[TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = {
.instance_size = sizeof(BlockDirtyBitmapState),
.instance_size = sizeof(BlockDirtyBitmapState),
.prepare = block_dirty_bitmap_disable_prepare,
.abort = block_dirty_bitmap_disable_abort,
- }
+ },
+ [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_MERGE] = {
+ .instance_size = sizeof(BlockDirtyBitmapState),
+ .prepare = block_dirty_bitmap_merge_prepare,
+ .commit = block_dirty_bitmap_free_backup,
+ .abort = block_dirty_bitmap_restore,
+ },
+ /* Where are transactions for MIRROR, COMMIT and STREAM?
+ * Although these blockjobs use transaction callbacks like the backup job,
+ * these jobs do not necessarily adhere to transaction semantics.
+ * These jobs may not fully undo all of their actions on abort, nor do they
+ * necessarily work in transactions with more than one job in them.
+ */
};
/**
bdrv_flags = blk_get_open_flags_from_root_state(blk);
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
- BDRV_O_PROTOCOL);
+ BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
if (!has_read_only) {
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
return;
}
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
+ if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp,
- "Bitmap '%s' is currently frozen and cannot be removed",
- name);
- return;
- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
- error_setg(errp,
- "Bitmap '%s' is currently locked and cannot be removed",
- name);
+ "Bitmap '%s' is currently in use by another operation and"
+ " cannot be removed", name);
return;
}
return;
}
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
- error_setg(errp,
- "Bitmap '%s' is currently frozen and cannot be modified",
- name);
- return;
- } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) {
- error_setg(errp,
- "Bitmap '%s' is currently locked and cannot be modified",
- name);
- return;
- } else if (!bdrv_dirty_bitmap_enabled(bitmap)) {
+ if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp,
- "Bitmap '%s' is currently disabled and cannot be cleared",
- name);
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be cleared", name);
return;
} else if (bdrv_dirty_bitmap_readonly(bitmap)) {
error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name);
return;
}
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
+ if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp,
- "Bitmap '%s' is currently frozen and cannot be enabled",
- name);
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be enabled", name);
return;
}
return;
}
- if (bdrv_dirty_bitmap_frozen(bitmap)) {
+ if (bdrv_dirty_bitmap_user_locked(bitmap)) {
error_setg(errp,
- "Bitmap '%s' is currently frozen and cannot be disabled",
- name);
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be disabled", name);
return;
}
return;
}
- if (bdrv_dirty_bitmap_frozen(dst)) {
- error_setg(errp, "Bitmap '%s' is frozen and cannot be modified",
- dst_name);
- return;
- } else if (bdrv_dirty_bitmap_readonly(dst)) {
- error_setg(errp, "Bitmap '%s' is readonly and cannot be modified",
- dst_name);
- return;
- }
-
src = bdrv_find_dirty_bitmap(bs, src_name);
if (!src) {
error_setg(errp, "Dirty bitmap '%s' not found", src_name);
return;
}
- bdrv_merge_dirty_bitmap(dst, src, errp);
+ bdrv_merge_dirty_bitmap(dst, src, NULL, errp);
}
BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node,
bool has_backing_file, const char *backing_file,
bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
+ bool has_auto_finalize, bool auto_finalize,
+ bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
BlockDriverState *bs, *iter;
AioContext *aio_context;
Error *local_err = NULL;
const char *base_name = NULL;
+ int job_flags = JOB_DEFAULT;
if (!has_on_error) {
on_error = BLOCKDEV_ON_ERROR_REPORT;
/* 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;
+ }
+ if (has_auto_dismiss && !auto_dismiss) {
+ job_flags |= JOB_MANUAL_DISMISS;
+ }
+
stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name,
- has_speed ? speed : 0, on_error, &local_err);
+ job_flags, has_speed ? speed : 0, on_error, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
+ bool has_base_node, const char *base_node,
bool has_base, const char *base,
+ bool has_top_node, const char *top_node,
bool has_top, const char *top,
bool has_backing_file, const char *backing_file,
bool has_speed, int64_t speed,
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 *bs;
* BlockdevOnError change for blkmirror makes it in
*/
BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT;
+ int job_flags = JOB_DEFAULT;
if (!has_speed) {
speed = 0;
if (!has_filter_node_name) {
filter_node_name = NULL;
}
+ if (has_auto_finalize && !auto_finalize) {
+ job_flags |= JOB_MANUAL_FINALIZE;
+ }
+ if (has_auto_dismiss && !auto_dismiss) {
+ job_flags |= JOB_MANUAL_DISMISS;
+ }
/* Important Note:
* libvirt relies on the DeviceNotFound error class in order to probe for
/* default top_bs is the active layer */
top_bs = bs;
- if (has_top && top) {
+ if (has_top_node && has_top) {
+ error_setg(errp, "'top-node' and 'top' are mutually exclusive");
+ goto out;
+ } else if (has_top_node) {
+ top_bs = bdrv_lookup_bs(NULL, top_node, errp);
+ if (top_bs == NULL) {
+ goto out;
+ }
+ if (!bdrv_chain_contains(bs, top_bs)) {
+ error_setg(errp, "'%s' is not in this backing file chain",
+ top_node);
+ goto out;
+ }
+ } else if (has_top && top) {
if (strcmp(bs->filename, top) != 0) {
top_bs = bdrv_find_backing_image(bs, top);
}
assert(bdrv_get_aio_context(top_bs) == aio_context);
- if (has_base && base) {
+ if (has_base_node && has_base) {
+ error_setg(errp, "'base-node' and 'base' are mutually exclusive");
+ goto out;
+ } else if (has_base_node) {
+ base_bs = bdrv_lookup_bs(NULL, base_node, errp);
+ if (base_bs == NULL) {
+ goto out;
+ }
+ if (!bdrv_chain_contains(top_bs, base_bs)) {
+ error_setg(errp, "'%s' is not in this backing file chain",
+ base_node);
+ goto out;
+ }
+ } else if (has_base && base) {
base_bs = bdrv_find_backing_image(top_bs, base);
} else {
base_bs = bdrv_find_base(top_bs);
goto out;
}
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
- JOB_DEFAULT, speed, on_error,
+ job_flags, speed, on_error,
filter_node_name, NULL, NULL, false, &local_err);
} else {
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) {
goto out;
}
- commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed,
- on_error, has_backing_file ? backing_file : NULL,
+ commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags,
+ speed, on_error, has_backing_file ? backing_file : NULL,
filter_node_name, &local_err);
}
if (local_err != NULL) {
bdrv_unref(target_bs);
goto out;
}
- if (bdrv_dirty_bitmap_qmp_locked(bmap)) {
+ if (bdrv_dirty_bitmap_user_locked(bmap)) {
error_setg(errp,
- "Bitmap '%s' is currently locked and cannot be used for "
- "backup", backup->bitmap);
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be used for backup", backup->bitmap);
goto out;
}
}
BlockDriverState *bs;
BlockDriverState *target_bs;
Error *local_err = NULL;
+ BdrvDirtyBitmap *bmap = NULL;
AioContext *aio_context;
BlockJob *job = NULL;
int job_flags = JOB_DEFAULT;
goto out;
}
}
+
+ if (backup->has_bitmap) {
+ bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
+ if (!bmap) {
+ error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
+ goto out;
+ }
+ if (bdrv_dirty_bitmap_user_locked(bmap)) {
+ error_setg(errp,
+ "Bitmap '%s' is currently in use by another operation"
+ " and cannot be used for backup", backup->bitmap);
+ goto out;
+ }
+ }
+
if (!backup->auto_finalize) {
job_flags |= JOB_MANUAL_FINALIZE;
}
job_flags |= JOB_MANUAL_DISMISS;
}
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
- backup->sync, NULL, backup->compress,
+ backup->sync, bmap, backup->compress,
backup->on_source_error, backup->on_target_error,
job_flags, NULL, NULL, txn, &local_err);
if (local_err != NULL) {
bool has_filter_node_name,
const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
+ bool has_auto_finalize, bool auto_finalize,
+ bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
+ int job_flags = JOB_DEFAULT;
if (!has_speed) {
speed = 0;
if (!has_copy_mode) {
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
}
+ if (has_auto_finalize && !auto_finalize) {
+ job_flags |= JOB_MANUAL_FINALIZE;
+ }
+ if (has_auto_dismiss && !auto_dismiss) {
+ job_flags |= JOB_MANUAL_DISMISS;
+ }
if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) {
error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity",
* and will allow to check whether the node still exist at mirror completion
*/
mirror_start(job_id, bs, target,
- has_replaces ? replaces : NULL,
+ has_replaces ? replaces : NULL, job_flags,
speed, granularity, buf_size, sync, backing_mode,
on_source_error, on_target_error, unmap, filter_node_name,
copy_mode, errp);
arg->has_unmap, arg->unmap,
false, NULL,
arg->has_copy_mode, arg->copy_mode,
+ arg->has_auto_finalize, arg->auto_finalize,
+ arg->has_auto_dismiss, arg->auto_dismiss,
&local_err);
bdrv_unref(target_bs);
error_propagate(errp, local_err);
bool has_filter_node_name,
const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
+ bool has_auto_finalize, bool auto_finalize,
+ bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
{
BlockDriverState *bs;
true, true,
has_filter_node_name, filter_node_name,
has_copy_mode, copy_mode,
+ has_auto_finalize, auto_finalize,
+ has_auto_dismiss, auto_dismiss,
&local_err);
error_propagate(errp, local_err);
{
BlockBackend *blk = blk_by_name(device);
BlockAcctStats *stats;
+ int ret;
if (!blk) {
error_setg(errp, "Device '%s' not found", device);
}
if (has_boundaries || has_boundaries_read) {
- block_latency_histogram_set(
+ ret = block_latency_histogram_set(
stats, BLOCK_ACCT_READ,
has_boundaries_read ? boundaries_read : boundaries);
+ if (ret) {
+ error_setg(errp, "Device '%s' set read boundaries fail", device);
+ return;
+ }
}
if (has_boundaries || has_boundaries_write) {
- block_latency_histogram_set(
+ ret = block_latency_histogram_set(
stats, BLOCK_ACCT_WRITE,
has_boundaries_write ? boundaries_write : boundaries);
+ if (ret) {
+ error_setg(errp, "Device '%s' set write boundaries fail", device);
+ return;
+ }
}
if (has_boundaries || has_boundaries_flush) {
- block_latency_histogram_set(
+ ret = block_latency_histogram_set(
stats, BLOCK_ACCT_FLUSH,
has_boundaries_flush ? boundaries_flush : boundaries);
+ if (ret) {
+ error_setg(errp, "Device '%s' set flush boundaries fail", device);
+ return;
+ }
}
}