With drive-backup block job, we can have a point-in-time snapshot of a device. With snapshot=on, a backup block job is started on the device to do CoW to a temporary image and this image is exported to nbd. The image is deleted after nbd server stops.
Signed-off-by: Fam Zheng <f...@redhat.com> --- blockdev-nbd.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- hmp.c | 5 ++-- qapi-schema.json | 3 ++- qmp-commands.hx | 2 +- 4 files changed, 80 insertions(+), 6 deletions(-) diff --git a/blockdev-nbd.c b/blockdev-nbd.c index c75df19..f12b57c 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -17,6 +17,8 @@ #include "qmp-commands.h" #include "trace.h" #include "block/nbd.h" +#include "block/block_int.h" +#include "block/block.h" #include "qemu/sockets.h" static int server_fd = -1; @@ -78,8 +80,62 @@ static void nbd_server_put_ref(NBDExport *exp) } } +static void snapshot_drive_backup_cb(void *opaque, int ret) +{ + BlockDriverState *bs = opaque; + bs->backing_hd = NULL; +} + +/* create a point-in-time snapshot BDS from an existing BDS */ +static BlockDriverState *nbd_create_snapshot(BlockDriverState *orig_bs) +{ + int ret; + char filename[1024]; + BlockDriver *drv; + BlockDriverState *bs; + QEMUOptionParameter *options; + Error *local_err = NULL; + + bs = bdrv_new(""); + ret = get_tmp_filename(filename, sizeof(filename)); + if (ret < 0) { + goto err; + } + drv = bdrv_find_format("qcow2"); + if (drv < 0) { + goto err; + } + options = parse_option_parameters("", drv->create_options, NULL); + set_option_parameter_int(options, BLOCK_OPT_SIZE, bdrv_getlength(orig_bs)); + + ret = bdrv_create(drv, filename, options); + if (ret < 0) { + goto err; + } + ret = bdrv_open(bs, filename, NULL, BDRV_O_RDWR, drv); + if (ret < 0) { + goto err; + } + bs->backing_hd = orig_bs; + + backup_start(orig_bs, bs, 1, + MIRROR_SYNC_MODE_NONE, + BLOCKDEV_ON_ERROR_REPORT, + BLOCKDEV_ON_ERROR_REPORT, + snapshot_drive_backup_cb, bs, &local_err); + if (error_is_set(&local_err)) { + goto err; + } + return bs; + +err: + bdrv_delete(bs); + unlink(filename); + return NULL; +} + void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, - Error **errp) + bool has_snapshot, bool snapshot, Error **errp) { BlockDriverState *bs; NBDExport *exp; @@ -104,21 +160,37 @@ void qmp_nbd_server_add(const char *device, bool has_writable, bool writable, if (!has_writable) { writable = false; } + + if (!has_snapshot) { + snapshot = false; + } + if (bdrv_is_read_only(bs)) { writable = false; } + if (snapshot) { + bs = nbd_create_snapshot(bs); + if (!bs) { + error_setg(errp, "Can't create snapshot for device"); + return; + } + } + exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, nbd_server_put_ref); nbd_export_set_name(exp, device); - drive_get_ref(drive_get_by_blockdev(bs)); + if (!snapshot) { + drive_get_ref(drive_get_by_blockdev(bs)); + } n = g_malloc0(sizeof(NBDCloseNotifier)); n->n.notify = nbd_close_notifier; n->exp = exp; bdrv_add_close_notifier(bs, &n->n); QTAILQ_INSERT_TAIL(&close_notifiers, n, next); + return; } void qmp_nbd_server_stop(Error **errp) diff --git a/hmp.c b/hmp.c index c45514b..5cc97fe 100644 --- a/hmp.c +++ b/hmp.c @@ -1440,7 +1440,8 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) continue; } - qmp_nbd_server_add(info->value->device, true, writable, &local_err); + qmp_nbd_server_add(info->value->device, true, writable, false, false, + &local_err); if (local_err != NULL) { qmp_nbd_server_stop(NULL); @@ -1460,7 +1461,7 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) int writable = qdict_get_try_bool(qdict, "writable", 0); Error *local_err = NULL; - qmp_nbd_server_add(device, true, writable, &local_err); + qmp_nbd_server_add(device, true, writable, false, false, &local_err); if (local_err != NULL) { hmp_handle_error(mon, &local_err); diff --git a/qapi-schema.json b/qapi-schema.json index f82d829..bfdbe33 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3225,7 +3225,8 @@ # # Since: 1.3.0 ## -{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool'} } +{ 'command': 'nbd-server-add', 'data': {'device': 'str', '*writable': 'bool', + '*snapshot': 'bool'} } ## # @nbd-server-stop: diff --git a/qmp-commands.hx b/qmp-commands.hx index 2e59b0d..e398d88 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -2871,7 +2871,7 @@ EQMP }, { .name = "nbd-server-add", - .args_type = "device:B,writable:b?", + .args_type = "device:B,writable:b?,snapshot:b?", .mhandler.cmd_new = qmp_marshal_input_nbd_server_add, }, { -- 1.8.3.4