diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/iscsi.c | 139 | ||||
-rw-r--r-- | block/qcow2.c | 194 | ||||
-rw-r--r-- | block/qcow2.h | 9 | ||||
-rw-r--r-- | block/qed.c | 125 | ||||
-rw-r--r-- | block/qed.h | 7 | ||||
-rw-r--r-- | block/sheepdog.c | 3 | ||||
-rw-r--r-- | block/vpc.c | 282 |
7 files changed, 582 insertions, 177 deletions
diff --git a/block/iscsi.c b/block/iscsi.c index 938c568..bd3ca11 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -455,6 +455,109 @@ iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data, } } +static int parse_chap(struct iscsi_context *iscsi, const char *target) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *user = NULL; + const char *password = NULL; + + list = qemu_find_opts("iscsi"); + if (!list) { + return 0; + } + + opts = qemu_opts_find(list, target); + if (opts == NULL) { + opts = QTAILQ_FIRST(&list->head); + if (!opts) { + return 0; + } + } + + user = qemu_opt_get(opts, "user"); + if (!user) { + return 0; + } + + password = qemu_opt_get(opts, "password"); + if (!password) { + error_report("CHAP username specified but no password was given"); + return -1; + } + + if (iscsi_set_initiator_username_pwd(iscsi, user, password)) { + error_report("Failed to set initiator username and password"); + return -1; + } + + return 0; +} + +static void parse_header_digest(struct iscsi_context *iscsi, const char *target) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *digest = NULL; + + list = qemu_find_opts("iscsi"); + if (!list) { + return; + } + + opts = qemu_opts_find(list, target); + if (opts == NULL) { + opts = QTAILQ_FIRST(&list->head); + if (!opts) { + return; + } + } + + digest = qemu_opt_get(opts, "header-digest"); + if (!digest) { + return; + } + + if (!strcmp(digest, "CRC32C")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C); + } else if (!strcmp(digest, "NONE")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE); + } else if (!strcmp(digest, "CRC32C-NONE")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE); + } else if (!strcmp(digest, "NONE-CRC32C")) { + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + } else { + error_report("Invalid header-digest setting : %s", digest); + } +} + +static char *parse_initiator_name(const char *target) +{ + QemuOptsList *list; + QemuOpts *opts; + const char *name = NULL; + + list = qemu_find_opts("iscsi"); + if (!list) { + return g_strdup("iqn.2008-11.org.linux-kvm"); + } + + opts = qemu_opts_find(list, target); + if (opts == NULL) { + opts = QTAILQ_FIRST(&list->head); + if (!opts) { + return g_strdup("iqn.2008-11.org.linux-kvm"); + } + } + + name = qemu_opt_get(opts, "initiator-name"); + if (!name) { + return g_strdup("iqn.2008-11.org.linux-kvm"); + } + + return g_strdup(name); +} + /* * We support iscsi url's on the form * iscsi://[<username>%<password>@]<host>[:<port>]/<targetname>/<lun> @@ -465,6 +568,7 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) struct iscsi_context *iscsi = NULL; struct iscsi_url *iscsi_url = NULL; struct IscsiTask task; + char *initiator_name = NULL; int ret; if ((BDRV_SECTOR_SIZE % 512) != 0) { @@ -474,16 +578,6 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) return -EINVAL; } - memset(iscsilun, 0, sizeof(IscsiLun)); - - /* Should really append the KVM name after the ':' here */ - iscsi = iscsi_create_context("iqn.2008-11.org.linux-kvm:"); - if (iscsi == NULL) { - error_report("iSCSI: Failed to create iSCSI context."); - ret = -ENOMEM; - goto failed; - } - iscsi_url = iscsi_parse_full_url(iscsi, filename); if (iscsi_url == NULL) { error_report("Failed to parse URL : %s %s", filename, @@ -492,6 +586,17 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) goto failed; } + memset(iscsilun, 0, sizeof(IscsiLun)); + + initiator_name = parse_initiator_name(iscsi_url->target); + + iscsi = iscsi_create_context(initiator_name); + if (iscsi == NULL) { + error_report("iSCSI: Failed to create iSCSI context."); + ret = -ENOMEM; + goto failed; + } + if (iscsi_set_targetname(iscsi, iscsi_url->target)) { error_report("iSCSI: Failed to set target name."); ret = -EINVAL; @@ -507,6 +612,14 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) goto failed; } } + + /* check if we got CHAP username/password via the options */ + if (parse_chap(iscsi, iscsi_url->target) != 0) { + error_report("iSCSI: Failed to set CHAP user/password"); + ret = -EINVAL; + goto failed; + } + if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) { error_report("iSCSI: Failed to set session type to normal."); ret = -EINVAL; @@ -515,6 +628,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + /* check if we got HEADER_DIGEST via the options */ + parse_header_digest(iscsi, iscsi_url->target); + task.iscsilun = iscsilun; task.status = 0; task.complete = 0; @@ -548,6 +664,9 @@ static int iscsi_open(BlockDriverState *bs, const char *filename, int flags) return 0; failed: + if (initiator_name != NULL) { + g_free(initiator_name); + } if (iscsi_url != NULL) { iscsi_destroy_url(iscsi_url); } diff --git a/block/qcow2.c b/block/qcow2.c index aa32e8d..3692b45 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -77,8 +77,10 @@ static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, uint64_t end_offset) { + BDRVQcowState *s = bs->opaque; QCowExtension ext; uint64_t offset; + int ret; #ifdef DEBUG_EXT printf("qcow2_read_extensions: start=%ld end=%ld\n", start_offset, end_offset); @@ -129,8 +131,22 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, break; default: - /* unknown magic -- just skip it */ - offset = ((offset + ext.len + 7) & ~7); + /* unknown magic - save it in case we need to rewrite the header */ + { + Qcow2UnknownHeaderExtension *uext; + + uext = g_malloc0(sizeof(*uext) + ext.len); + uext->magic = ext.magic; + uext->len = ext.len; + QLIST_INSERT_HEAD(&s->unknown_header_ext, uext, next); + + ret = bdrv_pread(bs->file, offset , uext->data, uext->len); + if (ret < 0) { + return ret; + } + + offset = ((offset + ext.len + 7) & ~7); + } break; } } @@ -138,6 +154,16 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return 0; } +static void cleanup_unknown_header_ext(BlockDriverState *bs) +{ + BDRVQcowState *s = bs->opaque; + Qcow2UnknownHeaderExtension *uext, *next; + + QLIST_FOREACH_SAFE(uext, &s->unknown_header_ext, next, next) { + QLIST_REMOVE(uext, next); + g_free(uext); + } +} static int qcow2_open(BlockDriverState *bs, int flags) { @@ -291,6 +317,7 @@ static int qcow2_open(BlockDriverState *bs, int flags) return ret; fail: + cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); qcow2_refcount_close(bs); g_free(s->l1_table); @@ -632,6 +659,7 @@ static void qcow2_close(BlockDriverState *bs) qcow2_cache_destroy(bs, s->l2_table_cache); qcow2_cache_destroy(bs, s->refcount_block_cache); + cleanup_unknown_header_ext(bs); g_free(s->cluster_cache); qemu_vfree(s->cluster_data); qcow2_refcount_close(bs); @@ -669,103 +697,149 @@ static void qcow2_invalidate_cache(BlockDriverState *bs) } } +static size_t header_ext_add(char *buf, uint32_t magic, const void *s, + size_t len, size_t buflen) +{ + QCowExtension *ext_backing_fmt = (QCowExtension*) buf; + size_t ext_len = sizeof(QCowExtension) + ((len + 7) & ~7); + + if (buflen < ext_len) { + return -ENOSPC; + } + + *ext_backing_fmt = (QCowExtension) { + .magic = cpu_to_be32(magic), + .len = cpu_to_be32(len), + }; + memcpy(buf + sizeof(QCowExtension), s, len); + + return ext_len; +} + /* - * Updates the variable length parts of the qcow2 header, i.e. the backing file - * name and all extensions. qcow2 was not designed to allow such changes, so if - * we run out of space (we can only use the first cluster) this function may - * fail. + * Updates the qcow2 header, including the variable length parts of it, i.e. + * the backing file name and all extensions. qcow2 was not designed to allow + * such changes, so if we run out of space (we can only use the first cluster) + * this function may fail. * * Returns 0 on success, -errno in error cases. */ -static int qcow2_update_ext_header(BlockDriverState *bs, - const char *backing_file, const char *backing_fmt) +int qcow2_update_header(BlockDriverState *bs) { - size_t backing_file_len = 0; - size_t backing_fmt_len = 0; BDRVQcowState *s = bs->opaque; - QCowExtension ext_backing_fmt = {0, 0}; + QCowHeader *header; + char *buf; + size_t buflen = s->cluster_size; int ret; + uint64_t total_size; + uint32_t refcount_table_clusters; + Qcow2UnknownHeaderExtension *uext; - /* Backing file format doesn't make sense without a backing file */ - if (backing_fmt && !backing_file) { - return -EINVAL; - } + buf = qemu_blockalign(bs, buflen); + memset(buf, 0, s->cluster_size); - /* Prepare the backing file format extension if needed */ - if (backing_fmt) { - ext_backing_fmt.len = cpu_to_be32(strlen(backing_fmt)); - ext_backing_fmt.magic = cpu_to_be32(QCOW2_EXT_MAGIC_BACKING_FORMAT); - backing_fmt_len = ((sizeof(ext_backing_fmt) - + strlen(backing_fmt) + 7) & ~7); - } + /* Header structure */ + header = (QCowHeader*) buf; - /* Check if we can fit the new header into the first cluster */ - if (backing_file) { - backing_file_len = strlen(backing_file); - } - - size_t header_size = sizeof(QCowHeader) + backing_file_len - + backing_fmt_len; - - if (header_size > s->cluster_size) { - return -ENOSPC; + if (buflen < sizeof(*header)) { + ret = -ENOSPC; + goto fail; } - /* Rewrite backing file name and qcow2 extensions */ - size_t ext_size = header_size - sizeof(QCowHeader); - uint8_t buf[ext_size]; - size_t offset = 0; - size_t backing_file_offset = 0; + total_size = bs->total_sectors * BDRV_SECTOR_SIZE; + refcount_table_clusters = s->refcount_table_size >> (s->cluster_bits - 3); + + *header = (QCowHeader) { + .magic = cpu_to_be32(QCOW_MAGIC), + .version = cpu_to_be32(QCOW_VERSION), + .backing_file_offset = 0, + .backing_file_size = 0, + .cluster_bits = cpu_to_be32(s->cluster_bits), + .size = cpu_to_be64(total_size), + .crypt_method = cpu_to_be32(s->crypt_method_header), + .l1_size = cpu_to_be32(s->l1_size), + .l1_table_offset = cpu_to_be64(s->l1_table_offset), + .refcount_table_offset = cpu_to_be64(s->refcount_table_offset), + .refcount_table_clusters = cpu_to_be32(refcount_table_clusters), + .nb_snapshots = cpu_to_be32(s->nb_snapshots), + .snapshots_offset = cpu_to_be64(s->snapshots_offset), + }; - if (backing_file) { - if (backing_fmt) { - int padding = backing_fmt_len - - (sizeof(ext_backing_fmt) + strlen(backing_fmt)); + buf += sizeof(*header); + buflen -= sizeof(*header); - memcpy(buf + offset, &ext_backing_fmt, sizeof(ext_backing_fmt)); - offset += sizeof(ext_backing_fmt); + /* Backing file format header extension */ + if (*bs->backing_format) { + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_BACKING_FORMAT, + bs->backing_format, strlen(bs->backing_format), + buflen); + if (ret < 0) { + goto fail; + } - memcpy(buf + offset, backing_fmt, strlen(backing_fmt)); - offset += strlen(backing_fmt); + buf += ret; + buflen -= ret; + } - memset(buf + offset, 0, padding); - offset += padding; + /* Keep unknown header extensions */ + QLIST_FOREACH(uext, &s->unknown_header_ext, next) { + ret = header_ext_add(buf, uext->magic, uext->data, uext->len, buflen); + if (ret < 0) { + goto fail; } - memcpy(buf + offset, backing_file, backing_file_len); - backing_file_offset = sizeof(QCowHeader) + offset; + buf += ret; + buflen -= ret; } - ret = bdrv_pwrite_sync(bs->file, sizeof(QCowHeader), buf, ext_size); + /* End of header extensions */ + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_END, NULL, 0, buflen); if (ret < 0) { goto fail; } - /* Update header fields */ - uint64_t be_backing_file_offset = cpu_to_be64(backing_file_offset); - uint32_t be_backing_file_size = cpu_to_be32(backing_file_len); + buf += ret; + buflen -= ret; - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_offset), - &be_backing_file_offset, sizeof(uint64_t)); - if (ret < 0) { - goto fail; + /* Backing file name */ + if (*bs->backing_file) { + size_t backing_file_len = strlen(bs->backing_file); + + if (buflen < backing_file_len) { + ret = -ENOSPC; + goto fail; + } + + strncpy(buf, bs->backing_file, buflen); + + header->backing_file_offset = cpu_to_be64(buf - ((char*) header)); + header->backing_file_size = cpu_to_be32(backing_file_len); } - ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, backing_file_size), - &be_backing_file_size, sizeof(uint32_t)); + /* Write the new header */ + ret = bdrv_pwrite(bs->file, 0, header, s->cluster_size); if (ret < 0) { goto fail; } ret = 0; fail: + qemu_vfree(header); return ret; } static int qcow2_change_backing_file(BlockDriverState *bs, const char *backing_file, const char *backing_fmt) { - return qcow2_update_ext_header(bs, backing_file, backing_fmt); + /* Backing file format doesn't make sense without a backing file */ + if (backing_fmt && !backing_file) { + return -EINVAL; + } + + pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); + pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + + return qcow2_update_header(bs); } static int preallocate(BlockDriverState *bs) diff --git a/block/qcow2.h b/block/qcow2.h index 99e4536..fc35838 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -87,6 +87,13 @@ typedef struct QCowSnapshot { struct Qcow2Cache; typedef struct Qcow2Cache Qcow2Cache; +typedef struct Qcow2UnknownHeaderExtension { + uint32_t magic; + uint32_t len; + QLIST_ENTRY(Qcow2UnknownHeaderExtension) next; + uint8_t data[]; +} Qcow2UnknownHeaderExtension; + typedef struct BDRVQcowState { int cluster_bits; int cluster_size; @@ -127,6 +134,7 @@ typedef struct BDRVQcowState { QCowSnapshot *snapshots; int flags; + QLIST_HEAD(, Qcow2UnknownHeaderExtension) unknown_header_ext; } BDRVQcowState; /* XXX: use std qcow open function ? */ @@ -178,6 +186,7 @@ static inline int64_t align_offset(int64_t offset, int n) /* qcow2.c functions */ int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int64_t sector_num, int nb_sectors); +int qcow2_update_header(BlockDriverState *bs); /* qcow2-refcount.c functions */ int qcow2_refcount_init(BlockDriverState *bs); diff --git a/block/qed.c b/block/qed.c index 8da3ebe..a041d31 100644 --- a/block/qed.c +++ b/block/qed.c @@ -875,6 +875,12 @@ static void qed_aio_complete(QEDAIOCB *acb, int ret) qemu_iovec_destroy(&acb->cur_qiov); qed_unref_l2_cache_entry(acb->request.l2_table); + /* Free the buffer we may have allocated for zero writes */ + if (acb->flags & QED_AIOCB_ZERO) { + qemu_vfree(acb->qiov->iov[0].iov_base); + acb->qiov->iov[0].iov_base = NULL; + } + /* Arrange for a bh to invoke the completion function */ acb->bh_ret = ret; acb->bh = qemu_bh_new(qed_aio_complete_bh, acb); @@ -941,9 +947,8 @@ static void qed_aio_write_l1_update(void *opaque, int ret) /** * Update L2 table with new cluster offsets and write them out */ -static void qed_aio_write_l2_update(void *opaque, int ret) +static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset) { - QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); bool need_alloc = acb->find_cluster_ret == QED_CLUSTER_L1; int index; @@ -959,7 +964,7 @@ static void qed_aio_write_l2_update(void *opaque, int ret) index = qed_l2_index(s, acb->cur_pos); qed_update_l2_table(s, acb->request.l2_table->table, index, acb->cur_nclusters, - acb->cur_cluster); + offset); if (need_alloc) { /* Write out the whole new L2 table */ @@ -976,6 +981,12 @@ err: qed_aio_complete(acb, ret); } +static void qed_aio_write_l2_update_cb(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + qed_aio_write_l2_update(acb, ret, acb->cur_cluster); +} + /** * Flush new data clusters before updating the L2 table * @@ -990,7 +1001,7 @@ static void qed_aio_write_flush_before_l2_update(void *opaque, int ret) QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); - if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update, opaque)) { + if (!bdrv_aio_flush(s->bs->file, qed_aio_write_l2_update_cb, opaque)) { qed_aio_complete(acb, -EIO); } } @@ -1019,7 +1030,7 @@ static void qed_aio_write_main(void *opaque, int ret) if (s->bs->backing_hd) { next_fn = qed_aio_write_flush_before_l2_update; } else { - next_fn = qed_aio_write_l2_update; + next_fn = qed_aio_write_l2_update_cb; } } @@ -1081,6 +1092,18 @@ static bool qed_should_set_need_check(BDRVQEDState *s) return !(s->header.features & QED_F_NEED_CHECK); } +static void qed_aio_write_zero_cluster(void *opaque, int ret) +{ + QEDAIOCB *acb = opaque; + + if (ret) { + qed_aio_complete(acb, ret); + return; + } + + qed_aio_write_l2_update(acb, 0, 1); +} + /** * Write new data cluster * @@ -1092,6 +1115,7 @@ static bool qed_should_set_need_check(BDRVQEDState *s) static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) { BDRVQEDState *s = acb_to_s(acb); + BlockDriverCompletionFunc *cb; /* Cancel timer when the first allocating request comes in */ if (QSIMPLEQ_EMPTY(&s->allocating_write_reqs)) { @@ -1109,14 +1133,26 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) acb->cur_nclusters = qed_bytes_to_clusters(s, qed_offset_into_cluster(s, acb->cur_pos) + len); - acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters); qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); + if (acb->flags & QED_AIOCB_ZERO) { + /* Skip ahead if the clusters are already zero */ + if (acb->find_cluster_ret == QED_CLUSTER_ZERO) { + qed_aio_next_io(acb, 0); + return; + } + + cb = qed_aio_write_zero_cluster; + } else { + cb = qed_aio_write_prefill; + acb->cur_cluster = qed_alloc_clusters(s, acb->cur_nclusters); + } + if (qed_should_set_need_check(s)) { s->header.features |= QED_F_NEED_CHECK; - qed_write_header(s, qed_aio_write_prefill, acb); + qed_write_header(s, cb, acb); } else { - qed_aio_write_prefill(acb, 0); + cb(acb, 0); } } @@ -1131,6 +1167,16 @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len) */ static void qed_aio_write_inplace(QEDAIOCB *acb, uint64_t offset, size_t len) { + /* Allocate buffer for zero writes */ + if (acb->flags & QED_AIOCB_ZERO) { + struct iovec *iov = acb->qiov->iov; + + if (!iov->iov_base) { + iov->iov_base = qemu_blockalign(acb->common.bs, iov->iov_len); + memset(iov->iov_base, 0, iov->iov_len); + } + } + /* Calculate the I/O vector */ acb->cur_cluster = offset; qemu_iovec_copy(&acb->cur_qiov, acb->qiov, acb->qiov_offset, len); @@ -1233,8 +1279,8 @@ static void qed_aio_next_io(void *opaque, int ret) { QEDAIOCB *acb = opaque; BDRVQEDState *s = acb_to_s(acb); - QEDFindClusterFunc *io_fn = - acb->is_write ? qed_aio_write_data : qed_aio_read_data; + QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ? + qed_aio_write_data : qed_aio_read_data; trace_qed_aio_next_io(s, acb, ret, acb->cur_pos + acb->cur_qiov.size); @@ -1264,14 +1310,14 @@ static BlockDriverAIOCB *qed_aio_setup(BlockDriverState *bs, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, BlockDriverCompletionFunc *cb, - void *opaque, bool is_write) + void *opaque, int flags) { QEDAIOCB *acb = qemu_aio_get(&qed_aio_pool, bs, cb, opaque); trace_qed_aio_setup(bs->opaque, acb, sector_num, nb_sectors, - opaque, is_write); + opaque, flags); - acb->is_write = is_write; + acb->flags = flags; acb->finished = NULL; acb->qiov = qiov; acb->qiov_offset = 0; @@ -1291,7 +1337,7 @@ static BlockDriverAIOCB *bdrv_qed_aio_readv(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { - return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, false); + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, 0); } static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs, @@ -1300,7 +1346,8 @@ static BlockDriverAIOCB *bdrv_qed_aio_writev(BlockDriverState *bs, BlockDriverCompletionFunc *cb, void *opaque) { - return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, opaque, true); + return qed_aio_setup(bs, sector_num, qiov, nb_sectors, cb, + opaque, QED_AIOCB_WRITE); } static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs, @@ -1310,6 +1357,53 @@ static BlockDriverAIOCB *bdrv_qed_aio_flush(BlockDriverState *bs, return bdrv_aio_flush(bs->file, cb, opaque); } +typedef struct { + Coroutine *co; + int ret; + bool done; +} QEDWriteZeroesCB; + +static void coroutine_fn qed_co_write_zeroes_cb(void *opaque, int ret) +{ + QEDWriteZeroesCB *cb = opaque; + + cb->done = true; + cb->ret = ret; + if (cb->co) { + qemu_coroutine_enter(cb->co, NULL); + } +} + +static int coroutine_fn bdrv_qed_co_write_zeroes(BlockDriverState *bs, + int64_t sector_num, + int nb_sectors) +{ + BlockDriverAIOCB *blockacb; + QEDWriteZeroesCB cb = { .done = false }; + QEMUIOVector qiov; + struct iovec iov; + + /* Zero writes start without an I/O buffer. If a buffer becomes necessary + * then it will be allocated during request processing. + */ + iov.iov_base = NULL, + iov.iov_len = nb_sectors * BDRV_SECTOR_SIZE, + + qemu_iovec_init_external(&qiov, &iov, 1); + blockacb = qed_aio_setup(bs, sector_num, &qiov, nb_sectors, + qed_co_write_zeroes_cb, &cb, + QED_AIOCB_WRITE | QED_AIOCB_ZERO); + if (!blockacb) { + return -EIO; + } + if (!cb.done) { + cb.co = qemu_coroutine_self(); + qemu_coroutine_yield(); + } + assert(cb.done); + return cb.ret; +} + static int bdrv_qed_truncate(BlockDriverState *bs, int64_t offset) { BDRVQEDState *s = bs->opaque; @@ -1469,6 +1563,7 @@ static BlockDriver bdrv_qed = { .bdrv_aio_readv = bdrv_qed_aio_readv, .bdrv_aio_writev = bdrv_qed_aio_writev, .bdrv_aio_flush = bdrv_qed_aio_flush, + .bdrv_co_write_zeroes = bdrv_qed_co_write_zeroes, .bdrv_truncate = bdrv_qed_truncate, .bdrv_getlength = bdrv_qed_getlength, .bdrv_get_info = bdrv_qed_get_info, diff --git a/block/qed.h b/block/qed.h index 62cbd3b..62624a1 100644 --- a/block/qed.h +++ b/block/qed.h @@ -123,12 +123,17 @@ typedef struct QEDRequest { CachedL2Table *l2_table; } QEDRequest; +enum { + QED_AIOCB_WRITE = 0x0001, /* read or write? */ + QED_AIOCB_ZERO = 0x0002, /* zero write, used with QED_AIOCB_WRITE */ +}; + typedef struct QEDAIOCB { BlockDriverAIOCB common; QEMUBH *bh; int bh_ret; /* final return status for completion bh */ QSIMPLEQ_ENTRY(QEDAIOCB) next; /* next request */ - bool is_write; /* false - read, true - write */ + int flags; /* QED_AIOCB_* bits ORed together */ bool *finished; /* signal for cancel completion */ uint64_t end_pos; /* request end on block device, in bytes */ diff --git a/block/sheepdog.c b/block/sheepdog.c index 9416400..00276f6f 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -629,6 +629,9 @@ static void coroutine_fn aio_read_response(void *opaque) switch (acb->aiocb_type) { case AIOCB_WRITE_UDATA: + /* this coroutine context is no longer suitable for co_recv + * because we may send data to update vdi objects */ + s->co_recv = NULL; if (!is_data_obj(aio_req->oid)) { break; } diff --git a/block/vpc.c b/block/vpc.c index 89a5ee2..6b4816f 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -161,13 +161,27 @@ static int vpc_open(BlockDriverState *bs, int flags) uint8_t buf[HEADER_SIZE]; uint32_t checksum; int err = -1; + int disk_type = VHD_DYNAMIC; if (bdrv_pread(bs->file, 0, s->footer_buf, HEADER_SIZE) != HEADER_SIZE) goto fail; footer = (struct vhd_footer*) s->footer_buf; - if (strncmp(footer->creator, "conectix", 8)) - goto fail; + if (strncmp(footer->creator, "conectix", 8)) { + int64_t offset = bdrv_getlength(bs->file); + if (offset < HEADER_SIZE) { + goto fail; + } + /* If a fixed disk, the footer is found only at the end of the file */ + if (bdrv_pread(bs->file, offset-HEADER_SIZE, s->footer_buf, HEADER_SIZE) + != HEADER_SIZE) { + goto fail; + } + if (strncmp(footer->creator, "conectix", 8)) { + goto fail; + } + disk_type = VHD_FIXED; + } checksum = be32_to_cpu(footer->checksum); footer->checksum = 0; @@ -186,49 +200,54 @@ static int vpc_open(BlockDriverState *bs, int flags) goto fail; } - if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, HEADER_SIZE) - != HEADER_SIZE) - goto fail; - - dyndisk_header = (struct vhd_dyndisk_header*) buf; + if (disk_type == VHD_DYNAMIC) { + if (bdrv_pread(bs->file, be64_to_cpu(footer->data_offset), buf, + HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } - if (strncmp(dyndisk_header->magic, "cxsparse", 8)) - goto fail; + dyndisk_header = (struct vhd_dyndisk_header *) buf; + if (strncmp(dyndisk_header->magic, "cxsparse", 8)) { + goto fail; + } - s->block_size = be32_to_cpu(dyndisk_header->block_size); - s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; + s->block_size = be32_to_cpu(dyndisk_header->block_size); + s->bitmap_size = ((s->block_size / (8 * 512)) + 511) & ~511; - s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); - s->pagetable = g_malloc(s->max_table_entries * 4); + s->max_table_entries = be32_to_cpu(dyndisk_header->max_table_entries); + s->pagetable = g_malloc(s->max_table_entries * 4); - s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); - if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, - s->max_table_entries * 4) != s->max_table_entries * 4) - goto fail; + s->bat_offset = be64_to_cpu(dyndisk_header->table_offset); + if (bdrv_pread(bs->file, s->bat_offset, s->pagetable, + s->max_table_entries * 4) != s->max_table_entries * 4) { + goto fail; + } - s->free_data_block_offset = - (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; + s->free_data_block_offset = + (s->bat_offset + (s->max_table_entries * 4) + 511) & ~511; - for (i = 0; i < s->max_table_entries; i++) { - be32_to_cpus(&s->pagetable[i]); - if (s->pagetable[i] != 0xFFFFFFFF) { - int64_t next = (512 * (int64_t) s->pagetable[i]) + - s->bitmap_size + s->block_size; + for (i = 0; i < s->max_table_entries; i++) { + be32_to_cpus(&s->pagetable[i]); + if (s->pagetable[i] != 0xFFFFFFFF) { + int64_t next = (512 * (int64_t) s->pagetable[i]) + + s->bitmap_size + s->block_size; - if (next> s->free_data_block_offset) - s->free_data_block_offset = next; + if (next > s->free_data_block_offset) { + s->free_data_block_offset = next; + } + } } - } - s->last_bitmap_offset = (int64_t) -1; + s->last_bitmap_offset = (int64_t) -1; #ifdef CACHE - s->pageentry_u8 = g_malloc(512); - s->pageentry_u32 = s->pageentry_u8; - s->pageentry_u16 = s->pageentry_u8; - s->last_pagetable = -1; + s->pageentry_u8 = g_malloc(512); + s->pageentry_u32 = s->pageentry_u8; + s->pageentry_u16 = s->pageentry_u8; + s->last_pagetable = -1; #endif + } qemu_co_mutex_init(&s->lock); @@ -395,7 +414,11 @@ static int vpc_read(BlockDriverState *bs, int64_t sector_num, int ret; int64_t offset; int64_t sectors, sectors_per_block; + struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf; + if (cpu_to_be32(footer->type) == VHD_FIXED) { + return bdrv_read(bs->file, sector_num, buf, nb_sectors); + } while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 0); @@ -440,7 +463,11 @@ static int vpc_write(BlockDriverState *bs, int64_t sector_num, int64_t offset; int64_t sectors, sectors_per_block; int ret; + struct vhd_footer *footer = (struct vhd_footer *) s->footer_buf; + if (cpu_to_be32(footer->type) == VHD_FIXED) { + return bdrv_write(bs->file, sector_num, buf, nb_sectors); + } while (nb_sectors > 0) { offset = get_sector_offset(bs, sector_num, 1); @@ -533,70 +560,14 @@ static int calculate_geometry(int64_t total_sectors, uint16_t* cyls, return 0; } -static int vpc_create(const char *filename, QEMUOptionParameter *options) +static int create_dynamic_disk(int fd, uint8_t *buf, int64_t total_sectors) { - uint8_t buf[1024]; - struct vhd_footer* footer = (struct vhd_footer*) buf; struct vhd_dyndisk_header* dyndisk_header = (struct vhd_dyndisk_header*) buf; - int fd, i; - uint16_t cyls = 0; - uint8_t heads = 0; - uint8_t secs_per_cyl = 0; size_t block_size, num_bat_entries; - int64_t total_sectors = 0; + int i; int ret = -EIO; - // Read out options - total_sectors = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n / - BDRV_SECTOR_SIZE; - - // Create the file - fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); - if (fd < 0) - return -EIO; - - /* Calculate matching total_size and geometry. Increase the number of - sectors requested until we get enough (or fail). */ - for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { - if (calculate_geometry(total_sectors + i, - &cyls, &heads, &secs_per_cyl)) { - ret = -EFBIG; - goto fail; - } - } - total_sectors = (int64_t) cyls * heads * secs_per_cyl; - - // Prepare the Hard Disk Footer - memset(buf, 0, 1024); - - memcpy(footer->creator, "conectix", 8); - // TODO Check if "qemu" creator_app is ok for VPC - memcpy(footer->creator_app, "qemu", 4); - memcpy(footer->creator_os, "Wi2k", 4); - - footer->features = be32_to_cpu(0x02); - footer->version = be32_to_cpu(0x00010000); - footer->data_offset = be64_to_cpu(HEADER_SIZE); - footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); - - // Version of Virtual PC 2007 - footer->major = be16_to_cpu(0x0005); - footer->minor =be16_to_cpu(0x0003); - - footer->orig_size = be64_to_cpu(total_sectors * 512); - footer->size = be64_to_cpu(total_sectors * 512); - - footer->cyls = be16_to_cpu(cyls); - footer->heads = heads; - footer->secs_per_cyl = secs_per_cyl; - - footer->type = be32_to_cpu(VHD_DYNAMIC); - - // TODO uuid is missing - - footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); - // Write the footer (twice: at the beginning and at the end) block_size = 0x200000; num_bat_entries = (total_sectors + block_size / 512) / (block_size / 512); @@ -624,7 +595,6 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) } } - // Prepare the Dynamic Disk Header memset(buf, 0, 1024); @@ -653,6 +623,129 @@ static int vpc_create(const char *filename, QEMUOptionParameter *options) ret = 0; fail: + return ret; +} + +static int create_fixed_disk(int fd, uint8_t *buf, int64_t total_size) +{ + int ret = -EIO; + + /* Add footer to total size */ + total_size += 512; + if (ftruncate(fd, total_size) != 0) { + ret = -errno; + goto fail; + } + if (lseek(fd, -512, SEEK_END) < 0) { + goto fail; + } + if (write(fd, buf, HEADER_SIZE) != HEADER_SIZE) { + goto fail; + } + + ret = 0; + + fail: + return ret; +} + +static int vpc_create(const char *filename, QEMUOptionParameter *options) +{ + uint8_t buf[1024]; + struct vhd_footer *footer = (struct vhd_footer *) buf; + QEMUOptionParameter *disk_type_param; + int fd, i; + uint16_t cyls = 0; + uint8_t heads = 0; + uint8_t secs_per_cyl = 0; + int64_t total_sectors; + int64_t total_size; + int disk_type; + int ret = -EIO; + + /* Read out options */ + total_size = get_option_parameter(options, BLOCK_OPT_SIZE)->value.n; + + disk_type_param = get_option_parameter(options, BLOCK_OPT_SUBFMT); + if (disk_type_param && disk_type_param->value.s) { + if (!strcmp(disk_type_param->value.s, "dynamic")) { + disk_type = VHD_DYNAMIC; + } else if (!strcmp(disk_type_param->value.s, "fixed")) { + disk_type = VHD_FIXED; + } else { + return -EINVAL; + } + } else { + disk_type = VHD_DYNAMIC; + } + + /* Create the file */ + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); + if (fd < 0) { + return -EIO; + } + + /* + * Calculate matching total_size and geometry. Increase the number of + * sectors requested until we get enough (or fail). This ensures that + * qemu-img convert doesn't truncate images, but rather rounds up. + */ + total_sectors = total_size / BDRV_SECTOR_SIZE; + for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) { + if (calculate_geometry(total_sectors + i, &cyls, &heads, + &secs_per_cyl)) + { + ret = -EFBIG; + goto fail; + } + } + + total_sectors = (int64_t) cyls * heads * secs_per_cyl; + + /* Prepare the Hard Disk Footer */ + memset(buf, 0, 1024); + + memcpy(footer->creator, "conectix", 8); + /* TODO Check if "qemu" creator_app is ok for VPC */ + memcpy(footer->creator_app, "qemu", 4); + memcpy(footer->creator_os, "Wi2k", 4); + + footer->features = be32_to_cpu(0x02); + footer->version = be32_to_cpu(0x00010000); + if (disk_type == VHD_DYNAMIC) { + footer->data_offset = be64_to_cpu(HEADER_SIZE); + } else { + footer->data_offset = be64_to_cpu(0xFFFFFFFFFFFFFFFFULL); + } + footer->timestamp = be32_to_cpu(time(NULL) - VHD_TIMESTAMP_BASE); + + /* Version of Virtual PC 2007 */ + footer->major = be16_to_cpu(0x0005); + footer->minor = be16_to_cpu(0x0003); + if (disk_type == VHD_DYNAMIC) { + footer->orig_size = be64_to_cpu(total_sectors * 512); + footer->size = be64_to_cpu(total_sectors * 512); + } else { + footer->orig_size = be64_to_cpu(total_size); + footer->size = be64_to_cpu(total_size); + } + footer->cyls = be16_to_cpu(cyls); + footer->heads = heads; + footer->secs_per_cyl = secs_per_cyl; + + footer->type = be32_to_cpu(disk_type); + + /* TODO uuid is missing */ + + footer->checksum = be32_to_cpu(vpc_checksum(buf, HEADER_SIZE)); + + if (disk_type == VHD_DYNAMIC) { + ret = create_dynamic_disk(fd, buf, total_sectors); + } else { + ret = create_fixed_disk(fd, buf, total_size); + } + + fail: close(fd); return ret; } @@ -675,6 +768,13 @@ static QEMUOptionParameter vpc_create_options[] = { .type = OPT_SIZE, .help = "Virtual disk size" }, + { + .name = BLOCK_OPT_SUBFMT, + .type = OPT_STRING, + .help = + "Type of virtual hard disk format. Supported formats are " + "{dynamic (default) | fixed} " + }, { NULL } }; |