error_printf("\n");
return NULL;
}
- drv = bdrv_find_whitelisted_format(buf);
+ drv = bdrv_find_whitelisted_format(buf, ro);
if (!drv) {
error_report("'%s' invalid format", buf);
return NULL;
static void blockdev_do_action(int kind, void *data, Error **errp)
{
- BlockdevAction action;
- BlockdevActionList list;
+ TransactionAction action;
+ TransactionActionList list;
action.kind = kind;
action.data = data;
.has_mode = has_mode,
.mode = mode,
};
- blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot,
- errp);
+ blockdev_do_action(TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC,
+ &snapshot, errp);
}
/* New and old BlockDriverState structs for group snapshots */
-typedef struct BlkTransactionStates {
+
+typedef struct BlkTransactionStates BlkTransactionStates;
+
+/* Only prepare() may fail. In a single transaction, only one of commit() or
+ abort() will be called, clean() will always be called if it present. */
+typedef struct BdrvActionOps {
+ /* Size of state struct, in bytes. */
+ size_t instance_size;
+ /* Prepare the work, must NOT be NULL. */
+ void (*prepare)(BlkTransactionStates *common, Error **errp);
+ /* Commit the changes, must NOT be NULL. */
+ void (*commit)(BlkTransactionStates *common);
+ /* Abort the changes on fail, can be NULL. */
+ void (*abort)(BlkTransactionStates *common);
+ /* Clean up resource in the end, can be NULL. */
+ void (*clean)(BlkTransactionStates *common);
+} BdrvActionOps;
+
+/*
+ * This structure must be arranged as first member in child type, assuming
+ * that compiler will also arrange it to the same address with parent instance.
+ * Later it will be used in free().
+ */
+struct BlkTransactionStates {
+ TransactionAction *action;
+ const BdrvActionOps *ops;
+ QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
+};
+
+/* external snapshot private data */
+typedef struct ExternalSnapshotStates {
+ BlkTransactionStates common;
BlockDriverState *old_bs;
BlockDriverState *new_bs;
- QSIMPLEQ_ENTRY(BlkTransactionStates) entry;
-} BlkTransactionStates;
+} ExternalSnapshotStates;
-static void external_snapshot_prepare(BlockdevAction *action,
- BlkTransactionStates *states,
+static void external_snapshot_prepare(BlkTransactionStates *common,
Error **errp)
{
BlockDriver *proto_drv;
const char *new_image_file;
const char *format = "qcow2";
enum NewImageMode mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS;
+ ExternalSnapshotStates *states =
+ DO_UPCAST(ExternalSnapshotStates, common, common);
+ TransactionAction *action = common->action;
/* get parameters */
- g_assert(action->kind == BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
+ g_assert(action->kind == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC);
device = action->blockdev_snapshot_sync->device;
new_image_file = action->blockdev_snapshot_sync->snapshot_file;
}
}
-static void external_snapshot_commit(BlkTransactionStates *states)
+static void external_snapshot_commit(BlkTransactionStates *common)
{
+ ExternalSnapshotStates *states =
+ DO_UPCAST(ExternalSnapshotStates, common, common);
+
/* This removes our old bs from the bdrv_states, and adds the new bs */
bdrv_append(states->new_bs, states->old_bs);
/* We don't need (or want) to use the transactional
NULL);
}
+static void external_snapshot_abort(BlkTransactionStates *common)
+{
+ ExternalSnapshotStates *states =
+ DO_UPCAST(ExternalSnapshotStates, common, common);
+ if (states->new_bs) {
+ bdrv_delete(states->new_bs);
+ }
+}
+
+static const BdrvActionOps actions[] = {
+ [TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC] = {
+ .instance_size = sizeof(ExternalSnapshotStates),
+ .prepare = external_snapshot_prepare,
+ .commit = external_snapshot_commit,
+ .abort = external_snapshot_abort,
+ },
+};
+
/*
* 'Atomic' group snapshots. The snapshots are taken as a set, and if any fail
* then we do not pivot any of the devices in the group, and abandon the
* snapshots
*/
-void qmp_transaction(BlockdevActionList *dev_list, Error **errp)
+void qmp_transaction(TransactionActionList *dev_list, Error **errp)
{
- BlockdevActionList *dev_entry = dev_list;
+ TransactionActionList *dev_entry = dev_list;
BlkTransactionStates *states, *next;
Error *local_err = NULL;
/* We don't do anything in this loop that commits us to the snapshot */
while (NULL != dev_entry) {
- BlockdevAction *dev_info = NULL;
+ TransactionAction *dev_info = NULL;
+ const BdrvActionOps *ops;
dev_info = dev_entry->value;
dev_entry = dev_entry->next;
- states = g_malloc0(sizeof(BlkTransactionStates));
+ assert(dev_info->kind < ARRAY_SIZE(actions));
+
+ ops = &actions[dev_info->kind];
+ states = g_malloc0(ops->instance_size);
+ states->ops = ops;
+ states->action = dev_info;
QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry);
- switch (dev_info->kind) {
- case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC:
- external_snapshot_prepare(dev_info, states, errp);
- if (error_is_set(&local_err)) {
- error_propagate(errp, local_err);
- goto delete_and_fail;
- }
- break;
- default:
- abort();
+ states->ops->prepare(states, &local_err);
+ if (error_is_set(&local_err)) {
+ error_propagate(errp, local_err);
+ goto delete_and_fail;
}
-
}
-
- /* Now we are going to do the actual pivot. Everything up to this point
- * is reversible, but we are committed at this point */
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
- external_snapshot_commit(states);
+ states->ops->commit(states);
}
/* success */
* the original bs for all images
*/
QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) {
- if (states->new_bs) {
- bdrv_delete(states->new_bs);
+ if (states->ops->abort) {
+ states->ops->abort(states);
}
}
exit:
QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) {
+ if (states->ops->clean) {
+ states->ops->clean(states);
+ }
g_free(states);
}
}
}
if (format) {
- drv = bdrv_find_whitelisted_format(format);
+ drv = bdrv_find_whitelisted_format(format, bs->read_only);
if (!drv) {
error_set(errp, QERR_INVALID_BLOCK_FORMAT, format);
return;
*/
if (bdrv_get_attached_dev(bs)) {
bdrv_make_anon(bs);
+
+ /* Further I/O must not pause the guest */
+ bdrv_set_on_error(bs, BLOCKDEV_ON_ERROR_REPORT,
+ BLOCKDEV_ON_ERROR_REPORT);
} else {
drive_uninit(drive_get_by_blockdev(bs));
}