Merge remote-tracking branch 'remotes/stefanha/tags/block-pull-request' into staging

Pull request

# gpg: Signature made Tue 27 Aug 2019 21:16:27 BST
# gpg:                using RSA key 8695A8BFD3F97CDAAC35775A9CA4ABB381AB73C8
# gpg: Good signature from "Stefan Hajnoczi <stefanha@redhat.com>" [full]
# gpg:                 aka "Stefan Hajnoczi <stefanha@gmail.com>" [full]
# Primary key fingerprint: 8695 A8BF D3F9 7CDA AC35  775A 9CA4 ABB3 81AB 73C8

* remotes/stefanha/tags/block-pull-request:
  block/qcow2: implement .bdrv_co_pwritev(_compressed)_part
  block/qcow2: implement .bdrv_co_preadv_part
  block/qcow2: refactor qcow2_co_preadv to use buffer-based io
  block/io: introduce bdrv_co_p{read, write}v_part
  block/io: bdrv_aligned_pwritev: use and support qiov_offset
  block/io: bdrv_aligned_preadv: use and support qiov_offset
  block/io: bdrv_co_do_copy_on_readv: lazy allocation
  block/io: bdrv_co_do_copy_on_readv: use and support qiov_offset
  block: define .*_part io handlers in BlockDriver
  block/io: refactor padding
  util/iov: improve qemu_iovec_is_zero
  util/iov: introduce qemu_iovec_init_extended

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell
2019-09-03 09:43:26 +01:00
9 changed files with 569 additions and 310 deletions
+1 -1
View File
@@ -674,7 +674,7 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
return NULL;
}
if (compress && target->drv->bdrv_co_pwritev_compressed == NULL) {
if (compress && !block_driver_can_compress(target->drv)) {
error_setg(errp, "Compression is not supported for this drive %s",
bdrv_get_device_name(target));
return NULL;
+325 -218
View File
File diff suppressed because it is too large Load Diff
+9 -5
View File
@@ -452,8 +452,9 @@ static int coroutine_fn do_perform_cow_read(BlockDriverState *bs,
* 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_preadv(bs, src_cluster_offset + offset_in_cluster,
qiov->size, qiov, 0);
ret = bs->drv->bdrv_co_preadv_part(bs,
src_cluster_offset + offset_in_cluster,
qiov->size, qiov, 0, 0);
if (ret < 0) {
return ret;
}
@@ -828,7 +829,6 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
assert(start->nb_bytes <= UINT_MAX - end->nb_bytes);
assert(start->nb_bytes + end->nb_bytes <= UINT_MAX - data_bytes);
assert(start->offset + start->nb_bytes <= end->offset);
assert(!m->data_qiov || m->data_qiov->size == data_bytes);
if ((start->nb_bytes == 0 && end->nb_bytes == 0) || m->skip_cow) {
return 0;
@@ -860,7 +860,11 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
/* The part of the buffer where the end region is located */
end_buffer = start_buffer + buffer_size - end->nb_bytes;
qemu_iovec_init(&qiov, 2 + (m->data_qiov ? m->data_qiov->niov : 0));
qemu_iovec_init(&qiov, 2 + (m->data_qiov ?
qemu_iovec_subvec_niov(m->data_qiov,
m->data_qiov_offset,
data_bytes)
: 0));
qemu_co_mutex_unlock(&s->lock);
/* First we read the existing data from both COW regions. We
@@ -903,7 +907,7 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m)
if (start->nb_bytes) {
qemu_iovec_add(&qiov, start_buffer, start->nb_bytes);
}
qemu_iovec_concat(&qiov, m->data_qiov, 0, data_bytes);
qemu_iovec_concat(&qiov, m->data_qiov, m->data_qiov_offset, data_bytes);
if (end->nb_bytes) {
qemu_iovec_add(&qiov, end_buffer, end->nb_bytes);
}
+65 -66
View File
@@ -76,7 +76,8 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
uint64_t file_cluster_offset,
uint64_t offset,
uint64_t bytes,
QEMUIOVector *qiov);
QEMUIOVector *qiov,
size_t qiov_offset);
static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename)
{
@@ -1967,21 +1968,18 @@ out:
return ret;
}
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
static coroutine_fn int qcow2_co_preadv_part(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov,
size_t qiov_offset, int flags)
{
BDRVQcow2State *s = bs->opaque;
int offset_in_cluster;
int ret;
unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0;
uint64_t bytes_done = 0;
QEMUIOVector hd_qiov;
uint8_t *cluster_data = NULL;
qemu_iovec_init(&hd_qiov, qiov->niov);
while (bytes != 0) {
/* prepare next request */
@@ -2000,34 +1998,31 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
offset_in_cluster = offset_into_cluster(s, offset);
qemu_iovec_reset(&hd_qiov);
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
switch (ret) {
case QCOW2_CLUSTER_UNALLOCATED:
if (bs->backing) {
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
&hd_qiov, 0);
ret = bdrv_co_preadv_part(bs->backing, offset, cur_bytes,
qiov, qiov_offset, 0);
if (ret < 0) {
goto fail;
}
} else {
/* Note: in this case, no need to wait */
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
}
break;
case QCOW2_CLUSTER_ZERO_PLAIN:
case QCOW2_CLUSTER_ZERO_ALLOC:
qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
qemu_iovec_memset(qiov, qiov_offset, 0, cur_bytes);
break;
case QCOW2_CLUSTER_COMPRESSED:
ret = qcow2_co_preadv_compressed(bs, cluster_offset,
offset, cur_bytes,
&hd_qiov);
qiov, qiov_offset);
if (ret < 0) {
goto fail;
}
@@ -2059,19 +2054,15 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
}
assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
}
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
ret = bdrv_co_preadv(s->data_file,
cluster_offset + offset_in_cluster,
cur_bytes, &hd_qiov, 0);
if (ret < 0) {
goto fail;
}
if (bs->encrypted) {
assert(s->crypto);
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
ret = bdrv_co_pread(s->data_file,
cluster_offset + offset_in_cluster,
cur_bytes, cluster_data, 0);
if (ret < 0) {
goto fail;
}
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((cur_bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
if (qcow2_co_decrypt(bs, cluster_offset, offset,
@@ -2079,7 +2070,15 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
ret = -EIO;
goto fail;
}
qemu_iovec_from_buf(qiov, bytes_done, cluster_data, cur_bytes);
qemu_iovec_from_buf(qiov, qiov_offset, cluster_data, cur_bytes);
} else {
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
ret = bdrv_co_preadv_part(s->data_file,
cluster_offset + offset_in_cluster,
cur_bytes, qiov, qiov_offset, 0);
if (ret < 0) {
goto fail;
}
}
break;
@@ -2091,12 +2090,11 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
bytes -= cur_bytes;
offset += cur_bytes;
bytes_done += cur_bytes;
qiov_offset += cur_bytes;
}
ret = 0;
fail:
qemu_iovec_destroy(&hd_qiov);
qemu_vfree(cluster_data);
return ret;
@@ -2105,7 +2103,8 @@ fail:
/* Check if it's possible to merge a write request with the writing of
* the data from the COW regions */
static bool merge_cow(uint64_t offset, unsigned bytes,
QEMUIOVector *hd_qiov, QCowL2Meta *l2meta)
QEMUIOVector *qiov, size_t qiov_offset,
QCowL2Meta *l2meta)
{
QCowL2Meta *m;
@@ -2134,11 +2133,12 @@ static bool merge_cow(uint64_t offset, unsigned bytes,
/* Make sure that adding both COW regions to the QEMUIOVector
* does not exceed IOV_MAX */
if (hd_qiov->niov > IOV_MAX - 2) {
if (qemu_iovec_subvec_niov(qiov, qiov_offset, bytes) > IOV_MAX - 2) {
continue;
}
m->data_qiov = hd_qiov;
m->data_qiov = qiov;
m->data_qiov_offset = qiov_offset;
return true;
}
@@ -2220,24 +2220,22 @@ static int handle_alloc_space(BlockDriverState *bs, QCowL2Meta *l2meta)
return 0;
}
static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
static coroutine_fn int qcow2_co_pwritev_part(
BlockDriverState *bs, uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset, int flags)
{
BDRVQcow2State *s = bs->opaque;
int offset_in_cluster;
int ret;
unsigned int cur_bytes; /* number of sectors in current iteration */
uint64_t cluster_offset;
QEMUIOVector hd_qiov;
QEMUIOVector encrypted_qiov;
uint64_t bytes_done = 0;
uint8_t *cluster_data = NULL;
QCowL2Meta *l2meta = NULL;
trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes);
qemu_iovec_init(&hd_qiov, qiov->niov);
qemu_co_mutex_lock(&s->lock);
while (bytes != 0) {
@@ -2270,9 +2268,6 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
qemu_co_mutex_unlock(&s->lock);
qemu_iovec_reset(&hd_qiov);
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
if (bs->encrypted) {
assert(s->crypto);
if (!cluster_data) {
@@ -2285,9 +2280,9 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
}
}
assert(hd_qiov.size <=
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
qemu_iovec_to_buf(qiov, qiov_offset + bytes_done,
cluster_data, cur_bytes);
if (qcow2_co_encrypt(bs, cluster_offset, offset,
cluster_data, cur_bytes) < 0) {
@@ -2295,8 +2290,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
goto out_unlocked;
}
qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
qemu_iovec_init_buf(&encrypted_qiov, cluster_data, cur_bytes);
}
/* Try to efficiently initialize the physical space with zeroes */
@@ -2309,13 +2303,17 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
* writing of the guest data together with that of the COW regions.
* If it's not possible (or not necessary) then write the
* guest data now. */
if (!merge_cow(offset, cur_bytes, &hd_qiov, l2meta)) {
if (!merge_cow(offset, cur_bytes,
bs->encrypted ? &encrypted_qiov : qiov,
bs->encrypted ? 0 : qiov_offset + bytes_done, l2meta))
{
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
trace_qcow2_writev_data(qemu_coroutine_self(),
cluster_offset + offset_in_cluster);
ret = bdrv_co_pwritev(s->data_file,
cluster_offset + offset_in_cluster,
cur_bytes, &hd_qiov, 0);
ret = bdrv_co_pwritev_part(
s->data_file, cluster_offset + offset_in_cluster, cur_bytes,
bs->encrypted ? &encrypted_qiov : qiov,
bs->encrypted ? 0 : qiov_offset + bytes_done, 0);
if (ret < 0) {
goto out_unlocked;
}
@@ -2344,7 +2342,6 @@ out_locked:
qemu_co_mutex_unlock(&s->lock);
qemu_iovec_destroy(&hd_qiov);
qemu_vfree(cluster_data);
trace_qcow2_writev_done_req(qemu_coroutine_self(), ret);
@@ -4009,8 +4006,9 @@ fail:
/* XXX: put compressed sectors first, then all the cluster aligned
tables to avoid losing bytes in alignment */
static coroutine_fn int
qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov)
qcow2_co_pwritev_compressed_part(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset)
{
BDRVQcow2State *s = bs->opaque;
int ret;
@@ -4047,7 +4045,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
/* Zero-pad last write if image size is not cluster aligned */
memset(buf + bytes, 0, s->cluster_size - bytes);
}
qemu_iovec_to_buf(qiov, 0, buf, bytes);
qemu_iovec_to_buf(qiov, qiov_offset, buf, bytes);
out_buf = g_malloc(s->cluster_size);
@@ -4055,7 +4053,7 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset,
buf, s->cluster_size);
if (out_len == -ENOMEM) {
/* could not compress: write normal cluster */
ret = qcow2_co_pwritev(bs, offset, bytes, qiov, 0);
ret = qcow2_co_pwritev_part(bs, offset, bytes, qiov, qiov_offset, 0);
if (ret < 0) {
goto fail;
}
@@ -4097,7 +4095,8 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
uint64_t file_cluster_offset,
uint64_t offset,
uint64_t bytes,
QEMUIOVector *qiov)
QEMUIOVector *qiov,
size_t qiov_offset)
{
BDRVQcow2State *s = bs->opaque;
int ret = 0, csize, nb_csectors;
@@ -4128,7 +4127,7 @@ qcow2_co_preadv_compressed(BlockDriverState *bs,
goto fail;
}
qemu_iovec_from_buf(qiov, 0, out_buf + offset_in_cluster, bytes);
qemu_iovec_from_buf(qiov, qiov_offset, out_buf + offset_in_cluster, bytes);
fail:
qemu_vfree(out_buf);
@@ -4665,8 +4664,8 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
BDRVQcow2State *s = bs->opaque;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
return bs->drv->bdrv_co_pwritev(bs, qcow2_vm_state_offset(s) + pos,
qiov->size, qiov, 0);
return bs->drv->bdrv_co_pwritev_part(bs, qcow2_vm_state_offset(s) + pos,
qiov->size, qiov, 0, 0);
}
static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
@@ -4675,8 +4674,8 @@ static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
BDRVQcow2State *s = bs->opaque;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
return bs->drv->bdrv_co_preadv(bs, qcow2_vm_state_offset(s) + pos,
qiov->size, qiov, 0);
return bs->drv->bdrv_co_preadv_part(bs, qcow2_vm_state_offset(s) + pos,
qiov->size, qiov, 0, 0);
}
/*
@@ -5218,8 +5217,8 @@ BlockDriver bdrv_qcow2 = {
.bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
.bdrv_co_block_status = qcow2_co_block_status,
.bdrv_co_preadv = qcow2_co_preadv,
.bdrv_co_pwritev = qcow2_co_pwritev,
.bdrv_co_preadv_part = qcow2_co_preadv_part,
.bdrv_co_pwritev_part = qcow2_co_pwritev_part,
.bdrv_co_flush_to_os = qcow2_co_flush_to_os,
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,
@@ -5227,7 +5226,7 @@ BlockDriver bdrv_qcow2 = {
.bdrv_co_copy_range_from = qcow2_co_copy_range_from,
.bdrv_co_copy_range_to = qcow2_co_copy_range_to,
.bdrv_co_truncate = qcow2_co_truncate,
.bdrv_co_pwritev_compressed = qcow2_co_pwritev_compressed,
.bdrv_co_pwritev_compressed_part = qcow2_co_pwritev_compressed_part,
.bdrv_make_empty = qcow2_make_empty,
.bdrv_snapshot_create = qcow2_snapshot_create,
+1
View File
@@ -420,6 +420,7 @@ typedef struct QCowL2Meta
* from @cow_start and @cow_end into one single write operation.
*/
QEMUIOVector *data_qiov;
size_t data_qiov_offset;
/** Pointer to next L2Meta of the same write request */
struct QCowL2Meta *next;
+21
View File
@@ -210,6 +210,9 @@ struct BlockDriver {
*/
int coroutine_fn (*bdrv_co_preadv)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
int coroutine_fn (*bdrv_co_preadv_part)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset, int flags);
int coroutine_fn (*bdrv_co_writev)(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov, int flags);
/**
@@ -229,6 +232,9 @@ struct BlockDriver {
*/
int coroutine_fn (*bdrv_co_pwritev)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags);
int coroutine_fn (*bdrv_co_pwritev_part)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes,
QEMUIOVector *qiov, size_t qiov_offset, int flags);
/*
* Efficiently zero a region of the disk image. Typically an image format
@@ -339,6 +345,9 @@ struct BlockDriver {
int coroutine_fn (*bdrv_co_pwritev_compressed)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov);
int coroutine_fn (*bdrv_co_pwritev_compressed_part)(BlockDriverState *bs,
uint64_t offset, uint64_t bytes, QEMUIOVector *qiov,
size_t qiov_offset);
int (*bdrv_snapshot_create)(BlockDriverState *bs,
QEMUSnapshotInfo *sn_info);
@@ -570,6 +579,12 @@ struct BlockDriver {
const char *const *strong_runtime_opts;
};
static inline bool block_driver_can_compress(BlockDriver *drv)
{
return drv->bdrv_co_pwritev_compressed ||
drv->bdrv_co_pwritev_compressed_part;
}
typedef struct BlockLimits {
/* Alignment requirement, in bytes, for offset/length of I/O
* requests. Must be a power of 2 less than INT_MAX; defaults to
@@ -944,9 +959,15 @@ extern BlockDriver bdrv_qcow2;
int coroutine_fn bdrv_co_preadv(BdrvChild *child,
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
int64_t offset, unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags);
int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
BdrvRequestFlags flags);
int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
int64_t offset, unsigned int bytes,
QEMUIOVector *qiov, size_t qiov_offset, BdrvRequestFlags flags);
static inline int coroutine_fn bdrv_co_pread(BdrvChild *child,
int64_t offset, unsigned int bytes, void *buf, BdrvRequestFlags flags)
+9 -1
View File
@@ -199,13 +199,21 @@ static inline void *qemu_iovec_buf(QEMUIOVector *qiov)
void qemu_iovec_init(QEMUIOVector *qiov, int alloc_hint);
void qemu_iovec_init_external(QEMUIOVector *qiov, struct iovec *iov, int niov);
void qemu_iovec_init_extended(
QEMUIOVector *qiov,
void *head_buf, size_t head_len,
QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len,
void *tail_buf, size_t tail_len);
void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
size_t offset, size_t len);
int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len);
void qemu_iovec_add(QEMUIOVector *qiov, void *base, size_t len);
void qemu_iovec_concat(QEMUIOVector *dst,
QEMUIOVector *src, size_t soffset, size_t sbytes);
size_t qemu_iovec_concat_iov(QEMUIOVector *dst,
struct iovec *src_iov, unsigned int src_cnt,
size_t soffset, size_t sbytes);
bool qemu_iovec_is_zero(QEMUIOVector *qiov);
bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t qiov_offeset, size_t bytes);
void qemu_iovec_destroy(QEMUIOVector *qiov);
void qemu_iovec_reset(QEMUIOVector *qiov);
size_t qemu_iovec_to_buf(QEMUIOVector *qiov, size_t offset,
+2 -2
View File
@@ -2388,7 +2388,7 @@ static int img_convert(int argc, char **argv)
const char *preallocation =
qemu_opt_get(opts, BLOCK_OPT_PREALLOC);
if (drv && !drv->bdrv_co_pwritev_compressed) {
if (drv && !block_driver_can_compress(drv)) {
error_report("Compression not supported for this file format");
ret = -1;
goto out;
@@ -2459,7 +2459,7 @@ static int img_convert(int argc, char **argv)
}
out_bs = blk_bs(s.target);
if (s.compressed && !out_bs->drv->bdrv_co_pwritev_compressed) {
if (s.compressed && !block_driver_can_compress(out_bs->drv)) {
error_report("Compression not supported for this file format");
ret = -1;
goto out;
+136 -17
View File
@@ -354,34 +354,153 @@ void qemu_iovec_concat(QEMUIOVector *dst,
}
/*
* Check if the contents of the iovecs are all zero
* qiov_find_iov
*
* Return pointer to iovec structure, where byte at @offset in original vector
* @iov exactly is.
* Set @remaining_offset to be offset inside that iovec to the same byte.
*/
bool qemu_iovec_is_zero(QEMUIOVector *qiov)
static struct iovec *iov_skip_offset(struct iovec *iov, size_t offset,
size_t *remaining_offset)
{
int i;
for (i = 0; i < qiov->niov; i++) {
size_t offs = QEMU_ALIGN_DOWN(qiov->iov[i].iov_len, 4 * sizeof(long));
uint8_t *ptr = qiov->iov[i].iov_base;
if (offs && !buffer_is_zero(qiov->iov[i].iov_base, offs)) {
while (offset > 0 && offset >= iov->iov_len) {
offset -= iov->iov_len;
iov++;
}
*remaining_offset = offset;
return iov;
}
/*
* qiov_slice
*
* Find subarray of iovec's, containing requested range. @head would
* be offset in first iov (returned by the function), @tail would be
* count of extra bytes in last iovec (returned iov + @niov - 1).
*/
static struct iovec *qiov_slice(QEMUIOVector *qiov,
size_t offset, size_t len,
size_t *head, size_t *tail, int *niov)
{
struct iovec *iov, *end_iov;
assert(offset + len <= qiov->size);
iov = iov_skip_offset(qiov->iov, offset, head);
end_iov = iov_skip_offset(iov, *head + len, tail);
if (*tail > 0) {
assert(*tail < end_iov->iov_len);
*tail = end_iov->iov_len - *tail;
end_iov++;
}
*niov = end_iov - iov;
return iov;
}
int qemu_iovec_subvec_niov(QEMUIOVector *qiov, size_t offset, size_t len)
{
size_t head, tail;
int niov;
qiov_slice(qiov, offset, len, &head, &tail, &niov);
return niov;
}
/*
* Compile new iovec, combining @head_buf buffer, sub-qiov of @mid_qiov,
* and @tail_buf buffer into new qiov.
*/
void qemu_iovec_init_extended(
QEMUIOVector *qiov,
void *head_buf, size_t head_len,
QEMUIOVector *mid_qiov, size_t mid_offset, size_t mid_len,
void *tail_buf, size_t tail_len)
{
size_t mid_head, mid_tail;
int total_niov, mid_niov = 0;
struct iovec *p, *mid_iov;
if (mid_len) {
mid_iov = qiov_slice(mid_qiov, mid_offset, mid_len,
&mid_head, &mid_tail, &mid_niov);
}
total_niov = !!head_len + mid_niov + !!tail_len;
if (total_niov == 1) {
qemu_iovec_init_buf(qiov, NULL, 0);
p = &qiov->local_iov;
} else {
qiov->niov = qiov->nalloc = total_niov;
qiov->size = head_len + mid_len + tail_len;
p = qiov->iov = g_new(struct iovec, qiov->niov);
}
if (head_len) {
p->iov_base = head_buf;
p->iov_len = head_len;
p++;
}
if (mid_len) {
memcpy(p, mid_iov, mid_niov * sizeof(*p));
p[0].iov_base = (uint8_t *)p[0].iov_base + mid_head;
p[0].iov_len -= mid_head;
p[mid_niov - 1].iov_len -= mid_tail;
p += mid_niov;
}
if (tail_len) {
p->iov_base = tail_buf;
p->iov_len = tail_len;
}
}
/*
* Check if the contents of subrange of qiov data is all zeroes.
*/
bool qemu_iovec_is_zero(QEMUIOVector *qiov, size_t offset, size_t bytes)
{
struct iovec *iov;
size_t current_offset;
assert(offset + bytes <= qiov->size);
iov = iov_skip_offset(qiov->iov, offset, &current_offset);
while (bytes) {
uint8_t *base = (uint8_t *)iov->iov_base + current_offset;
size_t len = MIN(iov->iov_len - current_offset, bytes);
if (!buffer_is_zero(base, len)) {
return false;
}
for (; offs < qiov->iov[i].iov_len; offs++) {
if (ptr[offs]) {
return false;
}
}
current_offset = 0;
bytes -= len;
iov++;
}
return true;
}
void qemu_iovec_init_slice(QEMUIOVector *qiov, QEMUIOVector *source,
size_t offset, size_t len)
{
qemu_iovec_init_extended(qiov, NULL, 0, source, offset, len, NULL, 0);
}
void qemu_iovec_destroy(QEMUIOVector *qiov)
{
assert(qiov->nalloc != -1);
if (qiov->nalloc != -1) {
g_free(qiov->iov);
}
qemu_iovec_reset(qiov);
g_free(qiov->iov);
qiov->nalloc = 0;
qiov->iov = NULL;
memset(qiov, 0, sizeof(*qiov));
}
void qemu_iovec_reset(QEMUIOVector *qiov)