[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 20:20:24 +0800 |
add code, shrink l1 table while resize.
zhangzhiming
address@hidden
--
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
index 31ecc10..3e07510 100644
--- a/block/qcow2-cluster.c
+++ b/block/qcow2-cluster.c
@@ -31,6 +31,36 @@
#include "block/qcow2.h"
#include "trace.h"
+int shrink_l1_table(BlockDriverState *bs, int64_t new_l1_size)
+{
+ BDRVQcow2State *s = bs->opaque;
+ int64_t old_l1_size = s->l1_size;
+ s->l1_size = new_l1_size;
+ int ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset,
+ s->l1_size, 1);
+ if(ret < 0){
+ return ret;
+ }
+
+ s->l1_size = old_l1_size;
+ ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset,
+ s->l1_size, -1);
+ if(ret < 0){
+ return ret;
+ }
+ s->l1_size = new_l1_size;
+
+ uint32_t be_l1_size = cpu_to_be32(s->l1_size);
+ ret = bdrv_pwrite_sync(bs->file->bs, offsetof(QCowHeader, l1_size),
+ &be_l1_size, sizeof(be_l1_size));
+ if (ret < 0) {
+ return ret;
+ }
+ // realloc l1 table ?
+
+ return 0;
+}
+
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size)
{
diff --git a/block/qcow2.c b/block/qcow2.c
index 6535f92..6de3910 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -2516,9 +2516,16 @@ static int qcow2_truncate(BlockDriverState *bs, int64_t
offset)
}
new_l1_size = size_to_l1(s, offset);
- ret = qcow2_grow_l1_table(bs, new_l1_size, true);
- if (ret < 0) {
- return ret;
+ if(offset < bs->total_sectors * 512){
+ ret = shrink_l1_table(bs, new_l1_size);
+ if (ret < 0) {
+ return ret;
+ }
+ } else {
+ ret = qcow2_grow_l1_table(bs, new_l1_size, true);
+ if (ret < 0) {
+ return ret;
+ }
}
/* write updated header.size */
diff --git a/block/qcow2.h b/block/qcow2.h
index a063a3c..dab9e48 100644
--- a/block/qcow2.h
+++ b/block/qcow2.h
@@ -534,6 +534,7 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int
refcount_order,
void *cb_opaque, Error **errp);
/* qcow2-cluster.c functions */
+int shrink_l1_table(BlockDriverState *bs, int64_t new_l1_size);
int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size,
bool exact_size);
int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);
--
> On May 19, 2016, at 7:41 PM, zhangzhiming <address@hidden> wrote:
>
> hi, and some extra code need to be changed.
>
> zhangzhiming
> address@hidden <mailto: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
>> <mailto: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
>>>
>>> --
>>>
>>>
>>
>