diff options
Diffstat (limited to 'block/parallels.c')
-rw-r--r-- | block/parallels.c | 108 |
1 files changed, 48 insertions, 60 deletions
diff --git a/block/parallels.c b/block/parallels.c index 9545761f49..d3802085e3 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -36,6 +36,7 @@ #include "qemu/bswap.h" #include "qemu/bitmap.h" #include "migration/blocker.h" +#include "parallels.h" /**************************************************************/ @@ -45,30 +46,6 @@ #define HEADER_INUSE_MAGIC (0x746F6E59) #define MAX_PARALLELS_IMAGE_FACTOR (1ull << 32) -#define DEFAULT_CLUSTER_SIZE 1048576 /* 1 MiB */ - - -// always little-endian -typedef struct ParallelsHeader { - char magic[16]; // "WithoutFreeSpace" - uint32_t version; - uint32_t heads; - uint32_t cylinders; - uint32_t tracks; - uint32_t bat_entries; - uint64_t nb_sectors; - uint32_t inuse; - uint32_t data_off; - char padding[12]; -} QEMU_PACKED ParallelsHeader; - - -typedef enum ParallelsPreallocMode { - PRL_PREALLOC_MODE_FALLOCATE = 0, - PRL_PREALLOC_MODE_TRUNCATE = 1, - PRL_PREALLOC_MODE__MAX = 2, -} ParallelsPreallocMode; - static QEnumLookup prealloc_mode_lookup = { .array = (const char *const[]) { "falloc", @@ -77,34 +54,6 @@ static QEnumLookup prealloc_mode_lookup = { .size = PRL_PREALLOC_MODE__MAX }; -typedef struct BDRVParallelsState { - /** Locking is conservative, the lock protects - * - image file extending (truncate, fallocate) - * - any access to block allocation table - */ - CoMutex lock; - - ParallelsHeader *header; - uint32_t header_size; - bool header_unclean; - - unsigned long *bat_dirty_bmap; - unsigned int bat_dirty_block; - - uint32_t *bat_bitmap; - unsigned int bat_size; - - int64_t data_end; - uint64_t prealloc_size; - ParallelsPreallocMode prealloc_mode; - - unsigned int tracks; - - unsigned int off_multiplier; - Error *migration_blocker; -} BDRVParallelsState; - - #define PARALLELS_OPT_PREALLOC_MODE "prealloc-mode" #define PARALLELS_OPT_PREALLOC_SIZE "prealloc-size" @@ -193,6 +142,7 @@ static int64_t block_status(BDRVParallelsState *s, int64_t sector_num, static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, int nb_sectors, int *pnum) { + int ret; BDRVParallelsState *s = bs->opaque; int64_t pos, space, idx, to_allocate, i, len; @@ -221,7 +171,6 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, return len; } if (s->data_end + space > (len >> BDRV_SECTOR_BITS)) { - int ret; space += s->prealloc_size; if (s->prealloc_mode == PRL_PREALLOC_MODE_FALLOCATE) { ret = bdrv_pwrite_zeroes(bs->file, @@ -237,6 +186,37 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, } } + /* Try to read from backing to fill empty clusters + * FIXME: 1. previous write_zeroes may be redundant + * 2. most of data we read from backing will be rewritten by + * parallels_co_writev. On aligned-to-cluster write we do not need + * this read at all. + * 3. it would be good to combine write of data from backing and new + * data into one write call */ + if (bs->backing) { + int64_t nb_cow_sectors = to_allocate * s->tracks; + int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS; + QEMUIOVector qiov; + struct iovec iov = { + .iov_len = nb_cow_bytes, + .iov_base = qemu_blockalign(bs, nb_cow_bytes) + }; + qemu_iovec_init_external(&qiov, &iov, 1); + + ret = bdrv_co_readv(bs->backing, idx * s->tracks, nb_cow_sectors, + &qiov); + if (ret < 0) { + qemu_vfree(iov.iov_base); + return ret; + } + + ret = bdrv_co_writev(bs->file, s->data_end, nb_cow_sectors, &qiov); + qemu_vfree(iov.iov_base); + if (ret < 0) { + return ret; + } + } + for (i = 0; i < to_allocate; i++) { s->bat_bitmap[idx + i] = cpu_to_le32(s->data_end / s->off_multiplier); s->data_end += s->tracks; @@ -360,12 +340,19 @@ static coroutine_fn int parallels_co_readv(BlockDriverState *bs, nbytes = n << BDRV_SECTOR_BITS; + qemu_iovec_reset(&hd_qiov); + qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); + if (position < 0) { - qemu_iovec_memset(qiov, bytes_done, 0, nbytes); + if (bs->backing) { + ret = bdrv_co_readv(bs->backing, sector_num, n, &hd_qiov); + if (ret < 0) { + break; + } + } else { + qemu_iovec_memset(&hd_qiov, 0, 0, nbytes); + } } else { - qemu_iovec_reset(&hd_qiov); - qemu_iovec_concat(&hd_qiov, qiov, bytes_done, nbytes); - ret = bdrv_co_readv(bs->file, position, n, &hd_qiov); if (ret < 0) { break; @@ -527,8 +514,9 @@ static int parallels_create(const char *filename, QemuOpts *opts, Error **errp) memcpy(header.magic, HEADER_MAGIC2, sizeof(header.magic)); header.version = cpu_to_le32(HEADER_VERSION); /* don't care much about geometry, it is not used on image level */ - header.heads = cpu_to_le32(16); - header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE / 16 / 32); + header.heads = cpu_to_le32(HEADS_NUMBER); + header.cylinders = cpu_to_le32(total_size / BDRV_SECTOR_SIZE + / HEADS_NUMBER / SEC_IN_CYL); header.tracks = cpu_to_le32(cl_size >> BDRV_SECTOR_BITS); header.bat_entries = cpu_to_le32(bat_entries); header.nb_sectors = cpu_to_le64(DIV_ROUND_UP(total_size, BDRV_SECTOR_SIZE)); @@ -798,7 +786,7 @@ static BlockDriver bdrv_parallels = { .bdrv_co_flush_to_os = parallels_co_flush_to_os, .bdrv_co_readv = parallels_co_readv, .bdrv_co_writev = parallels_co_writev, - + .supports_backing = true, .bdrv_create = parallels_create, .bdrv_check = parallels_check, .create_opts = ¶llels_create_opts, |