diff options
author | Ot ten Thije <ottenthije@google.com> | 2010-07-29 10:26:01 +0100 |
---|---|---|
committer | Ot ten Thije <ottenthije@google.com> | 2010-07-29 16:16:29 +0100 |
commit | 7fd67eba0b961d94a5d6baa8e3c3de37b729f738 (patch) | |
tree | 7c73ed8c8452f90b816689b14f691297241659ba /block | |
parent | 8acd2219729896fea80a363076f31e64c0b54e12 (diff) | |
download | external_qemu-7fd67eba0b961d94a5d6baa8e3c3de37b729f738.zip external_qemu-7fd67eba0b961d94a5d6baa8e3c3de37b729f738.tar.gz external_qemu-7fd67eba0b961d94a5d6baa8e3c3de37b729f738.tar.bz2 |
Fixed rudimentary suspend/resume.
This patch replaces the failing asynchronous I/O code in the qcow2
driver with a working synchronous implementation and fixes another
minor bug in iteration over QField structs. As a result, the VM can
now be suspended and resumed with the savevm and loadvm commands,
albeit with the following (major) caveats:
1. The ADBd and other network services do not resume correctly.
2. Normal SD card usage is blocked, since snapshots are written to a
qcow2 image that must (for now) be mounted on hda.
3. Resume only works after the emulator is fully booted. It is not
(yet) possible to load an image immediately after the machine
started.
The synchronous I/O code is ported from commit ef845c3b in mainline
QEMU, which addresses a similar issue. Further upstream mainline also
introduces a fix for the async I/O, but that patch is quite involved,
and for now this relatively simple fix works.
Change-Id: I660c19c22316aecdd7dcae357e66da20e0ab6d80
Diffstat (limited to 'block')
-rw-r--r-- | block/qcow2-cluster.c | 12 | ||||
-rw-r--r-- | block/qcow2.c | 70 | ||||
-rw-r--r-- | block/qcow2.h | 7 |
3 files changed, 80 insertions, 9 deletions
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index d349655..fdedf17 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -306,8 +306,8 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, } -static int qcow_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) +int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, + int nb_sectors) { BDRVQcowState *s = bs->opaque; int ret, index_in_cluster, n, n1; @@ -358,7 +358,7 @@ static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, n = n_end - n_start; if (n <= 0) return 0; - ret = qcow_read(bs, start_sect + n_start, s->cluster_data, n); + ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); if (ret < 0) return ret; if (s->crypt_method) { @@ -732,6 +732,12 @@ uint64_t qcow2_alloc_cluster_offset(BlockDriverState *bs, } nb_clusters = i; + if (!nb_clusters) { + abort(); + } + + QLIST_INSERT_HEAD(&s->cluster_allocs, m, next_in_flight); + /* allocate a new cluster */ cluster_offset = qcow2_alloc_clusters(bs, nb_clusters * s->cluster_size); diff --git a/block/qcow2.c b/block/qcow2.c index 9acbddf..aa8ff35 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -230,6 +230,8 @@ static int qcow_open(BlockDriverState *bs, const char *filename, int flags) if (qcow2_refcount_init(bs) < 0) goto fail; + QLIST_INIT(&s->cluster_allocs); + /* read qcow2 extensions */ if (header.backing_file_offset) ext_end = header.backing_file_offset; @@ -826,6 +828,54 @@ static int qcow_make_empty(BlockDriverState *bs) return 0; } +/** + * Write data synchronously + */ +static int qcow2_write(BlockDriverState *bs, int64_t sector_num, + const uint8_t *buf, int nb_sectors) +{ + BDRVQcowState *s = bs->opaque; + int ret, index_in_cluster, n; + uint64_t cluster_offset; + int n_end; + QCowL2Meta l2meta; + + while (nb_sectors > 0) { + memset(&l2meta, 0, sizeof(l2meta)); + + index_in_cluster = sector_num & (s->cluster_sectors - 1); + n_end = index_in_cluster + nb_sectors; + if (s->crypt_method && + n_end > QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors) + n_end = QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors; + cluster_offset = qcow2_alloc_cluster_offset(bs, sector_num << 9, + index_in_cluster, + n_end, &n, &l2meta); + if (!cluster_offset) + return -1; + if (s->crypt_method) { + qcow2_encrypt_sectors(s, sector_num, s->cluster_data, buf, n, 1, + &s->aes_encrypt_key); + ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, + s->cluster_data, n * 512); + } else { + ret = bdrv_pwrite(s->hd, cluster_offset + index_in_cluster * 512, buf, n * 512); + } + if (ret != n * 512 || qcow2_alloc_cluster_link_l2(bs, cluster_offset, &l2meta) < 0) { + qcow2_free_any_clusters(bs, cluster_offset, l2meta.nb_clusters); + return -1; + } + nb_sectors -= n; + sector_num += n; + buf += n * 512; + if (l2meta.nb_clusters != 0) { + QLIST_REMOVE(&l2meta, next_in_flight); + } + } + s->cluster_cache_offset = -1; /* disable compressed cache */ + return 0; +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static int qcow_write_compressed(BlockDriverState *bs, int64_t sector_num, @@ -901,12 +951,16 @@ static void qcow_flush(BlockDriverState *bs) bdrv_flush(s->hd); } +static int64_t qcow_vm_state_offset(BDRVQcowState *s) +{ + return (int64_t)s->l1_vm_state_index << (s->cluster_bits + s->l2_bits); +} + static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) { BDRVQcowState *s = bs->opaque; bdi->cluster_size = s->cluster_size; - bdi->vm_state_offset = (int64_t)s->l1_vm_state_index << - (s->cluster_bits + s->l2_bits); + bdi->vm_state_offset = qcow_vm_state_offset(s); return 0; } @@ -939,10 +993,11 @@ static void dump_refcounts(BlockDriverState *bs) static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size) { + BDRVQcowState *s = bs->opaque; int growable = bs->growable; bs->growable = 1; - bdrv_pwrite(bs, pos, buf, size); + bdrv_pwrite(bs, qcow_vm_state_offset(s) + pos, buf, size); bs->growable = growable; return size; @@ -951,11 +1006,12 @@ static int qcow_put_buffer(BlockDriverState *bs, const uint8_t *buf, static int qcow_get_buffer(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { + BDRVQcowState *s = bs->opaque; int growable = bs->growable; int ret; bs->growable = 1; - ret = bdrv_pread(bs, pos, buf, size); + ret = bdrv_pread(bs, qcow_vm_state_offset(s) + pos, buf, size); bs->growable = growable; return ret; @@ -1002,8 +1058,10 @@ static BlockDriver bdrv_qcow2 = { .bdrv_set_key = qcow_set_key, .bdrv_make_empty = qcow_make_empty, - .bdrv_aio_readv = qcow_aio_readv, - .bdrv_aio_writev = qcow_aio_writev, + .bdrv_read = qcow2_read, + .bdrv_write = qcow2_write, + .bdrv_aio_readv = qcow_aio_readv, + .bdrv_aio_writev = qcow_aio_writev, .bdrv_write_compressed = qcow_write_compressed, .bdrv_snapshot_create = qcow2_snapshot_create, diff --git a/block/qcow2.h b/block/qcow2.h index d734003..542292d 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -94,6 +94,7 @@ typedef struct BDRVQcowState { uint8_t *cluster_cache; uint8_t *cluster_data; uint64_t cluster_cache_offset; + QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs; uint64_t *refcount_table; uint64_t refcount_table_offset; @@ -131,6 +132,9 @@ typedef struct QCowL2Meta int n_start; int nb_available; int nb_clusters; + struct QCowL2Meta *depends_on; + + QLIST_ENTRY(QCowL2Meta) next_in_flight; } QCowL2Meta; static inline int size_to_clusters(BDRVQcowState *s, int64_t size) @@ -191,6 +195,9 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, uint64_t cluster_offset, QCowL2Meta *m); +int qcow2_read(BlockDriverState *bs, int64_t sector_num, uint8_t *buf, + int nb_sectors); + /* qcow2-snapshot.c functions */ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info); int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id); |