diff options
author | Marcelo Tosatti <mtosatti@redhat.com> | 2010-02-25 23:20:30 -0300 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2010-02-25 23:20:30 -0300 |
commit | 25de3b156c5e35b1a46acccd8293212d5d9cf231 (patch) | |
tree | b4d7d29e8cbf87896dad45d6afd435556a94106a /block | |
parent | Merge commit 'c502715a74675b3554cf7bcd684b82c9733ecfae' into stable-0.12-merge (diff) | |
parent | cirrus: Properly re-register cirrus_linear_io_addr on vram unmap (diff) | |
download | qemu-kvm-25de3b156c5e35b1a46acccd8293212d5d9cf231.tar.gz qemu-kvm-25de3b156c5e35b1a46acccd8293212d5d9cf231.tar.bz2 qemu-kvm-25de3b156c5e35b1a46acccd8293212d5d9cf231.zip |
Merge commit '299e0bc52a5d56ff89ad8d7d09c82233cd8ccb6a' into stable-0.12-merge
* commit '299e0bc52a5d56ff89ad8d7d09c82233cd8ccb6a':
cirrus: Properly re-register cirrus_linear_io_addr on vram unmap
qcow2: Don't ignore qcow2_alloc_clusters return value
qcow2: Don't ignore update_refcount return value
qcow2: Allow updating no refcounts
qcow2: Improve error handling in update_refcount
qcow2: Fix error handling in grow_refcount_table
block: Return original error codes in bdrv_pread/write
qcow2: Return 0/-errno in qcow2_alloc_cluster_offset
qcow2: Return 0/-errno in get_cluster_table
qcow2: Fix error handling in qcow_save_vmstate
qcow2: Fix error handling in qcow2_grow_l1_table
win32/sdl: Fix toggle full screen
win32: pair qemu_memalign() with qemu_vfree()
vnc_refresh: calling vnc_update_client might free vs
Musicpal: Fix descriptor walk in eth_send
Musicpal: Fix wm8750 I2C address
fix savevm command without id or tag
reduce number of reinjects on ACK
QMP: Fix asynchronous events delivery
Conflicts:
hw/cirrus_vga.c
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/qcow2-cluster.c | 87 | ||||
-rw-r--r-- | block/qcow2-refcount.c | 84 | ||||
-rw-r--r-- | block/qcow2-snapshot.c | 11 | ||||
-rw-r--r-- | block/qcow2.c | 41 | ||||
-rw-r--r-- | block/qcow2.h | 4 | ||||
-rw-r--r-- | block/raw-posix.c | 2 |
6 files changed, 159 insertions, 70 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index f88118cdc..4e30d161a 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -33,7 +33,7 @@ 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; @@ -55,6 +55,10 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) /* write new table (align to cluster) */ 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; + } for(i = 0; i < s->l1_size; i++) new_l1_table[i] = cpu_to_be64(new_l1_table[i]); @@ -67,9 +71,10 @@ int qcow2_grow_l1_table(BlockDriverState *bs, int min_size) /* set new 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(s->hd, offsetof(QCowHeader, l1_size), data,sizeof(data)); + if (ret != sizeof(data)) { 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; @@ -77,8 +82,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 < 0 ? ret : -EIO; } void qcow2_l2_cache_reset(BlockDriverState *bs) @@ -220,6 +226,9 @@ static uint64_t *l2_allocate(BlockDriverState *bs, int l1_index) /* allocate a new l2 entry */ l2_offset = qcow2_alloc_clusters(bs, s->l2_size * sizeof(uint64_t)); + if (l2_offset < 0) { + return NULL; + } /* update the L1 entry */ @@ -479,8 +488,8 @@ 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, @@ -496,8 +505,9 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, 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]; @@ -507,14 +517,16 @@ static int get_cluster_table(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; + if (l2_table == NULL) { + return -EIO; + } } 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; + if (l2_table == NULL) { + return -EIO; + } l2_offset = s->l1_table[l1_index] & ~QCOW_OFLAG_COPIED; } @@ -526,7 +538,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; } /* @@ -552,8 +564,9 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, 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) @@ -563,6 +576,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); @@ -605,12 +622,12 @@ static int write_l2_entries(BDRVQcowState *s, uint64_t *l2_table, 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; @@ -633,10 +650,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 @@ -670,16 +688,22 @@ 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, @@ -687,13 +711,15 @@ uint64_t qcow2_alloc_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; 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); @@ -709,6 +735,7 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, cluster_offset &= ~QCOW_OFLAG_COPIED; m->nb_clusters = 0; + m->depends_on = NULL; goto out; } @@ -779,6 +806,9 @@ 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) { + return cluster_offset; + } /* save info needed for meta data update */ m->offset = offset; @@ -787,10 +817,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, diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 54b19f86d..c2a5c0471 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -168,9 +168,12 @@ static int grow_refcount_table(BlockDriverState *bs, int min_size) cpu_to_be64w((uint64_t*)data, table_offset); cpu_to_be32w((uint32_t*)(data + 8), refcount_table_clusters); - if (bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset), - data, sizeof(data)) != sizeof(data)) + ret = bdrv_pwrite(s->hd, offsetof(QCowHeader, refcount_table_offset), + data, sizeof(data)); + if (ret != sizeof(data)) { goto fail; + } + qemu_free(s->refcount_table); old_table_offset = s->refcount_table_offset; old_table_size = s->refcount_table_size; @@ -183,7 +186,7 @@ static int grow_refcount_table(BlockDriverState *bs, int min_size) return 0; fail: qemu_free(new_table); - return -EIO; + return ret < 0 ? ret : -EIO; } @@ -266,22 +269,26 @@ static int write_refcount_block_entries(BDRVQcowState *s, } /* XXX: cache several refcount block clusters ? */ -static int update_refcount(BlockDriverState *bs, - int64_t offset, int64_t length, - int addend) +static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, + int64_t offset, int64_t length, int addend) { BDRVQcowState *s = bs->opaque; int64_t start, last, cluster_offset; int64_t refcount_block_offset = 0; int64_t table_index = -1, old_table_index; int first_index = -1, last_index = -1; + int ret; #ifdef DEBUG_ALLOC2 printf("update_refcount: offset=%" PRId64 " size=%" PRId64 " addend=%d\n", offset, length, addend); #endif - if (length <= 0) + if (length < 0) { return -EINVAL; + } else if (length == 0) { + return 0; + } + start = offset & ~(s->cluster_size - 1); last = (offset + length - 1) & ~(s->cluster_size - 1); for(cluster_offset = start; cluster_offset <= last; @@ -289,6 +296,7 @@ static int update_refcount(BlockDriverState *bs, { int block_index, refcount; int64_t cluster_index = cluster_offset >> s->cluster_bits; + int64_t new_block; /* Only write refcount block to disk when we are done with it */ old_table_index = table_index; @@ -306,10 +314,12 @@ static int update_refcount(BlockDriverState *bs, } /* Load the refcount block and allocate it if needed */ - refcount_block_offset = alloc_refcount_block(bs, cluster_index); - if (refcount_block_offset < 0) { - return refcount_block_offset; + new_block = alloc_refcount_block(bs, cluster_index); + if (new_block < 0) { + ret = new_block; + goto fail; } + refcount_block_offset = new_block; /* we can update the count and save it */ block_index = cluster_index & @@ -323,24 +333,38 @@ static int update_refcount(BlockDriverState *bs, refcount = be16_to_cpu(s->refcount_block_cache[block_index]); refcount += addend; - if (refcount < 0 || refcount > 0xffff) - return -EINVAL; + if (refcount < 0 || refcount > 0xffff) { + ret = -EINVAL; + goto fail; + } if (refcount == 0 && cluster_index < s->free_cluster_index) { s->free_cluster_index = cluster_index; } s->refcount_block_cache[block_index] = cpu_to_be16(refcount); } + ret = 0; +fail: + /* Write last changed block to disk */ if (refcount_block_offset != 0) { if (write_refcount_block_entries(s, refcount_block_offset, first_index, last_index) < 0) { - return -EIO; + return ret < 0 ? ret : -EIO; } } - return 0; + /* + * Try do undo any updates if an error is returned (This may succeed in + * some cases like ENOSPC for allocating a new refcount block) + */ + if (ret < 0) { + int dummy; + dummy = update_refcount(bs, offset, cluster_offset - offset, -addend); + } + + return ret; } /* addend must be 1 or -1 */ @@ -390,9 +414,13 @@ retry: int64_t qcow2_alloc_clusters(BlockDriverState *bs, int64_t size) { int64_t offset; + int ret; offset = alloc_clusters_noref(bs, size); - update_refcount(bs, offset, size, 1); + ret = update_refcount(bs, offset, size, 1); + if (ret < 0) { + return ret; + } return offset; } @@ -407,6 +435,9 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) assert(size > 0 && size <= s->cluster_size); if (s->free_byte_offset == 0) { s->free_byte_offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (s->free_byte_offset < 0) { + return s->free_byte_offset; + } } redo: free_in_cluster = s->cluster_size - @@ -422,6 +453,9 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) update_cluster_refcount(bs, offset >> s->cluster_bits, 1); } else { offset = qcow2_alloc_clusters(bs, s->cluster_size); + if (offset < 0) { + return offset; + } cluster_offset = s->free_byte_offset & ~(s->cluster_size - 1); if ((cluster_offset + s->cluster_size) == offset) { /* we are lucky: contiguous data */ @@ -439,7 +473,13 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) void qcow2_free_clusters(BlockDriverState *bs, int64_t offset, int64_t size) { - update_refcount(bs, offset, size, -1); + int ret; + + ret = update_refcount(bs, offset, size, -1); + if (ret < 0) { + fprintf(stderr, "qcow2_free_clusters failed: %s\n", strerror(-ret)); + abort(); + } } /* @@ -549,9 +589,15 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, if (offset & QCOW_OFLAG_COMPRESSED) { nb_csectors = ((offset >> s->csize_shift) & s->csize_mask) + 1; - if (addend != 0) - update_refcount(bs, (offset & s->cluster_offset_mask) & ~511, - nb_csectors * 512, addend); + if (addend != 0) { + int ret; + ret = update_refcount(bs, + (offset & s->cluster_offset_mask) & ~511, + nb_csectors * 512, addend); + if (ret < 0) { + goto fail; + } + } /* compressed clusters are never modified */ refcount = 2; } else { diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index d63c7e17d..8ddaea23b 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -139,6 +139,9 @@ static int qcow_write_snapshots(BlockDriverState *bs) snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); offset = snapshots_offset; + if (offset < 0) { + return offset; + } for(i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; @@ -235,6 +238,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) QCowSnapshot *snapshots1, sn1, *sn = &sn1; int i, ret; uint64_t *l1_table = NULL; + int64_t l1_table_offset; memset(sn, 0, sizeof(*sn)); @@ -263,7 +267,12 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) goto fail; /* create the L1 table of the snapshot */ - sn->l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); + if (l1_table_offset < 0) { + goto fail; + } + + sn->l1_table_offset = l1_table_offset; sn->l1_size = s->l1_size; if (s->l1_size != 0) { diff --git a/block/qcow2.c b/block/qcow2.c index 984264b3e..4ae8f193d 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -561,7 +561,7 @@ static void qcow_aio_write_cb(void *opaque, int ret) acb->hd_aiocb = NULL; if (ret >= 0) { - ret = qcow2_alloc_cluster_link_l2(bs, acb->cluster_offset, &acb->l2meta); + ret = qcow2_alloc_cluster_link_l2(bs, &acb->l2meta); } run_dependent_requests(&acb->l2meta); @@ -585,21 +585,23 @@ static void qcow_aio_write_cb(void *opaque, int ret) n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; - acb->cluster_offset = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, - index_in_cluster, - n_end, &acb->n, &acb->l2meta); + ret = qcow2_alloc_cluster_offset(bs, acb->sector_num << 9, + index_in_cluster, n_end, &acb->n, &acb->l2meta); + if (ret < 0) { + goto done; + } + + acb->cluster_offset = acb->l2meta.cluster_offset; /* Need to wait for another request? If so, we are done for now. */ - if (!acb->cluster_offset && acb->l2meta.depends_on != NULL) { + if (acb->l2meta.nb_clusters == 0 && acb->l2meta.depends_on != NULL) { QLIST_INSERT_HEAD(&acb->l2meta.depends_on->dependent_requests, acb, next_depend); return; } - if (!acb->cluster_offset || (acb->cluster_offset & 511) != 0) { - ret = -EIO; - goto done; - } + assert((acb->cluster_offset & 511) == 0); + if (s->crypt_method) { if (!acb->cluster_data) { acb->cluster_data = qemu_mallocz(QCOW_MAX_CRYPT_CLUSTERS * @@ -683,27 +685,27 @@ static int get_bits_from_size(size_t size) static int preallocate(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; - uint64_t cluster_offset = 0; uint64_t nb_sectors; uint64_t offset; int num; + int ret; QCowL2Meta meta; nb_sectors = bdrv_getlength(bs) >> 9; offset = 0; QLIST_INIT(&meta.dependent_requests); + meta.cluster_offset = 0; while (nb_sectors) { num = MIN(nb_sectors, INT_MAX >> 9); - cluster_offset = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, - &meta); + ret = qcow2_alloc_cluster_offset(bs, offset, 0, num, &num, &meta); - if (cluster_offset == 0) { + if (ret < 0) { return -1; } - if (qcow2_alloc_cluster_link_l2(bs, cluster_offset, &meta) < 0) { - qcow2_free_any_clusters(bs, cluster_offset, meta.nb_clusters); + if (qcow2_alloc_cluster_link_l2(bs, &meta) < 0) { + qcow2_free_any_clusters(bs, meta.cluster_offset, meta.nb_clusters); return -1; } @@ -722,10 +724,10 @@ static int preallocate(BlockDriverState *bs) * all of the allocated clusters (otherwise we get failing reads after * EOF). Extend the image to the last allocated sector. */ - if (cluster_offset != 0) { + if (meta.cluster_offset != 0) { uint8_t buf[512]; memset(buf, 0, 512); - bdrv_write(s->hd, (cluster_offset >> 9) + num - 1, buf, 1); + bdrv_write(s->hd, (meta.cluster_offset >> 9) + num - 1, buf, 1); } return 0; @@ -1056,12 +1058,13 @@ static int qcow_save_vmstate(BlockDriverState *bs, const uint8_t *buf, { BDRVQcowState *s = bs->opaque; int growable = bs->growable; + int ret; bs->growable = 1; - bdrv_pwrite(bs, qcow_vm_state_offset(s) + pos, buf, size); + ret = bdrv_pwrite(bs, qcow_vm_state_offset(s) + pos, buf, size); bs->growable = growable; - return size; + return ret; } static int qcow_load_vmstate(BlockDriverState *bs, uint8_t *buf, diff --git a/block/qcow2.h b/block/qcow2.h index 26ab5d952..d9ea6abc5 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -135,6 +135,7 @@ struct QCowAIOCB; typedef struct QCowL2Meta { uint64_t offset; + uint64_t cluster_offset; int n_start; int nb_available; int nb_clusters; @@ -199,8 +200,7 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t offset, int compressed_size); -int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, - QCowL2Meta *m); +int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); diff --git a/block/raw-posix.c b/block/raw-posix.c index 6dcc65189..c204cf94e 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -597,7 +597,7 @@ static void raw_close(BlockDriverState *bs) close(s->fd); s->fd = -1; if (s->aligned_buf != NULL) - qemu_free(s->aligned_buf); + qemu_vfree(s->aligned_buf); } } |