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)
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 (*action)(BlkActionState *common, Transaction *tran, Error **errp);
-} 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;
- QTAILQ_ENTRY(BlkActionState) entry;
-};
-
/* internal snapshot private data */
typedef struct InternalSnapshotState {
- BlkActionState common;
BlockDriverState *bs;
QEMUSnapshotInfo sn;
bool created;
.clean = internal_snapshot_clean,
};
-static void internal_snapshot_action(BlkActionState *common,
+static void internal_snapshot_action(BlockdevSnapshotInternal *internal,
Transaction *tran, Error **errp)
{
Error *local_err = NULL;
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);
-
tran_add(tran, &internal_snapshot_drv, state);
device = internal->device;
/* external snapshot private data */
typedef struct ExternalSnapshotState {
- BlkActionState common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
bool overlay_appended;
.clean = external_snapshot_clean,
};
-static void external_snapshot_action(BlkActionState *common, Transaction *tran,
- Error **errp)
+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
}
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.
}
typedef struct DriveBackupState {
- BlkActionState common;
BlockDriverState *bs;
BlockJob *job;
} DriveBackupState;
.clean = drive_backup_clean,
};
-static void drive_backup_action(BlkActionState *common, Transaction *tran,
- Error **errp)
+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;
tran_add(tran, &drive_backup_drv, state);
- assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
- backup = common->action->u.drive_backup.data;
-
if (!backup->has_mode) {
backup->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
}
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);
}
typedef struct BlockdevBackupState {
- BlkActionState common;
BlockDriverState *bs;
BlockJob *job;
} BlockdevBackupState;
.clean = blockdev_backup_clean,
};
-static void blockdev_backup_action(BlkActionState *common, Transaction *tran,
- Error **errp)
+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;
tran_add(tran, &blockdev_backup_drv, state);
- assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
- backup = common->action->u.blockdev_backup.data;
-
bs = bdrv_lookup_bs(backup->device, backup->device, errp);
if (!bs) {
return;
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);
}
}
typedef struct BlockDirtyBitmapState {
- BlkActionState common;
BdrvDirtyBitmap *bitmap;
BlockDriverState *bs;
HBitmap *backup;
- bool prepared;
bool was_enabled;
} BlockDirtyBitmapState;
.clean = g_free,
};
-static void block_dirty_bitmap_add_action(BlkActionState *common,
+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);
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(void *opaque)
{
- BlockDirtyBitmapAdd *action;
BlockDirtyBitmapState *state = opaque;
- action = state->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);
}
}
.clean = g_free,
};
-static void block_dirty_bitmap_clear_action(BlkActionState *common,
+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);
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,
.clean = g_free,
};
-static void block_dirty_bitmap_enable_action(BlkActionState *common,
+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);
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,
.clean = g_free,
};
-static void block_dirty_bitmap_disable_action(BlkActionState *common,
+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);
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,
.clean = g_free,
};
-static void block_dirty_bitmap_merge_action(BlkActionState *common,
+static void block_dirty_bitmap_merge_action(BlockDirtyBitmapMerge *action,
Transaction *tran, Error **errp)
{
- BlockDirtyBitmapMerge *action;
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
- common, common);
+ BlockDirtyBitmapState *state = g_new0(BlockDirtyBitmapState, 1);
tran_add(tran, &block_dirty_bitmap_merge_drv, state);
- action = common->action->u.block_dirty_bitmap_merge.data;
-
state->bitmap = block_dirty_bitmap_merge(action->node, action->target,
action->bitmaps, &state->backup,
errp);
.clean = g_free,
};
-static void block_dirty_bitmap_remove_action(BlkActionState *common,
+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);
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 abort_commit(void *opaque);
TransactionActionDrv abort_drv = {
.commit = abort_commit,
- .clean = g_free,
};
-static void abort_action(BlkActionState *common, Transaction *tran,
- Error **errp)
+static void abort_action(Transaction *tran, Error **errp)
{
- tran_add(tran, &abort_drv, common);
+ tran_add(tran, &abort_drv, NULL);
error_setg(errp, "Transaction aborted using Abort action");
}
g_assert_not_reached(); /* this action never succeeds */
}
-static const BlkActionOps actions_map[] = {
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
- .instance_size = sizeof(ExternalSnapshotState),
- .action = external_snapshot_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
- .instance_size = sizeof(ExternalSnapshotState),
- .action = external_snapshot_action,
- },
- [TRANSACTION_ACTION_KIND_DRIVE_BACKUP] = {
- .instance_size = sizeof(DriveBackupState),
- .action = drive_backup_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP] = {
- .instance_size = sizeof(BlockdevBackupState),
- .action = blockdev_backup_action,
- },
- [TRANSACTION_ACTION_KIND_ABORT] = {
- .instance_size = sizeof(BlkActionState),
- .action = abort_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_INTERNAL_SYNC] = {
- .instance_size = sizeof(InternalSnapshotState),
- .action = internal_snapshot_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ADD] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_add_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_CLEAR] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_clear_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_enable_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_disable_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_merge_action,
- },
- [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_REMOVE] = {
- .instance_size = sizeof(BlockDirtyBitmapState),
- .action = block_dirty_bitmap_remove_action,
- },
- /* 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.
*/
-};
+ case TRANSACTION_ACTION_KIND__MAX:
+ default:
+ g_assert_not_reached();
+ };
+}
+
/*
* 'Atomic' group operations. The operations are performed as a set, and if
/* We don't do anything in this loop that commits us to the operations */
for (act = actions; act; act = act->next) {
- TransactionAction *dev_info = act->value;
- const BlkActionOps *ops;
- BlkActionState *state;
-
- assert(dev_info->type < ARRAY_SIZE(actions_map));
-
- ops = &actions_map[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->ops->action(state, tran, &local_err);
+ transaction_action(act->value, block_job_txn, tran, &local_err);
if (local_err) {
error_propagate(errp, local_err);
goto delete_and_fail;
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 (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;
}
zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
/* 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(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;
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 (!child == !node) {
} else {
error_setg(errp, "Either child or node must be specified");
}
- return;
+ goto out;
}
if (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);
}
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();
}
BlockJobInfoList *qmp_query_block_jobs(Error **errp)