diff options
Diffstat (limited to 'blockdev.c')
| -rw-r--r-- | blockdev.c | 207 |
1 files changed, 157 insertions, 50 deletions
diff --git a/blockdev.c b/blockdev.c index 2c132a308b..1a500b830d 100644 --- a/blockdev.c +++ b/blockdev.c @@ -627,12 +627,15 @@ void do_commit(Monitor *mon, const QDict *qdict) { const char *device = qdict_get_str(qdict, "device"); BlockDriverState *bs; + int ret; if (!strcmp(device, "all")) { - bdrv_commit_all(); + ret = bdrv_commit_all(); + if (ret == -EBUSY) { + qerror_report(QERR_DEVICE_IN_USE, device); + return; + } } else { - int ret; - bs = bdrv_find(device); if (!bs) { qerror_report(QERR_DEVICE_NOT_FOUND, device); @@ -646,74 +649,178 @@ void do_commit(Monitor *mon, const QDict *qdict) } } +static void blockdev_do_action(int kind, void *data, Error **errp) +{ + BlockdevAction action; + BlockdevActionList list; + + action.kind = kind; + action.data = data; + list.value = &action; + list.next = NULL; + qmp_transaction(&list, errp); +} + void qmp_blockdev_snapshot_sync(const char *device, const char *snapshot_file, bool has_format, const char *format, + bool has_mode, enum NewImageMode mode, Error **errp) { - BlockDriverState *bs; - BlockDriver *drv, *old_drv, *proto_drv; + BlockdevSnapshot snapshot = { + .device = (char *) device, + .snapshot_file = (char *) snapshot_file, + .has_format = has_format, + .format = (char *) format, + .has_mode = has_mode, + .mode = mode, + }; + blockdev_do_action(BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC, &snapshot, + errp); +} + + +/* New and old BlockDriverState structs for group snapshots */ +typedef struct BlkTransactionStates { + BlockDriverState *old_bs; + BlockDriverState *new_bs; + QSIMPLEQ_ENTRY(BlkTransactionStates) entry; +} BlkTransactionStates; + +/* + * '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) +{ int ret = 0; - int flags; - char old_filename[1024]; + BlockdevActionList *dev_entry = dev_list; + BlkTransactionStates *states, *next; - bs = bdrv_find(device); - if (!bs) { - error_set(errp, QERR_DEVICE_NOT_FOUND, device); - return; - } - if (bdrv_in_use(bs)) { - error_set(errp, QERR_DEVICE_IN_USE, device); - return; - } + QSIMPLEQ_HEAD(snap_bdrv_states, BlkTransactionStates) snap_bdrv_states; + QSIMPLEQ_INIT(&snap_bdrv_states); - pstrcpy(old_filename, sizeof(old_filename), bs->filename); + /* drain all i/o before any snapshots */ + bdrv_drain_all(); - old_drv = bs->drv; - flags = bs->open_flags; + /* We don't do anything in this loop that commits us to the snapshot */ + while (NULL != dev_entry) { + BlockdevAction *dev_info = NULL; + BlockDriver *proto_drv; + BlockDriver *drv; + int flags; + enum NewImageMode mode; + const char *new_image_file; + const char *device; + const char *format = "qcow2"; + + dev_info = dev_entry->value; + dev_entry = dev_entry->next; + + states = g_malloc0(sizeof(BlkTransactionStates)); + QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, states, entry); + + switch (dev_info->kind) { + case BLOCKDEV_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC: + device = dev_info->blockdev_snapshot_sync->device; + if (!dev_info->blockdev_snapshot_sync->has_mode) { + dev_info->blockdev_snapshot_sync->mode = NEW_IMAGE_MODE_ABSOLUTE_PATHS; + } + new_image_file = dev_info->blockdev_snapshot_sync->snapshot_file; + if (dev_info->blockdev_snapshot_sync->has_format) { + format = dev_info->blockdev_snapshot_sync->format; + } + mode = dev_info->blockdev_snapshot_sync->mode; + break; + default: + abort(); + } - if (!has_format) { - format = "qcow2"; - } + drv = bdrv_find_format(format); + if (!drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + goto delete_and_fail; + } - drv = bdrv_find_format(format); - if (!drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; - } + states->old_bs = bdrv_find(device); + if (!states->old_bs) { + error_set(errp, QERR_DEVICE_NOT_FOUND, device); + goto delete_and_fail; + } - proto_drv = bdrv_find_protocol(snapshot_file); - if (!proto_drv) { - error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); - return; + if (bdrv_in_use(states->old_bs)) { + error_set(errp, QERR_DEVICE_IN_USE, device); + goto delete_and_fail; + } + + if (!bdrv_is_read_only(states->old_bs) && + bdrv_is_inserted(states->old_bs)) { + + if (bdrv_flush(states->old_bs)) { + error_set(errp, QERR_IO_ERROR); + goto delete_and_fail; + } + } + + flags = states->old_bs->open_flags; + + proto_drv = bdrv_find_protocol(new_image_file); + if (!proto_drv) { + error_set(errp, QERR_INVALID_BLOCK_FORMAT, format); + goto delete_and_fail; + } + + /* create new image w/backing file */ + if (mode != NEW_IMAGE_MODE_EXISTING) { + ret = bdrv_img_create(new_image_file, format, + states->old_bs->filename, + states->old_bs->drv->format_name, + NULL, -1, flags); + if (ret) { + error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); + goto delete_and_fail; + } + } + + /* We will manually add the backing_hd field to the bs later */ + states->new_bs = bdrv_new(""); + ret = bdrv_open(states->new_bs, new_image_file, + flags | BDRV_O_NO_BACKING, drv); + if (ret != 0) { + error_set(errp, QERR_OPEN_FILE_FAILED, new_image_file); + goto delete_and_fail; + } } - ret = bdrv_img_create(snapshot_file, format, bs->filename, - bs->drv->format_name, NULL, -1, flags); - if (ret) { - error_set(errp, QERR_UNDEFINED_ERROR); - return; + + /* 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) { + /* This removes our old bs from the bdrv_states, and adds the new bs */ + bdrv_append(states->new_bs, states->old_bs); } - bdrv_drain_all(); - bdrv_flush(bs); + /* success */ + goto exit; - bdrv_close(bs); - ret = bdrv_open(bs, snapshot_file, flags, drv); +delete_and_fail: /* - * If reopening the image file we just created fails, fall back - * and try to re-open the original image. If that fails too, we - * are in serious trouble. - */ - if (ret != 0) { - ret = bdrv_open(bs, old_filename, flags, old_drv); - if (ret != 0) { - error_set(errp, QERR_OPEN_FILE_FAILED, old_filename); - } else { - error_set(errp, QERR_OPEN_FILE_FAILED, snapshot_file); + * failure, and it is all-or-none; abandon each new bs, and keep using + * the original bs for all images + */ + QSIMPLEQ_FOREACH(states, &snap_bdrv_states, entry) { + if (states->new_bs) { + bdrv_delete(states->new_bs); } } +exit: + QSIMPLEQ_FOREACH_SAFE(states, &snap_bdrv_states, entry, next) { + g_free(states); + } + return; } + static void eject_device(BlockDriverState *bs, int force, Error **errp) { if (bdrv_in_use(bs)) { |