diff options
Diffstat (limited to 'block/qcow2-cluster.c')
-rw-r--r-- | block/qcow2-cluster.c | 343 |
1 files changed, 230 insertions, 113 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index fdedf17..166922f 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -33,12 +33,15 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) BDRVQcowState *s = bs->opaque; int new_l1_size, new_l1_size2, ret, i; uint64_t *new_l1_table; - uint64_t new_l1_table_offset; + int64_t new_l1_table_offset; uint8_t data[12]; new_l1_size = s->l1_size; if (min_size <= new_l1_size) return 0; + if (new_l1_size == 0) { + new_l1_size = 1; + } while (min_size > new_l1_size) { new_l1_size = (new_l1_size * 3 + 1) / 2; } @@ -47,26 +50,34 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) #endif new_l1_size2 = sizeof(uint64_t) * new_l1_size; - new_l1_table = qemu_mallocz(new_l1_size2); + new_l1_table = qemu_mallocz(align_offset(new_l1_size2, 512)); memcpy(new_l1_table, s->l1_table, s->l1_size * sizeof(uint64_t)); /* write new table (align to cluster) */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ALLOC_TABLE); new_l1_table_offset = qcow2_alloc_clusters(bs, new_l1_size2); + if (new_l1_table_offset < 0) { + qemu_free(new_l1_table); + return new_l1_table_offset; + } + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_WRITE_TABLE); for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); - ret = bdrv_pwrite(s->hd, new_l1_table_offset, new_l1_table, new_l1_size2); - if (ret != new_l1_size2) + ret = bdrv_pwrite_sync(bs->file, new_l1_table_offset, new_l1_table, new_l1_size2); + if (ret < 0) goto fail; for(i = 0; i < s->l1_size; i++) new_l1_table[i] = be64_to_cpu(new_l1_table[i]); /* set new table */ + BLKDBG_EVENT(bs->file, BLKDBG_L1_GROW_ACTIVATE_TABLE); cpu_to_be32w((uint32_t*)data, new_l1_size); cpu_to_be64w((uint64_t*)(data + 4), new_l1_table_offset); - if (bdrv_pwrite(s->hd, offsetof(QCowHeader, l1_size), data, - sizeof(data)) != sizeof(data)) + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, l1_size), data,sizeof(data)); + if (ret < 0) { goto fail; + } qemu_free(s->l1_table); qcow2_free_clusters(bs, s->l1_table_offset, s->l1_size * sizeof(uint64_t)); s->l1_table_offset = new_l1_table_offset; @@ -74,8 +85,9 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) s->l1_size = new_l1_size; return 0; fail: - qemu_free(s->l1_table); - return -EIO; + qemu_free(new_l1_table); + qcow2_free_clusters(bs, new_l1_table_offset, new_l1_size2); + return ret; } void qcow2_l2_cache_reset(BlockDriverState *bs) @@ -145,29 +157,36 @@ static uint64_t *seek_l2_table(BDRVQcowState *s, uint64_t l2_offset) * the image file failed. */ -static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset) +static int l2_load(BlockDriverState *bs, uint64_t l2_offset, + uint64_t **l2_table) { BDRVQcowState *s = bs->opaque; int min_index; - uint64_t *l2_table; + int ret; /* seek if the table for the given offset is in the cache */ - l2_table = seek_l2_table(s, l2_offset); - if (l2_table != NULL) - return l2_table; + *l2_table = seek_l2_table(s, l2_offset); + if (*l2_table != NULL) { + return 0; + } /* not found: load a new entry in the least used one */ min_index = l2_cache_new_entry(bs); - l2_table = s->l2_cache + (min_index << s->l2_bits); - if (bdrv_pread(s->hd, l2_offset, l2_table, s->l2_size * sizeof(uint64_t)) != - s->l2_size * sizeof(uint64_t)) - return NULL; + *l2_table = s->l2_cache + (min_index << s->l2_bits); + + BLKDBG_EVENT(bs->file, BLKDBG_L2_LOAD); + ret = bdrv_pread(bs->file, l2_offset, *l2_table, + s->l2_size * sizeof(uint64_t)); + if (ret < 0) { + return ret; + } + s->l2_cache_offsets[min_index] = l2_offset; s->l2_cache_counts[min_index] = 1; - return l2_table; + return 0; } /* @@ -175,21 +194,23 @@ static uint64_t *l2_load(BlockDriverState *bs, uint64_t l2_offset) * and we really don't want bdrv_pread to perform a read-modify-write) */ #define L1_ENTRIES_PER_SECTOR (512 / 8) -static int write_l1_entry(BDRVQcowState *s, int l1_index) +static int write_l1_entry(BlockDriverState *bs, int l1_index) { + BDRVQcowState *s = bs->opaque; uint64_t buf[L1_ENTRIES_PER_SECTOR]; int l1_start_index; - int i; + int i, ret; l1_start_index = l1_index & ~(L1_ENTRIES_PER_SECTOR - 1); for (i = 0; i < L1_ENTRIES_PER_SECTOR; i++) { buf[i] = cpu_to_be64(s->l1_table[l1_start_index + i]); } - if (bdrv_pwrite(s->hd, s->l1_table_offset + 8 * l1_start_index, - buf, sizeof(buf)) != sizeof(buf)) - { - return -1; + BLKDBG_EVENT(bs->file, BLKDBG_L1_UPDATE); + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset + 8 * l1_start_index, + buf, sizeof(buf)); + if (ret < 0) { + return ret; } return 0; @@ -205,24 +226,22 @@ static int write_l1_entry(BDRVQcowState *s, int l1_index) * */ -static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) +static int l2_allocate(BlockDriverState *bs, int l1_index, uint64_t **table) { BDRVQcowState *s = bs->opaque; int min_index; uint64_t old_l2_offset; - uint64_t *l2_table, l2_offset; + uint64_t *l2_table; + int64_t l2_offset; + int ret; old_l2_offset = s->l1_table[l1_index]; /* allocate a new l2 entry */ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); - - /* update the L1 entry */ - - s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; - if (write_l1_entry(s, l1_index) < 0) { - return NULL; + if (l2_offset < 0) { + return l2_offset; } /* allocate a new entry in the l2 cache */ @@ -235,23 +254,40 @@ static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) memset(l2_table, 0, s->l2_size * sizeof(uint64_t)); } else { /* if there was an old l2 table, read it from the disk */ - if (bdrv_pread(s->hd, old_l2_offset, - l2_table, s->l2_size * sizeof(uint64_t)) != - s->l2_size * sizeof(uint64_t)) - return NULL; + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_COW_READ); + ret = bdrv_pread(bs->file, old_l2_offset, l2_table, + s->l2_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } } /* write the l2 table to the file */ - if (bdrv_pwrite(s->hd, l2_offset, - l2_table, s->l2_size * sizeof(uint64_t)) != - s->l2_size * sizeof(uint64_t)) - return NULL; + BLKDBG_EVENT(bs->file, BLKDBG_L2_ALLOC_WRITE); + ret = bdrv_pwrite_sync(bs->file, l2_offset, l2_table, + s->l2_size * sizeof(uint64_t)); + if (ret < 0) { + goto fail; + } + + /* update the L1 entry */ + s->l1_table[l1_index] = l2_offset | QCOW_OFLAG_COPIED; + ret = write_l1_entry(bs, l1_index); + if (ret < 0) { + goto fail; + } /* update the l2 cache entry */ s->l2_cache_offsets[min_index] = l2_offset; s->l2_cache_counts[min_index] = 1; - return l2_table; + *table = l2_table; + return 0; + +fail: + s->l1_table[l1_index] = old_l2_offset; + qcow2_l2_cache_reset(bs); + return ret; } static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, @@ -264,7 +300,7 @@ static int count_contiguous_clusters(uint64_t nb_clusters, int cluster_size, return 0; for (i = start; i < start + nb_clusters; i++) - if (offset + i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask)) + if (offset + (uint64_t) i * cluster_size != (be64_to_cpu(l2_table[i]) & ~mask)) break; return (i - start); @@ -306,8 +342,8 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, } -int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, - int nb_sectors) +static int qcow_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) { BDRVQcowState *s = bs->opaque; int ret, index_in_cluster, n, n1; @@ -315,13 +351,20 @@ int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, while (nb_sectors > 0) { n = nb_sectors; - cluster_offset = qcow2_get_cluster_offset(bs, sector_num << 9, &n); + + ret = qcow2_get_cluster_offset(bs, sector_num << 9, &n, + &cluster_offset); + if (ret < 0) { + return ret; + } + index_in_cluster = sector_num & (s->cluster_sectors - 1); if (!cluster_offset) { if (bs->backing_hd) { /* read from the base image */ n1 = qcow2_backing_read1(bs->backing_hd, sector_num, buf, n); if (n1 > 0) { + BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING); ret = bdrv_read(bs->backing_hd, sector_num, buf, n1); if (ret < 0) return -1; @@ -330,11 +373,12 @@ int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, memset(buf, 0, 512 * n); } } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - if (qcow2_decompress_cluster(s, cluster_offset) < 0) + if (qcow2_decompress_cluster(bs, cluster_offset) < 0) return -1; memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); } else { - ret = bdrv_pread(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); + BLKDBG_EVENT(bs->file, BLKDBG_READ); + ret = bdrv_pread(bs->file, cluster_offset + index_in_cluster * 512, buf, n * 512); if (ret != n * 512) return -1; if (s->crypt_method) { @@ -358,7 +402,8 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, n = n_end - n_start; if (n <= 0) return 0; - ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); + BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); + ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); if (ret < 0) return ret; if (s->crypt_method) { @@ -367,8 +412,9 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, s->cluster_data, n, 1, &s->aes_encrypt_key); } - ret = bdrv_write(s->hd, (cluster_offset >> 9) + n_start, - s->cluster_data, n); + BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); + ret = bdrv_write_sync(bs->file, (cluster_offset >> 9) + n_start, + s->cluster_data, n); if (ret < 0) return ret; return 0; @@ -378,27 +424,29 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, /* * get_cluster_offset * - * For a given offset of the disk image, return cluster offset in - * qcow2 file. + * For a given offset of the disk image, find the cluster offset in + * qcow2 file. The offset is stored in *cluster_offset. * * on entry, *num is the number of contiguous clusters we'd like to * access following offset. * * on exit, *num is the number of contiguous clusters we can read. * - * Return 1, if the offset is found - * Return 0, otherwise. + * Return 0, if the offset is found + * Return -errno, otherwise. * */ -uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, - int *num) +int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, + int *num, uint64_t *cluster_offset) { BDRVQcowState *s = bs->opaque; - int l1_index, l2_index; - uint64_t l2_offset, *l2_table, cluster_offset; + unsigned int l1_index, l2_index; + uint64_t l2_offset, *l2_table; int l1_bits, c; - int index_in_cluster, nb_available, nb_needed, nb_clusters; + unsigned int index_in_cluster, nb_clusters; + uint64_t nb_available, nb_needed; + int ret; index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); nb_needed = *num + index_in_cluster; @@ -409,7 +457,7 @@ uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, * the end of the l1 entry */ - nb_available = (1 << l1_bits) - (offset & ((1 << l1_bits) - 1)); + nb_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1)); /* compute the number of available sectors */ @@ -419,7 +467,7 @@ uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, nb_needed = nb_available; } - cluster_offset = 0; + *cluster_offset = 0; /* seek the the l2 offset in the l1 table */ @@ -437,17 +485,18 @@ uint64_t qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, /* load the l2 table in memory */ l2_offset &= ~QCOW_OFLAG_COPIED; - l2_table = l2_load(bs, l2_offset); - if (l2_table == NULL) - return 0; + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; + } /* find the cluster offset for the given disk offset */ l2_index = (offset >> s->cluster_bits) & (s->l2_size - 1); - cluster_offset = be64_to_cpu(l2_table[l2_index]); + *cluster_offset = be64_to_cpu(l2_table[l2_index]); nb_clusters = size_to_clusters(s, nb_needed << 9); - if (!cluster_offset) { + if (!*cluster_offset) { /* how many empty clusters ? */ c = count_contiguous_free_clusters(nb_clusters, &l2_table[l2_index]); } else { @@ -463,7 +512,8 @@ out: *num = nb_available - index_in_cluster; - return cluster_offset & ~QCOW_OFLAG_COPIED; + *cluster_offset &=~QCOW_OFLAG_COPIED; + return 0; } /* @@ -475,24 +525,27 @@ out: * the l2 table offset in the qcow2 file and the cluster index * in the l2 table are given to the caller. * + * Returns 0 on success, -errno in failure case */ - static int get_cluster_table(BlockDriverState *bs, uint64_t offset, uint64_t **new_l2_table, uint64_t *new_l2_offset, int *new_l2_index) { BDRVQcowState *s = bs->opaque; - int l1_index, l2_index, ret; - uint64_t l2_offset, *l2_table; + unsigned int l1_index, l2_index; + uint64_t l2_offset; + uint64_t *l2_table = NULL; + int ret; /* seek the the l2 offset in the l1 table */ l1_index = offset >> (s->l2_bits + s->cluster_bits); if (l1_index >= s->l1_size) { ret = qcow2_grow_l1_table(bs, l1_index + 1); - if (ret < 0) - return 0; + if (ret < 0) { + return ret; + } } l2_offset = s->l1_table[l1_index]; @@ -501,15 +554,17 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, if (l2_offset & QCOW_OFLAG_COPIED) { /* load the l2 table in memory */ l2_offset &= ~QCOW_OFLAG_COPIED; - l2_table = l2_load(bs, l2_offset); - if (l2_table == NULL) - return 0; + ret = l2_load(bs, l2_offset, &l2_table); + if (ret < 0) { + return ret; + } } else { if (l2_offset) qcow2_free_clusters(bs, l2_offset, s->l2_size * sizeof(uint64_t)); - l2_table = l2_allocate(bs, l1_index); - if (l2_table == NULL) - return 0; + ret = l2_allocate(bs, l1_index, &l2_table); + if (ret < 0) { + return ret; + } l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; } @@ -521,7 +576,7 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, *new_l2_offset = l2_offset; *new_l2_index = l2_index; - return 1; + return 0; } /* @@ -543,12 +598,14 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, { BDRVQcowState *s = bs->opaque; int l2_index, ret; - uint64_t l2_offset, *l2_table, cluster_offset; + uint64_t l2_offset, *l2_table; + int64_t cluster_offset; int nb_csectors; ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); - if (ret == 0) + if (ret < 0) { return 0; + } cluster_offset = be64_to_cpu(l2_table[l2_index]); if (cluster_offset & QCOW_OFLAG_COPIED) @@ -558,6 +615,10 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, qcow2_free_any_clusters(bs, cluster_offset, 1); cluster_offset = qcow2_alloc_bytes(bs, compressed_size); + if (cluster_offset < 0) { + return 0; + } + nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - (cluster_offset >> 9); @@ -568,11 +629,12 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, /* compressed clusters never have the copied flag */ + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE_COMPRESSED); l2_table[l2_index] = cpu_to_be64(cluster_offset); - if (bdrv_pwrite(s->hd, + if (bdrv_pwrite_sync(bs->file, l2_offset + l2_index * sizeof(uint64_t), l2_table + l2_index, - sizeof(uint64_t)) != sizeof(uint64_t)) + sizeof(uint64_t)) < 0) return 0; return cluster_offset; @@ -583,29 +645,31 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, * read-modify-write in bdrv_pwrite */ #define L2_ENTRIES_PER_SECTOR (512 / 8) -static int write_l2_entries(BDRVQcowState *s, uint64_t *l2_table, +static int write_l2_entries(BlockDriverState *bs, uint64_t *l2_table, uint64_t l2_offset, int l2_index, int num) { int l2_start_index = l2_index & ~(L1_ENTRIES_PER_SECTOR - 1); int start_offset = (8 * l2_index) & ~511; int end_offset = (8 * (l2_index + num) + 511) & ~511; size_t len = end_offset - start_offset; + int ret; - if (bdrv_pwrite(s->hd, l2_offset + start_offset, &l2_table[l2_start_index], - len) != len) - { - return -1; + BLKDBG_EVENT(bs->file, BLKDBG_L2_UPDATE); + ret = bdrv_pwrite_sync(bs->file, l2_offset + start_offset, + &l2_table[l2_start_index], len); + if (ret < 0) { + return ret; } return 0; } -int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, - QCowL2Meta *m) +int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) { BDRVQcowState *s = bs->opaque; int i, j = 0, l2_index, ret; uint64_t *old_cluster, start_sect, l2_offset, *l2_table; + uint64_t cluster_offset = m->cluster_offset; if (m->nb_clusters == 0) return 0; @@ -628,10 +692,11 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, goto err; } - ret = -EIO; /* update L2 table */ - if (!get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index)) + ret = get_cluster_table(bs, m->offset, &l2_table, &l2_offset, &l2_index); + if (ret < 0) { goto err; + } for (i = 0; i < m->nb_clusters; i++) { /* if two concurrent writes happen to the same unallocated cluster @@ -647,8 +712,9 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, (i << s->cluster_bits)) | QCOW_OFLAG_COPIED); } - if (write_l2_entries(s, l2_table, l2_offset, l2_index, m->nb_clusters) < 0) { - ret = -1; + ret = write_l2_entries(bs, l2_table, l2_offset, l2_index, m->nb_clusters); + if (ret < 0) { + qcow2_l2_cache_reset(bs); goto err; } @@ -665,29 +731,36 @@ err: /* * alloc_cluster_offset * - * For a given offset of the disk image, return cluster offset in - * qcow2 file. - * + * For a given offset of the disk image, return cluster offset in qcow2 file. * If the offset is not found, allocate a new cluster. * - * Return the cluster offset if successful, - * Return 0, otherwise. + * If the cluster was already allocated, m->nb_clusters is set to 0, + * m->depends_on is set to NULL and the other fields in m are meaningless. + * + * If the cluster is newly allocated, m->nb_clusters is set to the number of + * contiguous clusters that have been allocated. This may be 0 if the request + * conflict with another write request in flight; in this case, m->depends_on + * is set and the remaining fields of m are meaningless. + * + * If m->nb_clusters is non-zero, the other fields of m are valid and contain + * information about the first allocated cluster. * + * Return 0 on success and -errno in error cases */ - -uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, - uint64_t offset, - int n_start, int n_end, - int *num, QCowL2Meta *m) +int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, + int n_start, int n_end, int *num, QCowL2Meta *m) { BDRVQcowState *s = bs->opaque; int l2_index, ret; - uint64_t l2_offset, *l2_table, cluster_offset; - int nb_clusters, i = 0; + uint64_t l2_offset, *l2_table; + int64_t cluster_offset; + unsigned int nb_clusters, i = 0; + QCowL2Meta *old_alloc; ret = get_cluster_table(bs, offset, &l2_table, &l2_offset, &l2_index); - if (ret == 0) - return 0; + if (ret < 0) { + return ret; + } nb_clusters = size_to_clusters(s, n_end << 9); @@ -703,6 +776,7 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, cluster_offset &= ~QCOW_OFLAG_COPIED; m->nb_clusters = 0; + m->depends_on = NULL; goto out; } @@ -717,12 +791,15 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, while (i < nb_clusters) { i += count_contiguous_clusters(nb_clusters - i, s->cluster_size, &l2_table[l2_index], i, 0); - - if(be64_to_cpu(l2_table[l2_index + i])) + if ((i >= nb_clusters) || be64_to_cpu(l2_table[l2_index + i])) { break; + } i += count_contiguous_free_clusters(nb_clusters - i, &l2_table[l2_index + i]); + if (i >= nb_clusters) { + break; + } cluster_offset = be64_to_cpu(l2_table[l2_index + i]); @@ -730,8 +807,41 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, (cluster_offset & QCOW_OFLAG_COMPRESSED)) break; } + assert(i <= nb_clusters); nb_clusters = i; + /* + * Check if there already is an AIO write request in flight which allocates + * the same cluster. In this case we need to wait until the previous + * request has completed and updated the L2 table accordingly. + */ + QLIST_FOREACH(old_alloc, &s->cluster_allocs, next_in_flight) { + + uint64_t end_offset = offset + nb_clusters * s->cluster_size; + uint64_t old_offset = old_alloc->offset; + uint64_t old_end_offset = old_alloc->offset + + old_alloc->nb_clusters * s->cluster_size; + + if (end_offset < old_offset || offset > old_end_offset) { + /* No intersection */ + } else { + if (offset < old_offset) { + /* Stop at the start of a running allocation */ + nb_clusters = (old_offset - offset) >> s->cluster_bits; + } else { + nb_clusters = 0; + } + + if (nb_clusters == 0) { + /* Set dependency and wait for a callback */ + m->depends_on = old_alloc; + m->nb_clusters = 0; + *num = 0; + return 0; + } + } + } + if (!nb_clusters) { abort(); } @@ -741,6 +851,10 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, /* allocate a new cluster */ cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); + if (cluster_offset < 0) { + QLIST_REMOVE(m, next_in_flight); + return cluster_offset; + } /* save info needed for meta data update */ m->offset = offset; @@ -749,10 +863,11 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, out: m->nb_available = MIN(nb_clusters << (s->cluster_bits - 9), n_end); + m->cluster_offset = cluster_offset; *num = m->nb_available - n_start; - return cluster_offset; + return 0; } static int decompress_buffer(uint8_t *out_buf, int out_buf_size, @@ -782,8 +897,9 @@ static int decompress_buffer(uint8_t *out_buf, int out_buf_size, return 0; } -int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) +int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) { + BDRVQcowState *s = bs->opaque; int ret, csize, nb_csectors, sector_offset; uint64_t coffset; @@ -792,7 +908,8 @@ int qcow2_decompress_cluster(BDRVQcowState *s, uint64_t cluster_offset) nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; sector_offset = coffset & 511; csize = nb_csectors * 512 - sector_offset; - ret = bdrv_read(s->hd, coffset >> 9, s->cluster_data, nb_csectors); + BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, nb_csectors); if (ret < 0) { return -1; } |