[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [Qemu-block] [PATCH v1 1/1] qcow2 resize with snapshots
From: |
zhangzhiming |
Subject: |
Re: [Qemu-devel] [Qemu-block] [PATCH v1 1/1] qcow2 resize with snapshots |
Date: |
Thu, 19 May 2016 19:41:05 +0800 |
hi, and some extra code need to be changed.
zhangzhiming
address@hidden
--
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
index d872cf2..fb888b0 100644
--- a/block/qcow2-snapshot.c
+++ b/block/qcow2-snapshot.c
@@ -498,7 +498,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
*snapshot_id)
* Decrease the refcount referenced by the old one only when the L1
* table is overwritten.
*/
- sn_l1_table = g_try_malloc0(cur_l1_bytes);
+ sn_l1_table = g_try_malloc0(sn_l1_bytes);
if (cur_l1_bytes && sn_l1_table == NULL) {
ret = -ENOMEM;
goto fail;
@@ -523,7 +523,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
*snapshot_id)
}
ret = bdrv_pwrite_sync(bs->file->bs, s->l1_table_offset, sn_l1_table,
- cur_l1_bytes);
+ sn_l1_bytes);
if (ret < 0) {
goto fail;
}
@@ -536,6 +536,15 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
*snapshot_id)
goto fail;
}
+ uint32_t be_l1_size = cpu_to_be32(sn->l1_size);
+ ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size),
+ &be_l1_size, sizeof(be_l1_size));
+ if (ret < 0) {
+ goto fail;
+ }
+
+ s->l1_vm_state_index = sn->l1_size;
+
/*
* Decrease refcount of clusters of current L1 table.
*
@@ -553,10 +562,12 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
*snapshot_id)
* Now update the in-memory L1 table to be in sync with the on-disk one. We
* need to do this even if updating refcounts failed.
*/
- for(i = 0;i < s->l1_size; i++) {
+ memset(s->l1_table, 0, s->l1_size*sizeof(uint64_t));
+ for(i = 0;i < sn->l1_size; i++) {
s->l1_table[i] = be64_to_cpu(sn_l1_table[i]);
}
+
if (ret < 0) {
goto fail;
}
@@ -564,6 +575,7 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
*snapshot_id)
g_free(sn_l1_table);
sn_l1_table = NULL;
+ s->l1_size = sn->l1_size;
/*
* Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed
* when we decreased the refcount of the old snapshot.
--
> On May 19, 2016, at 4:56 PM, zhangzhiming <address@hidden> wrote:
>
> hi, missed some code.
>
> zhangzhiming
> address@hidden <mailto:address@hidden>
>
> --
>
> diff --git a/block.c b/block.c
> index 047698a..ff83134 100644
> --- a/block.c
> +++ b/block.c
> @@ -2641,7 +2641,7 @@ int bdrv_apply_snapshot(BlockDriverState *bs, const
> char *snapshot_id, uint64_t
> return ret;
> }
>
> - ret = refresh_total_sectors(bs, snapshot_size);
> + ret = refresh_total_sectors(bs, snapshot_size >> BDRV_SECTOR_BITS);
> bdrv_dirty_bitmap_truncate(bs);
> if (bs->blk) {
> blk_dev_resize_cb(bs->blk);
> diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
> index 9bc987f..d872cf2 100644
> --- a/block/qcow2-snapshot.c
> +++ b/block/qcow2-snapshot.c
> @@ -528,6 +528,14 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char
> *snapshot_id)
> goto fail;
> }
>
> + /* write updated header.size */
> + uint64_t be_disk_size = cpu_to_be64(sn->disk_size);
> + ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, size),
> + &be_disk_size, sizeof(uint64_t));
> + if (ret < 0) {
> + goto fail;
> + }
> +
> /*
> * Decrease refcount of clusters of current L1 table.
> *
>
>
> --
>
>> On May 19, 2016, at 3:46 PM, zhangzhiming <address@hidden
>> <mailto:address@hidden>> wrote:
>>
>> hi, i wrote some code for 'qcow2 resize' with snapshot with v3 image and
>> 'qcow2 goto’ too,
>> different size of snapshots are supported.
>> and i have tested the function and it seems work well.
>> there are some code copied from snapshot_delete_blkdev_internal, and
>> qmp_block_resize,
>> it feels not very good.
>>
>> please review it for me. thanks.
>>
>> zhangzhiming
>> address@hidden <mailto:address@hidden>
>>
>> --
>>
>> diff --git a/block.c b/block.c
>> index 18a497f..047698a 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -2632,6 +2632,24 @@ int bdrv_truncate(BlockDriverState *bs, int64_t
>> offset)
>> }
>>
>> /**
>> + * goto a snapshot
>> + */
>> +int bdrv_apply_snapshot(BlockDriverState *bs, const char *snapshot_id,
>> uint64_t snapshot_size)
>> +{
>> + int ret = bdrv_snapshot_goto(bs, snapshot_id);
>> + if(ret < 0){
>> + return ret;
>> + }
>> +
>> + ret = refresh_total_sectors(bs, snapshot_size);
>> + bdrv_dirty_bitmap_truncate(bs);
>> + if (bs->blk) {
>> + blk_dev_resize_cb(bs->blk);
>> + }
>> + return ret;
>> +}
>> +
>> +/**
>> * Length of a allocated file in bytes. Sparse files are counted by actual
>> * allocated space. Return < 0 if error or unknown.
>> */
>> diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
>> index 5f4a17e..9bc987f 100644
>> --- a/block/qcow2-snapshot.c
>> +++ b/block/qcow2-snapshot.c
>> @@ -477,13 +477,6 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const
>> char *snapshot_id)
>> }
>> sn = &s->snapshots[snapshot_index];
>>
>> - if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
>> - error_report("qcow2: Loading snapshots with different disk "
>> - "size is not implemented");
>> - ret = -ENOTSUP;
>> - goto fail;
>> - }
>> -
>> /*
>> * Make sure that the current L1 table is big enough to contain the whole
>> * L1 table of the snapshot. If the snapshot L1 table is smaller, the
>> @@ -675,6 +668,7 @@ int qcow2_snapshot_list(BlockDriverState *bs,
>> QEMUSnapshotInfo **psn_tab)
>> sn_info->date_sec = sn->date_sec;
>> sn_info->date_nsec = sn->date_nsec;
>> sn_info->vm_clock_nsec = sn->vm_clock_nsec;
>> + sn_info->disk_size = sn->disk_size;
>> }
>> *psn_tab = sn_tab;
>> return s->nb_snapshots;
>> diff --git a/block/qcow2.c b/block/qcow2.c
>> index 62febfc..6535f92 100644
>> --- a/block/qcow2.c
>> +++ b/block/qcow2.c
>> @@ -2501,15 +2501,17 @@ static int qcow2_truncate(BlockDriverState *bs,
>> int64_t offset)
>> return -EINVAL;
>> }
>>
>> - /* cannot proceed if image has snapshots */
>> - if (s->nb_snapshots) {
>> - error_report("Can't resize an image which has snapshots");
>> + bool v3_truncate = (s->qcow_version == 3);
>> +
>> + /* cannot proceed if image has snapshots and qcow_version is not 3*/
>> + if (!v3_truncate && s->nb_snapshots) {
>> + error_report("Can't resize an image which has snapshots and
>> qcow_version is not 3");
>> return -ENOTSUP;
>> }
>>
>> - /* shrinking is currently not supported */
>> - if (offset < bs->total_sectors * 512) {
>> - error_report("qcow2 doesn't support shrinking images yet");
>> + /* shrinking is supported from version 3*/
>> + if (!v3_truncate && offset < bs->total_sectors * 512) {
>> + error_report("qcow2 doesn't support shrinking images yet while
>> qcow_version is not 3");
>> return -ENOTSUP;
>> }
>>
>> diff --git a/block/sheepdog.c b/block/sheepdog.c
>> index 23fbace..bc12f7b 100644
>> --- a/block/sheepdog.c
>> +++ b/block/sheepdog.c
>> @@ -2693,6 +2693,7 @@ static int sd_snapshot_list(BlockDriverState *bs,
>> QEMUSnapshotInfo **psn_tab)
>> sn_tab[found].date_nsec = inode.snap_ctime & 0xffffffff;
>> sn_tab[found].vm_state_size = inode.vm_state_size;
>> sn_tab[found].vm_clock_nsec = inode.vm_clock_nsec;
>> + sn_tab[found].disk_size = inode.vdi_size;
>>
>> snprintf(sn_tab[found].id_str, sizeof(sn_tab[found].id_str),
>> "%" PRIu32, inode.snap_id);
>> diff --git a/blockdev.c b/blockdev.c
>> index 1892b8e..36c66c1 100644
>> --- a/blockdev.c
>> +++ b/blockdev.c
>> @@ -2961,6 +2961,120 @@ out:
>> aio_context_release(aio_context);
>> }
>>
>> +SnapshotInfo *qmp_blockdev_snapshot_goto_internal_sync(const char *device,
>> + bool has_id,
>> + const char *id,
>> + bool has_name,
>> + const char *name,
>> + Error **errp)
>> +{
>> + BlockDriverState *bs;
>> + BlockBackend *blk;
>> + AioContext *aio_context;
>> + QEMUSnapshotInfo sn;
>> + Error *local_err = NULL;
>> + SnapshotInfo *info = NULL;
>> + int ret;
>> +
>> + blk = blk_by_name(device);
>> + if (!blk) {
>> + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND,
>> + "Device '%s' not found", device);
>> + return NULL;
>> + }
>> + aio_context = blk_get_aio_context(blk);
>> + aio_context_acquire(aio_context);
>> +
>> + if(!has_id){
>> + id = NULL;
>> + }
>> +
>> + if(!has_name){
>> + name = NULL;
>> + }
>> +
>> + if(!id && !name){
>> + error_setg(errp, "Name or id must be provided");
>> + goto out_aio_context;
>> + }
>> +
>> + if(!blk_is_available(blk)){
>> + error_setg(errp, "Device '%s' has no medium", device);
>> + goto out_aio_context;
>> + }
>> +
>> + bs = blk_bs(blk);
>> +
>> + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_APPLY,
>> errp)){
>> + goto out_aio_context;
>> + }
>> +
>> + ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err);
>> + if(local_err){
>> + error_propagate(errp, local_err);
>> + goto out_aio_context;
>> + }
>> + if(!ret){
>> + error_setg(errp,
>> + "Snapshot with id '%s' and name '%s' does not exist on "
>> + "device '%s'",
>> + STR_OR_NULL(id), STR_OR_NULL(name), device);
>> + goto out_aio_context;
>> + }
>> + if(!sn.disk_size){
>> + error_setg(errp,
>> + "Snapshot with id '%s' and name '%s' does not has a disk
>> size "
>> + "device '%s'",
>> + STR_OR_NULL(id), STR_OR_NULL(name), device);
>> + goto out_aio_context;
>> + }
>> +
>> + /* complete all in-flight operations before resizing the device */
>> + bdrv_drain_all();
>> +
>> + ret = bdrv_apply_snapshot(bs, sn.id_str, sn.disk_size);
>> + switch (ret) {
>> + case 0:
>> + break;
>> + case -ENOMEDIUM:
>> + error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
>> + break;
>> + case -ENOTSUP:
>> + error_setg(errp, QERR_UNSUPPORTED);
>> + break;
>> + case -EACCES:
>> + error_setg(errp, "Device '%s' is read only", device);
>> + break;
>> + case -EBUSY:
>> + error_setg(errp, QERR_DEVICE_IN_USE, device);
>> + break;
>> + default:
>> + error_setg_errno(errp, -ret, "Could not resize");
>> + break;
>> + }
>> +
>> + if(ret < 0){
>> + goto out_aio_context;
>> + }
>> +
>> + aio_context_release(aio_context);
>> +
>> + info = g_new0(SnapshotInfo, 1);
>> + info->id = g_strdup(sn.id_str);
>> + info->name = g_strdup(sn.name);
>> + info->date_nsec = sn.date_nsec;
>> + info->date_sec = sn.date_sec;
>> + info->vm_state_size = sn.vm_state_size;
>> + info->vm_clock_nsec = sn.vm_clock_nsec % 1000000000;
>> + info->vm_clock_sec = sn.vm_clock_nsec / 1000000000;
>> +
>> + return info;
>> +
>> +out_aio_context:
>> + aio_context_release(aio_context);
>> + return NULL;
>> +}
>> +
>> static void block_job_cb(void *opaque, int ret)
>> {
>> /* Note that this function may be executed from another AioContext
>> besides
>> diff --git a/hmp-commands.hx b/hmp-commands.hx
>> index 4f4f60a..5848a57 100644
>> --- a/hmp-commands.hx
>> +++ b/hmp-commands.hx
>> @@ -1159,6 +1159,24 @@ Delete an internal snapshot on device if it support
>> ETEXI
>>
>> {
>> + .name = "snapshot_goto_blkdev_internal",
>> + .args_type = "device:B,name:s,id:s?",
>> + .params = "device name [id]",
>> + .help = "apply an internal snapshot of device.\n\t\t\t"
>> + "If id is specified, qemu will try apply\n\t\t\t"
>> + "the snapshot matching both id and name.\n\t\t\t"
>> + "The format of the image used by device must\n\t\t\t"
>> + "support it, such as qcow2.\n\t\t\t",
>> + .mhandler.cmd = hmp_snapshot_goto_blkdev_internal,
>> + },
>> +
>> +STEXI
>> address@hidden snapshot_goto_blkdev_internal
>> address@hidden snapshot_goto_blkdev_internal
>> +Apply an internal snapshot on device if it support
>> +ETEXI
>> +
>> + {
>> .name = "drive_mirror",
>> .args_type = "reuse:-n,full:-f,device:B,target:s,format:s?",
>> .params = "[-n] [-f] device target [format]",
>> diff --git a/hmp.c b/hmp.c
>> index d510236..3f1d146 100644
>> --- a/hmp.c
>> +++ b/hmp.c
>> @@ -1057,6 +1057,8 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict)
>> hmp_handle_error(mon, &err);
>> }
>>
>> +
>> +
>> void hmp_drive_mirror(Monitor *mon, const QDict *qdict)
>> {
>> const char *device = qdict_get_str(qdict, "device");
>> @@ -1163,6 +1165,17 @@ void hmp_snapshot_delete_blkdev_internal(Monitor
>> *mon, const QDict *qdict)
>> hmp_handle_error(mon, &err);
>> }
>>
>> +void hmp_snapshot_goto_blkdev_internal(Monitor *mon, const QDict *qdict)
>> +{
>> + const char *device = qdict_get_str(qdict, "device");
>> + const char *name = qdict_get_str(qdict, "name");
>> + const char *id = qdict_get_try_str(qdict, "id");
>> + Error *err = NULL;
>> +
>> + qmp_blockdev_snapshot_goto_internal_sync(device, !!id, id, true, name,
>> &err);
>> + hmp_handle_error(mon, &err);
>> +}
>> +
>> void hmp_migrate_cancel(Monitor *mon, const QDict *qdict)
>> {
>> qmp_migrate_cancel(NULL);
>> diff --git a/hmp.h b/hmp.h
>> index 093d65f..b1ad2f5 100644
>> --- a/hmp.h
>> +++ b/hmp.h
>> @@ -59,6 +59,7 @@ void hmp_block_resize(Monitor *mon, const QDict *qdict);
>> void hmp_snapshot_blkdev(Monitor *mon, const QDict *qdict);
>> void hmp_snapshot_blkdev_internal(Monitor *mon, const QDict *qdict);
>> void hmp_snapshot_delete_blkdev_internal(Monitor *mon, const QDict *qdict);
>> +void hmp_snapshot_goto_blkdev_internal(Monitor *mon, const QDict *qdict);
>> void hmp_drive_mirror(Monitor *mon, const QDict *qdict);
>> void hmp_drive_backup(Monitor *mon, const QDict *qdict);
>> void hmp_migrate_cancel(Monitor *mon, const QDict *qdict);
>> diff --git a/include/block/block.h b/include/block/block.h
>> index b210832..393ca6b 100644
>> --- a/include/block/block.h
>> +++ b/include/block/block.h
>> @@ -173,6 +173,7 @@ typedef enum BlockOpType {
>> BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
>> BLOCK_OP_TYPE_INTERNAL_SNAPSHOT,
>> BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
>> + BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_APPLY,
>> BLOCK_OP_TYPE_MIRROR_SOURCE,
>> BLOCK_OP_TYPE_MIRROR_TARGET,
>> BLOCK_OP_TYPE_RESIZE,
>> @@ -266,6 +267,7 @@ BlockDriverState
>> *bdrv_find_backing_image(BlockDriverState *bs,
>> int bdrv_get_backing_file_depth(BlockDriverState *bs);
>> void bdrv_refresh_filename(BlockDriverState *bs);
>> int bdrv_truncate(BlockDriverState *bs, int64_t offset);
>> +int bdrv_apply_snapshot(BlockDriverState *bs, const char *snapshot_id,
>> uint64_t snapshot_size);
>> int64_t bdrv_nb_sectors(BlockDriverState *bs);
>> int64_t bdrv_getlength(BlockDriverState *bs);
>> int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
>> diff --git a/include/block/snapshot.h b/include/block/snapshot.h
>> index e5c0553..7279c12 100644
>> --- a/include/block/snapshot.h
>> +++ b/include/block/snapshot.h
>> @@ -44,6 +44,7 @@ typedef struct QEMUSnapshotInfo {
>> uint32_t date_sec; /* UTC date of the snapshot */
>> uint32_t date_nsec;
>> uint64_t vm_clock_nsec; /* VM clock relative to boot */
>> + uint64_t disk_size;
>> } QEMUSnapshotInfo;
>>
>> int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info,
>> diff --git a/pixman b/pixman
>> index 87eea99..7c6066b 160000
>> --- a/pixman
>> +++ b/pixman
>> @@ -1 +1 @@
>> -Subproject commit 87eea99e443b389c978cf37efc52788bf03a0ee0
>> +Subproject commit 7c6066b700c7cdd4aeb8be426b14b3a5f0de4b6c
>>
>> --
>>
>>
>