block/snapshot: move drain outside of read-locked bdrv_snapshot_delete()

This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED.

More granular draining is not trivially possible, because
bdrv_snapshot_delete() can recursively call itself.

The return value of bdrv_all_delete_snapshot() changes from -1 to
-errno propagated from failed sub-calls. This is fine for the existing
callers of bdrv_all_delete_snapshot().

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Message-ID: <20250530151125.955508-4-f.ebner@proxmox.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
Fiona Ebner 2025-05-30 17:10:40 +02:00 committed by Kevin Wolf
parent e1d681b3e1
commit d4c5f8c980
3 changed files with 34 additions and 19 deletions

View file

@ -327,7 +327,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs,
/** /**
* Delete an internal snapshot by @snapshot_id and @name. * Delete an internal snapshot by @snapshot_id and @name.
* @bs: block device used in the operation * @bs: block device used in the operation, must be drained
* @snapshot_id: unique snapshot ID, or NULL * @snapshot_id: unique snapshot ID, or NULL
* @name: snapshot name, or NULL * @name: snapshot name, or NULL
* @errp: location to store error * @errp: location to store error
@ -358,6 +358,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
assert(bs->quiesce_counter > 0);
if (!drv) { if (!drv) {
error_setg(errp, "Device '%s' has no medium", error_setg(errp, "Device '%s' has no medium",
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
@ -368,9 +370,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
return -EINVAL; return -EINVAL;
} }
/* drain all pending i/o before deleting snapshot */
bdrv_drained_begin(bs);
if (drv->bdrv_snapshot_delete) { if (drv->bdrv_snapshot_delete) {
ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp);
} else if (fallback_bs) { } else if (fallback_bs) {
@ -382,7 +381,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs,
ret = -ENOTSUP; ret = -ENOTSUP;
} }
bdrv_drained_end(bs);
return ret; return ret;
} }
@ -571,19 +569,22 @@ int bdrv_all_delete_snapshot(const char *name,
ERRP_GUARD(); ERRP_GUARD();
g_autoptr(GList) bdrvs = NULL; g_autoptr(GList) bdrvs = NULL;
GList *iterbdrvs; GList *iterbdrvs;
int ret = 0;
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { bdrv_drain_all_begin();
return -1; bdrv_graph_rdlock_main_loop();
ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp);
if (ret < 0) {
goto out;
} }
iterbdrvs = bdrvs; iterbdrvs = bdrvs;
while (iterbdrvs) { while (iterbdrvs) {
BlockDriverState *bs = iterbdrvs->data; BlockDriverState *bs = iterbdrvs->data;
QEMUSnapshotInfo sn1, *snapshot = &sn1; QEMUSnapshotInfo sn1, *snapshot = &sn1;
int ret = 0;
if ((devices || bdrv_all_snapshots_includes_bs(bs)) && if ((devices || bdrv_all_snapshots_includes_bs(bs)) &&
bdrv_snapshot_find(bs, snapshot, name) >= 0) bdrv_snapshot_find(bs, snapshot, name) >= 0)
@ -594,13 +595,16 @@ int bdrv_all_delete_snapshot(const char *name,
if (ret < 0) { if (ret < 0) {
error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", error_prepend(errp, "Could not delete snapshot '%s' on '%s': ",
name, bdrv_get_device_or_node_name(bs)); name, bdrv_get_device_or_node_name(bs));
return -1; goto out;
} }
iterbdrvs = iterbdrvs->next; iterbdrvs = iterbdrvs->next;
} }
return 0; out:
bdrv_graph_rdunlock_main_loop();
bdrv_drain_all_end();
return ret;
} }

View file

@ -1132,39 +1132,41 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
int ret; int ret;
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
bs = qmp_get_root_bs(device, errp); bs = qmp_get_root_bs(device, errp);
if (!bs) { if (!bs) {
return NULL; goto error;
} }
if (!id && !name) { if (!id && !name) {
error_setg(errp, "Name or id must be provided"); error_setg(errp, "Name or id must be provided");
return NULL; goto error;
} }
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) {
return NULL; goto error;
} }
ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return NULL; goto error;
} }
if (!ret) { if (!ret) {
error_setg(errp, error_setg(errp,
"Snapshot with id '%s' and name '%s' does not exist on " "Snapshot with id '%s' and name '%s' does not exist on "
"device '%s'", "device '%s'",
STR_OR_NULL(id), STR_OR_NULL(name), device); STR_OR_NULL(id), STR_OR_NULL(name), device);
return NULL; goto error;
} }
bdrv_snapshot_delete(bs, id, name, &local_err); bdrv_snapshot_delete(bs, id, name, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return NULL; goto error;
} }
info = g_new0(SnapshotInfo, 1); info = g_new0(SnapshotInfo, 1);
@ -1180,6 +1182,9 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device,
info->has_icount = true; info->has_icount = true;
} }
error:
bdrv_graph_rdunlock_main_loop();
bdrv_drain_all_end();
return info; return info;
} }
@ -1295,12 +1300,14 @@ static void internal_snapshot_abort(void *opaque)
Error *local_error = NULL; Error *local_error = NULL;
GLOBAL_STATE_CODE(); GLOBAL_STATE_CODE();
GRAPH_RDLOCK_GUARD_MAINLOOP();
if (!state->created) { if (!state->created) {
return; return;
} }
bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop();
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
error_reportf_err(local_error, error_reportf_err(local_error,
"Failed to delete snapshot with id '%s' and " "Failed to delete snapshot with id '%s' and "
@ -1308,6 +1315,8 @@ static void internal_snapshot_abort(void *opaque)
sn->id_str, sn->name, sn->id_str, sn->name,
bdrv_get_device_name(bs)); bdrv_get_device_name(bs));
} }
bdrv_graph_rdunlock_main_loop();
bdrv_drain_all_end();
} }
static void internal_snapshot_clean(void *opaque) static void internal_snapshot_clean(void *opaque)

View file

@ -3505,6 +3505,7 @@ static int img_snapshot(int argc, char **argv)
break; break;
case SNAPSHOT_DELETE: case SNAPSHOT_DELETE:
bdrv_drain_all_begin();
bdrv_graph_rdlock_main_loop(); bdrv_graph_rdlock_main_loop();
ret = bdrv_snapshot_find(bs, &sn, snapshot_name); ret = bdrv_snapshot_find(bs, &sn, snapshot_name);
if (ret < 0) { if (ret < 0) {
@ -3520,6 +3521,7 @@ static int img_snapshot(int argc, char **argv)
} }
} }
bdrv_graph_rdunlock_main_loop(); bdrv_graph_rdunlock_main_loop();
bdrv_drain_all_end();
break; break;
} }