@@ -53,6 +53,7 @@
# @blockdev-snapshot-internal-sync: Since 1.7
# @blockdev-snapshot-sync: since 1.1
# @drive-backup: Since 1.6
+# @blockdev-add: since 7.0
#
# Features:
# @deprecated: Member @drive-backup is deprecated. Use member
@@ -66,6 +67,7 @@
'block-dirty-bitmap-disable', 'block-dirty-bitmap-merge',
'blockdev-backup', 'blockdev-snapshot',
'blockdev-snapshot-internal-sync', 'blockdev-snapshot-sync',
+ 'blockdev-add',
{ 'name': 'drive-backup', 'features': [ 'deprecated' ] } ] }
##
@@ -140,6 +142,14 @@
{ 'struct': 'DriveBackupWrapper',
'data': { 'data': 'DriveBackup' } }
+##
+# @BlockdevAddWrapper:
+#
+# Since: 7.0
+##
+{ 'struct': 'BlockdevAddWrapper',
+ 'data': { 'data': 'BlockdevOptions' } }
+
##
# @TransactionAction:
#
@@ -163,6 +173,7 @@
'blockdev-snapshot': 'BlockdevSnapshotWrapper',
'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternalWrapper',
'blockdev-snapshot-sync': 'BlockdevSnapshotSyncWrapper',
+ 'blockdev-add': 'BlockdevAddWrapper',
'drive-backup': 'DriveBackupWrapper'
} }
@@ -2180,6 +2180,55 @@ static void abort_commit(BlkActionState *common)
g_assert_not_reached(); /* this action never succeeds */
}
+static BlockDriverState *blockdev_add(BlockdevOptions *options, Error **errp)
+{
+ BlockDriverState *bs = NULL;
+ QObject *obj;
+ Visitor *v = qobject_output_visitor_new(&obj);
+ QDict *qdict;
+
+ visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
+ visit_complete(v, &obj);
+ qdict = qobject_to(QDict, obj);
+
+ qdict_flatten(qdict);
+
+ if (!qdict_get_try_str(qdict, "node-name")) {
+ error_setg(errp, "'node-name' must be specified for the root node");
+ goto fail;
+ }
+
+ bs = bds_tree_init(qdict, errp);
+ if (!bs) {
+ goto fail;
+ }
+
+ bdrv_set_monitor_owned(bs);
+
+fail:
+ visit_free(v);
+ return bs;
+}
+
+typedef struct BlockdevAddState {
+ BlkActionState common;
+ BlockDriverState *bs;
+} BlockdevAddState;
+
+static void blockdev_add_prepare(BlkActionState *common, Error **errp)
+{
+ BlockdevAddState *s = DO_UPCAST(BlockdevAddState, common, common);
+
+ s->bs = blockdev_add(common->action->u.blockdev_add.data, errp);
+}
+
+static void blockdev_add_abort(BlkActionState *common)
+{
+ BlockdevAddState *s = DO_UPCAST(BlockdevAddState, common, common);
+
+ bdrv_unref(s->bs);
+}
+
static const BlkActionOps actions[] = {
[TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT] = {
.instance_size = sizeof(ExternalSnapshotState),
@@ -2253,6 +2302,11 @@ static const BlkActionOps actions[] = {
.commit = block_dirty_bitmap_remove_commit,
.abort = block_dirty_bitmap_remove_abort,
},
+ [TRANSACTION_ACTION_KIND_BLOCKDEV_ADD] = {
+ .instance_size = sizeof(BlockdevAddState),
+ .prepare = blockdev_add_prepare,
+ .abort = blockdev_add_abort,
+ },
/* 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.
@@ -3499,31 +3553,7 @@ out:
void qmp_blockdev_add(BlockdevOptions *options, Error **errp)
{
- BlockDriverState *bs;
- QObject *obj;
- Visitor *v = qobject_output_visitor_new(&obj);
- QDict *qdict;
-
- visit_type_BlockdevOptions(v, NULL, &options, &error_abort);
- visit_complete(v, &obj);
- qdict = qobject_to(QDict, obj);
-
- qdict_flatten(qdict);
-
- if (!qdict_get_try_str(qdict, "node-name")) {
- error_setg(errp, "'node-name' must be specified for the root node");
- goto fail;
- }
-
- bs = bds_tree_init(qdict, errp);
- if (!bs) {
- goto fail;
- }
-
- bdrv_set_monitor_owned(bs);
-
-fail:
- visit_free(v);
+ blockdev_add(options, errp);
}
void qmp_blockdev_reopen(BlockdevOptionsList *reopen_list, Error **errp)
Simply do blockdev_add() in .prepare() and bdrv_unref() in .abort() and that's it. Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com> --- qapi/transaction.json | 11 ++++++ blockdev.c | 80 +++++++++++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 25 deletions(-)