block: add ability to specify list of blockdevs during snapshot

When running snapshot operations, there are various rules for which
blockdevs are included/excluded. While this provides reasonable default
behaviour, there are scenarios that are not well handled by the default
logic. Some of the conditions do not have a single correct answer.

Thus there needs to be a way for the mgmt app to provide an explicit
list of blockdevs to perform snapshots across. This can be achieved
by passing a list of node names that should be used.

Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
Message-Id: <20210204124834.774401-5-berrange@redhat.com>
Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com>
This commit is contained in:
Daniel P. Berrangé 2021-02-04 12:48:26 +00:00 committed by Dr. David Alan Gilbert
parent f61fe11aa6
commit cf3a74c94f
6 changed files with 159 additions and 63 deletions

View file

@ -447,6 +447,41 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs,
return ret;
}
static int bdrv_all_get_snapshot_devices(bool has_devices, strList *devices,
GList **all_bdrvs,
Error **errp)
{
g_autoptr(GList) bdrvs = NULL;
if (has_devices) {
if (!devices) {
error_setg(errp, "At least one device is required for snapshot");
return -1;
}
while (devices) {
BlockDriverState *bs = bdrv_find_node(devices->value);
if (!bs) {
error_setg(errp, "No block device node '%s'", devices->value);
return -1;
}
bdrvs = g_list_append(bdrvs, bs);
devices = devices->next;
}
} else {
BlockDriverState *bs;
BdrvNextIterator it;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
bdrvs = g_list_append(bdrvs, bs);
}
}
*all_bdrvs = g_steal_pointer(&bdrvs);
return 0;
}
static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs)
{
if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs)) {
@ -462,43 +497,59 @@ static bool bdrv_all_snapshots_includes_bs(BlockDriverState *bs)
* These functions will properly handle dataplane (take aio_context_acquire
* when appropriate for appropriate block drivers) */
bool bdrv_all_can_snapshot(Error **errp)
bool bdrv_all_can_snapshot(bool has_devices, strList *devices,
Error **errp)
{
BlockDriverState *bs;
BdrvNextIterator it;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return false;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
bool ok = true;
aio_context_acquire(ctx);
if (bdrv_all_snapshots_includes_bs(bs)) {
if (devices || bdrv_all_snapshots_includes_bs(bs)) {
ok = bdrv_can_snapshot(bs);
}
aio_context_release(ctx);
if (!ok) {
error_setg(errp, "Device '%s' is writable but does not support "
"snapshots", bdrv_get_device_or_node_name(bs));
bdrv_next_cleanup(&it);
return false;
}
iterbdrvs = iterbdrvs->next;
}
return true;
}
int bdrv_all_delete_snapshot(const char *name, Error **errp)
int bdrv_all_delete_snapshot(const char *name,
bool has_devices, strList *devices,
Error **errp)
{
BlockDriverState *bs;
BdrvNextIterator it;
QEMUSnapshotInfo sn1, *snapshot = &sn1;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return -1;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret = 0;
aio_context_acquire(ctx);
if (bdrv_all_snapshots_includes_bs(bs) &&
if ((devices || bdrv_all_snapshots_includes_bs(bs)) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0)
{
ret = bdrv_snapshot_delete(bs, snapshot->id_str,
@ -508,61 +559,80 @@ int bdrv_all_delete_snapshot(const char *name, Error **errp)
if (ret < 0) {
error_prepend(errp, "Could not delete snapshot '%s' on '%s': ",
name, bdrv_get_device_or_node_name(bs));
bdrv_next_cleanup(&it);
return -1;
}
iterbdrvs = iterbdrvs->next;
}
return 0;
}
int bdrv_all_goto_snapshot(const char *name, Error **errp)
int bdrv_all_goto_snapshot(const char *name,
bool has_devices, strList *devices,
Error **errp)
{
BlockDriverState *bs;
BdrvNextIterator it;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return -1;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
int ret = 0;
aio_context_acquire(ctx);
if (bdrv_all_snapshots_includes_bs(bs)) {
if (devices || bdrv_all_snapshots_includes_bs(bs)) {
ret = bdrv_snapshot_goto(bs, name, errp);
}
aio_context_release(ctx);
if (ret < 0) {
error_prepend(errp, "Could not load snapshot '%s' on '%s': ",
name, bdrv_get_device_or_node_name(bs));
bdrv_next_cleanup(&it);
return -1;
}
iterbdrvs = iterbdrvs->next;
}
return 0;
}
int bdrv_all_find_snapshot(const char *name, Error **errp)
int bdrv_all_find_snapshot(const char *name,
bool has_devices, strList *devices,
Error **errp)
{
QEMUSnapshotInfo sn;
BlockDriverState *bs;
BdrvNextIterator it;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return -1;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
QEMUSnapshotInfo sn;
int ret = 0;
aio_context_acquire(ctx);
if (bdrv_all_snapshots_includes_bs(bs)) {
if (devices || bdrv_all_snapshots_includes_bs(bs)) {
ret = bdrv_snapshot_find(bs, &sn, name);
}
aio_context_release(ctx);
if (ret < 0) {
error_setg(errp, "Could not find snapshot '%s' on '%s'",
name, bdrv_get_device_or_node_name(bs));
bdrv_next_cleanup(&it);
return -1;
}
iterbdrvs = iterbdrvs->next;
}
return 0;
@ -571,12 +641,19 @@ int bdrv_all_find_snapshot(const char *name, Error **errp)
int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
BlockDriverState *vm_state_bs,
uint64_t vm_state_size,
bool has_devices, strList *devices,
Error **errp)
{
BlockDriverState *bs;
BdrvNextIterator it;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return -1;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
int ret = 0;
@ -584,7 +661,7 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
if (bs == vm_state_bs) {
sn->vm_state_size = vm_state_size;
ret = bdrv_snapshot_create(bs, sn);
} else if (bdrv_all_snapshots_includes_bs(bs)) {
} else if (devices || bdrv_all_snapshots_includes_bs(bs)) {
sn->vm_state_size = 0;
ret = bdrv_snapshot_create(bs, sn);
}
@ -592,34 +669,43 @@ int bdrv_all_create_snapshot(QEMUSnapshotInfo *sn,
if (ret < 0) {
error_setg(errp, "Could not create snapshot '%s' on '%s'",
sn->name, bdrv_get_device_or_node_name(bs));
bdrv_next_cleanup(&it);
return -1;
}
iterbdrvs = iterbdrvs->next;
}
return 0;
}
BlockDriverState *bdrv_all_find_vmstate_bs(Error **errp)
BlockDriverState *bdrv_all_find_vmstate_bs(bool has_devices, strList *devices,
Error **errp)
{
BlockDriverState *bs;
BdrvNextIterator it;
g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) {
return NULL;
}
iterbdrvs = bdrvs;
while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data;
AioContext *ctx = bdrv_get_aio_context(bs);
bool found;
bool found = false;
aio_context_acquire(ctx);
found = bdrv_all_snapshots_includes_bs(bs) && bdrv_can_snapshot(bs);
found = (devices || bdrv_all_snapshots_includes_bs(bs)) &&
bdrv_can_snapshot(bs);
aio_context_release(ctx);
if (found) {
bdrv_next_cleanup(&it);
break;
return bs;
}
iterbdrvs = iterbdrvs->next;
}
if (!bs) {
error_setg(errp, "No block device supports snapshots");
}
return bs;
error_setg(errp, "No block device supports snapshots");
return NULL;
}