savevm: Really verify if a drive supports snapshots
Both bdrv_can_snapshot() and bdrv_has_snapshot() does not work as advertized.
First issue: Their names implies different porpouses, but they do the same thing
and have exactly the same code. Maybe copied and pasted and forgotten?
bdrv_has_snapshot() is called in various places for actually checking if there
is snapshots or not.
Second issue: the way bdrv_can_snapshot() verifies if a block driver supports or
not snapshots does not catch all cases. E.g.: a raw image.
So when do_savevm() is called, first thing it does is to set a global
BlockDriverState to save the VM memory state calling get_bs_snapshots().
static BlockDriverState *get_bs_snapshots(void)
{
    BlockDriverState *bs;
    DriveInfo *dinfo;
    if (bs_snapshots)
        return bs_snapshots;
    QTAILQ_FOREACH(dinfo, &drives, next) {
        bs = dinfo->bdrv;
        if (bdrv_can_snapshot(bs))
            goto ok;
    }
    return NULL;
 ok:
    bs_snapshots = bs;
    return bs;
}
bdrv_can_snapshot() may return a BlockDriverState that does not support
snapshots and do_savevm() goes on.
Later on in do_savevm(), we find:
    QTAILQ_FOREACH(dinfo, &drives, next) {
        bs1 = dinfo->bdrv;
        if (bdrv_has_snapshot(bs1)) {
            /* Write VM state size only to the image that contains the state */
            sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
            ret = bdrv_snapshot_create(bs1, sn);
            if (ret < 0) {
                monitor_printf(mon, "Error while creating snapshot on '%s'\n",
                               bdrv_get_device_name(bs1));
            }
        }
    }
bdrv_has_snapshot(bs1) is not checking if the device does support or has
snapshots as explained above. Only in bdrv_snapshot_create() the device is
actually checked for snapshot support.
So, in cases where the first device supports snapshots, and the second does not,
the snapshot on the first will happen anyways. I believe this is not a good
behavior. It should be an all or nothing process.
This patch addresses these issues by making bdrv_can_snapshot() actually do
what it must do and enforces better tests to avoid errors in the middle of
do_savevm(). bdrv_has_snapshot() is removed and replaced by bdrv_can_snapshot()
where appropriate.
bdrv_can_snapshot() was moved from savevm.c to block.c. It makes more sense to me.
The loadvm_state() function was updated too to enforce that when loading a VM at
least all writable devices must support snapshots too.
Signed-off-by: Miguel Di Ciurcio Filho <miguel.filho@gmail.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									e14e8ba5d0
								
							
						
					
					
						commit
						feeee5aca7
					
				
							
								
								
									
										17
									
								
								block.c
								
								
								
								
							
							
						
						
									
										17
									
								
								block.c
								
								
								
								
							| 
						 | 
				
			
			@ -1666,6 +1666,23 @@ void bdrv_debug_event(BlockDriverState *bs, BlkDebugEvent event)
 | 
			
		|||
/**************************************************************/
 | 
			
		||||
/* handling of snapshots */
 | 
			
		||||
 | 
			
		||||
