#include "sysemu/blockdev.h"
#include "hw/block/block.h"
#include "block/blockjob.h"
+#include "block/dirty-bitmap.h"
#include "block/qdict.h"
#include "block/throttle-groups.h"
#include "monitor/monitor.h"
JOB_LOCK_GUARD();
- for (job = block_job_next_locked(NULL); job;
- job = block_job_next_locked(job)) {
- if (block_job_has_bdrv(job, blk_bs(blk))) {
+ do {
+ job = block_job_next_locked(NULL);
+ while (job && (job->job.cancelled ||
+ job->job.deferred_to_main_loop ||
+ !block_job_has_bdrv(job, blk_bs(blk))))
+ {
+ job = block_job_next_locked(job);
+ }
+ if (job) {
+ /*
+ * This drops the job lock temporarily and polls, so we need to
+ * restart processing the list from the start after this.
+ */
job_cancel_locked(&job->job, false);
}
- }
+ } while (job);
dinfo->auto_del = 1;
}
* Ignore default drives, because we create certain default
* drives unconditionally, then leave them unclaimed. Not the
* users fault.
- * Ignore IF_VIRTIO, because it gets desugared into -device,
- * so we can leave failing to -device.
+ * Ignore IF_VIRTIO or IF_XEN, because it gets desugared into
+ * -device, so we can leave failing to -device.
* Ignore IF_NONE, because leaving unclaimed IF_NONE remains
* available for device_add is a feature.
*/
if (dinfo->is_default || dinfo->type == IF_VIRTIO
- || dinfo->type == IF_NONE) {
+ || dinfo->type == IF_XEN || dinfo->type == IF_NONE) {
continue;
}
if (!blk_get_attached_dev(blk)) {
switch (qobject_type(entry->value)) {
case QTYPE_QSTRING: {
- unsigned long long length;
+ uint64_t length;
const char *str = qstring_get_str(qobject_to(QString,
entry->value));
- if (parse_uint_full(str, &length, 10) == 0 &&
+ if (parse_uint_full(str, 10, &length) == 0 &&
length > 0 && length <= UINT_MAX) {
block_acct_add_interval(stats, (unsigned) length);
} else {
/* Takes the ownership of bs_opts */
BlockDriverState *bds_tree_init(QDict *bs_opts, Error **errp)
{
+ BlockDriverState *bs;
int bdrv_flags = 0;
GLOBAL_STATE_CODE();
bdrv_flags |= BDRV_O_INACTIVE;
}
- return bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
+ aio_context_acquire(qemu_get_aio_context());
+ bs = bdrv_open(NULL, NULL, bs_opts, bdrv_flags, errp);
+ aio_context_release(qemu_get_aio_context());
+
+ return bs;
}
void blockdev_close_all_bdrv_states(void)
qemu_opt_set(devopts, "driver", "virtio-blk", &error_abort);
qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
&error_abort);
+ } else if (type == IF_XEN) {
+ QemuOpts *devopts;
+ devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0,
+ &error_abort);
+ qemu_opt_set(devopts, "driver",
+ (media == MEDIA_CDROM) ? "xen-cdrom" : "xen-disk",
+ &error_abort);
+ qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"),
+ &error_abort);
}
filename = qemu_opt_get(legacy_opts, "file");
static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
{
BlockDriverState *bs;
+ AioContext *aio_context;
+
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
bs = bdrv_lookup_bs(name, name, errp);
if (bs == NULL) {
return NULL;
}
+ aio_context = bdrv_get_aio_context(bs);
+ aio_context_acquire(aio_context);
+
if (!bdrv_is_inserted(bs)) {
error_setg(errp, "Device has no medium");
- return NULL;
+ bs = NULL;
}
+ aio_context_release(aio_context);
+
return bs;
}
list.value = action;
list.next = NULL;
- qmp_transaction(&list, false, NULL, errp);
+ qmp_transaction(&list, NULL, errp);
}
-void qmp_blockdev_snapshot_sync(bool has_device, const char *device,
- bool has_node_name, const char *node_name,
+void qmp_blockdev_snapshot_sync(const char *device, const char *node_name,
const char *snapshot_file,
- bool has_snapshot_node_name,
const char *snapshot_node_name,
- bool has_format, const char *format,
+ const char *format,
bool has_mode, NewImageMode mode, Error **errp)
{
BlockdevSnapshotSync snapshot = {
- .has_device = has_device,
.device = (char *) device,
- .has_node_name = has_node_name,
.node_name = (char *) node_name,
.snapshot_file = (char *) snapshot_file,
- .has_snapshot_node_name = has_snapshot_node_name,
.snapshot_node_name = (char *) snapshot_node_name,
- .has_format = has_format,
.format = (char *) format,
.has_mode = has_mode,
.mode = mode,
}
SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
- bool has_id,
const char *id,
- bool has_name,
const char *name,
Error **errp)
{
SnapshotInfo *info = NULL;
int ret;
+ GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
bs = qmp_get_root_bs(device, errp);
if (!bs) {
return NULL;
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (!has_id) {
- id = NULL;
- }
-
- if (!has_name) {
- name = NULL;
- }
-
if (!id && !name) {
error_setg(errp, "Name or id must be provided");
goto out_aio_context;
return NULL;
}
-/* New and old BlockDriverState structs for atomic group operations */
-
-typedef struct BlkActionState BlkActionState;
-
-/**
- * BlkActionOps:
- * Table of operations that define an Action.
- *
- * @instance_size: Size of state struct, in bytes.
- * @prepare: Prepare the work, must NOT be NULL.
- * @commit: Commit the changes, can be NULL.
- * @abort: Abort the changes on fail, can be NULL.
- * @clean: Clean up resources after all transaction actions have called
- * commit() or abort(). Can be NULL.
- *
- * Only prepare() may fail. In a single transaction, only one of commit() or
- * abort() will be called. clean() will always be called if it is present.
- *
- * Always run under BQL.
- */
-typedef struct BlkActionOps {
- size_t instance_size;
- void (*prepare)(BlkActionState *common, Error **errp);
- void (*commit)(BlkActionState *common);
- void (*abort)(BlkActionState *common);
- void (*clean)(BlkActionState *common);
-} BlkActionOps;
-
-/**
- * BlkActionState:
- * Describes one Action's state within a Transaction.
- *
- * @action: QAPI-defined enum identifying which Action to perform.
- * @ops: Table of ActionOps this Action can perform.
- * @block_job_txn: Transaction which this action belongs to.
- * @entry: List membership for all Actions in this Transaction.
- *
- * This structure must be arranged as first member in a subclassed type,
- * assuming that the compiler will also arrange it to the same offsets as the
- * base class.
- */
-struct BlkActionState {
- TransactionAction *action;
- const BlkActionOps *ops;
- JobTxn *block_job_txn;
- TransactionProperties *txn_props;
- QTAILQ_ENTRY(BlkActionState) entry;
-};
-
/* internal snapshot private data */
typedef struct InternalSnapshotState {
- BlkActionState common;
BlockDriverState *bs;
QEMUSnapshotInfo sn;
bool created;
} InternalSnapshotState;
+static void internal_snapshot_abort(void *opaque);
+static void internal_snapshot_clean(void *opaque);
+TransactionActionDrv internal_snapshot_drv = {
+ .abort = internal_snapshot_abort,
+ .clean = internal_snapshot_clean,
+};
-static int action_check_completion_mode(BlkActionState *s, Error **errp)
-{
- if (s->txn_props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
- error_setg(errp,
- "Action '%s' does not support Transaction property "
- "completion-mode = %s",
- TransactionActionKind_str(s->action->type),
- ActionCompletionMode_str(s->txn_props->completion_mode));
- return -1;
- }
- return 0;
-}
-
-static void internal_snapshot_prepare(BlkActionState *common,
- Error **errp)
+static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
+ Transaction *tran, Error **errp)
{
Error *local_err = NULL;
const char *device;
QEMUSnapshotInfo old_sn, *sn;
bool ret;
int64_t rt;
- BlockdevSnapshotInternal *internal;
- InternalSnapshotState *state;
+ InternalSnapshotState *state = g_new0(InternalSnapshotState, 1);
AioContext *aio_context;
int ret1;
- g_assert(common->action->type ==
- TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC);
- internal = common->action->u.blockdev_snapshot_internal_sync.data;
- state = DO_UPCAST(InternalSnapshotState, common, common);
+ GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ tran_add(tran, &internal_snapshot_drv, state);
- /* 1. parse input */
device = internal->device;
name = internal->name;
- /* 2. check for validation */
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
-
bs = qmp_get_root_bs(device, errp);
if (!bs) {
return;
aio_context_release(aio_context);
}
-static void internal_snapshot_abort(BlkActionState *common)
+static void internal_snapshot_abort(void *opaque)
{
- InternalSnapshotState *state =
- DO_UPCAST(InternalSnapshotState, common, common);
+ InternalSnapshotState *state = opaque;
BlockDriverState *bs = state->bs;
QEMUSnapshotInfo *sn = &state->sn;
AioContext *aio_context;
Error *local_error = NULL;
+ GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
if (!state->created) {
return;
}
aio_context_release(aio_context);
}
-static void internal_snapshot_clean(BlkActionState *common)
+static void internal_snapshot_clean(void *opaque)
{
- InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
- common, common);
+ g_autofree InternalSnapshotState *state = opaque;
AioContext *aio_context;
if (!state->bs) {
/* external snapshot private data */
typedef struct ExternalSnapshotState {
- BlkActionState common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
bool overlay_appended;
} ExternalSnapshotState;
-static void external_snapshot_prepare(BlkActionState *common,
- Error **errp)
+static void external_snapshot_commit(void *opaque);
+static void external_snapshot_abort(void *opaque);
+static void external_snapshot_clean(void *opaque);
+TransactionActionDrv external_snapshot_drv = {
+ .commit = external_snapshot_commit,
+ .abort = external_snapshot_abort,
+ .clean = external_snapshot_clean,
+};
+
+static void external_snapshot_action(TransactionAction *action,
+ Transaction *tran, Error **errp)
{
int ret;
int flags = 0;
const char *snapshot_ref;
/* File name of the new image (for 'blockdev-snapshot-sync') */
const char *new_image_file;
- ExternalSnapshotState *state =
- DO_UPCAST(ExternalSnapshotState, common, common);
- TransactionAction *action = common->action;
+ ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1);
AioContext *aio_context;
uint64_t perm, shared;
+ /* TODO We'll eventually have to take a writer lock in this function */
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
+ tran_add(tran, &external_snapshot_drv, state);
+
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
* purpose but a different set of parameters */
switch (action->type) {
case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
{
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
- device = s->has_device ? s->device : NULL;
- node_name = s->has_node_name ? s->node_name : NULL;
+ device = s->device;
+ node_name = s->node_name;
new_image_file = s->snapshot_file;
snapshot_ref = NULL;
}
}
/* start processing */
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
state->old_bs = bdrv_lookup_bs(device, node_name, errp);
if (!state->old_bs) {
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
BlockdevSnapshotSync *s = action->u.blockdev_snapshot_sync.data;
- const char *format = s->has_format ? s->format : "qcow2";
+ const char *format = s->format ?: "qcow2";
enum NewImageMode mode;
- const char *snapshot_node_name =
- s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
+ const char *snapshot_node_name = s->snapshot_node_name;
if (node_name && !snapshot_node_name) {
error_setg(errp, "New overlay node-name missing");
goto out;
}
bdrv_refresh_filename(state->old_bs);
+
+ aio_context_release(aio_context);
bdrv_img_create(new_image_file, format,
state->old_bs->filename,
state->old_bs->drv->format_name,
NULL, size, flags, false, &local_err);
+ aio_context_acquire(aio_context);
+
if (local_err) {
error_propagate(errp, local_err);
goto out;
}
qdict_put_str(options, "driver", format);
}
+ aio_context_release(aio_context);
+ aio_context_acquire(qemu_get_aio_context());
state->new_bs = bdrv_open(new_image_file, snapshot_ref, options, flags,
errp);
+ aio_context_release(qemu_get_aio_context());
+
/* We will manually add the backing_hd field to the bs later */
if (!state->new_bs) {
- goto out;
+ return;
}
+ aio_context_acquire(aio_context);
+
/*
* Allow attaching a backing file to an overlay that's already in use only
* if the parents don't assume that they are already seeing a valid image.
aio_context_release(aio_context);
}
-static void external_snapshot_commit(BlkActionState *common)
+static void external_snapshot_commit(void *opaque)
{
- ExternalSnapshotState *state =
- DO_UPCAST(ExternalSnapshotState, common, common);
+ ExternalSnapshotState *state = opaque;
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->old_bs);
aio_context_release(aio_context);
}
-static void external_snapshot_abort(BlkActionState *common)
+static void external_snapshot_abort(void *opaque)
{
- ExternalSnapshotState *state =
- DO_UPCAST(ExternalSnapshotState, common, common);
+ ExternalSnapshotState *state = opaque;
if (state->new_bs) {
if (state->overlay_appended) {
AioContext *aio_context;
aio_context_acquire(aio_context);
}
+ bdrv_drained_begin(state->new_bs);
+ bdrv_graph_wrlock(state->old_bs);
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
+ bdrv_graph_wrunlock(state->old_bs);
+ bdrv_drained_end(state->new_bs);
+
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
aio_context_release(aio_context);
}
}
-static void external_snapshot_clean(BlkActionState *common)
+static void external_snapshot_clean(void *opaque)
{
- ExternalSnapshotState *state =
- DO_UPCAST(ExternalSnapshotState, common, common);
+ g_autofree ExternalSnapshotState *state = opaque;
AioContext *aio_context;
if (!state->old_bs) {
}
typedef struct DriveBackupState {
- BlkActionState common;
BlockDriverState *bs;
BlockJob *job;
} DriveBackupState;
AioContext *aio_context,
JobTxn *txn, Error **errp);
-static void drive_backup_prepare(BlkActionState *common, Error **errp)
+static void drive_backup_commit(void *opaque);
+static void drive_backup_abort(void *opaque);
+static void drive_backup_clean(void *opaque);
+TransactionActionDrv drive_backup_drv = {
+ .commit = drive_backup_commit,
+ .abort = drive_backup_abort,
+ .clean = drive_backup_clean,
+};
+
+static void drive_backup_action(DriveBackup *backup,
+ JobTxn *block_job_txn,
+ Transaction *tran, Error **errp)
{
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
- DriveBackup *backup;
+ DriveBackupState *state = g_new0(DriveBackupState, 1);
BlockDriverState *bs;
BlockDriverState *target_bs;
BlockDriverState *source = NULL;
AioContext *aio_context;
AioContext *old_context;
+ const char *format;
QDict *options;
Error *local_err = NULL;
int flags;
bool set_backing_hd = false;
int ret;
- assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
- backup = common->action->u.drive_backup.data;
+ GLOBAL_STATE_CODE();
+
+ tran_add(tran, &drive_backup_drv, state);
if (!backup->has_mode) {
backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
/* Paired with .clean() */
bdrv_drained_begin(bs);
- if (!backup->has_format) {
- backup->format = backup->mode == NEW_IMAGE_MODE_EXISTING ?
- NULL : (char *) bs->drv->format_name;
+ format = backup->format;
+ if (!format && backup->mode != NEW_IMAGE_MODE_EXISTING) {
+ format = bs->drv->format_name;
}
/* Early check to avoid creating target */
+ bdrv_graph_rdlock_main_loop();
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_BACKUP_SOURCE, errp)) {
+ bdrv_graph_rdunlock_main_loop();
goto out;
}
flags |= BDRV_O_NO_BACKING;
set_backing_hd = true;
}
+ bdrv_graph_rdunlock_main_loop();
size = bdrv_getlength(bs);
if (size < 0) {
}
if (backup->mode != NEW_IMAGE_MODE_EXISTING) {
- assert(backup->format);
+ assert(format);
if (source) {
/* Implicit filters should not appear in the filename */
- BlockDriverState *explicit_backing =
- bdrv_skip_implicit_filters(source);
+ BlockDriverState *explicit_backing;
+ bdrv_graph_rdlock_main_loop();
+ explicit_backing = bdrv_skip_implicit_filters(source);
bdrv_refresh_filename(explicit_backing);
- bdrv_img_create(backup->target, backup->format,
+ bdrv_graph_rdunlock_main_loop();
+
+ bdrv_img_create(backup->target, 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,
+ bdrv_img_create(backup->target, format, NULL, NULL, NULL,
size, flags, false, &local_err);
}
}
options = qdict_new();
qdict_put_str(options, "discard", "unmap");
qdict_put_str(options, "detect-zeroes", "unmap");
- if (backup->format) {
- qdict_put_str(options, "driver", backup->format);
+ if (format) {
+ qdict_put_str(options, "driver", format);
}
+ aio_context_release(aio_context);
+ aio_context_acquire(qemu_get_aio_context());
target_bs = bdrv_open(backup->target, NULL, options, flags, errp);
+ aio_context_release(qemu_get_aio_context());
+
if (!target_bs) {
- goto out;
+ return;
}
/* Honor bdrv_try_change_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(target_bs);
- aio_context_release(aio_context);
aio_context_acquire(old_context);
ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
state->job = do_backup_common(qapi_DriveBackup_base(backup),
bs, target_bs, aio_context,
- common->block_job_txn, errp);
+ block_job_txn, errp);
unref:
bdrv_unref(target_bs);
aio_context_release(aio_context);
}
-static void drive_backup_commit(BlkActionState *common)
+static void drive_backup_commit(void *opaque)
{
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ DriveBackupState *state = opaque;
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_release(aio_context);
}
-static void drive_backup_abort(BlkActionState *common)
+static void drive_backup_abort(void *opaque)
{
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ DriveBackupState *state = opaque;
if (state->job) {
job_cancel_sync(&state->job->job, true);
}
}
-static void drive_backup_clean(BlkActionState *common)
+static void drive_backup_clean(void *opaque)
{
- DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
+ g_autofree DriveBackupState *state = opaque;
AioContext *aio_context;
if (!state->bs) {
}
typedef struct BlockdevBackupState {
- BlkActionState common;
BlockDriverState *bs;
BlockJob *job;
} BlockdevBackupState;
-static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
+static void blockdev_backup_commit(void *opaque);
+static void blockdev_backup_abort(void *opaque);
+static void blockdev_backup_clean(void *opaque);
+TransactionActionDrv blockdev_backup_drv = {
+ .commit = blockdev_backup_commit,
+ .abort = blockdev_backup_abort,
+ .clean = blockdev_backup_clean,
+};
+
+static void blockdev_backup_action(BlockdevBackup *backup,
+ JobTxn *block_job_txn,
+ Transaction *tran, Error **errp)
{
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
- BlockdevBackup *backup;
+ BlockdevBackupState *state = g_new0(BlockdevBackupState, 1);
BlockDriverState *bs;
BlockDriverState *target_bs;
AioContext *aio_context;
AioContext *old_context;
int ret;
- assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
- backup = common->action->u.blockdev_backup.data;
+ tran_add(tran, &blockdev_backup_drv, state);
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) {
state->job = do_backup_common(qapi_BlockdevBackup_base(backup),
bs, target_bs, aio_context,
- common->block_job_txn, errp);
+ block_job_txn, errp);
aio_context_release(aio_context);
}
-static void blockdev_backup_commit(BlkActionState *common)
+static void blockdev_backup_commit(void *opaque)
{
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ BlockdevBackupState *state = opaque;
AioContext *aio_context;
aio_context = bdrv_get_aio_context(state->bs);
aio_context_release(aio_context);
}
-static void blockdev_backup_abort(BlkActionState *common)
+static void blockdev_backup_abort(void *opaque)
{
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ BlockdevBackupState *state = opaque;
if (state->job) {
job_cancel_sync(&state->job->job, true);
}
}
-static void blockdev_backup_clean(BlkActionState *common)
+static void blockdev_backup_clean(void *opaque)
{
- BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
+ g_autofree BlockdevBackupState *state = opaque;
AioContext *aio_context;
if (!state->bs) {
}
typedef struct BlockDirtyBitmapState {
- BlkActionState common;
BdrvDirtyBitmap *bitmap;
BlockDriverState *bs;
HBitmap *backup;
- bool prepared;
bool was_enabled;
} BlockDirtyBitmapState;
-static void block_dirty_bitmap_add_prepare(BlkActionState *common,
- Error **errp)
+static void block_dirty_bitmap_add_abort(void *opaque);
+TransactionActionDrv block_dirty_bitmap_add_drv = {
+ .abort = block_dirty_bitmap_add_abort,
+ .clean = g_free,
+};
+
+static void block_dirty_bitmap_add_action(BlockDirtyBitmapAdd *action,
+ Transaction *tran, Error **errp)
{
Error *local_err = NULL;
- BlockDirtyBitmapAdd *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+ tran_add(tran, &block_dirty_bitmap_add_drv, state);
- action = common->action->u.block_dirty_bitmap_add.data;
/* AIO context taken and released within qmp_block_dirty_bitmap_add */
qmp_block_dirty_bitmap_add(action->node, action->name,
action->has_granularity, action->granularity,
&local_err);
if (!local_err) {
- state->prepared = true;
+ state->bitmap = block_dirty_bitmap_lookup(action->node, action->name,
+ NULL, &error_abort);
} else {
error_propagate(errp, local_err);
}
}
-static void block_dirty_bitmap_add_abort(BlkActionState *common)
+static void block_dirty_bitmap_add_abort(void *opaque)
{
- BlockDirtyBitmapAdd *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
- action = common->action->u.block_dirty_bitmap_add.data;
- /* Should not be able to fail: IF the bitmap was added via .prepare(),
- * then the node reference and bitmap name must have been valid.
- */
- if (state->prepared) {
- qmp_block_dirty_bitmap_remove(action->node, action->name, &error_abort);
+ if (state->bitmap) {
+ bdrv_release_dirty_bitmap(state->bitmap);
}
}
-static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
- Error **errp)
+static void block_dirty_bitmap_restore(void *opaque);
+static void block_dirty_bitmap_free_backup(void *opaque);
+TransactionActionDrv block_dirty_bitmap_clear_drv = {
+ .abort = block_dirty_bitmap_restore,
+ .commit = block_dirty_bitmap_free_backup,
+ .clean = g_free,
+};
+
+static void block_dirty_bitmap_clear_action(BlockDirtyBitmap *action,
+ Transaction *tran, Error **errp)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
- BlockDirtyBitmap *action;
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+ tran_add(tran, &block_dirty_bitmap_clear_drv, state);
- action = common->action->u.block_dirty_bitmap_clear.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->name,
&state->bs,
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
}
-static void block_dirty_bitmap_restore(BlkActionState *common)
+static void block_dirty_bitmap_restore(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
if (state->backup) {
bdrv_restore_dirty_bitmap(state->bitmap, state->backup);
}
}
-static void block_dirty_bitmap_free_backup(BlkActionState *common)
+static void block_dirty_bitmap_free_backup(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
hbitmap_free(state->backup);
}
-static void block_dirty_bitmap_enable_prepare(BlkActionState *common,
- Error **errp)
+static void block_dirty_bitmap_enable_abort(void *opaque);
+TransactionActionDrv block_dirty_bitmap_enable_drv = {
+ .abort = block_dirty_bitmap_enable_abort,
+ .clean = g_free,
+};
+
+static void block_dirty_bitmap_enable_action(BlockDirtyBitmap *action,
+ Transaction *tran, Error **errp)
{
- BlockDirtyBitmap *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+ tran_add(tran, &block_dirty_bitmap_enable_drv, state);
- action = common->action->u.block_dirty_bitmap_enable.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->name,
NULL,
bdrv_enable_dirty_bitmap(state->bitmap);
}
-static void block_dirty_bitmap_enable_abort(BlkActionState *common)
+static void block_dirty_bitmap_enable_abort(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
if (!state->was_enabled) {
bdrv_disable_dirty_bitmap(state->bitmap);
}
}
-static void block_dirty_bitmap_disable_prepare(BlkActionState *common,
- Error **errp)
+static void block_dirty_bitmap_disable_abort(void *opaque);
+TransactionActionDrv block_dirty_bitmap_disable_drv = {
+ .abort = block_dirty_bitmap_disable_abort,
+ .clean = g_free,
+};
+
+static void block_dirty_bitmap_disable_action(BlockDirtyBitmap *action,
+ Transaction *tran, Error **errp)
{
- BlockDirtyBitmap *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+ tran_add(tran, &block_dirty_bitmap_disable_drv, state);
- action = common->action->u.block_dirty_bitmap_disable.data;
state->bitmap = block_dirty_bitmap_lookup(action->node,
action->name,
NULL,
bdrv_disable_dirty_bitmap(state->bitmap);
}
-static void block_dirty_bitmap_disable_abort(BlkActionState *common)
+static void block_dirty_bitmap_disable_abort(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
if (state->was_enabled) {
bdrv_enable_dirty_bitmap(state->bitmap);
}
}
-static void block_dirty_bitmap_merge_prepare(BlkActionState *common,
- Error **errp)
-{
- BlockDirtyBitmapMerge *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+TransactionActionDrv block_dirty_bitmap_merge_drv = {
+ .commit = block_dirty_bitmap_free_backup,
+ .abort = block_dirty_bitmap_restore,
+ .clean = g_free,
+};
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action,
+ Transaction *tran, Error **errp)
+{
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- action = common->action->u.block_dirty_bitmap_merge.data;
+ tran_add(tran, &block_dirty_bitmap_merge_drv, state);
state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
action->bitmaps, &state->backup,
errp);
}
-static void block_dirty_bitmap_remove_prepare(BlkActionState *common,
- Error **errp)
+static void block_dirty_bitmap_remove_commit(void *opaque);
+static void block_dirty_bitmap_remove_abort(void *opaque);
+TransactionActionDrv block_dirty_bitmap_remove_drv = {
+ .commit = block_dirty_bitmap_remove_commit,
+ .abort = block_dirty_bitmap_remove_abort,
+ .clean = g_free,
+};
+
+static void block_dirty_bitmap_remove_action(BlockDirtyBitmap *action,
+ Transaction *tran, Error **errp)
{
- BlockDirtyBitmap *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
- if (action_check_completion_mode(common, errp) < 0) {
- return;
- }
+ tran_add(tran, &block_dirty_bitmap_remove_drv, state);
- action = common->action->u.block_dirty_bitmap_remove.data;
state->bitmap = block_dirty_bitmap_remove(action->node, action->name,
false, &state->bs, errp);
}
}
-static void block_dirty_bitmap_remove_abort(BlkActionState *common)
+static void block_dirty_bitmap_remove_abort(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
if (state->bitmap) {
bdrv_dirty_bitmap_skip_store(state->bitmap, false);
}
}
-static void block_dirty_bitmap_remove_commit(BlkActionState *common)
+static void block_dirty_bitmap_remove_commit(void *opaque)
{
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = opaque;
bdrv_dirty_bitmap_set_busy(state->bitmap, false);
bdrv_release_dirty_bitmap(state->bitmap);
}
-static void abort_prepare(BlkActionState *common, Error **errp)
+static void abort_commit(void *opaque);
+TransactionActionDrv abort_drv = {
+ .commit = abort_commit,
+};
+
+static void abort_action(Transaction *tran, Error **errp)
{
+ tran_add(tran, &abort_drv, NULL);
error_setg(errp, "Transaction aborted using Abort action");
}
-static void abort_commit(BlkActionState *common)
+static void abort_commit(void *opaque)
{
g_assert_not_reached(); /* this action never succeeds */
}
-static const BlkActionOps actions[] = {
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
- .instance_size = sizeof(ExternalSnapshotState),
- .prepare = external_snapshot_prepare,
- .commit = external_snapshot_commit,
- .abort = external_snapshot_abort,
- .clean = external_snapshot_clean,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
- .instance_size = sizeof(ExternalSnapshotState),
- .prepare = external_snapshot_prepare,
- .commit = external_snapshot_commit,
- .abort = external_snapshot_abort,
- .clean = external_snapshot_clean,
- },
- [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
- .instance_size = sizeof(DriveBackupState),
- .prepare = drive_backup_prepare,
- .commit = drive_backup_commit,
- .abort = drive_backup_abort,
- .clean = drive_backup_clean,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
- .instance_size = sizeof(BlockdevBackupState),
- .prepare = blockdev_backup_prepare,
- .commit = blockdev_backup_commit,
- .abort = blockdev_backup_abort,
- .clean = blockdev_backup_clean,
- },
- [TRANSACTION_ACTION_KIND_ABORT] = {
- .instance_size = sizeof(BlkActionState),
- .prepare = abort_prepare,
- .commit = abort_commit,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
- .instance_size = sizeof(InternalSnapshotState),
- .prepare = internal_snapshot_prepare,
- .abort = internal_snapshot_abort,
- .clean = internal_snapshot_clean,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .prepare = block_dirty_bitmap_add_prepare,
- .abort = block_dirty_bitmap_add_abort,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .prepare = block_dirty_bitmap_clear_prepare,
- .commit = block_dirty_bitmap_free_backup,
- .abort = block_dirty_bitmap_restore,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .prepare = block_dirty_bitmap_enable_prepare,
- .abort = block_dirty_bitmap_enable_abort,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .prepare = block_dirty_bitmap_disable_prepare,
- .abort = block_dirty_bitmap_disable_abort,
- },
- [TRANSACTION_ACTION_KIND_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,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .prepare = block_dirty_bitmap_remove_prepare,
- .commit = block_dirty_bitmap_remove_commit,
- .abort = block_dirty_bitmap_remove_abort,
- },
- /* Where are transactions for MIRROR, COMMIT and STREAM?
+static void transaction_action(TransactionAction *act, JobTxn *block_job_txn,
+ Transaction *tran, Error **errp)
+{
+ switch (act->type) {
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT:
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
+ external_snapshot_action(act, tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_DRIVE_BACKUP:
+ drive_backup_action(act->u.drive_backup.data,
+ block_job_txn, tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP:
+ blockdev_backup_action(act->u.blockdev_backup.data,
+ block_job_txn, tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_ABORT:
+ abort_action(tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC:
+ internal_snapshot_action(act->u.blockdev_snapshot_internal_sync.data,
+ tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD:
+ block_dirty_bitmap_add_action(act->u.block_dirty_bitmap_add.data,
+ tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR:
+ block_dirty_bitmap_clear_action(act->u.block_dirty_bitmap_clear.data,
+ tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE:
+ block_dirty_bitmap_enable_action(act->u.block_dirty_bitmap_enable.data,
+ tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE:
+ block_dirty_bitmap_disable_action(
+ act->u.block_dirty_bitmap_disable.data, tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE:
+ block_dirty_bitmap_merge_action(act->u.block_dirty_bitmap_merge.data,
+ tran, errp);
+ return;
+ case TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE:
+ block_dirty_bitmap_remove_action(act->u.block_dirty_bitmap_remove.data,
+ tran, errp);
+ return;
+ /*
+ * 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.
*/
-};
-
-/**
- * Allocate a TransactionProperties structure if necessary, and fill
- * that structure with desired defaults if they are unset.
- */
-static TransactionProperties *get_transaction_properties(
- TransactionProperties *props)
-{
- if (!props) {
- props = g_new0(TransactionProperties, 1);
- }
-
- if (!props->has_completion_mode) {
- props->has_completion_mode = true;
- props->completion_mode = ACTION_COMPLETION_MODE_INDIVIDUAL;
- }
-
- return props;
+ case TRANSACTION_ACTION_KIND__MAX:
+ default:
+ g_assert_not_reached();
+ };
}
+
/*
* 'Atomic' group operations. The operations are performed as a set, and if
* any fail then we roll back all operations in the group.
*
* Always run under BQL.
*/
-void qmp_transaction(TransactionActionList *dev_list,
- bool has_props,
- struct TransactionProperties *props,
+void qmp_transaction(TransactionActionList *actions,
+ struct TransactionProperties *properties,
Error **errp)
{
- TransactionActionList *dev_entry = dev_list;
+ TransactionActionList *act;
JobTxn *block_job_txn = NULL;
- BlkActionState *state, *next;
Error *local_err = NULL;
+ Transaction *tran;
+ ActionCompletionMode comp_mode =
+ properties ? properties->completion_mode :
+ ACTION_COMPLETION_MODE_INDIVIDUAL;
GLOBAL_STATE_CODE();
- QTAILQ_HEAD(, BlkActionState) snap_bdrv_states;
- QTAILQ_INIT(&snap_bdrv_states);
-
/* Does this transaction get canceled as a group on failure?
* If not, we don't really need to make a JobTxn.
*/
- props = get_transaction_properties(props);
- if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
+ if (comp_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
+ for (act = actions; act; act = act->next) {
+ TransactionActionKind type = act->value->type;
+
+ if (type != TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP &&
+ type != TRANSACTION_ACTION_KIND_DRIVE_BACKUP)
+ {
+ error_setg(errp,
+ "Action '%s' does not support transaction property "
+ "completion-mode = %s",
+ TransactionActionKind_str(type),
+ ActionCompletionMode_str(comp_mode));
+ return;
+ }
+ }
+
block_job_txn = job_txn_new();
}
/* drain all i/o before any operations */
bdrv_drain_all();
- /* We don't do anything in this loop that commits us to the operations */
- while (NULL != dev_entry) {
- TransactionAction *dev_info = NULL;
- const BlkActionOps *ops;
-
- dev_info = dev_entry->value;
- dev_entry = dev_entry->next;
-
- assert(dev_info->type < ARRAY_SIZE(actions));
-
- ops = &actions[dev_info->type];
- assert(ops->instance_size > 0);
-
- state = g_malloc0(ops->instance_size);
- state->ops = ops;
- state->action = dev_info;
- state->block_job_txn = block_job_txn;
- state->txn_props = props;
- QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry);
+ tran = tran_new();
- state->ops->prepare(state, &local_err);
+ /* We don't do anything in this loop that commits us to the operations */
+ for (act = actions; act; act = act->next) {
+ transaction_action(act->value, block_job_txn, tran, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto delete_and_fail;
}
}
- QTAILQ_FOREACH(state, &snap_bdrv_states, entry) {
- if (state->ops->commit) {
- state->ops->commit(state);
- }
- }
+ tran_commit(tran);
/* success */
goto exit;
delete_and_fail:
/* failure, and it is all-or-none; roll back all operations */
- QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) {
- if (state->ops->abort) {
- state->ops->abort(state);
- }
- }
+ tran_abort(tran);
exit:
- QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) {
- if (state->ops->clean) {
- state->ops->clean(state);
- }
- g_free(state);
- }
- if (!has_props) {
- qapi_free_TransactionProperties(props);
- }
job_txn_unref(block_job_txn);
}
return ret;
}
-void coroutine_fn qmp_block_resize(bool has_device, const char *device,
- bool has_node_name, const char *node_name,
+void coroutine_fn qmp_block_resize(const char *device, const char *node_name,
int64_t size, Error **errp)
{
Error *local_err = NULL;
BlockDriverState *bs;
AioContext *old_ctx;
- bs = bdrv_lookup_bs(has_device ? device : NULL,
- has_node_name ? node_name : NULL,
- &local_err);
+ bs = bdrv_lookup_bs(device, node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
return;
}
+ bdrv_graph_co_rdlock();
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_RESIZE, NULL)) {
error_setg(errp, QERR_DEVICE_IN_USE, device);
+ bdrv_graph_co_rdunlock();
return;
}
+ bdrv_graph_co_rdunlock();
- blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
+ blk = blk_co_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
if (!blk) {
return;
}
bdrv_co_lock(bs);
bdrv_drained_end(bs);
- blk_unref(blk);
+ blk_co_unref(blk);
bdrv_co_unlock(bs);
}
-void qmp_block_stream(bool has_job_id, const char *job_id, const char *device,
- bool has_base, const char *base,
- bool has_base_node, const char *base_node,
- bool has_backing_file, const char *backing_file,
- bool has_bottom, const char *bottom,
+void qmp_block_stream(const char *job_id, const char *device,
+ const char *base,
+ const char *base_node,
+ const char *backing_file,
+ const char *bottom,
bool has_speed, int64_t speed,
bool has_on_error, BlockdevOnError on_error,
- bool has_filter_node_name, const char *filter_node_name,
+ const char *filter_node_name,
bool has_auto_finalize, bool auto_finalize,
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
Error *local_err = NULL;
int job_flags = JOB_DEFAULT;
- if (has_base && has_base_node) {
+ GLOBAL_STATE_CODE();
+
+ if (base && base_node) {
error_setg(errp, "'base' and 'base-node' cannot be specified "
"at the same time");
return;
}
- if (has_base && has_bottom) {
+ if (base && bottom) {
error_setg(errp, "'base' and 'bottom' cannot be specified "
"at the same time");
return;
}
- if (has_bottom && has_base_node) {
+ if (bottom && base_node) {
error_setg(errp, "'bottom' and 'base-node' cannot be specified "
"at the same time");
return;
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
- if (has_base) {
+ bdrv_graph_rdlock_main_loop();
+ if (base) {
base_bs = bdrv_find_backing_image(bs, base);
if (base_bs == NULL) {
error_setg(errp, "Can't find '%s' in the backing chain", base);
- goto out;
+ goto out_rdlock;
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
}
- if (has_base_node) {
+ if (base_node) {
base_bs = bdrv_lookup_bs(NULL, base_node, errp);
if (!base_bs) {
- goto out;
+ goto out_rdlock;
}
if (bs == base_bs || !bdrv_chain_contains(bs, base_bs)) {
error_setg(errp, "Node '%s' is not a backing image of '%s'",
base_node, device);
- goto out;
+ goto out_rdlock;
}
assert(bdrv_get_aio_context(base_bs) == aio_context);
+
bdrv_refresh_filename(base_bs);
}
- if (has_bottom) {
+ if (bottom) {
bottom_bs = bdrv_lookup_bs(NULL, bottom, errp);
if (!bottom_bs) {
- goto out;
+ goto out_rdlock;
}
if (!bottom_bs->drv) {
error_setg(errp, "Node '%s' is not open", bottom);
- goto out;
+ goto out_rdlock;
}
if (bottom_bs->drv->is_filter) {
error_setg(errp, "Node '%s' is a filter, use a non-filter node "
"as 'bottom'", bottom);
- goto out;
+ goto out_rdlock;
}
if (!bdrv_chain_contains(bs, bottom_bs)) {
error_setg(errp, "Node '%s' is not in a chain starting from '%s'",
bottom, device);
- goto out;
+ goto out_rdlock;
}
assert(bdrv_get_aio_context(bottom_bs) == aio_context);
}
/*
* Check for op blockers in the whole chain between bs and base (or bottom)
*/
- iter_end = has_bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
+ iter_end = bottom ? bdrv_filter_or_cow_bs(bottom_bs) : base_bs;
for (iter = bs; iter && iter != iter_end;
iter = bdrv_filter_or_cow_bs(iter))
{
if (bdrv_op_is_blocked(iter, BLOCK_OP_TYPE_STREAM, errp)) {
- goto out;
+ goto out_rdlock;
}
}
+ bdrv_graph_rdunlock_main_loop();
/* if we are streaming the entire chain, the result will have no backing
* file, and specifying one is therefore an error */
- if (base_bs == NULL && has_backing_file) {
+ if (!base_bs && backing_file) {
error_setg(errp, "backing file specified, but streaming the "
"entire chain");
goto out;
job_flags |= JOB_MANUAL_DISMISS;
}
- stream_start(has_job_id ? job_id : NULL, bs, base_bs, backing_file,
+ stream_start(job_id, bs, base_bs, backing_file,
bottom_bs, job_flags, has_speed ? speed : 0, on_error,
filter_node_name, &local_err);
if (local_err) {
out:
aio_context_release(aio_context);
+ return;
+
+out_rdlock:
+ bdrv_graph_rdunlock_main_loop();
+ aio_context_release(aio_context);
}
-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,
+void qmp_block_commit(const char *job_id, const char *device,
+ const char *base_node,
+ const char *base,
+ const char *top_node,
+ const char *top,
+ 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,
+ const char *filter_node_name,
bool has_auto_finalize, bool auto_finalize,
bool has_auto_dismiss, bool auto_dismiss,
Error **errp)
int job_flags = JOB_DEFAULT;
uint64_t top_perm, top_shared;
+ /* TODO We'll eventually have to take a writer lock in this function */
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
if (!has_speed) {
speed = 0;
}
if (!has_on_error) {
on_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!has_filter_node_name) {
- filter_node_name = NULL;
- }
if (has_auto_finalize && !auto_finalize) {
job_flags |= JOB_MANUAL_FINALIZE;
}
/* default top_bs is the active layer */
top_bs = bs;
- if (has_top_node && has_top) {
+ if (top_node && top) {
error_setg(errp, "'top-node' and 'top' are mutually exclusive");
goto out;
- } else if (has_top_node) {
+ } else if (top_node) {
top_bs = bdrv_lookup_bs(NULL, top_node, errp);
if (top_bs == NULL) {
goto out;
top_node);
goto out;
}
- } else if (has_top && top) {
+ } else if (top) {
/* This strcmp() is just a shortcut, there is no need to
* refresh @bs's filename. If it mismatches,
* bdrv_find_backing_image() will do the refresh and may still
assert(bdrv_get_aio_context(top_bs) == aio_context);
- if (has_base_node && has_base) {
+ if (base_node && base) {
error_setg(errp, "'base-node' and 'base' are mutually exclusive");
goto out;
- } else if (has_base_node) {
+ } else if (base_node) {
base_bs = bdrv_lookup_bs(NULL, base_node, errp);
if (base_bs == NULL) {
goto out;
base_node);
goto out;
}
- } else if (has_base && base) {
+ } else if (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);
if (top_perm & BLK_PERM_WRITE ||
bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs))
{
- if (has_backing_file) {
+ if (backing_file) {
if (bdrv_skip_filters(top_bs) == bdrv_skip_filters(bs)) {
error_setg(errp, "'backing-file' specified,"
" but 'top' is the active layer");
}
goto out;
}
- if (!has_job_id) {
+ if (!job_id) {
/*
* Emulate here what block_job_create() does, because it
* is possible that @bs != @top_bs (the block job should
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, job_flags,
- speed, on_error, has_backing_file ? backing_file : NULL,
+ commit_start(job_id, bs, base_bs, top_bs, job_flags,
+ speed, on_error, backing_file,
filter_node_name, &local_err);
}
if (local_err != NULL) {
if (!backup->has_on_target_error) {
backup->on_target_error = BLOCKDEV_ON_ERROR_REPORT;
}
- if (!backup->has_job_id) {
- backup->job_id = NULL;
- }
if (!backup->has_auto_finalize) {
backup->auto_finalize = true;
}
if ((backup->sync == MIRROR_SYNC_MODE_BITMAP) ||
(backup->sync == MIRROR_SYNC_MODE_INCREMENTAL)) {
/* done before desugaring 'incremental' to print the right message */
- if (!backup->has_bitmap) {
+ if (!backup->bitmap) {
error_setg(errp, "must provide a valid bitmap name for "
"'%s' sync mode", MirrorSyncMode_str(backup->sync));
return NULL;
backup->bitmap_mode = BITMAP_SYNC_MODE_ON_SUCCESS;
}
- if (backup->has_bitmap) {
+ if (backup->bitmap) {
bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap);
if (!bmap) {
error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap);
}
}
- if (!backup->has_bitmap && backup->has_bitmap_mode) {
+ if (!backup->bitmap && backup->has_bitmap_mode) {
error_setg(errp, "Cannot specify bitmap sync mode without a bitmap");
return NULL;
}
XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp)
{
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
return bdrv_get_xdbg_block_graph(errp);
}
**/
static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs,
BlockDriverState *target,
- bool has_replaces, const char *replaces,
+ const char *replaces,
enum MirrorSyncMode sync,
BlockMirrorBackingMode backing_mode,
bool zero_target,
bool has_on_target_error,
BlockdevOnError on_target_error,
bool has_unmap, bool unmap,
- bool has_filter_node_name,
const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
bool has_auto_finalize, bool auto_finalize,
BlockDriverState *unfiltered_bs;
int job_flags = JOB_DEFAULT;
+ GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
if (!has_speed) {
speed = 0;
}
if (!has_unmap) {
unmap = true;
}
- if (!has_filter_node_name) {
- filter_node_name = NULL;
- }
if (!has_copy_mode) {
copy_mode = MIRROR_COPY_MODE_BACKGROUND;
}
sync = MIRROR_SYNC_MODE_FULL;
}
- if (!has_replaces) {
+ if (!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) {
+ if (replaces) {
BlockDriverState *to_replace_bs;
+ AioContext *aio_context;
AioContext *replace_aio_context;
int64_t bs_size, replace_size;
return;
}
+ aio_context = bdrv_get_aio_context(bs);
replace_aio_context = bdrv_get_aio_context(to_replace_bs);
- aio_context_acquire(replace_aio_context);
+ /*
+ * bdrv_getlength() is a co-wrapper and uses AIO_WAIT_WHILE. Be sure not
+ * to acquire the same AioContext twice.
+ */
+ if (replace_aio_context != aio_context) {
+ aio_context_acquire(replace_aio_context);
+ }
replace_size = bdrv_getlength(to_replace_bs);
- aio_context_release(replace_aio_context);
+ if (replace_aio_context != aio_context) {
+ aio_context_release(replace_aio_context);
+ }
if (replace_size < 0) {
error_setg_errno(errp, -replace_size,
* and will allow to check whether the node still exist at mirror completion
*/
mirror_start(job_id, bs, target,
- has_replaces ? replaces : NULL, job_flags,
+ replaces, job_flags,
speed, granularity, buf_size, sync, backing_mode, zero_target,
on_source_error, on_target_error, unmap, filter_node_name,
copy_mode, errp);
}
/* Early check to avoid creating target */
+ bdrv_graph_rdlock_main_loop();
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_MIRROR_SOURCE, errp)) {
+ bdrv_graph_rdunlock_main_loop();
return;
}
arg->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
- if (!arg->has_format) {
+ if (!arg->format) {
format = (arg->mode == NEW_IMAGE_MODE_EXISTING
? NULL : bs->drv->format_name);
}
if (arg->sync == MIRROR_SYNC_MODE_NONE) {
target_backing_bs = bs;
}
+ bdrv_graph_rdunlock_main_loop();
size = bdrv_getlength(bs);
if (size < 0) {
goto out;
}
- if (arg->has_replaces) {
- if (!arg->has_node_name) {
+ if (arg->replaces) {
+ if (!arg->node_name) {
error_setg(errp, "a node-name must be provided when replacing a"
" named node of the graph");
goto out;
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);
+ BlockDriverState *explicit_backing;
switch (arg->mode) {
case NEW_IMAGE_MODE_EXISTING:
break;
case NEW_IMAGE_MODE_ABSOLUTE_PATHS:
- /* create new image with backing file */
+ /*
+ * Create new image with backing file.
+ * Implicit filters should not appear in the filename.
+ */
+ bdrv_graph_rdlock_main_loop();
+ explicit_backing = bdrv_skip_implicit_filters(target_backing_bs);
bdrv_refresh_filename(explicit_backing);
+ bdrv_graph_rdunlock_main_loop();
+
bdrv_img_create(arg->target, format,
explicit_backing->filename,
explicit_backing->drv->format_name,
}
options = qdict_new();
- if (arg->has_node_name) {
+ if (arg->node_name) {
qdict_put_str(options, "node-name", arg->node_name);
}
if (format) {
qdict_put_str(options, "driver", format);
}
+ aio_context_release(aio_context);
/* Mirroring takes care of copy-on-write using the source's backing
* file.
*/
+ aio_context_acquire(qemu_get_aio_context());
target_bs = bdrv_open(arg->target, NULL, options, flags, errp);
+ aio_context_release(qemu_get_aio_context());
+
if (!target_bs) {
- goto out;
+ return;
}
+ bdrv_graph_rdlock_main_loop();
zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
(arg->mode == NEW_IMAGE_MODE_EXISTING ||
!bdrv_has_zero_init(target_bs)));
+ bdrv_graph_rdunlock_main_loop();
/* Honor bdrv_try_change_aio_context() context acquisition requirements. */
old_context = bdrv_get_aio_context(target_bs);
- aio_context_release(aio_context);
aio_context_acquire(old_context);
ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp);
aio_context_release(old_context);
aio_context_acquire(aio_context);
- blockdev_mirror_common(arg->has_job_id ? arg->job_id : NULL, bs, target_bs,
- arg->has_replaces, arg->replaces, arg->sync,
+ blockdev_mirror_common(arg->job_id, bs, target_bs,
+ arg->replaces, arg->sync,
backing_mode, zero_target,
arg->has_speed, arg->speed,
arg->has_granularity, arg->granularity,
arg->has_on_source_error, arg->on_source_error,
arg->has_on_target_error, arg->on_target_error,
arg->has_unmap, arg->unmap,
- false, NULL,
+ NULL,
arg->has_copy_mode, arg->copy_mode,
arg->has_auto_finalize, arg->auto_finalize,
arg->has_auto_dismiss, arg->auto_dismiss,
aio_context_release(aio_context);
}
-void qmp_blockdev_mirror(bool has_job_id, const char *job_id,
+void qmp_blockdev_mirror(const char *job_id,
const char *device, const char *target,
- bool has_replaces, const char *replaces,
+ const char *replaces,
MirrorSyncMode sync,
bool has_speed, int64_t speed,
bool has_granularity, uint32_t granularity,
BlockdevOnError on_source_error,
bool has_on_target_error,
BlockdevOnError on_target_error,
- bool has_filter_node_name,
const char *filter_node_name,
bool has_copy_mode, MirrorCopyMode copy_mode,
bool has_auto_finalize, bool auto_finalize,
goto out;
}
- blockdev_mirror_common(has_job_id ? job_id : NULL, bs, target_bs,
- has_replaces, replaces, sync, backing_mode,
+ blockdev_mirror_common(job_id, bs, target_bs,
+ replaces, sync, backing_mode,
zero_target, has_speed, speed,
has_granularity, granularity,
has_buf_size, buf_size,
has_on_source_error, on_source_error,
has_on_target_error, on_target_error,
- true, true,
- has_filter_node_name, filter_node_name,
+ true, true, filter_node_name,
has_copy_mode, copy_mode,
has_auto_finalize, auto_finalize,
has_auto_dismiss, auto_dismiss,
job_dismiss_locked(&job, errp);
}
+void qmp_block_job_change(BlockJobChangeOptions *opts, Error **errp)
+{
+ BlockJob *job;
+
+ JOB_LOCK_GUARD();
+ job = find_block_job_locked(opts->id, errp);
+
+ if (!job) {
+ return;
+ }
+
+ block_job_change_locked(job, opts, errp);
+}
+
void qmp_change_backing_file(const char *device,
const char *image_node_name,
const char *backing_file,
aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context);
+ bdrv_graph_rdlock_main_loop();
+
image_bs = bdrv_lookup_bs(NULL, image_node_name, &local_err);
if (local_err) {
error_propagate(errp, local_err);
- goto out;
+ goto out_rdlock;
}
if (!image_bs) {
error_setg(errp, "image file not found");
- goto out;
+ goto out_rdlock;
}
if (bdrv_find_base(image_bs) == image_bs) {
error_setg(errp, "not allowing backing file change on an image "
"without a backing file");
- goto out;
+ goto out_rdlock;
}
/* even though we are not necessarily operating on bs, we need it to
* determine if block ops are currently prohibited on the chain */
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_CHANGE, errp)) {
- goto out;
+ goto out_rdlock;
}
/* final sanity check */
if (!bdrv_chain_contains(bs, image_bs)) {
error_setg(errp, "'%s' and image file are not in the same chain",
device);
- goto out;
+ goto out_rdlock;
}
+ bdrv_graph_rdunlock_main_loop();
/* if not r/w, reopen to make r/w */
ro = bdrv_is_read_only(image_bs);
out:
aio_context_release(aio_context);
+ return;
+
+out_rdlock:
+ bdrv_graph_rdunlock_main_loop();
+ aio_context_release(aio_context);
}
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
{
BlockReopenQueue *queue = NULL;
- GSList *drained = NULL;
- GSList *p;
/* Add each one of the BDS that we want to reopen to the queue */
for (; reopen_list != NULL; reopen_list = reopen_list->next) {
QDict *qdict;
/* Check for the selected node name */
- if (!options->has_node_name) {
+ if (!options->node_name) {
error_setg(errp, "node-name not specified");
goto fail;
}
ctx = bdrv_get_aio_context(bs);
aio_context_acquire(ctx);
- bdrv_subtree_drained_begin(bs);
queue = bdrv_reopen_queue(queue, bs, qdict, false);
- drained = g_slist_prepend(drained, bs);
aio_context_release(ctx);
}
fail:
bdrv_reopen_queue_free(queue);
- for (p = drained; p; p = p->next) {
- BlockDriverState *bs = p->data;
- AioContext *ctx = bdrv_get_aio_context(bs);
-
- aio_context_acquire(ctx);
- bdrv_subtree_drained_end(bs);
- aio_context_release(ctx);
- }
- g_slist_free(drained);
}
void qmp_blockdev_del(const char *node_name, Error **errp)
BlockDriverState *bs;
GLOBAL_STATE_CODE();
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
bs = bdrv_find_node(node_name);
if (!bs) {
aio_context_release(aio_context);
}
-static BdrvChild *bdrv_find_child(BlockDriverState *parent_bs,
- const char *child_name)
+static BdrvChild * GRAPH_RDLOCK
+bdrv_find_child(BlockDriverState *parent_bs, const char *child_name)
{
BdrvChild *child;
return NULL;
}
-void qmp_x_blockdev_change(const char *parent, bool has_child,
- const char *child, bool has_node,
+void qmp_x_blockdev_change(const char *parent, const char *child,
const char *node, Error **errp)
{
BlockDriverState *parent_bs, *new_bs = NULL;
BdrvChild *p_child;
+ bdrv_graph_wrlock(NULL);
+
parent_bs = bdrv_lookup_bs(parent, parent, errp);
if (!parent_bs) {
- return;
+ goto out;
}
- if (has_child == has_node) {
- if (has_child) {
+ if (!child == !node) {
+ if (child) {
error_setg(errp, "The parameters child and node are in conflict");
} else {
error_setg(errp, "Either child or node must be specified");
}
- return;
+ goto out;
}
- if (has_child) {
+ if (child) {
p_child = bdrv_find_child(parent_bs, child);
if (!p_child) {
error_setg(errp, "Node '%s' does not have child '%s'",
parent, child);
- return;
+ goto out;
}
bdrv_del_child(parent_bs, p_child, errp);
}
- if (has_node) {
+ if (node) {
new_bs = bdrv_find_node(node);
if (!new_bs) {
error_setg(errp, "Node '%s' not found", node);
- return;
+ goto out;
}
bdrv_add_child(parent_bs, new_bs, errp);
}
+
+out:
+ bdrv_graph_wrunlock(NULL);
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)
AioContext *new_context;
BlockDriverState *bs;
+ GRAPH_RDLOCK_GUARD_MAINLOOP();
+
bs = bdrv_find_node(node_name);
if (!bs) {
error_setg(errp, "Failed to find node with node-name='%s'", node_name);