aboutsummaryrefslogtreecommitdiffstats
path: root/tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch
diff options
context:
space:
mode:
Diffstat (limited to 'tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch')
-rw-r--r--tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch557
1 files changed, 557 insertions, 0 deletions
diff --git a/tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch b/tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch
new file mode 100644
index 0000000000..054b143245
--- /dev/null
+++ b/tools/qemu/patches/0002-VMDK-introduce-VmdkExtent.patch
@@ -0,0 +1,557 @@
+From fcd9c52d160376184cbd25e04586aa6eef6abd61 Mon Sep 17 00:00:00 2001
+From: Fam Zheng <famcool@gmail.com>
+Date: Tue, 12 Jul 2011 19:56:28 +0800
+Subject: [PATCH 02/12] VMDK: introduce VmdkExtent
+
+Introduced VmdkExtent array into BDRVVmdkState, enable holding multiple
+image extents for multiple file image support.
+
+Signed-off-by: Fam Zheng <famcool@gmail.com>
+Reviewed-by: Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
+Signed-off-by: Kevin Wolf <kwolf@redhat.com>
+---
+ block/vmdk.c | 348 +++++++++++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 246 insertions(+), 102 deletions(-)
+
+--- a/block/vmdk.c
++++ b/block/vmdk.c
+@@ -60,7 +60,11 @@ typedef struct {
+
+ #define L2_CACHE_SIZE 16
+
+-typedef struct BDRVVmdkState {
++typedef struct VmdkExtent {
++ BlockDriverState *file;
++ bool flat;
++ int64_t sectors;
++ int64_t end_sector;
+ int64_t l1_table_offset;
+ int64_t l1_backup_table_offset;
+ uint32_t *l1_table;
+@@ -74,7 +78,13 @@ typedef struct BDRVVmdkState {
+ uint32_t l2_cache_counts[L2_CACHE_SIZE];
+
+ unsigned int cluster_sectors;
++} VmdkExtent;
++
++typedef struct BDRVVmdkState {
+ uint32_t parent_cid;
++ int num_extents;
++ /* Extent array with num_extents entries, ascend ordered by address */
++ VmdkExtent *extents;
+ } BDRVVmdkState;
+
+ typedef struct VmdkMetaData {
+@@ -105,6 +115,19 @@ static int vmdk_probe(const uint8_t *buf
+ #define DESC_SIZE 20*SECTOR_SIZE // 20 sectors of 512 bytes each
+ #define HEADER_SIZE 512 // first sector of 512 bytes
+
++static void vmdk_free_extents(BlockDriverState *bs)
++{
++ int i;
++ BDRVVmdkState *s = bs->opaque;
++
++ for (i = 0; i < s->num_extents; i++) {
++ qemu_free(s->extents[i].l1_table);
++ qemu_free(s->extents[i].l2_cache);
++ qemu_free(s->extents[i].l1_backup_table);
++ }
++ qemu_free(s->extents);
++}
++
+ static uint32_t vmdk_read_cid(BlockDriverState *bs, int parent)
+ {
+ char desc[DESC_SIZE];
+@@ -358,11 +381,50 @@ static int vmdk_parent_open(BlockDriverS
+ return 0;
+ }
+
++/* Create and append extent to the extent array. Return the added VmdkExtent
++ * address. return NULL if allocation failed. */
++static VmdkExtent *vmdk_add_extent(BlockDriverState *bs,
++ BlockDriverState *file, bool flat, int64_t sectors,
++ int64_t l1_offset, int64_t l1_backup_offset,
++ uint32_t l1_size,
++ int l2_size, unsigned int cluster_sectors)
++{
++ VmdkExtent *extent;
++ BDRVVmdkState *s = bs->opaque;
++
++ s->extents = qemu_realloc(s->extents,
++ (s->num_extents + 1) * sizeof(VmdkExtent));
++ extent = &s->extents[s->num_extents];
++ s->num_extents++;
++
++ memset(extent, 0, sizeof(VmdkExtent));
++ extent->file = file;
++ extent->flat = flat;
++ extent->sectors = sectors;
++ extent->l1_table_offset = l1_offset;
++ extent->l1_backup_table_offset = l1_backup_offset;
++ extent->l1_size = l1_size;
++ extent->l1_entry_sectors = l2_size * cluster_sectors;
++ extent->l2_size = l2_size;
++ extent->cluster_sectors = cluster_sectors;
++
++ if (s->num_extents > 1) {
++ extent->end_sector = (*(extent - 1)).end_sector + extent->sectors;
++ } else {
++ extent->end_sector = extent->sectors;
++ }
++ bs->total_sectors = extent->end_sector;
++ return extent;
++}
++
++
+ static int vmdk_open(BlockDriverState *bs, int flags)
+ {
+ BDRVVmdkState *s = bs->opaque;
+ uint32_t magic;
+- int l1_size, i;
++ int i;
++ uint32_t l1_size, l1_entry_sectors;
++ VmdkExtent *extent = NULL;
+
+ if (bdrv_pread(bs->file, 0, &magic, sizeof(magic)) != sizeof(magic))
+ goto fail;
+@@ -370,32 +432,34 @@ static int vmdk_open(BlockDriverState *b
+ magic = be32_to_cpu(magic);
+ if (magic == VMDK3_MAGIC) {
+ VMDK3Header header;
+-
+- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
++ if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header))
++ != sizeof(header)) {
+ goto fail;
+- s->cluster_sectors = le32_to_cpu(header.granularity);
+- s->l2_size = 1 << 9;
+- s->l1_size = 1 << 6;
+- bs->total_sectors = le32_to_cpu(header.disk_sectors);
+- s->l1_table_offset = le32_to_cpu(header.l1dir_offset) << 9;
+- s->l1_backup_table_offset = 0;
+- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
++ }
++ extent = vmdk_add_extent(bs, bs->file, false,
++ le32_to_cpu(header.disk_sectors),
++ le32_to_cpu(header.l1dir_offset) << 9, 0,
++ 1 << 6, 1 << 9, le32_to_cpu(header.granularity));
+ } else if (magic == VMDK4_MAGIC) {
+ VMDK4Header header;
+-
+- if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header)) != sizeof(header))
++ if (bdrv_pread(bs->file, sizeof(magic), &header, sizeof(header))
++ != sizeof(header)) {
+ goto fail;
+- bs->total_sectors = le64_to_cpu(header.capacity);
+- s->cluster_sectors = le64_to_cpu(header.granularity);
+- s->l2_size = le32_to_cpu(header.num_gtes_per_gte);
+- s->l1_entry_sectors = s->l2_size * s->cluster_sectors;
+- if (s->l1_entry_sectors <= 0)
++ }
++ l1_entry_sectors = le32_to_cpu(header.num_gtes_per_gte)
++ * le64_to_cpu(header.granularity);
++ l1_size = (le64_to_cpu(header.capacity) + l1_entry_sectors - 1)
++ / l1_entry_sectors;
++ extent = vmdk_add_extent(bs, bs->file, false,
++ le64_to_cpu(header.capacity),
++ le64_to_cpu(header.gd_offset) << 9,
++ le64_to_cpu(header.rgd_offset) << 9,
++ l1_size,
++ le32_to_cpu(header.num_gtes_per_gte),
++ le64_to_cpu(header.granularity));
++ if (extent->l1_entry_sectors <= 0) {
+ goto fail;
+- s->l1_size = (bs->total_sectors + s->l1_entry_sectors - 1)
+- / s->l1_entry_sectors;
+- s->l1_table_offset = le64_to_cpu(header.rgd_offset) << 9;
+- s->l1_backup_table_offset = le64_to_cpu(header.gd_offset) << 9;
+-
++ }
+ // try to open parent images, if exist
+ if (vmdk_parent_open(bs) != 0)
+ goto fail;
+@@ -406,40 +470,49 @@ static int vmdk_open(BlockDriverState *b
+ }
+
+ /* read the L1 table */
+- l1_size = s->l1_size * sizeof(uint32_t);
+- s->l1_table = qemu_malloc(l1_size);
+- if (bdrv_pread(bs->file, s->l1_table_offset, s->l1_table, l1_size) != l1_size)
+- goto fail;
+- for(i = 0; i < s->l1_size; i++) {
+- le32_to_cpus(&s->l1_table[i]);
+- }
+-
+- if (s->l1_backup_table_offset) {
+- s->l1_backup_table = qemu_malloc(l1_size);
+- if (bdrv_pread(bs->file, s->l1_backup_table_offset, s->l1_backup_table, l1_size) != l1_size)
++ l1_size = extent->l1_size * sizeof(uint32_t);
++ extent->l1_table = qemu_malloc(l1_size);
++ if (bdrv_pread(bs->file,
++ extent->l1_table_offset,
++ extent->l1_table,
++ l1_size)
++ != l1_size) {
++ goto fail;
++ }
++ for (i = 0; i < extent->l1_size; i++) {
++ le32_to_cpus(&extent->l1_table[i]);
++ }
++
++ if (extent->l1_backup_table_offset) {
++ extent->l1_backup_table = qemu_malloc(l1_size);
++ if (bdrv_pread(bs->file,
++ extent->l1_backup_table_offset,
++ extent->l1_backup_table,
++ l1_size)
++ != l1_size) {
+ goto fail;
+- for(i = 0; i < s->l1_size; i++) {
+- le32_to_cpus(&s->l1_backup_table[i]);
++ }
++ for (i = 0; i < extent->l1_size; i++) {
++ le32_to_cpus(&extent->l1_backup_table[i]);
+ }
+ }
+
+- s->l2_cache = qemu_malloc(s->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
++ extent->l2_cache =
++ qemu_malloc(extent->l2_size * L2_CACHE_SIZE * sizeof(uint32_t));
+ return 0;
+ fail:
+- qemu_free(s->l1_backup_table);
+- qemu_free(s->l1_table);
+- qemu_free(s->l2_cache);
++ vmdk_free_extents(bs);
+ return -1;
+ }
+
+-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+- uint64_t offset, int allocate);
+-
+-static int get_whole_cluster(BlockDriverState *bs, uint64_t cluster_offset,
+- uint64_t offset, int allocate)
++static int get_whole_cluster(BlockDriverState *bs,
++ VmdkExtent *extent,
++ uint64_t cluster_offset,
++ uint64_t offset,
++ bool allocate)
+ {
+- BDRVVmdkState *s = bs->opaque;
+- uint8_t whole_grain[s->cluster_sectors*512]; // 128 sectors * 512 bytes each = grain size 64KB
++ /* 128 sectors * 512 bytes each = grain size 64KB */
++ uint8_t whole_grain[extent->cluster_sectors * 512];
+
+ // we will be here if it's first write on non-exist grain(cluster).
+ // try to read from parent image, if exist
+@@ -450,14 +523,14 @@ static int get_whole_cluster(BlockDriver
+ return -1;
+
+ ret = bdrv_read(bs->backing_hd, offset >> 9, whole_grain,
+- s->cluster_sectors);
++ extent->cluster_sectors);
+ if (ret < 0) {
+ return -1;
+ }
+
+ //Write grain only into the active image
+- ret = bdrv_write(bs->file, cluster_offset, whole_grain,
+- s->cluster_sectors);
++ ret = bdrv_write(extent->file, cluster_offset, whole_grain,
++ extent->cluster_sectors);
+ if (ret < 0) {
+ return -1;
+ }
+@@ -465,29 +538,39 @@ static int get_whole_cluster(BlockDriver
+ return 0;
+ }
+
+-static int vmdk_L2update(BlockDriverState *bs, VmdkMetaData *m_data)
++static int vmdk_L2update(VmdkExtent *extent, VmdkMetaData *m_data)
+ {
+- BDRVVmdkState *s = bs->opaque;
+-
+ /* update L2 table */
+- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+- &(m_data->offset), sizeof(m_data->offset)) < 0)
++ if (bdrv_pwrite_sync(
++ extent->file,
++ ((int64_t)m_data->l2_offset * 512)
++ + (m_data->l2_index * sizeof(m_data->offset)),
++ &(m_data->offset),
++ sizeof(m_data->offset)
++ ) < 0) {
+ return -1;
++ }
+ /* update backup L2 table */
+- if (s->l1_backup_table_offset != 0) {
+- m_data->l2_offset = s->l1_backup_table[m_data->l1_index];
+- if (bdrv_pwrite_sync(bs->file, ((int64_t)m_data->l2_offset * 512) + (m_data->l2_index * sizeof(m_data->offset)),
+- &(m_data->offset), sizeof(m_data->offset)) < 0)
++ if (extent->l1_backup_table_offset != 0) {
++ m_data->l2_offset = extent->l1_backup_table[m_data->l1_index];
++ if (bdrv_pwrite_sync(
++ extent->file,
++ ((int64_t)m_data->l2_offset * 512)
++ + (m_data->l2_index * sizeof(m_data->offset)),
++ &(m_data->offset), sizeof(m_data->offset)
++ ) < 0) {
+ return -1;
++ }
+ }
+
+ return 0;
+ }
+
+-static uint64_t get_cluster_offset(BlockDriverState *bs, VmdkMetaData *m_data,
+- uint64_t offset, int allocate)
++static uint64_t get_cluster_offset(BlockDriverState *bs,
++ VmdkExtent *extent,
++ VmdkMetaData *m_data,
++ uint64_t offset, int allocate)
+ {
+- BDRVVmdkState *s = bs->opaque;
+ unsigned int l1_index, l2_offset, l2_index;
+ int min_index, i, j;
+ uint32_t min_count, *l2_table, tmp = 0;
+@@ -496,21 +579,23 @@ static uint64_t get_cluster_offset(Block
+ if (m_data)
+ m_data->valid = 0;
+
+- l1_index = (offset >> 9) / s->l1_entry_sectors;
+- if (l1_index >= s->l1_size)
++ l1_index = (offset >> 9) / extent->l1_entry_sectors;
++ if (l1_index >= extent->l1_size) {
+ return 0;
+- l2_offset = s->l1_table[l1_index];
+- if (!l2_offset)
++ }
++ l2_offset = extent->l1_table[l1_index];
++ if (!l2_offset) {
+ return 0;
++ }
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+- if (l2_offset == s->l2_cache_offsets[i]) {
++ if (l2_offset == extent->l2_cache_offsets[i]) {
+ /* increment the hit count */
+- if (++s->l2_cache_counts[i] == 0xffffffff) {
++ if (++extent->l2_cache_counts[i] == 0xffffffff) {
+ for(j = 0; j < L2_CACHE_SIZE; j++) {
+- s->l2_cache_counts[j] >>= 1;
++ extent->l2_cache_counts[j] >>= 1;
+ }
+ }
+- l2_table = s->l2_cache + (i * s->l2_size);
++ l2_table = extent->l2_cache + (i * extent->l2_size);
+ goto found;
+ }
+ }
+@@ -518,20 +603,25 @@ static uint64_t get_cluster_offset(Block
+ min_index = 0;
+ min_count = 0xffffffff;
+ for(i = 0; i < L2_CACHE_SIZE; i++) {
+- if (s->l2_cache_counts[i] < min_count) {
+- min_count = s->l2_cache_counts[i];
++ if (extent->l2_cache_counts[i] < min_count) {
++ min_count = extent->l2_cache_counts[i];
+ min_index = i;
+ }
+ }
+- l2_table = s->l2_cache + (min_index * s->l2_size);
+- if (bdrv_pread(bs->file, (int64_t)l2_offset * 512, l2_table, s->l2_size * sizeof(uint32_t)) !=
+- s->l2_size * sizeof(uint32_t))
++ l2_table = extent->l2_cache + (min_index * extent->l2_size);
++ if (bdrv_pread(
++ extent->file,
++ (int64_t)l2_offset * 512,
++ l2_table,
++ extent->l2_size * sizeof(uint32_t)
++ ) != extent->l2_size * sizeof(uint32_t)) {
+ return 0;
++ }
+
+- s->l2_cache_offsets[min_index] = l2_offset;
+- s->l2_cache_counts[min_index] = 1;
++ extent->l2_cache_offsets[min_index] = l2_offset;
++ extent->l2_cache_counts[min_index] = 1;
+ found:
+- l2_index = ((offset >> 9) / s->cluster_sectors) % s->l2_size;
++ l2_index = ((offset >> 9) / extent->cluster_sectors) % extent->l2_size;
+ cluster_offset = le32_to_cpu(l2_table[l2_index]);
+
+ if (!cluster_offset) {
+@@ -539,8 +629,11 @@ static uint64_t get_cluster_offset(Block
+ return 0;
+
+ // Avoid the L2 tables update for the images that have snapshots.
+- cluster_offset = bdrv_getlength(bs->file);
+- bdrv_truncate(bs->file, cluster_offset + (s->cluster_sectors << 9));
++ cluster_offset = bdrv_getlength(extent->file);
++ bdrv_truncate(
++ extent->file,
++ cluster_offset + (extent->cluster_sectors << 9)
++ );
+
+ cluster_offset >>= 9;
+ tmp = cpu_to_le32(cluster_offset);
+@@ -551,7 +644,8 @@ static uint64_t get_cluster_offset(Block
+ * This problem may occur because of insufficient space on host disk
+ * or inappropriate VM shutdown.
+ */
+- if (get_whole_cluster(bs, cluster_offset, offset, allocate) == -1)
++ if (get_whole_cluster(
++ bs, extent, cluster_offset, offset, allocate) == -1)
+ return 0;
+
+ if (m_data) {
+@@ -566,33 +660,69 @@ static uint64_t get_cluster_offset(Block
+ return cluster_offset;
+ }
+
++static VmdkExtent *find_extent(BDRVVmdkState *s,
++ int64_t sector_num, VmdkExtent *start_hint)
++{
++ VmdkExtent *extent = start_hint;
++
++ if (!extent) {
++ extent = &s->extents[0];
++ }
++ while (extent < &s->extents[s->num_extents]) {
++ if (sector_num < extent->end_sector) {
++ return extent;
++ }
++ extent++;
++ }
++ return NULL;
++}
++
+ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num,
+ int nb_sectors, int *pnum)
+ {
+ BDRVVmdkState *s = bs->opaque;
+- int index_in_cluster, n;
+- uint64_t cluster_offset;
+
+- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+- index_in_cluster = sector_num % s->cluster_sectors;
+- n = s->cluster_sectors - index_in_cluster;
++ int64_t index_in_cluster, n, ret;
++ uint64_t offset;
++ VmdkExtent *extent;
++
++ extent = find_extent(s, sector_num, NULL);
++ if (!extent) {
++ return 0;
++ }
++ if (extent->flat) {
++ n = extent->end_sector - sector_num;
++ ret = 1;
++ } else {
++ offset = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0);
++ index_in_cluster = sector_num % extent->cluster_sectors;
++ n = extent->cluster_sectors - index_in_cluster;
++ ret = offset ? 1 : 0;
++ }
+ if (n > nb_sectors)
+ n = nb_sectors;
+ *pnum = n;
+- return (cluster_offset != 0);
++ return ret;
+ }
+
+ static int vmdk_read(BlockDriverState *bs, int64_t sector_num,
+ uint8_t *buf, int nb_sectors)
+ {
+ BDRVVmdkState *s = bs->opaque;
+- int index_in_cluster, n, ret;
++ int ret;
++ uint64_t n, index_in_cluster;
++ VmdkExtent *extent = NULL;
+ uint64_t cluster_offset;
+
+ while (nb_sectors > 0) {
+- cluster_offset = get_cluster_offset(bs, NULL, sector_num << 9, 0);
+- index_in_cluster = sector_num % s->cluster_sectors;
+- n = s->cluster_sectors - index_in_cluster;
++ extent = find_extent(s, sector_num, extent);
++ if (!extent) {
++ return -EIO;
++ }
++ cluster_offset = get_cluster_offset(
++ bs, extent, NULL, sector_num << 9, 0);
++ index_in_cluster = sector_num % extent->cluster_sectors;
++ n = extent->cluster_sectors - index_in_cluster;
+ if (n > nb_sectors)
+ n = nb_sectors;
+ if (!cluster_offset) {
+@@ -621,10 +751,12 @@ static int vmdk_write(BlockDriverState *
+ const uint8_t *buf, int nb_sectors)
+ {
+ BDRVVmdkState *s = bs->opaque;
+- VmdkMetaData m_data;
+- int index_in_cluster, n;
++ VmdkExtent *extent = NULL;
++ int n;
++ int64_t index_in_cluster;
+ uint64_t cluster_offset;
+ static int cid_update = 0;
++ VmdkMetaData m_data;
+
+ if (sector_num > bs->total_sectors) {
+ fprintf(stderr,
+@@ -635,20 +767,35 @@ static int vmdk_write(BlockDriverState *
+ }
+
+ while (nb_sectors > 0) {
+- index_in_cluster = sector_num & (s->cluster_sectors - 1);
+- n = s->cluster_sectors - index_in_cluster;
+- if (n > nb_sectors)
+- n = nb_sectors;
+- cluster_offset = get_cluster_offset(bs, &m_data, sector_num << 9, 1);
+- if (!cluster_offset)
++ extent = find_extent(s, sector_num, extent);
++ if (!extent) {
++ return -EIO;
++ }
++ cluster_offset = get_cluster_offset(
++ bs,
++ extent,
++ &m_data,
++ sector_num << 9, 1);
++ if (!cluster_offset) {
+ return -1;
++ }
++ index_in_cluster = sector_num % extent->cluster_sectors;
++ n = extent->cluster_sectors - index_in_cluster;
++ if (n > nb_sectors) {
++ n = nb_sectors;
++ }
+
+- if (bdrv_pwrite(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512) != n * 512)
++ if (bdrv_pwrite(bs->file,
++ cluster_offset + index_in_cluster * 512,
++ buf, n * 512)
++ != n * 512) {
+ return -1;
++ }
+ if (m_data.valid) {
+ /* update L2 tables */
+- if (vmdk_L2update(bs, &m_data) == -1)
++ if (vmdk_L2update(extent, &m_data) == -1) {
+ return -1;
++ }
+ }
+ nb_sectors -= n;
+ sector_num += n;
+@@ -822,10 +969,7 @@ exit:
+
+ static void vmdk_close(BlockDriverState *bs)
+ {
+- BDRVVmdkState *s = bs->opaque;
+-
+- qemu_free(s->l1_table);
+- qemu_free(s->l2_cache);
++ vmdk_free_extents(bs);
+ }
+
+ static int vmdk_flush(BlockDriverState *bs)