diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/blkverify.c | 24 | ||||
-rw-r--r-- | block/cow.c | 92 | ||||
-rw-r--r-- | block/curl.c | 4 | ||||
-rw-r--r-- | block/nbd.c | 4 | ||||
-rw-r--r-- | block/qcow.c | 24 | ||||
-rw-r--r-- | block/qcow2-cluster.c | 115 | ||||
-rw-r--r-- | block/qcow2-refcount.c | 7 | ||||
-rw-r--r-- | block/qcow2-snapshot.c | 362 | ||||
-rw-r--r-- | block/qcow2.c | 33 | ||||
-rw-r--r-- | block/qcow2.h | 2 | ||||
-rw-r--r-- | block/qed-table.c | 24 | ||||
-rw-r--r-- | block/qed.c | 75 | ||||
-rw-r--r-- | block/raw-posix.c | 4 | ||||
-rw-r--r-- | block/rbd.c | 6 | ||||
-rw-r--r-- | block/sheepdog.c | 6 | ||||
-rw-r--r-- | block/vdi.c | 89 | ||||
-rw-r--r-- | block/vmdk.c | 24 | ||||
-rw-r--r-- | block/vpc.c | 18 | ||||
-rw-r--r-- | block/vvfat.c | 21 |
19 files changed, 559 insertions, 375 deletions
diff --git a/block/blkverify.c b/block/blkverify.c index 483f3b3cfe..4ca8584b88 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -310,14 +310,10 @@ static BlockDriverAIOCB *blkverify_aio_readv(BlockDriverState *bs, qemu_iovec_init(&acb->raw_qiov, acb->qiov->niov); blkverify_iovec_clone(&acb->raw_qiov, qiov, acb->buf); - if (!bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, - blkverify_aio_cb, acb)) { - blkverify_aio_cb(acb, -EIO); - } - if (!bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, - blkverify_aio_cb, acb)) { - blkverify_aio_cb(acb, -EIO); - } + bdrv_aio_readv(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); + bdrv_aio_readv(bs->file, sector_num, &acb->raw_qiov, nb_sectors, + blkverify_aio_cb, acb); return &acb->common; } @@ -329,14 +325,10 @@ static BlockDriverAIOCB *blkverify_aio_writev(BlockDriverState *bs, BlkverifyAIOCB *acb = blkverify_aio_get(bs, true, sector_num, qiov, nb_sectors, cb, opaque); - if (!bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, - blkverify_aio_cb, acb)) { - blkverify_aio_cb(acb, -EIO); - } - if (!bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, - blkverify_aio_cb, acb)) { - blkverify_aio_cb(acb, -EIO); - } + bdrv_aio_writev(s->test_file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); + bdrv_aio_writev(bs->file, sector_num, qiov, nb_sectors, + blkverify_aio_cb, acb); return &acb->common; } diff --git a/block/cow.c b/block/cow.c index 089d395c40..bb5927c6aa 100644 --- a/block/cow.c +++ b/block/cow.c @@ -64,15 +64,26 @@ static int cow_open(BlockDriverState *bs, int flags) struct cow_header_v2 cow_header; int bitmap_size; int64_t size; + int ret; /* see if it is a cow image */ - if (bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)) != - sizeof(cow_header)) { + ret = bdrv_pread(bs->file, 0, &cow_header, sizeof(cow_header)); + if (ret < 0) { + goto fail; + } + + if (be32_to_cpu(cow_header.magic) != COW_MAGIC) { + ret = -EINVAL; goto fail; } - if (be32_to_cpu(cow_header.magic) != COW_MAGIC || - be32_to_cpu(cow_header.version) != COW_VERSION) { + if (be32_to_cpu(cow_header.version) != COW_VERSION) { + char version[64]; + snprintf(version, sizeof(version), + "COW version %d", cow_header.version); + qerror_report(QERR_UNKNOWN_BLOCK_FORMAT_FEATURE, + bs->device_name, "cow", version); + ret = -ENOTSUP; goto fail; } @@ -88,11 +99,11 @@ static int cow_open(BlockDriverState *bs, int flags) qemu_co_mutex_init(&s->lock); return 0; fail: - return -1; + return ret; } /* - * XXX(hch): right now these functions are extremly ineffcient. + * XXX(hch): right now these functions are extremely ineffcient. * We should just read the whole bitmap we'll need in one go instead. */ static inline int cow_set_bit(BlockDriverState *bs, int64_t bitnum) @@ -132,8 +143,8 @@ static inline int is_bit_set(BlockDriverState *bs, int64_t bitnum) /* Return true if first block has been changed (ie. current version is * in COW file). Set the number of continuous blocks for which that * is true. */ -static int cow_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *num_same) +static int coroutine_fn cow_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *num_same) { int changed; @@ -171,28 +182,30 @@ static int cow_update_bitmap(BlockDriverState *bs, int64_t sector_num, return error; } -static int cow_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) +static int coroutine_fn cow_read(BlockDriverState *bs, int64_t sector_num, + uint8_t *buf, int nb_sectors) { BDRVCowState *s = bs->opaque; int ret, n; while (nb_sectors > 0) { - if (cow_is_allocated(bs, sector_num, nb_sectors, &n)) { + if (bdrv_co_is_allocated(bs, sector_num, nb_sectors, &n)) { ret = bdrv_pread(bs->file, s->cow_sectors_offset + sector_num * 512, buf, n * 512); - if (ret != n * 512) - return -1; + if (ret < 0) { + return ret; + } } else { if (bs->backing_hd) { /* read from the base image */ ret = bdrv_read(bs->backing_hd, sector_num, buf, n); - if (ret < 0) - return -1; + if (ret < 0) { + return ret; + } } else { - memset(buf, 0, n * 512); - } + memset(buf, 0, n * 512); + } } nb_sectors -= n; sector_num += n; @@ -220,8 +233,9 @@ static int cow_write(BlockDriverState *bs, int64_t sector_num, ret = bdrv_pwrite(bs->file, s->cow_sectors_offset + sector_num * 512, buf, nb_sectors * 512); - if (ret != nb_sectors * 512) - return -1; + if (ret < 0) { + return ret; + } return cow_update_bitmap(bs, sector_num, nb_sectors); } @@ -243,12 +257,12 @@ static void cow_close(BlockDriverState *bs) static int cow_create(const char *filename, QEMUOptionParameter *options) { - int fd, cow_fd; struct cow_header_v2 cow_header; struct stat st; int64_t image_sectors = 0; const char *image_filename = NULL; int ret; + BlockDriverState *cow_bs; /* Read out options */ while (options && options->name) { @@ -260,10 +274,16 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) options++; } - cow_fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, - 0644); - if (cow_fd < 0) - return -errno; + ret = bdrv_create_file(filename, options); + if (ret < 0) { + return ret; + } + + ret = bdrv_file_open(&cow_bs, filename, BDRV_O_RDWR); + if (ret < 0) { + return ret; + } + memset(&cow_header, 0, sizeof(cow_header)); cow_header.magic = cpu_to_be32(COW_MAGIC); cow_header.version = cpu_to_be32(COW_VERSION); @@ -271,16 +291,9 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) /* Note: if no file, we put a dummy mtime */ cow_header.mtime = cpu_to_be32(0); - fd = open(image_filename, O_RDONLY | O_BINARY); - if (fd < 0) { - close(cow_fd); + if (stat(image_filename, &st) != 0) { goto mtime_fail; } - if (fstat(fd, &st) != 0) { - close(fd); - goto mtime_fail; - } - close(fd); cow_header.mtime = cpu_to_be32(st.st_mtime); mtime_fail: pstrcpy(cow_header.backing_file, sizeof(cow_header.backing_file), @@ -288,21 +301,20 @@ static int cow_create(const char *filename, QEMUOptionParameter *options) } cow_header.sectorsize = cpu_to_be32(512); cow_header.size = cpu_to_be64(image_sectors * 512); - ret = qemu_write_full(cow_fd, &cow_header, sizeof(cow_header)); - if (ret != sizeof(cow_header)) { - ret = -errno; + ret = bdrv_pwrite(cow_bs, 0, &cow_header, sizeof(cow_header)); + if (ret < 0) { goto exit; } /* resize to include at least all the bitmap */ - ret = ftruncate(cow_fd, sizeof(cow_header) + ((image_sectors + 7) >> 3)); - if (ret) { - ret = -errno; + ret = bdrv_truncate(cow_bs, + sizeof(cow_header) + ((image_sectors + 7) >> 3)); + if (ret < 0) { goto exit; } exit: - close(cow_fd); + bdrv_delete(cow_bs); return ret; } @@ -337,7 +349,7 @@ static BlockDriver bdrv_cow = { .bdrv_read = cow_co_read, .bdrv_write = cow_co_write, .bdrv_co_flush_to_disk = cow_co_flush, - .bdrv_is_allocated = cow_is_allocated, + .bdrv_co_is_allocated = cow_co_is_allocated, .create_options = cow_create_options, }; diff --git a/block/curl.c b/block/curl.c index 4209ac88ce..e9102e3e20 100644 --- a/block/curl.c +++ b/block/curl.c @@ -509,10 +509,6 @@ static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs, acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque); - if (!acb) { - return NULL; - } - acb->qiov = qiov; acb->sector_num = sector_num; acb->nb_sectors = nb_sectors; diff --git a/block/nbd.c b/block/nbd.c index 882b2dc84a..95212dac64 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -189,7 +189,7 @@ static int nbd_read(BlockDriverState *bs, int64_t sector_num, request.type = NBD_CMD_READ; request.handle = (uint64_t)(intptr_t)bs; - request.from = sector_num * 512;; + request.from = sector_num * 512; request.len = nb_sectors * 512; if (nbd_send_request(s->sock, &request) == -1) @@ -219,7 +219,7 @@ static int nbd_write(BlockDriverState *bs, int64_t sector_num, request.type = NBD_CMD_WRITE; request.handle = (uint64_t)(intptr_t)bs; - request.from = sector_num * 512;; + request.from = sector_num * 512; request.len = nb_sectors * 512; if (nbd_send_request(s->sock, &request) == -1) diff --git a/block/qcow.c b/block/qcow.c index adecee06c9..b16955d764 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -26,6 +26,7 @@ #include "module.h" #include <zlib.h> #include "aes.h" +#include "migration.h" /**************************************************************/ /* QEMU COW block driver with compression and encryption support */ @@ -74,6 +75,7 @@ typedef struct BDRVQcowState { AES_KEY aes_encrypt_key; AES_KEY aes_decrypt_key; CoMutex lock; + Error *migration_blocker; } BDRVQcowState; static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); @@ -160,6 +162,12 @@ static int qcow_open(BlockDriverState *bs, int flags) bs->backing_file[len] = '\0'; } + /* Disable migration when qcow images are used */ + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "qcow", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + qemu_co_mutex_init(&s->lock); return 0; @@ -360,14 +368,16 @@ static uint64_t get_cluster_offset(BlockDriverState *bs, return cluster_offset; } -static int qcow_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int coroutine_fn qcow_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) { BDRVQcowState *s = bs->opaque; int index_in_cluster, n; uint64_t cluster_offset; + qemu_co_mutex_lock(&s->lock); cluster_offset = get_cluster_offset(bs, sector_num << 9, 0, 0, 0, 0); + qemu_co_mutex_unlock(&s->lock); index_in_cluster = sector_num & (s->cluster_sectors - 1); n = s->cluster_sectors - index_in_cluster; if (n > nb_sectors) @@ -425,7 +435,7 @@ static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) return 0; } -static int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, +static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; @@ -523,7 +533,7 @@ fail: goto done; } -static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, +static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; @@ -604,10 +614,14 @@ static int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, static void qcow_close(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; + g_free(s->l1_table); g_free(s->l2_cache); g_free(s->cluster_cache); g_free(s->cluster_data); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } static int qcow_create(const char *filename, QEMUOptionParameter *options) @@ -832,7 +846,7 @@ static BlockDriver bdrv_qcow = { .bdrv_co_readv = qcow_co_readv, .bdrv_co_writev = qcow_co_writev, .bdrv_co_flush_to_disk = qcow_co_flush, - .bdrv_is_allocated = qcow_is_allocated, + .bdrv_co_is_allocated = qcow_co_is_allocated, .bdrv_set_key = qcow_set_key, .bdrv_make_empty = qcow_make_empty, diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index f4e049fa90..07a2e936fd 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -289,89 +289,62 @@ void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num, } } - -static int qcow2_read(BlockDriverState *bs, int64_t sector_num, - uint8_t *buf, int nb_sectors) +static int coroutine_fn copy_sectors(BlockDriverState *bs, + uint64_t start_sect, + uint64_t cluster_offset, + int n_start, int n_end) { BDRVQcowState *s = bs->opaque; - int ret, index_in_cluster, n, n1; - uint64_t cluster_offset; - struct iovec iov; QEMUIOVector qiov; + struct iovec iov; + int n, ret; - while (nb_sectors > 0) { - n = nb_sectors; - - 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 */ - iov.iov_base = buf; - iov.iov_len = n * 512; - qemu_iovec_init_external(&qiov, &iov, 1); - - n1 = qcow2_backing_read1(bs->backing_hd, &qiov, sector_num, 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; - } - } else { - memset(buf, 0, 512 * n); - } - } else if (cluster_offset & QCOW_OFLAG_COMPRESSED) { - if (qcow2_decompress_cluster(bs, cluster_offset) < 0) - return -1; - memcpy(buf, s->cluster_cache + index_in_cluster * 512, 512 * n); - } else { - 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) { - qcow2_encrypt_sectors(s, sector_num, buf, buf, n, 0, - &s->aes_decrypt_key); - } - } - nb_sectors -= n; - sector_num += n; - buf += n * 512; + /* + * If this is the last cluster and it is only partially used, we must only + * copy until the end of the image, or bdrv_check_request will fail for the + * bdrv_read/write calls below. + */ + if (start_sect + n_end > bs->total_sectors) { + n_end = bs->total_sectors - start_sect; } - return 0; -} - -static int copy_sectors(BlockDriverState *bs, uint64_t start_sect, - uint64_t cluster_offset, int n_start, int n_end) -{ - BDRVQcowState *s = bs->opaque; - int n, ret; n = n_end - n_start; - if (n <= 0) + if (n <= 0) { return 0; + } + + iov.iov_len = n * BDRV_SECTOR_SIZE; + iov.iov_base = qemu_blockalign(bs, iov.iov_len); + + qemu_iovec_init_external(&qiov, &iov, 1); + BLKDBG_EVENT(bs->file, BLKDBG_COW_READ); - ret = qcow2_read(bs, start_sect + n_start, s->cluster_data, n); - if (ret < 0) - return ret; + + /* Call .bdrv_co_readv() directly instead of using the public block-layer + * interface. This avoids double I/O throttling and request tracking, + * which can lead to deadlock when block layer copy-on-read is enabled. + */ + ret = bs->drv->bdrv_co_readv(bs, start_sect + n_start, n, &qiov); + if (ret < 0) { + goto out; + } + if (s->crypt_method) { qcow2_encrypt_sectors(s, start_sect + n_start, - s->cluster_data, - s->cluster_data, n, 1, + iov.iov_base, iov.iov_base, n, 1, &s->aes_encrypt_key); } + BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); - ret = bdrv_write(bs->file, (cluster_offset >> 9) + n_start, - s->cluster_data, n); - if (ret < 0) - return ret; - return 0; + ret = bdrv_co_writev(bs->file, (cluster_offset >> 9) + n_start, n, &qiov); + if (ret < 0) { + goto out; + } + + ret = 0; +out: + qemu_vfree(iov.iov_base); + return ret; } @@ -620,7 +593,9 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) start_sect = (m->offset & ~(s->cluster_size - 1)) >> 9; if (m->n_start) { cow = true; + qemu_co_mutex_unlock(&s->lock); ret = copy_sectors(bs, start_sect, cluster_offset, 0, m->n_start); + qemu_co_mutex_lock(&s->lock); if (ret < 0) goto err; } @@ -628,8 +603,10 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m) if (m->nb_available & (s->cluster_sectors - 1)) { uint64_t end = m->nb_available & ~(uint64_t)(s->cluster_sectors - 1); cow = true; + qemu_co_mutex_unlock(&s->lock); ret = copy_sectors(bs, start_sect + end, cluster_offset + (end << 9), m->nb_available - end, s->cluster_sectors); + qemu_co_mutex_lock(&s->lock); if (ret < 0) goto err; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 9605367777..2db2ede3d1 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -700,6 +700,10 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, l2_table = NULL; l1_table = NULL; l1_size2 = l1_size * sizeof(uint64_t); + + /* WARNING: qcow2_snapshot_goto relies on this function not using the + * l1_table_offset when it is the current s->l1_table_offset! Be careful + * when changing this! */ if (l1_table_offset != s->l1_table_offset) { if (l1_size2 != 0) { l1_table = g_malloc0(align_offset(l1_size2, 512)); @@ -819,7 +823,8 @@ fail: qcow2_cache_set_writethrough(bs, s->refcount_block_cache, old_refcount_writethrough); - if (l1_modified) { + /* Update L1 only if it isn't deleted anyway (addend = -1) */ + if (addend >= 0 && l1_modified) { for(i = 0; i < l1_size; i++) cpu_to_be64s(&l1_table[i]); if (bdrv_pwrite_sync(bs->file, l1_table_offset, l1_table, diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index bdc33ba94c..7d3fde5a8a 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -46,6 +46,10 @@ typedef struct QEMU_PACKED QCowSnapshotHeader { /* name follows */ } QCowSnapshotHeader; +typedef struct QEMU_PACKED QCowSnapshotExtraData { + uint64_t vm_state_size_large; +} QCowSnapshotExtraData; + void qcow2_free_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; @@ -64,10 +68,12 @@ int qcow2_read_snapshots(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; QCowSnapshotHeader h; + QCowSnapshotExtraData extra; QCowSnapshot *sn; int i, id_str_size, name_size; int64_t offset; uint32_t extra_data_size; + int ret; if (!s->nb_snapshots) { s->snapshots = NULL; @@ -77,10 +83,15 @@ int qcow2_read_snapshots(BlockDriverState *bs) offset = s->snapshots_offset; s->snapshots = g_malloc0(s->nb_snapshots * sizeof(QCowSnapshot)); + for(i = 0; i < s->nb_snapshots; i++) { + /* Read statically sized part of the snapshot header */ offset = align_offset(offset, 8); - if (bdrv_pread(bs->file, offset, &h, sizeof(h)) != sizeof(h)) + ret = bdrv_pread(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { goto fail; + } + offset += sizeof(h); sn = s->snapshots + i; sn->l1_table_offset = be64_to_cpu(h.l1_table_offset); @@ -94,25 +105,43 @@ int qcow2_read_snapshots(BlockDriverState *bs) id_str_size = be16_to_cpu(h.id_str_size); name_size = be16_to_cpu(h.name_size); + /* Read extra data */ + ret = bdrv_pread(bs->file, offset, &extra, + MIN(sizeof(extra), extra_data_size)); + if (ret < 0) { + goto fail; + } offset += extra_data_size; + if (extra_data_size >= 8) { + sn->vm_state_size = be64_to_cpu(extra.vm_state_size_large); + } + + /* Read snapshot ID */ sn->id_str = g_malloc(id_str_size + 1); - if (bdrv_pread(bs->file, offset, sn->id_str, id_str_size) != id_str_size) + ret = bdrv_pread(bs->file, offset, sn->id_str, id_str_size); + if (ret < 0) { goto fail; + } offset += id_str_size; sn->id_str[id_str_size] = '\0'; + /* Read snapshot name */ sn->name = g_malloc(name_size + 1); - if (bdrv_pread(bs->file, offset, sn->name, name_size) != name_size) + ret = bdrv_pread(bs->file, offset, sn->name, name_size); + if (ret < 0) { goto fail; + } offset += name_size; sn->name[name_size] = '\0'; } + s->snapshots_size = offset - s->snapshots_offset; return 0; - fail: + +fail: qcow2_free_snapshots(bs); - return -1; + return ret; } /* add at the end of the file a new list of snapshots */ @@ -121,10 +150,14 @@ static int qcow2_write_snapshots(BlockDriverState *bs) BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; QCowSnapshotHeader h; + QCowSnapshotExtraData extra; int i, name_size, id_str_size, snapshots_size; - uint64_t data64; - uint32_t data32; + struct { + uint32_t nb_snapshots; + uint64_t snapshots_offset; + } QEMU_PACKED header_data; int64_t offset, snapshots_offset; + int ret; /* compute the size of the snapshots */ offset = 0; @@ -132,11 +165,13 @@ static int qcow2_write_snapshots(BlockDriverState *bs) sn = s->snapshots + i; offset = align_offset(offset, 8); offset += sizeof(h); + offset += sizeof(extra); offset += strlen(sn->id_str); offset += strlen(sn->name); } snapshots_size = offset; + /* Allocate space for the new snapshot list */ snapshots_offset = qcow2_alloc_clusters(bs, snapshots_size); bdrv_flush(bs->file); offset = snapshots_offset; @@ -144,49 +179,85 @@ static int qcow2_write_snapshots(BlockDriverState *bs) return offset; } + /* Write all snapshots to the new list */ for(i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; memset(&h, 0, sizeof(h)); h.l1_table_offset = cpu_to_be64(sn->l1_table_offset); h.l1_size = cpu_to_be32(sn->l1_size); - h.vm_state_size = cpu_to_be32(sn->vm_state_size); + /* If it doesn't fit in 32 bit, older implementations should treat it + * as a disk-only snapshot rather than truncate the VM state */ + if (sn->vm_state_size <= 0xffffffff) { + h.vm_state_size = cpu_to_be32(sn->vm_state_size); + } h.date_sec = cpu_to_be32(sn->date_sec); h.date_nsec = cpu_to_be32(sn->date_nsec); h.vm_clock_nsec = cpu_to_be64(sn->vm_clock_nsec); + h.extra_data_size = cpu_to_be32(sizeof(extra)); + + memset(&extra, 0, sizeof(extra)); + extra.vm_state_size_large = cpu_to_be64(sn->vm_state_size); id_str_size = strlen(sn->id_str); name_size = strlen(sn->name); h.id_str_size = cpu_to_be16(id_str_size); h.name_size = cpu_to_be16(name_size); offset = align_offset(offset, 8); - if (bdrv_pwrite_sync(bs->file, offset, &h, sizeof(h)) < 0) + + ret = bdrv_pwrite(bs->file, offset, &h, sizeof(h)); + if (ret < 0) { goto fail; + } offset += sizeof(h); - if (bdrv_pwrite_sync(bs->file, offset, sn->id_str, id_str_size) < 0) + + ret = bdrv_pwrite(bs->file, offset, &extra, sizeof(extra)); + if (ret < 0) { goto fail; + } + offset += sizeof(extra); + + ret = bdrv_pwrite(bs->file, offset, sn->id_str, id_str_size); + if (ret < 0) { + goto fail; + } offset += id_str_size; - if (bdrv_pwrite_sync(bs->file, offset, sn->name, name_size) < 0) + + ret = bdrv_pwrite(bs->file, offset, sn->name, name_size); + if (ret < 0) { goto fail; + } offset += name_size; } - /* update the various header fields */ - data64 = cpu_to_be64(snapshots_offset); - if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, snapshots_offset), - &data64, sizeof(data64)) < 0) + /* + * Update the header to point to the new snapshot table. This requires the + * new table and its refcounts to be stable on disk. + */ + ret = bdrv_flush(bs); + if (ret < 0) { goto fail; - data32 = cpu_to_be32(s->nb_snapshots); - if (bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), - &data32, sizeof(data32)) < 0) + } + + QEMU_BUILD_BUG_ON(offsetof(QCowHeader, snapshots_offset) != + offsetof(QCowHeader, nb_snapshots) + sizeof(header_data.nb_snapshots)); + + header_data.nb_snapshots = cpu_to_be32(s->nb_snapshots); + header_data.snapshots_offset = cpu_to_be64(snapshots_offset); + + ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, nb_snapshots), + &header_data, sizeof(header_data)); + if (ret < 0) { goto fail; + } /* free the old snapshot table */ qcow2_free_clusters(bs, s->snapshots_offset, s->snapshots_size); s->snapshots_offset = snapshots_offset; s->snapshots_size = snapshots_size; return 0; - fail: - return -1; + +fail: + return ret; } static void find_new_snapshot_id(BlockDriverState *bs, @@ -236,72 +307,92 @@ static int find_snapshot_by_id_or_name(BlockDriverState *bs, const char *name) int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) { BDRVQcowState *s = bs->opaque; - QCowSnapshot *snapshots1, sn1, *sn = &sn1; + QCowSnapshot *new_snapshot_list = NULL; + QCowSnapshot *old_snapshot_list = NULL; + QCowSnapshot sn1, *sn = &sn1; int i, ret; uint64_t *l1_table = NULL; int64_t l1_table_offset; memset(sn, 0, sizeof(*sn)); + /* Generate an ID if it wasn't passed */ if (sn_info->id_str[0] == '\0') { - /* compute a new id */ find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); } - /* check that the ID is unique */ - if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) + /* Check that the ID is unique */ + if (find_snapshot_by_id(bs, sn_info->id_str) >= 0) { return -ENOENT; + } + /* Populate sn with passed data */ sn->id_str = g_strdup(sn_info->id_str); - if (!sn->id_str) - goto fail; sn->name = g_strdup(sn_info->name); - if (!sn->name) - goto fail; + sn->vm_state_size = sn_info->vm_state_size; sn->date_sec = sn_info->date_sec; sn->date_nsec = sn_info->date_nsec; sn->vm_clock_nsec = sn_info->vm_clock_nsec; - ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); - if (ret < 0) - goto fail; - - /* create the L1 table of the snapshot */ + /* Allocate the L1 table of the snapshot and copy the current one there. */ l1_table_offset = qcow2_alloc_clusters(bs, s->l1_size * sizeof(uint64_t)); if (l1_table_offset < 0) { + ret = l1_table_offset; goto fail; } - bdrv_flush(bs->file); sn->l1_table_offset = l1_table_offset; sn->l1_size = s->l1_size; - if (s->l1_size != 0) { - l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); - } else { - l1_table = NULL; - } - + l1_table = g_malloc(s->l1_size * sizeof(uint64_t)); for(i = 0; i < s->l1_size; i++) { l1_table[i] = cpu_to_be64(s->l1_table[i]); } - if (bdrv_pwrite_sync(bs->file, sn->l1_table_offset, - l1_table, s->l1_size * sizeof(uint64_t)) < 0) + + ret = bdrv_pwrite(bs->file, sn->l1_table_offset, l1_table, + s->l1_size * sizeof(uint64_t)); + if (ret < 0) { goto fail; + } + g_free(l1_table); l1_table = NULL; - snapshots1 = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot)); + /* + * Increase the refcounts of all clusters and make sure everything is + * stable on disk before updating the snapshot table to contain a pointer + * to the new L1 table. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1); + if (ret < 0) { + goto fail; + } + + ret = bdrv_flush(bs); + if (ret < 0) { + goto fail; + } + + /* Append the new snapshot to the snapshot list */ + new_snapshot_list = g_malloc((s->nb_snapshots + 1) * sizeof(QCowSnapshot)); if (s->snapshots) { - memcpy(snapshots1, s->snapshots, s->nb_snapshots * sizeof(QCowSnapshot)); - g_free(s->snapshots); + memcpy(new_snapshot_list, s->snapshots, + s->nb_snapshots * sizeof(QCowSnapshot)); + old_snapshot_list = s->snapshots; } - s->snapshots = snapshots1; + s->snapshots = new_snapshot_list; s->snapshots[s->nb_snapshots++] = *sn; - if (qcow2_write_snapshots(bs) < 0) + ret = qcow2_write_snapshots(bs); + if (ret < 0) { + g_free(s->snapshots); + s->snapshots = old_snapshot_list; goto fail; + } + + g_free(old_snapshot_list); + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -309,10 +400,13 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } #endif return 0; - fail: + +fail: + g_free(sn->id_str); g_free(sn->name); g_free(l1_table); - return -1; + + return ret; } /* copy the snapshot 'snapshot_name' into the current disk image */ @@ -322,38 +416,92 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) QCowSnapshot *sn; int i, snapshot_index; int cur_l1_bytes, sn_l1_bytes; + int ret; + uint64_t *sn_l1_table = NULL; + /* Search the snapshot */ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); - if (snapshot_index < 0) + if (snapshot_index < 0) { return -ENOENT; + } sn = &s->snapshots[snapshot_index]; - if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, -1) < 0) - goto fail; - - if (qcow2_grow_l1_table(bs, sn->l1_size, true) < 0) + /* + * 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 + * current one must be padded with zeros. + */ + ret = qcow2_grow_l1_table(bs, sn->l1_size, true); + if (ret < 0) { goto fail; + } cur_l1_bytes = s->l1_size * sizeof(uint64_t); sn_l1_bytes = sn->l1_size * sizeof(uint64_t); - if (cur_l1_bytes > sn_l1_bytes) { - memset(s->l1_table + sn->l1_size, 0, cur_l1_bytes - sn_l1_bytes); + /* + * Copy the snapshot L1 table to the current L1 table. + * + * Before overwriting the old current L1 table on disk, make sure to + * increase all refcounts for the clusters referenced by the new one. + * Decrease the refcount referenced by the old one only when the L1 + * table is overwritten. + */ + sn_l1_table = g_malloc0(cur_l1_bytes); + + ret = bdrv_pread(bs->file, sn->l1_table_offset, sn_l1_table, sn_l1_bytes); + if (ret < 0) { + goto fail; } - /* copy the snapshot l1 table to the current l1 table */ - if (bdrv_pread(bs->file, sn->l1_table_offset, - s->l1_table, sn_l1_bytes) < 0) + ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, + sn->l1_size, 1); + if (ret < 0) { goto fail; - if (bdrv_pwrite_sync(bs->file, s->l1_table_offset, - s->l1_table, cur_l1_bytes) < 0) + } + + ret = bdrv_pwrite_sync(bs->file, s->l1_table_offset, sn_l1_table, + cur_l1_bytes); + if (ret < 0) { goto fail; + } + + /* + * Decrease refcount of clusters of current L1 table. + * + * At this point, the in-memory s->l1_table points to the old L1 table, + * whereas on disk we already have the new one. + * + * qcow2_update_snapshot_refcount special cases the current L1 table to use + * the in-memory data instead of really using the offset to load a new one, + * which is why this works. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, + s->l1_size, -1); + + /* + * 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++) { - be64_to_cpus(&s->l1_table[i]); + s->l1_table[i] = be64_to_cpu(sn_l1_table[i]); } - if (qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 1) < 0) + if (ret < 0) { + goto fail; + } + + g_free(sn_l1_table); + sn_l1_table = NULL; + + /* + * Update QCOW_OFLAG_COPIED in the active L1 table (it may have changed + * when we decreased the refcount of the old snapshot. + */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); + if (ret < 0) { goto fail; + } #ifdef DEBUG_ALLOC { @@ -362,39 +510,59 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) } #endif return 0; - fail: - return -EIO; + +fail: + g_free(sn_l1_table); + return ret; } int qcow2_snapshot_delete(BlockDriverState *bs, const char *snapshot_id) { BDRVQcowState *s = bs->opaque; - QCowSnapshot *sn; + QCowSnapshot sn; int snapshot_index, ret; + /* Search the snapshot */ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); - if (snapshot_index < 0) + if (snapshot_index < 0) { return -ENOENT; - sn = &s->snapshots[snapshot_index]; + } + sn = s->snapshots[snapshot_index]; - ret = qcow2_update_snapshot_refcount(bs, sn->l1_table_offset, sn->l1_size, -1); - if (ret < 0) + /* Remove it from the snapshot list */ + memmove(s->snapshots + snapshot_index, + s->snapshots + snapshot_index + 1, + (s->nb_snapshots - snapshot_index - 1) * sizeof(sn)); + s->nb_snapshots--; + ret = qcow2_write_snapshots(bs); + if (ret < 0) { return ret; - /* must update the copied flag on the current cluster offsets */ - ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); - if (ret < 0) + } + + /* + * The snapshot is now unused, clean up. If we fail after this point, we + * won't recover but just leak clusters. + */ + g_free(sn.id_str); + g_free(sn.name); + + /* + * Now decrease the refcounts of clusters referenced by the snapshot and + * free the L1 table. + */ + ret = qcow2_update_snapshot_refcount(bs, sn.l1_table_offset, + sn.l1_size, -1); + if (ret < 0) { return ret; - qcow2_free_clusters(bs, sn->l1_table_offset, sn->l1_size * sizeof(uint64_t)); + } + qcow2_free_clusters(bs, sn.l1_table_offset, sn.l1_size * sizeof(uint64_t)); - g_free(sn->id_str); - g_free(sn->name); - memmove(sn, sn + 1, (s->nb_snapshots - snapshot_index - 1) * sizeof(*sn)); - s->nb_snapshots--; - ret = qcow2_write_snapshots(bs); + /* must update the copied flag on the current cluster offsets */ + ret = qcow2_update_snapshot_refcount(bs, s->l1_table_offset, s->l1_size, 0); if (ret < 0) { - /* XXX: restore snapshot if error ? */ return ret; } + #ifdef DEBUG_ALLOC { BdrvCheckResult result = {0}; @@ -435,32 +603,42 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) int qcow2_snapshot_load_tmp(BlockDriverState *bs, const char *snapshot_name) { - int i, snapshot_index, l1_size2; + int i, snapshot_index; BDRVQcowState *s = bs->opaque; QCowSnapshot *sn; + uint64_t *new_l1_table; + int new_l1_bytes; + int ret; + assert(bs->read_only); + + /* Search the snapshot */ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_name); if (snapshot_index < 0) { return -ENOENT; } - sn = &s->snapshots[snapshot_index]; - s->l1_size = sn->l1_size; - l1_size2 = s->l1_size * sizeof(uint64_t); - if (s->l1_table != NULL) { - g_free(s->l1_table); - } - s->l1_table_offset = sn->l1_table_offset; - s->l1_table = g_malloc0(align_offset(l1_size2, 512)); + /* Allocate and read in the snapshot's L1 table */ + new_l1_bytes = s->l1_size * sizeof(uint64_t); + new_l1_table = g_malloc0(align_offset(new_l1_bytes, 512)); - if (bdrv_pread(bs->file, sn->l1_table_offset, - s->l1_table, l1_size2) != l1_size2) { - return -1; + ret = bdrv_pread(bs->file, sn->l1_table_offset, new_l1_table, new_l1_bytes); + if (ret < 0) { + g_free(new_l1_table); + return ret; } + /* Switch the L1 table */ + g_free(s->l1_table); + + s->l1_size = sn->l1_size; + s->l1_table_offset = sn->l1_table_offset; + s->l1_table = new_l1_table; + for(i = 0;i < s->l1_size; i++) { be64_to_cpus(&s->l1_table[i]); } + return 0; } diff --git a/block/qcow2.c b/block/qcow2.c index d7805ce943..aa32e8d01a 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -92,7 +92,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, if (offset > s->cluster_size) printf("qcow2_read_extension: suspicious offset %lu\n", offset); - printf("attemting to read extended header in offset %lu\n", offset); + printf("attempting to read extended header in offset %lu\n", offset); #endif if (bdrv_pread(bs->file, offset, &ext, sizeof(ext)) != sizeof(ext)) { @@ -273,8 +273,9 @@ static int qcow2_open(BlockDriverState *bs, int flags) } bs->backing_file[len] = '\0'; } - if (qcow2_read_snapshots(bs) < 0) { - ret = -EINVAL; + + ret = qcow2_read_snapshots(bs); + if (ret < 0) { goto fail; } @@ -343,16 +344,19 @@ static int qcow2_set_key(BlockDriverState *bs, const char *key) return 0; } -static int qcow2_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int coroutine_fn qcow2_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) { + BDRVQcowState *s = bs->opaque; uint64_t cluster_offset; int ret; *pnum = nb_sectors; - /* FIXME We can get errors here, but the bdrv_is_allocated interface can't - * pass them on today */ + /* FIXME We can get errors here, but the bdrv_co_is_allocated interface + * can't pass them on today */ + qemu_co_mutex_lock(&s->lock); ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset); + qemu_co_mutex_unlock(&s->lock); if (ret < 0) { *pnum = 0; } @@ -377,7 +381,7 @@ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, return n1; } -static int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, +static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) { BDRVQcowState *s = bs->opaque; @@ -512,12 +516,12 @@ static void run_dependent_requests(BDRVQcowState *s, QCowL2Meta *m) /* Restart all dependent requests */ if (!qemu_co_queue_empty(&m->dependent_requests)) { qemu_co_mutex_unlock(&s->lock); - while(qemu_co_queue_next(&m->dependent_requests)); + qemu_co_queue_restart_all(&m->dependent_requests); qemu_co_mutex_lock(&s->lock); } } -static int qcow2_co_writev(BlockDriverState *bs, +static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, int64_t sector_num, int remaining_sectors, QEMUIOVector *qiov) @@ -631,6 +635,7 @@ static void qcow2_close(BlockDriverState *bs) g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); + qcow2_free_snapshots(bs); } static void qcow2_invalidate_cache(BlockDriverState *bs) @@ -821,7 +826,7 @@ static int qcow2_create2(const char *filename, int64_t total_size, int flags, size_t cluster_size, int prealloc, QEMUOptionParameter *options) { - /* Calulate cluster_bits */ + /* Calculate cluster_bits */ int cluster_bits; cluster_bits = ffs(cluster_size) - 1; if (cluster_bits < MIN_CLUSTER_BITS || cluster_bits > MAX_CLUSTER_BITS || @@ -1137,7 +1142,7 @@ fail: return ret; } -static int qcow2_co_flush_to_os(BlockDriverState *bs) +static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) { BDRVQcowState *s = bs->opaque; int ret; @@ -1159,7 +1164,7 @@ static int qcow2_co_flush_to_os(BlockDriverState *bs) return 0; } -static int qcow2_co_flush_to_disk(BlockDriverState *bs) +static coroutine_fn int qcow2_co_flush_to_disk(BlockDriverState *bs) { return bdrv_co_flush(bs->file); } @@ -1276,7 +1281,7 @@ static BlockDriver bdrv_qcow2 = { .bdrv_open = qcow2_open, .bdrv_close = qcow2_close, .bdrv_create = qcow2_create, - .bdrv_is_allocated = qcow2_is_allocated, + .bdrv_co_is_allocated = qcow2_co_is_allocated, .bdrv_set_key = qcow2_set_key, .bdrv_make_empty = qcow2_make_empty, diff --git a/block/qcow2.h b/block/qcow2.h index 4e44eea5ef..99e45361f5 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -78,7 +78,7 @@ typedef struct QCowSnapshot { uint32_t l1_size; char *id_str; char *name; - uint32_t vm_state_size; + uint64_t vm_state_size; uint32_t date_sec; uint32_t date_nsec; uint64_t vm_clock_nsec; diff --git a/block/qed-table.c b/block/qed-table.c index f31f9ff069..ce07b05549 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -29,7 +29,7 @@ static void qed_read_table_cb(void *opaque, int ret) { QEDReadTableCB *read_table_cb = opaque; QEDTable *table = read_table_cb->table; - int noffsets = read_table_cb->iov.iov_len / sizeof(uint64_t); + int noffsets = read_table_cb->qiov.size / sizeof(uint64_t); int i; /* Handle I/O error */ @@ -54,7 +54,6 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, QEDReadTableCB *read_table_cb = gencb_alloc(sizeof(*read_table_cb), cb, opaque); QEMUIOVector *qiov = &read_table_cb->qiov; - BlockDriverAIOCB *aiocb; trace_qed_read_table(s, offset, table); @@ -64,12 +63,9 @@ static void qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, read_table_cb->iov.iov_len = s->header.cluster_size * s->header.table_size, qemu_iovec_init_external(qiov, &read_table_cb->iov, 1); - aiocb = bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, - read_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, - qed_read_table_cb, read_table_cb); - if (!aiocb) { - qed_read_table_cb(read_table_cb, -EIO); - } + bdrv_aio_readv(s->bs->file, offset / BDRV_SECTOR_SIZE, qiov, + qiov->size / BDRV_SECTOR_SIZE, + qed_read_table_cb, read_table_cb); } typedef struct { @@ -127,7 +123,6 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, BlockDriverCompletionFunc *cb, void *opaque) { QEDWriteTableCB *write_table_cb; - BlockDriverAIOCB *aiocb; unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; unsigned int start, end, i; size_t len_bytes; @@ -158,13 +153,10 @@ static void qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, /* Adjust for offset into table */ offset += start * sizeof(uint64_t); - aiocb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, - &write_table_cb->qiov, - write_table_cb->iov.iov_len / BDRV_SECTOR_SIZE, - qed_write_table_cb, write_table_cb); - if (!aiocb) { - qed_write_table_cb(write_table_cb, -EIO); - } + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &write_table_cb->qiov, + write_table_cb->qiov.size / BDRV_SECTOR_SIZE, + qed_write_table_cb, write_table_cb); } /** diff --git a/block/qed.c b/block/qed.c index 7e22e77b9d..8da3ebe9d4 100644 --- a/block/qed.c +++ b/block/qed.c @@ -123,7 +123,6 @@ static void qed_write_header_read_cb(void *opaque, int ret) { QEDWriteHeaderCB *write_header_cb = opaque; BDRVQEDState *s = write_header_cb->s; - BlockDriverAIOCB *acb; if (ret) { qed_write_header_cb(write_header_cb, ret); @@ -133,12 +132,9 @@ static void qed_write_header_read_cb(void *opaque, int ret) /* Update header */ qed_header_cpu_to_le(&s->header, (QEDHeader *)write_header_cb->buf); - acb = bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, - write_header_cb->nsectors, qed_write_header_cb, - write_header_cb); - if (!acb) { - qed_write_header_cb(write_header_cb, -EIO); - } + bdrv_aio_writev(s->bs->file, 0, &write_header_cb->qiov, + write_header_cb->nsectors, qed_write_header_cb, + write_header_cb); } /** @@ -156,7 +152,6 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb, * them, and write back. */ - BlockDriverAIOCB *acb; int nsectors = (sizeof(QEDHeader) + BDRV_SECTOR_SIZE - 1) / BDRV_SECTOR_SIZE; size_t len = nsectors * BDRV_SECTOR_SIZE; @@ -170,11 +165,8 @@ static void qed_write_header(BDRVQEDState *s, BlockDriverCompletionFunc cb, write_header_cb->iov.iov_len = len; qemu_iovec_init_external(&write_header_cb->qiov, &write_header_cb->iov, 1); - acb = bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, - qed_write_header_read_cb, write_header_cb); - if (!acb) { - qed_write_header_cb(write_header_cb, -EIO); - } + bdrv_aio_readv(s->bs->file, 0, &write_header_cb->qiov, nsectors, + qed_write_header_read_cb, write_header_cb); } static uint64_t qed_max_image_size(uint32_t cluster_size, uint32_t table_size) @@ -661,6 +653,7 @@ static int bdrv_qed_create(const char *filename, QEMUOptionParameter *options) } typedef struct { + Coroutine *co; int is_allocated; int *pnum; } QEDIsAllocatedCB; @@ -670,10 +663,14 @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l QEDIsAllocatedCB *cb = opaque; *cb->pnum = len / BDRV_SECTOR_SIZE; cb->is_allocated = (ret == QED_CLUSTER_FOUND || ret == QED_CLUSTER_ZERO); + if (cb->co) { + qemu_coroutine_enter(cb->co, NULL); + } } -static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int coroutine_fn bdrv_qed_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors, int *pnum) { BDRVQEDState *s = bs->opaque; uint64_t pos = (uint64_t)sector_num * BDRV_SECTOR_SIZE; @@ -686,8 +683,10 @@ static int bdrv_qed_is_allocated(BlockDriverState *bs, int64_t sector_num, qed_find_cluster(s, &request, pos, len, qed_is_allocated_cb, &cb); + /* Now sleep if the callback wasn't invoked immediately */ while (cb.is_allocated == -1) { - qemu_aio_wait(); + cb.co = qemu_coroutine_self(); + qemu_coroutine_yield(); } qed_unref_l2_cache_entry(request.l2_table); @@ -721,7 +720,6 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, QEMUIOVector *qiov, BlockDriverCompletionFunc *cb, void *opaque) { - BlockDriverAIOCB *aiocb; uint64_t backing_length = 0; size_t size; @@ -753,11 +751,8 @@ static void qed_read_backing_file(BDRVQEDState *s, uint64_t pos, size = MIN((uint64_t)backing_length - pos, qiov->size); BLKDBG_EVENT(s->bs->file, BLKDBG_READ_BACKING); - aiocb = bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, - qiov, size / BDRV_SECTOR_SIZE, cb, opaque); - if (!aiocb) { - cb(opaque, -EIO); - } + bdrv_aio_readv(s->bs->backing_hd, pos / BDRV_SECTOR_SIZE, + qiov, size / BDRV_SECTOR_SIZE, cb, opaque); } typedef struct { @@ -779,7 +774,6 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret) { CopyFromBackingFileCB *copy_cb = opaque; BDRVQEDState *s = copy_cb->s; - BlockDriverAIOCB *aiocb; if (ret) { qed_copy_from_backing_file_cb(copy_cb, ret); @@ -787,13 +781,9 @@ static void qed_copy_from_backing_file_write(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_COW_WRITE); - aiocb = bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, - ©_cb->qiov, - copy_cb->qiov.size / BDRV_SECTOR_SIZE, - qed_copy_from_backing_file_cb, copy_cb); - if (!aiocb) { - qed_copy_from_backing_file_cb(copy_cb, -EIO); - } + bdrv_aio_writev(s->bs->file, copy_cb->offset / BDRV_SECTOR_SIZE, + ©_cb->qiov, copy_cb->qiov.size / BDRV_SECTOR_SIZE, + qed_copy_from_backing_file_cb, copy_cb); } /** @@ -1015,7 +1005,6 @@ static void qed_aio_write_main(void *opaque, int ret) uint64_t offset = acb->cur_cluster + qed_offset_into_cluster(s, acb->cur_pos); BlockDriverCompletionFunc *next_fn; - BlockDriverAIOCB *file_acb; trace_qed_aio_write_main(s, acb, ret, offset, acb->cur_qiov.size); @@ -1035,13 +1024,9 @@ static void qed_aio_write_main(void *opaque, int ret) } BLKDBG_EVENT(s->bs->file, BLKDBG_WRITE_AIO); - file_acb = bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, - &acb->cur_qiov, - acb->cur_qiov.size / BDRV_SECTOR_SIZE, - next_fn, acb); - if (!file_acb) { - qed_aio_complete(acb, -EIO); - } + bdrv_aio_writev(s->bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, + next_fn, acb); } /** @@ -1208,7 +1193,6 @@ static void qed_aio_read_data(void *opaque, int ret, QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); BlockDriverState *bs = acb->common.bs; - BlockDriverAIOCB *file_acb; /* Adjust offset into cluster */ offset += qed_offset_into_cluster(s, acb->cur_pos); @@ -1233,14 +1217,9 @@ static void qed_aio_read_data(void *opaque, int ret, } BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); - file_acb = bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, - &acb->cur_qiov, - acb->cur_qiov.size / BDRV_SECTOR_SIZE, - qed_aio_next_io, acb); - if (!file_acb) { - ret = -EIO; - goto err; - } + bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE, + &acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE, + qed_aio_next_io, acb); return; err: @@ -1485,7 +1464,7 @@ static BlockDriver bdrv_qed = { .bdrv_open = bdrv_qed_open, .bdrv_close = bdrv_qed_close, .bdrv_create = bdrv_qed_create, - .bdrv_is_allocated = bdrv_qed_is_allocated, + .bdrv_co_is_allocated = bdrv_qed_co_is_allocated, .bdrv_make_empty = bdrv_qed_make_empty, .bdrv_aio_readv = bdrv_qed_aio_readv, .bdrv_aio_writev = bdrv_qed_aio_writev, diff --git a/block/raw-posix.c b/block/raw-posix.c index a3de373586..2ee5d690e9 100644 --- a/block/raw-posix.c +++ b/block/raw-posix.c @@ -1153,7 +1153,7 @@ static int cdrom_open(BlockDriverState *bs, const char *filename, int flags) if (ret) return ret; - /* make sure the door isnt locked at this time */ + /* make sure the door isn't locked at this time */ ioctl(s->fd, CDIOCALLOW); return 0; } @@ -1184,7 +1184,7 @@ static int cdrom_reopen(BlockDriverState *bs) } s->fd = fd; - /* make sure the door isnt locked at this time */ + /* make sure the door isn't locked at this time */ ioctl(s->fd, CDIOCALLOW); return 0; } diff --git a/block/rbd.c b/block/rbd.c index 9088c52d24..7a2384c8f9 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -632,9 +632,6 @@ static BlockDriverAIOCB *rbd_aio_rw_vector(BlockDriverState *bs, BDRVRBDState *s = bs->opaque; acb = qemu_aio_get(&rbd_aio_pool, bs, cb, opaque); - if (!acb) { - return NULL; - } acb->write = write; acb->qiov = qiov; acb->bounce = qemu_blockalign(bs, qiov->size); @@ -808,7 +805,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, } while (snap_count == -ERANGE); if (snap_count <= 0) { - return snap_count; + goto done; } sn_tab = g_malloc0(snap_count * sizeof(QEMUSnapshotInfo)); @@ -827,6 +824,7 @@ static int qemu_rbd_snap_list(BlockDriverState *bs, } rbd_snap_list_end(snaps); + done: *psn_tab = sn_tab; return snap_count; } diff --git a/block/sheepdog.c b/block/sheepdog.c index 9f8060960f..aa9707f2ae 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -1116,6 +1116,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, /* send a header */ ret = do_write(s->fd, &hdr, sizeof(hdr)); if (ret) { + qemu_co_mutex_unlock(&s->lock); error_report("failed to send a req, %s", strerror(errno)); return -EIO; } @@ -1123,6 +1124,7 @@ static int coroutine_fn add_aio_request(BDRVSheepdogState *s, AIOReq *aio_req, if (wlen) { ret = do_writev(s->fd, iov, wlen, aio_req->iov_offset); if (ret) { + qemu_co_mutex_unlock(&s->lock); error_report("failed to send a data, %s", strerror(errno)); return -EIO; } @@ -1713,7 +1715,7 @@ out: return 1; } -static int sd_co_writev(BlockDriverState *bs, int64_t sector_num, +static coroutine_fn int sd_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { SheepdogAIOCB *acb; @@ -1742,7 +1744,7 @@ static int sd_co_writev(BlockDriverState *bs, int64_t sector_num, return acb->ret; } -static int sd_co_readv(BlockDriverState *bs, int64_t sector_num, +static coroutine_fn int sd_co_readv(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) { SheepdogAIOCB *acb; diff --git a/block/vdi.c b/block/vdi.c index 684a4a87b6..31cdfabdea 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -52,6 +52,7 @@ #include "qemu-common.h" #include "block_int.h" #include "module.h" +#include "migration.h" #if defined(CONFIG_UUID) #include <uuid/uuid.h> @@ -203,6 +204,8 @@ typedef struct { uint32_t bmap_sector; /* VDI header (converted to host endianness). */ VdiHeader header; + + Error *migration_blocker; } BDRVVdiState; /* Change UUID from little endian (IPRT = VirtualBox format) to big endian @@ -454,6 +457,12 @@ static int vdi_open(BlockDriverState *bs, int flags) goto fail_free_bmap; } + /* Disable migration when vdi images are used */ + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "vdi", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + return 0; fail_free_bmap: @@ -463,8 +472,8 @@ static int vdi_open(BlockDriverState *bs, int flags) return -1; } -static int vdi_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int coroutine_fn vdi_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) { /* TODO: Check for too large sector_num (in bdrv_is_allocated or here). */ BDRVVdiState *s = (BDRVVdiState *)bs->opaque; @@ -506,28 +515,26 @@ static VdiAIOCB *vdi_aio_setup(BlockDriverState *bs, int64_t sector_num, bs, sector_num, qiov, nb_sectors, cb, opaque, is_write); acb = qemu_aio_get(&vdi_aio_pool, bs, cb, opaque); - if (acb) { - acb->hd_aiocb = NULL; - acb->sector_num = sector_num; - acb->qiov = qiov; - acb->is_write = is_write; - - if (qiov->niov > 1) { - acb->buf = qemu_blockalign(bs, qiov->size); - acb->orig_buf = acb->buf; - if (is_write) { - qemu_iovec_to_buffer(qiov, acb->buf); - } - } else { - acb->buf = (uint8_t *)qiov->iov->iov_base; + acb->hd_aiocb = NULL; + acb->sector_num = sector_num; + acb->qiov = qiov; + acb->is_write = is_write; + + if (qiov->niov > 1) { + acb->buf = qemu_blockalign(bs, qiov->size); + acb->orig_buf = acb->buf; + if (is_write) { + qemu_iovec_to_buffer(qiov, acb->buf); } - acb->nb_sectors = nb_sectors; - acb->n_sectors = 0; - acb->bmap_first = VDI_UNALLOCATED; - acb->bmap_last = VDI_UNALLOCATED; - acb->block_buffer = NULL; - acb->header_modified = 0; - } + } else { + acb->buf = (uint8_t *)qiov->iov->iov_base; + } + acb->nb_sectors = nb_sectors; + acb->n_sectors = 0; + acb->bmap_first = VDI_UNALLOCATED; + acb->bmap_last = VDI_UNALLOCATED; + acb->block_buffer = NULL; + acb->header_modified = 0; return acb; } @@ -624,10 +631,6 @@ static void vdi_aio_read_cb(void *opaque, int ret) qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); acb->hd_aiocb = bdrv_aio_readv(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_read_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; - } } return; done: @@ -648,10 +651,6 @@ static BlockDriverAIOCB *vdi_aio_readv(BlockDriverState *bs, logout("\n"); acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); - if (!acb) { - return NULL; - } - ret = vdi_schedule_bh(vdi_aio_rw_bh, acb); if (ret < 0) { if (acb->qiov->niov > 1) { @@ -699,10 +698,6 @@ static void vdi_aio_write_cb(void *opaque, int ret) qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); acb->hd_aiocb = bdrv_aio_writev(bs->file, 0, &acb->hd_qiov, 1, vdi_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; - } return; } else if (VDI_IS_ALLOCATED(acb->bmap_first)) { /* One or more new blocks were allocated. */ @@ -729,10 +724,6 @@ static void vdi_aio_write_cb(void *opaque, int ret) n_sectors, bmap_first); acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; - } return; } ret = 0; @@ -780,10 +771,6 @@ static void vdi_aio_write_cb(void *opaque, int ret) acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, s->block_sectors, vdi_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; - } } else { uint64_t offset = s->header.offset_data / SECTOR_SIZE + (uint64_t)bmap_entry * s->block_sectors + @@ -793,10 +780,6 @@ static void vdi_aio_write_cb(void *opaque, int ret) qemu_iovec_init_external(&acb->hd_qiov, &acb->hd_iov, 1); acb->hd_aiocb = bdrv_aio_writev(bs->file, offset, &acb->hd_qiov, n_sectors, vdi_aio_write_cb, acb); - if (acb->hd_aiocb == NULL) { - ret = -EIO; - goto done; - } } return; @@ -818,10 +801,6 @@ static BlockDriverAIOCB *vdi_aio_writev(BlockDriverState *bs, logout("\n"); acb = vdi_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 1); - if (!acb) { - return NULL; - } - ret = vdi_schedule_bh(vdi_aio_rw_bh, acb); if (ret < 0) { if (acb->qiov->niov > 1) { @@ -939,6 +918,12 @@ static int vdi_create(const char *filename, QEMUOptionParameter *options) static void vdi_close(BlockDriverState *bs) { + BDRVVdiState *s = bs->opaque; + + g_free(s->bmap); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } static coroutine_fn int vdi_co_flush(BlockDriverState *bs) @@ -981,7 +966,7 @@ static BlockDriver bdrv_vdi = { .bdrv_close = vdi_close, .bdrv_create = vdi_create, .bdrv_co_flush_to_disk = vdi_co_flush, - .bdrv_is_allocated = vdi_is_allocated, + .bdrv_co_is_allocated = vdi_co_is_allocated, .bdrv_make_empty = vdi_make_empty, .bdrv_aio_readv = vdi_aio_readv, diff --git a/block/vmdk.c b/block/vmdk.c index 96f7d5d90f..5623ac10cd 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -26,6 +26,7 @@ #include "qemu-common.h" #include "block_int.h" #include "module.h" +#include "migration.h" #include <zlib.h> #define VMDK3_MAGIC (('C' << 24) | ('O' << 16) | ('W' << 8) | 'D') @@ -97,6 +98,7 @@ typedef struct BDRVVmdkState { int num_extents; /* Extent array with num_extents entries, ascend ordered by address */ VmdkExtent *extents; + Error *migration_blocker; } BDRVVmdkState; typedef struct VmdkMetaData { @@ -659,7 +661,14 @@ static int vmdk_open(BlockDriverState *bs, int flags) } s->parent_cid = vmdk_read_cid(bs, 1); qemu_co_mutex_init(&s->lock); - return ret; + + /* Disable migration when VMDK images are used */ + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "vmdk", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + + return 0; fail: vmdk_free_extents(bs); @@ -852,8 +861,8 @@ static VmdkExtent *find_extent(BDRVVmdkState *s, return NULL; } -static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, - int nb_sectors, int *pnum) +static int coroutine_fn vmdk_co_is_allocated(BlockDriverState *bs, + int64_t sector_num, int nb_sectors, int *pnum) { BDRVVmdkState *s = bs->opaque; int64_t index_in_cluster, n, ret; @@ -864,8 +873,10 @@ static int vmdk_is_allocated(BlockDriverState *bs, int64_t sector_num, if (!extent) { return 0; } + qemu_co_mutex_lock(&s->lock); ret = get_cluster_offset(bs, extent, NULL, sector_num * 512, 0, &offset); + qemu_co_mutex_unlock(&s->lock); /* get_cluster_offset returning 0 means success */ ret = !ret; @@ -1504,7 +1515,12 @@ exit: static void vmdk_close(BlockDriverState *bs) { + BDRVVmdkState *s = bs->opaque; + vmdk_free_extents(bs); + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } static coroutine_fn int vmdk_co_flush(BlockDriverState *bs) @@ -1582,7 +1598,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_close = vmdk_close, .bdrv_create = vmdk_create, .bdrv_co_flush_to_disk = vmdk_co_flush, - .bdrv_is_allocated = vmdk_is_allocated, + .bdrv_co_is_allocated = vmdk_co_is_allocated, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, .create_options = vmdk_create_options, diff --git a/block/vpc.c b/block/vpc.c index 39a324705d..89a5ee2668 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -25,6 +25,7 @@ #include "qemu-common.h" #include "block_int.h" #include "module.h" +#include "migration.h" /**************************************************************/ @@ -128,6 +129,8 @@ typedef struct BDRVVPCState { uint64_t last_bitmap; #endif + + Error *migration_blocker; } BDRVVPCState; static uint32_t vpc_checksum(uint8_t* buf, size_t size) @@ -228,6 +231,13 @@ static int vpc_open(BlockDriverState *bs, int flags) #endif qemu_co_mutex_init(&s->lock); + + /* Disable migration when VHD images are used */ + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "vpc", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + return 0; fail: return err; @@ -352,8 +362,11 @@ static int64_t alloc_block(BlockDriverState* bs, int64_t sector_num) // Initialize the block's bitmap memset(bitmap, 0xff, s->bitmap_size); - bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, + ret = bdrv_pwrite_sync(bs->file, s->free_data_block_offset, bitmap, s->bitmap_size); + if (ret < 0) { + return ret; + } // Write new footer (the old one will be overwritten) s->free_data_block_offset += s->block_size + s->bitmap_size; @@ -651,6 +664,9 @@ static void vpc_close(BlockDriverState *bs) #ifdef CACHE g_free(s->pageentry_u8); #endif + + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); } static QEMUOptionParameter vpc_create_options[] = { diff --git a/block/vvfat.c b/block/vvfat.c index 131680f6f4..eeffc4a4a8 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -27,6 +27,7 @@ #include "qemu-common.h" #include "block_int.h" #include "module.h" +#include "migration.h" #ifndef S_IWGRP #define S_IWGRP 0 @@ -350,6 +351,8 @@ typedef struct BDRVVVFATState { array_t commits; const char* path; int downcase_short_names; + + Error *migration_blocker; } BDRVVVFATState; /* take the sector position spos and convert it to Cylinder/Head/Sector position @@ -1073,6 +1076,15 @@ DLOG(if (stderr == NULL) { // assert(is_consistent(s)); qemu_co_mutex_init(&s->lock); + + /* Disable migration when vvfat is used rw */ + if (s->qcow) { + error_set(&s->migration_blocker, + QERR_BLOCK_FORMAT_FEATURE_NOT_SUPPORTED, + "vvfat (rw)", bs->device_name, "live migration"); + migrate_add_blocker(s->migration_blocker); + } + return 0; } @@ -2746,7 +2758,7 @@ static coroutine_fn int vvfat_co_write(BlockDriverState *bs, int64_t sector_num, return ret; } -static int vvfat_is_allocated(BlockDriverState *bs, +static int coroutine_fn vvfat_co_is_allocated(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int* n) { BDRVVVFATState* s = bs->opaque; @@ -2829,6 +2841,11 @@ static void vvfat_close(BlockDriverState *bs) array_free(&(s->directory)); array_free(&(s->mapping)); g_free(s->cluster_buffer); + + if (s->qcow) { + migrate_del_blocker(s->migration_blocker); + error_free(s->migration_blocker); + } } static BlockDriver bdrv_vvfat = { @@ -2838,7 +2855,7 @@ static BlockDriver bdrv_vvfat = { .bdrv_read = vvfat_co_read, .bdrv_write = vvfat_co_write, .bdrv_close = vvfat_close, - .bdrv_is_allocated = vvfat_is_allocated, + .bdrv_co_is_allocated = vvfat_co_is_allocated, .protocol_name = "fat", }; |