int bdrv_can_snapshot(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriver *drv = bs->drv;
 | 
			
		||||
    if (!drv || bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!drv->bdrv_snapshot_create) {
 | 
			
		||||
        if (bs->file != NULL) {
 | 
			
		||||
            return bdrv_can_snapshot(bs->file);
 | 
			
		||||
        }
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int bdrv_snapshot_create(BlockDriverState *bs,
 | 
			
		||||
                         QEMUSnapshotInfo *sn_info)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1
									
								
								block.h
								
								
								
								
							
							
						
						
									
										1
									
								
								block.h
								
								
								
								
							| 
						 | 
				
			
			@ -175,6 +175,7 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
 | 
			
		|||
const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
 | 
			
		||||
void bdrv_get_backing_filename(BlockDriverState *bs,
 | 
			
		||||
                               char *filename, int filename_size);
 | 
			
		||||
int bdrv_can_snapshot(BlockDriverState *bs);
 | 
			
		||||
int bdrv_snapshot_create(BlockDriverState *bs,
 | 
			
		||||
                         QEMUSnapshotInfo *sn_info);
 | 
			
		||||
int bdrv_snapshot_goto(BlockDriverState *bs,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										58
									
								
								savevm.c
								
								
								
								
							
							
						
						
									
										58
									
								
								savevm.c
								
								
								
								
							| 
						 | 
				
			
			@ -1575,22 +1575,6 @@ out:
 | 
			
		|||
    return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* device can contain snapshots */
 | 
			
		||||
static int bdrv_can_snapshot(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return (bs &&
 | 
			
		||||
            !bdrv_is_removable(bs) &&
 | 
			
		||||
            !bdrv_is_read_only(bs));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* device must be snapshots in order to have a reliable snapshot */
 | 
			
		||||
static int bdrv_has_snapshot(BlockDriverState *bs)
 | 
			
		||||
{
 | 
			
		||||
    return (bs &&
 | 
			
		||||
            !bdrv_is_removable(bs) &&
 | 
			
		||||
            !bdrv_is_read_only(bs));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static BlockDriverState *get_bs_snapshots(void)
 | 
			
		||||
{
 | 
			
		||||
    BlockDriverState *bs;
 | 
			
		||||
| 
						 | 
				
			
			@ -1600,9 +1584,10 @@ static BlockDriverState *get_bs_snapshots(void)
 | 
			
		|||
        return bs_snapshots;
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs = dinfo->bdrv;
 | 
			
		||||
        if (bdrv_can_snapshot(bs))
 | 
			
		||||
        if (bdrv_can_snapshot(bs)) {
 | 
			
		||||
            goto ok;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return NULL;
 | 
			
		||||
 ok:
 | 
			
		||||
    bs_snapshots = bs;
 | 
			
		||||
| 
						 | 
				
			
			@ -1675,12 +1660,26 @@ void do_savevm(Monitor *mon, const QDict *qdict)
 | 
			
		|||
#endif
 | 
			
		||||
    const char *name = qdict_get_try_str(qdict, "name");
 | 
			
		||||
 | 
			
		||||
    /* Verify if there is a device that doesn't support snapshots and is writable */
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs = dinfo->bdrv;
 | 
			
		||||
 | 
			
		||||
        if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!bdrv_can_snapshot(bs)) {
 | 
			
		||||
            monitor_printf(mon, "Device '%s' is writable but does not support snapshots.\n",
 | 
			
		||||
                               bdrv_get_device_name(bs));
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = get_bs_snapshots();
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        monitor_printf(mon, "No block device can accept snapshots\n");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* ??? Should this occur after vm_stop?  */
 | 
			
		||||
    qemu_aio_flush();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1733,7 +1732,7 @@ void do_savevm(Monitor *mon, const QDict *qdict)
 | 
			
		|||
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs1 = dinfo->bdrv;
 | 
			
		||||
        if (bdrv_has_snapshot(bs1)) {
 | 
			
		||||
        if (bdrv_can_snapshot(bs1)) {
 | 
			
		||||
            /* Write VM state size only to the image that contains the state */
 | 
			
		||||
            sn->vm_state_size = (bs == bs1 ? vm_state_size : 0);
 | 
			
		||||
            ret = bdrv_snapshot_create(bs1, sn);
 | 
			
		||||
| 
						 | 
				
			
			@ -1757,6 +1756,21 @@ int load_vmstate(const char *name)
 | 
			
		|||
    QEMUFile *f;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    /* Verify if there is a device that doesn't support snapshots and is writable */
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs = dinfo->bdrv;
 | 
			
		||||
 | 
			
		||||
        if (bdrv_is_removable(bs) || bdrv_is_read_only(bs)) {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!bdrv_can_snapshot(bs)) {
 | 
			
		||||
            error_report("Device '%s' is writable but does not support snapshots.",
 | 
			
		||||
                               bdrv_get_device_name(bs));
 | 
			
		||||
            return -ENOTSUP;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bs = get_bs_snapshots();
 | 
			
		||||
    if (!bs) {
 | 
			
		||||
        error_report("No block device supports snapshots");
 | 
			
		||||
| 
						 | 
				
			
			@ -1768,7 +1782,7 @@ int load_vmstate(const char *name)
 | 
			
		|||
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs1 = dinfo->bdrv;
 | 
			
		||||
        if (bdrv_has_snapshot(bs1)) {
 | 
			
		||||
        if (bdrv_can_snapshot(bs1)) {
 | 
			
		||||
            ret = bdrv_snapshot_goto(bs1, name);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                switch(ret) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1830,7 +1844,7 @@ void do_delvm(Monitor *mon, const QDict *qdict)
 | 
			
		|||
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs1 = dinfo->bdrv;
 | 
			
		||||
        if (bdrv_has_snapshot(bs1)) {
 | 
			
		||||
        if (bdrv_can_snapshot(bs1)) {
 | 
			
		||||
            ret = bdrv_snapshot_delete(bs1, name);
 | 
			
		||||
            if (ret < 0) {
 | 
			
		||||
                if (ret == -ENOTSUP)
 | 
			
		||||
| 
						 | 
				
			
			@ -1861,7 +1875,7 @@ void do_info_snapshots(Monitor *mon)
 | 
			
		|||
    monitor_printf(mon, "Snapshot devices:");
 | 
			
		||||
    QTAILQ_FOREACH(dinfo, &drives, next) {
 | 
			
		||||
        bs1 = dinfo->bdrv;
 | 
			
		||||
        if (bdrv_has_snapshot(bs1)) {
 | 
			
		||||
        if (bdrv_can_snapshot(bs1)) {
 | 
			
		||||
            if (bs == bs1)
 | 
			
		||||
                monitor_printf(mon, " %s", bdrv_get_device_name(bs1));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue