Block layer patches

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iQIcBAABAgAGBQJXYrE3AAoJEH8JsnLIjy/WYAgP/0EG2PZ4YmxbbN9H9Z2jn7Zc
 KCXgYnM3IkL7hcKPqT4UqRDcMlEUt2RqRJYcybLWMuIb36h5SF+Hz7z3lSV92oPC
 0n719DLp325+jsCcMm4kWT69lDMOCd7Xj69zjtgpu6eZgf1zpRZlWDWoZ1XphvC7
 jnXxjXnS9JUbzND2Bq0YpIo24qatHifsuh7h2We3kVDTEEnwyK2og9cWiWNnlfU8
 dC98sgaDMCo0BHbxvraFGDS56Hmh9Uh2uNzZ9J+g/kQyv4ySZcGxarsmoFX0mqXY
 jkYLrStTAXixdIRMo3mRNItXn7sXwBA1z+4DqO+FY+6wlYb6NX/5/1Lpdnb3ph/W
 HxJa+tc+aUocbPiT4eODbSxlRweyCU2TSPxv/36wuOyBh//S1zixbrMtJK1VZsXB
 5wt2sGPi6seYRFG3ywolxuB2OE1eKhxJhmVwyVbq5lWF5anuGaRT1yyUYQGq9LR9
 QKp1nzCt3UqhEg/k3qOrcVLPB4X/m2R09M3qdUjbtNTiRVKHSgdJWFgMwsTXXu1A
 fLIX6wSe0nSVPh8QMHWYlXHR1RZMUP5IYNPIOoGgBsaERjr3ewDsyRvYRa6DFdtw
 7To3CfasFyeXss8Uva8Cc+Iv4fKX89upCnNIuTk+YWuikreInrblFvWHQ1CJ65XQ
 eEn0T7XfTcZwR50z7YEk
 =oSJo
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/kevin/tags/for-upstream' into staging

Block layer patches

# gpg: Signature made Thu 16 Jun 2016 15:01:27 BST
# gpg:                using RSA key 0x7F09B272C88F2FD6
# gpg: Good signature from "Kevin Wolf <kwolf@redhat.com>"
# Primary key fingerprint: DC3D EB15 9A9A F95D 3D74  56FE 7F09 B272 C88F 2FD6

* remotes/kevin/tags/for-upstream: (39 commits)
  hbitmap: add 'pos < size' asserts
  iotests: Add test for oVirt-like storage migration
  iotests: Add test for post-mirror backing chains
  block/null: Implement bdrv_refresh_filename()
  block/mirror: Fix target backing BDS
  block: Allow replacement of a BDS by its overlay
  rbd:change error_setg() to error_setg_errno()
  iotests: 095: Clean up QEMU before showing image info
  block: Create the commit block job before reopening any image
  block: Prevent sleeping jobs from resuming if they have been paused
  block: use the block job list in qmp_query_block_jobs()
  block: use the block job list in bdrv_drain_all()
  block: Fix snapshot=on with aio=native
  block: Remove bs->zero_beyond_eof
  qcow2: Let vmstate call qcow2_co_preadv/pwrite directly
  block: Make bdrv_load/save_vmstate coroutine_fns
  block: Allow .bdrv_load/save_vmstate() to return 0/-errno
  block: Make .bdrv_load_vmstate() vectored
  block: Introduce bdrv_preadv()
  doc: Fix mailing list address in tests/qemu-iotests/README
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2016-06-16 15:22:56 +01:00
commit dc278c58fa
31 changed files with 1187 additions and 488 deletions

32
block.c
View File

@ -684,6 +684,10 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options,
/* For temporary files, unconditional cache=unsafe is fine */ /* For temporary files, unconditional cache=unsafe is fine */
qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off"); qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off");
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
/* aio=native doesn't work for cache.direct=off, so disable it for the
* temporary snapshot */
*child_flags &= ~BDRV_O_NATIVE_AIO;
} }
/* /*
@ -937,8 +941,7 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file,
goto fail_opts; goto fail_opts;
} }
bs->request_alignment = 512; bs->request_alignment = drv->bdrv_co_preadv ? 1 : 512;
bs->zero_beyond_eof = true;
bs->read_only = !(bs->open_flags & BDRV_O_RDWR); bs->read_only = !(bs->open_flags & BDRV_O_RDWR);
if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) {
@ -2192,7 +2195,6 @@ static void bdrv_close(BlockDriverState *bs)
bs->encrypted = 0; bs->encrypted = 0;
bs->valid_key = 0; bs->valid_key = 0;
bs->sg = 0; bs->sg = 0;
bs->zero_beyond_eof = false;
QDECREF(bs->options); QDECREF(bs->options);
QDECREF(bs->explicit_options); QDECREF(bs->explicit_options);
bs->options = NULL; bs->options = NULL;
@ -2224,9 +2226,23 @@ void bdrv_close_all(void)
static void change_parent_backing_link(BlockDriverState *from, static void change_parent_backing_link(BlockDriverState *from,
BlockDriverState *to) BlockDriverState *to)
{ {
BdrvChild *c, *next; BdrvChild *c, *next, *to_c;
QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) { QLIST_FOREACH_SAFE(c, &from->parents, next_parent, next) {
if (c->role == &child_backing) {
/* @from is generally not allowed to be a backing file, except for
* when @to is the overlay. In that case, @from may not be replaced
* by @to as @to's backing node. */
QLIST_FOREACH(to_c, &to->children, next) {
if (to_c == c) {
break;
}
}
if (to_c) {
continue;
}
}
assert(c->role != &child_backing); assert(c->role != &child_backing);
bdrv_ref(to); bdrv_ref(to);
bdrv_replace_child(c, to); bdrv_replace_child(c, to);
@ -2275,14 +2291,6 @@ void bdrv_replace_in_backing_chain(BlockDriverState *old, BlockDriverState *new)
change_parent_backing_link(old, new); change_parent_backing_link(old, new);
/* Change backing files if a previously independent node is added to the
* chain. For active commit, we replace top by its own (indirect) backing
* file and don't do anything here so we don't build a loop. */
if (new->backing == NULL && !bdrv_chain_contains(backing_bs(old), new)) {
bdrv_set_backing_hd(new, backing_bs(old));
bdrv_set_backing_hd(old, NULL);
}
bdrv_unref(old); bdrv_unref(old);
} }

View File

@ -236,6 +236,11 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
return; return;
} }
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
orig_base_flags = bdrv_get_flags(base); orig_base_flags = bdrv_get_flags(base);
orig_overlay_flags = bdrv_get_flags(overlay_bs); orig_overlay_flags = bdrv_get_flags(overlay_bs);
@ -252,16 +257,12 @@ void commit_start(BlockDriverState *bs, BlockDriverState *base,
bdrv_reopen_multiple(reopen_queue, &local_err); bdrv_reopen_multiple(reopen_queue, &local_err);
if (local_err != NULL) { if (local_err != NULL) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
block_job_unref(&s->common);
return; return;
} }
} }
s = block_job_create(&commit_job_driver, bs, speed, cb, opaque, errp);
if (!s) {
return;
}
s->base = blk_new(); s->base = blk_new();
blk_insert_bs(s->base, base); blk_insert_bs(s->base, base);

View File

@ -289,15 +289,21 @@ void bdrv_drain_all(void)
bool busy = true; bool busy = true;
BlockDriverState *bs; BlockDriverState *bs;
BdrvNextIterator it; BdrvNextIterator it;
BlockJob *job = NULL;
GSList *aio_ctxs = NULL, *ctx; GSList *aio_ctxs = NULL, *ctx;
while ((job = block_job_next(job))) {
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
block_job_pause(job);
aio_context_release(aio_context);
}
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
AioContext *aio_context = bdrv_get_aio_context(bs); AioContext *aio_context = bdrv_get_aio_context(bs);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
if (bs->job) {
block_job_pause(bs->job);
}
bdrv_parent_drained_begin(bs); bdrv_parent_drained_begin(bs);
bdrv_io_unplugged_begin(bs); bdrv_io_unplugged_begin(bs);
bdrv_drain_recurse(bs); bdrv_drain_recurse(bs);
@ -340,12 +346,18 @@ void bdrv_drain_all(void)
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
bdrv_io_unplugged_end(bs); bdrv_io_unplugged_end(bs);
bdrv_parent_drained_end(bs); bdrv_parent_drained_end(bs);
if (bs->job) {
block_job_resume(bs->job);
}
aio_context_release(aio_context); aio_context_release(aio_context);
} }
g_slist_free(aio_ctxs); g_slist_free(aio_ctxs);
job = NULL;
while ((job = block_job_next(job))) {
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context);
block_job_resume(job);
aio_context_release(aio_context);
}
} }
/** /**
@ -404,12 +416,12 @@ static void mark_request_serialising(BdrvTrackedRequest *req, uint64_t align)
} }
/** /**
* Round a region to cluster boundaries * Round a region to cluster boundaries (sector-based)
*/ */
void bdrv_round_to_clusters(BlockDriverState *bs, void bdrv_round_sectors_to_clusters(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int64_t sector_num, int nb_sectors,
int64_t *cluster_sector_num, int64_t *cluster_sector_num,
int *cluster_nb_sectors) int *cluster_nb_sectors)
{ {
BlockDriverInfo bdi; BlockDriverInfo bdi;
@ -424,6 +436,26 @@ void bdrv_round_to_clusters(BlockDriverState *bs,
} }
} }
/**
* Round a region to cluster boundaries
*/
void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t offset, unsigned int bytes,
int64_t *cluster_offset,
unsigned int *cluster_bytes)
{
BlockDriverInfo bdi;
if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) {
*cluster_offset = offset;
*cluster_bytes = bytes;
} else {
int64_t c = bdi.cluster_size;
*cluster_offset = QEMU_ALIGN_DOWN(offset, c);
*cluster_bytes = QEMU_ALIGN_UP(offset - *cluster_offset + bytes, c);
}
}
static int bdrv_get_cluster_size(BlockDriverState *bs) static int bdrv_get_cluster_size(BlockDriverState *bs)
{ {
BlockDriverInfo bdi; BlockDriverInfo bdi;
@ -680,6 +712,18 @@ int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags)
} }
} }
int bdrv_preadv(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov)
{
int ret;
ret = bdrv_prwv_co(bs, offset, qiov, false, 0);
if (ret < 0) {
return ret;
}
return qiov->size;
}
int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes) int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes)
{ {
QEMUIOVector qiov; QEMUIOVector qiov;
@ -687,19 +731,13 @@ int bdrv_pread(BlockDriverState *bs, int64_t offset, void *buf, int bytes)
.iov_base = (void *)buf, .iov_base = (void *)buf,
.iov_len = bytes, .iov_len = bytes,
}; };
int ret;
if (bytes < 0) { if (bytes < 0) {
return -EINVAL; return -EINVAL;
} }
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
ret = bdrv_prwv_co(bs, offset, &qiov, false, 0); return bdrv_preadv(bs, offset, &qiov);
if (ret < 0) {
return ret;
}
return bytes;
} }
int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov) int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov)
@ -776,6 +814,8 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
int64_t sector_num; int64_t sector_num;
unsigned int nb_sectors; unsigned int nb_sectors;
assert(!(flags & ~BDRV_REQ_MASK));
if (drv->bdrv_co_preadv) { if (drv->bdrv_co_preadv) {
return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags); return drv->bdrv_co_preadv(bs, offset, bytes, qiov, flags);
} }
@ -815,6 +855,8 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs,
unsigned int nb_sectors; unsigned int nb_sectors;
int ret; int ret;
assert(!(flags & ~BDRV_REQ_MASK));
if (drv->bdrv_co_pwritev) { if (drv->bdrv_co_pwritev) {
ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov, ret = drv->bdrv_co_pwritev(bs, offset, bytes, qiov,
flags & bs->supported_write_flags); flags & bs->supported_write_flags);
@ -861,7 +903,7 @@ emulate_flags:
} }
static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs, static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, QEMUIOVector *qiov) int64_t offset, unsigned int bytes, QEMUIOVector *qiov)
{ {
/* Perform I/O through a temporary buffer so that users who scribble over /* Perform I/O through a temporary buffer so that users who scribble over
* their read buffer while the operation is in progress do not end up * their read buffer while the operation is in progress do not end up
@ -873,21 +915,20 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
struct iovec iov; struct iovec iov;
QEMUIOVector bounce_qiov; QEMUIOVector bounce_qiov;
int64_t cluster_sector_num; int64_t cluster_offset;
int cluster_nb_sectors; unsigned int cluster_bytes;
size_t skip_bytes; size_t skip_bytes;
int ret; int ret;
/* Cover entire cluster so no additional backing file I/O is required when /* Cover entire cluster so no additional backing file I/O is required when
* allocating cluster in the image file. * allocating cluster in the image file.
*/ */
bdrv_round_to_clusters(bs, sector_num, nb_sectors, bdrv_round_to_clusters(bs, offset, bytes, &cluster_offset, &cluster_bytes);
&cluster_sector_num, &cluster_nb_sectors);
trace_bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, trace_bdrv_co_do_copy_on_readv(bs, offset, bytes,
cluster_sector_num, cluster_nb_sectors); cluster_offset, cluster_bytes);
iov.iov_len = cluster_nb_sectors * BDRV_SECTOR_SIZE; iov.iov_len = cluster_bytes;
iov.iov_base = bounce_buffer = qemu_try_blockalign(bs, iov.iov_len); iov.iov_base = bounce_buffer = qemu_try_blockalign(bs, iov.iov_len);
if (bounce_buffer == NULL) { if (bounce_buffer == NULL) {
ret = -ENOMEM; ret = -ENOMEM;
@ -896,8 +937,7 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
qemu_iovec_init_external(&bounce_qiov, &iov, 1); qemu_iovec_init_external(&bounce_qiov, &iov, 1);
ret = bdrv_driver_preadv(bs, cluster_sector_num * BDRV_SECTOR_SIZE, ret = bdrv_driver_preadv(bs, cluster_offset, cluster_bytes,
cluster_nb_sectors * BDRV_SECTOR_SIZE,
&bounce_qiov, 0); &bounce_qiov, 0);
if (ret < 0) { if (ret < 0) {
goto err; goto err;
@ -905,16 +945,12 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
if (drv->bdrv_co_pwrite_zeroes && if (drv->bdrv_co_pwrite_zeroes &&
buffer_is_zero(bounce_buffer, iov.iov_len)) { buffer_is_zero(bounce_buffer, iov.iov_len)) {
ret = bdrv_co_do_pwrite_zeroes(bs, ret = bdrv_co_do_pwrite_zeroes(bs, cluster_offset, cluster_bytes, 0);
cluster_sector_num * BDRV_SECTOR_SIZE,
cluster_nb_sectors * BDRV_SECTOR_SIZE,
0);
} else { } else {
/* This does not change the data on the disk, it is not necessary /* This does not change the data on the disk, it is not necessary
* to flush even in cache=writethrough mode. * to flush even in cache=writethrough mode.
*/ */
ret = bdrv_driver_pwritev(bs, cluster_sector_num * BDRV_SECTOR_SIZE, ret = bdrv_driver_pwritev(bs, cluster_offset, cluster_bytes,
cluster_nb_sectors * BDRV_SECTOR_SIZE,
&bounce_qiov, 0); &bounce_qiov, 0);
} }
@ -926,9 +962,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BlockDriverState *bs,
goto err; goto err;
} }
skip_bytes = (sector_num - cluster_sector_num) * BDRV_SECTOR_SIZE; skip_bytes = offset - cluster_offset;
qemu_iovec_from_buf(qiov, 0, bounce_buffer + skip_bytes, qemu_iovec_from_buf(qiov, 0, bounce_buffer + skip_bytes, bytes);
nb_sectors * BDRV_SECTOR_SIZE);
err: err:
qemu_vfree(bounce_buffer); qemu_vfree(bounce_buffer);
@ -944,15 +979,15 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
BdrvTrackedRequest *req, int64_t offset, unsigned int bytes, BdrvTrackedRequest *req, int64_t offset, unsigned int bytes,
int64_t align, QEMUIOVector *qiov, int flags) int64_t align, QEMUIOVector *qiov, int flags)
{ {
int64_t total_bytes, max_bytes;
int ret; int ret;
int64_t sector_num = offset >> BDRV_SECTOR_BITS; assert(is_power_of_2(align));
unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS; assert((offset & (align - 1)) == 0);
assert((bytes & (align - 1)) == 0);
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
assert(!qiov || bytes == qiov->size); assert(!qiov || bytes == qiov->size);
assert((bs->open_flags & BDRV_O_NO_IO) == 0); assert((bs->open_flags & BDRV_O_NO_IO) == 0);
assert(!(flags & ~BDRV_REQ_MASK));
/* Handle Copy on Read and associated serialisation */ /* Handle Copy on Read and associated serialisation */
if (flags & BDRV_REQ_COPY_ON_READ) { if (flags & BDRV_REQ_COPY_ON_READ) {
@ -969,59 +1004,50 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,
} }
if (flags & BDRV_REQ_COPY_ON_READ) { if (flags & BDRV_REQ_COPY_ON_READ) {
int64_t start_sector = offset >> BDRV_SECTOR_BITS;
int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
unsigned int nb_sectors = end_sector - start_sector;
int pnum; int pnum;
ret = bdrv_is_allocated(bs, sector_num, nb_sectors, &pnum); ret = bdrv_is_allocated(bs, start_sector, nb_sectors, &pnum);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
if (!ret || pnum != nb_sectors) { if (!ret || pnum != nb_sectors) {
ret = bdrv_co_do_copy_on_readv(bs, sector_num, nb_sectors, qiov); ret = bdrv_co_do_copy_on_readv(bs, offset, bytes, qiov);
goto out; goto out;
} }
} }
/* Forward the request to the BlockDriver */ /* Forward the request to the BlockDriver */
if (!bs->zero_beyond_eof) { total_bytes = bdrv_getlength(bs);
if (total_bytes < 0) {
ret = total_bytes;
goto out;
}
max_bytes = ROUND_UP(MAX(0, total_bytes - offset), align);
if (bytes < max_bytes) {
ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0); ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0);
} else if (max_bytes > 0) {
QEMUIOVector local_qiov;
qemu_iovec_init(&local_qiov, qiov->niov);
qemu_iovec_concat(&local_qiov, qiov, 0, max_bytes);
ret = bdrv_driver_preadv(bs, offset, max_bytes, &local_qiov, 0);
qemu_iovec_destroy(&local_qiov);
} else { } else {
/* Read zeros after EOF */ ret = 0;
int64_t total_sectors, max_nb_sectors; }
total_sectors = bdrv_nb_sectors(bs); /* Reading beyond end of file is supposed to produce zeroes */
if (total_sectors < 0) { if (ret == 0 && total_bytes < offset + bytes) {
ret = total_sectors; uint64_t zero_offset = MAX(0, total_bytes - offset);
goto out; uint64_t zero_bytes = offset + bytes - zero_offset;
} qemu_iovec_memset(qiov, zero_offset, 0, zero_bytes);
max_nb_sectors = ROUND_UP(MAX(0, total_sectors - sector_num),
align >> BDRV_SECTOR_BITS);
if (nb_sectors < max_nb_sectors) {
ret = bdrv_driver_preadv(bs, offset, bytes, qiov, 0);
} else if (max_nb_sectors > 0) {
QEMUIOVector local_qiov;
qemu_iovec_init(&local_qiov, qiov->niov);
qemu_iovec_concat(&local_qiov, qiov, 0,
max_nb_sectors * BDRV_SECTOR_SIZE);
ret = bdrv_driver_preadv(bs, offset,
max_nb_sectors * BDRV_SECTOR_SIZE,
&local_qiov, 0);
qemu_iovec_destroy(&local_qiov);
} else {
ret = 0;
}
/* Reading beyond end of file is supposed to produce zeroes */
if (ret == 0 && total_sectors < sector_num + nb_sectors) {
uint64_t offset = MAX(0, total_sectors - sector_num);
uint64_t bytes = (sector_num + nb_sectors - offset) *
BDRV_SECTOR_SIZE;
qemu_iovec_memset(qiov, offset * BDRV_SECTOR_SIZE, 0, bytes);
}
} }
out: out:
@ -1038,8 +1064,7 @@ int coroutine_fn bdrv_co_preadv(BlockDriverState *bs,
BlockDriver *drv = bs->drv; BlockDriver *drv = bs->drv;
BdrvTrackedRequest req; BdrvTrackedRequest req;
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */ uint64_t align = bs->request_alignment;
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
uint8_t *head_buf = NULL; uint8_t *head_buf = NULL;
uint8_t *tail_buf = NULL; uint8_t *tail_buf = NULL;
QEMUIOVector local_qiov; QEMUIOVector local_qiov;
@ -1235,13 +1260,12 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
bool waited; bool waited;
int ret; int ret;
int64_t sector_num = offset >> BDRV_SECTOR_BITS; int64_t start_sector = offset >> BDRV_SECTOR_BITS;
unsigned int nb_sectors = bytes >> BDRV_SECTOR_BITS; int64_t end_sector = DIV_ROUND_UP(offset + bytes, BDRV_SECTOR_SIZE);
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
assert(!qiov || bytes == qiov->size); assert(!qiov || bytes == qiov->size);
assert((bs->open_flags & BDRV_O_NO_IO) == 0); assert((bs->open_flags & BDRV_O_NO_IO) == 0);
assert(!(flags & ~BDRV_REQ_MASK));
waited = wait_serialising_requests(req); waited = wait_serialising_requests(req);
assert(!waited || !req->serialising); assert(!waited || !req->serialising);
@ -1263,22 +1287,21 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,
/* Do nothing, write notifier decided to fail this request */ /* Do nothing, write notifier decided to fail this request */
} else if (flags & BDRV_REQ_ZERO_WRITE) { } else if (flags & BDRV_REQ_ZERO_WRITE) {
bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO); bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
ret = bdrv_co_do_pwrite_zeroes(bs, sector_num << BDRV_SECTOR_BITS, ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
nb_sectors << BDRV_SECTOR_BITS, flags);
} else { } else {
bdrv_debug_event(bs, BLKDBG_PWRITEV); bdrv_debug_event(bs, BLKDBG_PWRITEV);
ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags); ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, flags);
} }
bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE); bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
bdrv_set_dirty(bs, sector_num, nb_sectors); bdrv_set_dirty(bs, start_sector, end_sector - start_sector);
if (bs->wr_highest_offset < offset + bytes) { if (bs->wr_highest_offset < offset + bytes) {
bs->wr_highest_offset = offset + bytes; bs->wr_highest_offset = offset + bytes;
} }
if (ret >= 0) { if (ret >= 0) {
bs->total_sectors = MAX(bs->total_sectors, sector_num + nb_sectors); bs->total_sectors = MAX(bs->total_sectors, end_sector);
} }
return ret; return ret;
@ -1293,7 +1316,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BlockDriverState *bs,
uint8_t *buf = NULL; uint8_t *buf = NULL;
QEMUIOVector local_qiov; QEMUIOVector local_qiov;
struct iovec iov; struct iovec iov;
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment); uint64_t align = bs->request_alignment;
unsigned int head_padding_bytes, tail_padding_bytes; unsigned int head_padding_bytes, tail_padding_bytes;
int ret = 0; int ret = 0;
@ -1380,8 +1403,7 @@ int coroutine_fn bdrv_co_pwritev(BlockDriverState *bs,
BdrvRequestFlags flags) BdrvRequestFlags flags)
{ {
BdrvTrackedRequest req; BdrvTrackedRequest req;
/* TODO Lift BDRV_SECTOR_SIZE restriction in BlockDriver interface */ uint64_t align = bs->request_alignment;
uint64_t align = MAX(BDRV_SECTOR_SIZE, bs->request_alignment);
uint8_t *head_buf = NULL; uint8_t *head_buf = NULL;
uint8_t *tail_buf = NULL; uint8_t *tail_buf = NULL;
QEMUIOVector local_qiov; QEMUIOVector local_qiov;
@ -1824,6 +1846,62 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors); return drv->bdrv_write_compressed(bs, sector_num, buf, nb_sectors);
} }
typedef struct BdrvVmstateCo {
BlockDriverState *bs;
QEMUIOVector *qiov;
int64_t pos;
bool is_read;
int ret;
} BdrvVmstateCo;
static int coroutine_fn
bdrv_co_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
bool is_read)
{
BlockDriver *drv = bs->drv;
if (!drv) {
return -ENOMEDIUM;
} else if (drv->bdrv_load_vmstate) {
return is_read ? drv->bdrv_load_vmstate(bs, qiov, pos)
: drv->bdrv_save_vmstate(bs, qiov, pos);
} else if (bs->file) {
return bdrv_co_rw_vmstate(bs->file->bs, qiov, pos, is_read);
}
return -ENOTSUP;
}
static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque)
{
BdrvVmstateCo *co = opaque;
co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read);
}
static inline int
bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos,
bool is_read)
{
if (qemu_in_coroutine()) {
return bdrv_co_rw_vmstate(bs, qiov, pos, is_read);
} else {
BdrvVmstateCo data = {
.bs = bs,
.qiov = qiov,
.pos = pos,
.is_read = is_read,
.ret = -EINPROGRESS,
};
Coroutine *co = qemu_coroutine_create(bdrv_co_rw_vmstate_entry);
qemu_coroutine_enter(co, &data);
while (data.ret == -EINPROGRESS) {
aio_poll(bdrv_get_aio_context(bs), true);
}
return data.ret;
}
}
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size) int64_t pos, int size)
{ {
@ -1832,37 +1910,45 @@ int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
.iov_base = (void *) buf, .iov_base = (void *) buf,
.iov_len = size, .iov_len = size,
}; };
int ret;
qemu_iovec_init_external(&qiov, &iov, 1); qemu_iovec_init_external(&qiov, &iov, 1);
return bdrv_writev_vmstate(bs, &qiov, pos);
ret = bdrv_writev_vmstate(bs, &qiov, pos);
if (ret < 0) {
return ret;
}
return size;
} }
int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
{ {
BlockDriver *drv = bs->drv; return bdrv_rw_vmstate(bs, qiov, pos, false);
if (!drv) {
return -ENOMEDIUM;
} else if (drv->bdrv_save_vmstate) {
return drv->bdrv_save_vmstate(bs, qiov, pos);
} else if (bs->file) {
return bdrv_writev_vmstate(bs->file->bs, qiov, pos);
}
return -ENOTSUP;
} }
int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf,
int64_t pos, int size) int64_t pos, int size)
{ {
BlockDriver *drv = bs->drv; QEMUIOVector qiov;
if (!drv) struct iovec iov = {
return -ENOMEDIUM; .iov_base = buf,
if (drv->bdrv_load_vmstate) .iov_len = size,
return drv->bdrv_load_vmstate(bs, buf, pos, size); };
if (bs->file) int ret;
return bdrv_load_vmstate(bs->file->bs, buf, pos, size);
return -ENOTSUP; qemu_iovec_init_external(&qiov, &iov, 1);
ret = bdrv_readv_vmstate(bs, &qiov, pos);
if (ret < 0) {
return ret;
}
return size;
}
int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
{
return bdrv_rw_vmstate(bs, qiov, pos, true);
} }
/**************************************************************/ /**************************************************************/

View File

@ -11,8 +11,10 @@
#include "qemu-common.h" #include "qemu-common.h"
#include "block/aio.h" #include "block/aio.h"
#include "qemu/queue.h" #include "qemu/queue.h"
#include "block/block.h"
#include "block/raw-aio.h" #include "block/raw-aio.h"
#include "qemu/event_notifier.h" #include "qemu/event_notifier.h"
#include "qemu/coroutine.h"
#include <libaio.h> #include <libaio.h>
@ -30,6 +32,7 @@
struct qemu_laiocb { struct qemu_laiocb {
BlockAIOCB common; BlockAIOCB common;
Coroutine *co;
LinuxAioState *ctx; LinuxAioState *ctx;
struct iocb iocb; struct iocb iocb;
ssize_t ret; ssize_t ret;
@ -88,9 +91,14 @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
} }
} }
} }
laiocb->common.cb(laiocb->common.opaque, ret);
qemu_aio_unref(laiocb); laiocb->ret = ret;
if (laiocb->co) {
qemu_coroutine_enter(laiocb->co, NULL);
} else {
laiocb->common.cb(laiocb->common.opaque, ret);
qemu_aio_unref(laiocb);
}
} }
/* The completion BH fetches completed I/O requests and invokes their /* The completion BH fetches completed I/O requests and invokes their
@ -141,6 +149,8 @@ static void qemu_laio_completion_bh(void *opaque)
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
ioq_submit(s); ioq_submit(s);
} }
qemu_bh_cancel(s->completion_bh);
} }
static void qemu_laio_completion_cb(EventNotifier *e) static void qemu_laio_completion_cb(EventNotifier *e)
@ -148,7 +158,7 @@ static void qemu_laio_completion_cb(EventNotifier *e)
LinuxAioState *s = container_of(e, LinuxAioState, e); LinuxAioState *s = container_of(e, LinuxAioState, e);
if (event_notifier_test_and_clear(&s->e)) { if (event_notifier_test_and_clear(&s->e)) {
qemu_bh_schedule(s->completion_bh); qemu_laio_completion_bh(s);
} }
} }
@ -230,22 +240,12 @@ void laio_io_unplug(BlockDriverState *bs, LinuxAioState *s)
} }
} }
BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd, static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int type)
BlockCompletionFunc *cb, void *opaque, int type)
{ {
struct qemu_laiocb *laiocb; LinuxAioState *s = laiocb->ctx;
struct iocb *iocbs; struct iocb *iocbs = &laiocb->iocb;
off_t offset = sector_num * 512; QEMUIOVector *qiov = laiocb->qiov;
laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque);
laiocb->nbytes = nb_sectors * 512;
laiocb->ctx = s;
laiocb->ret = -EINPROGRESS;
laiocb->is_read = (type == QEMU_AIO_READ);
laiocb->qiov = qiov;
iocbs = &laiocb->iocb;
switch (type) { switch (type) {
case QEMU_AIO_WRITE: case QEMU_AIO_WRITE:
@ -258,7 +258,7 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
default: default:
fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", fprintf(stderr, "%s: invalid AIO request type 0x%x.\n",
__func__, type); __func__, type);
goto out_free_aiocb; return -EIO;
} }
io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e)); io_set_eventfd(&laiocb->iocb, event_notifier_get_fd(&s->e));
@ -268,11 +268,53 @@ BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
(!s->io_q.plugged || s->io_q.n >= MAX_QUEUED_IO)) { (!s->io_q.plugged || s->io_q.n >= MAX_QUEUED_IO)) {
ioq_submit(s); ioq_submit(s);
} }
return &laiocb->common;
out_free_aiocb: return 0;
qemu_aio_unref(laiocb); }
return NULL;
int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
uint64_t offset, QEMUIOVector *qiov, int type)
{
int ret;
struct qemu_laiocb laiocb = {
.co = qemu_coroutine_self(),
.nbytes = qiov->size,
.ctx = s,
.is_read = (type == QEMU_AIO_READ),
.qiov = qiov,
};
ret = laio_do_submit(fd, &laiocb, offset, type);
if (ret < 0) {
return ret;
}
qemu_coroutine_yield();
return laiocb.ret;
}
BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque, int type)
{
struct qemu_laiocb *laiocb;
off_t offset = sector_num * BDRV_SECTOR_SIZE;
int ret;
laiocb = qemu_aio_get(&laio_aiocb_info, bs, cb, opaque);
laiocb->nbytes = nb_sectors * BDRV_SECTOR_SIZE;
laiocb->ctx = s;
laiocb->ret = -EINPROGRESS;
laiocb->is_read = (type == QEMU_AIO_READ);
laiocb->qiov = qiov;
ret = laio_do_submit(fd, laiocb, offset, type);
if (ret < 0) {
qemu_aio_unref(laiocb);
return NULL;
}
return &laiocb->common;
} }
void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context) void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)

View File

@ -44,6 +44,7 @@ typedef struct MirrorBlockJob {
/* Used to block operations on the drive-mirror-replace target */ /* Used to block operations on the drive-mirror-replace target */
Error *replace_blocker; Error *replace_blocker;
bool is_none_mode; bool is_none_mode;
BlockMirrorBackingMode backing_mode;
BlockdevOnError on_source_error, on_target_error; BlockdevOnError on_source_error, on_target_error;
bool synced; bool synced;
bool should_complete; bool should_complete;
@ -157,8 +158,7 @@ static void mirror_read_complete(void *opaque, int ret)
return; return;
} }
blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov, blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov,
op->nb_sectors * BDRV_SECTOR_SIZE, 0, mirror_write_complete, op);
mirror_write_complete, op);
} }
static inline void mirror_clip_sectors(MirrorBlockJob *s, static inline void mirror_clip_sectors(MirrorBlockJob *s,
@ -186,8 +186,9 @@ static int mirror_cow_align(MirrorBlockJob *s,
need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors, need_cow |= !test_bit((*sector_num + *nb_sectors - 1) / chunk_sectors,
s->cow_bitmap); s->cow_bitmap);
if (need_cow) { if (need_cow) {
bdrv_round_to_clusters(blk_bs(s->target), *sector_num, *nb_sectors, bdrv_round_sectors_to_clusters(blk_bs(s->target), *sector_num,
&align_sector_num, &align_nb_sectors); *nb_sectors, &align_sector_num,
&align_nb_sectors);
} }
if (align_nb_sectors > max_sectors) { if (align_nb_sectors > max_sectors) {
@ -274,8 +275,7 @@ static int mirror_do_read(MirrorBlockJob *s, int64_t sector_num,
s->sectors_in_flight += nb_sectors; s->sectors_in_flight += nb_sectors;
trace_mirror_one_iteration(s, sector_num, nb_sectors); trace_mirror_one_iteration(s, sector_num, nb_sectors);
blk_aio_preadv(source, sector_num * BDRV_SECTOR_SIZE, &op->qiov, blk_aio_preadv(source, sector_num * BDRV_SECTOR_SIZE, &op->qiov, 0,
nb_sectors * BDRV_SECTOR_SIZE,
mirror_read_complete, op); mirror_read_complete, op);
return ret; return ret;
} }
@ -386,8 +386,9 @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
} else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) { } else if (ret >= 0 && !(ret & BDRV_BLOCK_DATA)) {
int64_t target_sector_num; int64_t target_sector_num;
int target_nb_sectors; int target_nb_sectors;
bdrv_round_to_clusters(blk_bs(s->target), sector_num, io_sectors, bdrv_round_sectors_to_clusters(blk_bs(s->target), sector_num,
&target_sector_num, &target_nb_sectors); io_sectors, &target_sector_num,
&target_nb_sectors);
if (target_sector_num == sector_num && if (target_sector_num == sector_num &&
target_nb_sectors == io_sectors) { target_nb_sectors == io_sectors) {
mirror_method = ret & BDRV_BLOCK_ZERO ? mirror_method = ret & BDRV_BLOCK_ZERO ?
@ -742,20 +743,26 @@ static void mirror_set_speed(BlockJob *job, int64_t speed, Error **errp)
static void mirror_complete(BlockJob *job, Error **errp) static void mirror_complete(BlockJob *job, Error **errp)
{ {
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
Error *local_err = NULL; BlockDriverState *src, *target;
int ret;
src = blk_bs(job->blk);
target = blk_bs(s->target);
ret = bdrv_open_backing_file(blk_bs(s->target), NULL, "backing",
&local_err);
if (ret < 0) {
error_propagate(errp, local_err);
return;
}
if (!s->synced) { if (!s->synced) {
error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id); error_setg(errp, QERR_BLOCK_JOB_NOT_READY, job->id);
return; return;
} }
if (s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) {
int ret;
assert(!target->backing);
ret = bdrv_open_backing_file(target, NULL, "backing", errp);
if (ret < 0) {
return;
}
}
/* check the target bs is not blocked and block all operations on it */ /* check the target bs is not blocked and block all operations on it */
if (s->replaces) { if (s->replaces) {
AioContext *replace_aio_context; AioContext *replace_aio_context;
@ -777,6 +784,13 @@ static void mirror_complete(BlockJob *job, Error **errp)
aio_context_release(replace_aio_context); aio_context_release(replace_aio_context);
} }
if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) {
BlockDriverState *backing = s->is_none_mode ? src : s->base;
if (backing_bs(target) != backing) {
bdrv_set_backing_hd(target, backing);
}
}
s->should_complete = true; s->should_complete = true;
block_job_enter(&s->common); block_job_enter(&s->common);
} }
@ -799,6 +813,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t speed, uint32_t granularity,
int64_t buf_size, int64_t buf_size,
BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error, BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
@ -836,6 +851,7 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
s->on_source_error = on_source_error; s->on_source_error = on_source_error;
s->on_target_error = on_target_error; s->on_target_error = on_target_error;
s->is_none_mode = is_none_mode; s->is_none_mode = is_none_mode;
s->backing_mode = backing_mode;
s->base = base; s->base = base;
s->granularity = granularity; s->granularity = granularity;
s->buf_size = ROUND_UP(buf_size, granularity); s->buf_size = ROUND_UP(buf_size, granularity);
@ -859,7 +875,8 @@ static void mirror_start_job(BlockDriverState *bs, BlockDriverState *target,
void mirror_start(BlockDriverState *bs, BlockDriverState *target, void mirror_start(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,
@ -875,7 +892,7 @@ void mirror_start(BlockDriverState *bs, BlockDriverState *target,
is_none_mode = mode == MIRROR_SYNC_MODE_NONE; is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
mirror_start_job(bs, target, replaces, mirror_start_job(bs, target, replaces,
speed, granularity, buf_size, speed, granularity, buf_size, backing_mode,
on_source_error, on_target_error, unmap, cb, opaque, errp, on_source_error, on_target_error, unmap, cb, opaque, errp,
&mirror_job_driver, is_none_mode, base); &mirror_job_driver, is_none_mode, base);
} }
@ -922,7 +939,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
} }
} }
mirror_start_job(bs, base, NULL, speed, 0, 0, mirror_start_job(bs, base, NULL, speed, 0, 0, MIRROR_LEAVE_BACKING_CHAIN,
on_error, on_error, false, cb, opaque, &local_err, on_error, on_error, false, cb, opaque, &local_err,
&commit_active_job_driver, false, base); &commit_active_job_driver, false, base);
if (local_err) { if (local_err) {

View File

@ -12,6 +12,8 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qapi/qmp/qdict.h"
#include "qapi/qmp/qstring.h"
#include "block/block_int.h" #include "block/block_int.h"
#define NULL_OPT_LATENCY "latency-ns" #define NULL_OPT_LATENCY "latency-ns"
@ -223,6 +225,20 @@ static int64_t coroutine_fn null_co_get_block_status(BlockDriverState *bs,
} }
} }
static void null_refresh_filename(BlockDriverState *bs, QDict *opts)
{
QINCREF(opts);
qdict_del(opts, "filename");
if (!qdict_size(opts)) {
snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://",
bs->drv->format_name);
}
qdict_put(opts, "driver", qstring_from_str(bs->drv->format_name));
bs->full_open_options = opts;
}
static BlockDriver bdrv_null_co = { static BlockDriver bdrv_null_co = {
.format_name = "null-co", .format_name = "null-co",
.protocol_name = "null-co", .protocol_name = "null-co",
@ -238,6 +254,8 @@ static BlockDriver bdrv_null_co = {
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,
.bdrv_co_get_block_status = null_co_get_block_status, .bdrv_co_get_block_status = null_co_get_block_status,
.bdrv_refresh_filename = null_refresh_filename,
}; };
static BlockDriver bdrv_null_aio = { static BlockDriver bdrv_null_aio = {
@ -255,6 +273,8 @@ static BlockDriver bdrv_null_aio = {
.bdrv_reopen_prepare = null_reopen_prepare, .bdrv_reopen_prepare = null_reopen_prepare,
.bdrv_co_get_block_status = null_co_get_block_status, .bdrv_co_get_block_status = null_co_get_block_status,
.bdrv_refresh_filename = null_refresh_filename,
}; };
static void bdrv_null_init(void) static void bdrv_null_init(void)

View File

@ -162,10 +162,16 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
if (s->crypt_method_header) { if (s->crypt_method_header) {
if (bdrv_uses_whitelist() && if (bdrv_uses_whitelist() &&
s->crypt_method_header == QCOW_CRYPT_AES) { s->crypt_method_header == QCOW_CRYPT_AES) {
error_report("qcow built-in AES encryption is deprecated"); error_setg(errp,
error_printf("Support for it will be removed in a future release.\n" "Use of AES-CBC encrypted qcow images is no longer "
"You can use 'qemu-img convert' to switch to an\n" "supported in system emulators");
"unencrypted qcow image, or a LUKS raw image.\n"); error_append_hint(errp,
"You can use 'qemu-img convert' to convert your "
"image to an alternative supported format, such "
"as unencrypted qcow, or raw with the LUKS "
"format instead.\n");
ret = -ENOSYS;
goto fail;
} }
bs->encrypted = 1; bs->encrypted = 1;

View File

@ -390,22 +390,18 @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
return 0; return 0;
} }
static int coroutine_fn copy_sectors(BlockDriverState *bs, static int coroutine_fn do_perform_cow(BlockDriverState *bs,
uint64_t start_sect, uint64_t src_cluster_offset,
uint64_t cluster_offset, uint64_t cluster_offset,
int n_start, int n_end) int offset_in_cluster,
int bytes)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
QEMUIOVector qiov; QEMUIOVector qiov;
struct iovec iov; struct iovec iov;
int n, ret; int ret;
n = n_end - n_start; iov.iov_len = bytes;
if (n <= 0) {
return 0;
}
iov.iov_len = n * BDRV_SECTOR_SIZE;
iov.iov_base = qemu_try_blockalign(bs, iov.iov_len); iov.iov_base = qemu_try_blockalign(bs, iov.iov_len);
if (iov.iov_base == NULL) { if (iov.iov_base == NULL) {
return -ENOMEM; return -ENOMEM;
@ -424,17 +420,21 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
* interface. This avoids double I/O throttling and request tracking, * interface. This avoids double I/O throttling and request tracking,
* which can lead to deadlock when block layer copy-on-read is enabled. * 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); ret = bs->drv->bdrv_co_preadv(bs, src_cluster_offset + offset_in_cluster,
bytes, &qiov, 0);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
if (bs->encrypted) { if (bs->encrypted) {
Error *err = NULL; Error *err = NULL;
int64_t sector = (cluster_offset + offset_in_cluster)
>> BDRV_SECTOR_BITS;
assert(s->cipher); assert(s->cipher);
if (qcow2_encrypt_sectors(s, start_sect + n_start, assert((offset_in_cluster & ~BDRV_SECTOR_MASK) == 0);
iov.iov_base, iov.iov_base, n, assert((bytes & ~BDRV_SECTOR_MASK) == 0);
true, &err) < 0) { if (qcow2_encrypt_sectors(s, sector, iov.iov_base, iov.iov_base,
bytes >> BDRV_SECTOR_BITS, true, &err) < 0) {
ret = -EIO; ret = -EIO;
error_free(err); error_free(err);
goto out; goto out;
@ -442,14 +442,14 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,
} }
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + n_start * BDRV_SECTOR_SIZE, n * BDRV_SECTOR_SIZE); cluster_offset + offset_in_cluster, bytes);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE);
ret = bdrv_co_writev(bs->file->bs, (cluster_offset >> 9) + n_start, n, ret = bdrv_co_pwritev(bs->file->bs, cluster_offset + offset_in_cluster,
&qiov); bytes, &qiov, 0);
if (ret < 0) { if (ret < 0) {
goto out; goto out;
} }
@ -464,47 +464,44 @@ out:
/* /*
* get_cluster_offset * get_cluster_offset
* *
* For a given offset of the disk image, find the cluster offset in * For a given offset of the virtual disk, find the cluster type and offset in
* qcow2 file. The offset is stored in *cluster_offset. * the qcow2 file. The offset is stored in *cluster_offset.
* *
* on entry, *num is the number of contiguous sectors we'd like to * On entry, *bytes is the maximum number of contiguous bytes starting at
* access following offset. * offset that we are interested in.
* *
* on exit, *num is the number of contiguous sectors we can read. * On exit, *bytes is the number of bytes starting at offset that have the same
* cluster type and (if applicable) are stored contiguously in the image file.
* Compressed clusters are always returned one by one.
* *
* Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error * Returns the cluster type (QCOW2_CLUSTER_*) on success, -errno in error
* cases. * cases.
*/ */
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset) unsigned int *bytes, uint64_t *cluster_offset)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
unsigned int l2_index; unsigned int l2_index;
uint64_t l1_index, l2_offset, *l2_table; uint64_t l1_index, l2_offset, *l2_table;
int l1_bits, c; int l1_bits, c;
unsigned int index_in_cluster, nb_clusters; unsigned int offset_in_cluster, nb_clusters;
uint64_t nb_available, nb_needed; uint64_t bytes_available, bytes_needed;
int ret; int ret;
index_in_cluster = (offset >> 9) & (s->cluster_sectors - 1); offset_in_cluster = offset_into_cluster(s, offset);
nb_needed = *num + index_in_cluster; bytes_needed = (uint64_t) *bytes + offset_in_cluster;
l1_bits = s->l2_bits + s->cluster_bits; l1_bits = s->l2_bits + s->cluster_bits;
/* compute how many bytes there are between the offset and /* compute how many bytes there are between the start of the cluster
* the end of the l1 entry * containing offset and the end of the l1 entry */
*/ bytes_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1))
+ offset_in_cluster;
nb_available = (1ULL << l1_bits) - (offset & ((1ULL << l1_bits) - 1)); if (bytes_needed > bytes_available) {
bytes_needed = bytes_available;
/* compute the number of available sectors */
nb_available = (nb_available >> 9) + index_in_cluster;
if (nb_needed > nb_available) {
nb_needed = nb_available;
} }
assert(nb_needed <= INT_MAX); assert(bytes_needed <= INT_MAX);
*cluster_offset = 0; *cluster_offset = 0;
@ -542,7 +539,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
*cluster_offset = be64_to_cpu(l2_table[l2_index]); *cluster_offset = be64_to_cpu(l2_table[l2_index]);
/* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */ /* nb_needed <= INT_MAX, thus nb_clusters <= INT_MAX, too */
nb_clusters = size_to_clusters(s, nb_needed << 9); nb_clusters = size_to_clusters(s, bytes_needed);
ret = qcow2_get_cluster_type(*cluster_offset); ret = qcow2_get_cluster_type(*cluster_offset);
switch (ret) { switch (ret) {
@ -589,13 +586,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table); qcow2_cache_put(bs, s->l2_table_cache, (void**) &l2_table);
nb_available = (c * s->cluster_sectors); bytes_available = (c * s->cluster_size);
out: out:
if (nb_available > nb_needed) if (bytes_available > bytes_needed) {
nb_available = nb_needed; bytes_available = bytes_needed;
}
*num = nb_available - index_in_cluster; *bytes = bytes_available - offset_in_cluster;
return ret; return ret;
@ -741,14 +739,12 @@ static int perform_cow(BlockDriverState *bs, QCowL2Meta *m, Qcow2COWRegion *r)
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int ret; int ret;
if (r->nb_sectors == 0) { if (r->nb_bytes == 0) {
return 0; return 0;
} }
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = copy_sectors(bs, m->offset / BDRV_SECTOR_SIZE, m->alloc_offset, ret = do_perform_cow(bs, m->offset, m->alloc_offset, r->offset, r->nb_bytes);
r->offset / BDRV_SECTOR_SIZE,
r->offset / BDRV_SECTOR_SIZE + r->nb_sectors);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
@ -810,13 +806,14 @@ int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m)
assert(l2_index + m->nb_clusters <= s->l2_size); assert(l2_index + m->nb_clusters <= s->l2_size);
for (i = 0; i < m->nb_clusters; i++) { for (i = 0; i < m->nb_clusters; i++) {
/* if two concurrent writes happen to the same unallocated cluster /* if two concurrent writes happen to the same unallocated cluster
* each write allocates separate cluster and writes data concurrently. * each write allocates separate cluster and writes data concurrently.
* The first one to complete updates l2 table with pointer to its * The first one to complete updates l2 table with pointer to its
* cluster the second one has to do RMW (which is done above by * cluster the second one has to do RMW (which is done above by
* copy_sectors()), update l2 table with its cluster pointer and free * perform_cow()), update l2 table with its cluster pointer and free
* old cluster. This is what this loop does */ * old cluster. This is what this loop does */
if(l2_table[l2_index + i] != 0) if (l2_table[l2_index + i] != 0) {
old_cluster[j++] = l2_table[l2_index + i]; old_cluster[j++] = l2_table[l2_index + i];
}
l2_table[l2_index + i] = cpu_to_be64((cluster_offset + l2_table[l2_index + i] = cpu_to_be64((cluster_offset +
(i << s->cluster_bits)) | QCOW_OFLAG_COPIED); (i << s->cluster_bits)) | QCOW_OFLAG_COPIED);
@ -1198,25 +1195,20 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
/* /*
* Save info needed for meta data update. * Save info needed for meta data update.
* *
* requested_sectors: Number of sectors from the start of the first * requested_bytes: Number of bytes from the start of the first
* newly allocated cluster to the end of the (possibly shortened * newly allocated cluster to the end of the (possibly shortened
* before) write request. * before) write request.
* *
* avail_sectors: Number of sectors from the start of the first * avail_bytes: Number of bytes from the start of the first
* newly allocated to the end of the last newly allocated cluster. * newly allocated to the end of the last newly allocated cluster.
* *
* nb_sectors: The number of sectors from the start of the first * nb_bytes: The number of bytes from the start of the first
* newly allocated cluster to the end of the area that the write * newly allocated cluster to the end of the area that the write
* request actually writes to (excluding COW at the end) * request actually writes to (excluding COW at the end)
*/ */
int requested_sectors = uint64_t requested_bytes = *bytes + offset_into_cluster(s, guest_offset);
(*bytes + offset_into_cluster(s, guest_offset)) int avail_bytes = MIN(INT_MAX, nb_clusters << s->cluster_bits);
>> BDRV_SECTOR_BITS; int nb_bytes = MIN(requested_bytes, avail_bytes);
int avail_sectors = nb_clusters
<< (s->cluster_bits - BDRV_SECTOR_BITS);
int alloc_n_start = offset_into_cluster(s, guest_offset)
>> BDRV_SECTOR_BITS;
int nb_sectors = MIN(requested_sectors, avail_sectors);
QCowL2Meta *old_m = *m; QCowL2Meta *old_m = *m;
*m = g_malloc0(sizeof(**m)); *m = g_malloc0(sizeof(**m));
@ -1227,23 +1219,21 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset,
.alloc_offset = alloc_cluster_offset, .alloc_offset = alloc_cluster_offset,
.offset = start_of_cluster(s, guest_offset), .offset = start_of_cluster(s, guest_offset),
.nb_clusters = nb_clusters, .nb_clusters = nb_clusters,
.nb_available = nb_sectors,
.cow_start = { .cow_start = {
.offset = 0, .offset = 0,
.nb_sectors = alloc_n_start, .nb_bytes = offset_into_cluster(s, guest_offset),
}, },
.cow_end = { .cow_end = {
.offset = nb_sectors * BDRV_SECTOR_SIZE, .offset = nb_bytes,
.nb_sectors = avail_sectors - nb_sectors, .nb_bytes = avail_bytes - nb_bytes,
}, },
}; };
qemu_co_queue_init(&(*m)->dependent_requests); qemu_co_queue_init(&(*m)->dependent_requests);
QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight); QLIST_INSERT_HEAD(&s->cluster_allocs, *m, next_in_flight);
*host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset); *host_offset = alloc_cluster_offset + offset_into_cluster(s, guest_offset);
*bytes = MIN(*bytes, (nb_sectors * BDRV_SECTOR_SIZE) *bytes = MIN(*bytes, nb_bytes - offset_into_cluster(s, guest_offset));
- offset_into_cluster(s, guest_offset));
assert(*bytes != 0); assert(*bytes != 0);
return 1; return 1;
@ -1275,7 +1265,8 @@ fail:
* Return 0 on success and -errno in error cases * Return 0 on success and -errno in error cases
*/ */
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *host_offset, QCowL2Meta **m) unsigned int *bytes, uint64_t *host_offset,
QCowL2Meta **m)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
uint64_t start, remaining; uint64_t start, remaining;
@ -1283,13 +1274,11 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
uint64_t cur_bytes; uint64_t cur_bytes;
int ret; int ret;
trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *num); trace_qcow2_alloc_clusters_offset(qemu_coroutine_self(), offset, *bytes);
assert((offset & ~BDRV_SECTOR_MASK) == 0);
again: again:
start = offset; start = offset;
remaining = (uint64_t)*num << BDRV_SECTOR_BITS; remaining = *bytes;
cluster_offset = 0; cluster_offset = 0;
*host_offset = 0; *host_offset = 0;
cur_bytes = 0; cur_bytes = 0;
@ -1375,8 +1364,8 @@ again:
} }
} }
*num -= remaining >> BDRV_SECTOR_BITS; *bytes -= remaining;
assert(*num > 0); assert(*bytes > 0);
assert(*host_offset != 0); assert(*host_offset != 0);
return 0; return 0;

View File

@ -968,13 +968,22 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags,
if (s->crypt_method_header) { if (s->crypt_method_header) {
if (bdrv_uses_whitelist() && if (bdrv_uses_whitelist() &&
s->crypt_method_header == QCOW_CRYPT_AES) { s->crypt_method_header == QCOW_CRYPT_AES) {
error_report("qcow2 built-in AES encryption is deprecated"); error_setg(errp,
error_printf("Support for it will be removed in a future release.\n" "Use of AES-CBC encrypted qcow2 images is no longer "
"You can use 'qemu-img convert' to switch to an\n" "supported in system emulators");
"unencrypted qcow2 image, or a LUKS raw image.\n"); error_append_hint(errp,
"You can use 'qemu-img convert' to convert your "
"image to an alternative supported format, such "
"as unencrypted qcow2, or raw with the LUKS "
"format instead.\n");
ret = -ENOSYS;
goto fail;
} }
bs->encrypted = 1; bs->encrypted = 1;
/* Encryption works on a sector granularity */
bs->request_alignment = BDRV_SECTOR_SIZE;
} }
s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */ s->l2_bits = s->cluster_bits - 3; /* L2 is always one cluster */
@ -1331,16 +1340,20 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
uint64_t cluster_offset; uint64_t cluster_offset;
int index_in_cluster, ret; int index_in_cluster, ret;
unsigned int bytes;
int64_t status = 0; int64_t status = 0;
*pnum = nb_sectors; bytes = MIN(INT_MAX, nb_sectors * BDRV_SECTOR_SIZE);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
ret = qcow2_get_cluster_offset(bs, sector_num << 9, pnum, &cluster_offset); ret = qcow2_get_cluster_offset(bs, sector_num << 9, &bytes,
&cluster_offset);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
*pnum = bytes >> BDRV_SECTOR_BITS;
if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED &&
!s->cipher) { !s->cipher) {
index_in_cluster = sector_num & (s->cluster_sectors - 1); index_in_cluster = sector_num & (s->cluster_sectors - 1);
@ -1358,28 +1371,34 @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
/* handle reading after the end of the backing file */ /* handle reading after the end of the backing file */
int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov, int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t sector_num, int nb_sectors) int64_t offset, int bytes)
{ {
uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
int n1; int n1;
if ((sector_num + nb_sectors) <= bs->total_sectors)
return nb_sectors;
if (sector_num >= bs->total_sectors)
n1 = 0;
else
n1 = bs->total_sectors - sector_num;
qemu_iovec_memset(qiov, 512 * n1, 0, 512 * (nb_sectors - n1)); if ((offset + bytes) <= bs_size) {
return bytes;
}
if (offset >= bs_size) {
n1 = 0;
} else {
n1 = bs_size - offset;
}
qemu_iovec_memset(qiov, n1, 0, bytes - n1);
return n1; return n1;
} }
static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num, static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
int remaining_sectors, QEMUIOVector *qiov) uint64_t bytes, QEMUIOVector *qiov,
int flags)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int index_in_cluster, n1; int offset_in_cluster, n1;
int ret; int ret;
int cur_nr_sectors; /* number of sectors in current iteration */ unsigned int cur_bytes; /* number of bytes in current iteration */
uint64_t cluster_offset = 0; uint64_t cluster_offset = 0;
uint64_t bytes_done = 0; uint64_t bytes_done = 0;
QEMUIOVector hd_qiov; QEMUIOVector hd_qiov;
@ -1389,26 +1408,24 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
while (remaining_sectors != 0) { while (bytes != 0) {
/* prepare next request */ /* prepare next request */
cur_nr_sectors = remaining_sectors; cur_bytes = MIN(bytes, INT_MAX);
if (s->cipher) { if (s->cipher) {
cur_nr_sectors = MIN(cur_nr_sectors, cur_bytes = MIN(cur_bytes,
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors); QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
} }
ret = qcow2_get_cluster_offset(bs, sector_num << 9, ret = qcow2_get_cluster_offset(bs, offset, &cur_bytes, &cluster_offset);
&cur_nr_sectors, &cluster_offset);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
index_in_cluster = sector_num & (s->cluster_sectors - 1); offset_in_cluster = offset_into_cluster(s, offset);
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
cur_nr_sectors * 512);
switch (ret) { switch (ret) {
case QCOW2_CLUSTER_UNALLOCATED: case QCOW2_CLUSTER_UNALLOCATED:
@ -1416,18 +1433,17 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
if (bs->backing) { if (bs->backing) {
/* read from the base image */ /* read from the base image */
n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov, n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
sector_num, cur_nr_sectors); offset, cur_bytes);
if (n1 > 0) { if (n1 > 0) {
QEMUIOVector local_qiov; QEMUIOVector local_qiov;
qemu_iovec_init(&local_qiov, hd_qiov.niov); qemu_iovec_init(&local_qiov, hd_qiov.niov);
qemu_iovec_concat(&local_qiov, &hd_qiov, 0, qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
n1 * BDRV_SECTOR_SIZE);
BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_readv(bs->backing->bs, sector_num, ret = bdrv_co_preadv(bs->backing->bs, offset, n1,
n1, &local_qiov); &local_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
qemu_iovec_destroy(&local_qiov); qemu_iovec_destroy(&local_qiov);
@ -1438,12 +1454,12 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
} }
} else { } else {
/* Note: in this case, no need to wait */ /* Note: in this case, no need to wait */
qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors); qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
} }
break; break;
case QCOW2_CLUSTER_ZERO: case QCOW2_CLUSTER_ZERO:
qemu_iovec_memset(&hd_qiov, 0, 0, 512 * cur_nr_sectors); qemu_iovec_memset(&hd_qiov, 0, 0, cur_bytes);
break; break;
case QCOW2_CLUSTER_COMPRESSED: case QCOW2_CLUSTER_COMPRESSED:
@ -1454,8 +1470,8 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
} }
qemu_iovec_from_buf(&hd_qiov, 0, qemu_iovec_from_buf(&hd_qiov, 0,
s->cluster_cache + index_in_cluster * 512, s->cluster_cache + offset_in_cluster,
512 * cur_nr_sectors); cur_bytes);
break; break;
case QCOW2_CLUSTER_NORMAL: case QCOW2_CLUSTER_NORMAL:
@ -1482,34 +1498,34 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
} }
} }
assert(cur_nr_sectors <= assert(cur_bytes <= QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors);
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cluster_data, qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
512 * cur_nr_sectors);
} }
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
ret = bdrv_co_readv(bs->file->bs, ret = bdrv_co_preadv(bs->file->bs,
(cluster_offset >> 9) + index_in_cluster, cluster_offset + offset_in_cluster,
cur_nr_sectors, &hd_qiov); cur_bytes, &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
if (bs->encrypted) { if (bs->encrypted) {
assert(s->cipher); assert(s->cipher);
assert((offset & (BDRV_SECTOR_SIZE - 1)) == 0);
assert((cur_bytes & (BDRV_SECTOR_SIZE - 1)) == 0);
Error *err = NULL; Error *err = NULL;
if (qcow2_encrypt_sectors(s, sector_num, cluster_data, if (qcow2_encrypt_sectors(s, offset >> BDRV_SECTOR_BITS,
cluster_data, cur_nr_sectors, false, cluster_data, cluster_data,
&err) < 0) { cur_bytes >> BDRV_SECTOR_BITS,
false, &err) < 0) {
error_free(err); error_free(err);
ret = -EIO; ret = -EIO;
goto fail; goto fail;
} }
qemu_iovec_from_buf(qiov, bytes_done, qemu_iovec_from_buf(qiov, bytes_done, cluster_data, cur_bytes);
cluster_data, 512 * cur_nr_sectors);
} }
break; break;
@ -1519,9 +1535,9 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,
goto fail; goto fail;
} }
remaining_sectors -= cur_nr_sectors; bytes -= cur_bytes;
sector_num += cur_nr_sectors; offset += cur_bytes;
bytes_done += cur_nr_sectors * 512; bytes_done += cur_bytes;
} }
ret = 0; ret = 0;
@ -1534,23 +1550,21 @@ fail:
return ret; return ret;
} }
static coroutine_fn int qcow2_co_writev(BlockDriverState *bs, static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset,
int64_t sector_num, uint64_t bytes, QEMUIOVector *qiov,
int remaining_sectors, int flags)
QEMUIOVector *qiov)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int index_in_cluster; int offset_in_cluster;
int ret; int ret;
int cur_nr_sectors; /* number of sectors in current iteration */ unsigned int cur_bytes; /* number of sectors in current iteration */
uint64_t cluster_offset; uint64_t cluster_offset;
QEMUIOVector hd_qiov; QEMUIOVector hd_qiov;
uint64_t bytes_done = 0; uint64_t bytes_done = 0;
uint8_t *cluster_data = NULL; uint8_t *cluster_data = NULL;
QCowL2Meta *l2meta = NULL; QCowL2Meta *l2meta = NULL;
trace_qcow2_writev_start_req(qemu_coroutine_self(), sector_num, trace_qcow2_writev_start_req(qemu_coroutine_self(), offset, bytes);
remaining_sectors);
qemu_iovec_init(&hd_qiov, qiov->niov); qemu_iovec_init(&hd_qiov, qiov->niov);
@ -1558,22 +1572,21 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
while (remaining_sectors != 0) { while (bytes != 0) {
l2meta = NULL; l2meta = NULL;
trace_qcow2_writev_start_part(qemu_coroutine_self()); trace_qcow2_writev_start_part(qemu_coroutine_self());
index_in_cluster = sector_num & (s->cluster_sectors - 1); offset_in_cluster = offset_into_cluster(s, offset);
cur_nr_sectors = remaining_sectors; cur_bytes = MIN(bytes, INT_MAX);
if (bs->encrypted && if (bs->encrypted) {
cur_nr_sectors > cur_bytes = MIN(cur_bytes,
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster) { QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size
cur_nr_sectors = - offset_in_cluster);
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_sectors - index_in_cluster;
} }
ret = qcow2_alloc_cluster_offset(bs, sector_num << 9, ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
&cur_nr_sectors, &cluster_offset, &l2meta); &cluster_offset, &l2meta);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1581,8 +1594,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
assert((cluster_offset & 511) == 0); assert((cluster_offset & 511) == 0);
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_concat(&hd_qiov, qiov, bytes_done, qemu_iovec_concat(&hd_qiov, qiov, bytes_done, cur_bytes);
cur_nr_sectors * 512);
if (bs->encrypted) { if (bs->encrypted) {
Error *err = NULL; Error *err = NULL;
@ -1601,8 +1613,9 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size);
qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size); qemu_iovec_to_buf(&hd_qiov, 0, cluster_data, hd_qiov.size);
if (qcow2_encrypt_sectors(s, sector_num, cluster_data, if (qcow2_encrypt_sectors(s, offset >> BDRV_SECTOR_BITS,
cluster_data, cur_nr_sectors, cluster_data, cluster_data,
cur_bytes >>BDRV_SECTOR_BITS,
true, &err) < 0) { true, &err) < 0) {
error_free(err); error_free(err);
ret = -EIO; ret = -EIO;
@ -1610,13 +1623,11 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
} }
qemu_iovec_reset(&hd_qiov); qemu_iovec_reset(&hd_qiov);
qemu_iovec_add(&hd_qiov, cluster_data, qemu_iovec_add(&hd_qiov, cluster_data, cur_bytes);
cur_nr_sectors * 512);
} }
ret = qcow2_pre_write_overlap_check(bs, 0, ret = qcow2_pre_write_overlap_check(bs, 0,
cluster_offset + index_in_cluster * BDRV_SECTOR_SIZE, cluster_offset + offset_in_cluster, cur_bytes);
cur_nr_sectors * BDRV_SECTOR_SIZE);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
} }
@ -1624,10 +1635,10 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO);
trace_qcow2_writev_data(qemu_coroutine_self(), trace_qcow2_writev_data(qemu_coroutine_self(),
(cluster_offset >> 9) + index_in_cluster); cluster_offset + offset_in_cluster);
ret = bdrv_co_writev(bs->file->bs, ret = bdrv_co_pwritev(bs->file->bs,
(cluster_offset >> 9) + index_in_cluster, cluster_offset + offset_in_cluster,
cur_nr_sectors, &hd_qiov); cur_bytes, &hd_qiov, 0);
qemu_co_mutex_lock(&s->lock); qemu_co_mutex_lock(&s->lock);
if (ret < 0) { if (ret < 0) {
goto fail; goto fail;
@ -1653,10 +1664,10 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,
l2meta = next; l2meta = next;
} }
remaining_sectors -= cur_nr_sectors; bytes -= cur_bytes;
sector_num += cur_nr_sectors; offset += cur_bytes;
bytes_done += cur_nr_sectors * 512; bytes_done += cur_bytes;
trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_nr_sectors); trace_qcow2_writev_done_part(qemu_coroutine_self(), cur_bytes);
} }
ret = 0; ret = 0;
@ -1998,19 +2009,19 @@ static int qcow2_change_backing_file(BlockDriverState *bs,
static int preallocate(BlockDriverState *bs) static int preallocate(BlockDriverState *bs)
{ {
uint64_t nb_sectors; uint64_t bytes;
uint64_t offset; uint64_t offset;
uint64_t host_offset = 0; uint64_t host_offset = 0;
int num; unsigned int cur_bytes;
int ret; int ret;
QCowL2Meta *meta; QCowL2Meta *meta;
nb_sectors = bdrv_nb_sectors(bs); bytes = bdrv_getlength(bs);
offset = 0; offset = 0;
while (nb_sectors) { while (bytes) {
num = MIN(nb_sectors, INT_MAX >> BDRV_SECTOR_BITS); cur_bytes = MIN(bytes, INT_MAX);
ret = qcow2_alloc_cluster_offset(bs, offset, &num, ret = qcow2_alloc_cluster_offset(bs, offset, &cur_bytes,
&host_offset, &meta); &host_offset, &meta);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -2036,8 +2047,8 @@ static int preallocate(BlockDriverState *bs)
/* TODO Preallocate data if requested */ /* TODO Preallocate data if requested */
nb_sectors -= num; bytes -= cur_bytes;
offset += num << BDRV_SECTOR_BITS; offset += cur_bytes;
} }
/* /*
@ -2046,11 +2057,9 @@ static int preallocate(BlockDriverState *bs)
* EOF). Extend the image to the last allocated sector. * EOF). Extend the image to the last allocated sector.
*/ */
if (host_offset != 0) { if (host_offset != 0) {
uint8_t buf[BDRV_SECTOR_SIZE]; uint8_t data = 0;
memset(buf, 0, BDRV_SECTOR_SIZE); ret = bdrv_pwrite(bs->file->bs, (host_offset + cur_bytes) - 1,
ret = bdrv_write(bs->file->bs, &data, 1);
(host_offset >> BDRV_SECTOR_BITS) + num - 1,
buf, 1);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
} }
@ -2435,7 +2444,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
if (head || tail) { if (head || tail) {
int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS; int64_t cl_start = (offset - head) >> BDRV_SECTOR_BITS;
uint64_t off; uint64_t off;
int nr; unsigned int nr;
assert(head + count <= s->cluster_size); assert(head + count <= s->cluster_size);
@ -2452,7 +2461,7 @@ static coroutine_fn int qcow2_co_pwrite_zeroes(BlockDriverState *bs,
/* We can have new write after previous check */ /* We can have new write after previous check */
offset = cl_start << BDRV_SECTOR_BITS; offset = cl_start << BDRV_SECTOR_BITS;
count = s->cluster_size; count = s->cluster_size;
nr = s->cluster_sectors; nr = s->cluster_size;
ret = qcow2_get_cluster_offset(bs, offset, &nr, &off); ret = qcow2_get_cluster_offset(bs, offset, &nr, &off);
if (ret != QCOW2_CLUSTER_UNALLOCATED && ret != QCOW2_CLUSTER_ZERO) { if (ret != QCOW2_CLUSTER_UNALLOCATED && ret != QCOW2_CLUSTER_ZERO) {
qemu_co_mutex_unlock(&s->lock); qemu_co_mutex_unlock(&s->lock);
@ -2900,36 +2909,20 @@ static int qcow2_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos) int64_t pos)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
int64_t total_sectors = bs->total_sectors;
bool zero_beyond_eof = bs->zero_beyond_eof;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE); BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_SAVE);
bs->zero_beyond_eof = false; return bs->drv->bdrv_co_pwritev(bs, qcow2_vm_state_offset(s) + pos,
ret = bdrv_pwritev(bs, qcow2_vm_state_offset(s) + pos, qiov); qiov->size, qiov, 0);
bs->zero_beyond_eof = zero_beyond_eof;
/* bdrv_co_do_writev will have increased the total_sectors value to include
* the VM state - the VM state is however not an actual part of the block
* device, therefore, we need to restore the old value. */
bs->total_sectors = total_sectors;
return ret;
} }
static int qcow2_load_vmstate(BlockDriverState *bs, uint8_t *buf, static int qcow2_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos, int size) int64_t pos)
{ {
BDRVQcow2State *s = bs->opaque; BDRVQcow2State *s = bs->opaque;
bool zero_beyond_eof = bs->zero_beyond_eof;
int ret;
BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD); BLKDBG_EVENT(bs->file, BLKDBG_VMSTATE_LOAD);
bs->zero_beyond_eof = false; return bs->drv->bdrv_co_preadv(bs, qcow2_vm_state_offset(s) + pos,
ret = bdrv_pread(bs, qcow2_vm_state_offset(s) + pos, buf, size); qiov->size, qiov, 0);
bs->zero_beyond_eof = zero_beyond_eof;
return ret;
} }
/* /*
@ -3368,8 +3361,8 @@ BlockDriver bdrv_qcow2 = {
.bdrv_co_get_block_status = qcow2_co_get_block_status, .bdrv_co_get_block_status = qcow2_co_get_block_status,
.bdrv_set_key = qcow2_set_key, .bdrv_set_key = qcow2_set_key,
.bdrv_co_readv = qcow2_co_readv, .bdrv_co_preadv = qcow2_co_preadv,
.bdrv_co_writev = qcow2_co_writev, .bdrv_co_pwritev = qcow2_co_pwritev,
.bdrv_co_flush_to_os = qcow2_co_flush_to_os, .bdrv_co_flush_to_os = qcow2_co_flush_to_os,
.bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = qcow2_co_pwrite_zeroes,

View File

@ -302,8 +302,8 @@ typedef struct Qcow2COWRegion {
*/ */
uint64_t offset; uint64_t offset;
/** Number of sectors to copy */ /** Number of bytes to copy */
int nb_sectors; int nb_bytes;
} Qcow2COWRegion; } Qcow2COWRegion;
/** /**
@ -318,12 +318,6 @@ typedef struct QCowL2Meta
/** Host offset of the first newly allocated cluster */ /** Host offset of the first newly allocated cluster */
uint64_t alloc_offset; uint64_t alloc_offset;
/**
* Number of sectors from the start of the first allocated cluster to
* the end of the (possibly shortened) request
*/
int nb_available;
/** Number of newly allocated clusters */ /** Number of newly allocated clusters */
int nb_clusters; int nb_clusters;
@ -471,8 +465,7 @@ static inline uint64_t l2meta_cow_start(QCowL2Meta *m)
static inline uint64_t l2meta_cow_end(QCowL2Meta *m) static inline uint64_t l2meta_cow_end(QCowL2Meta *m)
{ {
return m->offset + m->cow_end.offset return m->offset + m->cow_end.offset + m->cow_end.nb_bytes;
+ (m->cow_end.nb_sectors << BDRV_SECTOR_BITS);
} }
static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2) static inline uint64_t refcount_diff(uint64_t r1, uint64_t r2)
@ -544,9 +537,10 @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,
int nb_sectors, bool enc, Error **errp); int nb_sectors, bool enc, Error **errp);
int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *cluster_offset); unsigned int *bytes, uint64_t *cluster_offset);
int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset,
int *num, uint64_t *host_offset, QCowL2Meta **m); unsigned int *bytes, uint64_t *host_offset,
QCowL2Meta **m);
uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs,
uint64_t offset, uint64_t offset,
int compressed_size); int compressed_size);

View File

@ -15,6 +15,7 @@
#ifndef QEMU_RAW_AIO_H #ifndef QEMU_RAW_AIO_H
#define QEMU_RAW_AIO_H #define QEMU_RAW_AIO_H
#include "qemu/coroutine.h"
#include "qemu/iov.h" #include "qemu/iov.h"
/* AIO request types */ /* AIO request types */
@ -38,6 +39,8 @@
typedef struct LinuxAioState LinuxAioState; typedef struct LinuxAioState LinuxAioState;
LinuxAioState *laio_init(void); LinuxAioState *laio_init(void);
void laio_cleanup(LinuxAioState *s); void laio_cleanup(LinuxAioState *s);
int coroutine_fn laio_co_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
uint64_t offset, QEMUIOVector *qiov, int type);
BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd, BlockAIOCB *laio_submit(BlockDriverState *bs, LinuxAioState *s, int fd,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque, int type); BlockCompletionFunc *cb, void *opaque, int type);

View File

@ -1325,14 +1325,13 @@ static BlockAIOCB *paio_submit(BlockDriverState *bs, int fd,
return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque);
} }
static BlockAIOCB *raw_aio_submit(BlockDriverState *bs, static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, uint64_t bytes, QEMUIOVector *qiov, int type)
BlockCompletionFunc *cb, void *opaque, int type)
{ {
BDRVRawState *s = bs->opaque; BDRVRawState *s = bs->opaque;
if (fd_open(bs) < 0) if (fd_open(bs) < 0)
return NULL; return -EIO;
/* /*
* Check if the underlying device requires requests to be aligned, * Check if the underlying device requires requests to be aligned,
@ -1345,14 +1344,28 @@ static BlockAIOCB *raw_aio_submit(BlockDriverState *bs,
type |= QEMU_AIO_MISALIGNED; type |= QEMU_AIO_MISALIGNED;
#ifdef CONFIG_LINUX_AIO #ifdef CONFIG_LINUX_AIO
} else if (s->use_aio) { } else if (s->use_aio) {
return laio_submit(bs, s->aio_ctx, s->fd, sector_num, qiov, assert(qiov->size == bytes);
nb_sectors, cb, opaque, type); return laio_co_submit(bs, s->aio_ctx, s->fd, offset, qiov, type);
#endif #endif
} }
} }
return paio_submit(bs, s->fd, sector_num, qiov, nb_sectors, return paio_submit_co(bs, s->fd, offset, qiov, bytes, type);
cb, opaque, type); }
static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
{
return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_READ);
}
static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, uint64_t offset,
uint64_t bytes, QEMUIOVector *qiov,
int flags)
{
assert(flags == 0);
return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE);
} }
static void raw_aio_plug(BlockDriverState *bs) static void raw_aio_plug(BlockDriverState *bs)
@ -1375,22 +1388,6 @@ static void raw_aio_unplug(BlockDriverState *bs)
#endif #endif
} }
static BlockAIOCB *raw_aio_readv(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque)
{
return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
cb, opaque, QEMU_AIO_READ);
}
static BlockAIOCB *raw_aio_writev(BlockDriverState *bs,
int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
BlockCompletionFunc *cb, void *opaque)
{
return raw_aio_submit(bs, sector_num, qiov, nb_sectors,
cb, opaque, QEMU_AIO_WRITE);
}
static BlockAIOCB *raw_aio_flush(BlockDriverState *bs, static BlockAIOCB *raw_aio_flush(BlockDriverState *bs,
BlockCompletionFunc *cb, void *opaque) BlockCompletionFunc *cb, void *opaque)
{ {
@ -1957,8 +1954,8 @@ BlockDriver bdrv_file = {
.bdrv_co_get_block_status = raw_co_get_block_status, .bdrv_co_get_block_status = raw_co_get_block_status,
.bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
.bdrv_aio_readv = raw_aio_readv, .bdrv_co_preadv = raw_co_preadv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = raw_aio_discard, .bdrv_aio_discard = raw_aio_discard,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
@ -2405,8 +2402,8 @@ static BlockDriver bdrv_host_device = {
.create_opts = &raw_create_opts, .create_opts = &raw_create_opts,
.bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes,
.bdrv_aio_readv = raw_aio_readv, .bdrv_co_preadv = raw_co_preadv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_aio_discard = hdev_aio_discard, .bdrv_aio_discard = hdev_aio_discard,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
@ -2535,8 +2532,9 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_create = hdev_create, .bdrv_create = hdev_create,
.create_opts = &raw_create_opts, .create_opts = &raw_create_opts,
.bdrv_aio_readv = raw_aio_readv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_co_preadv = raw_co_preadv,
.bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug, .bdrv_io_plug = raw_aio_plug,
@ -2670,8 +2668,8 @@ static BlockDriver bdrv_host_cdrom = {
.bdrv_create = hdev_create, .bdrv_create = hdev_create,
.create_opts = &raw_create_opts, .create_opts = &raw_create_opts,
.bdrv_aio_readv = raw_aio_readv, .bdrv_co_preadv = raw_co_preadv,
.bdrv_aio_writev = raw_aio_writev, .bdrv_co_pwritev = raw_co_pwritev,
.bdrv_aio_flush = raw_aio_flush, .bdrv_aio_flush = raw_aio_flush,
.bdrv_refresh_limits = raw_refresh_limits, .bdrv_refresh_limits = raw_refresh_limits,
.bdrv_io_plug = raw_aio_plug, .bdrv_io_plug = raw_aio_plug,

View File

@ -290,7 +290,8 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
if (only_read_conf_file) { if (only_read_conf_file) {
ret = rados_conf_read_file(cluster, value); ret = rados_conf_read_file(cluster, value);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "error reading conf file %s", value); error_setg_errno(errp, -ret, "error reading conf file %s",
value);
break; break;
} }
} }
@ -299,7 +300,7 @@ static int qemu_rbd_set_conf(rados_t cluster, const char *conf,
} else if (!only_read_conf_file) { } else if (!only_read_conf_file) {
ret = rados_conf_set(cluster, name, value); ret = rados_conf_set(cluster, name, value);
if (ret < 0) { if (ret < 0) {
error_setg(errp, "invalid conf option %s", name); error_setg_errno(errp, -ret, "invalid conf option %s", name);
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
@ -354,9 +355,10 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
} }
clientname = qemu_rbd_parse_clientname(conf, clientname_buf); clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
if (rados_create(&cluster, clientname) < 0) { ret = rados_create(&cluster, clientname);
error_setg(errp, "error initializing"); if (ret < 0) {
return -EIO; error_setg_errno(errp, -ret, "error initializing");
return ret;
} }
if (strstr(conf, "conf=") == NULL) { if (strstr(conf, "conf=") == NULL) {
@ -381,21 +383,27 @@ static int qemu_rbd_create(const char *filename, QemuOpts *opts, Error **errp)
return -EIO; return -EIO;
} }
if (rados_connect(cluster) < 0) { ret = rados_connect(cluster);
error_setg(errp, "error connecting"); if (ret < 0) {
error_setg_errno(errp, -ret, "error connecting");
rados_shutdown(cluster); rados_shutdown(cluster);
return -EIO; return ret;
} }
if (rados_ioctx_create(cluster, pool, &io_ctx) < 0) { ret = rados_ioctx_create(cluster, pool, &io_ctx);
error_setg(errp, "error opening pool %s", pool); if (ret < 0) {
error_setg_errno(errp, -ret, "error opening pool %s", pool);
rados_shutdown(cluster); rados_shutdown(cluster);
return -EIO; return ret;
} }
ret = rbd_create(io_ctx, name, bytes, &obj_order); ret = rbd_create(io_ctx, name, bytes, &obj_order);
rados_ioctx_destroy(io_ctx); rados_ioctx_destroy(io_ctx);
rados_shutdown(cluster); rados_shutdown(cluster);
if (ret < 0) {
error_setg_errno(errp, -ret, "error rbd create");
return ret;
}
return ret; return ret;
} }
@ -500,7 +508,7 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
clientname = qemu_rbd_parse_clientname(conf, clientname_buf); clientname = qemu_rbd_parse_clientname(conf, clientname_buf);
r = rados_create(&s->cluster, clientname); r = rados_create(&s->cluster, clientname);
if (r < 0) { if (r < 0) {
error_setg(errp, "error initializing"); error_setg_errno(errp, -r, "error initializing");
goto failed_opts; goto failed_opts;
} }
@ -546,19 +554,19 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
r = rados_connect(s->cluster); r = rados_connect(s->cluster);
if (r < 0) { if (r < 0) {
error_setg(errp, "error connecting"); error_setg_errno(errp, -r, "error connecting");
goto failed_shutdown; goto failed_shutdown;
} }
r = rados_ioctx_create(s->cluster, pool, &s->io_ctx); r = rados_ioctx_create(s->cluster, pool, &s->io_ctx);
if (r < 0) { if (r < 0) {
error_setg(errp, "error opening pool %s", pool); error_setg_errno(errp, -r, "error opening pool %s", pool);
goto failed_shutdown; goto failed_shutdown;
} }
r = rbd_open(s->io_ctx, s->name, &s->image, s->snap); r = rbd_open(s->io_ctx, s->name, &s->image, s->snap);
if (r < 0) { if (r < 0) {
error_setg(errp, "error reading header from %s", s->name); error_setg_errno(errp, -r, "error reading header from %s", s->name);
goto failed_open; goto failed_open;
} }

View File

@ -2784,12 +2784,19 @@ static int sd_save_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
return ret; return ret;
} }
static int sd_load_vmstate(BlockDriverState *bs, uint8_t *data, static int sd_load_vmstate(BlockDriverState *bs, QEMUIOVector *qiov,
int64_t pos, int size) int64_t pos)
{ {
BDRVSheepdogState *s = bs->opaque; BDRVSheepdogState *s = bs->opaque;
void *buf;
int ret;
return do_load_save_vmstate(s, data, pos, size, 1); buf = qemu_blockalign(bs, qiov->size);
ret = do_load_save_vmstate(s, buf, pos, qiov->size, 1);
qemu_iovec_from_buf(qiov, 0, buf, qiov->size);
qemu_vfree(buf);
return ret;
} }

View File

@ -2544,6 +2544,7 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *medium_bs = NULL; BlockDriverState *medium_bs = NULL;
int bdrv_flags; int bdrv_flags;
int rc;
QDict *options = NULL; QDict *options = NULL;
Error *err = NULL; Error *err = NULL;
@ -2598,11 +2599,13 @@ void qmp_blockdev_change_medium(const char *device, const char *filename,
goto fail; goto fail;
} }
qmp_blockdev_open_tray(device, false, false, &err); rc = do_open_tray(device, false, &err);
if (err) { if (rc && rc != -ENOSYS) {
error_propagate(errp, err); error_propagate(errp, err);
goto fail; goto fail;
} }
error_free(err);
err = NULL;
qmp_x_blockdev_remove_medium(device, &err); qmp_x_blockdev_remove_medium(device, &err);
if (err) { if (err) {
@ -3423,6 +3426,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
BlockDriverState *target, BlockDriverState *target,
bool has_replaces, const char *replaces, bool has_replaces, const char *replaces,
enum MirrorSyncMode sync, enum MirrorSyncMode sync,
BlockMirrorBackingMode backing_mode,
bool has_speed, int64_t speed, bool has_speed, int64_t speed,
bool has_granularity, uint32_t granularity, bool has_granularity, uint32_t granularity,
bool has_buf_size, int64_t buf_size, bool has_buf_size, int64_t buf_size,
@ -3480,7 +3484,7 @@ static void blockdev_mirror_common(BlockDriverState *bs,
*/ */
mirror_start(bs, target, mirror_start(bs, target,
has_replaces ? replaces : NULL, has_replaces ? replaces : NULL,
speed, granularity, buf_size, sync, speed, granularity, buf_size, sync, backing_mode,
on_source_error, on_target_error, unmap, on_source_error, on_target_error, unmap,
block_job_cb, bs, errp); block_job_cb, bs, errp);
} }
@ -3503,6 +3507,7 @@ void qmp_drive_mirror(const char *device, const char *target,
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *source, *target_bs; BlockDriverState *source, *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode;
Error *local_err = NULL; Error *local_err = NULL;
QDict *options = NULL; QDict *options = NULL;
int flags; int flags;
@ -3576,6 +3581,12 @@ void qmp_drive_mirror(const char *device, const char *target,
} }
} }
if (mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) {
backing_mode = MIRROR_SOURCE_BACKING_CHAIN;
} else {
backing_mode = MIRROR_OPEN_BACKING_CHAIN;
}
if ((sync == MIRROR_SYNC_MODE_FULL || !source) if ((sync == MIRROR_SYNC_MODE_FULL || !source)
&& mode != NEW_IMAGE_MODE_EXISTING) && mode != NEW_IMAGE_MODE_EXISTING)
{ {
@ -3624,7 +3635,7 @@ void qmp_drive_mirror(const char *device, const char *target,
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
blockdev_mirror_common(bs, target_bs, blockdev_mirror_common(bs, target_bs,
has_replaces, replaces, sync, has_replaces, replaces, sync, backing_mode,
has_speed, speed, has_speed, speed,
has_granularity, granularity, has_granularity, granularity,
has_buf_size, buf_size, has_buf_size, buf_size,
@ -3656,6 +3667,7 @@ void qmp_blockdev_mirror(const char *device, const char *target,
BlockBackend *blk; BlockBackend *blk;
BlockDriverState *target_bs; BlockDriverState *target_bs;
AioContext *aio_context; AioContext *aio_context;
BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN;
Error *local_err = NULL; Error *local_err = NULL;
blk = blk_by_name(device); blk = blk_by_name(device);
@ -3681,7 +3693,7 @@ void qmp_blockdev_mirror(const char *device, const char *target,
bdrv_set_aio_context(target_bs, aio_context); bdrv_set_aio_context(target_bs, aio_context);
blockdev_mirror_common(bs, target_bs, blockdev_mirror_common(bs, target_bs,
has_replaces, replaces, sync, has_replaces, replaces, sync, backing_mode,
has_speed, speed, has_speed, speed,
has_granularity, granularity, has_granularity, granularity,
has_buf_size, buf_size, has_buf_size, buf_size,
@ -4154,22 +4166,18 @@ void qmp_x_blockdev_change(const char *parent, bool has_child,
BlockJobInfoList *qmp_query_block_jobs(Error **errp) BlockJobInfoList *qmp_query_block_jobs(Error **errp)
{ {
BlockJobInfoList *head = NULL, **p_next = &head; BlockJobInfoList *head = NULL, **p_next = &head;
BlockDriverState *bs; BlockJob *job;
BdrvNextIterator it;
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { for (job = block_job_next(NULL); job; job = block_job_next(job)) {
AioContext *aio_context = bdrv_get_aio_context(bs); BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
AioContext *aio_context = blk_get_aio_context(job->blk);
aio_context_acquire(aio_context); aio_context_acquire(aio_context);
elem->value = block_job_query(job);
if (bs->job) {
BlockJobInfoList *elem = g_new0(BlockJobInfoList, 1);
elem->value = block_job_query(bs->job);
*p_next = elem;
p_next = &elem->next;
}
aio_context_release(aio_context); aio_context_release(aio_context);
*p_next = elem;
p_next = &elem->next;
} }
return head; return head;

View File

@ -361,10 +361,12 @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
} }
job->busy = false; job->busy = false;
if (!block_job_is_paused(job)) {
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
}
/* The job can be paused while sleeping, so check this again */
if (block_job_is_paused(job)) { if (block_job_is_paused(job)) {
qemu_coroutine_yield(); qemu_coroutine_yield();
} else {
co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
} }
job->busy = true; job->busy = true;
} }

5
hmp.c
View File

@ -1955,7 +1955,12 @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
blk = blk_by_name(device); blk = blk_by_name(device);
if (blk) { if (blk) {
AioContext *aio_context = blk_get_aio_context(blk);
aio_context_acquire(aio_context);
qemuio_command(blk, command); qemuio_command(blk, command);
aio_context_release(aio_context);
} else { } else {
error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND, error_set(&err, ERROR_CLASS_DEVICE_NOT_FOUND,
"Device '%s' not found", device); "Device '%s' not found", device);

View File

@ -900,7 +900,7 @@ static int m25p80_init(SSISlave *ss)
s->storage = blk_blockalign(s->blk, s->size); s->storage = blk_blockalign(s->blk, s->size);
/* FIXME: Move to late init */ /* FIXME: Move to late init */
if (blk_pread(s->blk, 0, s->storage, s->size)) { if (blk_pread(s->blk, 0, s->storage, s->size) != s->size) {
fprintf(stderr, "Failed to initialize SPI flash!\n"); fprintf(stderr, "Failed to initialize SPI flash!\n");
return 1; return 1;
} }

View File

@ -65,6 +65,9 @@ typedef enum {
BDRV_REQ_MAY_UNMAP = 0x4, BDRV_REQ_MAY_UNMAP = 0x4,
BDRV_REQ_NO_SERIALISING = 0x8, BDRV_REQ_NO_SERIALISING = 0x8,
BDRV_REQ_FUA = 0x10, BDRV_REQ_FUA = 0x10,
/* Mask of valid flags */
BDRV_REQ_MASK = 0x1f,
} BdrvRequestFlags; } BdrvRequestFlags;
typedef struct BlockSizes { typedef struct BlockSizes {
@ -232,6 +235,7 @@ int bdrv_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags); int bdrv_make_zero(BlockDriverState *bs, BdrvRequestFlags flags);
int bdrv_pread(BlockDriverState *bs, int64_t offset, int bdrv_pread(BlockDriverState *bs, int64_t offset,
void *buf, int count); void *buf, int count);
int bdrv_preadv(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov);
int bdrv_pwrite(BlockDriverState *bs, int64_t offset, int bdrv_pwrite(BlockDriverState *bs, int64_t offset,
const void *buf, int count); const void *buf, int count);
int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov); int bdrv_pwritev(BlockDriverState *bs, int64_t offset, QEMUIOVector *qiov);
@ -401,10 +405,14 @@ int bdrv_write_compressed(BlockDriverState *bs, int64_t sector_num,
const uint8_t *buf, int nb_sectors); const uint8_t *buf, int nb_sectors);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs); ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
void bdrv_round_sectors_to_clusters(BlockDriverState *bs,
int64_t sector_num, int nb_sectors,
int64_t *cluster_sector_num,
int *cluster_nb_sectors);
void bdrv_round_to_clusters(BlockDriverState *bs, void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t sector_num, int nb_sectors, int64_t offset, unsigned int bytes,
int64_t *cluster_sector_num, int64_t *cluster_offset,
int *cluster_nb_sectors); unsigned int *cluster_bytes);
const char *bdrv_get_encrypted_filename(BlockDriverState *bs); const char *bdrv_get_encrypted_filename(BlockDriverState *bs);
void bdrv_get_backing_filename(BlockDriverState *bs, void bdrv_get_backing_filename(BlockDriverState *bs,
@ -423,6 +431,7 @@ void path_combine(char *dest, int dest_size,
const char *base_path, const char *base_path,
const char *filename); const char *filename);
int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf,
int64_t pos, int size); int64_t pos, int size);

View File

@ -224,10 +224,12 @@ struct BlockDriver {
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs); ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
int (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
int64_t pos); QEMUIOVector *qiov,
int (*bdrv_load_vmstate)(BlockDriverState *bs, uint8_t *buf, int64_t pos);
int64_t pos, int size); int coroutine_fn (*bdrv_load_vmstate)(BlockDriverState *bs,
QEMUIOVector *qiov,
int64_t pos);
int (*bdrv_change_backing_file)(BlockDriverState *bs, int (*bdrv_change_backing_file)(BlockDriverState *bs,
const char *backing_file, const char *backing_fmt); const char *backing_file, const char *backing_fmt);
@ -449,9 +451,6 @@ struct BlockDriverState {
/* I/O Limits */ /* I/O Limits */
BlockLimits bl; BlockLimits bl;
/* Whether produces zeros when read beyond eof */
bool zero_beyond_eof;
/* Alignment requirement for offset/length of I/O requests */ /* Alignment requirement for offset/length of I/O requests */
unsigned int request_alignment; unsigned int request_alignment;
/* Flags honored during pwrite (so far: BDRV_REQ_FUA) */ /* Flags honored during pwrite (so far: BDRV_REQ_FUA) */
@ -510,6 +509,20 @@ struct BlockBackendRootState {
BlockdevDetectZeroesOptions detect_zeroes; BlockdevDetectZeroesOptions detect_zeroes;
}; };
typedef enum BlockMirrorBackingMode {
/* Reuse the existing backing chain from the source for the target.
* - sync=full: Set backing BDS to NULL.
* - sync=top: Use source's backing BDS.
* - sync=none: Use source as the backing BDS. */
MIRROR_SOURCE_BACKING_CHAIN,
/* Open the target's backing chain completely anew */
MIRROR_OPEN_BACKING_CHAIN,
/* Do not change the target's backing BDS after job completion */
MIRROR_LEAVE_BACKING_CHAIN,
} BlockMirrorBackingMode;
static inline BlockDriverState *backing_bs(BlockDriverState *bs) static inline BlockDriverState *backing_bs(BlockDriverState *bs)
{ {
return bs->backing ? bs->backing->bs : NULL; return bs->backing ? bs->backing->bs : NULL;
@ -672,6 +685,7 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
* @granularity: The chosen granularity for the dirty bitmap. * @granularity: The chosen granularity for the dirty bitmap.
* @buf_size: The amount of data that can be in flight at one time. * @buf_size: The amount of data that can be in flight at one time.
* @mode: Whether to collapse all images in the chain to the target. * @mode: Whether to collapse all images in the chain to the target.
* @backing_mode: How to establish the target's backing chain after completion.
* @on_source_error: The action to take upon error reading from the source. * @on_source_error: The action to take upon error reading from the source.
* @on_target_error: The action to take upon error writing to the target. * @on_target_error: The action to take upon error writing to the target.
* @unmap: Whether to unmap target where source sectors only contain zeroes. * @unmap: Whether to unmap target where source sectors only contain zeroes.
@ -687,7 +701,8 @@ void commit_active_start(BlockDriverState *bs, BlockDriverState *base,
void mirror_start(BlockDriverState *bs, BlockDriverState *target, void mirror_start(BlockDriverState *bs, BlockDriverState *target,
const char *replaces, const char *replaces,
int64_t speed, uint32_t granularity, int64_t buf_size, int64_t speed, uint32_t granularity, int64_t buf_size,
MirrorSyncMode mode, BlockdevOnError on_source_error, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode,
BlockdevOnError on_source_error,
BlockdevOnError on_target_error, BlockdevOnError on_target_error,
bool unmap, bool unmap,
BlockCompletionFunc *cb, BlockCompletionFunc *cb,

View File

@ -3570,7 +3570,7 @@ static int img_bench(int argc, char **argv)
BlockBackend *blk = NULL; BlockBackend *blk = NULL;
BenchData data = {}; BenchData data = {};
int flags = 0; int flags = 0;
bool writethrough; bool writethrough = false;
struct timeval t1, t2; struct timeval t1, t2;
int i; int i;

View File

@ -42,22 +42,14 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on
Testing: -S Testing: -S
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
IMGFMT built-in AES encryption is deprecated {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}}
Support for it will be removed in a future release.
You can use 'qemu-img convert' to switch to an
unencrypted IMGFMT image, or a LUKS raw image.
{"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
Testing: Testing:
QMP_VERSION QMP_VERSION
{"return": {}} {"return": {}}
IMGFMT built-in AES encryption is deprecated {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}}
Support for it will be removed in a future release.
You can use 'qemu-img convert' to switch to an
unencrypted IMGFMT image, or a LUKS raw image.
{"error": {"class": "GenericError", "desc": "Guest must be stopped for opening of encrypted image"}}
{"return": {}} {"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}

View File

@ -74,6 +74,8 @@ _send_qemu_cmd $h "{ 'execute': 'block-commit',
'arguments': { 'device': 'test', 'arguments': { 'device': 'test',
'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED" 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED"
_cleanup_qemu
echo echo
echo "=== Base image info after commit and resize ===" echo "=== Base image info after commit and resize ==="
TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info

261
tests/qemu-iotests/155 Executable file
View File

@ -0,0 +1,261 @@
#!/usr/bin/env python
#
# Test whether the backing BDSs are correct after completion of a
# mirror block job; in "existing" modes (drive-mirror with
# mode=existing and blockdev-mirror) the backing chain should not be
# overridden.
#
# Copyright (C) 2016 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import iotests
from iotests import qemu_img
back0_img = os.path.join(iotests.test_dir, 'back0.' + iotests.imgfmt)
back1_img = os.path.join(iotests.test_dir, 'back1.' + iotests.imgfmt)
back2_img = os.path.join(iotests.test_dir, 'back2.' + iotests.imgfmt)
source_img = os.path.join(iotests.test_dir, 'source.' + iotests.imgfmt)
target_img = os.path.join(iotests.test_dir, 'target.' + iotests.imgfmt)
# Class variables for controlling its behavior:
#
# existing: If True, explicitly create the target image and blockdev-add it
# target_backing: If existing is True: Use this filename as the backing file
# of the target image
# (None: no backing file)
# target_blockdev_backing: If existing is True: Pass this dict as "backing"
# for the blockdev-add command
# (None: do not pass "backing")
# target_real_backing: If existing is True: The real filename of the backing
# image during runtime, only makes sense if
# target_blockdev_backing is not None
# (None: same as target_backing)
class BaseClass(iotests.QMPTestCase):
target_blockdev_backing = None
target_real_backing = None
def setUp(self):
qemu_img('create', '-f', iotests.imgfmt, back0_img, '1M')
qemu_img('create', '-f', iotests.imgfmt, '-b', back0_img, back1_img)
qemu_img('create', '-f', iotests.imgfmt, '-b', back1_img, back2_img)
qemu_img('create', '-f', iotests.imgfmt, '-b', back2_img, source_img)
self.vm = iotests.VM()
self.vm.add_drive(None, '', 'none')
self.vm.launch()
# Add the BDS via blockdev-add so it stays around after the mirror block
# job has been completed
result = self.vm.qmp('blockdev-add',
options={'node-name': 'source',
'driver': iotests.imgfmt,
'file': {'driver': 'file',
'filename': source_img}})
self.assert_qmp(result, 'return', {})
result = self.vm.qmp('x-blockdev-insert-medium',
device='drive0', node_name='source')
self.assert_qmp(result, 'return', {})
self.assertIntactSourceBackingChain()
if self.existing:
if self.target_backing:
qemu_img('create', '-f', iotests.imgfmt,
'-b', self.target_backing, target_img, '1M')
else:
qemu_img('create', '-f', iotests.imgfmt, target_img, '1M')
if self.cmd == 'blockdev-mirror':
options = { 'node-name': 'target',
'driver': iotests.imgfmt,
'file': { 'driver': 'file',
'filename': target_img } }
if self.target_blockdev_backing:
options['backing'] = self.target_blockdev_backing
result = self.vm.qmp('blockdev-add', options=options)
self.assert_qmp(result, 'return', {})
def tearDown(self):
self.vm.shutdown()
os.remove(source_img)
os.remove(back2_img)
os.remove(back1_img)
os.remove(back0_img)
try:
os.remove(target_img)
except OSError:
pass
def findBlockNode(self, node_name, id=None):
if id:
result = self.vm.qmp('query-block')
for device in result['return']:
if device['device'] == id:
if node_name:
self.assert_qmp(device, 'inserted/node-name', node_name)
return device['inserted']
else:
result = self.vm.qmp('query-named-block-nodes')
for node in result['return']:
if node['node-name'] == node_name:
return node
self.fail('Cannot find node %s/%s' % (id, node_name))
def assertIntactSourceBackingChain(self):
node = self.findBlockNode('source')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
source_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',
back2_img)
self.assert_qmp(node, 'image' + '/backing-image' * 2 + '/filename',
back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 3 + '/filename',
back0_img)
self.assert_qmp_absent(node, 'image' + '/backing-image' * 4)
def assertCorrectBackingImage(self, node, default_image):
if self.existing:
if self.target_real_backing:
image = self.target_real_backing
else:
image = self.target_backing
else:
image = default_image
if image:
self.assert_qmp(node, 'image/backing-image/filename', image)
else:
self.assert_qmp_absent(node, 'image/backing-image')
# Class variables for controlling its behavior:
#
# cmd: Mirroring command to execute, either drive-mirror or blockdev-mirror
class MirrorBaseClass(BaseClass):
def runMirror(self, sync):
if self.cmd == 'blockdev-mirror':
result = self.vm.qmp(self.cmd, device='drive0', sync=sync,
target='target')
else:
if self.existing:
mode = 'existing'
else:
mode = 'absolute-paths'
result = self.vm.qmp(self.cmd, device='drive0', sync=sync,
target=target_img, format=iotests.imgfmt,
mode=mode, node_name='target')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_READY')
result = self.vm.qmp('block-job-complete', device='drive0')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_COMPLETED')
def testFull(self):
self.runMirror('full')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, None)
self.assertIntactSourceBackingChain()
def testTop(self):
self.runMirror('top')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, back2_img)
self.assertIntactSourceBackingChain()
def testNone(self):
self.runMirror('none')
node = self.findBlockNode('target', 'drive0')
self.assertCorrectBackingImage(node, source_img)
self.assertIntactSourceBackingChain()
class TestDriveMirrorAbsolutePaths(MirrorBaseClass):
cmd = 'drive-mirror'
existing = False
class TestDriveMirrorExistingNoBacking(MirrorBaseClass):
cmd = 'drive-mirror'
existing = True
target_backing = None
class TestDriveMirrorExistingBacking(MirrorBaseClass):
cmd = 'drive-mirror'
existing = True
target_backing = 'null-co://'
class TestBlockdevMirrorNoBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = None
class TestBlockdevMirrorBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = 'null-co://'
class TestBlockdevMirrorForcedBacking(MirrorBaseClass):
cmd = 'blockdev-mirror'
existing = True
target_backing = None
target_blockdev_backing = { 'driver': 'null-co' }
target_real_backing = 'null-co://'
class TestCommit(BaseClass):
existing = False
def testCommit(self):
result = self.vm.qmp('block-commit', device='drive0', base=back1_img)
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_READY')
result = self.vm.qmp('block-job-complete', device='drive0')
self.assert_qmp(result, 'return', {})
self.vm.event_wait('BLOCK_JOB_COMPLETED')
node = self.findBlockNode(None, 'drive0')
self.assert_qmp(node, 'image' + '/backing-image' * 0 + '/filename',
back1_img)
self.assert_qmp(node, 'image' + '/backing-image' * 1 + '/filename',
back0_img)
self.assert_qmp_absent(node, 'image' + '/backing-image' * 2 +
'/filename')
self.assertIntactSourceBackingChain()
BaseClass = None
MirrorBaseClass = None
if __name__ == '__main__':
iotests.main(supported_fmts=['qcow2'])

View File

@ -0,0 +1,5 @@
...................
----------------------------------------------------------------------
Ran 19 tests
OK

174
tests/qemu-iotests/156 Executable file
View File

@ -0,0 +1,174 @@
#!/bin/bash
#
# Tests oVirt-like storage migration:
# - Create snapshot
# - Create target image with (not yet existing) target backing chain
# (i.e. just write the name of a soon-to-be-copied-over backing file into it)
# - drive-mirror the snapshot to the target with mode=existing and sync=top
# - In the meantime, copy the original source files to the destination via
# conventional means (i.e. outside of qemu)
# - Complete the drive-mirror job
# - Delete all source images
#
# Copyright (C) 2016 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# creator
owner=mreitz@redhat.com
seq="$(basename $0)"
echo "QA output created by $seq"
here="$PWD"
status=1 # failure is the default!
_cleanup()
{
rm -f "$TEST_IMG{,.target}{,.backing,.overlay}"
}
trap "_cleanup; exit \$status" 0 1 2 3 15
# get standard environment, filters and checks
. ./common.rc
. ./common.filter
. ./common.qemu
_supported_fmt qcow2 qed
_supported_proto generic
_supported_os Linux
# Create source disk
TEST_IMG="$TEST_IMG.backing" _make_test_img 1M
_make_test_img -b "$TEST_IMG.backing" 1M
$QEMU_IO -c 'write -P 1 0 256k' "$TEST_IMG.backing" | _filter_qemu_io
$QEMU_IO -c 'write -P 2 64k 192k' "$TEST_IMG" | _filter_qemu_io
_launch_qemu -drive if=none,id=source,file="$TEST_IMG"
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'qmp_capabilities' }" \
'return'
# Create snapshot
TEST_IMG="$TEST_IMG.overlay" _make_test_img -b "$TEST_IMG" 1M
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'blockdev-snapshot-sync',
'arguments': { 'device': 'source',
'snapshot-file': '$TEST_IMG.overlay',
'format': '$IMGFMT',
'mode': 'existing' } }" \
'return'
# Write something to the snapshot
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"write -P 3 128k 128k\"' } }" \
'return'
# Create target image
TEST_IMG="$TEST_IMG.target.overlay" _make_test_img -b "$TEST_IMG.target" 1M
# Mirror snapshot
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'drive-mirror',
'arguments': { 'device': 'source',
'target': '$TEST_IMG.target.overlay',
'mode': 'existing',
'sync': 'top' } }" \
'return'
# Wait for convergence
_send_qemu_cmd $QEMU_HANDLE \
'' \
'BLOCK_JOB_READY'
# Write some more
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"write -P 4 192k 64k\"' } }" \
'return'
# Copy source backing chain to the target before completing the job
cp "$TEST_IMG.backing" "$TEST_IMG.target.backing"
cp "$TEST_IMG" "$TEST_IMG.target"
$QEMU_IMG rebase -u -b "$TEST_IMG.target.backing" "$TEST_IMG.target"
# Complete block job
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'block-job-complete',
'arguments': { 'device': 'source' } }" \
''
_send_qemu_cmd $QEMU_HANDLE \
'' \
'BLOCK_JOB_COMPLETED'
# Remove the source images
rm -f "$TEST_IMG{,.backing,.overlay}"
echo
# Check online disk contents
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 1 0k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 2 64k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 3 128k 64k\"' } }" \
'return'
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'human-monitor-command',
'arguments': { 'command-line':
'qemu-io source \"read -P 4 192k 64k\"' } }" \
'return'
echo
_send_qemu_cmd $QEMU_HANDLE \
"{ 'execute': 'quit' }" \
'return'
wait=1 _cleanup_qemu
echo
# Check offline disk contents
$QEMU_IO -c 'read -P 1 0k 64k' \
-c 'read -P 2 64k 64k' \
-c 'read -P 3 128k 64k' \
-c 'read -P 4 192k 64k' \
"$TEST_IMG.target.overlay" | _filter_qemu_io
echo
# success, all done
echo '*** done'
rm -f $seq.full
status=0

View File

@ -0,0 +1,48 @@
QA output created by 156
Formatting 'TEST_DIR/t.IMGFMT.backing', fmt=IMGFMT size=1048576
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.backing
wrote 262144/262144 bytes at offset 0
256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
wrote 196608/196608 bytes at offset 65536
192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": {}}
Formatting 'TEST_DIR/t.IMGFMT.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT
{"return": {}}
wrote 131072/131072 bytes at offset 131072
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}}
wrote 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}}
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
read 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
{"return": ""}
{"return": {}}
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
read 65536/65536 bytes at offset 0
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 65536
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 131072
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
read 65536/65536 bytes at offset 196608
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
*** done

View File

@ -17,4 +17,5 @@ additional options to test further image formats or I/O methods.
* Feedback and patches * Feedback and patches
Please send improvements to the test suite, general feedback or just Please send improvements to the test suite, general feedback or just
reports of failing tests cases to qemu-devel@savannah.nongnu.org. reports of failing tests cases to qemu-devel@nongnu.org with a CC:
to qemu-block@nongnu.org.

View File

@ -154,3 +154,5 @@
150 rw auto quick 150 rw auto quick
152 rw auto quick 152 rw auto quick
154 rw auto backing quick 154 rw auto backing quick
155 rw auto
156 rw auto quick

View File

@ -73,7 +73,7 @@ bdrv_aio_writev(void *bs, int64_t sector_num, int nb_sectors, void *opaque) "bs
bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_readv(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d" bdrv_co_writev(void *bs, int64_t sector_num, int nb_sector) "bs %p sector_num %"PRId64" nb_sectors %d"
bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags %#x" bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags %#x"
bdrv_co_do_copy_on_readv(void *bs, int64_t sector_num, int nb_sectors, int64_t cluster_sector_num, int cluster_nb_sectors) "bs %p sector_num %"PRId64" nb_sectors %d cluster_sector_num %"PRId64" cluster_nb_sectors %d" bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t cluster_offset, unsigned int cluster_bytes) "bs %p offset %"PRId64" bytes %u cluster_offset %"PRId64" cluster_bytes %u"
# block/stream.c # block/stream.c
stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d" stream_one_iteration(void *s, int64_t sector_num, int nb_sectors, int is_allocated) "s %p sector_num %"PRId64" nb_sectors %d is_allocated %d"
@ -606,16 +606,16 @@ qemu_system_shutdown_request(void) ""
qemu_system_powerdown_request(void) "" qemu_system_powerdown_request(void) ""
# block/qcow2.c # block/qcow2.c
qcow2_writev_start_req(void *co, int64_t sector, int nb_sectors) "co %p sector %" PRIx64 " nb_sectors %d" qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset %" PRIx64 " bytes %d"
qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_done_req(void *co, int ret) "co %p ret %d"
qcow2_writev_start_part(void *co) "co %p" qcow2_writev_start_part(void *co) "co %p"
qcow2_writev_done_part(void *co, int cur_nr_sectors) "co %p cur_nr_sectors %d" qcow2_writev_done_part(void *co, int cur_bytes) "co %p cur_bytes %d"
qcow2_writev_data(void *co, uint64_t offset) "co %p offset %" PRIx64 qcow2_writev_data(void *co, uint64_t offset) "co %p offset %" PRIx64
qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int count) "co %p offset %" PRIx64 " count %d" qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int count) "co %p offset %" PRIx64 " count %d"
qcow2_pwrite_zeroes(void *co, int64_t offset, int count) "co %p offset %" PRIx64 " count %d" qcow2_pwrite_zeroes(void *co, int64_t offset, int count) "co %p offset %" PRIx64 " count %d"
# block/qcow2-cluster.c # block/qcow2-cluster.c
qcow2_alloc_clusters_offset(void *co, uint64_t offset, int num) "co %p offset %" PRIx64 " num %d" qcow2_alloc_clusters_offset(void *co, uint64_t offset, int bytes) "co %p offset %" PRIx64 " bytes %d"
qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64
qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64 qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " bytes %" PRIx64
qcow2_do_alloc_clusters_offset(void *co, uint64_t guest_offset, uint64_t host_offset, int nb_clusters) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " nb_clusters %d" qcow2_do_alloc_clusters_offset(void *co, uint64_t guest_offset, uint64_t host_offset, int nb_clusters) "co %p guest_offset %" PRIx64 " host_offset %" PRIx64 " nb_clusters %d"

View File

@ -269,6 +269,7 @@ void hbitmap_set(HBitmap *hb, uint64_t start, uint64_t count)
start >>= hb->granularity; start >>= hb->granularity;
last >>= hb->granularity; last >>= hb->granularity;
count = last - start + 1; count = last - start + 1;
assert(last < hb->size);
hb->count += count - hb_count_between(hb, start, last); hb->count += count - hb_count_between(hb, start, last);
hb_set_between(hb, HBITMAP_LEVELS - 1, start, last); hb_set_between(hb, HBITMAP_LEVELS - 1, start, last);
@ -348,6 +349,7 @@ void hbitmap_reset(HBitmap *hb, uint64_t start, uint64_t count)
start >>= hb->granularity; start >>= hb->granularity;
last >>= hb->granularity; last >>= hb->granularity;
assert(last < hb->size);
hb->count -= hb_count_between(hb, start, last); hb->count -= hb_count_between(hb, start, last);
hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last); hb_reset_between(hb, HBITMAP_LEVELS - 1, start, last);
@ -371,6 +373,7 @@ bool hbitmap_get(const HBitmap *hb, uint64_t item)
/* Compute position and bit in the last layer. */ /* Compute position and bit in the last layer. */
uint64_t pos = item >> hb->granularity; uint64_t pos = item >> hb->granularity;
unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1)); unsigned long bit = 1UL << (pos & (BITS_PER_LONG - 1));
assert(pos < hb->size);
return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0; return (hb->levels[HBITMAP_LEVELS - 1][pos >> BITS_PER_LEVEL] & bit) != 0;
} }